A brief introduction to Jini

Jini is aimed towards the creation of networks of services that can be searched for and found without prior knowledge of their location.

A service provider is anyone that wishes to make his services available to the community.

A Jini service will try to locate an entity called the Lookup Service. It is a Java class that uses the Jini specification protocols for announcement and discovery.

A given Jini service will have methods that can be programatically invoked. Those methods must be defined in a Java interface. This interface will describe the Jini service methods. It must be written by the service provider.

The interface methods need not to map one-by-one with the operations provided by the service itself. The interface must describe those methods the service provider wishes to make available to the community.

When someone wants to publish a service he writes a service proxy. A service proxy is a Java class that communicates with the real service. The service itself needn't be a Java class. The service proxy acts as a gateway for Java clients to use the service. The service proxy is an implementation of the service interface. This means that the Java code for the service proxy will include an implements statement followed by the name of the service interface.

The service proxy must implement all the service interface public methods. The implementation methods should contact the real service and use it to do some work.

For instance, a Jini service might be a printer server. The printer server class will contact the printer (by any means available to the service class). It could provide with just one method, print, which takes as a parameter a String with the contents of the text file that is wished to print. The service interface could have the following look:

public interface JiniPrinter {

public void print( String contents );

}

Although the service could have more methods no client will be able to take advantage of them, because the interface is publishing just the method print.

The service proxy could be:

public class JiniPrinterProxy implements JiniPrinter, java.io.Serializable {

JiniPrinterImpl remote;

public JiniPrinterProxy() {

}

public JiniPrinterProxy( JiniPrinterImpl impl ) {

this.remote = impl;

}

public void print( String contents ) {

remote.print( contents );

}

}

The class JiniPrinterProxy implements java.io.Serializable. This will allow the class to be sent from JVM to JVM.

The class JiniPrinterProxy has a no argument constructor. All classes that implement java.io.Serializable must have an explicit or inherited no argument constructor.

Once the service proxy has been created, the service provider contacts a Jini Lookup Service and publishes (registers) the service proxy, giving a description of it through some standard Jini attributes (called Entry).

In a Local Area Network (LAN from now on), a Jini Lookup Service can be discovered using broadcast protocols. That is, from any point of the LAN, a call to the discovery methods will locate any Jini Lookup Service anywhere in the same LAN. There can be any number of Lookup Services in a LAN. The discovery classes and protocols allow to find all of them or just the first one to respond to the discovery call.

Once the Jini Lookup Service has been located the service proxy can be registered. Registration takes several parameters. The most important of them is the service proxy instance. It is the Java object that implements the service interface. In the example above it would be an instance of the class JiniPrinterProxy. The class that registers the service proxy must obtain an instance of the service proxy. That’s why the service proxy offers another constructor, one that will never be used by the client. The client never instantiates service proxies, it obtains serialized proxies instantiated by some external entity.

A way to contact a Jini Lookup Service and to register the service proxy is to use a loader. A loader is a class that instantiates the service proxy, locates the Jini Lookup Service and registers the former in the latter.

Other needed parameters are the service attributes. These attributes are classes that are implementations of the standard Jini interface Entry. They must be serializable. It means that implementations of Entry must also implement java.io.Serializable. These attributes will be checked when doing lookup.

A client wishing to use a Jini service needn't be aware of where the service is. It needn't use the rmiregistry to locate it. It even needn't use rmi by itself at all. All it has to do is to locate a Jini Lookup Service through the Jini discovery classes. Once it has got a reference to such Lookup Service it can use its methods to lookup for a given service.

What is really happening is that, when a client or service uses the Jini classes to locate the Jini Lookup Service it gets a serialized object: the Jini Lookup Service proxy. This serialized object travels within a MarshalledObject, a Java class that contains the serialization of another object AND an annotation. This annotation tells the client JVM where the code for the serialized object can be found. When the serialized object (retrieved by the Jini run-time classes used by the client or service) is delivered to the client (or service) the JVM class loader will download the .class file for the serialized object, searching for it at the MarshalledObject annotation.

With the serialized data and the .class file (which won't be copied onto disk, but will be sitting at the client JVM's memory) it can use the proxy object received. The client or service knows the proxy object methods, as it is an implementation of an already known interface (a Jini interface). When the client (or service) calls the methods on the service proxy the latter can (if needed) contact the remote service object. So the client can call the Lookup Service proxy lookup method, that will take the parameters and make an RMI call to the real Lookup Service, whose location is already known to the proxy. The client doesn't know where it is, how it is implemented or any additional detail. The proxy knows that for the client.

The method lookup will make the Lookup Service to find an instance of the specified interface (in our example, the JiniPrinter interface). The search can be guided through the use of the Jini attributes (instances of classes that implement the interface Entry). When the Lookup Service finishes the search it will deliver the results to the Lookup Service Proxy. The Lookup Service Proxy will return those results to the client. The client can then choose any of the implementations of the service that it wishes. When it selects one implementation it is getting the JiniPrinterProxy object, embedded in a MarshalledObject with an annotation. When it gets the reference and casts it to a JiniPrinter variable (note the variable is declared to be the type of the interface, we needn't concern with the actual class that really implements the service interface) the JiniPrinteProxy.class file will be found by the client's JVM class loaders. With all that we can use the print method, which, when called, will use the RMI reference the proxy has within to make the real service work