New to Java? We'll help you get started with our revised beginner's tutorial, or our free online textbook.


Get the latest Java books
h t t p : / /w w w . j a v a c o f f e e b r e a k . c o m /

Java Coffee Break

Chapter 6: Client Search

Contents

This chapter looks at what the client has to do once it has found a service locator and wishes to find a service.

6.1. ServiceRegistrar

A client gets a ServiceRegistrar object from the lookup service. It uses this to search for a service stored on that lookup service using the lookup() method:


public Class ServiceRegistrar {
    public java.lang.Object lookup(ServiceTemplate tmpl)
                            throws java.rmi.RemoteException;
    public ServiceMatches lookup(ServiceTemplate tmpl,
                                 int maxMatches)
                            throws java.rmi.RemoteException;
}
The first of these methods just finds a service that matches the request. The second finds a set (upto the maxMatches) requested.

The lookup methods use a class of type ServiceTemplate to specify the service looked for:


package net.jini.core.lookup;

public Class ServiceTemplate {
    public ServiceID serviceID;
    public java.lang.Class[] serviceTypes;
    public Entry[] attributeSetTemplates;

    ServiceTemplate(ServiceID serviceID, 
                    java.lang.Class[] serviceTypes, 
                    Entry[] attrSetTemplates);
}
The serviceID is null if this is the first time a search is being made. If it is a repeat, then the non-null value from previous searches should be used. The attributeSetTemplates is a set of Entry elements to perform matching of attributes, as discussed later in this chapter.

The major parameter to the lookup() method is a list of serviceTypes. We know that services export instances of a class. How does the client ask so that it gets a suitable instance delivered from the lookup locator? The possibilities are

  1. The client could ask for it by using an instance in the search request. But this would be pretty stupid, as the client would then already have what it is searching for!
  2. The client could ask for a Class object, and this object could be the class of the instance it wants to find. This is also not very good. Firstly, the client may be able to do a new operation on this class, so that again it would not need to search for an instance on the network. The other possibility is that it doesn't have all the classes needed to create all the fields and needs to fetch them from somewhere. This has the problems discussed in the next point.
  3. The client could ask for a Class object which is a superclass of the instance on the lookup locator. This is better, as it does not require detailed knowledge of the properties of the instance, or even which subclass it is. However, on the down side, classes have implementation code, and code has version problems. Since the client and the server will probably be on different machines there is a real possibility of a version mismatch.
  4. The client could ask for a Class object which is a interface. Interfaces do not have implementation code that can change. Ideally, an interface should never change at all. A client can ask for an instance of an interface and not care what class it gets or what version of the class. As long as it implements the interface, that is enough. This is the preferred mechanism.
So the client will usually request a interface object.

To be more concrete, a toaster may be defined by an interface


public interface Toaster extends java.io.Serializable {
    public void setDarkness(int dark);
    public void startToasting();
}
A Breville ``Extra Lift'' toaster will implement this in one particular way, as will other toasters

public class BrevilleExtraLiftToaster implements Toaster {
    public void setDarkness(int dark) {
        ...
    }
    public void startToasting() {
        ...
    }
}
When the toaster service starts, it exports an object of class BrevilleExtraLiftToaster to the lookup service. However, the client does not know what type of toaster is out there, so will make a request such as

    System.setSecurityManager(new RMISecurityManager());

    // specify the interface object
    Class[] toasterClasses = new Class[1];
    toasterClasses[0] = Toaster.class;

    // prepare a search template of serviceID, classes and entries
    ServiceTemplate template = new ServiceTemplate(null, 
                                                   toasterClasses, 
                                                   null);

    // now find a toaster
    Toaster toaster = null;
    try {
        toaster = (Toaster) registrar.lookup(template);
    } catch(java.rmi.RemoteException e) {
        System.exit(2);
    }
Notice that the lookup() can throw an exception. This can occur, if say, the service requested is not serialisable.

At the end of this, an object has been transported across to the client that is an instance of a class implementing the Toaster interface, and has been coerced to be of this type. This object has two methods setDarkness() and startToasting(). No other information is available about the toaster capabilities, because the interface does not specify any more and in this case the set of attribute values was null. So the client can call


    toaster.setDarkness(1);
    toaster.startToasting();

Before leaving this section, what is the role of System.setSecurityManager(new RMISecurityManager())? A serialized object has been transported acrosss the network, and is reconstituted and coerced to an object implementing Toaster. We know that here it will in fact be an object of class BrevilleExtraLiftToaster, but the client doesn't need to know that. Or does it? Certainly the client will not have a class definition for this class on its side. But when the toaster object begins to run, then it must run using its BrevilleExtraLiftToaster code! Where does it get it from? From the server, most likely by an HTTP request on the server. This means that it is loading a class definition across the network, and this requires security access. So a security manager capable of granting this access must be installed before the load request is made.

Note the difference between loading a serialized instance and loading a class definition: the first does not require access rights, only the second one does. So if the client had the class definitions of all possible toasters then it would never need to load a class, and a new security manager would not be needed. This is not likely, but may perhaps be needed in a high security environment.

6.2. ServiceMatches

If a client wishes to search for more than one match to a service request from a particular lookup service, then it specifies the maximum number of matches it would like returned by the maxMatches parameter of the second constructor for lookup(). It gets back a ServiceMatches object.


package net.jini.core.lookup;

public Class ServiceMatches {
    public ServiceItem[] items;
    public int totalMatches ;
}
The number of elements in items need not be the same as totalMatches! Suppose there are five matching services stored on the locator. Then totalMatches will be set to five after a lookup. However, if you only specified to search for at most two matches, then items will be set to be an array with only two elements.

In addition, not all elements of this array need be non-null! Note that in lookup(tmpl) when asking for only one match, an exception can be returned such as when the service is not serialisable. No exception is thrown here, because although one match might be bad, the others may still be okay. So a value of null as the array element value is used to signify this


    ServiceMatches matches = registrar.lookup(template, 10);
    // NB: matches.totalMatches may be greater than matches.items.length
    for (int n = 0; n < matches.items.length; n++) {
        Toaster toaster = (Toaster) matches.items[n].service;
	if (toaster != null) {
	    toaster.setDarkness(1);
	    toaster.startToasting();
        }
    }
(which will start upto 10 toasters cooking at once!)

6.3. Matching

A client is attempting to find one or more services that satisfy its requirements. It does so by creating a ServiceTemplate object and using this in a registrar's lookup() call. A ServiceTemplate object has three fields


ServiceID         serviceID;
java.lang.Class[] serviceTypes;
Entry[]           attributeSetTemplates;
An overview: firstly, if the client is repeating a request, then it may have recorded the serviceID from an earlier request. The serviceID is a ``universally unique identifier'' so can be used to identify a service unambiguously. This can be used by the service locator as a filter to quickly discard other services. Secondly, a client may want to find a service satisfying a number of interface requirements at once. For example, a client may look for a service that implements both Toaster and FireAlarm (so that it can properly handle burnt toast). Finally, the client will specify a set of attributes that must be satisfied by each service. Each attribute required by the client is taken in turn and matched against the set offered by the service. For example, in addition to requesting a Toaster with a FireAlarm, a client entry may specify a location in ``GP South Building''.This will be tried against all the variations of location offered by the service. A single match is good enough. An additional client requirement of, say, manufacturer would also have to be matched by the service.

More formally from the ServiceTemplate API documentation:

  1. A service item (item) matches a service template (tmpl) if: item.serviceID equals tmpl.serviceID (or if tmpl.serviceID is null); and item.service is an instance of every type in tmpl.serviceTypes; and item.attributeSets contains at least one matching entry for each entry template in tmpl.attributeSetTemplates.
  2. 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.

6.4. Summary

A client prepares a ServiceTemplate which is a list of class objects and a list of entries. For each service locator that is found, the client can query this using the ServiceRegistrar object's lookup() method, to see if the locator has a service matching the template. If the match is successful, an object is returned that can be cast into the class required. Service methods can then be invoked on this object.


This file is Copyright (©) 1999, 2000 by Jan Newmarch (http://pandonia.canberra.edu.au) jan@ise.canberra.edu.au.
This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v0.4 or later (the latest version is presently available at http://www.opencontent.org/openpub/). Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.

Back to main


Copyright 1998, 1999, 2000 David Reilly

Privacy | Legal | Linking | Advertise!

Last updated: Monday, June 05, 2006