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 /
|
Contents
This chapter looks at what the client has to do once it has found a service locator and wishes to find a service.
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
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.
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.
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.
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.
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!)
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:
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
.
serviceTypes
and attributeSetTemplates
,
a null
field is equivalent to an empty array; both represent a wildcard.
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.
Copyright 1998, 1999, 2000 David Reilly
|
Privacy | Legal | Linking | Advertise! |
Last updated:
Monday, June 05, 2006
|