SDSU CS 696 Emerging Technologies: Java Distributed Computing
Spring Semester, 1999
Client-side Proxies
Previous    Lecture Notes Index    Next    
© 1999, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 05-Apr-99

Contents of Doc 22, Client-side Proxies


References

Jan Newmarch’s Jini Tutorial at: http://pandonia.canberra.edu.au/java/jini/tutorial/Jini.xml

Jini API, Local on-line version at: http://www-rohan.sdsu.edu/doc/jini/doc/api/index.html


Doc 22, Client-side Proxies Slide # 2

Server & Proxies


Note the presentation of Server Proxies follows very closely to the Simple example in Jan Newmarch’s Jini Tutorial at: http://pandonia.canberra.edu.au/java/jini/tutorial/Jini.xml. The server provides a different service in this example. A few other details relating to RMI usage and registration are also changed. It is instructive to look at both examples.

In designing a Jini service there are several different strategies:

When the client contacts the lookup service the code that provides the actual service is downloaded to the client. Think of this as an applet version without a browser. While this may not be a common solution, it might make sense in some situations.

When the client contacts the lookup a proxy to the server is downloaded to the client. We will look at two different ways of doing this: using an RMI proxy or building our own proxy. We have seen using an RMI proxy in other examples.


Doc 22, Client-side Proxies Slide # 3

Client-side proxy – Using RMI


This is easier to do than creating your own proxy object. Most of the examples up to this point have done this. See for instance the hello world example at: http://www.eli.sdsu.edu/courses/spring99/cs696/notes/jiniHello/jiniHello.html .

Client-side proxy – Creating A Proxy by Hand


There are a number of reasons for doing this:

If a device can not support rmi or Java, then we can write a proxy that will communicate with the device using another protocol.

Existing network aware devices & servers already use some communication protocol. Writing a client-side proxy that uses that protocol allows us to incorporate legacy devices in a djinn.

RMI will not operate as fast as well written protocol using sockets.


Doc 22, Client-side Proxies Slide # 4

Example


Will use the same example (an encryption server) that was used in Doc 20 (See http://www.eli.sdsu.edu/courses/spring99/cs696/notes/client-side/client-side.html#Heading3).

The classes:
Interface that proxy implements and the client uses to interact with the server. Added a throws RemoteException to indicate that method can fail due to network problems
Does the encryption using xor. This is the same as DirectEncryptionServer in Doc 20. This is used as the compute engine for the TCPEncryptionServer. The TCPEncryptionServer could have reimplemented the methods in Encrypter, but it saved me some time not to do that.
Uses the server. This is the same class that was used in Doc 20 for the client.
This class uses TCP to communicate with EncryptionServerProxy.
This is the client-side proxy. It is registered with the lookup service and downloaded to the client. The client interacts the EncryptionServerProxy. The EncryptionServerProxy using TCP to communicate with TCPEncryptionServer
Registers the encryption service using unicast join


Doc 22, Client-side Proxies Slide # 5

EncryptionInterface

package whitney.jini.examples.serverProxy;
import java.rmi.RemoteException;
public interface EncryptionInterface {
   public String[] encodeTypes() throws RemoteException;
   public String encode( String encodeType, String plainText ) 
      throws RemoteException;
   public String decode( String encodeType, String encodedText) 
      throws RemoteException;
}

DirectEncryptionClient

package whitney.jini.examples.serverProxy;
import java.rmi.RMISecurityManager;
import net.jini.core.discovery.LookupLocator;    
import net.jini.core.entry.Entry;
import net.jini.core.lookup.ServiceRegistrar; 
import net.jini.core.lookup.ServiceTemplate; 
import net.jini.lookup.entry.Name; 
public class DirectEncryptionClient {
   public static void main (String[] args) throws Exception {
      System.setSecurityManager (new RMISecurityManager ());
      LookupLocator lookup = new LookupLocator ("jini://eli.sdsu.edu");
      ServiceRegistrar registrar = lookup.getRegistrar ();
      
      Entry[] serverAttributes = new Entry[1];
      serverAttributes[0] = new Name ("EncryptionService");
      ServiceTemplate template =  new ServiceTemplate (null, null, serverAttributes);
      EncryptionInterface encoder = (EncryptionInterface) registrar.lookup (template);
      String testText = "Hi Mom";
      String encodedText = encoder.encode( "xor", testText );
      String decodedText = encoder.decode( "xor", encodedText );
      System.out.println ( "Original Text: " + testText );
      System.out.println ( "Encoded Text: " + encodedText );
      System.out.println ( "Decoded Text: " + decodedText );
   }
}

Doc 22, Client-side Proxies Slide # 6

Encrypter

package whitney.jini.examples.serverProxy;
import java.io.Serializable;
import sdsu.io.XorOutputStream;
import sdsu.io.XorOutputStream;
public class Encrypter implements EncryptionInterface {
   private static final String XOR = "xor";
   private byte mask;
   private String[] encodeTypes = { XOR };
   
   public  Encrypter( byte aMask ) { mask = aMask; }
   
   public String[] encodeTypes() { return encodeTypes; }
   public String encode( String encodeType, String plainText ) {
      if ( encodeType.equals( XOR) )
         return  xorText( plainText );
      else // just an example. Save space by not using exception
         return "";
   }
   public String decode( String encodeType, String encodedText) {
      if ( encodeType.equals( XOR) )
         return  xorText( encodedText );
      else // just an example. Save space by not using exception
         return "";
   }
   private String xorText(String text)  {
      byte inputBytes[] = text.getBytes();
      byte[] xorBytes = new byte[inputBytes.length];
      for (int k = 0; k < inputBytes.length;k++)
         xorBytes[k ] =(byte) (inputBytes[k] ^ mask);
      return new String(xorBytes);
   }
}

Doc 22, Client-side Proxies Slide # 7

TCPEncryptionServer


Client opens connection to server
Server accepts connection
Sends request to server
Server sends response to client
Server and client close the connection

Basic syntax of protocol of message between client-server

key1=value1; key2=value2;...; keyn=valuen;\u000C'

keys and values cannot contain \u000C, which indicates end of message

sdsu.util.LabeledData, a subclass of java.util.Properties, is used to form strings of the form:

key1=value1; key2=value2;...; keyn=valuen;


Doc 22, Client-side Proxies Slide # 8
TCPEncryptionServer Continued
package whitney.jini.examples.serverProxy;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import sdsu.io.SimpleReader;
import sdsu.util.LabeledData;
public class TCPEncryptionServer extends Thread {
   private int serverPort;
   DirectEncryptionServer encrypter;
   
   static final String COMMAND = "command";
   static final String ENCODE = "encode";
   static final String DECODE = "decode";
   static final String GET_TYPES = "types";
   static final String ERROR = "error";
   static final String DATA = "data";
   static final String CODE_TYPE = "codeType";
   static final char END_OF_MESSAGE  = '\u000C';
   public TCPEncryptionServer( int port, byte mask) {
      serverPort = port;
      encrypter = new DirectEncryptionServer( mask);
   }
   public void run() {
      try {
         ServerSocket acceptor = new ServerSocket( serverPort );
         while (true) {
            Socket client = acceptor.accept();
            processClientRequest(client.getInputStream(),
                           client.getOutputStream());
            client.close();
         }
      } catch (IOException startup) {
         System.err.println( "Socket error: " + startup);
      }
   }

Doc 22, Client-side Proxies Slide # 9
TCPEncryptionServer Continued
   public void processClientRequest( InputStream in, OutputStream out ) {
      out = new BufferedOutputStream( out );
      in = new BufferedInputStream( in );
      try {
         String clientMessage = readMessage( in );
         LabeledData clientRequest = new LabeledData();
         clientRequest.fromString(clientMessage);
         
         String clientCommand = clientRequest.getData( COMMAND, "none" );
         
         String returnMessage;
         
         if (clientCommand.equals( ENCODE ) )
            returnMessage = encoded(  clientRequest.getData( CODE_TYPE ),
                                 clientRequest.getData( DATA ) );
         else if (clientCommand.equals( DECODE ) )
            returnMessage = decoded( clientRequest.getData( CODE_TYPE ),
                                 clientRequest.getData( DATA ) );
         else
            returnMessage = errorMessage( "Command not valid" );
            
         out.write( returnMessage.getBytes() );
         out.write( END_OF_MESSAGE );
         out.flush();
      } catch (IOException  error) {
         System.err.println( "IOException in processing client request: " +
            error.getMessage() );
      }
   }

Doc 22, Client-side Proxies Slide # 10
TCPEncryptionServer Continued
   private String readMessage( InputStream in ) throws IOException {
      StringBuffer inputBuffer = new StringBuffer();
      int inputChar = in.read();
      while ( (inputChar != -1) && (inputChar != END_OF_MESSAGE)) {
         inputBuffer.append( (char) inputChar);
         inputChar = in.read();
      }
      return  inputBuffer.toString();
   }
   private String encoded( String codeType, String text) {
      String encrypted =  encrypter.encode( codeType, text );
      return formatReturnMessage( DATA, encrypted );
   }
   private String decoded( String codeType, String text) {
      String decrypted =  encrypter.decode( codeType, text );
      return formatReturnMessage( DATA, decrypted );
   }
   private String formatReturnMessage( String key, String value) {
      LabeledData formater = new LabeledData();
      formater.put( key, value );
      return formater.toString();
   }
   
   private String errorMessage( String error)  {
      return formatReturnMessage( ERROR, error );
   }
}

Doc 22, Client-side Proxies Slide # 11

EncryptionServerProxy

package whitney.jini.examples.serverProxy;
import java.io.Serializable;
import net.jini.core.lookup.ServiceID;
import java.rmi.RemoteException;
import java.net.Socket;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import sdsu.io.SimpleReader;
import sdsu.util.LabeledData;
import sdsu.util.ConversionException;
public class EncryptionServerProxy implements EncryptionInterface, Serializable {
   private int serverPort;
   private String serverHost;
   
   static final String COMMAND = "command";
   static final String ENCODE = "encode";
   static final String DECODE = "decode";
   static final String ERROR = "error";
   static final String DATA = "data";
   static final String CODE_TYPE = "codeType";
   static final char END_OF_MESSAGE  = '\u000C';
   
   public EncryptionServerProxy( int port, String host ) {
      serverPort = port;
      serverHost = host;
   }
   public String[] encodeTypes() throws RemoteException {
      //Implemenation left to reader
      return null;
   }
   public String encode( String encodeType, String plainText ) 
         throws RemoteException {
      return sendToServer( ENCODE, encodeType, plainText );
   }

Doc 22, Client-side Proxies Slide # 12
EncryptionServerProxy Continued
   public String decode( String encodeType, String encodedText) 
         throws RemoteException {
      return sendToServer( DECODE, encodeType, encodedText );
   }
   private String sendToServer( String command, String encodeType, String data ) 
         throws RemoteException {
      LabeledData message = new LabeledData();
      message.put( COMMAND, command );
      message.put( DATA, data );
      message.put( CODE_TYPE, encodeType.trim() );
      
      String result = socketIO( message.toString());
      LabeledData returnMessage = new LabeledData();
      
      try {
         returnMessage.fromString( result );
      } catch (ConversionException parseError ) {
         throw new RemoteException( "A error ocurred parsing server response: " + 
            parseError.getMessage() );
      }
      if ( returnMessage.containsKey( ERROR ) )
         throw new RemoteException( "An error ocurred" + 
            returnMessage.getData( ERROR) );
      else
         return returnMessage.getData( DATA);
      }

Doc 22, Client-side Proxies Slide # 13
EncryptionServerProxy Continued
   private String readMessage( InputStream in ) throws IOException {
      StringBuffer inputBuffer = new StringBuffer();
      int inputChar = in.read();
      while ( (inputChar != -1) && (inputChar != END_OF_MESSAGE)) {
         inputBuffer.append( (char)inputChar);
         inputChar = in.read();
      }
      String inputMessage = inputBuffer.toString();
      return  inputMessage;
   }
   private String socketIO( String request )  throws RemoteException {
      try {
         Socket toServer = new Socket( serverHost, serverPort );
         OutputStream out = new BufferedOutputStream(
                                    toServer.getOutputStream() );
         out.write( request.getBytes() );
         out.write( END_OF_MESSAGE );
         out.flush();
         
         InputStream in = toServer.getInputStream();
         String response =readMessage( in );
         
         out.close();
         in.close();
         return response;
      } catch (IOException socketProblem ) {
         throw new RemoteException( "Problem will socket to server" );
      }
   }

Doc 22, Client-side Proxies Slide # 14
EncryptionServerProxy Continued
   public int hashCode() {
      return serverHost.hashCode() + serverPort;
      // not a great hashCode, but points out the need to implement this method
   }
   public boolean equals(Object object)  {
      if  ( !(object instanceof EncryptionServerProxy) )
         return false;
      
      EncryptionServerProxy otherObject = (EncryptionServerProxy) object;
      return ( (serverHost.equals( otherObject.serverHost)  ) &&
                  (serverPort == otherObject.serverPort )
               );
   }
}

Doc 22, Client-side Proxies Slide # 15

RegisterTCPEncryption

package whitney.jini.examples.serverProxy;
import com.sun.jini.lease.LeaseRenewalManager;
import java.rmi.RMISecurityManager;
import java.util.Date;
import net.jini.core.discovery.LookupLocator;    
import net.jini.core.entry.Entry;
import net.jini.core.lease.Lease;
import net.jini.core.lookup.ServiceID;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceRegistrar; 
import net.jini.core.lookup.ServiceRegistration;
import net.jini.lookup.entry.Name; 
public class RegisterTCPEncryption {
   public static void main (String[] args) throws Exception {
      System.setSecurityManager (new RMISecurityManager ());
      LookupLocator lookup = new LookupLocator ("jini://eli.sdsu.edu");
      ServiceRegistrar registrar = lookup.getRegistrar ();
      
      Entry[] serverAttributes = new Entry[1];
      serverAttributes[0] = new Name ("EncryptionService");
      ServiceID serverID = null;
      
      int serverPort = 5432;
      String serverHost = "eli.sdsu.edu";
      
      TCPEncryptionServer  serverImpl = 
         new TCPEncryptionServer( serverPort, (byte) 11 );
      serverImpl.setDaemon( true);
      serverImpl.start();
      
      EncryptionServerProxy theServerProxy = 
         new EncryptionServerProxy(serverPort, serverHost);
      ServiceItem encryptServer =  
         new ServiceItem( serverID, theServerProxy, serverAttributes);
      
      ServiceRegistration serverReg =
         registrar.register(encryptServer, 1000 * 60 );
   
      System.out.println ( "Server registered" );

Doc 22, Client-side Proxies Slide # 16
RegisterTCPEncryption Continued

      Lease serverLease = serverReg.getLease();
      LeaseRenewalManager manageLease = 
         new LeaseRenewalManager( serverLease, Lease.FOREVER, null );
         
      serverID = serverReg.getServiceID();
      
      long now = (new Date()).getTime();
      long leaseDuration = (serverLease.getExpiration() - now)/1000;
      System.out.println ( "Server id: " + serverID );
      System.out.println ( "Server lease: " + serverLease );
      System.out.println ( "Lease duration in seconds " + leaseDuration );
      
      for (int k = 1; k <= 2; k++) {
         Thread.sleep( 1000 * 60  );
         now = (new Date()).getTime();
         leaseDuration = (serverLease.getExpiration() - now)/1000;
         System.out.println ( "Lease duration in seconds " + leaseDuration );
      }
      System.out.println ( "Server going down: ");
      manageLease.cancel(serverLease);
      System.out.println ( "Done? ");
      serverImpl.interrupt();
      System.out.println ( "Done: ");
   }
}

Copyright ©, All rights reserved.
1999 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.

Previous    visitors since 05-Apr-99    Next