SDSU CS 696 Emerging Technologies: Java Distributed Computing
Spring Semester, 1999
RMI Intro
Previous    Lecture Notes Index    Next    
© 1999, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 04-Feb-99

Contents of Doc 6, RMI Intro


References
Default Policy Implementation and Policy File Syntax (JDK 1.2)
Local Copy: http://www-rohan.sdsu.edu/java/jdk1.2/docs/guide/security/PolicyFiles.html
Sun Site: http://java.sun.com/products/jdk/1.2/docs/guide/security/PolicyFiles.html

Getting Started Using RMI,
Local copy : http://www-rohan.sdsu.edu/java/jdk1.2/docs/guide/rmi/getstart.doc.html
Sun Site: http://java.sun.com/products/jdk/1.2/docs/guide/rmi/getstart.doc.html

Java Remote Method Invocation Specification
Local HTML Copy: http://www-rohan.sdsu.edu/java/jdk1.2/docs/guide/rmi/spec/rmiTOC.doc.html
Sun HTML Site: http://java.sun.com/products/jdk/1.2/docs/guide/rmi/spec/rmiTOC.doc.html
Hard copy is available at Cal Copy and on reserve at Love Library

JDK 1.2 API Documentation
Local Copy: http://www-rohan.sdsu.edu/java/jdk1.2/docs/api/overview-summary.html
Sun Site: http://java.sun.com/products/jdk/1.2/docs/api/overview-summary.html

Permissions in JDK1.2
Local Copy: http://www-rohan.sdsu.edu/java/jdk1.2/docs/guide/security/permissions.html
Sun Site: http://java.sun.com/products/jdk/1.2/docs/guide/security/permissions.html

Remote Method Invocation (RMI) Tools Documentation (rmic, rmiregistry)
Local Copy: http://www-rohan.sdsu.edu/java/jdk1.2/docs/tooldocs/tools.html#rmi
Sun Site: http://java.sun.com/products/jdk/1.2/docs/tooldocs/tools.html#rmi

RMI Source Code
Should be available at /opt/jdk1.2/src soon


Doc 6, 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 6, RMI Intro Slide # 3

The Server Implementation


import java.net.InetAddress;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.server.UnicastRemoteObject;
public class HelloServer 
      extends UnicastRemoteObject
      implements Hello 
   {
   public HelloServer() throws RemoteException {  }
   public String sayHello() 
      {
      return  "Hello World from " + getHostName();
      }
   protected static String getHostName() 
      {
      try 
         {
         return InetAddress.getLocalHost().getHostName();
         }
      catch (java.net.UnknownHostException who) 
         {
         return "Unknown";
         }
      }
      

Doc 6, RMI Intro Slide # 4
  public static void main(String args[])
      {
      // Create and install a security manager
      System.setSecurityManager(new RMISecurityManager());
      
      try 
         {
         // Create and register the server object
         HelloServer serverObject = new HelloServer();
         Naming.rebind("rmi://eli.sdsu.edu/HelloServer", 
                           serverObject );
         // Signal successful registration
         System.out.println("HelloServer bound in registry");
         } 
      catch (Exception e)  
         {
         System.out.println("HelloServer err: ");
         e.printStackTrace();
         }
      }
   }
  

Doc 6, RMI Intro Slide # 5

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( 
                              "rmi://eli.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 6, RMI Intro Slide # 6

Running The Example

Server Side

Step 0 . Install a policy file for socket permissions

Place the following in a file called “.java.policy” in your home directory.
This is needed when running rmi in JDK 1.2, but not in JDK 1.1.x.

   grant
   {
        permission java.net.SocketPermission "*:1024-65535", 
                "connect,accept,resolve";
        permission java.net.SocketPermission "*:1-1023", 
                "connect,resolve";
   };

Step 1 . Compile the source code

Server side needs interface Hello and class HelloServer

javac Hello.java HelloServer.java


Doc 6, RMI Intro Slide # 7
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

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 must manually kill the rmiregistry



Doc 6, RMI Intro Slide # 8
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 6, 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 6, RMI Intro Slide # 10

Basic Issues

Multiple JVMs Running


After testing (running and rerunning and rerunning and ...) your server you may end up with many JVMs that will not quit once 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



Put the following in a file, make it executable

Running the file will then kill all your Java processes

kill ´/usr/bin/ps -o pid,comm -u$USER | egrep java | awk '{print $1}'´

Doc 6, RMI Intro Slide # 11

Port Contention


Only one server can use a port at a time!

Not everyone can use the same port number for the RMI registry on rohan. The RMI HelloServer example runs the RMI registry on the default port 1099

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

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

Last year some students would start one RMI registry and use if for several days.


Doc 6, RMI Intro Slide # 12
Run RMI registry on open Port

The following program will start the RMI registry on an open port

import  java.net.ServerSocket;
import  sdsu.util.ProgramProperties;
import  java.io.IOException;
/**
 * This class starts the rmi registry on a random
 * open port. User can suggest a port using the -p flag
 */
public class StartRMIRegistry
   {
   public static void main( String[] args )
      {
      try
         {
         ProgramProperties flags = new ProgramProperties( args );
         int suggestedPort = flags.getInt( "p", 0);
         int port = getPort( suggestedPort );
         String randomPortRegistry = "rmiregistry " + port;
         Runtime.getRuntime().exec( randomPortRegistry );
         System.out.println( "rmiregistry running on port " + port );
         System.exit( 0 );
         }
      catch (IOException error )
         {
         System.out.println( "Had trouble trying to find a port\n " +
            error);
         }
      }

Doc 6, RMI Intro Slide # 13
   /**
    * Return an open port on current machine. Try the 
    * suggested port first.
    * If suggestedPort is zero, just select a random port
    */
   private static int getPort( int suggestedPort ) throws IOException
      {
      ServerSocket openSocket;
      try
         {
         openSocket = new ServerSocket( suggestedPort );
         }
      catch (java.net.BindException portbusy)
         {
         // find random open port
         openSocket = new ServerSocket( 0 );
         }
      
      int port = openSocket.getLocalPort();
      openSocket.close();
      
      return port;
      } 
   }

Doc 6, RMI Intro Slide # 14
Clients & RMI Registry Ports

The full syntax of the argument to Naming.lookup() is:

   "rmi://host:port/ServerLabel"
The "rmi:" is optional

The default port is 1099

The following are equivalent

Hello remote = 
   (Hello) Naming.lookup( "rmi://eli.sdsu.edu:1099/HelloServer");

Hello remote = 
   (Hello) Naming.lookup( "//eli.sdsu.edu:1099/HelloServer");

Hello remote = 
   (Hello) Naming.lookup( "rmi//eli.sdsu.edu/HelloServer");
Hello remote = 
   (Hello) Naming.lookup( "//eli.sdsu.edu/HelloServer");

Doc 6, RMI Intro Slide # 15

Program Configuration


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


Hello remote = (Hello) Naming.lookup(
                   "//eli.sdsu.edu/HelloServer");

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


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

Use command line flags and configuration files!


Doc 6, RMI Intro Slide # 16

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 6, RMI Intro Slide # 17

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 = "eli.sdsu.edu";
      String host = options.getString( "host", defaultHost );
      int port   = options.getInt( "port", 1099 );
      
      System.out.println( "host: " + host + " port: " + port );
      }
   }
config.properties
#contents of config.properties
# file goes in directory the program is started from
host=rohan.sdsu.edu
port=1099

Doc 6, RMI Intro Slide # 18
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
Serializable

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 6, RMI Intro Slide # 19

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, "config" );
      String host = flags.getString( "host" );
      int defaultPort = Registry.REGISTRY_PORT;
      int port = flags.getInt( "port", defaultPort );
      
      String serverLabel = flags.getString( "label" );
      return "rmi://" + host + ":" + port + "/" + serverLabel;
   }
}

Doc 6, RMI Intro Slide # 20
The Server

import java.io.IOException;
import java.net.InetAddress;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.registry.Registry;      // for port number
import java.rmi.server.UnicastRemoteObject;
import sdsu.util.ProgramProperties;
public class HelloServer 
      extends UnicastRemoteObject
      implements Hello
   {
   public HelloServer() throws RemoteException 
      { }
   public String sayHello() throws RemoteException 
      {
      return  "Hello World from " + getHostName();
      }
   protected static String getHostName() 
      {
      try 
         {
         return InetAddress.getLocalHost().getHostName();
         }
      catch (java.net.UnknownHostException who) 
         {
         return "Unknown";
         }
      }

Doc 6, RMI Intro Slide # 21
//The changed Part

   private static String getHelloHostAddress( String args[] ) 
      throws IOException
      {
      ProgramProperties flags = 
         new ProgramProperties( args, "config" );
      int defaultPort = Registry.REGISTRY_PORT;
      int port = flags.getInt( "port", defaultPort );
      String serverLabel = flags.getString( "label" );
      // can only bind to local host, protocol defaults
      // to local host, do not add a host here
      return "rmi://" + ":" + port + "/" + serverLabel ;
      }
   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 6, RMI Intro Slide # 22

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 6, RMI Intro Slide # 23

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 6, RMI Intro Slide # 24
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 6, RMI Intro Slide # 25

Proxies & Stubs

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 6, RMI Intro Slide # 26
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 final long serialVersionUID = 2;
   
   private static java.lang.reflect.Method $method_sayHello_0;
   
   static {
   try {
      $method_sayHello_0 = Hello.class.getMethod("sayHello", new java.lang.Class[] {});
   } catch (java.lang.NoSuchMethodException e) {
      throw new java.lang.NoSuchMethodError(
      "stub class initialization failed");
   }
   }
   
   // constructors
   public HelloServer_Stub(java.rmi.server.RemoteRef ref) {
   super(ref);
   }
   
   // methods from remote interfaces
   
   // implementation of sayHello()
   public java.lang.String sayHello()
   throws java.rmi.RemoteException
   {
   try {
      Object $result = ref.invoke(this, $method_sayHello_0, null, 6043973830760146143L);
      return ((java.lang.String) $result);
   } catch (java.lang.RuntimeException e) {
      throw e;
   } catch (java.rmi.RemoteException e) {
      throw e;
   } catch (java.lang.Exception e) {
      throw new java.rmi.UnexpectedException("undeclared checked exception", e);
   }
   }
}

Doc 6, RMI Intro Slide # 27

JDK 1.2, Stubs, Skeletons


Rmi in JDK 1.1.x required both stubs and skeletons. When JDK 1.2 rmi clients talk to JDK 1.2 servers, the skeleton is not needed. If you only have JDK 1.2 rmi clients and JDK 1.2 servers you can use the –v1.2 option in the rmic command.

The following command will only generate a stub
The stub only works with JDK 1.2 clients

   rmic –v1.2 ServerClass
Server Side needs the Stub

The stub is used by the client. If the stub class is not available on the server side, you will get an error when you register the server.

There are ways to have the server download the stub to the client

This is why the server needs access to the stub class.

Doc 6, RMI Intro Slide # 28

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 stubs?
Running a new VM for each server object


Doc 6, RMI Intro Slide # 29

RMIRegistry and Stubs


Registry load the stub using

   Class.forName( nameHere ).newInstance();

where nameHere is the full class name of the stub

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

Several ways 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 6, RMI Intro Slide # 30
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 6, RMI Intro Slide # 31
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 6, RMI Intro Slide # 32
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 6, RMI Intro Slide # 33

Policy File


The policy file is part of the new Java security model introduced in JDK 1.2 (Java 2)

The general form for SocketPermission is:
   grant
   {
      permission java.net.SocketPermission "host", "actions";
   };

where:

   host = (hostname | IPaddress ) [:portrange]
   portrange = portnumber | -portnumber | 
                  portnumber-portnumber | portnumber-
   actions = action | action,actions
   action = accept | connect | listen | resolve
Host
host can be:
a DNS name
“localhost” for the local machine
a DNS name with wildcard character "*"

Wildcard examples:

Pattern
Meaning
*
*.edu
*.sdsu.edu
All machines
All machines in edu domain
All machines in the sdsu.edu domain

The wildcard must occur at the beginning of the host name

Doc 6, RMI Intro Slide # 34
Port Range

Pattern
Meaning
-N
N-
N-M
N
ports N and below
ports N and above
ports N through M
the port N

Action

Meaning
accept
connect
listen
resolve
accept connections on listed port(s)
connect to listed port(s)
listen on listed port(s)
resolve host names

listen is meaningful only on the local host

resolve is implied when any other action is present

Examples
grant
{
permission java.net.SocketPermission "localhost", 
   "connect,accept,listen";
permission java.net.SocketPermission "rohan.sdsu.edu:1024-", 
   "connect,accept,resolve";
permission java.net.SocketPermission "rohan.sdsu.edu:-1024", 
   "connect";
};

Doc 6, RMI Intro Slide # 35
Policy File

Policy files can set a number of permissions beside SocketPermission.

See http://java.sun.com/products/jdk1.2/docs/guide/security/permissions.html for more details.

System wide Policy File
Located in {JavaHome}/jre/lib/security/ java.policy
Loaded before user policy file

User Policy File
Default location is {UserHomeDirectory}/.java.policy

The location loading order and number of policy files loaded can be change by editing the file {JavaHome}/jre/lib/security/ java.security.

Loading another Policy File
The command

java –Djava.security.policy=policyFileURL  anApp

will load in addition to the system wide policy file and the default user policy file (if it exits) the file indicated by the url. The url can be just a path to a file or an http url. (I don’t know if other forms of URLs will work.)

java –Djava.security.policy==policyFileURL  anApp
loads only the specified policy file. No other policy files are loaded. Using this flag we can set a different policy file per server object.

appletviewer –J-Djava.security.policy=policyFileURL  applet
loads the policy file in the appletviewer.


Doc 6, RMI Intro Slide # 36
Policy File Gotcha’sPolicy File Flag not Used

The -Djava.security.policy flag will be ignored if the "policy.allowSystemProperty" in the file {JavaHome}/jre/lib/security/java.security is set to false.

The default value is set to true


Policy Files not Checked

If a security manager is not installed in an application, the policy files will not be checked.

The java flag:

-Djava.security.manager

insures that the default security manager will be installed.

Often you use this flag when specifying a policy file option:

java -Djava.security.manager –Djava.security.policy=~whitney/rmi/policFile myApp
Note: The HelloServer class loads a security manager, so we needed a policy file to allow the HelloServer to accept socket connections. The HelloClient class does not load a security manager, so it did not need a policy file to make socket connections

Copyright © 1999 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.

Previous    visitors since 04-Feb-99    Next