previous chapter table of contents next chapter

RMI Proxy for FileClassifier

An RMI proxy can be used when all of the work done by the service is done on the server side. In that case, the server exports a thin proxy that simply channels method calls from the client across the network to the "real" service in the server, and returns the result back to the client. The programming for this is relatively simple. The service has to do two major things in its class structure:

  1. Implement Remote . This is because methods will be called on the service from the proxy, and these will be remote calls on the service.
  2. Inherit from UnicastRemoteObject (or Activatable ). This means that it's the backend service's constructor that will create and export a proxy or stub object without the programmer having to do anything more. (An alternative to inheritance is for the object to call the UnicastRemoteObject.exportObject() method.)

What Doesn't Change

In Chapter 8, we discussed a file-classifier application built from a client and a service, and in this chapter we have shown a different implementation of the service. A new file-classifier application can be built using this new implementation of the service. Clearly, some things must change in this new version, but because of the Jini architecture, the changes are basically localized to the service implementation. That is, most of the file-classifier application doesn't change at all, even if the service implementation changes.

The client is not concerned about the implementation of the service at all, and so the client doesn't change. The FileClassifier interface doesn't change either, since this is fixed and used by any client and any service implementation. We have already declared its methods to throw RemoteException , so a proxy is able to call its methods remotely. The MIMEType doesn't change either, since we have already declared it to implement Serializable- it is passed back across the network from the service to its proxy.


An implementation of the service using an RMI proxy will need to implement both the FileClassifier and the Remote interfaces. It is convenient to define another interface, called RemoteFileClassifier , just to do this. This interface will be used fairly frequently in the rest of this book.

 package rmi; import common.FileClassifier; import java.rmi.Remote; /**  * RemoteFileClassifier.java  */ public interface RemoteFileClassifier extends FileClassifier, Remote { } // RemoteFileClasssifier 


The service provider will run the backend service. When the backend service exports an RMI proxy, it will look like this:

 package rmi; import java.rmi.server.UnicastRemoteObject; import common.MIMEType; import common.FileClassifier; /**  * FileClassifierImpl.java  */ public class FileClassifierImpl extends UnicastRemoteObject                                 implements RemoteFileClassifier {     public MIMEType getMIMEType(String fileName)         throws java.rmi.RemoteException {         System.out.println("Called with " + fileName);         if (fileName.endsWith(".gif")) {             return new MIMEType("image", "gif");         } else if (fileName.endsWith(".jpeg")) {             return new MIMEType("image", "jpeg");         } else if (fileName.endsWith(".mpg")) {             return new MIMEType("video", "mpeg");         } else if (fileName.endsWith(".txt")) {             return new MIMEType("text", "plain");         } else if (fileName.endsWith(".html")) {             return new MIMEType("text", "html");         } else             // fill in lots of other types,             // but eventually give up and             return new MIMEType(null, null);     }     public FileClassifierImpl()  throws java.rmi.RemoteException {         // empty constructor required by RMI     }   } // FileClassifierImpl 


The service provider changes very little from the version in Chapter 8, which exported a complete service. Both this server and the earlier one export a service object with register() , but at this point the RMI runtime intervenes and substitutes an RMI stub object. The other major change is that the server no longer needs to explicitly stay alive. While the RMI system keeps a reference to the RMI stub object, it keeps alive the JVM that contains the stub object. This means that the daemon threads that are looking after the discovery process will continue to run, and in turn , since they have a reference to the service provider as listener, the service provider will continue to exist.

The following server creates and manages the RMI service:

 package rmi; import rmi.FileClassifierImpl; import rmi.RemoteFileClassifier; 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.ServiceItem; import net.jini.core.lookup.ServiceRegistration; import net.jini.core.lease.Lease; // import com.sun.jini.lease.LeaseRenewalManager; // import com.sun.jini.lease.LeaseListener; // import com.sun.jini.lease.LeaseRenewalEvent; import net.jini.lease.LeaseRenewalManager; import net.jini.lease.LeaseListener; import net.jini.lease.LeaseRenewalEvent; import java.rmi.RMISecurityManager; /**  * FileClassifierServer.java  */ public class FileClassifierServerRMI implements DiscoveryListener, LeaseListener {     protected FileClassifierImpl impl;     protected LeaseRenewalManager leaseManager = new LeaseRenewalManager();     public static void main(String argv[]) {         new FileClassifierServerRMI();         //no need to keep server alive, RMI will do that     }     public FileClassifierServerRMI() {         try {             impl = new FileClassifierImpl();         } catch(Exception e) {             System.err.println("New impl: " + e.toString());             System.exit(1);         }         // install suitable security manager         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);     }       public void discovered(DiscoveryEvent evt) {         ServiceRegistrar[] registrars = evt.getRegistrars();         RemoteFileClassifier service;         for (int n = 0; n < registrars.length; n++) {             ServiceRegistrar registrar = registrars[n];             // export the proxy service             ServiceItem item = new ServiceItem(null,                                                impl,                                                null);             ServiceRegistration reg = null;             try {                 reg = registrar.register(item, Lease.FOREVER);             } catch(java.rmi.RemoteException e) {                 System.err.print("Register exception: ");                 e.printStackTrace();                 // System.exit(2);                 continue;             }             try {                 System.out.println("service registered at " +                                    registrar.getLocator().getHost());             } catch(Exception e) {             }             leaseManager.renewUntil(reg.getLease(), Lease.FOREVER, this);         }     }     public void discarded(DiscoveryEvent evt) {     }     public void notify(LeaseRenewalEvent evt) {         System.out.println("Lease expired " + evt.toString());     } } // FileClassifierServerRMI 

What Classes Need to Be Where?

This chapter deals with a number of different implementations of the file-classifier service. Each implementation introduces some new classes, but also depends on some of the classes we have developed in earlier chapters. In deploying the service, we need to pay attention to this set of classes and determine which classes need to be known to the different parts of the Jini system. This "What Classes Need to Be Where?" section is repeated for each of the different service implementations, and it describes the configuration issues for each of these different implementation choices.

For the RMI proxy implementation, we need to consider these classes:

  • common.MIMEType
  • common.FileClassifier
  • rmi.RemoteFileClassifier
  • rmi.FileClassifierImpl
  • rmi.FileClassifierImpl_Stub
  • rmi.FileClassifierServer
  • client.TestFileClassifier

(The FileClassifierImpl_Stub class is added to our classes by rmic as discussed in the next section.)

These classes could be running on up to four different machines:

  • The server machine for FileClassifierServer
  • The HTTP server, which may be on a different machine
  • The machine for the lookup service
  • The machine running the TestFileClassifier client

So, which classes need to be known to which machines?

The server running FileClassifierServer needs to know the following classes and interfaces:

  • The common.FileClassifier interface
  • The rmi.RemoteFileClassifier interface
  • The common.MIMEType class
  • The rmi.FileClassifierServer class
  • The rmi.FileClassifierImpl class

The lookup service does not need to know any of these classes. It just deals with them in the form of a java.rmi.MarshalledObject .

The client needs to know the following:

  • The common.FileClassifier interface
  • The common.MIMEType class

In addition, the HTTP server needs to be able to load and store classes. It needs to be able to access the following:

  • The rmi.FileClassifierImpl_Stub interface
  • The rmi.RemoteFileClassifier interface
  • The common.FileClassifier interface
  • The common.MIMEType class

The reason for all of these is slightly complex. In the FileClassifierProxy constructor, the FileClassifierImpl class is passed in. The RMI runtime converts this to FileClassifierImpl_Stub . This class implements the same interfaces as FileClassifierImpl , that is, RemoteFileClassifier and hence FileClassifier , so these also need to be available. In the implementation, FileClassifierImpl references the MIMEType class, so this must also be available.

So, what does the phrase "available" mean in the last sentence ? The HTTP server will look for files based on the java.rmi.server. codebase property of the application server. The value of this property is a URL. Often, URLs can be file references such as file://home/jan/index.html or HTTP references such as http://host/index.html . But for this case, clients running anywhere will use the URL, so it cannot be a file reference specific to a particular machine. For the same reason, it cannot be just localhost , unless you are running every part of a Jini federation on a single computer!

If java.rmi.server.codebase is an HTTP reference, then the preceding class files must be accessible from that reference. For example, suppose the property is set to

 java.rmi.server.codebase=http://   myWebHost   /classes 

(where myWebHost is the name of the HTTP server's host) and this Web server has its DocumentRoot set to /home/webdocs . In that case, these files must exist:

 /home/webdocs/classes/rmi/FileClassifierImpl_Stub.class /home/webdocs/classes/rmi/RemoteFileClassifier.class /home/webdocs/classes/common/FileClassifier.class /home/webdocs/classes/common/MIMEType.class 

Running the RMI Proxy FileClassifier

As with the file classifier developed in Chapter 8:

 java -Djava.security.policy=policy.all client.TestFileClassifier 

The server in this situation is more complex than the one in Chapter 8, because the RMI runtime is manipulating RMI stubs, and these have additional requirements. Firstly, RMI stubs must be generated during compilation. Secondly, security rights must be set, because an RMISecurityManager is used.

Although the FileClassifierImpl is created explicitly by the server, it is not this class file that is moved around. The FileClassifierImpl object continues to exist on the server machine. Rather, a stub object is moved around and will run on the client machine. This stub is responsible for sending the method requests back to the implementation class on the server. The client machine must be able to access the class file for the stub. This class file has to be generated from the implementation class by the stub compiler rmic with the following command:

 rmic -v1.2 -d /home/webdocs/classes rmi.FileClassifierImpl 

Here, the -v1.2 option says to generate JDK 1.2 stubs only, and the -d option says where to place the resultant stub class files so that they can be located by the HTTP server (in this case, in the local file system). If the -v1.2 option is omitted, rmic will also generate Java 1.1 skeleton files, which are not needed. In Java 1.3, it may not be necessary to even run rmic . Note that the pathnames for directories here and later do not include the package name of the class files. The class files (here FileClassifierImpl_Stub.class ) will be placed in and looked for in the appropriate subdirectories.

The value of java.rmi.server.codebase must specify the protocol used by the HTTP server to find the class files. This could be the file protocol or the http protocol. For example, if the class files are stored on my Web server's pages under classes/rmi/FileClassifierImpl_Stub.class . the codebase would be specified as

 java.rmi.server.codebase=http://   myWebHost   /classes/ 

(where myWebHost is the name of the HTTP server).

The server also sets a security manager. This is a restrictive one, so it needs to be told to allow access. This can be done by setting the java.security.policy property to point to a security policy file, such as policy.all .

Combining all these points leads to startups such as this:

 java -Djava.rmi.server.codebase=http://myWebHost/classes/ \      -Djava.security.policy=policy.all \       rmi.FileClassifierServer 

A Programmer[ap]s Guide to Jini Technology
A Programmer[ap]s Guide to Jini Technology
ISBN: 1893115801
Year: 2000
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net