Questions often asked are: how do you find all services, and how do you deal with a service if you don't know what it is? The first is answered by searching for
Object . Introspection is the answer to the second, but if you require your services to be introspected then you have to pay extra attention to the deployment environment.
The client of the chapter "Simple Example" looked for a particular
class by specifying the class in the
package client;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.lookup.ServiceMatches;
import net.jini.core.lookup.ServiceItem;
/**
* BasicServiceLister
*/
public class BasicServiceLister implements DiscoveryListener {
/** Entry point to the program
*
* @param argv Command line arguments
*/
public static void main(String argv[]) {
new BasicServiceLister();
// stay around long enough to receive replies
try {
Thread.currentThread().sleep(1000000L);
} catch(java.lang.InterruptedException e) {
// do nothing
}
}
/** Constructor
*/
public BasicServiceLister() {
System.setSecurityManager(new RMISecurityManager());
LookupDiscovery discover = null;
try {
discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
} catch(Exception e) {
System.err.println(e.toString());
System.exit(1);
}
discover.addDiscoveryListener(this);
}
/** One or more registrars has been discovered
*
* @param evt Discovery event
*/
public void discovered(DiscoveryEvent evt) {
ServiceRegistrar[] registrars = evt.getRegistrars();
Class [] classes = new Class[] {Object.class};
ServiceTemplate template = new ServiceTemplate(null, classes,
null);
for (int n = 0; n < registrars.length; n++) {
ServiceRegistrar registrar = registrars[n];
System.out.print("Lookup service found at ");
try {
System.out.println(registrar.getLocator().getHost());
} catch(RemoteException e) {
continue;
}
ServiceMatches matches = null;
try {
matches = registrar.lookup(template, Integer.MAX_VALUE);
} catch(RemoteException e) {
System.err.println("Can't decribe service: " + e.toString());
continue;
}
ServiceItem[] items = matches.items;
for (int m = 0; m < items.length; m++) {
Object service = items[m].service;
if (service != null) {
printObject(service);
} else {
System.out.println("Got null service");
}
}
}
}
/** @param evt
*/
public void discarded(DiscoveryEvent evt) {
// empty
}
/** Print the object's class information within its hierarchy
*
* @param obj the object to be printed
*/
private void printObject(Object obj) {
System.out.println("Discovered service belongs to class \n" +
obj.getClass().getName());
printInterfaces(obj.getClass());
/*
Class[] interfaces = obj.getClass().getInterfaces();
if (interfaces.length != 0) {
System.out.println(" Implements interfaces");
for (int n = 0; n < interfaces.length; n++) {
System.out.println(" " + interfaces[n].getName());
}
}
*/
printSuperClasses(obj.getClass());
}
/** Print information about superclasses
*
* @param cls The class to be displayed
*/
private void printSuperClasses(Class cls) {
System.out.println(" With superclasses");
while ((cls = cls.getSuperclass()) != null) {
System.out.println(" " + cls.getName());
printInterfaces(cls);
}
}
private void printInterfaces(Class cls) {
Class[] interfaces = cls.getInterfaces();
if (interfaces.length != 0) {
System.out.println(" which implements interfaces");
for (int n = 0; n < interfaces.length; n++) {
System.out.println(" " + interfaces[n]);
printInterfaces(interfaces[n]);
}
}
}
} // BasicServiceLister
A common question is "how do I deal with services that I know nothing about?" There are two answers:
Java has a well-developed introspection library. This allows a Java program to take a class and find all of the methods (including contructors) and to find the parameters and return types of these methods. For non-interface classes, the fields can also be found. The classes that a class implements or extends can also be determined. The access methods (private, public, protected) and the thrown exceptions can be found as well. In other words, all of the important information (except JavaDoc comments) can be retrieved from the object's class.
The starting point for these are various Class
methods
which include
Constructor[] getConstructors();
Class[] getClasses();
Field[] getFields();
Class[] getInterfaces();
Method[] getMethods();
int getModifiers();
Package getPackage();
Methods in the classes Field
, Method
, etc,
allow you to gain extra details.
For example, to find information about interfaces of services on a lookup service,
ServiceRegistrar registrar = ...
ServiceTemplate templ = new ServiceTemplate(null, null, null);
ServiceMatches matches = registrar.lookup(templ, Integer.MAX_VALUE);
ServiceItem[] items = matches.items;
for (int n = 0; n < items.length; n++) {
Object service = items[n].service;
if (service != null) {
Class cls = service.getClass();
System.out.println("Class is " + cls.getName());
Class[] ifaces = cls.getInterfaces();
for (int m = 0; m < ifaces.length; m++) {
System.out.println(" implements " + ifaces[m].getName());
}
}
}
In earlier chapters we have assumed that a client will know at least
the interfaces of the services it is attempting to use. For a browser
or a "smart" client this may not be the case: the client will often
come across services that it does not know much about. When a client
discovers a service, it must be able to reconstitute it into an object
that it can deal with, and for this it needs to be able to find the
class files. If any one of the needed class files are missing, then
the service comes back as null
. That is why there is a
check for null
service in the last example code: a
service has been found, but cannot be rebuilt into an object due
to missing class files.
Clients get the class files from two sources:
java.rmi.server.codebase
property of the service
jini-ext.jar
may not be present.
The example that we have been using so far is an implementation
of FileClassifier
. A typical implementation uses these
non-core classes/interfaces:
FileClassifier
RemoteFileClassifier
MIMEType
FileClassifierImpl
FileClassifier
and MIMEType
are "well known" and the others need to be
accessible from a Web server. For robust introspection, this assumption
must be dropped: FileClassifier
and MIMEType
must also be available from the service's Web
server. This a server responsibility, not
a client responsibility; the client can do nothing
if the service does not make its class files available.
To summarise: if a service wishes to be discovered by clients that have no prior knowledge of the service, then it must make all of its interface and specification classes publically available from a Web server. Any other classes that are non-standard or potentially missing from the client should be on this public Web server too. There is a restriction: you cannot make some classes available to non-signatories to various copyright agreements, meaning that licensing restrictions may not allow you to make some classes publically available. For example, you cannot make any of the Jini files publically available "just in case the client doesn't have them."
If you found this chapter of value, the full book is available from APress or Amazon . There is a review of the book at Java Zone . The current edition of the book does not yet deal with Jini 2.0, but the next edition will.
This work is licensed under a
Creative Commons License, the replacement for the earlier Open Content License.