Introduction to Java Remote Method Invocation (RMI) |
|
What is this course about:
The example used in this article is an amortization
schedule application. The client requests a local schedule object
from the server through a remote object and passes an amount and duration
of a loan to the server. The server instantiates a local schedule object
with the amount and duration along with the interest rate the server
knows about. Then the schedule object is serialized and returned back to
the client. The client can then print the object or modify it at this point.
The client has its own private copy of the schedule object.
Creating the Interface Definition File
/****************************************************
* module: mathCalc.java
****************************************************/
import java.lang.*;
import java.rmi.*;
public interface mathCalc extends java.rmi.Remote
{
public schedule amortizeSchedule( float ammount,
int duration ) throws
java.rmi.RemoteException;
public void printRate() throws java.rmi.RemoteException;
}
Creating the Interface Implementation File
/****************************************************
* module: mathCalcImp.java
****************************************************/
import java.rmi.*;
import java.rmi.server.*;
class mathCalcImp extends UnicastRemoteObject implements mathCalc
{
float interestRate = (float)7.5;
mathCalcImp() throws java.rmi.RemoteException
{
}
public schedule amortizeSchedule( float ammount, int duration ) throws
java.rmi.RemoteException
{
System.out.println("Amortizeing Schedule.");
// return a locally created server object to the client
return( new schedule( interestRate, ammount, duration ) );
}
public void printRate() throws java.rmi.RemoteException
{
System.out.println("Current Interest Rate is " + interestRate );
}
}
Object Serialization
/****************************************************
* module: schedule.java
****************************************************/
import java.lang.*;
import java.util.*;
import java.io.*;
class schedule implements Serializable
{
float totalLoanAmt;
float usrAmmount;
float interestRate;
int loanDuration;
schedule( float rate, float ammount, int duration )
{
interestRate = rate;
usrAmmount = ammount;
loanDuration = duration;
totalLoanAmt = ammount + (ammount / rate);
}
void print()
{
System.out.println("Schedule Created.");
System.out.println("Calculation information based on:");
System.out.println(" Rate [%" + interestRate + "]" );
System.out.println(" Ammount [$" + usrAmmount + "]" );
System.out.println(" Duration [ " + loanDuration + "]" );
System.out.println(" Total Loan [$" + totalLoanAmt + "]" );
int couponNum = 0;
float balanceRemaining = totalLoanAmt;
float monthlyPayment = 0;
System.out.println();
System.out.println( "Payment Monthly Payment Ammount Balance Remaining");
System.out.println( "------- ----------------------- -----------------");
while( balanceRemaining 0 )
{
couponNum++;
monthlyPayment = totalLoanAmt/loanDuration;
if( balanceRemaining < monthlyPayment )
{
monthlyPayment = balanceRemaining;
balanceRemaining = 0;
}
else
{
balanceRemaining = balanceRemaining - monthlyPayment;
}
System.out.println( couponNum + " " + monthlyPayment + " " +
balanceRemaining );
}
}
}
If we were to implement externalizable instead of serializable , then the schedule.java class would have to provide the serialize/deserialize methods. This would require the schedule class to serialize and deserialize its own data. If you try to pass a local object that has not implemented the serializeable/externalizeable interface, Java will throw a marshaling exception on the server/client.
Creating the Stubs/Skeletons
rmic mathCalcImp
/****************************************************
* module: calcClient.java
****************************************************/
import java.util.*;
import java.net.*;
import java.rmi.*;
import java.rmi.RMISecurityManager;
public class calcClient
{
public static void main( String args[] )
{
mathCalc cm = null;
int i = 0;
System.setSecurityManager( new RMISecurityManager());
try
{
System.out.println("Starting calcClient");
String target = new String( "//"+ args[0] + "/calcMath");
boolean notBound = true;
while (notBound) {
try {
cm = (mathCalc)Naming.lookup( target );
notBound = false;
} catch (NotBoundException e) {
System.out.println("Trying to connect " + target);
try { Thread.sleep(100000); } catch (InterruptedException e2) {}
} catch (RemoteException e) {
System.out.println("Trying to connect " + target);
try { Thread.sleep(100000); } catch (InterruptedException e2) { }
} catch (java.net.MalformedURLException e) {
System.out.println("URL error: " + e);
System.exit(0);
} // try
} // while
System.out.println("Connection established to " + target);
System.out.println("Calc Server Lookup: url =" + url);
if( cm != null )
{
String testStr = "Requesting Current Interest Rate...";
// Print Current Interest Rate from the server
cm.printRate();
// Amortize a schedule using the server interest rate.
float amount = (float)10000.50;
int duration = 36;
schedule curschd = cm.amortizeSchedule( amount, duration );
// Print the schedule
curschd.print();
}
else
{
System.out.println("Requested Remote object is null.");
}
}
catch( Exception e )
{
System.out.println("An error occured");
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
Notice the object returned from the Naming.lookup method is cast to the actual interface class. This is because the lookup call returns a reference of type Object, an abstract type that needs to be casted to a concrete class (e.g., the interface definition file, mathCalc). The URL name lookup format for an RMI object via the registry may look like this: rmi://pl01-itec.uni-klu.ac.at:Portnumber/myObject
Creating the Server
/****************************************************
* module: calcServ.java
****************************************************/
import java.util.*;
import java.rmi.*;
import java.rmi.RMISecurityManager;
public class calcServ
{
public static void main( String args[] )
{
System.setSecurityManager( new RMISecurityManager());
try
{
System.out.println("Starting calcServer");
mathCalcImp cm = new mathCalcImp();
System.out.println("Binding Server");
Naming.rebind("calcMath", cm );
System.out.println("Server is waiting");
}
catch( Exception e )
{
System.out.println("An error occured");
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
/**************************************************** * module: calcServ.java ****************************************************/ import java.util.*; import java.rmi.*; import java.rmi.registry.*;
import java.rmi.RMISecurityManager;
public class calcServ
{
public static void main( String args[] )
{
try
{
System.out.println("Starting calcServer");
Registry reg = null;
System.setSecurityManager(new RMISecurityManager());
try { reg = LocateRegistry.createRegistry(DefaultPort);}
catch (RemoteException e) {
try { reg = LocateRegistry.getRegistry(); }
catch (RemoteException e2) {
System.out.println("Registry could not be established" + e);
System.exit(0);
} // try-catch-e2
} // try-catch-e
System.out.println("Registry established");
mathCalcImp cm = new mathCalcImp();
System.out.println("Binding Server");
Naming.rebind("calcMath", cm );
System.out.println("Server is waiting");
}
catch( Exception e )
{
System.out.println("An error occured");
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
Building the SampleYou need to compile the client and the server code by doing the following:javac calcClient.java javac calcServ.java Starting the SampleNow you are ready to run the sample RMI application. The first thing to do is to start the rmiregistry on the server. Ensure that your CLASSPATH is set up so that the registry can find your server classes in its path. Start the rmiregistry as follows:rmiregistry & (optional port : default port 1099 )[The optional port number can be left out, in which case it defaults to 1099. If this is not the desired port, specify one as in "rmiregistry 1095 &". ] Next, start the server as follows: java -Djava.security.policy=java.policy calcServ & with the java.policy file as e.g. : grant {
permission java.net.SocketPermission "*:1024-65535","connect,accept,resolve"; permission java.net.SocketPermission "*:80,"connect"; }; or a general one : grant {
// Allow everything for now
permission java.security.AllPermission;
};
The server will start and print a message that it is waiting for requests.
Now you are ready to start the client application as follows: java -Djava.security.policy=java.policy calcClient pl01-itec.uni-klu.ac.atAt this point you should see a request come into the server to print the interest rate and request a remote object reference. The client will then display the contents of the schedule object returned from the server.
|