Locate services with the Jini lookup service

Discover the power and limitations of the ServiceRegistrar interface

The Jini lookup service, the central component of Jini's runtime infrastructure, offers Jini clients a flexible and powerful way to find Jini services. It enables service providers to advertise their services and enables clients to locate and enlist the help of those services.

To interact with the lookup service, the client must first obtain a service registrar object via discovery, a network-level protocol used by Jini's runtime infrastructure. Discovery enables clients and services to locate lookup services. (For more information on discovery, see Resources.) The service registrar object, which implements the net.jini.core.lookup.ServiceRegistrar interface, enables the client to interact with the lookup service. To find desired services, clients build a ServiceTemplate, an instance of class net.jini.core.lookup.ServiceTemplate, and pass it to one of two lookup() methods declared in the ServiceRegistrar interface. Each lookup() method sends the service template to the lookup service, which performs the query and returns matching service objects to the client.

In general, a client looks up a service by Java type, usually an interface. For example, if a client needs to use a printer, it composes a service template that includes a Class object for a well-known interface to printer services. All printer services implement the interface. The lookup service returns a service object (or objects) that implement this interface. You can include attributes in the service template to narrow the number of matches for such a type-based search. The client uses the printer service by invoking on the service object the methods declared in the well-known interface.

The ServiceTemplate class

With the ServiceTemplate class, you can express the search criteria for Jini lookups. The class consists solely of these three public fields:

public Entry[] attributeSetTemplates;
public ServiceID serviceID;
public Class[] serviceTypes;

ServiceTemplate has no methods, and its instances merely serve as "struct"-like containers for lookup service queries. Matches are performed as described by the following excerpt from ServiceTemplate's javadoc page:

Items in the lookup service are matched using an instance of [ServiceTemplate]. A service item (item) matches a service template (tmpl) if:

  • item.serviceID equals tmpl.serviceID (or if tmpl.serviceID is null)
  • item.service [the service object] is an instance of every type in tmpl.serviceTypes
  • item.attributeSets contains at least one matching entry for each entry template in tmpl.attributeSetTemplates

An entry matches an entry template if the class of the template is the same as, or a superclass of, the class of the entry, and every non-null field in the template equals the corresponding field of the entry. Every entry can be used to match more than one template. Note that in a service template, for serviceTypes and attributeSetTemplates, a null field is equivalent to an empty array; both represent a wildcard.

As described here, the service template can include a reference to an array of Class objects. These objects indicate to the lookup service the Java type (or types) of the service object the client desires. The service template can also include a service ID, which uniquely identifies a service, and attributes, which must exactly match the attributes uploaded by the service provider in the service item. The service template can also contain wild cards for any of those fields. A wild card in the service ID field, for example, will match any service ID.

The lookup() methods

The ServiceRegistrar's lookup() methods take two overloaded forms. The two forms differ mainly in the number of matches and service items each one returns. The two-parameter form can return multiple matches of the query expressed in the ServiceTemplate, while the one-parameter form returns only one match. In addition, the two-parameter form returns entire service items; the one-parameter form returns only the service object.

The two-parameter form of lookup()

Here's a javadoc excerpt that explains the two-parameter form of lookup():

public ServiceMatches lookup(ServiceTemplate tmpl,
    int maxMatches) throws java.rmi.RemoteException;

[It] returns, at most, maxMatches items matching the template, plus the total number of items that match the template. The return value is never null, and the returned items array is only null if maxMatches is zero. For each returned item, if the service object cannot be deserialized, the service field of the item is set to null and no exception is thrown. Similarly, if an attribute set cannot be deserialized, that element of the attributeSets array is set to null and no exception is thrown.

Here is the ServiceMatches class:

package net.jini.core.lookup;

public class ServiceMatches extends java.lang.Object implements java.io.Serializable {

public ServiceItem[] items; public int totalMatches; }

And here is the ServiceItem class:

package net.jini.core.lookup;

public class ServiceMatches extends java.lang.Object implements java.io.Serializable {

public Entry[] attributeSets; public java.lang.Object service; public ServiceID serviceID; }

As mentioned previously, each element of the items array returned by the two-parameter form is a complete service item, which includes the service object, service ID, and all the attribute sets. The maxMatches field helps clients manage the number of objects returned by this lookup().

The length of the items array in the returned ServiceMatches object is less than or equal to the value passed to lookup() in maxMatches. The total number of matching service items (returned in totalMatches) is greater than or equal to the length of the items array.

For example, if maxMatches is 50 and the service template matches 25 items, the length of the returned items array and the value of totalMatches are both 25. Alternatively, if maxMatches is 50 but the service template matches 100 items, the length of the returned items array is 50 and the value of totalMatches is 100. When a service template matches more than maxMatches service items, the service items returned by the two-parameter lookup() are selected randomly from the full set of matching service items.

The one-parameter form of lookup()

The one-parameter lookup() method returns one matching service object chosen randomly from all the matches. Here's a javadoc excerpt explaining this form:

public Object lookup(ServiceTemplate tmpl)
    throws java.rmi.RemoteException;
Returns the service object (i.e., just ServiceItem.service) from an item matching the template, or null if there is no match. If multiple items match the template, it is arbitrary as to which service object is returned. If the returned object cannot be deserialized, an UnmarshalException is thrown with the standard RMI semantics.

Because the one-parameter lookup() returns only one matching service object, clients can minimize the number of downloaded object state and class files. But because the returned service object is selected arbitrarily, and not identified by a service ID or described by associated attribute sets, the client must be confident that any matching service object will suffice.

The browsing methods

In addition to the two lookup() methods, the ServiceRegistrar has three browsing methods, which yield information about registered service items. The three methods -- getServiceTypes(), getEntryClasses(), and getFieldValues() -- are called browsing methods because they enable clients to browse the services and attributes in the lookup service.

The getServiceTypes() method takes a ServiceTemplate (the same ServiceTemplate that is passed to the lookup() methods) and a String prefix. It returns an array of Class instances representing the most specific types (classes or interfaces) of the service objects that match the template. These service objects are neither equal to, nor a superclass of, any of the types specified in the template, and they have names that start with the specified prefix. The service object or objects for which Class instances are returned are all instances of all the types (if any) passed in the template, but the Class instances are all more specific than (and are subclasses or subinterfaces of) those types. Each class appears only once in the returned array, and in arbitrary order.

Here's what getServiceTypes() looks like:

public java.lang.Class[] getServiceTypes(ServiceTemplate tmpl,
    java.lang.String prefix) throws java.rmi.RemoteException;

The getEntryTypes() method takes a ServiceTemplate and returns an array of Class instances that represent the most specific classes of entries for the service items that match the template, which either don't match any entry template or are a subclass of one. Each class appears only once in the returned array, again in arbitrary order.

Here's what getEntryClasses() looks like:

public java.lang.Class[] getEntryClasses(ServiceTemplate tmpl)
    throws java.rmi.RemoteException;

The getFieldValues() method takes a ServiceTemplate, an integer index, and a String field name. It returns an array of Objects for the named field of all instances of the entry that appears in the ServiceTemplate's Entry[] array at any matching service item's passed index. Each object of a particular class and value appears only once in the returned array, and in arbitrary order.

Here's what getFieldValues() looks like:

public java.lang.Object[] getFieldValues(ServiceTemplate tmpl,
    int setIndex, java.lang.String field)
    throws java.lang.NoSuchFieldException, java.rmi.RemoteException;

The behavior and purpose of these browsing methods can be obscure. You might think of them as tools that incrementally narrow queries of the lookup service.

For example, a client such as a graphical lookup service browser could first invoke getServiceTypes() with an empty template. The getServiceTemplate() method returns all possible service types registered in the lookup service, which the browser could display. The user could select one or more types, then push the Requery button. The browser would add that type (or types) to the service template and invoke getServiceTypes() again. A smaller list of types would be returned and displayed by the browser. The user could select one and press an Entries button. The browser would form a template with the most recently selected service type or types, and then invoke getEntryTypes(). The getEntryTypes() method would return an array of entry classes, which the browser could then display.

The user could select some entries -- and a field of a selected entry -- and push a Fields button. The browser would build a template using the currently selected service and entry types. It would then pass the index of the entry class in which the user selected the field, and the name of the selected field, to getFieldValues(). The browser would display all the values that getFieldValues() returned. With those values the user could further narrow the search for a service, eventually choosing a particular service. Thus, these methods help clients, whether or not a human user is involved, to browse the services registered in a lookup service. The arrays returned from the browsing methods can help the client further refine its queries, ultimately resulting in a ServiceTemplate that, when passed to lookup(), returns the most appropriate service object.

The notify() method

In addition to the lookup and browsing methods, the ServiceRegistrar interface also has a notify() method that notifies clients when new services register or unregister with a lookup service:

public EventRegistration notify(ServiceTemplate tmpl,
    int transitions, RemoteEventListener listener,
    MarshalledObject handback, long leaseDuration)
    throws RemoteException;

You invoke notify() to register yourself (or another listener) to receive a distributed event whenever the services that match the passed ServiceTemplate undergo a state change described by the transitions parameter.

The transitions parameter is a bitwise OR of any nonempty set of these three values, defined as constants in ServiceRegistrar:

TRANSITION_MATCH_MATCH
TRANSITION_MATCH_NOMATCH
TRANSITION_NOMATCH_MATCH

You build the ServiceTemplate for notify() in the same way you build it for lookup(). You can indicate explicit types, a service ID, attributes (which must exactly match), or wild cards (which match anything) in any of those fields. The transitions are based on a change (or nonchange) in the status of whatever matches your ServiceTemplate before and after any operation is performed on the lookup service.

For example, TRANSITION_MATCH_MATCH indicates that at least one service item matched your template before and after an operation. TRANSITION_MATCH_NOMATCH indicates that, although at least one particular service item matched your template before an operation, it no longer matched your template after the operation. To receive notification when any new services are added to a lookup service, you simply specify a template that matches any service and pass TRANSITION_NOMATCH_MATCH as the transition to the notify() method.

SUBHEAD_BREAK: Lookup service versus name servers

1 2 3 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more