The Berkeley Service Discovery Service
Tutorial for writing Clients
[Homepage] [System overview] [Clients] [Services] [Servers] [Config options]

Background

Most Ninja developers will want to use the Berkeley SDS to help locate services within the network. For example, a Ninja Jukebox client might want to find all local Ninja Jukebox servers when it starts up. This can be done easily by using the Berkeley SDS to make a query, specifying that you want all services with the type ninja.jukebox.MusicDirectory . This tutorial will describe how you can do this.

Using the Berkeley SDS to make queries is fairly easy. The typical routines that a client application will use to make these queries can be found in ninja.sds.SDS. Usually, the steps involved with initializing the Berkeley SDS and making queries are as follow:

  1. Initializing the SDS: Specifying the client certificate and inputting the capabilities that represent the client's access rigths
  2. Creating a query: Typically by just creating an ninja.sds.Query object and specifically what tags to look for.
  3. Sending the query: Done using the helper routines in ninja.sds.SDS.
  4. Processing the results: The matched service descriptions are returned as ninja.sds.SearchResult.
In the current implementation, you don't necessarily have to specify a certificate that identifies the client's principal. However, if no principal is specified, then the client can only make insecure SDS queries, and only match to service descriptions that do not require capabilities to discover them.

Making a secure SDS query

So, how do you make an SDS query? Well, let's step through the sample code to show you how this is done. You can look also look at the sample client code that's in the ninja.sds.examples.SampleClient class. That file contains the most up-to-date information, so see it if you have any problems. The code given below is a paraphrasing of that code.

First, be sure to import all the needed classes

     import ninja.sds.SDS; 
     import ninja.sds.Query;
     import ninja.sds.SearchResult;
     import ninja.sds.NoSDSfound;
     import ninja.ispace.auth.KeyReader;
     import ninja.ispace.auth.Authenticator;
     import java.rmi.Remote;

Next, we need the certificate and authenticator associated with the principal that's going to make the query (usually the client's identity). We need this so that we can prove that we are indeed the principal we claim to be, and also that we should have the right to discover whatever services that are available to us.

     KeyReader pr = new KeyReader(new File("~/my_private_key"));
    
     if (pr.needsDecryption())
        pr.decrypt(ninja.utils.PlatformDependent.readPassphrase("Enter passphrase for client's private key:"));

     Authenticator privateAuthenticator = pr.getAuthenticator(); 

After we have the authenticator and certificate, we can initialize the SDS help routines in ninja.sds.SDS.

     try {
       SDS.init("czerwin@cs.berkeley.edu.", privateAuthenticator, -1);
     } catch (NoSDSFound e) {
       System.err.println("Failed in finding an SDS server");
     }

We should point out that there are several variants on the SDS.Init, and you should examine them to see which one is appropriate for you. This variant is probably the easiest to use, since it automates many of the tasks you would have to perform.

The SDS.Init code performs the following actions for the user:

  1. Blocks until a SDS server announcement is heard: Once a server announcement is heard, then the client knows how to contact the SDS and which certificate registry and capability distributor to use. Note, it will use the first announcement heard, not necessarily the best one, which is decided based on narrowest scope.
  2. Contacts the certificate registry to retrieve the user's certificate: Note, the initialization code only contacts the registry specified in the server announcement. If you want a different one used, you'll have to use a different SDS.Init call.
  3. Contacts the capability distributor to retrieve all the user's capabilities: By default, the system uses all the capabilities that are available for the user. You can directly specify which capabilities to use or not use by using the SDS.setCapabilities routine. Again, the initialization code only contacts the distributor specified in the server announcement.

The next step is to create a query object, and passing it to the SDS server. We use the ninja.sds.Query class to compose the query, so that users don't typically have to mess around with XML. However, there are interfaces to bypass this and allow direct XML manipulation if so desired.

Using the ninja.sds.Query class, you can specify which tags you are looking for, and the value you want to match on. In our case, we are looking for a particular type.

     Query theQuery = new Query();
     theQuery.addTag("TYPE", "ninja.jukebox.MusicDirectory");

Now we submit the query the SDS, indicating that we want to use the #1 hierarchy. This hierarchy is just a place holder for the future wide area system.

     Vector result = SDS.Lookup(theQuery, 1);

     SearchResult first = (SearchResult) result.elementAt(0);
     Remote aService = SDS.GetServiceRMIref(first.getTag("URL"));

And that's it! We now have a stub to the service we were looking for.

Of course, this is the simplest way to use the SDS system, so you should look over the API's offered in ninja.sds.SDS to see what else you can do. Also, be sure to look over the cofig options page to see what options you can specify to change the behavior of the SDS. For example, you might want to change the multicast channel that is used to publish server announcements in order to use a private one for your own SDS servers during development stages.

Making an insecure SDS query

Making an insecure SDS query is even easier, since you don't have to worry about the client's certificate or private authenticator. Here's some sample code showing how to make an insecure query:
      SDS.init();

      Query theQuery = new Query();
      theQuery.addTag("TYPE", "ninja.jukebox.MusicDirectory");

      Vector result = SDS.Insecure_Lookup(query, 1);

      SearchResult first = (SearchResult) result.elementAt(0);
      Remote aService = SDS.GetServiceRMIref(first.getTag("URL")));

Of course, making queries in this manner bypasses all the very nice security features of the Berkeley SDS. We are only offering this service to ease the burden of developers during testing phases. In the future, we might remove this functionality all together.