Discovering a Lookup Service

This chapter looks at what is involved in discovering a lookup service/service locator. This is common to both services and clients.

1. Lookup Service

A client locates a service by querying a lookup service. In order to do this, it must first locate such a service. On the other hand, a service must register itself with the lookup service, and in order to do so it must also first locate a service.

The initial phase of both a client and a service is thus discovering a lookup service. Such a service (or set of services) will usually have been started by some independent mechanism. The search for a lookup service can be done either by unicast or by broadcast.

2. Unicast discovery

Unicast discovery can be used when you already know the machine on which the lookup service resides, so you can ask for it directly. This is expected to be used for a lookup service that is outside of your local network, which you know the address of anyway (possibly advertised in some newsgroup, or maybe even on TV!).

2.1 LookupLocator

The class LookupLocator in package net.jini.core.discovery is used for this. There are two constructors,


package net.jini.core.discovery;

public Class LookupLocator {

    LookupLocator(java.lang.String url)
                  throws java.net.MalformedURLException;
    LookupLocator(java.lang.String host,int port);
}
For the first constructor, the URL must be of the form jini://host/ or jini://host:port/. If no port is given, it defaults to 4160. The host should be localhost or some other valid DNS name. No unicast discovery is performed at this stage, though, so any rubbish could be entered. Only a check for syntactic validity is performed. This is not even done for the second constructor.

The following program creates some objects with valid and invalid host/URLs. They are only checked for syntactic validity rather than existence as URLs:



import net.jini.core.discovery.LookupLocator;

/**
 * InvalidLookupLocator.java
 *
 *
 * Created: Tue Mar  9 14:14:06 1999
 *
 * @author Jan Newmarch
 * @version
 */

public class InvalidLookupLocator  {

    static public void main(String argv[]) {
	new InvalidLookupLocator();
    }
   
    public InvalidLookupLocator() {
	LookupLocator lookup;

	// this is valid
	try {
	    lookup = new LookupLocator("jini://localhost");
	    System.out.println("First lookup creation succeeded");
	} catch(java.net.MalformedURLException e) {
	    System.err.println("First lookup failed: " + e.toString());
	}

	// this is probably an invalid URL, 
	// but the URL is syntactically okay
	try {
	    lookup = new LookupLocator("jini://ABCDEFG.org");
	    System.out.println("Second lookup creation succeeded");
	} catch(java.net.MalformedURLException e) {
	    System.err.println("Second lookup failed: " + e.toString());
	}

	// this IS a malformed URL
	try {
	    lookup = new LookupLocator("A:B:C://ABCDEFG.org");
	    System.out.println("Third lookup creation succeeded");
	} catch(java.net.MalformedURLException e) {
	    System.err.println("Third lookup failed: " + e.toString());
	}

	// this is valid
	lookup = new LookupLocator("localhost", 80);
	System.out.println("Fourth lookup creation succeeded");
    }
    
} // InvalidLookupLocator

2.2 Running the InvalidLookupLocator

All programs will need to be compiled using the JDK 1.2 compiler. The CLASSPATH will need to include the Jini files reggie.jar and jini-core.jar for compilation of the source code. When a service is run, these Jini files will need to be in its CLASSPATH. Similarly, when a client runs, it will also need these files in its CLASSPATH. And finally, when a lookup service is run it too will need these files in its CLASSPATH. The reason for this repetition is that the service, the client and lookup service all three separate applications, running in three separate Java Virtual Machines, and quite likely will be on three separate computers. They will each have separate requirements, but at least this is common to all three!

The InvalidLookupLocator has no additional requirements. It does not perform any network calls, and does not require any additional service to be running. So it can be run simply by


java -classpath ... InvalidLookupLocator

2.3 Get Registrar

Search and lookup is performed by the method getRegistrar() of the LookupLocator which returns an object of class ServiceRegistrar.


public ServiceRegistrar getRegistrar()
                              throws java.io.IOException,
                                     java.lang.ClassNotFoundException
The ServiceRegistrar is discussed in detail later. By this stage, the program looks like


import net.jini.core.discovery.LookupLocator;
import net.jini.core.lookup.ServiceRegistrar;

/**
 * UnicastRegistrar.java
 *
 *
 * Created: Fri Mar 12 22:34:53 1999
 *
 * @author Jan Newmarch
 * @version
 */

public class UnicastRegistrar  {
    
    static public void main(String argv[]) {
        new UnicastRegistrar();
    }
   
    public UnicastRegistrar() {
	LookupLocator lookup = null;
	ServiceRegistrar registrar = null;

        try {
            lookup = new LookupLocator("jini://localhost");
        } catch(java.net.MalformedURLException e) {
            System.err.println("Lookup failed: " + e.toString());
	    System.exit(1);
        }

	try {
	    registrar = lookup.getRegistrar();
	} catch (java.io.IOException e) {
            System.err.println("Registrar search failed: " + e.toString());
	    System.exit(1);
	} catch (java.lang.ClassNotFoundException e) {
            System.err.println("Registrar search failed: " + e.toString());
	    System.exit(1);
	}

	// the code takes separate routes from here for client or service
    }
   
} // UnicastRegistrar

This program might not run as is, due to security issues. If that is the case, see the chapter on Security.

2.4 Running the UnicastRegistrar

The program needs to be compiled and run with reggie.jar and jini-code.jar in its CLASSPATH. When run, it will attempt to connect to the service locator, so obviously one needs to be running on the machine specified in order for this to happen. Otherwise, the program will throw an exception and terminate.

This program will receive a ServiceRegistrar from the service locator. However, it does so by a simple readObject() on a socket connected to the service locator, and so does not need any additional support services such as rmiregistry.

3. Broadcast discovery

If the location of a lookup service is unknown, it is neccessary to make a broadcast search for one. The class LookupDiscovery in package net.jini.discovery is used for this. There is a single constructor


LookupDiscovery(java.lang.String[] groups)

The parameter to the LookupDiscovery constructor can take three cases

The concept of groups will be dealt with later, if I ever write a section on lookup locators. For now, there is an explanation of them by Roger Whitney at http://www.eli.sdsu.edu/courses/spring99/cs696/notes/

3.1 DiscoveryListener

A broadcast is an open-ended call across the (local??? What is the boundary of search???) network, expecting lookup services to reply as they receive it. Doing so may take time, and there will generally be an unknown number of lookup services that can reply. To handle this indeterminacy, the LookupDiscovery object can have a listener registered with it that is invoked as each reply comes in.


public void addDiscoveryListener(DiscoveryListener l)
The listener must implement the DiscoveryListener interface:

package net.jini.discovery;

public abstract interface DiscoveryListener {
    public void discovered(DiscoveryEvent e);
    public void discarded(DiscoveryEvent e);
}

The discovered() method is invoked whenever a lookup service has been discovered. The API recommends that this method should return quickly, and not make any remote calls. However, for a service it is the natural place to register the service, and for a client it is the natural place to ask if there is a service available and to invoke this service. It may be better to perform these lengthy operations in a separate thread.

There are other timing issues involved: when the DiscoveryListener is created, the broadcast is made. After this, a listener is added to this discovery object. What happens if replies come in very quickly, before the listener is added? The ``Jini Discovery Utilities Specification'' guarantees that these replies will be buffered and not lost (is there a limit to the buffer size???). Conversely, no replies may come in for a long time - what is the application supposed to do in the meantime? It cannot simply exit, because then there would be no object to reply to! it has to be made persistent enough to last till replies come in. One way of handling this is if the application has a GUI interface - then it will stay till the user dismisses it. Another is to go to sleep for, say, a thousand seconds.

The discarded() method is invoked whenever a lookup service is discarded (meaning ???).

3.2 DiscoveryEvent

The parameter to the discover()method is a DiscoveryEvent object.

package net.jini.discovery;

public Class DiscoveryEvent {
    public net.jini.core.lookup.ServiceRegistrar[] getRegistrars();
}
This has one public method, getRegistrars() which returns an array of ServiceRegistrar objects. (How can more than one be returned???). Each one of these is the same class as the object returned from a unicast search for a lookup service.

By this stage the program looks like



import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;

/**
 * MulticastRegistrar.java
 *
 *
 * Created: Fri Mar 12 22:49:33 1999
 *
 * @author Jan Newmarch
 * @version
 */

public class MulticastRegistrar implements DiscoveryListener {
 
    static public void main(String argv[]) {
        new MulticastRegistrar();
    }
      
    public MulticastRegistrar() {
        LookupDiscovery discover = null;
        try {
            discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
        } catch(Exception e) {
            System.err.println(e.toString());
	    e.printStackTrace();
	    System.exit(1);
        }

        discover.addDiscoveryListener(this);

	// stay around long enough to receive replies
	try {
	    Thread.currentThread().sleep(1000000L);
	} catch(java.lang.InterruptedException e) {
	    // do nothing
	}
    }
    
    public void discovered(DiscoveryEvent evt) {

        ServiceRegistrar[] registrars = evt.getRegistrars();

        for (int n = 0; n < registrars.length; n++) {
	    ServiceRegistrar registrar = registrars[n];

	    // the code takes separate routes from here for client or service
	    System.out.println("found a service locator");
  	}
    }

    public void discarded(DiscoveryEvent evt) {

    }
} // MulticastRegistrar



3.3 Running the MulticastRegistrar

The program needs to be compiled and run with reggie.jar and jini-code.jar in its CLASSPATH. When run, it will attempt to find all service locators that it can. If there are none, it will find none - pretty boring. So one or more should be set running in the near network or on the local machine. How far away can they be???.

This program will receive ServiceRegistrar's from the service locators. However, it does so by a simple readObject() on a socket connected to a service locator, and so does not need any additional support services such as rmiregistry.

4. ServiceRegistrar

The ServiceRegistrar is an abstract class which is implemented by each lookup service. The actual details of this implementation are not relevant here. The role of a ServiceRegistrar is to act as a proxy for the lookup service. This proxy runs in the application, which may be a service or a client.

This is the first object that is moved from one Java process to another in Jini. It is shipped from the lookup service to the application looking for the lookup service, using a socket connection. From then it runs as an object in the application's address space, and the application makes normal method calls to it. When needed, it communicates back to its lookup service, but does so without explicit RMI calls being needed by the application. Presumably it caches some info about the lookup service - exactly what??? The set of services???

This object has two major methods, one used by a service attempting to register


public ServiceRegistration register(ServiceItem item,
                                    long leaseDuration)
                             throws java.rmi.RemoteException
and the other(s) by a client trying to locate a particular service

public java.lang.Object lookup(ServiceTemplate tmpl)
                        throws java.rmi.RemoteException;
public ServiceMatches lookup(ServiceTemplate tmpl,
                             int maxMatches)
                      throws java.rmi.RemoteException;
The details of these are given later. For now, an overview will suffice.

A service will register an object (that is, an instance of a class), and a set of attributes for that object. For example, a printer may specify that it can handle Postscript documents, or a toaster that it can deal with frozen slices of bread. The service may register itself, or it may register a proxy that can act on its behalf. Note carefully: the registered object will be shipped around using RMI. When it finally gets to run, it may be a long way away from where it was originally created. It will have been created in the service's JVM, transferred to the lookup locator by register() and then to the client's JVM by lookup().

A client is trying to find a service, using some properties of the service that it knows about. Whereas the service can export a live object, the client cannot use a service object as a property - because then it would already have the thing, and wouldn't need to try to find one! What it can do is to use a class object, and try to find if there are any instances of this class lying around in service locators. In addition, it may specify a set of attribute values that it requires from the service.

The next step is to look at the possible forms of attribute values, and how matching will be performed. This is done using Jini Entry objects. The simplest services, and the least demanding clients, will not require any attributes: the Entry[] array will be null. You may wish to skip ahead to service registration or to client search

This file is Copyright ©Jan Newmarch (http://jan.newmarch.name) jan@newmarch.name

The copyright is the OpenContent License (http://www.opencontent.org/opl.shtml), which is the ``document'' version of the GNU OpenSource license.