RMI-IIOP in the enterprise

RMI-IIOP in the enterprise

An introduction to running RMI over IIOP


Level: Intermediate

Damian Hagge ([email protected]), 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.



Back to top


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.



Back to top


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.



Back to top


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.



Back to top


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?



Back to top


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.



Back to top


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:

  1. Download the source file.

  2. javac all the files by typing javac *.java.

  3. Run rmic on the server class with the IIOP flag: rmic -iiop Server.

  4. Start the server: on Windows, type start java Server.

  5. Start the client: on Windows, type start java Client.



Back to top


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.



Back to top


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 [email protected].


From: http://www.ibm.com/developerworks/library/j-rmi-iiop/

你可能感兴趣的:(java,object,server,serialization,interface,reference)