Sun and Oracle Community Voices How to Buy Log In United States [Change] English

»  Spotlight Articles
»  Projects
»  Publications
»  People
»  Awards
»  Events
»  Downloads
»  Internships
»  Contrarian Minds
»  About Sun Labs

OPJ Tutorial: Using Java RMI with OPJ

Introduction

The first step in implementing support for distribution in PJama is the porting of RMI to the persistent context. A first implementation of persistent RMI (PJRMI) has been produced and is described in this document.

The current implementation of Persistent RMI supports

  • the running of standard RMI programs plus
  • the running of persistent RMI programs. These include support for:
    • persistence of all remotely-invokable objects,
    • lookup by name of remotely-invokable objects that are bound to a name in the Registry,
    • automatic re-exportation of persistent, remotely-invokable objects on first use and
    • automatic reestablishment of the connection between remote, persistent references and remotely-invokable objects on first use of the reference after store restart.

Section 1 introduces a non-persistent RMI program. Section 2 then builds on this example to illustrate what changes are necessary to a standard RMI program to make it work in the context of a persistent system.

The following documentation is written in terms of server and client, where the server is the provider of the persistent, remotely-invokable object (service) and the client is the remote user, obtaining and holding a reference to the remote service and using the reference to making method calls on the service which are remote method invokations.

Other useful information included in this documentation consists of

  • section 3 providing an example of a program that can be used to cleanly shut down a persistent store containing remotely-invokable objects and
  • section 4 containing a list of common exceptions that may be raised during the execution of the example programs in this documentation, each with an explanation of why the exception is likely to have been raised.

Note that the sources for the example code as used in this document are available as part of the PJama release in the directory $PJAMAHOME/demo/pjrmi/Message.

    A non-persistent RMI program

The diagram in figure 1 illustrates, using the example classes introduced below, the objects involved in an RMI call.

  
Figure 1: Objects used for RMI
&bs;begin&ob;figure&cb; &bs;begin&ob;center&cb; &ob;&bs;hbox&ob;&bs;vbox&ob;&bs;makebox[&bs;columnwidth]&ob;&bs;epsfbox&ob;setup.eps&cb;&cb;&cb;&cb;&cb; &bs;end&ob;center&cb;&bs;end&ob;figure&cb;

An RMI-based MessageService

The example used in this document to illustrate the use of RMI uses a remotely-invokable object providing a MessageService. This service stores a message as a String and provides two remotely-invokable methods: setMessage to set the message to a given string and getMessage to retrieve the current message. The code in figure 2 defines a Java interface for this service, suitable for remote use.

  
Figure 2: Interface MessageService
package message.service;

import java.rmi.Remote;
import java.rmi.RemoteException;


public interface MessageService 
  extends Remote 
{
  public void setMessage(String s)    
    throws RemoteException;

  public String getMessage()          
    throws RemoteException;
}

The code in figure 3 defines a Java class that implements this interface.

  
Figure 3: Class MessageServiceImpl
package message.service;

import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;


public class MessageServiceImpl 
  extends UnicastRemoteObject 
  implements MessageService
{
  private String message;

  public MessageServiceImpl() 
    throws RemoteException
  {
    super(); //exports object for remote use
    message = new String("Hello World"); 
  }

  public void setMessage(String s)    
    throws RemoteException
  {
    System.out.println("\nMessageService.setMessage:\n" +
                       "Setting message to: " + s);
    message = s;
  }

  public String getMessage()        
    throws RemoteException
  {
    System.out.println("\nMessageService.getMessage:\n" +
                       "Returning message: " + message);
    return message; 
  }
}

RMI places certain requirements on the definition of the class and interface providing the implementation of a remotely-invokable object.

  • A remotely-invokable object can only be accessed remotely via an interface.

  • The interface for the remotely-invokable object must implement the interface java.rmi.Remote. The RMI implementation relies on the use of this interface Remote to determine whether to pass an object by copy or by reference: passing a remotely-invokable object by reference results in the creation of a stub/proxy object in the remote VM.

  • The class of the remotely-invokable object must implicitly or explicitly support the exportation of instances of that class to make them remotely usable. The example in figure 3 gains this functionality by inheriting it from the class java.rmi.server.UnicastRemoteObject.

  • Every method of an interface to a remotely-invokable object must throw java.rmi.RemoteException. This ensures that distribution-related errors that occur during a remote method invocation can be signalled via the throwing of an appropriate exception.

  • Where not inherited, the class of a remotely-invokable object is expected to define appropriate methods for toString, equals and clone.

In order to create an instance of a MessageService and make it available to support remote invocations on the methods in its interface, the following steps must be taken:

1.
Run name service rmiregistry &

2.
Run a program to make a MessageService available for remote use An example program that creates and registers the service is shown in figure 4. It can be compiled and run using the following commands: java message.service.nonpersistent.RunService

This program has two significant steps:

(a)
Create MessageService (exports it for remote use) MessageService messageService = new MessageServiceImpl();

(b)
Register object by name Naming.rebind("MessageService", messageService); This then allows clients to do a look up by name to obtain a reference to the published messageService.

The execution of the program RunService on the machine called kona should produce the following output:

susan@kona: java message.service.nonpersistent.RunService
MessageService ready for remote use

It is possible that the following exception may be thrown by this program:

PJamaPJExported create/static code: exception raised: 
java.lang.NullPointerException
This exception is harmless and can safely be ignored.
  
Figure 4: class RunService creates MessageService
package message.service.nonpersistent;

import message.service.MessageService;
import message.service.MessageServiceImpl;
import java.rmi.Naming;

public class RunService {

  public static void main(String[] args) {

    try {
  
      MessageService messageService = new MessageServiceImpl(); 
      Naming.rebind("MessageService", messageService);
      System.out.println("MessageService ready for remote use");
 
    } catch (Exception e) {
      System.out.println("RunService.main: exception raised: ");
      e.printStackTrace();
    }
  }  
}

A non-persistent client for the MessageService

An object in a different VM from the one where the MessageService has been created needs to obtain a reference to the service before it can use it. The code in figure 5 defines a client that, given a reference to a MessageService as an argument to its constructor, supports one method to report the current message held at a MessageService and change it to a new one.

  
Figure 5: MessageClient uses MessageService
package message.client;

import message.service.MessageService;


public class MessageClient 
{
  private MessageService msRef;

  public MessageClient(MessageService ms) {
    try {
      msRef = ms;
    } catch (Exception e) {
        System.out.println("MessageClient: exception occurred:");
        e.printStackTrace();
    }
  }

  public void changeMessage(String newMessage) {
    try {
      String oldMessage = msRef.getMessage();
      msRef.setMessage(newMessage); 
      String checkedMessage = msRef.getMessage();
      System.out.println("MessageClient: message changed from \"" 
                         + oldMessage + "\" to \"" + checkedMessage + "\"");
    } catch (Exception e) {
      System.out.println("MessageClient: exception occurred:");
      e.printStackTrace();
    }
  }
}
An example program that creates and uses the MessageClient is shown in figure 6.
  
Figure 6: RunClient creates and uses MessageClient
package message.client.nonpersistent;

import message.service.MessageService;
import message.client.MessageClient;
import java.rmi.Naming;


public class RunClient{
 
  public static void main(String[] args) {
     try {

      String service = new String("//");
      try {
        service = service.concat(args[0]);
      } catch (ArrayIndexOutOfBoundsException ae) {
        System.out.println("\nUsage: RunClient  ");
        System.exit(-1);
      }  
      service = service.concat("/MessageService");
      System.out.println("RunClient: using service " + service);
 
      MessageService msRef = (MessageService) Naming.lookup(service);     
      MessageClient messageClient= new MessageClient(msRef);
      messageClient.changeMessage(args[1]);
 
    } catch (Exception e) {
      System.out.println("RunClient.main: exception raised: ");
      e.printStackTrace();
    }
  }
}
In order to create and use the MessageClient, the following steps must be taken:
1.
Run client program, in this example supplying the name of the host where the MessageService should be available for remote use and the new message to set at the MessageService.
java message.client.nonpersistent.RunClient kona.dcs.gla.ac.uk "This is a new message"

This program has three significant steps:

(a)
Lookup service by name MessageService msRef = (MessageService) Naming.lookup(service);

(b)
Create client to use service MessageClient messageClient= new MessageClient(msRef);

(c)
Use service messageClient.changeMessage(args[1]);

The execution of the program RunService should produce the following output:

susan@hawaii: java message.client.nonpersistent.RunClient 
kona.dcs.gla.ac.uk "This is a new message"
RunClient: using service //kona.dcs.gla.ac.uk/MessageService
MessageClient: message changed from "Hello World" to 
"This is a new message"
The standard RMI interface Naming provides a method lookup which, given a URL supplying the name of the service and the name of the host where the service is located, will obtain and return a stub object representing that service for use by the client. Note that the class of the service from the client's point of view is that of the interface to the service.

    A persistent RMI program

Creating and using persistent, remotely-invokable objects

The previous section introduced an example of a program that uses standard RMI to create an object that supports the MessageService interface and make it available for remote use. The modifications necessary to provide MessageService as a persistent, remotely-invokable object are now described.

Firstly, a small change in programming model is recommended. In the standard RMI program, the MessageService was created, which also automatically exports it for remote use, and then the program runs indefinitely, waiting to service incoming method calls from other VMs. If the program execution is killed, then the next time the MessageService is required, the program must be run again, creating and exporting the MessageService again, and again it runs indefinitely. In the persistent RMI model, we would like to create the service once, make it persistent and then have it available in the persistent store to service appropriate incoming method calls during future sessions that use that store. Thus, we recommend two distinct stages in persistent RMI: in the first stage a service is created and made persistent; in the second, an existing persistent service is available for remote use. To model these stages, we have two programs: the first program runs over a persistent store, creates a MessageService, exports it for remote use and makes it persistent; the second runs over the same store, making the persistent services in that store available for remote use.1

Populating the persistent store with support services

Before presenting the code for creating and using a persistent application service, here are the details for setting up a persistent store and populating it with a couple of remotely-invokable objects that will be of general use to programs using persistent RMI. The store is set up using the following steps:

1.
Decide where the persistent store will reside and create a configuration file for,it.
2.
Run the program CreateSupportServices, illustrated in figure 7, to create a couple of standard persistent RMI services:
opj -store /local/stores/services.pjc
support.service.persistent.CreateSupportServices

(a)
A Registry is created, which includes automatically exporting it for remote use, and registered as a listener for OPJ events. (See OPJ API documentation for information about the need for OPJ event listeners to be associated with some classes.)

(b)
A SuspendService object is created, exported and bound to a name. This service has just one method suspendAndQuit which suspends all currently running threads (including all the threads associated with exported, remotely-invokable objects listening for incoming method calls), stabilises the persistent store and terminates the current execution of the VM.

(c)
The call System.exit(0) is made explicitly to terminate the potentially-indefinite running of the threads associated with the exported, remotely-invokable objects and stabilise the persistent store, which includes capturing the state of the newly-persistent objects.
  
Figure 7: CreateServiceSupport creates persistent support services
package support.service.persistent;

import java.rmi.registry.Registry;
import sun.rmi.registry.RegistryImpl;
import org.opj.store.PJCtore;
import org.opj.store.PJCtoreImpl;
import org.opj.store.PJActionHandler;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.Naming;

import org.opj.distribution.PJamaPJExported;

public class CreateSupportServices{

  public static void main(String[] args) {
    int portnumber;

    try {
      if (args.length > 0) {  //user specified Registry port number
        portnumber = Integer.parseInt(args[0]);
        System.out.println("CreateSupportServices creating Registry on port " + portnumber);
      }
      else { 
        portnumber = Registry.REGISTRY_PORT;
        System.out.println("CreateSupportServices creating Registry");
      }
      System.out.flush();
      
      Registry registry = new RegistryImpl(portnumber);
      OPRuntime.addListener(registry);

      System.out.println("CreateSupportServices creating suspendservice");
      System.out.flush();
      
      SuspendService suspendService = new SuspendServiceImpl();
      UnicastRemoteObject.exportObject(suspendService);
      Naming.rebind("SuspendService", suspendService);

      System.exit(0);
    } catch (Exception e) {
      System.out.println("CreateSupportServices.main: exception raised: ");
      e.printStackTrace();
    }
  }
}
The explicit call to the static method UnicastRemoteObject.exportObject made for the object suspendService demonstrates the alternative way to make objects available for remote invocation. While, in section 1 the class MessageServiceImpl extends UnicastRemoteObject in order to inherit the code necessary for automatically exporting objects of that class on creation of an instance of the class, the static exportObject method of UnicastRemoteObject supports the exportation of, in principal, any object, at any point during the life of that object. The other RMI restrictions, as specified in section 1, do still apply to the class of that object though.

The persistence of instances of the Registry and SuspendService simplify the running of programs that use RMI. Creating and making persistent an instance of the Registry means that it is no longer necessary to start an rmiregistry process running before invoking a program that creates and uses remotely-invokable objects.

Note that support is not now provided for dynamic, automatic generation of the _Stub and _Skel classes associated with remotely-invokable objects. As with standard RMI, remember to invoke the rmic compiler to generate SuspendServiceImpl_Stub.class and SuspendServiceImpl_Skel.class before running the persistent RMI program CreateSupportServices. Also remember to do the same for MessageServiceImpl_Stub,class and MessageServiceImpl_Skel.class in between compiling the application classes and running the program to create the MessageService.

Populating the store with persistent, remotely-invokable application objects

Now that we have a persistent store prepared to support persistent, remotely-invokable objects, we can run a program to create some. Firstly, it is important to note that the code of the interface MessageService and the class MessageServiceImpl used by the persistent RMI program is unchanged from that used by the standard, non-persistent RMI program; it is still the code as illustrated in figures 2 and 3.

The code in figure 8 is an example of a persistent RMI program that creates a MessageService, exports it for remote use and makes it persistent.

  
Figure 8: CreateService creates persistent MessageService
package message.service.persistent;

import message.service.MessageService;
import message.service.MessageServiceImpl;
import java.rmi.Naming;


public class CreateService{

  public static void main(String[] args) {
    try {
	  
      MessageService messageService = new MessageServiceImpl();
      Naming.rebind("MessageService", messageService);
      System.exit(0);

    } catch (Exception e) {
      System.out.println("CreateService.main: exception raised: ");
      e.printStackTrace();
    }
  }
}
This program is run as follows:
1.
Run the program to create the service, over our existing persistent store
opj -store /local/stores/services.pjc message.service.persistent.CreateService

(a)
The creation of the MessageService object includes the object's exportation for remote use, since the class MessageServiceImpl inherits this functionality from UnicastRemoteObject. Exportation of the object automatically registers it with a persistent table, thus also making the MessageService object persistent too.

(b)
The call to Naming.rebind supports remote lookup of the MessageService by name.

(c)
The call System.exit(0) is made explicitly to terminate the potentially-indefinite running of the threads associated with the exported, remotely-invokable objects and stabilise the persistent store, which includes capturing the state of the newly-persistent objects.
The program should complete with no output.

Using existing persistent, remotely-invokable objects

Having populated the store with persistent, remotely-invokable objects, one program can be used repeatedly to open a session over the store where the persistent, remotely-invokable objects will be automatically available for remote use. The code in figure 9 is an example of such a program.

  
Figure 9: UseService makes persistent, remotely-invokable objects available
package message.service.persistent;

import org.opj.store.PJCtore;
import org.opj.store.PJCtoreImpl;


public class UseService {

  public static void main(String[] args) {
    try {

      PJStore pjs = PJStoreImpl.getStore();

      if (pjs == null)
        System.out.println("UseService.main: no store");
      else
        System.out.println("UseService.main: running...");

      if (pjs != null) {

        // maintain the running of this JVM indefinitely
        // to service incoming method invocations
        while (true) {
          try {
            System.out.println("\nUseService.main: waiting for incoming connections");
            //copied from sun.rmi.registry.RegistryImpl
            Thread.sleep(Integer.MAX_VALUE - 1);
          } catch (InterruptedException e) {
          }
        }
      } 
      else
        System.out.println("UseService.main: no store");

    } catch (Exception e) {
      System.out.println("UseService.main: exception raised: ");
      e.printStackTrace();
    }
  }
}
To use this program, the following steps are taken:
Run it to make the persistent, remotely-invokable objects available opj -store /local/stores/services.pjc message.service.persistent.UseService

The program should give the following output:

susan@kona: opj -store /local/stores/services.pjc 
message.service.persistent.UseService
UseService.main: running...
Each persistent, remotely-invokable object, with which we populated our example store, is automatically re-exported the first time that a client tries to access it. The program above just ensures that the session running over the store continues running as long as it is required. The SuspendService of this store can be used from a different VM to terminate the session of this VM, when the services available from this store are no longer required for the time being. (See the latter part of section 2.2 for information on using the SuspendService.)

The next time an application programmer wants these services to be available again, all they have to do is rerun this UseService program and the re-establishment of the services is done automatically once again.

    Creating and using persistent references to remote, remotely-invokable objects

Just as remotely-invokable objects can be made persistent, references to them can also be made persistent. The program with the code in figure 10 is a client of MessageService; it has one method changeMessage that changes the message held at the MessageService and reports what message is held by the MessageService before and after it is changed.
  
Figure 10: MessageClient uses MessageService
package message.client;

import message.service.MessageService;


public class MessageClient 
{
  private MessageService msRef;

  public MessageClient(MessageService ms) {
    try {
      msRef = ms;
    } catch (Exception e) {
        System.out.println("MessageClient: exception occurred:");
        e.printStackTrace();
    }
  }

  public void changeMessage(String newMessage) {
    try {
      String oldMessage = msRef.getMessage();
      msRef.setMessage(newMessage); 
      String checkedMessage = msRef.getMessage();
      System.out.println("MessageClient: message changed from \"" 
                         + oldMessage + "\" to \"" + checkedMessage + "\"");
    } catch (Exception e) {
      System.out.println("MessageClient: exception occurred:");
      e.printStackTrace();
    }
  }
}
Just as a two-stage model of use was proposed for the creation and use of remotely-invokable objects, the same idea is proposed here for the creation and use of a persistent reference to a remote, remotely-invokable object. The program with the code in figure 11 creates a MessageClient object, as defined in 5, including the establishment of a reference to a MessageService, and makes it persistent. The program is used as follows:
1.
Run the program to create the MessageClient and make it persistent, in this example supplying the name of the host where the MessageService should be available for remote use.
opj -store /local/stores/clients.pjc message.client.persistent.CreateClient kona.dcs.gla.ac.uk

(a)
A reference to the MessageService is obtained from the specified host, using the standard call to Naming.lookup.

(b)
A MessageClient is created and supplied with the MessageService reference.

(c)
The new instance of MessageClient is made persistent.
The program should give the following output:
susan@hawaii: opj -store /local/stores/clients.pjc
message.client.persistent.CreateClient kona.dcs.gla.ac.uk 
Client using service: //kona.dcs.gla.ac.uk/MessageService
  
Figure 11: CreateClient creates a persistent MessageClient
package message.client.persistent;

import message.service.MessageService;
import message.client.MessageClient;
import java.rmi.Naming;
import org.opj.store.PJCtore;
import org.opj.store.PJCtoreImpl;


public class CreateClient {

  public static void main(String[] args) {    
     try {
  
      String service = new String("//");
      try {
        service = service.concat(args[0]);
      } catch (java.lang.ArrayIndexOutOfBoundsException ae) {
        System.out.println("\nUsage: CreateClient ");
        System.exit(-1);
      }
      service = service.concat("/MessageService");
      System.out.println("Client using service: " + service);

      MessageService msRef = (MessageService) Naming.lookup(service);
      MessageClient messageClient= new MessageClient(msRef);
     
      PJStore pjs = PJStoreImpl.getStore();
      pjs.newPRoot("MessageClient", messageClient);

    } catch (Exception e) {
      System.out.println("CreateClient.main: exception raised: ");
      e.printStackTrace();
    }
  }
}

Using an existing, persistent reference to a service

To use an existing, persistent reference to a remote, remotely-invokable object, an application program can be written that uses the reference just as if it were a local reference. Any re-establishment of connections between the persistent reference and the remote, remotely-invokable object that is necessary is done automatically the first time the persistent reference is accessed after the reopening of the persistent store containing the reference. The example program with the code in figure 12 demonstrates the use of a persistent MessageClient that contains and uses a persistent reference to a MessageService.

  
Figure 12: UseClient uses MessageClient
package message.client.persistent;

import message.client.MessageClient;
import org.opj.store.PJCtore;
import org.opj.store.PJCtoreImpl;


public class UseClient {

  public static void main(String[] args) {
    try {

      PJStore pjs = PJStoreImpl.getStore();  
      MessageClient messageClient = 
        (MessageClient) (pjs.getPRoot("MessageClient")); 

      try {
        messageClient.changeMessage(args[0]);
      } catch (java.lang.ArrayIndexOutOfBoundsException ae) {
        System.out.println("\nUsage: UseClient ");
        System.exit(-1);
      }

    } catch (Exception e) {
      System.out.println("UseClient.main: exception raised: ");
      e.printStackTrace();
    }

  }

}
To use the program:
1.
Run the client program, supplying a String to change the message to at the MessageService
opj -store /local/stores/clients.pjc message.client.persistent.UseClient "Working persistent service and client - hurray"

(a)
The MessageClient is looked up by name in the persistent store.

(b)
The method changeMessage is called, passing the given string as a parameter. It will use the persistent reference to a MessageService to change its message.
This program will give the following output:
susan@hawaii: opj -store /local/stores/clients.pjc 
message.client.persistent.UseClient "Working persistent 
service and client - hurray"
MessageClient: message changed from "Hello World" to 
"Working persistent service and client - hurray"

    Using the SuspendService to close down a persistent store

As in standard RMI, a program running over a persistent store supporting remotely-invokable objects will run indefinitely, until it is interrupted or killed. However, an alternative to this is to make use of the SuspendService to close down the store cleanly. A clean shutdown ensures that persistent objects or updates to existing persistent objects are really made persistent.

The interface in figure 13 supports remote invocation of a method to shut down a store cleanly. It may be implemented as in figure 14.

  
Figure 13: SuspendService
package support.service.persistent;


public interface SuspendService extends java.rmi.Remote 
{
  public void suspendAndQuit()
    throws java.rmi.RemoteException;
}
  
Figure 14: SuspendServiceImpl implements SuspendService
package support.service.persistent;


public class SuspendServiceImpl implements SuspendService
{
  public void suspendAndQuit()        
    throws java.rmi.RemoteException
  {
    RealSuspendService realSuspendService = new RealSuspendService();
    new Thread(realSuspendService).start();
  }
}

class RealSuspendService implements Runnable
{
  public void run() {
    try {
      System.out.println("RealSuspendService running...");
      Thread.sleep(30000);  // 30 secs
      System.exit(0);
    } catch (Exception e) {
      System.out.println("RealSuspendService.run: exception raised " + e.getMessage()); 
      e.printStackTrace();
    }
  }
}
The code in figure 15 is an example of a client program that gets a reference to the SuspendService and then calls its method suspendAndQuit to close down the store cleanly. Note that in the implementation of figure 14 a thread is started to close down the store, separately from the thread executing the remotely-invoked method suspendAndQuit. This allows the remote method execution to complete and return cleanly before the store is closed down.
  
Figure 15: SuspendClient uses SuspendService
package support.client.persistent;

import support.service.persistent.SuspendService;
import java.rmi.Naming;

public class SuspendClient {

  public static void main(String[] args) {
    try {
  
      String service = new String("//");
      try {
        service = service.concat(args[0]);
      } catch (java.lang.ArrayIndexOutOfBoundsException ae) {
        System.out.println("\nUsage: CreateClient ");
        System.exit(-1);
      }
      service = service.concat("/SuspendService");
      System.out.println("Client using service: " + service);

      SuspendService ssRef = (SuspendService) Naming.lookup(service);
      ssRef.suspendAndQuit(); 

    } catch (Exception e) {
      System.out.println("SuspendClient.main: exception raised " + e.getMessage()); 
      e.printStackTrace();
    }
  }
}

    RMI Exceptions

If one of the following exceptions is raised, this section may help in a diagnosis of the problem.

java.lang.ClassNotFoundException

The exception java.lang.ClassNotFoundException may be raised when a client, having made a remote method call, tries to receive a stub object as the return value but cannot find its corresponding class. Ensure that the stub class is available to the client, e.g. included in its CLASSPATH.

java.rmi.server.ExportException

The exception java.rmi.server.ExportException may be raised when a server tries to export an object for remote use. Ensure that appropriate stub and skeleton classes have been generated for the object being exported. Remember that it is necessary to call the rmic compiler to generate the required class files before running the program that uses them.

java.lang.IllegalAccessException

The exception java.lang.IllegalAccessException may be raised if a server picks up the wrong version of stub files generated to support PJRMI operation. Ensure the PJRMI classes rather than standard JDK RMI classes are being used i.e. if both appear in the CLASSPATH, check the PJRMI classes appear first.

java.lang.NullPointerException

The exception java.lang.NullPointerException may be raised at a client when it tries to use a persistent reference to a remote, remotely-invokable object. Ensure that the stub held by the client has been generated by PJRMI. Since the stubs representing remotely-invokable objects have slightly different functionality for PJRMI, stubs generated by standard JDK RMI code will not always work after they have been made persistent.

    Comments

Your feedback on the current implementation of PJRMI would be appreciated.
  • If you find any errors in the documentation, think something in the documentation could be explained better or think something should be added to the documentation or
  • if there is something about the design of PJRMI that you think should be changed
Please email comments to forest-info@sunlabs.com


Footnotes

... use.1
The alternative to the recommended model is to create and populate a store with remotely-invokable objects and make them available for remote use all in one program. However, it is useful to be able to separate population of a store from use of the objects in the store since normal usage often involves populating a store once and then using the store contents repeatedly.

PREV: Using AWT and Swing with OPJ, Tutorial Home Page


Last changed: Dec 7th 1999

Questions and comments to forest-info@sunlabs.com