Chapter 24: Introspection

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.

24.1. Basic Service Lister

The client of the chapter "Simple Example" looked for a particular class by specifying the class in the ServiceItem. How do we find all services? Well, any class inherits from Object, so one way is to just look for all services that implement Object (this is one of the few cases in which we might specify a class instead of an interface). We can adapt the client quite simply by looking for all services, and then doing something simple like printing its class


	
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

      

24.2. Unknown services

A common question is "how do I deal with services that I know nothing about?" There are two answers:

  1. Are you sure you want to? If you don't know about the service beforehand, then what are you going to sensibly infer about its behaviour just from discovering the interface? In most cases, if you don't already know the service interface and have some idea of what it is supposed to do, then getting this service isn't going to be of much use
  2. On the other hand, service browsers may no have prior knowledge of the services they discover but may still wish to use information about these services. For example, a service browser could present this information to a user and ask if the user wants to invoke the service. Or a client using AI techniques may be able to guess at behaviour from the interface, and invoke the service based on this
This (short) chapter is concerned with the second case.

24.3. Introspection

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());
        }
    }
}

24.4. Unknown classes

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:

  1. Already known, and in their class path
  2. Accessed from a Web server, by the java.rmi.server.codebase property of the service
If you are a service provider, you may wish to make your service available to clients who have never heard of your service before. In this case, you cannot rely on the client knowing anything except for the core Java classes. This may be in doubt if the client is using one of the "limited device" Java memory models - this is not a problem yet, since these models do not yet support Jini. You can make a pretty solid bet that the core Jini classes will have to be there too, but non-essential classes in the package 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:

  1. FileClassifier
  2. RemoteFileClassifier
  3. MIMEType
  4. FileClassifierImpl
The assumption in earlier chapters is that 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."

24.5. Copyright

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 file is Copyright (©) 1999, 2000, 2001, 2003, 2004 by Jan Newmarch (http://jan.netcomp.edu.au) jan@newmarch.name.

Creative Commons License This work is licensed under a Creative Commons License, the replacement for the earlier Open Content License.