Level: Intermediate Damian Hagge (Hagge@uk.ibm.com), Software Engineer, IBM
12 Mar 2002 RMI and CORBA are often seen as competing technologies, since both provide transparent access to remotely distributed objects. But the two technologies are actually complementary, in that each one has strengths that can address the weaknesses of the other. The marriage of RMI and CORBA has resulted in RMI-IIOP, a cornerstone of server-side Java development for the enterprise. In this article, Java developer Damian Hagge offers a brief introduction to RMI-IIOP, then shows you how to build and run a simple, Java-based RMI-IIOP client/server application. See for yourself how well RMI works over IIOP.
IBM and Sun Microsystems launched a joint initiative in 1997 to promote the advancement of
Java as an enterprise-development technology. In particular, the two companies focused on
how Java could be used as a server-side language, producing
enterprise-level code that could be incorporated into existing architectures.
What was needed was a remote transport technology that combined the small footprint of Java's RMI
(Remote Method Invocation) and the sturdiness of the more mature CORBA (Common Object
Request Broker Architecture) technology. Out of this need was born RMI-IIOP, which
has helped propel the Java language into its current position as the leading
language for server-side enterprise development. In this article, I'll provide a basic introduction to RMI-IIOP, with the goal of getting you started using the technology in your enterprise development solutions.
In order to explain what RMI-IIOP really is I think it's important to present
information about CORBA and RMI that you might not find in your typical
introduction to either technology. If you're unfamiliar with the basics of either
CORBA or RMI, I suggest you go through some introductory information
before you proceed. See Resources for a selection
of articles and tutorials. Before I talk specifically about RMI-IIOP, we'll take a look at the mechanisms that
CORBA and RMI use to marshall requests. CORBA will be our primary example, because RMI-IIOP
marshalling is based on the CORBA transport protocol (IIOP).
We'll go over the basic functions of the transport protocol and ORB (object request broker) in
sending a request, locating a remote object, and transporting that object
over a network. Remote object transport
When a CORBA request is marshalled, it is done using the IIOP protocol. Simply put,
IIOP represents the elements of any IDL (Interface Definition Language)
construct in a standardized format as a series of bytes. So, assume that a Java
client is dispatching a CORBA request to a C++ server. The client application
has a reference to the remote object in the form of a Java interface, and it
invokes an operation on that interface. Under the hood, the interface invokes
its corresponding implementation of the operation, which will be in the stub
(which you will have generated from IDL using idlj). The stub dispatches the method call down
into the ORB, which consists of two parts: the
client ORB and the server ORB. The client ORB's job is to marshall requests onto the network
destined for a specific location. The server ORB's job is to listen for requests
coming off the network and to convert them into method calls that a language implementation can
understand. For a more in-depth discussion of the role of a CORBA ORB, refer to the Resources section. After the stub dispatches the method call, the client ORB converts the request, including all
parameters, into a standardized byte format, in this case IIOP. The request is
then sent out on the wire to the server ORB, which should be listening for the
incoming request. The server-side ORB will read in the bytes of
data and convert the request to something meaningful to a C++ server implementation.
The C++ server method will do its thing (that is, invoke the requested method) and
return the result to the client over IIOP, using the same mechanism. RMI handles requests similarly, but uses JRMP (Java Remote Messaging Protocol) as
its transport protocol. And, of course, RMI transport involves the serialization of
Java objects.  |
Differences between CORBA and RMI
- CORBA runs over the IIOP protocol; RMI uses JRMP.
- CORBA is language independent; RMI is purely Java-to-Java.
- RMI uses JNDI to locate remote objects; CORBA uses CosNaming.
- RMI serializes objects; CORBA does not.
|
|
Remote object location
CORBA uses the CosNaming naming service to locate remote objects. CosNaming provides
a framework for a name server to hold bindings (or references) to CORBA server
processes. When a CORBA client sends a
CosNaming request to the name service for a server process of a given
name, the name service returns an interoperable object reference (IOR) for
that process. The client then uses that IOR to communicate directly with the
server process. The IOR contains information about the server process, such as
its location. One of the downsides of the CosNaming service is
that an IOR is illegible to humans -- at least those of us who
don't have cyborg brains! RMI, on the other hand, is a bit more user friendly.
It locates remote objects using a registry (much like a naming service)
running on top of JNDI. The RMI
registry uses the Java Reference object, which is composed
of RefAddr objects, to identify and locate remote objects.
These Java objects are more user friendly than an IOR. COBRA has fairly recently incorporated the Interoperable Naming Service (INS)
into its object-location scheme. INS runs over CosNaming, using human-readable URLs
as its object locations. INS doesn't make use of a naming service; instead,
it sends invocations directly to the specified URL. See Resources
to learn more about INS.
RMI versus CORBA
So, which is better: CORBA or RMI? The answer depends on what you
want to do. CORBA is a big tried-and-tested architecture running over an
industry-standard third- or fourth-generation protocol. When you take into account
all the add-ons CORBA offers (such as transaction processing, interceptors for security, event
channels, and more), CORBA seems to be the answer for enterprise applications.
The big drawback with CORBA is that it's complicated. Developers generally have
a steep training curve to reach CORBA fluency. RMI, on the other hand, is fairly easy to learn. It's quite simple to create
a client/server implementation, bind to a registry and remote object, and
invoke and/or receive a request using RMI. RMI also has a
much smaller footprint than CORBA, since JRMP is a considerably less costly
protocol than IIOP. But RMI lacks CORBA's industrial-strength add-ons,
and is a purely Java-based mechanism. So, what we really want is
the flexibility and ease-of-use of RMI coupled with the enterprise-readiness of
CORBA, right? Enter RMI-IIOP.  |
Why RMI-IIOP?
- RMI-IIOP combines the strength of CORBA with the flexibility of RMI.
- RMI-IIOP is easy for developers to use and integrates easily into most corporate infrastructures.
|
|
Overview of RMI-IIOP
RMI-IIOP lets you run RMI invocations over IIOP with very little modification. With
RMI-IIOP you can write straightforward Java code and also use the rich
suite of enterprise features that CORBA has to offer. Furthermore,
the code is flexible enough to run over either RMI or IIOP. This means your
code can run in a pure Java environment when a small footprint and
flexibility are key or integrate into an existing CORBA infrastructure
with minimal code changes. One of the very powerful features of RMI-IIOP is that it lets you
write pure-Java client/server implementations
without losing the flexibility of RMI class serialization. RMI-IIOP
accomplishes this by overriding Java serialization and converting the Java
classes to IIOP on the wire. On the other side, the Java class is read off the
wire as IIOP and a new instance of the class is created (using reflection) with
all its member's values intact -- and voila: Java serialization over
IIOP! For RMI-IIOP to achieve transparent object location, ORB vendors
have historically used the Java CosNaming service provider (or plug-in in
layman's terms). The plug-in acts beneath the JNDI API to access the CORBA naming
service. Although I do not have the space to go into the reasons here, this naming solution isn't ideal.
As a result, numerous vendors -- particularly application server
vendors -- have developed proprietary object location mechanisms for RMI-IIOP. RMI-IIOP also supports INS as an extension to the Java CosNaming service. Because I believe that INS will set the direction of object location in the future, the code example we'll work with in this article uses INS.
Note: Because Sun has not yet fully complied with the OMG INS standard and has not exposed
register_initial_reference on the org.omg.CORBA.ORB interface, the source code presented in this article will not work with the Sun JDK. You will need to use the IBM Developer Kit for Java technology, version 1.3.1 or greater. I have, however, created a Sun-compatible example that uses a naming service, which you can download from the Resources section.
Do-it-yourself RMI-IIOP
Enough talk, let's code! In the following sections we'll build a simple, Java-based
client/server RMI-IIOP application. This application consists of three parts: the RMI interface,
the server application, and the client application. The example features
Java serialization over IIOP, so you can see how a Java class can be instantiated by the client,
passed to the server, altered by the server, and passed back to the client with the alterations
intact.
Part 1: Define the interface
Under RMI-IIOP we can choose to define our interface using RMI or IDL. Because we want
to see how RMI runs over IIOP, we'll define the example interface using RMI.
Listing 1 is the RMI interface for our simple example: Listing 1. RMIInterface.java
/*
* Remote interface
*/
public interface RMIInterface extends java.rmi.Remote {
public String hello() throws java.rmi.RemoteException;
public SerClass alterClass(SerClass classObject)
throws java.rmi.RemoteException;
}
|
The RMIInterface defines a
hello() method and an alterClass(SerClass) method. This method
takes SerClass, a Java
class that implements Serializable, and returns
a class of the same type. SerClass is a simple class with a few members
and corresponding getter methods. The methods are shown in Listing 2: Listing 2. SerClass.java
/**
* This class is intended to be serialized over RMI-IIOP.
*/
public class SerClass implements java.io.Serializable {
// members
private int x;
private String myString;
// constructor
public SerClass(int x, String myString)
throws java.rmi.RemoteException {
this.x=x;
this.myString=myString;
}
// some accessor methods
public int getX() { return x;}
public void setX(int x) { this.x=x; }
public String getString() { return myString; }
public void setString(String str) { myString=str; }
}
|
That's all there is to our simple interface. Now let's check out the
server class.
Part 2: Build the server
We'll use a server class (Server.java) that both acts
as the RMIInterface implementation class and contains the main method to
start our service. Server.java extends javax.rmi.PortableRemoteObject. In this way, it contains
all the functionality it needs to bind itself as a Remote interface to the ORB and begin listening for
requests. Listing 3 is the code for the server: Listing 3. Server.java
/*
* Simple server
*/
import java.util.*;
import java.rmi.Remote;
import java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject;
import javax.rmi.CORBA.Tie;
import javax.rmi.CORBA.Util;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.*;
import org.omg.PortableServer.Servant;
import org.omg.CORBA.ORB;
public class Server extends PortableRemoteObject
implements RMIInterface {
// must explicitly create default constructor
// to throw RemoteException
public Server() throws RemoteException {
}
// implementation of RMIInterface methods
public String hello() throws RemoteException {
return "Hello there!";
}
public SerClass alterClass(SerClass classObject)
throws RemoteException {
// change the values of SerClass and return it.
// add 5 to X
classObject.setX(
classObject.getX() + 5 );
// alter the string
classObject.setString(
classObject.getString() + " : I've altered you" );
return classObject;
}
public static void main(String[] args) {
try {
// create the ORB passing in the port to listen on
Properties props = new Properties();
props.put("com.ibm.CORBA.ListenerPort","8080");
ORB orb = ORB.init(args, props);
// instantiate the Server
// this will automatically call exportObject(this)
Server s = new Server();
// now get the Stub for our server object -
// this will be both
// a remote interface and an org.omg.CORBA.Object
Remote r=PortableRemoteObject.toStub(s);
// register the process under the name
// by which it can be found
((com.ibm.CORBA.iiop.ORB)orb).
register_initial_reference("OurLittleClient",
(org.omg.CORBA.Object)r);
System.out.println("Hello Server waiting...");
// it's that easy -
// we're registered and listening for incoming requests
orb.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
Er, what's happening here?
The server application is code heavy, so let's break it down. First of all, as
previously mentioned, the Server class implements
RMIInterface and provides implementations for all of
its methods. You can see the implementations of the RMIInterface's hello() and alterClass(SerClass) methods at the beginning of the code.
The hello() method simply returns the String "Hello
there!" The alterClass(SerClass) method takes a
SerClass object, alters the member values, and
returns the new object -- all using RMI-IIOP.
Server.java's main method initializes an ORB. It passes in the com.ibm.CORBA.ListenerPort property set to 8080 as a parameter. This will
cause the ORB to listen for incoming requests on port 8080. Note that
com.ibm.CORBA.ListenerPort
is a proprietary IBM property. If you want to run this code on another vendor's
ORB you should refer to that vendor's documentation for the appropriate property. (Sun uses
com.sun.CORBA.POA.ORBPersistentServerPort, but it only works if
you're using POA (portable object adapter) servants.) With the ORB initialized, the main method moves on to instantiate a
Server object. Because the server object is
also a PortableRemoteObject, the default constructor
automatically makes a call to exportObject(this).
The object is now ready to receive remote calls. Next, we need to register the object with a call to ORB.register_initial_reference(String,orb.omg.CORBA.Object).
For this, we need to have a reference to our server object
as an org.omg.CORBA.Object. The call
PortableRemoteObject.toStub(s) accomplishes this, because
the returned object implements both java.rmi.Remote
and org.omg.CORBA.Object. The returned org.omg.CORBA.Object object is then
registered with the server-side ORB as "OurLittleClient". To ensure that an INS
request can locate the object, we use the
registration call register_initial_reference. When an
INS call comes into an ORB, the ORB will look for an object that has been registered
under the name being requested. Since we registered the object as "OurLittleClient",
we'll know what object the client is looking for when an INS call
comes into our server ORB for "OurLittleClient". Finally, I'm sure that you've noticed that we cast the ORB
to a com.ibm.CORBA.iiop.ORB. Because Sun
has not yet exposed register_initial_reference on
the org.omg.CORBA.ORB interface,
the IBM SDK cannot expose it either. Therefore we must cast our ORB into an IBM ORB.
Future versions (post 1.4.0) of the JDK will probably not require this cast, as Sun's
compliance with the OMG evolves. And that's it! Easy-peasy -- well, sort of. Our server is now
awaiting incoming client INS requests. But what about that client?
Part 3: Build the client
The code for the client application is shown in Listing 4: Listing 4. Client.java
/*
* Client application
*/
import javax.rmi.PortableRemoteObject;
import org.omg.CORBA.ORB;
public class Client {
public static void main(String[] args) {
try {
ORB orb = ORB.init(args, null);
// here's the URL for the local host
String INSUrl =
"corbaloc:iiop:1.2@localhost:8080/OurLittleClient";
// get the reference to the remote process
org.omg.CORBA.Object objRef=orb.string_to_object(INSUrl);
// narrow it into our RMIInterface
RMIInterface ri =
(RMIInterface)PortableRemoteObject.narrow(objRef, RMIInterface.class);
// call the hello method
System.out.println("received from server: "+ri.hello()+"\n");
// try RMI serialization
SerClass se = new SerClass(5, "Client string! ");
// pass the class to be altered on the server
// of course behind the scenes this class is being
// serialized over IIOP
se = ri.alterClass(se);
// now let's see the result
System.out.println("Serialization results :\n"+
"Integer was 5 now is "+se.getX()+"\n"+
"String was \"Client String! \"
now is \""+se.getString()+"\"");
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
How the client code breaks down
The client code is somewhat simpler than the server code. We initialize an ORB and
make a call to string_to_object(String), where the
string is our INS URL. Constructing the INS URL is relatively simple: first we specify
that we're using a corbaloc URL (see Resources) and
the IIOP protocol version 1.2. Next we add in the host name (www.whatever.com) and the port
on which to connect. And, finally, we specify the name of the service we're looking for.
The resulting INS URL is corbaloc:iiop:1.2@localhost:8080/OurLittleClient. When we pass this URL into ORB.string_to_object(String)
the ORB will dispatch a request to the specified server for the requested service.
Assuming that everything is working as it should, the ORB will receive back
an object reference (really an IOR) for the service. We then narrow the object
reference into something we can use, namely an RMIInterface, and we're ready to start calling
methods. After we've called the simple hello method (which should require no explanation),
we can begin exploring RMI-IIOP's serialization feature. First, we create a
SerClass, a serializable Java class, and initialize its member
variables. Next, we pass this class into our method, which writes it out over
IIOP to the server. The server reads the class in and re-creates it as a server-side
Java object, alters its member values, and returns it (using IIOP) as the return value
of our method. When we receive the re-created object after the remote method call, we see that
its members have indeed been changed by the server. And it's that simple:
Java serialization over IIOP.
Part 4: Run the example
Note that the example we've created here must be run with an IBM Developer Kit for Java technology, version 1.3.1 or greater. If you prefer to use a Sun JDK please download the Sun-specific source code, which should be run with a Sun 1.4.0 JDK or greater. This source code includes a readme.txt file that explains the differences between the IBM SDK and the Sun JDK versions. If you don't have an IBM Developer Kit for Java technology (and you want one) download one now; they're free. Here's how to run the example:
- Download the source file.
- javac all the files by typing
javac *.java.
- Run
rmic on the server class with the IIOP flag:
rmic -iiop Server.
- Start the server: on Windows, type
start java Server.
- Start the client: on Windows, type
start java Client.
A note about RMI-IIOP and EJB components
The EJB 2.0 specification states that EJB components must be able
to run over both RMI and RMI-IIOP. The addition of RMI-IIOP as an on-the-wire
protocol for EJB components has greatly assisted the integration of the J2EE environment
into existing corporate infrastructures, most of which are quite
CORBA intensive. But it also poses some problems. The short version is that integrating custom-built components and EJB components requires
you (the developer) to deal with the plumbing, which would otherwise be
abstracted for you by the EJB architecture. As of yet, there is no simple solution
to this problem, and there may never be. The solution could come from
evolving technologies such as Web services, but that is as yet unknown.
Conclusion: Where to go from here
It is my hope that this article has shown you how easy it is to build and run RMI-IIOP
client/server applications. You can play around with the example we used by replacing
the client or server with pure CORBA, though doing so would cut
Java serialization out of your application. If you're thinking of using RMI-IIOP in a
CORBA environment it would be worthwhile to see how IDL maps into Java and
vice versa. If you're thinking of deploying RMI-IIOP in an insecure
environment (that is, not your own PC) it would be a good idea to look into
CORBA security features such as interceptors and the CORBA security model, as
well as other CORBA enterprise features such as transaction processing.
All of CORBA's rich features are available to you when you run RMI-IIOP.
Resources - Download the IBM-compatible source for this article.
- Download the Sun-compatible source for this article.
- The IIOP protocol is defined by the Object Management Group (OMG),
which also develops and maintains the CORBA specification.
- To learn more about CORBA, visit the OMG's CORBA Web site.
- To learn more about RMI, visit the RMI homepage.
- The Java Developer Connection offers an INS tutorial that also provides a general introduction to the naming services, the CosNaming service, and the
corbaloc URL format.
- Want to expand your Java technology options? See the complete listing of IBM Developer Kits for Java technology.
- For a further introduction to programming with RMI and CORBA, take the tutorial, "Java distributed objects: Using RMI and CORBA" (developerWorks, November 2002).
- If you're new to programming with EJB components, you might want to take the tutorial,
"Getting started with Enterprise JavaBeans technology" (developerWorks, April 2003).
- To learn more about the relationship between EJB technology and CORBA, see Ken Nordby's
"Deploying and using Enterprise JavaBeans components" -- Part 3 in a three-part introduction to EJB technology
(developerWorks, July 2000).
- RMI-IIOP's lead architect offers his perspective on the technology in
the JavaWorld article, "RMI over IIOP"
(JavaWorld, December 1999).
- For a well-rounded perspective on RMI-IIOP, see the ServerSide.com article "RMI/IIOP, nice idea but
the reality is turning out to be different," which focuses
on what RMI-IIOP doesn't deliver (TheServerside.com).
- You'll find hundreds of articles about every aspect of Java programming in
the
developerWorks Java technology zone.
About the author  | |  | Damian Hagge works at IBM's Hursley
Development Laboratories. Although he currently lives in the UK, he
originated five hours counterclockwise from his present location just
above the forty-ninth parallel (that's Canada for those whose geography
isn't up to scratch). Aside from working as a developer on the IBM Java
ORB team and writing long articles on obscure subject matter, Damian is
a veritable encyclopedia of useless facts; for instance, did you know
that Iceland supports a self-sustaining banana-growing industry? Damian also
does some normal stuff like power lifting (if you call that normal!). You can
contact him at Hagge@uk.ibm.com.
|
Rate this page
|