I would like to see a "minimalist" version of the official Sun Java tutorial on how to use a MarshalledObject for data persistence in remote object activation.

Avi Kak

I believe that the educational essence of the otherwise very instructive Sun tutorial on how to use a MarshalledObject for data persistence can be demonstrated with a program that is somewhat easier to comprehend by a beginner than the one shown in the tutorial. My own position is that since remote object activation is fairly convoluted as it is, to the extent possible one should make an attempt to illustrate each of its central ideas in isolation from the other ideas.

I have included below a "minimalist" version of the tutorial. As my code illustrates and contrary to what is recommended in the tutorial, it is not necessary to install the RMISecurityManager at the beginning of the setup program. And, it is also not necessary for a client to invoke the RMISecurityManager if no dynamic loading of classes is involved. If the goal is to construct an example that focuses solely on showing how data persistence can be carried out, the setup program can be further simplified by 1) using the command line invocation of the activation daemon rmid to specify the security policy to be used for the VM's launched by the daemon; and 2) giving a null value to the "location" parameter of the ActivationDesc constructor, since with a null value the location defaults to the current directory.

If you are just beginning with the ideas of data persistence in remote object activation, the following remarks about the enclosed code should prove helpful:

  1. The activatable server object, of type MyPersistentClass, has a Vector data member transactions in which is stored a set of strings each time a client invokes the server object's method callServer( Vector v ). By object persistence here is meant saving to the disk the latest state of the server object, which in this case boils down to saving the latest set of strings stored in the data member transactions.
  2. When a client invokes the callServer( Vector v ) method of the server object, the Vector parameter v is set to the following list of strings:
           Deposited money
           Withdrew money
           Transferred money from Savings
           Check cleared
           Point-of-sale charge at grocery store
    On the server side, these five strings are added to the contents of the data member vector transactions. Therefore, each client call adds the above five strings to those already in transactions.
  3. The server object returns to the client the latest contents of the transactions vector and the client prints out all of the strings thus received from the server. Therefore, after N client calls to the server, you should see the client print out N repetitions of the above five strings.
  4. You can verify data persistence by entirely shutting down the activation system on the server side (by killing all its processes) and starting all over. If the next client call is the Nth call, you will see N repetitions of the above five strings -- as long as you do not delete the file persistentObjectStore.ser file in which the latest state of the object is stored.
  5. A call from a client will invoke MyPersistentClass's constructor only if it was not already invoked by a previous call from a client. The constructor will also be invoked if you shut down the entire activation system by killing all its processes and starting over (or if the server computer crashes and you start over). If the MyPersistentClass's constructor is invoked, it will restore the server object from the file persistentObjectStore.ser file if such a file exists at all (see statement marked (A) in the code). The file in which the state of the server object is stored is specified in the statement marked (C) in the Setup.java program.
  6. As shown in the statement marked (B), each time a client invokes the server object method callServer(), the server object with its changed state is saved to the disk file persistentObjectStore.ser.
  7. Since I used a Solaris machine as a server and an NT machine as a client, for command line invocations I have a shell file on the server side and a bat file on the client side.
  8. To run this code on your system, you'd obviously have to change the strings "rvl4.ecn.purdue.edu" and "/home/rvl4/a/kak/RMI18.d/" to the name of your server machine and the name of your server-side directory, respectively.
  9. The stub file generated on the server side by running the rmic compiler on the MyPersistentClass class would need to be copied over to the client side.
Here is the code:

//////  server and client file: RemoteInterface.java  //////

import java.rmi.*;
import java.util.Vector;

public interface RemoteInterface extends Remote {
  public Vector callServer( Vector v ) throws RemoteException;

//////      server file: MyPersistentClass.java      //////

import java.io.*;
import java.rmi.*;
import java.rmi.activation.*;
import java.util.Vector;

public class MyPersistentClass extends Activatable
                         implements RemoteInterface {

  private Vector transactions;
  private File holder;

  public MyPersistentClass( ActivationID id, MarshalledObject data )
      throws RemoteException, ClassNotFoundException, java.io.IOException
    super( id, 0 );

    holder = (File) data.get();

    if ( holder.exists() )                                  // (A)
    else {
      transactions = new Vector( 1, 1 );
      transactions.addElement( "Initializing transaction vector" );

  public Vector callServer( Vector v ) throws RemoteException
    int limit = v.size();
    for ( int i = 0; i < limit; i++ )
      transactions.addElement( v.elementAt( i ) );
    this.saveState();                                       // (B)
    return transactions;

  public Vector getTransactions() {
    return transactions;

 private void restoreState() throws IOException, ClassNotFoundException
    File f = holder;
    FileInputStream fis = new FileInputStream( f );
    ObjectInputStream ois = new ObjectInputStream( fis );
    transactions = ( Vector ) ois.readObject();

  private void saveState()
    FileOutputStream fos = null;
    ObjectOutputStream oos = null;

    try {
      File f = holder;
      fos = new FileOutputStream( f );
      oos = new ObjectOutputStream( fos );
      oos.writeObject( getTransactions() );
    } catch( Exception e ) {
        throw new RuntimeException( "Error saving the state of the object" );

//////            server file: Setup.java            //////

import java.io.File;
import java.rmi.*;
import java.rmi.activation.*;
import java.util.Properties;

public class Setup {

  public static void main( String[] args ) throws Exception
    ActivationGroupID agi = ActivationGroup.getSystem().registerGroup(
                                 new ActivationGroupDesc( null, null ) );
    MarshalledObject data = new MarshalledObject(
        new File( "persistentObjectStore.ser" ) );      // (C)
    ActivationDesc desc = new ActivationDesc( agi, "MyPersistentClass", null, data );
    RemoteInterface yari = (RemoteInterface) Activatable.register( desc );
    Naming.rebind( "MyPersistentClass", yari );
    System.exit( 0 );

//////              server file: policy              //////

grant codebase "file:/home/rvl4/a/kak/RMI18.d/" {
  permission java.security.AllPermission;

//////       server shellscript: activate.sh         //////

#! /bin/sh
rmiregistry &
rmid -C-Djava.security.policy=/home/rvl4/a/kak/RMI18.d/policy &
sleep 1
java Setup

//////          client file: Client.java             //////

import java.rmi.*;
import java.util.Vector;

public class Client {

  public static void main( String[] args )
    try {
      RemoteInterface ri = (RemoteInterface) Naming.lookup( "rmi://RVL4.ecn.purdue.edu/MyPersistentClass" );

      Vector result = new Vector( 1, 5 );
      result.addElement( "Deposited money" );
      result.addElement( "Withdrew money" );
      result.addElement( "Transferred money from Savings" );
      result.addElement( "Check cleared" );
      result.addElement( "Point-of-sale charge at grocery store" );

      result = (Vector) ri.callServer( result );
      System.out.println( "Called the remote method" );
      System.out.println( "Result: " );
      for ( int i = 0; i < result.size(); i++ ) 
        System.out.println( result.elementAt( i ) );
    } catch( Exception e ) { e.printStackTrace(); }

//////        client bat file: runclient.bat         //////

java Client