A348 Remote Server Invocation

powered by FreeFind
Modified: 
Overview - RMI is the infrastructure for a program on one computer to execute methods (procedures) located on another over a network. Methods may pass/receive parameters and return results, the calling program must normally wait until the called method completes execution as it would with a standard method call. Using RMI to build a service (i.e. the UPS shipping rate service) has advantages in that communication between the client and server consists of passing objects as parameters rather than text in a message. All the expressiveness of objects can be utilized (there are some restrictions such as complete thread state can't be passed) so that a linked list could be passed as a parameter rather than converted into text, transmitted, and converted back into a linked list on the receiving end. One disadvantage is that RMI only works with Java because other languages and computers represent data differently, a large problem for developing general services.

Because RMI is Java-based its use is limited as a general solution for building Web or Internet services. An open solution for distributed services being developed is .NET by Microsoft. Where Java passes data (method parameters and results) as Java obejcts, .NET converts data and passes as XML. We have examined ways in which programs can explicitly generate and parse XML, .NET performs this conversion between language representations and XML transparently, allowing one language to invoke methods of another (e.g. Java could call a C++ method, any parameters would be converted from Java-to-XML-to-C++ representation. Another key feature of .NET is that calls can be made to modules (methods/functions/subroutines) on the same or other computers with no change to the caller or calling program, offering the potential for a truly networked language.

Time Client/Server

We will implement a simple Time service client/server using Java RMI. Clients obtain the current time by invoking a method getTime( ) on the remote time server. There are three Java files necessary to implement a client/server relation using RMI:
  1. Interface - TimeServerInf.java - Interface definition of all server methods that can be called by the remote client. The server implementation must define String getTime( ) method.
  2. Server - TimeServerImpl.java - Implements the methods that are defined in the interface (i.e. String getTime( ) ).
  3. Client - TimeClient.java - Client calls to remote methods on the server, (i.e. getTime( ) ).
// TimeServerInf.java interface definition

import java.rmi.*;   

public interface TimeServerInf extends Remote {
   public String getTime() throws RemoteException;
}
// TimeServerImpl.java

import java.rmi.*;
import java.rmi.server.*;

public class TimeServerImpl extends UnicastRemoteObject implements TimeServerInf {

   public TimeServerImpl() throws RemoteException { super();  }

   // implementation for TimeServerInf interface method, return the current date/time as String
   public String getTime()  {
      return new java.util.Date().toString();
   }

   public static void main( String args[] ) throws Exception {     
      System.err.println( "Initializing server: please wait." );

      // create server object and bind TimeServerImpl object to the rmiregistry on default port 1099
      Naming.rebind( "//localhost/Time", new TimeServerImpl() );

      System.err.println("The Time Server is up and running." );
   }
}
// TimeClient.java
import java.rmi.*;   

public class TimeClient {

   public static void main( String args[] ) throws Exception {

         // lookup TimeServerInf remote object in rmiregistry on port 1099 of local host
         TimeServerInf ts = (TimeServerInf) Naming.lookup( "//localhost/Time" );

         // get time from server
         System.out.println(ts.getTime());
   }
}


TimeClient Output

The time is: Tue Jun 19 11:30:36 EDT 2001

RMI software components - The RMI software components include the infrastructure to suppport a programming model where apparent communication is directly between the client and server as illustrated at left below.
 

Apparent Client/Server Communication 

 
Actual Client/Server Communication

 
In fact, there are four main communicating software components in a minimal RMI client/server system, their relationship is illustrated in the diagram at right above. This model hides nearly all the details of communication between networked hosts. Chief issues handled are: The following differs from the original by (see bold parts of TimeServerImpl.java and TimeClient.java below):
  1. The TimeClient can call a TimeServerImpl on any machine address.
  2. The TimeServerImpl returns the machine name with the time.
Time Client/Server
// TimeServerInf.java interface definition

import java.rmi.*;   

public interface TimeServerInf extends Remote {
   public String getTime() throws RemoteException;
}
//   TimeServerImpl.java

import java.rmi.*;
import java.rmi.server.*;

public class TimeServerImpl extends UnicastRemoteObject implements TimeServerInf {

   public TimeServerImpl() throws RemoteException { super();  }

   // implementation for TimeServerInf interface method
   public String getTime()  {
      try {  return java.net.InetAddress.getLocalHost() + " " + new java.util.Date().toString(); }
      catch(Exception e) { return "Failed"; }
   }

   public static void main( String args[] ) throws Exception {     
      System.err.println( "Initializing server: please wait." );

      // create server object and bind TimeServerImpl object to the rmiregistry
      Naming.rebind( "//localhost/Time", new TimeServerImpl() );

      System.err.println("The Time Server is up and running." );
   }
}
//   TimeClient.java

import java.rmi.*;   

public class TimeClient {

   public static void main( String args[] ) throws Exception {

        String host = "localhost";
        if (args.length > 0) host = args[0];

        // lookup TimeServerInf remote object in rmiregistry
        TimeServerInf ts = (TimeServerInf) Naming.lookup( "//" + host + "/Time" );

        // get time from server
        System.out.println(ts.getTime());
   }
}
Output:
ray-laptop/127.0.0.1 Tue Jun 19 11:02:59 EDT 2001

Exercise 1 - Time Client/Server

  1. Copy and paste the three Java programs into the same directory using the names
    1. TimeServerInf.java
    2. TimeServerImpl.java
    3. TimeClient.java.
  2. Copy and paste the commands below left. Due to startup race conditions, the TimeClient may start before the TimeServerImpl is fully running, you will need to enter the last line again by hand if the TimeClient fails.
  3. Find out a class mate's machine IP or name that is running the TimeServerImpl and get its time. To get the time from lf111-b0.ius.edu by:
    1. java TimeClient lf111-b0.ius.edu
Commands to Compile and Execute Time RMI Client/Server
path=C:\JDK1.2.2\BIN;%path%
set classpath=%classpath%;.;C:\JDK1.2.2\lib\classes.zip 
javac *.java
rmic -v1.2 TimeServerImpl 
start rmiregistry
start java TimeServerImpl
java TimeClient
Define path to Java components
Define classpath to Java components
Compile all Java programs.
Generate a Java 2 TimeServerImpl_Stub.class used by client to invoke RMI server methods.
Start the RMI registry used to bind server objects to host and port number (normally port 1099).
Start the server and bind to host and port number.
Execute client invoking getTime method on server.

Distributed Processing

RMI is a general remote computing model that can support virtually any client/server application. The TimeClient/TimeServerImpl was an example of RMI as a server, where the server provided time a s a service to the client. As an example of distributed processing, sorting on a server will be examined where multiple servers each sort a part of an array and the client merges the parts into a whole sorted array. First a simpler example of a single server sorting a client array.

The key software elements are the same as before:

  1. Interface - SortServerInf.java - Interface definition of all server methods that can be called by the remote client. The server implementation must define method:
    1. int[] sort(int data[])
  2. Server - SortServerImpl.java - Implements the methods that are defined in the interface (i.e. int[] sort(int data[]) ).
  3. Client - SortClient.java - Client calls to remote methods on the server, (i.e. int[] sort(int data[]) ).
Sort Client/Server
// SortServerInf.java interface definition

import java.rmi.*;   

public interface SortServerInf extends Remote {
   public int[] sort( int data[] ) throws RemoteException;
}
//   SortServerImpl.java

import java.rmi.*;
import java.rmi.server.*;

public class SortServerImpl extends UnicastRemoteObject implements SortServerInf {

        public SortServerImpl() throws RemoteException { super();  }

   // implementation for SortServerInf interface method
        public int[] sort( int data[] ) {
                int n = data.length;
                int temp;

                for (int pass = 0; pass < n-1; pass++)
                        for (int pair=0; pair < n-pass-1; pair++)
                                if( data[pair] < data[pair+1]) {
                                        temp = data[pair];
                                        data[pair] = data[pair+1];
                                        data[pair+1] = temp;
                                }
                return data;
        }

   public static void main( String args[] ) throws Exception {     
      System.err.println( "Initializing server: please wait." );

      String number = "";
      if (args.length > 0) number = args[0];

      // create server object and bind SortServerImpl object to the rmiregistry
      Naming.rebind( "//localhost/Sort" + number, new SortServerImpl() );

      System.err.println("The Sort Server " + number + " is up and running." );
   }
}
//   SortClient.java

import java.rmi.*;   

public class SortClient {

   public static void main( String args[] ) throws Exception {

        int unsorted[] = {93, 81, 95, 74};
        int sorted[];

        String host = "localhost";
        if (args.length > 0) host = args[0];

        // lookup SortServerInf remote object in rmiregistry
        SortServerInf ss = (SortServerInf) Naming.lookup( "//" + host + "/Sort" );

        // Sort on server
        sorted = ss.sort(unsorted);

        System.out.println("Unsorted Sorted");
        for (int i=0; i<unsorted.length; i++)
                System.out.println(unsorted[i] + "       " + sorted[i]);
   }
}
Output:
Unsorted Sorted
93       95
81       93
95       81
74       74

Exercise 2 - Sort Client/Server

  1. Copy and paste the three Java programs below into the same directory using the names
    1. SortServerInf.java
    2. SortServerImpl.java
    3. SortClient.java.
  2. Copy and paste the commands below left. Due to startup race conditions, the SortClient may start before the SortServerImpl is fully running, you will need to enter the last line again by hand if the SortClient fails.
  3. Find out a class mate's machine IP or name that is running the SortServerImpl and have it. To sort from lf111-b0.ius.edu by:
  4. What changes are needed to run multiple servers:
    1. Changes to the SortServerInf.java?
    2. Changes to the SortServerImpl.java?
    3. Changes to the SortClient.java?
    4. Changes to the command below?
Running the Sort Client/Server
path=C:\JDK1.2.2\BIN;%path%
set classpath=%classpath%;.;C:\JDK1.2.2\lib\classes.zip 
javac *.java
rmic -v1.2 SortServerImpl 
start rmiregistry
start java SortServerImpl
java SortClient
Define path to Java components
Define classpath to Java components
Compile all Java programs.
Generate a Java 2 SortServerImpl_Stub.class used by client to invoke RMI server methods.
Start the RMI registry used to bind server objects to host and port number (normally port 1099).
Start the server and bind to host and port number.
Execute client invoking sort method on server.

Multiple SortServerImpl

A more realistic distributed processing example would be to have multiple SortServerImpl each sorting an array that is returned to the client and merged to produce a fully sorted array. The apparent object transfer between the client and the two server is: 
 
Sort Client with Multiple Servers
//   MultiSortClient.java

import java.rmi.*;   

public class MultiSortClient {

   public static void main( String args[] ) throws Exception {

        int unsorted1[] = {93, 81, 95, 74};
        int unsorted2[] = {93, 81, 95, 74};
        int sorted1[], sorted2[];
        int sorted[];

        String host1 = "localhost", host2 = "localhost";
        if (args.length == 2) { host1 = args[0]; host2 = args[1]; }

        // lookup SortServerInf remote object in rmiregistry
        SortServerInf ss1 = (SortServerInf) Naming.lookup( "//" + host1 + "/Sort1" );
        SortServerInf ss2 = (SortServerInf) Naming.lookup( "//" + host2 + "/Sort2" );

        // Sort on server
        sorted1 = ss1.sort(unsorted1);
        sorted2 = ss2.sort(unsorted2);

        sorted = new MultiSortClient().merge(sorted1, sorted2);

        System.out.println("Sorted");
        for (int i=0; i<sorted.length; i++)
                System.out.println(sorted[i]);
   }

   private int[] merge(int s1[], int s2[]) {
        int merged[] = new int[s1.length+s2.length];

        int i1=0, i2=0;

        for (int i=0; i<merged.length; i++)
                if (i1 == s1.length) merged[i] = s2[i2++];
                else if (i2 == s2.length) merged[i] = s1[i1++];
                     else if (s1[i1] > s2[i2]) merged[i]=s1[i1++];
                          else merged[i] = s2[i2++];
        return merged;
   }
}
Output:
Sorted
95
95
93
93
81
81
74
74

Exercise 3 - Multiple Sort Client

  1. Copy and paste the Java program above into the same directory as before using the name:
    1. MultiSortClient.java.
  2. Copy and paste the commands below left. Due to startup race conditions, the MultiSortClient may start before the SortServerImpl is fully running, you will need to enter the last line again by hand if the MultiSortClient fails.
  3. Find out a class mate's machine IP or name that is running the SortServerImpl. Sort from lf111-b0.ius.edu and lf111-b1.ius.edu by:
  4. Identify changes needed to run multiple servers:
    1. Changes to the SortServerInf.java?
    2. Changes to the SortServerImpl.java?
    3. Changes to the MultiSortClient.java?
    4. Changes to the command below?
Running the Sort Client and Multiple Servers
path=C:\JDK1.2.2\BIN;%path%
set classpath=%classpath%;.;C:\JDK1.2.2\lib\classes.zip 
javac *.java
rmic -v1.2 SortServerImpl 
start rmiregistry
start java SortServerImpl 1
start java SortServerImpl 2
java MultiSortClient
Define path to Java components
Define classpath to Java components
Compile all Java programs.
Generate a Java 2 SortServerImpl_Stub.class used by client to invoke RMI server methods.
Start the RMI registry used to bind server objects to host and port number (normally port 1099).
Start the server and bind to //localhost/Sort1 and port number.
Start the server and bind to //localhost/Sort2 and port number.
Execute client invoking sort method on server.

Threading

The multiple sort client above has one serious weakness, although the sorting is distributed amoung several sort servers no advantage is gained in reduced overall time. This can be understood by an examination of the client and recalling that remote method invocations behave as a regular method invocation, the caller is blocked until the method returns. In the following the second invocation is not made until the first is completed:
        sorted1 = ss1.sort(unsorted1);       // Wait till completed
        sorted2 = ss2.sort(unsorted2);       // Wait till completed
The time the client and server SS1 and SS2 execute can be illustrated as: where each executes sequentially. Ideally both sorts would occur simultaneously, This can be achieved by making each server sort invocation in a seperate thread on the client and waiting for each server to finished before merging the sorted arrays. The following implements a threaded client that is in other aspects the same as the previous example. The key changes are:
  1. an additional class SortThread
    1. that runs a thread for calling a sort server,
    2. can be checked for sort completion,
    3. returns the sorted array.
  2. the client
    1. creates and runs one thread for each server,
    2. while a server is sorting waits,
    3. when all servers are finished sorting, retrieves the sorted array.
With the use of threads the sort servers can be started and monitored for completion. The execution of the client and sort servers SS1 and SS2 is illustrated at right. Each can execute in parallel so that the time to complete is, in theory at least, reduced. Because of networking and RMI overhead, reduced execution time may not be a fact.
// TMultiSortClient.java

import java.rmi.*;   

class SortThread extends Thread {
   int unsorted[], sorted[];
   SortServerInf ss;
   String name;
   boolean sorting;

   public SortThread(SortServerInf ss, int unsorted[], String name) {
        this.unsorted = unsorted;
        this.ss = ss;
        this.name = name;
        sorting = true;
   }
   public int[] getSorted() {
        return sorted;
   }
   public void run() {
        System.out.println("Started thread " + name);
        try { 
                sorted = ss.sort(unsorted); 
        } catch(Exception e) {}
        System.out.println("Finished thread " + name);
        sorting = false;
   }
   public boolean sorting() {
        return sorting;
   }
}

public class TMultiSortClient {

   public static void main( String args[] ) throws Exception {

        int unsorted1[] = {93, 81, 95, 74};
        int unsorted2[] = {93, 81, 95, 74};
        int sorted1[], sorted2[];
        int sorted[];

        SortThread thread1, thread2;

        String host1 = "localhost", host2 = "localhost";
        if (args.length == 2) { host1 = args[0]; host2 = args[1]; }

        // lookup SortServerInf remote object in rmiregistry
        SortServerInf ss1 = (SortServerInf) Naming.lookup( "//" + host1 + "/Sort1" );
        SortServerInf ss2 = (SortServerInf) Naming.lookup( "//" + host2 + "/Sort2" );

        // Start sort threads
        thread1 = new SortThread(ss1, unsorted1, "Sort1");
        thread2 = new SortThread(ss2, unsorted2, "Sort2");

        thread1.start();
        thread2.start();

        // Wait while either thread is sorting
        while(thread1.sorting() || thread2.sorting()) System.out.println("Sorting");;

        sorted = new TMultiSortClient().merge(thread1.getSorted(), thread2.getSorted());

        System.out.println("Sorted");
        for (int i=0; i<sorted.length; i++)
                System.out.println(sorted[i]);
   }
   private int[] merge(int s1[], int s2[]) {
        int merged[] = new int[s1.length+s2.length];

        int i1=0, i2=0;

        for (int i=0; i<merged.length; i++)
                if (i1 == s1.length) merged[i] = s2[i2++];
                else if (i2 == s2.length) merged[i] = s1[i1++];
                     else if (s1[i1] > s2[i2]) merged[i]=s1[i1++];
                          else merged[i] = s2[i2++];
        return merged;
   }
}
Output

Sorting
Started thread Sort2
Started thread Sort1
Sorting
Sorting
Finished thread Sort2
Finished thread Sort1
Sorting
Sorted
95
95
93
93
81
81
74
74
Running the Threaded Sort Client and Multiple Servers
path=C:\JDK1.2.2\BIN;%path%
set classpath=%classpath%;.;C:\JDK1.2.2\lib\classes.zip 
javac *.java
rmic -v1.2 SortServerImpl 
start rmiregistry
start java SortServerImpl 1
start java SortServerImpl 2
java TMultiSortClient
Define path to Java components
Define classpath to Java components
Compile all Java programs.
Generate a Java 2 SortServerImpl_Stub.class used by client to invoke RMI server methods.
Start the RMI registry used to bind server objects to host and port number (normally port 1099).
Start the server and bind to //localhost/Sort1 and port number.
Start the server and bind to //localhost/Sort2 and port number.
Execute client invoking sort method on all servers.

Exercise 4 - Threaded Multiple Sort Client

  1. Copy and paste the Java program above into the same directory as before using the name:
    1. TMultiSortClient.java.
  2. Copy and paste the commands below left. Due to startup race conditions, the TMultiSortClient may start before the SortServerImpl is fully running, you will need to enter the last line again by hand if the TMultiSortClient fails.
  3. Find out a class mate's machine IP or name that is running the SortServerImpl. Sort from lf111-b0.ius.edu and lf111-b1.ius.edu by:
  4. Run the TMultiSortClient several times and notice the difference in the output.
  5. Modify TMultiSortClient.java and the commands to handle three servers.