Retrofit existing
applications
with RMI - JavaWorld January 2001
Tutorial Details:
Retrofit existing applications with RMI
Retrofit existing applications with RMI
By: By Gregg Sporar
Minimize code changes and gain flexibility by using the Adapter pattern on the client side
ost tutorials on Java's Remote Method Invocation (RMI) technology use as examples new programs that were initially written to use RMI; that practice excludes those of us who modify existing Java code to use RMI. In this article, I'll show how applying the Adapter design pattern can make retrofitting your code much easier. I will build upon Dan Becker's excellent article, "Design Networked Applications in RMI Using the Adapter Design Pattern." (See Resources for a link.) Becker showed how to use the Adapter design pattern on the server side; I'll demonstrate its use on the client side.
If you are unfamiliar with RMI, I recommend jGuru's "Fundamentals of RMI" course. If you are unfamiliar with design patterns or the specifics of the Adapter pattern, I recommend Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, et al. Online tutorials are also available on the Patterns homepage. (See Resources for links to these materials.)
Why RMI?
RMI distributes an existing application's components across multiple systems. This is done for several reasons, including:
Portions of an application that once worked well on a single system have outgrown that system's processing capability
You need to move some parts of an application for security reasons
You wish to achieve reuse or concurrent use by multiple clients
Why the Adapter design pattern?
Initially, you must use RMI's protocol for making remote method calls. It would be beneficial, however, if most of your source code could keep using its existing protocol. The Adapter design pattern solves that problem by providing access to an object through a protocol that that object does not directly support. In this case, you should create adapter classes that wrap up the RMI protocol; this lets your existing code use a local protocol when making a method call on a remote object. Since an adapter class wraps up another object, it is frequently referred to as a wrapper class.
A simple example
The example application for this article has a class called StatisticsCalculator that performs statistical calculations. Given a Vector of numbers, StatisticsCalculator provides methods that calculate statistical values like standard deviation, median, mode, and mean. A snippet of the code is shown in Listing 1. (See Resources for the complete source code.)
public class StatisticsCalculator
{
private Vector values_;
public void setValues(Vector values)
{
values_ = values;
}
public double getStandardDeviation()
{
if ((values_ == null) || (values_.size() <= 1))
return 0;
.
.
.
}
}
Listing 1. The StatisticsCalculator class
A typical usage of the StatisticsCalculator class is shown in Listing 2.
StatisticsCalculator sc = new StatisticsCalculator();
Vector v = new Vector();
v.addElement(new Double(100));
v.addElement(new Double(200));
v.addElement(new Double(300));
sc.setValues(v);
System.out.println("Std Dev: " + sc.getStandardDeviation());
Listing 2. Client usage of a local StatisticsCalculator
If you run the StatisticsCalculator object on another system, the easiest way to access it on that remote system is with RMI. By following the standard steps for RMI support, you can create an interface that extends java.rmi.Remote . Let's call it RemoteStatisticsCalculatorService ; it is shown in Listing 3.
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Vector;
public interface RemoteStatisticsCalculatorService extends Remote
{
public static final String SERVICENAME =
"RemoteStatisticsCalculatorService";
public void setValues(Vector values) throws RemoteException;
public double getStandardDeviation() throws RemoteException;
}
Listing 3. The RemoteStatisticsCalculatorService interface
At this point, some people would modify the StatisticsCalculator class to directly implement the RemoteStatisticsCalculatorService . Having read Becker's article, though, I will implement the RemoteStatisticsCalculatorService interface with a wrapper class that provides remote access to a StatisticsCalculator object. The result, RemoteStatisticsCalculatorServer , is shown in Listing 4. It includes a main() method that creates the object being wrapped and passes it to the constructor.
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Enumeration;
import java.util.Vector;
public class RemoteStatisticsCalculatorServer
extends UnicastRemoteObject
implements RemoteStatisticsCalculatorService
{
// object being wrapped
private StatisticsCalculator calc_;
public RemoteStatisticsCalculatorServer(StatisticsCalculator calc)
throws RemoteException
{
calc_ = calc;
}
public void setValues(Vector values) throws RemoteException
{
calc_.setValues(values);
}
public double getStandardDeviation() throws RemoteException
{
return calc_.getStandardDeviation();
}
public static void main(String[] args)
{
try
{
// start the registry
Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
// create a StatisticsCalculator, and then bind our
// server object to the service
// name RemoteStatisticsCalculatorServer
registry.rebind(RemoteStatisticsCalculatorService.SERVICENAME,
new RemoteStatisticsCalculatorServer(new StatisticsCalculator()));
}
catch (RemoteException e)
{
System.out.println(e);
}
}
}
Listing 4. The StatisticsCalculator server program
With the RMI server program complete, you can finally change the code that uses a local StatisticsCalculator object. This program will now be an RMI client that attempts to establish a remote connection with an object that implements the RemoteStatisticsCalculatorService . This client's code is shown in Listing 5.
try
{
// find the registry
Registry remoteRegistry = LocateRegistry.getRegistry(rmiHostName_);
// get a reference to the remote calculator
RemoteStatisticsCalculatorService sc =
(RemoteStatisticsCalculatorService)
remoteRegistry.lookup(RemoteStatisticsCalculatorService.SERVICENAME);
Vector v = new Vector();
v.addElement(new Double(100));
v.addElement(new Double(200));
v.addElement(new Double(300));
sc.setValues(v);
System.out.println("Std Dev: " + sc.getStandardDeviation());
}
catch (NotBoundException e)
{
System.out.println(e);
}
catch (RemoteException e)
{
System.out.println(e);
}
Listing 5. Client usage of a remote StatisticsCalculator
Much has changed between Listing 2 and Listing 5. In addition to adding a call to remoteRegistry.lookup() , the RMI protocol requires adding try/catch blocks for the RMI exceptions that can be thrown. So everywhere you use StatisticsCalculator in the existing code, you must add this remote exception handling. This can be a considerable burden, which you can avoid by using the Adapter pattern for the RMI client code. The first step is to define a second interface that does not extend java.rmi.Remote . I'll call it StatisticsCalculatorService ; it is shown in Listing 6.
import java.util.Vector;
public interface StatisticsCalculatorService
{
public void setValues(Vector values);
public double getStandardDeviation();
}
Listing 6. The StatisticsCalculatorService interface
RMI is not mentioned in the StatisticsCalculatorService interface -- it just provides an abstraction of the functionality in the original StatisticsCalculator class. Now you can define an Adapter class for the client that wraps the RMI functionality for you. I'll call it StatisticsCalculatorAccess , since it provides access to a StatisticsCalculatorService .
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Vector;
public class StatisticsCalculatorAccess implements StatisticsCalculatorService
{
private String rmiHostName_ = "localhost";
// the remote calculator
private RemoteStatisticsCalculatorService remoteCalculator_;
public StatisticsCalculatorAccess()
{
try
{
// find the registry
Registry remoteRegistry = LocateRegistry.getRegistry(rmiHostName_);
// get a reference to the remote calculator
remoteCalculator_ =
(RemoteStatisticsCalculatorService)
remoteRegistry.lookup(RemoteStatisticsCalculatorService.SERVICENAME);
}
catch (NotBoundException e)
{
System.out.println(e);
}
catch (RemoteException e)
{
System.out.println(e);
}
}
public void setValues(Vector values)
{
try
{
remoteCalculator_.setValues(values);
}
catch (RemoteException e)
{
System.out.println(e);
}
}
public double getStandardDeviation()
{
double retVal = 0;
try
{
retVal = remoteCalculator_.getStandardDeviation();
}
catch (RemoteException e)
{
System.out.println(e);
}
return retVal;
}
}
Listing 7. An adapter for local access to a remote StatisticsCalculator
To modify your existing code to use StatisticsCalculatorAccess , you must change only one line, as shown in Listing 8.
StatisticsCalculatorService sc = new StatisticsCalculatorAccess() ;
Vector v = new Vector();
v.addElement(new Double(100));
v.addElement(new Double(200));
v.addElement(new Double(300));
sc.setValues(v);
System.out.println("Std Dev: " + sc.getStandardDeviation());
Listing 8. Client usage of the StatisticsCalculatorAccess adapter
If you compare Listing 8 with Listing 2, you will see that only the first line is different, because the StatisticsCalculatorAccess class is handling the RMI details. This lets you partition the application with RMI while minimizing the impact on your existing code. Also, placing the RMI code in a wrapper class will result in a smaller impact if you eventually stop using RMI.
You can even take the idea a couple of steps further. Suppose that under some circumstances,
Read
Tutorial at: Click here to view the tutorial
Rate Tutorial: Retrofit existing
applications
with RMI - JavaWorld January 2001
View Tutorial: Retrofit existing
applications
with RMI - JavaWorld January 2001
Related
Tutorials:
Saving and retrieving
objects with Java
Saving and retrieving
objects with Java |
Design networked
applications in
RMI using the Adapter design pattern
Design networked
applications in
RMI using the Adapter design pattern |
Accelerate your RMI
programming
Accelerate your RMI
programming |
Integrate EJBs with CORBA
Integrate EJBs with CORBA |
J2EE or J2SE?
JNDI works with both
J2EE or J2SE?
JNDI works with both |
Create your own type 3 JDBC driver, Part 2
Create your own type 3 JDBC driver, Part 2 |
Check out three
collections libraries
Check out three
collections libraries |
Sun boosts
Sun boosts enterprise Java |
TRMI
TRMI |
Jini Starter Kit 2.0 tightens Jini's security framework
Jini Starter Kit 2.0 tightens Jini's security framework |
Good
introduction
Good
introduction |
Once again, only
introduction
Once again, only
introduction |
RMI, Dynamic Proxies, and the Evolution of Deployment
RMI, Dynamic Proxies, and the Evolution of Deployment
Dynamic Generation of Stub Classes
This release adds support for the dynamic generation of stub classes at runtime, obviating the need to use the Java Remote Method Invocation (Java RMI) stub compi |
Ubik
Overview
Ubik aims to provide a set of distributed computing APIs that complement Java's current "official" offerings - such as EJB and Jini. The main API of the Ubik project is a RMI-like framework that allows to easily and transparently perform method |
ClassRemoter
ClassRemoter is a remote class wrapper generator for use with RMI. It can create remote wrappers for existing java classes and interfaces, that would not normally be used with RMI. The wrappers may then be used to access the public methods. |
Java Server Pages Dynamically Generated Web Content.
JavaServer PagesTM (JSP TM) technology allows Web developers and designers to rapidly develop and easily maintain, information-rich, dynamic Web pages that leverage existing business systems. |
Java RMI Tutorial
This is a brief introduction to Java Remote Method Invocation (RMI). Java RMI is a mechanism that allows one to invoke a method on an object that exists in another address space.The other address space could be on the same machine or a different one. The |
JLAN Server v3.5 - Database Filesystems
*JLAN Server* is a high performance Java based file server supporting Windows file sharing (SMB/CIFS), NFS and FTP protocols.
|
NetBeans IDE 4.1
Out-of-the-box support for J2EE 1.4 and Web Services. Check out what early access release 2 can do for you! |
New Technical Articles: 64-bit Programming on Solaris 10 OS for x86 Platforms
Four technical articles describe the new Sun Studio 10 software's 64-bit programming features on the Solaris 10 OS for x86 and AMD64 platforms. Important issues regarding the AMD64 ABI (Application Binary Interface), debugging, migration to 64-bits, and p |
|
|
|