SDSU CS 696 Emerging Technologies: Distributed Objects
Spring Semester, 1998
RMI Intro

To Lecture Notes Index
© 1998, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 21-Apr-98

Contents of Doc 4, RMI Intro

  1. References
  2. RMI - Introduction by Example
  3. A First Program - Hello World
    1. The Remote Interface
    2. The Server Implementation
    3. The Client Code
    4. Running The Example
      1. Server Side
      2. Client Side
  4. Basic Issues
    1. Port Contention
    2. Program Configuration
      1. Java Command Line Flags
      2. SDSU Library Tools
        1. sdsu.util.ProgramProperties
        2. sdsu.io.LocalRepository
      3. A Reasonable Implementation
    3. Why the Null Server Constructor?
    4. Orphan Processes
    5. Why Hello Interface?
  5. Proxies
    1. Some Implications of Proxies and Registering Objects

References

JDK API Documentation

Java Remote Method Invocation Specification

Getting Started Using RMI

RMI Source Code


Doc 4, RMI Intro Slide # 2

RMI - Introduction by Example

A First Program - Hello World


Modified from "Getting Started Using RMI"

The Remote Interface

public interface Hello extends java.rmi.Remote 
   {
   String sayHello() throws java.rmi.RemoteException;
   }


Doc 4, RMI Intro Slide # 3

The Server Implementation

// Required for Remote Implementation
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;

// Used in method getUnixHostName
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class HelloServer 
      extends UnicastRemoteObject
      implements Hello
   {

   public HelloServer() throws RemoteException 
      {
      }

   // The actual remote sayHello
   public String sayHello() throws RemoteException 
      {
      return  "Hello World from " + getUnixHostName();
      }


Doc 4, RMI Intro Slide # 4
// Works only on UNIX machines
   protected String getUnixHostName()
      {
      try
         {
         Process  hostName;
         BufferedReader  answer;

         hostName = Runtime.getRuntime().exec( "hostname" );
         answer = new BufferedReader( 
                        new InputStreamReader( 
                           hostName.getInputStream()) );

         hostName.waitFor();
         return answer.readLine().trim();
         }
      catch (Exception noName)
         {
         return "Nameless";
         }
      }
      

Doc 4, RMI Intro Slide # 5
// Main that registers with Server with Registry
   public static void main(String args[])
      {
      // Create and install a security manager
      System.setSecurityManager(new RMISecurityManager());

      try 
         {
         HelloServer serverObject = new HelloServer ();

         Naming.rebind("//roswell.sdsu.edu/HelloServer", 
                           serverObject );

         System.out.println("HelloServer bound in registry");

         } 
      catch (Exception error) 
         {
         System.out.println("HelloServer err: ");
         error.printStackTrace();
         }
      }
   }

Doc 4, RMI Intro Slide # 6

The Client Code

import java.rmi.*;
import java.net.MalformedURLException;

public class HelloClient 
   {
   public static void main(String args[]) 
      {
      try {
         Hello remote = (Hello) Naming.lookup( 
                              "//roswell.sdsu.edu/HelloServer");

         String message = remote.sayHello();
         System.out.println( message );
         } 
      catch ( NotBoundException error)
         {
         error.printStackTrace();
         }
      catch ( MalformedURLException error)
         {
         error.printStackTrace();
         }
      catch ( UnknownHostException error)
         {
         error.printStackTrace();
         }
      catch ( RemoteException error)
         {
         error.printStackTrace();
         }
      }
   }

Note the multiple catches are to illustrate which exceptions are thrown

Doc 4, RMI Intro Slide # 7

Running The Example

Server Side


Step 1. Compile the source code
Server side needs interface Hello and class HelloServer
javac Hello.java HelloServer.java
Step 2. Generate Stubs and Skeletons (to be explained later)

The rmi compiler generates the stubs and skeletons
rmic HelloServer

This produces the files:
HelloServer_Skel.class
HelloServer_Stub.class

The Stub is used by the client and server
The Skel is used by the server


The normal command is:
rmic fullClassname



Doc 4, RMI Intro Slide # 8

Step 3. Insure that the RMI Registry is running

For the default port number
rmiregistry &

For a specific port number
rmiregistry portNumber &
On a UNIX machine the rmiregistry will run in the background and will continue to run after you log out

This means you manually kill the rmiregistry


Step 4. Register the server object with the rmiregistry by running HelloServer.main()

java HelloServer &

Important the above command will create a new thread that will not stop until you kill it!

This can be done at the command level (kill in UNIX) or by the object it self.

Doc 4, RMI Intro Slide # 9

Client Side


The client can be run on the same machine or a different machine than the server

Step 1. Compile the source code
Client side needs interface Hello and class HelloClient
javac Hello.java HelloClient.java

Step 2. Make the HelloServer_Stub.class is available
Either copy the file from the server machine
or
Compile HelloServer.java on client machine and rum rmic

Step 3. Run the client code
java HelloClient


Doc 4, RMI Intro Slide # 10

Basic Issues

Port Contention


Only one server can use a port at a time!

You will need to find a port that is unused for your server!

Try using your uid

The command "id" will give you your uid

Ports you use must be in the range: 5001-65536

Doc 4, RMI Intro Slide # 11

Program Configuration


Location of server object is hard coded into both server and client

Hello remote = (Hello) Naming.lookup(
                   "//roswell.sdsu.edu/HelloServer");
Naming.rebind("//roswell.sdsu.edu/HelloServer", serverObject);


This is typical student/teacher code, but is still bad!

Use command line flags and configuration files!


Doc 4, RMI Intro Slide # 12

Java Command Line Flags


Java uses Properties for configuring a program

java.util.Properties
A hashtable that stores key-value pairs
Value is accessed via the key
Command line key-value Property pairs

Start a program with the following line:
   java -Dkey1=value1 -Dkey2=value2  className

Then the key-value pairs are added to the System properties


Then anywhere in the program the pairs can be accessed via:
   String value = System.getProperty( key);

   String defaultValue = "Hi Mom";
   value = System.getProperty( key, defaultValue);


Doc 4, RMI Intro Slide # 13

SDSU Library Tools

sdsu.util.ProgramProperties


Parses normal key-value pairs on command line

Reads configuration files in Properties or sdsu.util.LabeledData format


If a key occurs in both command line and configuration file then the command line value is used

import java.io.IOException;
import sdsu.util.ProgramProperties;
public class Tester
   {
   public static void main( String[] args) throws IOException
      {
      String shortConfigFileName = "config.properties";
      ProgramProperties options;
      
      options = new ProgramProperties( args, 
                                 shortConfigFileName );
      
      String defaultHost = "roswell.sdsu.edu";
      String host = options.getString( "host", defaultHost );

      int port   = options.getInt( "port", 1099 );
      
      System.out.println( "host: " + host + " port: " + port );
      }
   }

Doc 4, RMI Intro Slide # 14

sdsu.io.LocalRepository


Maps persistent objects to keys

Objects are stored in files

File names are:
key.ObjectType

Currently supports:
String, Properties,
LabeledData, Table, LabeledTable, List, Stringizable
public class SampleRepository
   {
   public static void main( String[] args ) throws IOException
      {
      // Open an existing Repository
      Repository files = new LocalRepository( "SimpleTest");

      // Add an object to the repository
      // test will be saved in the file: letter.Properties

      Properties test = new Properties();
      test.put( "hi", "mom" );
      test.put( "bye", "dad" );

      files.put( "letter", test );  
      
      // read file grades.Table and converts contents
      // to a table
      Table grades = (Table) files.get( "grades" );
      }
   }

Doc 4, RMI Intro Slide # 15

A Reasonable Implementation

The Client
import java.rmi.*;
import java.rmi.registry.Registry;
import java.net.MalformedURLException;
import java.io.IOException;
import sdsu.util.ProgramProperties;

public class HelloClient 
   {
   public static void main(String args[]) 
      {
      try 
         {
         String server = getHelloHostAddress( args);
         Hello remote = (Hello) Naming.lookup( server );
         String message = remote.sayHello();
         System.out.println( message );
         } 
      catch ( Exception error) 
         {  error.printStackTrace();
         }
      }

   private static String getHelloHostAddress( String args[] ) 
      throws IOException
      {
      ProgramProperties flags = new ProgramProperties( args );
      String host = flags.getString( "host" );

      int defaultPort = Registry.REGISTRY_PORT;
      int port = flags.getInt( "port", defaultPort );
      
      return "rmi://" + host + ":" + port + "/HelloServer";
      }
   }

Doc 4, RMI Intro Slide # 16
The Server
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.Registry;      // for port number
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import sdsu.util.ProgramProperties;

public class HelloServer 
      extends UnicastRemoteObject
      implements Hello
   {

   public HelloServer() throws RemoteException 
      { }

   public String sayHello() throws RemoteException 
      {
      return  "Hello World from " + getUnixHostName();
      }



Doc 4, RMI Intro Slide # 17
//The Server Continued
   protected String getUnixHostName()
      {
      try
         {
         Process  hostName;
         BufferedReader  answer;

         hostName = Runtime.getRuntime().exec( "hostname" );
         answer = new BufferedReader( 
                        new InputStreamReader( 
                           hostName.getInputStream()) );

         hostName.waitFor();
         return answer.readLine().trim();
         }
      catch (Exception noName)
         {
         return "Nameless";
         }
      }


Doc 4, RMI Intro Slide # 18
//The changed Part
   private static String getHelloHostAddress( String args[] ) 
      throws IOException
      {
      ProgramProperties flags = new ProgramProperties( args );
      int defaultPort = Registry.REGISTRY_PORT;
      int port = flags.getInt( "port", defaultPort );
      
      // can only bind to local host, protocol defaults
      // to local host, do not add a host here
      return "rmi://" + ":" + port + "/HelloServer";
      }


   public static void main(String args[])
      {
      // Create and install a security manager
      System.setSecurityManager(new RMISecurityManager());

      try 
         {
         String serverAddress = getHelloHostAddress( args );
         HelloServer serverObject = new HelloServer();
         
         Naming.rebind( serverAddress, serverObject);
         System.out.println("HelloServer bound in registry");
         } 
      catch (Exception e) 
         {
         System.out.println("HelloServer err: ");
         e.printStackTrace();
         }
      }
   }

Doc 4, RMI Intro Slide # 19

Why the Null Server Constructor?

   public HelloServer() throws RemoteException 
      { }

HelloServer's parent class constructor throws RemoteException

Since a constructor calls is parent class constructor, RemoteException can be thrown in HelloServer's constructor

So it is a compile error not to either handle the error or explicitly state it will be thrown


Doc 4, RMI Intro Slide # 20

Orphan Processes


rmiregistry:
creates a process that runs in the background
the process continues after you log out
uses a TCP port

In the simple example each server object
creates a process that runs in the background
the process continues after you log out

It is very easy to accumulate lots of used orphaned processes
Get rid of them!

I use the command:
   /usr/bin/ps -o pid,stime,comm -uwhitney

Which will find all my processes

to make it easier to use I alias this command by putting the following in my .cshrc file:
alias pps       '/usr/bin/ps -o pid,stime,comm -uwhitney'

Use the kill command to kill process


Doc 4, RMI Intro Slide # 21

Why Hello Interface?

public class HelloClient 
   {
   public static void main(String args[]) 
      {
      try 
         {
         String server = getHelloHostAddress( args);
         Hello remote = (Hello) Naming.lookup( server );


Naming.lookup() does not return the remote server but a proxy to the remote server

This is an example of programming to an interface!

Doc 4, RMI Intro Slide # 22
Design Principle 1

Program to an interface, not an implementation

Use abstract classes (and/or interfaces in Java) to define common interfaces for a set of classes

Declare variables to be instances of the abstract class not instances of particular classes


Benefits of programming to an interface


Doc 4, RMI Intro Slide # 23

Proxies

How do HelloClient and HelloSever communicate?



Client talks to a Stub that relays the request to the server over a network

Server responds via a skeleton that relays the response to the Client




Doc 4, RMI Intro Slide # 24
HelloServer_Stub
// Stub class generated by rmic, do not edit.
// Contents subject to change without notice.

public final class HelloServer_Stub
    extends java.rmi.server.RemoteStub
    implements Hello, java.rmi.Remote
{
    private static java.rmi.server.Operation[] operations = {
        new java.rmi.server.Operation("java.lang.String sayHello()")
    };
    
    private static final long interfaceHash = 
      6486744599627128933L;
    
    // Constructors
    public HelloServer_Stub() {
        super();
    }
    public HelloServer_Stub(java.rmi.server.RemoteRef rep) {
        super(rep);
    }

Doc 4, RMI Intro Slide # 25
HelloServer_Stub Continued

    // Methods from remote interfaces
    
    // Implementation of sayHello
    public java.lang.String sayHello() throws 
      java.rmi.RemoteException {

        int opnum = 0;
        java.rmi.server.RemoteRef sub = ref;
        java.rmi.server.RemoteCall call = sub.newCall(
            (java.rmi.server.RemoteObject)this, 
            operations, opnum, interfaceHash);
        try {
            sub.invoke(call);
        } catch (java.rmi.RemoteException ex) {
            throw ex;
        } catch (java.lang.Exception ex) {
            throw new java.rmi.UnexpectedException(
               "Unexpected exception", ex);
        };
        java.lang.String $result;
        try {
            java.io.ObjectInput in = call.getInputStream();
            $result = (java.lang.String)in.readObject();
        } catch (java.io.IOException ex) {
            throw new java.rmi.UnmarshalException("Error unmarshaling return", ex);
        } catch (java.lang.ClassNotFoundException ex) {
            throw new java.rmi.UnmarshalException("Return value class not found", ex);
        } catch (Exception ex) {
            throw new java.rmi.UnexpectedException("Unexpected exception", ex);
        } finally {
            sub.done(call);
        }
        return $result;
    }
}

Doc 4, RMI Intro Slide # 26

Some Implications of Proxies and Registering Objects


The following code will give us the above picture
rmiregistry portNumber &

javac HelloServer.java
rmic HelloServer
java HelloServer &

javac BankServer.java
rmic BankServer
java BankServer &

Two semi hidden problems (issues)
How does RMIRegistry find skeletons?
Running a new VM for each server object


Doc 4, RMI Intro Slide # 27
RMIRegistry and Skeletons

Registry load the skeleton using
   Class.forName( nameHere ).newInstance();

where nameHere is the full class name of the skeleton

So the skeleton code must be in the classpath of the registry!!

Several way to insure this is the case:
1. Start the rmiregistry in a directory that contains all the code for all server objects
2. Use package and insure your classpath points to the package location


Doc 4, RMI Intro Slide # 28
Class examples

I use the package whitney.rmi.examples.basic for the examples in the class

On rohan I place them in:
   ~whitney/languages/java/whitney/rmi/examples/basic

which is the same as:
   ~whitney/rmi/examples/basic


I make sure that my classpath points to the directory containing the whitney directory


Doc 4, RMI Intro Slide # 29
Some Package Problems

Sample program - HelloServer

Assume the following is in a file HelloServer.java

package whitney.rmi.examples.basic;

//stuff removed

public class HelloServer
   extends UnicastRemoteObject
   implements Hello
   { //more stuff removed


To compile:
   javac   HelloServer.java

To run the rmic compiler:
   rmic   whitney.rmi.examples.basic.HelloServer

To run HelloServer main to register object:
   java whitney.rmi.examples.basic.HelloServer


Doc 4, RMI Intro Slide # 30
Some Package Solutions

I alias commands!

For rmic I put in a file rmice:
   rmic whitney.rmi.examples.basic.$1

Make the file executable and place the file in my bin directory which is in my path

I alias:
   java whitney.rmi.examples.basic.$1

for running the programs.

Doc 4, RMI Intro Slide # 31
Reducing the Number of VMs

Use
sdsu.rmi.registry.UniVMRegistry
sdsu.rmi.registry.Registrar

UniVMRegistry will load objects directly into its VM
Sample usage

1. Run rmiregistry
   rmiregistry 7654 &

2. Run UniVMRegistry's main
   java sdsu.rmi.registry.UniVMRegistry  -p=7654  &

3. Use Registrar to register objects with UniVMRegistry
int port = 7654;

String serverClass =    "whitney.rmi.examples.basic.SingleHelloServer";

String nameList = 
   Registrar.verboseRebind( port, "HelloServer", serverClass);

System.out.println( nameList );

or
Registrar aRegistrar = new Registrar( "rohan", port );
aRegistrar.rebind( "HelloServer", serverClass );



visitors since 03-Feb-98