Previous lesson 2/6 |home| Next lesson 4/6

Remote Method Invocation (Level II)

Create an activatable object

Betty thought that if the RMI server keeps running without any connection, it seems wasting resources. It would be better if a remote object is delivered, shuts down itself when necessary and is activated on demand, rather than running all the time. Actually, Java RMI activation daemon, rmid is designed to do such job. The activation daemon will listen and handle the creation of activatable object on demand.

Based on the RMI specification, you should create an activatable object first, then make a setup program to deliver the activatable object.

What is an activatable remote object?

An activatable remote object is a remote object that starts executing when its remote methods are invoked and shuts itself down when necessary.

How to create an activatable object?

If a class extends java.rmi.activation.Activatable class and has a constructor to accept ActivationID and MarshalledObject parameters,that class is an activatable object.

In order to avoid unnecessary conflicts, Betty used a different name for the remote interface PaymentActivable and made an activable package listed as follows:

  
//PaymentActivable.java
package activable;

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

public interface PaymentActivable extends Remote {
    public double calculatePayment(double principal, double annualRate, int terms)
                    throws RemoteException;
}

Note that the alternation to the previous code is highlighted.

Betty used PaymentActivableImpl as the remote object and let it extend Activatable class which is required. Based on the RMI specification, she declared a two-argument constructor passing ActivationID to register the object with the activation system and a MarshalledObject.. This constructor is required and should throw RemoteException. The super(id, 0) method calls Activatable constructor to pass an activation ID and a port number. In this case, the port number is default 1099.

 
//PaymentActivableImpl.java
package activable;

import java.rmi.RemoteException;
import java.rmi.activation.Activatable;

public class PaymentActivableImpl extends Activatable implements PaymentActivable {

    public PaymentActivableImpl(ActivationID id, MarshalledObject data) 
                                                throws RemoteException {
        super(id, 0);
    }    
    public double calculatePayment(double principal, double annRate, int years)
                                  throws RemoteException {
        double monthlyInt = annRate / 12;
        double monthlyPayment = (principal * monthlyInt)
                    / (1 - Math.pow(1/ (1 + monthlyInt), years * 12));
        return format(monthlyPayment, 2);
    }
    public double format(double amount, int places) {
        double temp = amount; 
        temp = temp * Math.pow(10, places);
        temp = Math.round(temp);
        temp = temp/Math.pow(10, places);
        return temp;
    }
}

According to the RMI specification, to make a remote object accessible via an activation identifier over time, you need to register an activation descriptor for the remote object and include a special constructor that the RMI system calls when it activates the activatable object.

The following classes are involved with the activation process:

How to create a set-up program

The purpose of the set-up program is to register information about activable object with rmid and the rmiregistry. It takes the following steps:

 
//Setup.java
package activable; 

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

public class Setup {

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

	System.setSecurityManager(new RMISecurityManager());

 	Properties props = new Properties(); 
        props.put("java.security.policy", "/myrmi/myrmi.policy");

        ActivationGroupDesc.CommandEnvironment ace = null; 
 	ActivationGroupDesc exampleGroup = new ActivationGroupDesc(props, ace);
 
        ActivationGroupID agi = 
           ActivationGroup.getSystem().registerGroup(exampleGroup);

	String location = "file:/myrmi/";

	MarshalledObject data = null;

       	ActivationDesc desc = new ActivationDesc 
	    (agi, "activable.PaymentActivableImpl",location, data);

	PaymentActivable mort = (PaymentActivable)Activatable.register(desc);
	System.out.println("Got the stub for PaymentActivableImpl");
       
	Naming.rebind("Mortgage", mort);
	System.out.println("Exported from registration");

	System.exit(0);
    }

}

Now we are ready to test the functionality of the activatable object. Note that you don't need to change the existing client class to test RMI server. Since we changed the names of the remote interface and the remote object, we need to make changes accordingly for the existing class. The alteration has been highlighted.

 
//ActivableClient.java
package activable;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class ActivableClient {
    private static PaymentActivable stub = null;
    
    private ActivableClient() {}

    public static void main(String[] args) {

	double payment, principal = 80000;
        double annualInterest = .065;
        int years = 15;
	try {
	    Registry reg = LocateRegistry.getRegistry();
            stub = (PaymentActivable) reg.lookup("Mortgage");
	    
	    
	} catch (Exception e) {
	    System.err.println("Client exception thrown: " + e.toString());
	    e.printStackTrace();
	}
       if (args.length == 3) {
           try {
              principal = Double.parseDouble(args[0]);
              annualInterest = Double.parseDouble(args[1]);
              years = Integer.parseInt(args[2]);
           }
           catch (Exception e) {
              System.out.println("Wrong input " + e.getMessage() );
              System.exit(0);
           }
           print(principal, annualInterest, years);
        
        } else {
            System.out.println("Usage: java ActivableClient principal annualInterest years ");
            System.out.println("\nFor example: java ActivableClient 80000 .065 15 ");
            System.out.println("\nYou will get the output like the following: \n");
	    print(principal, annualInterest, years);
            System.exit(0);
        }        
        
    }
    
    public static void print(double pr, double annRate, int years){
        double mpayment = 0;
        try {
            mpayment = stub.calculatePayment(pr, annRate, years);

        }catch(Exception e) {
            System.out.println("Remote method exception thrown: " + e.getMessage());
        }
        System.out.println("The principal is $" + (int)pr);
        System.out.println("The annual interest rate is " + annRate*100 +"%");
        System.out.println("The term is " + years + " years");
        System.out.println("Your monthly payment is $" + mpayment);
    }
}

Now we have finished 4 classes. In order to run the code successfully, we need a policy file. The following policy file is for all permissions. For specific permission, you need to consult related documentation.

 
//myrmi.policy
grant {
  Permission java.security.AllPermission;
}; 

Note that we save this file in the C:\myrmi\ subdirectory. Please follow the following steps to compile and run.

  1. Compile 4 classes
  2. Start the rmiregistry
  3. Start the activation daemon, rmid
  4. Run the setup program
  5. Run the client

Note that if you use jdk version below 1.5.0, you may need to regenerate stub class first by using command below before start the rmiregistry.

rmic -v1.2 PaymentActivatableImpl

The following illustrates the process of compilation and execution. Note that 2 command-line windows will pop-up. Don't close them.

 C:\   Command Prompt
 
C:\myrmi>javac -d . Setup.java PaymentActivable.java PaymentActivableImpl.java ActivableClient.java

C:\myrmi>set classpath=

C:\myrmi>start rmiregistry

C:\myrmi>start rmid -J-Djava.security.policy=myrmi.policy

C:\myrmi>java -Djava.security.policy=myrmi.policy activable.Setup
Got the stub for PaymentActivableImpl
Exported from registration

C:\myrmi>java -Djava.security.policy=myrmi.policy activable.ActivableClient
Usage: java ActivableClient principal annualInterest years

For example: java ActivableClient 80000 .065 15

You will get the output like the following:

The principal is $80000
The annual interest rate is 6.5%
The term is 15 years
Your monthly payment is $696.89

C:\myrmi>java -Djava.security.policy=myrmi.policy activable.ActivableClient 150000 .06 15
The principal is $150000
The annual interest rate is 6.0%
The term is 15 years
Your monthly payment is $1265.79

C:\myrmi>

All right, let's recap, to create an activable object, you will:

  1. Make a remote object extends java.rmi.activation.Activatable.
  2. The activatable class should have a constructor to take ActivationID and MarshalledObject objects as parameters.
  3. Write a set-up program to register the activable object.

Check your skill

  1. Excecute above programs.
  2. Make the previous Hello program activable by following the instruction given above.

Previous lesson 2/6 |home| Next lesson 4/6