How does a client deactivate a server object immediately after a remote call to the object has been executed successfully?

Avi Kak

The community literature is somewhat confusing about how a client should go about deactivating a remote server object and, to the extent I know, there are no on-line tutorials dealing with this issue. My goal here is to clarify the confusion and to present sample code that works for deactivating an object that was activated previously.

But first let me cite two of the more important postings regarding object deactivation I found in the on-line community literature. In a posting dealing with object deactivation in the Sun's RMI-USERS mailing list archive, Adrian Colley says:

"An easier way is to first unexport the object in question, thus guaranteeing that you will get no future remote calls. Then deactivate, in a background thread if necessary."
But then in a subsequent posting dealing with object deactivation, William Grosso says
" .... there seems to be no reason to call unexportObject ...."
In the same posting, William Grosso presents a deactivating thread in which he has intentionally commented out the unexportObject() statement since, according to him, it adds nothing to the code. Evidently, William Grosso's observation runs counter to Adrian Colley's recommendation.

In the rest of this note, I'll first mention what I mean by object deactivation. I'll then present sample code that implements this definition.

If, as presented in a recent jGuru posting, object deactivation means that:

i) it should shut down the VM that was spawned by the activation system to activate the object; and ii) a client's request for service from the object should automatically spawn the VM again if it was shut down by a previous deactivation request.
then the following must hold true for an implementation of object deactivation:
  1. The object whose deactivation is desired must first be unexported by invoking Activatable.unexportObject() and then deactivated by invoking Activatable.inactive(). If, as suggested by William Grosso, you only invoke Activatable.inactive(), you will not be shutting down the VM that was spawned by the activation system to activate the object. This can easily be checked by listing the processes on the server side.
  2. The invocation of the Activatable.unexportObject() and the Activatable.inactive() methods must be carried out in a separate thread on the server side. If these methods are invoked in the main thread, the client side will throw the java.rmi.UnmarshalException.
What follows is a "minimalist" program to illustrate object deactivation. The client first invokes the sayHello() method on the remote object and then invokes the deactivate() method on the same object. That the latter invocation shuts down the activated VM on the server side can be seen by listing the processes on the server side. When the runclient.bat program on the client side is executed again, the HelloServer object on the server side is automatically activated again by spawning a new VM.

For those starting out with object deactivation, the following observations about the code that follows might prove useful:

  1. The policy file on the server side is needed for i) starting a deactivation thread in the deactivate() method; and for ii) finding out the name of the local host in the sayHello() method.
  2. 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.
  3. To run this code on your system, you'd obviously have to change the strings "rvl4.ecn.purdue.edu" and "/home/rvl4/a/kak/RMI16.d/" to the name of your server machine and the name of your server-side directory, respectively.
  4. The stub file generated on the server side by running the rmic compiler on the HelloServer class would need to be copied over to the client side.
  5. Since the invocation of Activatable.unexportObject() followed by the invocation of Activatable.inactive() effectively shuts down the VM that was spawned for activating the object, if you insert any Java code immediately after the lines marked (A) or (B), how many of those inserted statements get executed depends on how the operating system handles the shutdown. I found it interesting to insert print statements at these locations to see how many of them would get executed before the activated VM was actually shut down.
Here is the code:

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

public interface Hello extends java.rmi.Remote
  String sayHello() throws java.rmi.RemoteException;
  void deactivate() throws java.rmi.RemoteException;

//////  server file:   HelloServer.java   //////

import java.net.InetAddress;
import java.rmi.*;
import java.rmi.activation.*;

public class HelloServer extends Activatable implements Hello {

  Deactivator deactivator = null;

  public HelloServer(ActivationID id, MarshalledObject data)
      throws RemoteException 
    super( id, 0 );

  public String sayHello()
    String hostname=null;
      hostname= InetAddress.getLocalHost().getHostName();
    }catch (java.net.UnknownHostException who){}
    return  "Hello World from " + hostname;

  public void deactivate() throws RemoteException
    deactivator = new Deactivator( this, getID() );
    deactivator.start();                                             // (A)

class Deactivator extends Thread {

  ActivationID aid;
  Remote server;

  Deactivator( Remote rem, ActivationID id )
    aid = id;
    server = rem;

  public void run()
    try {
      Activatable.unexportObject( server, true );
      Activatable.inactive( aid );                               // (B)
    } catch( Exception e ) { e.printStackTrace(); }

//////  server file:  RegisterHelloServer.java  //////

import java.rmi.*;
import java.rmi.activation.*;

public class RegisterHelloServer {

  public static void main(String[] args) throws Exception 
    ActivationGroupID agi = ActivationGroup.getSystem().registerGroup(
                   new ActivationGroupDesc( null, null ) );
    ActivationDesc desc = new ActivationDesc( agi, "HelloServer", null, null );
    Hello stub = (Hello)Activatable.register(desc);
    Naming.rebind("HelloServer", stub);

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

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

//////  server shell file:  activate.sh  //////

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

//////  client file:   HelloClient.java   //////

import java.rmi.*;

public class HelloClient {

  public static void main(String args[]) 
    Hello obj = null;
    try {
      obj = ( Hello ) Naming.lookup( "rmi://RVL4.ecn.purdue.edu/HelloServer" );
    } catch( Exception e ) { e.printStackTrace(); }

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

java -Dsun.rmi.loader.logLevel=verbose HelloClient