Overview

Jini is a distributed computing technology developed by Sun Microsystems. It is now being continued under the auspices of the Apache River project.

Jini enables highly scalable and resilient distributed applications to run on an infrastructure consisting of unreliable hardware, networks, and operating systems; in other words, real world environments. The underlying protocols that enable this type of autonomic computing can, however, be a bit . . . fiddly, to use a highly technical term.

This page describes a set of utility classes that encapsulate the complexity of service lookup in Jini.

Jini Services and Proxies

Jini systems consist of many interacting services distributed across a network. These services make themselves available by registering with a lookup service (which is also a Jini service).

When a Jini service starts, it finds any available lookup services and registers a proxy with each of them. When a client starts, it finds the available lookup services in exactly the same way, then requests a proxy for each service it needs to use. Service lookup is done by interface — the client doesn't need to know which particular implementation sits behind the proxy, or even the implementation of the proxy itself.

There are three types of proxies in a typical Jini system. Standalone proxies contain all of the behaviors exposed by the service. Once a client has retrieved a copy of a standalone proxy from the lookup service, it simply uses it directly.

Thin or remote proxies pass all method invocations to a remote implementation. The communication protocol between the proxy and the remote service is not constrained by Jini. Often RMI is used, but Jini's protocol independence has resulted in systems using raw sockets, message-oriented middleware, and even CICS.

Finally, hybrid proxies combine aspects of standalone and thin proxies, implementing some methods locally and calling a remote service for others.

If a particular proxy becomes unusable, due to the remote service no longer being available, for example, the client simply requests another proxy for the same service interface from the lookup service.

Jini Service Discovery

Jini provides a very flexible set of options for discovering lookup services, partitioning services, and detecting service startup and shutdown. That flexibility means that, while service discovery in Jini is straightforward, it does require detailed configuration.

Multicast and Unicast

The lookup service can be found by unicast to a particular host machine and port number or by broadcast on a multicast address and port. Broadcast discovery is more flexible because it completely decouples clients from any knowledge of the location of the lookup service, but may not be available in some environments.

Lookup services, like any other Jini service, may start and stop at any time. When multicast lookup is used, these events are easily monitored. When unicast lookup is required, Jini supports the specification of multiple host and port pairs to provide resiliency.

Groups

Services, including the lookup service, may be members of one or more groups. Groups provide a mechanism for partitioning sets of services. A common use for groups is to allow development, quality assurance, pre-production, and production services to run on the same network simultaneously.

The Service Discovery Protocol

In order to discover and use Jini services, a client must:

  1. Register to receive notifications of available lookup services
  2. Notify existing lookup services that it is interested in the services they have registered
  3. Filter the available services to those of interest
  4. Manage lookup service start and stop events
  5. Manage service start and stop events

The ServiceCache Class

ServiceClass.java is an implementation of a class that encapsulates the details of finding available lookup service and maintaining a cache of lookup services and the services registered with them.

ServiceCache implements two interfaces, DiscoveryListener and ServiceDiscoveryListener. DiscoveryListener defines two methods, discovered and discarded, that are called when a lookup service starts up or shuts down, respectively. ServiceDiscoveryListener exposes serviceAdded, serviceChanged, and serviceRemoved, which are called when a service of interest is added to, changed in, or removed from a known lookup service.

Initializing The ServiceCache

The ServiceCache constructor initializes all of the Jini components required to ensure that the DiscoveryListener and ServiceDiscoveryListener methods are invoked when appropriate. The core of the constructor consists of three method calls:

  lookupDiscoveryManager_
    = new LookupDiscoveryManager(groups,locators,this);
  serviceDiscoveryManager_
    = new ServiceDiscoveryManager(lookupDiscoveryManager_,
                                  leaseRenewalManager_);
  lookupCache_
    = serviceDiscoveryManager_.createLookupCache(template,filter,this);

LookupDiscoveryManager is a utility class that registers a DiscoveryManager, the current ServiceCache object in this case, to receive events when lookup services start and shut down. It accepts both an array of Jini groups, for multicast lookup, and an array of locators, for unicast lookup.

The ServiceDiscoveryManager is a utility class similar to LookupDiscoveryManager but for services registered with lookup services rather than lookup services themselves. It uses the LookupDiscoveryManager created previously to find lookup services. The second parameter to the ServiceDiscoveryManager constructor is a LeaseRenewalManager. All Jini resources are leased to prevent resource accretion. The LeaseRenewalManager maintains the leases granted to the ServiceDiscoveryManager by the lookup services. If the ServiceDiscoveryManager terminates for any reason, the leases will not be renewed and the lookup services will stop sending events to it.

The LookupCache is simply a cache of services maintained by the ServiceDiscoveryManager. This cache is queried when service proxies are requested from the ServiceCache.

The ServiceCache Constructor

The signature for the full ServiceCache constructor is:

  public ServiceCache(String[] groups,
                      LookupLocator[] locators,
                      ServiceTemplate template,
                      ServiceItemFilter filter)
This allows the specification of multicast groups, unicast lookup locators, service templates, and filters. Most Jini clients won't require this many options. Accordingly, ServiceCache provides some utility constructors:
  public ServiceCache(ServiceTemplate template)
  public ServiceCache(String[] groups,Class serviceClass)
  public ServiceCache(Class serviceClass)
  public ServiceCache()
The default constructor uses the public multicast group and listens for all Jini services. The most frequently used constructor is the one that takes an array of multicast groups and the interface class of interest.

The ServiceCache Interface

ServiceCache currently exposes only three methods for accessing service proxies:

  public Object service(ServiceItemFilter filter)
  public Object service(final Class serviceClass)
  public Object service()
The first of these returns a proxy from the LookupCache matching the specified filter, if any. The second is a convenience method that creates a filter from the specified class and attempts to find a matching proxy. The third returns any service from the cache. This is only useful if the ServiceCache has been configured to cache only proxies for services of one particular type.

JavaSpaceCache and TransactionManagerCache

The ServiceCache class encapsulates much of the complexity of Jini service discovery, but even simpler utilities can be built on top of it. The most common requirement for a client in a Jini system is to find a service of a particular type registered with a lookup service for a particular group.

In a language with real support for generic programming, it would be possible to create a class called something like ServiceFinder, parameterized on the service of interest, to provide exactly this capability. Because the Java designers chose to emasculate the Java generics implementation with type erasure, thereby significantly increasing the complexity of the language with no correspondingly signficant benefits, this is not possible.

(Yes, I know the arguments in favor of type erasure. They're wrong. Generics should have been done right or not done at all.)

What I have done instead is implement convenience classes for two of the most often used Jini services, JavaSpaces and transaction managers. The convenience classes are named JavaSpaceCache and TransactionManagerCache respectively. The design of both is nearly identical — instances of the class are constructed with one or more lookup groups specified, the public group being the default, and when the service proxy is requested the method blocks until an instance is found.

Example

The main methods of both convenience classes demonstrate how to use them. Taking JavaSpaceCache as an example:

  JavaSpaceCache cache = new JavaSpaceCache();
  JavaSpace space = cache.javaSpace();
Finding a remote service isn't going to get much simpler than that.

Source Code

The full source is available as separate files:

or as a tarball.

In order to build and run the example, you will need the Jini 2.1 Starter Kit and J2SE 5.0 installed.

Please contact me by email if you have any questions, comments, or suggestions.

www.softwarematters.org