Abridged, Fundamentals of RMI tutorial

by Govind Seshadri                                              revised/ re-edit Nov 6 / 2000 Peter Komisar

additional references: 'Design Patterns' Gamma et al.


Good summaries on RMI are hard to find. To this point we have in this course been using an
abridged version of the RMI specification. Really, it was as easy to read as anything else.
The jGuru tutorial by Govind Seshadri has changed that. This one is very good and brings
clarity to topic areas rarely explained well. The tutorial is also up to date, touching on new
developments intoduced with JDK 1.2 and 1.3. Seshardri brings to one spot a lot of useful
accessory RMI information. I  summarize much of it below. If you would like to read the whole
tutorial, get missing details  and view it's diagrams, go to
http://developer.java.sun.com/developer/onlineTraining/rmi/index.html

On the second pass I have tried to simplify the edit further.              PK Nov 6 / 2000


The RMI architecture is based on the principle that the definition and implementation
of  behavior are separate concepts. In RMI code that defines a behavior and code that
implements that behavior can remain separate and run on separate Java Virtual Machines.

In RMI, a remote service definition is coded into a java interface. The implementation
of this service is coded into classes implementing the interface. RMI has one class
providing the behaviour running on the server and a second class, acting as a proxy for that
remote service running on the client.

A client program makes method calls on the proxy object, RMI sends the request to the
remote JVM, and forwards it to the implementation. Any return values provided by the
implementation are sent back to the proxy and then to the client's program.

RMI Architecture Layers

The RMI implementation is essentially built from three abstraction layers. The first is the
Stub and Skeleton layer, which lies just beneath the view of the developer. This layer
intercepts method calls made by the client to the interface reference variable and redirects
these calls to a remote RMI service.

The next layer is the Remote Reference Layer. This layer understands how to interpret
and manage references made from clients to the remote service objects. In JDK 1.1, this
layer connects clients to remote service objects that are running and exported on a server.
The connection is a one-to-one (unicast) link. In the Java 2 SDK, this layer was enhanced
to support the activation of dormant remote service objects via Remote Object Activation.

The transport layer is based on TCP/IP connections between machines in a network. It
provides basic connectivity, as well as some firewall penetration strategies.

Using a layered architecture allows layers to be enhanced or replaced without affecting the
rest of the system. For example, the transport layer could be replaced by a UDP/IP layer
without affecting the upper layers.

Stub and Skeleton Layer

In the stub and skeleton layer,  RMI uses the Proxy design pattern as described in the book,
Design Patterns by Gamma, Helm, Johnson and Vlissides.In the Proxy pattern, an object
in one context is represented by another (the proxy) in a separate context. The proxy
knows how to forward method calls between the participating objects. In RMI's use of the Proxy
pattern, the stub class plays the role of the proxy, and the remote service implementation
class plays the role of the real subject. Also in RMI, a helper class a skeleton is generated.
to use. The skeleton understands how to communicate with the stub across the RMI link. The
skeleton carries on a conversation with the stub; it reads the parameters for the method call
from the link, makes the call to the remote service implementation object, accepts the return
value, and then writes the return value back to the stub.

//Proxy -provides a surrogate or placeholder for another object to control access to it -'Design Patterns'

In the Java 2 SDK implementation of RMI, the new wire protocol has made skeleton classes obsolete.
RMI uses the reflection process to make the connection to the remote service object. You only have to
worry about skeleton classes and objects in JDK 1.1 and JDK 1.1-compatible system implementations.

Remote Reference Layer

The Remote Reference Layers defines and supports the invocation semantics of the RMI
connection. This layer provides a RemoteRef object that represents the link to the remote
service implementation object. The stub objects use the invoke( ) method in RemoteRef
to forward the method call. The RemoteRef object understands the invocation semantics
for remote services.

The JDK 1.1 implementation of RMI provides only one way for clients to connect to remote
service implementations: a unicast, point-to-point connection. Before a client can use a remote
service, the remote service must be instantiated on the server and exported to the RMI system.
(If it is the primary service, it must also be named and registered in the RMI Registry).

The Java 2 SDK implementation of RMI adds a new way for client and server to connect.
In this version, RMI supports activatable remote objects. When a method call is made
to the proxy for an activatable object, RMI determines if the remote service implementation
object is dormant. If it is dormant, RMI will instantiate the object and restore its state
from a disk file. Once an activatable object is in memory, it behaves just like JDK 1.1
remote service implementation objects.

Transport Layer

The Transport Layer makes the connection between JVMs. All connections are stream-based
network connections that use TCP/IP. Even if two JVMs are running on the same physical
computer, they connect through their host computer's TCP/IP network protocol stack. (This
is why you must have an operational TCP/IP configuration on your system even to run locally.)

In the current release of RMI, TCP/IP connections are used as the foundation for all machine-
to-machine connections. On top of TCP/IP, RMI uses a wire level protocol called Java
Remote Method Protocol (JRMP). JRMP is a proprietary, stream-based protocol that is
only partially specified and is now in two versions. The first version was released with the JDK
1.1 version of RMI and required the use of Skeleton classes on the server. The second version
released with Java 2 SDK, is performance optimized and does not require skeleton classes.

Sun and IBM have jointly worked on the next version of RMI, called RMI-IIOP, which will
be available with Java 2 SDK Version 1.3. The interesting thing about RMI-IIOP is that instead
of using JRMP, it will use the Object Management Group (OMG) Internet Inter-ORB Protocol,
IIOP, to communicate between clients and servers.

Naming Remote Objects

"How does a client find an RMI remote service? " Clients find remote services by using a naming
or directory service. A naming or directory service is run on a well-known host and port number.
(Well-known meaning everyone in an organization knowing what it is). RMI can use many different
directory services, including the Java Naming and Directory Interface (JNDI). RMI itself
includes a simple service called the RMI Registry, rmiregistry. The RMI Registry runs on each
machine hosting remote service objects and accepts queries for services, by default on port 1099.

On a host machine, a server program creates a remote service by first creating a local object that
implements that service. Next, it exports that object to RMI. When the object is exported, RMI
creates a listening service that waits for clients to connect and request the service. After exporting,
the server registers the object in the RMI Registry under a public name.

On the client side, the RMI Registry is accessed through the static class Naming . It provides the
method lookup( ) that a client uses to query a registry. The method lookup( ) accepts a URL that
specifies the server host name and the name of the desired service. The method returns a remote
reference to the service object. The URL takes the form

                rmi://<host_name> [:<name_service_port>] / <service_name>

//  where the host_name is a name recognized on the local area network (LAN) or a DNS name on the
// Internet. The name_service_port only needs to be specified only if the naming service is running on a
// different port  to the default 1099.


Using RMI

A working RMI system is composed of several parts.


You take the following steps to build an RMI system:

     1.  Write and compile Java code for interfaces
      2.  Write and compile Java code for implementation classes
      3.  Generate Stub and Skeleton class files from the implementation classes
      4.  Write Java code for a remote service host program
      5.  Develop Java code for RMI client program
      6.  Install and run RMI system
 

1.Interfaces

The first step is to write and compile the Java code for the service interface. The Calculator
interface defines all of the remote features offered by the service:
 
    public interface Calculator
                                    extends java.rmi.Remote {
                              public long add(long a, long b)
                                  throws java.rmi.RemoteException;

                              public long sub(long a, long b)
                                  throws java.rmi.RemoteException;

                              public long mul(long a, long b)
                                  throws java.rmi.RemoteException;

                              public long div(long a, long b)
                                  throws java.rmi.RemoteException;
                          }

Notice this interface extends Remote, and each method signature declares that it may
throw a RemoteException object.

Copy this file to your directory and compile it with the Java compiler:

                          >javac Calculator.java

2.Implementation

Next, you write the implementation for the remote service. This is the CalculatorImpl class:
 
        public class CalculatorImpl
                              extends
                                java.rmi.server.UnicastRemoteObject
                              implements Calculator {

                              // Implementations must have an
                              //explicit constructor
                              // in order to declare the
                              //RemoteException exception

                              public CalculatorImpl( )
                                  throws java.rmi.RemoteException {
                                  super();
                              }

                              public long add(long a, long b)
                                  throws java.rmi.RemoteException {
                                  return a + b;
                              }

                              public long sub(long a, long b)
                                  throws java.rmi.RemoteException {
                                  return a - b;
                              }

                              public long mul(long a, long b)
                                  throws java.rmi.RemoteException {
                                  return a * b;
                              }

                              public long div(long a, long b)
                                  throws java.rmi.RemoteException {
                                  return a / b;
                              }
                          }
.

Again, copy this code into your directory and compile it.

The implementation class uses UnicastRemoteObject to link into the RMI system.
In the example the implementation class directly extends UnicastRemoteObject. This
is not a requirement. A class that does not extend UnicastRemoteObject may use it's
exportObject( ) method to be linked into RMI.

When a class extends UnicastRemoteObject, it must provide a constructor that declares
that it may throw a RemoteException object. When this constructor calls super( ), it activates
code in UnicastRemoteObject that performs the RMI linking and remote object initialization.

3.Stubs and Skeletons

You next use the RMI compiler, rmic, to generate the stub and skeleton files. The compiler
runs on the remote service implementation class file.

                          >rmic CalculatorImpl

Try this in your directory. After you run rmic you should find the file Calculator_Stub.class
and, if you are running the Java 2 SDK, Calculator_Skel.class.
 
 
  Options for the JDK 1.1 version of the RMI compiler, rmic:


  Usage: rmic <options> <class names>  where <options> includes:

                    -keep  Do not delete intermediate generated source files
                    -keepgenerated (same as "-keep")
                    -g     Generate debugging info
                    -depend   Recompile out-of-date files recursively
                    -nowarn   Generate no warnings
                    -verbose   Output messages about what the compiler is doing
                    -classpath <path>  Specify where to find input sourrce and class files
                    -d <directory>   Specify where to place generated class files
                     -J<runtime flag> Pass argument to the java interpreter

   The Java 2 platform version of rmic add three new options:

                  -v1.1  Create stubs/skeletons for JDK 1.1 stub protocol version
                  -vcompat  (default) Create stubs/skeletons compatible with both 
                   JDK 1.1 and Java 2 stub protocol versions
                  -v1.2  Create stubs for Java 2 stub protocol version only
.

 4.Host Server

Remote RMI services must be hosted in a server process. The class CalculatorServer
is a very simple server that provides the bare essentials for hosting.
 

import java.rmi.Naming;

    public class CalculatorServer {

              public CalculatorServer() {
                   try {
                           Calculator c = new CalculatorImpl( );
                           Naming.rebind("rmi://localhost:1099/CalculatorService", c);
                           } catch (Exception e) {
                              System.out.println("Trouble: " + e);
                             }
                           }

                          public static void main(String args[]) {
                               new CalculatorServer();
                          }
                     }

5.The Client
 

                          import java.rmi.Naming;
                          import java.rmi.RemoteException;
                          import java.net.MalformedURLException;
                          import java.rmi.NotBoundException;

                          public class CalculatorClient {

                              public static void main(String[] args) {
                                  try {
                                      Calculator c = (Calculator)
                                                     Naming.lookup(
                                           "rmi://localhost
                                                  /CalculatorService");
                                      System.out.println( c.sub(4, 3) );
                                      System.out.println( c.add(4, 5) );
                                      System.out.println( c.mul(3, 6) );
                                      System.out.println( c.div(9, 3) );
                                  }
                                  catch (MalformedURLException murle) {
                                      System.out.println();
                                      System.out.println(
                                        "MalformedURLException");
                                      System.out.println(murle);
                                  }
                                  catch (RemoteException re) {
                                      System.out.println();
                                      System.out.println(
                                                  "RemoteException");
                                      System.out.println(re);
                                  }
                                  catch (NotBoundException nbe) {
                                      System.out.println();
                                      System.out.println(
                                                 "NotBoundException");
                                      System.out.println(nbe);
                                  }
                                  catch (
                                      java.lang.ArithmeticException
                                                                ae) {
                                      System.out.println();
                                      System.out.println(
                                       "java.lang.ArithmeticException");
                                      System.out.println(ae);
                                  }
                              }
                          }

6. Running the RMI System

You are now ready to run the system! You need to start three consoles, one for the
server, one for the client, and one for the RMIRegistry. Start with the Registry. You
must be in the directory that contains the classes you have written. From there, enter
the following:

                          rmiregistry

If all goes well, the registry will start running and you can switch to the next console.
In the second console start the server hosting the CalculatorService, and enter the
following:

                        >java CalculatorServer

It will start, load the implementation into memory and wait for a client connection.
In the last console, start the client program.

                        >java CalculatorClient

If all goes well you will see the output of four numbers 1,9,18, and 3.

That's it; you have created a working RMI system. Even though you ran the three consoles
on the same computer, RMI uses your network stack and TCP/IP to communicate between
the three separate JVMs. This is a full-fledged RMI system.


 Parameters in RMI

You have seen that RMI supports method calls to remote objects. When these calls involve
passing parameters or accepting a return value, how does RMI transfer these between JVMs?
Does RMI support pass-by-value or pass-by-reference? The answer depends on whether the
parameters are primitive data types, objects, or remote objects.

Parameters in a Single JVM

First, review how parameters are passed in a single JVM. The normal semantics for Java
technology is pass-by-value. When a parameter is passed to a method, the JVM makes a
copy of the value, places the copy on the stack* and then executes the method. When the
code inside a method uses a parameter, it accesses its stack and uses the copy of the
parameter. Values returned from methods are also copies.

// * in assembly the stack is a memory area used to sequence instructions in a First-In-Last-Out
//   (FILO) configuration

When a primitive data type (boolean, byte, short, int, long, char, float, or double) is passed as
a parameter to a method, the mechanics of pass-by-value are straightforward. The mechanics
of passing an object as a parameter are more complex. Recall that an object resides in heap*
memory and is accessed through one or more reference variables. And, while the following
code makes it look like an object is passed to the method println( )
                     String s = "Test";
                     System.out.println(s);

in the mechanics it is the reference variable that is passed to the method. In the example, a
copy of reference variable s is made (increasing the reference count to the String object by
one) and is placed on the stack. Inside the method, code uses the copy of the reference to
access the object.

// * from an assembly viewpoint the heap is all addressable memory left after system resource allocation

Now you will see how RMI passes parameters and return values between remote JVMs.

Primitive Parameters

When a primitive data type is passed as a parameter to a remote method, the RMI
system passes it by value. RMI will make a copy of a primitive data type and send
it to the remote method. If a method returns a primitive data type, it is also returned to the
calling JVM by value. Values are passed between JVMs in a machine-independent format,
allowing JVMs running on different platforms to communicate with each other reliably.

Object Parameters

When an object is passed to a remote method, the semantics change from the case of
the single JVM. RMI sends the object itself, not its reference, between JVMs. It is the
object that is passed by value, not the reference to the object. Similarly, when a remote
method returns an object, a copy of the whole object is returned to the calling program.

( Unlike primitive data types, sending an object to a remote JVM is a nontrivial task. A Java object can
be simple and self-contained, or it could refer to other Java objects in complex graph-like* structure.
Because different JVMs do not share heap memory, RMI must send the referenced object and all objects it
references. (Passing large object graphs can use a lot of CPU time and network bandwidth.)

// ed. ref.: graph -a generalization of a tree data model where a set of nodes connected by lines or arrows

RMI uses a technology called Object Serialization to transform an object into a linear format
that can then be sent over the network wire. Object serialization essentially flattens an object
and any objects it references. Serialized objects can be de-serialized in the memory of the
remote JVM and made ready for use by a Java program.

Remote Object Parameters

RMI introduces a third type of parameter to consider:remote objects. As you have
seen, a client program can obtain a reference to a remote object through the RMI Registry
program. There is another way in which a client can obtain a remote reference, it can be
returned to the client from a method call. In the following code, the BankManager service
getAccount( ) method is used to obtain a remote reference to an Account remote service.
 

        BankManager bm;
        Account a;
          try {
                bm = (BankManager) Naming.lookup("rmi://BankServer/BankManagerService" );
                 a  = bm.getAccount( "jGuru" );
               // code that uses the account
                 }
                 catch (RemoteException re) {  }

              //  In the implementation of getAccount(), the method returns a (local)
                //  reference to the remote service.

                     public Account
                        getAccount(String accountName) {
                         // Code to find the matching account
                         AccountImpl ai =  // return reference from search
                         return ai;
                     }

When a method returns a local reference to an exported remote object, RMI does
not return that object. Instead, it substitutes another object (the remote proxy for that
service) in the return stream.


 // Supplemental material
 

RMI Client-side Callbacks

In many architectures, a server may need to make a remote call to a client. Examples include
progress feedback, time tick notifications, warnings of problems, etc. To accomplish this, a
client must also act as an RMI server. There is nothing really special about this as RMI works
equally well between all computers. However, it may be impractical for a client to extend
java.rmi.server.UnicastRemoteObject. In these cases, a remote object may prepare itself
for remote use by calling the static method

               UnicastRemoteObject.exportObject (<remote_object>)

Distributing RMI Classes

To run an RMI application, the supporting class files must be placed in locations that can
be found by the server and the clients. For the server, the following classes must be
available to its class loader:


For the client, the following classes must be available to its class loader:


Once you know which files must be on the different nodes, it is a simple task to make
sure they are available to each JVM's class loader.

Automatic Distribution of Classes

The RMI designers extended the concept of class loading to include the loading of classes
from FTP servers and HTTP servers. This is a powerful extension as it means that classes
can be deployed in one, or only a few places, and all nodes in a RMI system will be able
to get the proper class files to operate.

RMI supports this remote class loading through the RMIClassLoader. If a client or server
is running an RMI system and it sees that it must load a class from a remote location, it calls
on the RMIClassLoader to do this work. The way RMI loads classes is controlled by a
number of properties.These properties can be set when each JVM is run:

             java [ -D<PropertyName>=<PropertyValue> ] + <ClassFile>

The property java.rmi.server.codebase is used to specify a URL. This URL points to a
file:, ftp:, or http: location that supplies classes for objects that are sent from this JVM. If
a program running in a JVM sends an object to another JVM (as the return value from a
method), that other JVM needs to load the class file for that object. When RMI sends the
object via serialization, RMI embeds the URL specified by this parameter into the
stream, alongside the object.

Note: RMI does not send class files along with the serialized objects. If the remote JVM needs
to load a class file for an object, it looks for the embedded URL and contacts the server at that
location for the file.

When the property java.rmi.server.useCodebaseOnly is set to true, then the JVM will load
classes from either a location specified by the CLASSPATH environment variable or the URL
specified in this property. By using different combinations of the available system properties, a
number of different RMI system configurations can be created.

Closed. All classes used by clients and the server must be located on the JVM and
referenced by the CLASSPATH environment variable. No dynamic class loading is
supported.

Server based. A client applet is loaded from the server's CODEBASE along with all
supporting classes. This is similar to the way applets are loaded from the same HTTP
server that supports the applet's web page.

Client dynamic. The primary classes are loaded by referencing the CLASSPATH
environment variable of the JVM for the client. Supporting classes are loaded by the
java.rmi.server.RMIClassLoader from an HTTP or FTP server on the network
at a location specified by the server.

Server-dynamic. The primary classes are loaded by referencing the CLASSPATH
environment variable of the JVM for the server. Supporting classes are loaded by the
java.rmi.server.RMIClassLoader from an HTTP or FTP server on the network at a
location specified by the client.

Bootstrap client. In this configuration, all of the client code is loaded from an HTTP or
FTP server across the network. The only code residing on the client machine is a small
bootstrap loader.

Bootstrap server. In this configuration, all of the server code is loaded from an HTTP or
FTP server located on the network. The only code residing on the server machine is a
small bootstrap loader.
// the original tutorial here has an excercise to create a bootstrap client configuration
 

Firewall Issues

Firewalls are inevitably encountered by any networked enterprise application that has to
operate beyond the sheltering confines of an Intranet. Typically, firewalls block all network
traffic, with the exception of those intended for certain "well-known" ports. Since the RMI
transport layer opens dynamic socket connections between the client and the server to
facilitate communication, the JRMP traffic is typically blocked by most firewall implementations.
But luckily, the RMI designers had anticipated this problem, and a solution is provided by the
RMI transport layer itself. To get across firewalls, RMI makes use of HTTP tunneling
by encapsulating the RMI calls within an HTTP POST request.
// Seshadri describes here a couple different firewall configurations vis a vis client and server

It should be noted that notwithstanding the built-in mechanism for overcoming firewalls,
RMI suffers a significant performance degradation imposed by HTTP tunneling.
There are other disadvantages to using HTTP tunneling too. For instance, your RMI
application will no longer be able to multiplex JRMP calls on a single connection, since it
would now follow a discrete request/response protocol. Additionally, using the java-rmi.
cgi script exposes a fairly large security loophole on your server machine, as now,
the script can redirect any incoming request to any port, completely bypassing your
firewalling mechanism.

Developers should also note that using HTTP tunneling precludes RMI applications
from using callbacks, which in itself could be a major design constraint. Consequently,
if a client detects a firewall, it can always disable the default HTTP tunneling feature by
setting the property:

                     java.rmi.server.disableHttp=true

Distributed Garbage Collection

One of the joys of programming for the Java platform is not worrying about memory allocation.
The JVM has an automatic garbage collector that will reclaim the memory from any object
that has been discarded by the running program. One of the design objectives for RMI
was seamless integration into the Java programming language, which includes garbage
collection. Designing an efficient single-machine garbage collector is hard; designing a
distributed garbage collector is very hard.

The RMI system provides a reference counting distributed garbage collection algorithm
based on Modula-3's Network Objects. This system works by having the server keep track
of which clients have requested access to remote objects running on the server. When a reference
is made, the server marks the object as "dirty" and when a client drops the reference, it is marked
as being "clean." // the server tracks when references are requested and when dropped
The interface to the DGC (distributed garbage collector) is hidden in the stubs and skeletons
layer. However, a remote object can implement the java.rmi.server.Unreferenced interface
and get a notification via the unreferenced method when there are no longer any clients holding
a live reference. In addition to the reference counting mechanism, a live client reference has a
lease with a specified time. If a client does not refresh the connection to the remote object before
the lease term expires, the reference is considered to be dead and the remote object may be
garbage collected. The lease time is controlled by the system property java.rmi.dgc.leaseValue.
The value is in milliseconds and defaults to 10 minutes. Because of these garbage collection
semantics, a client must be prepared to deal with remote objects that have "disappeared."
// Seshadri here has an excercise to experiment with the DGC

Serializing Remote Objects

When designing a system using RMI, there are times when you would like to have the flexibility
to control where a remote object runs. Today, when a remote object is brought to life on a
particular JVM, it will remain on that JVM. You cannot "send" the remote object to another
machine for execution at a new location. RMI makes it difficult to have the option of running
a service locally or remotely.
The very reason RMI makes it easy to build some distributed application can make it difficult
to move objects between JVMs. When you declare an object as implementing the Remote
interface, RMI will prevent it from being serialized and sent between JVMs as a parameter.
Instead of sending the implementation class for a java.rmi.Remote interface, RMI substitutes
the stub class. Because this substitution occurs in the RMI internal code, one cannot intercept
this operation.
There are two different ways to solve this problem. The first involves manually serializing the
remote object and sending it to the other JVM.To do this, there are two strategies. The first
strategy is to create an ObjectInputStream and ObjectOutputStream connection between
the two JVMs. With this, you can explicitly write the remote object to the stream. The second
way is to serialize the object into a byte array and send the byte array as the return value to an
RMI method call. Both of these techniques require that you code at a level below RMI and
this can lead to extra coding and maintenance complications.

In a second strategy, you can use a delegation pattern. In this pattern, you place the core
functionality into a class that does not implement java.rmi.Remote and does implement
java.io.Serializable. Then you build a remote interface that declares remote access to the
functionality. When you create an implementation of the remote interface, instead of
reimplementing the functionality, you allow the remote implementation to defer, or
delegate, to an instance of the local version.

Now look at the building blocks of this pattern. Note that this is a very simple example. A
real-world example would have a significant numberof local fields and methods.
 

// Place  functionality in a local object
        public class LocalModel implements java.io.Serializable{
             public String getVersionNumber( ){
             return "Version 1.0";
             }
          }
 // Next, declare an java.rmi.Remote interface with the same functionality:

     interface RemoteModelRef extends java.rmi.Remote{
            String getVersionNumber( ) 
            throws java.rmi.RemoteException;
           }
 // The implementation of the remote service accepts a reference to the
 // LocalModel and delegates the real work to that object:

      public class RemoteModelImpl extends
                        java.rmi.server.UnicastRemoteObject
                         implements RemoteModelRef  {
                              LocalModel lm;
                          public RemoteModelImpl (LocalModel lm) // pass in service
                                     throws java.rmi.RemoteException {
                           super( );
                           this.lm = lm;
                           }
  // Delegate to the local model implementation

                       public String getVersionNumber( )
                         throws java.rmi.RemoteException {
                         return lm.getVersionNumber( );
                         }
                      }
 //  Finally, you define a remote service that provides access to clients. This
 //  is done with a java.r mi.Remote interface and an implementation:

                interface RemoteModelMgr extends java.rmi.Remote {
                       RemoteModelRef getRemoteModelRef( )
                         throws java.rmi.RemoteException;
                         LocalModel     getLocalModel( )
                         throws java.rmi.RemoteException;
                     }
// remote model implementation

                     public class RemoteModelMgrImpl
                         extends java.rmi.server.UnicastRemoteObject
                         implements RemoteModelMgr {
                         LocalModel lm;
                         RemoteModelImpl rmImpl;
                        public RemoteModelMgrImpl( )
                           throws java.rmi.RemoteException {
                         super( );
                       }
                       public RemoteModelRef getRemoteModelRef( )
                           throws java.rmi.RemoteException{
 // Lazy instantiation of delgatee
                       if (null == lm) {
                           lm = new LocalModel();
                         }
 // Lazy instantiation of Remote Interface Wrapper
                         if (null == rmImpl){
                           rmImpl = new RemoteModelImpl (lm);
                         }
                          return ((RemoteModelRef) rmImpl);
                       }
                     public LocalModel getLocalModel( )
                           throws java.rmi.RemoteException{
 // returns a reference to the same LocalModel that exists 
 // as the delagtee of the RMI remote object wrapper
 // Lazy instantiation of delgatee
                         if (null == lm){
                           lm = new LocalModel();
                           }
                           return lm;
                          }
                      }