previous chapter table of contents next chapter

Non-RMI Proxy for FileClassifier

Many client-server programs communicate by message passing, often using a TCP socket. The two sides need to have an agreed-upon protocol; that is, they must have a standard set of message formats and know what messages to receive and what replies to send at any time. Jini can be used in this sort of case by providing a wrapper around the client and server, and making them available as a Jini service. The original client then becomes a proxy agent for the server and is distributed to Jini clients for execution. The original server runs within the Jini server and performs the real work of the service, just as in the thin proxy model. What differs is the class structure and how the components communicate.

The proxy and the service do not need to belong to the same class, or even share common superclasses. Unlike the RMI case, the proxy is not derived from the service, so they do not have a shared class structure. The proxy and the service are written independently, using their own appropriate class hierarchies. However, the proxy still has to implement the FileClassifier interface, since that is what the client is asking for and the proxy is delivering.

If RMI is not used, then any other distributed communication mechanism can be employed. Typically client-server systems will use something like reliable TCP ports ”this is not the only choice, but it is the one used in this example. Thus, the service listens on an agreed-upon port, the client connects to this port, and they exchange messages.

The message format adopted for this solution is really simple:

  • The proxy sends a message giving the file extension that it wants classified . This can be sent as a newline-terminated string ( terminated by the '\n' character).
  • The service will either succeed or fail in the classification. If it fails, it sends a single line of the empty string "" followed by a newline. If it succeeds, it sends two lines, the first being the content type, the second the subtype.

The proxy will then use this reply to either return null or a new MIMEType object.


The proxy object will be exported completely to a Jini client, such as TestFileClassifier . When this client calls the getMIMEType() method, the proxy opens up a connection to the service on an agreed-upon TCP port and exchanges messages on this port. It then returns a suitable result. The code looks like this:

 package socket; import common.FileClassifier; import common.MIMEType; import java.net.Socket; import java.io.Serializable; import java.io.IOException; import java.rmi.Naming; import java.io.*; /**  * FileClassifierProxy  */ public class FileClassifierProxy implements FileClassifier, Serializable {     static public final int PORT = 2981;     protected String host;     public FileClassifierProxy(String host) {         this.host = host;     }     public MIMEType getMIMEType(String fileName)         throws java.rmi.RemoteException {         // open a connection to the service on port XXX         int dotIndex = fileName.lastIndexOf('.');         if (dotIndex == 1  dotIndex + 1 == fileName.length()) {             // can't find suitable index             return null;         }         String fileExtension = fileName.substring(dotIndex + 1);         // open a client socket connection         Socket socket = null;         try {              socket = new Socket(host, PORT);         } catch(Exception e) {             return null;         }         String type = null;         String subType = null;         /*          * protocol:          * Write: file extension          * Read: "null" + '\n'          *       type + '\n' + subtype + '\n'          */         try {             InputStreamReader inputReader =                 new InputStreamReader(socket.getInputStream());             BufferedReader reader = new BufferedReader(inputReader);             OutputStreamWriter outputWriter =                 new OutputStreamWriter(socket.getOutputStream());             BufferedWriter writer = new BufferedWriter(outputWriter);             writer.write(fileExtension);             writer.newLine();             writer.flush();             type = reader.readLine();             if (type.equals("null")) {                 return null;             }             subType = reader.readLine();         } catch(IOException e) {             return null;         }         // and finally         return new MIMEType(type, subType);     } } // FileClassifierProxy 


The FileServerImpl service will be running on the server side. It will run in its own thread (inheriting from Thread ) and will listen for connections. When one is received, it will create a new Connection object in its own thread, to handle the message exchange. (This creation of another thread is probably overkill here where the entire message exchange is very short, but it is good practice for more complex situations.)

 /**  * FileServerImpl.java  */ package socket; import java.net.*; import java.io.*; public class FileServerImpl extends Thread {       protected ServerSocket listenSocket;     public FileServerImpl() {         try {             listenSocket = new ServerSocket(FileClassifierProxy.PORT);         } catch(IOException e) {             e.printStackTrace();         }     }          public void run() {         try {             while(true) {                 Socket clientSocket = listenSocket.accept();                 new Connection(clientSocket).start();             }         } catch(Exception e) {             e.printStackTrace();         }     } } // FileServerImpl class Connection extends Thread {       protected Socket client;     public Connection(Socket clientSocket) {         client = clientSocket;     }          public void run() {         String contentType = null;         String subType = null;         try {             InputStreamReader inputReader =                 new InputStreamReader(client.getInputStream());             BufferedReader reader = new BufferedReader(inputReader);             OutputStreamWriter outputWriter =                 new OutputStreamWriter(client.getOutputStream());             BufferedWriter writer = new BufferedWriter(outputWriter);             String fileExtension = reader.readLine();             if (fileExtension.equals("gif")) {                 contentType = "image";                 subType = "gif";             } else if (fileExtension.equals("txt")) {                 contentType = "text";                 subType = "plain";              } // etc                     if (contentType == null) {                 writer.write("null");             } else {                 writer.write(contentType);                 writer.newLine();                 writer.write(subType);             }             writer.newLine();             writer.close();         } catch(IOException e) {             e.printStackTrace();         }     } } 

Service Provider

The Jini service provider must start a FileServerImpl to listen for later connections. Then it can register a FileClassifierProxy proxy object with each lookup service, which will send them on to interested clients. The proxy object must know where the service backend object (the FileServerImpl ) is listening in order to attempt a connection to it, and this information is given by first making a query for the local host and then passing the hostname to the proxy in its constructor.

 package socket; 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; // Jini 1.0 // import com.sun.jini.lease.LeaseListener;       // Jini 1.0 // import com.sun.jini.lease.LeaseRenewalEvent;   // Jini 1.0 import net.jini.lease.LeaseRenewalManager; import net.jini.lease.LeaseListener; import net.jini.lease.LeaseRenewalEvent; import java.rmi.RMISecurityManager; import java.net.*; /**  * FileClassifierServer.java  */ public class FileClassifierServer implements DiscoveryListener, LeaseListener {     protected FileClassifierProxy proxy;     protected LeaseRenewalManager leaseManager = new LeaseRenewalManager();     public static void main(String argv[]) {         new FileClassifierServer();         try {             Thread.sleep(1000000L);         } catch(Exception e) {         }     }     public FileClassifierServer() {         try {             new FileServerImpl().start();         } catch(Exception e) {             System.err.println("New impl: " + e.toString());             System.exit(1);         }         // set RMI scurity manager         System.setSecurityManager(new RMISecurityManager());         // proxy primed with address         String host = null;         try {             host = InetAddress.getLocalHost().getHostName();         } catch(UnknownHostException e) {             e.printStackTrace();             System.exit(1);         }         proxy = new FileClassifierProxy(host);         // now continue as before         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();                 // 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());     } } // FileClassifierServer 

What Classes Need to Be Where?

This section has considered a non-RMI proxy implementation. An application that uses this service implementation will need to deal with these classes:

  • common.MIMEType
  • common.FileClassifier
  • socket.FileClassifierProxy
  • socket.FileServerImpl
  • socket.FileClassifierServer
  • client.TestFileClassifier

Objects in 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, what 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 common.MIMEType class
  • The socket.FileClassifierServer class
  • The socket.FileClassifierProxy class
  • The socket.FileServerImpl 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 socket.FileClassifierProxy interface
  • The common.FileClassifier interface
  • The common.MIMEType class

Running the RMI Proxy FileClassifier

A file classification application will have to run a server and a client, as in the earlier standalone implementation in Chapter 8 and the RMI implementation just a few pages earlier. The client is unchanged, as it does not care which server implementation is used:

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

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/socket/FileClassifierProxy.class , the codebase would be specified as

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

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

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 \       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