|home| |
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.
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:
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.
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:\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: