|
RMI and non-RMI Proxies for FileClassifierAn alternative that is often used for client/server systems instead of message passing is remote procedure calls (RPC). This involves a client that does some local processing and makes some RPC calls to the server. We can also bring this into the Jini world by using a proxy that does some processing on the client side, and that makes use of an RMI proxy/stub when it needs to make calls back to the service. The RPC mechanism would most naturally be done using RMI in Java. Some file types are more common than others: GIF, DOC, and HTML files, abound, but there are many more types ranging from less common ones, such as FrameMaker MIF files, to downright obscure ones, such as PDP11 overlay files. An implementation of a file classifier might place the common types in a proxy object that makes them quickly available to clients , and the less common ones back on the server, accessible through a (slower) RMI call. FileClassifierProxyThe proxy object will implement FileClassifier so that clients can find it. The implementation will handle some file types locally, but others it will pass on to another object that implements the ExtendedFileClassifier interface. The ExtendedFileClassifier has one method: getExtraMIMEType() . The proxy is told about this other object at constructor time. The FileClassifierProxy class is as follows : /** * FileClassifierProxy.java */ package extended; import common.FileClassifier; import common.ExtendedFileClassifier; import common.MIMEType; import java.rmi.RemoteException; public class FileClassifierProxy implements FileClassifier { /** * The service object that knows lots more MIME types */ protected ExtendedFileClassifier extension; public FileClassifierProxy(ExtendedFileClassifier ext) { this.extension = ext; } public MIMEType getMIMEType(String fileName) throws RemoteException { 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 { // we don't know it, pass it on to the service return extension.getExtraMIMEType(fileName); } } } // FileClassifierProxy ExtendedFileClassifierThe ExtendedFileClassifier interface will be the top-level interface for the service and an RMI proxy for the service. It will be publicly available for all clients to use. An immediate subinterface, RemoteExtendedFileClassifier , will add the Remote interface: /** * ExtendedFileClassifier.java */ package common; import java.io.Serializable; import java.rmi.RemoteException; public interface ExtendedFileClassifier extends Serializable { public MIMEType getExtraMIMEType(String fileName) throws RemoteException; } // ExtendedFileClassifier and /** * RemoteExtendedFileClassifier.java */ package extended; import java.rmi.Remote; interface RemoteExtendedFileClassifier extends common.ExtendedFileClassifier, Remote { } // RemoteExtendedFileClassifier ExtendedFileClassifierImplThe implementation of the ExtendedFileClassifier interface is done by an ExtendedFileClassifierImpl object. This will also need to extend UnicastRemoteObject so that the RMI runtime can create an RMI proxy for it. Since this object may handle requests from many proxies, an alternative implementation of searching for MIME types using a hash table is given. This is more efficient for repeated searches: /** * ExtendedFileClassifierImpl.java */ package extended; import java.rmi.server.UnicastRemoteObject; import common.MIMEType; import java.util.HashMap; import java.util.Map; public class ExtendedFileClassifierImpl extends UnicastRemoteObject implements RemoteExtendedFileClassifier { /** * Map of String extensions to MIME types */ protected Map map = new HashMap(); public ExtendedFileClassifierImpl() throws java.rmi.RemoteException { /* This object will handle all classification attempts * that fail in client-side classifiers. It will be around * a long time, and may be called frequently, so it is worth * optimizing the implementation by using a hash map * / map.put("rtf", new MIMEType("application", "rtf")); map.put("dvi", new MIMEType("application", "x-dvi")); map.put("png", new MIMEType("image", "png")); // etc } public MIMEType getExtraMIMEType(String fileName) throws java.rmi.RemoteException { MIMEType type; String fileExtension; int dotIndex = fileName.lastIndexOf('.'); if (dotIndex == 1 dotIndex + 1 == fileName.length()) { // can't find suitable suffix return null; } fileExtension= fileName.substring(dotIndex + 1); type = (MIMEType) map.get(fileExtension); return type; } } // ExtendedFileClassifierImpl FileClassifierServerThe final piece in this jigsaw puzzle is the server that creates the service (and implicitly the RMI proxy for the service) and also the proxy primed with knowledge of the service: package extended; 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 FileClassifierServer implements DiscoveryListener, LeaseListener { protected FileClassifierProxy proxy; protected ExtendedFileClassifierImpl impl; protected LeaseRenewalManager leaseManager = new LeaseRenewalManager(); public static void main(String argv[]) { new FileClassifierServer(); // RMI keeps this alive } public FileClassifierServer() { try { impl = new ExtendedFileClassifierImpl(); } catch(Exception e) { System.err.println("New impl: " + e.toString()); System.exit(1); } // set RMI scurity manager System.setSecurityManager(new RMISecurityManager()); // proxy primed with impl proxy = new FileClassifierProxy(impl); 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(); for (int n = 0; n < registrars.length; n++) { ServiceRegistrar registrar = registrars[n]; // export the proxy service ServiceItem item = new ServiceItem(null, proxy, null); ServiceRegistration reg = null; try { reg = registrar.register(item, Lease.FOREVER); } catch(java.rmi.RemoteException e) { System.err.print("Register exception: "); e.printStackTrace(); 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()); } } // FileClassifierServer What Classes Need to Be Where?The implementation of the file classifier in this section uses both RMI and non-RMI proxies. As in other implementations , there is a set of classes involved that need to be known to different parts of an application. We have these classes:
The server running FileClassifierServer needs to know the following classes and interfaces:
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:
In addition, the HTTP server needs to be able to load and store classes. It needs to be able to access the following:
|