RMI over IIOP with JacORB
By Don Busch, OCI Principal Software Engineer
April 2004
Introduction
Java Remote Method Invocation (RMI) is a popular mechanism for constructing distributed, object-oriented computing systems using the Java programming language. Utilizing RMI, a distributed application can be developed completely with Java. RMI has the advantage of making distributed computing available to a large number of Java programmers without a significant learning curve. RMI's underlying wire protocol is the Java Remote Messaging Protocol (JRMP).
RMI, however, has its limitations. RMI's primary limitation is the same as its greatest advantage, namely that all coding is done in Java. That's not a problem for a pure Java application. It is a problem for an application requiring integration with a code base of legacy C++ or COBOL. It's also a problem for an application that would like to take advantage of scripting languages such as Perl or Python for distributed administration tasks such as process monitoring. It's even a problem for a pure Java application with a long lifespan; eventually the system may have distributed components written in other languages. Most significant Java applications fall into one of those three categories.
Common Object Request Broker Architecture (CORBA)
The Object Management Group's (OMG) Common Object Request Broker Architecture (CORBA) is a specification for a distributed, object-oriented computing infrastructure. The OMG makes the CORBA specification freely available to anyone that wishes to implement it in any supported language. As a result, there are a wide variety of compliant CORBA implementations for many hardware platforms, operating systems, and languages.
Like RMI, CORBA's goal is to enable the development of distributed, object-oriented computing systems. CORBA extends its reach beyond Java, into languages such as C++, C, Ada, Smalltalk, Lisp, Python and Perl. CORBA's COM-to-CORBA bridge goes beyond that, extending CORBA's reach into languages such as Visual Basic, Delphi, and PowerBuilder.
CORBA's server-side Portable Object Adapter (POA) enables a server-side developer to separate the concept of an object reference, or proxy, as seen by the client from that of a servant, or implementation object, as seen by the server. This additional level of indirection places a great deal of power in the hands of a server-side developer. Some examples are as follows:
- Using one servant to handle requests from many unique object references
- Creating object references that are valid beyond the lifetime of the server process
- Activating new object references on demand
In other words, there are server-side benefits to using CORBA in addition to language independence.
The CORBA specification specifies a wire protocol called the Internet Inter-ORB Protocol (IIOP). This protocol is provided in every compliant implementation of the CORBA specification regardless of language.
The Best of Both Worlds
RMI over IIOP combines the ease of use of the Java-based RMI programming model with the flexibility and power of CORBA and the language-independent IIOP protocol. The reference implementation of IIOP was developed jointly by Sun and IBM in 1999, and it has been shipped as an integral part of Java 2 since J2SE 1.3.
RMI over IIOP also enables a distributed computing system to be developed completely in Java. In fact, no client-side code changes to existing JNDI-compliant RMI clients are required. Distributed components written on other languages can be plugged into an RMI/IIOP system by using the rmic
compiler to generate CORBA Interface Definition Language (IDL) code for the Java interface.
Figure 1 shows that an RMI/IIOP server offers maximum flexibility in client deployment. RMI/JRMP clients, RMI/IIOP clients, and CORBA clients can communicate with a properly configured RMI/IIOP server.
RMI over IIOP requires a CORBA implementation for the IIOP transport. JacORB is a popular, high-performance, open source Java ORB with a wide array of users. For example, JBoss uses JacORB as its RMI/IIOP engine. JacORB is commercially supported.
Building an RMI/IIOP Server
The examples that follow use J2SE 1.5.0 beta 1, Ant 1.6.1, and JacORB 2.1.
The first example is a simple RMI client and server. Subsequent examples show server modifications to enable IIOP protocol support. No client-side code changes are necessary. The final example demonstrates a server publishing its distributed objects via both IIOP and JRMP, allowing legacy RMI/JRMP clients to use the server with no knowledge that it is also available via IIOP.
Although J2SE 1.5.0 supports dynamic generation of RMI stubs and skeletons for the JRMP protocol, this example does not take advantage of it. Manual compilation of RMI/JRMP stubs permits compatibility with RMI/JRMP clients deployed with older versions of Java.
First Example: An RMI Client and Server
The example implements a server for Hotel
. Clients can check into the Hotel
to get a room, find the rate of the room, and get the room's balance. The Hotel
and GuestRoom
interfaces provide the contract with our client:
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- public interface HotelInterface extends Remote {
- public String getName() throws RemoteException;
- public short getNumberOfRooms() throws RemoteException;
- public GuestRoomInterface checkIn(short numNights)
- throws RemoteException;
- }
- import java.rmi.Remote;
- import java.rmi.RemoteException;
-
- public interface GuestRoomInterface extends Remote {
- public short getRoomNumber() throws RemoteException;
- public float getRate() throws RemoteException;
- public float getBalance() throws RemoteException;
- public void checkOut() throws RemoteException
- }
The implementation of the Hotel
interface extends PortableRemoteObject
. The most important part of the implementation itself is the checkIn()
method, which creates new GuestRoomImpl
remote objects on demand.
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- import javax.rmi.PortableRemoteObject;
-
- public class HotelImpl extends PortableRemoteObject
- implements HotelInterface
- {
- private static final int NUMBER_OF_ROOMS = 500;
- private GuestRoomImpl[] rooms = new GuestRoomImpl[NUMBER_OF_ROOMS];
-
- public HotelImpl() throws RemoteException
- {
- }
-
- public String getName() throws RemoteException
- {
- return "Hotel California";
- }
- public short getNumberOfRooms() throws RemoteException
- {
- return NUMBER_OF_ROOMS;
- }
- public GuestRoomInterface checkIn(short numNights)
- throws RemoteException
- {
- // Determine if a room is available.
- GuestRoomInterface availableRoom = null;
-
- short nextRoom = 0;
- while ( nextRoom < NUMBER_OF_ROOMS
- && rooms[nextRoom] != null
- && !rooms[nextRoom].isAvailable() ) {
- ++nextRoom;
- } // end of while ()
-
- if ( nextRoom < NUMBER_OF_ROOMS ) {
- if ( rooms[nextRoom] == null ) {
- rooms[nextRoom] = new GuestRoomImpl(nextRoom);
- } // end of if ()
-
- rooms[nextRoom].checkIn( numNights );
- availableRoom = rooms[nextRoom];
- } // end of else
-
- return availableRoom;
- }
- }
The implementation of the GuestRoom
interface also extends PortableRemoteObject
. The implementation itself is not important.
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- import javax.rmi.PortableRemoteObject;
-
- public class GuestRoomImpl extends PortableRemoteObject
- implements GuestRoomInterface
- {
- private boolean isAvailable = true;
- private short roomNumber;
- private float rate = 100.0f;
- private short numNights;
- private float balance = 0.0f;
-
- GuestRoomImpl( short roomNumber ) throws RemoteException
- {
- this.roomNumber = roomNumber;
- }
-
- public void checkIn( short numNights )
- {
- System.out.println( "Checking in to room " + this.roomNumber );
- this.isAvailable = false;
- this.numNights = numNights;
-
- this.balance = this.numNights * this.rate;
- }
-
- public short getRoomNumber() throws RemoteException
- {
- return this.roomNumber;
- }
- public static float getDefaultRate()
- {
- return 100.0f;
- }
- public float getRate() throws RemoteException
- {
- return this.rate;
- }
- public short getNumNights() throws RemoteException
- {
- return this.numNights;
- }
- public float getBalance() throws RemoteException
- {
- System.out.println( "Getting balance of room " + this.roomNumber );
- return this.balance;
- }
- public void checkOut() throws RemoteException
- {
- System.out.println( "Checking out of room " + this.roomNumber );
- this.isAvailable = true;
- this.numNights = 0;
- this.balance = 0.0f;
- }
- public boolean isAvailable() {
- return this.isAvailable;
- }
- }
The HotelServer
creates a HotelImpl
and binds it into the JNDI naming service. The JNDI naming service is bound to the rmiregistry
at runtime.
- import javax.naming.Context;
- import javax.naming.InitialContext;
-
- public class HotelServer {
- public static void main(String args[]) {
- try {
-
- // Instantiate the Servant
- HotelImpl hotelImpl = new HotelImpl();
-
- // Publish the reference in the Naming Service
- // using JNDI API
- javax.naming.Context initialCtx = new InitialContext();
- initialCtx.rebind("Hotel California", hotelImpl );
-
- System.out.println( "Ready" );
- } catch (Exception e) {
- System.err.println("ERROR: " + e);
- e.printStackTrace(System.out);
- }
- }
- }
The client retrieves the remote Hotel
from JNDI and invokes upon it. The underlying wire protocol is the Java Remote Remote Messaging Protocol (JRMP). Use the PortableRemoteObject.narrow()
method on the object reference instead of a straight Java downcast.
- import javax.rmi.PortableRemoteObject;
- import javax.naming.Context;
- import javax.naming.InitialContext;
- import javax.naming.NamingException;
-
- public class HotelClient
- {
- public static void main( String[] args )
- {
- Context ic;
- Object objref;
- HotelInterface hi;
-
- try {
- ic = new InitialContext();
-
- // Get the Object reference from the Name Service
- // using JNDI call.
- objref = ic.lookup("Hotel California");
-
- // Narrow the object reference to the concrete type and
- // invoke the method.
- hi = (HotelInterface) PortableRemoteObject.narrow(
- objref, HotelInterface.class);
-
- // Invoke operations on the Hotel as if it were a local object.
- // Note that "name" attribute is accessed via "name()" method
- String hotelName = hi.getName();
- System.out.println("This is the Hotel " + hotelName );
-
- // Check into a room for 5 nights.
- short numNights = 5;
- GuestRoomInterface room = hi.checkIn(numNights);
- System.out.println( "Balance at checkout for room "
- + room.getRoomNumber()
- + " will be: "
- + room.getBalance() );
-
- } catch( Exception e ) {
- System.err.println( "Exception " + e + "Caught" );
- e.printStackTrace( );
- return;
- }
- }
- }
Before executing the client and server, start the rmiregistry
. Then, run the server and client in separate windows, using the RMI RegistryContextFactory
as the JNDI naming factory:
rmiregistry
java -Djava.naming.factory.initial=
com.sun.jndi.rmi.registry.RegistryContextFactory
HotelServer
java -Djava.naming.factory.initial=
com.sun.jndi.rmi.registry.RegistryContextFactory
HotelClient
Figure 2 shows the RMI/JRMP client and server.
Second Example: RMI/IIOP on the Server Side
The second example modifies the server to use RMI over IIOP as the wire protocol. The server uses the CORBA Portable Object Adapter for object activation. This requires code changes to both the server's main
method and to any part of the server that instantiates a remote object. In this example, the code in HotelServer.main()
and HotelImpl.checkIn()
must be modified. No client-side code changes are required.
First, the build process's execution of the rmic
compiler must generate IIOP stubs and skeletons rather than JRMP stubs and skeletons. The rmic
command-line options for this are -iiop -poa
. For example:
rmic -iiop -poa GuestRoomImpl
rmic -iiop -poa HotelImpl
There are order-of-compilation issues to be aware of. The HotelImpl
's implementation depends on the GuestRoomImpl
's IIOP stubs and skeletons. However, the rmic
compiler requires compiled Java classes to generate IIOP stubs and skeletons. To handle this, compile GuestRoomImpl
first, then execute rmic -iiop -poa GuestRoomImpl
to generate the GuestRoom
stubs and skeletons. Then, compile HotelImpl
and execute rmic -iiop -poa HotelImpl
to generate Hotel
stubs and skeletons. The Ant build files in the attached code samples illustrate this.
The RMI/IIOP HotelServer
main method follows. Code changes are highlighted.
- import javax.naming.Context;
- import javax.naming.InitialContext;
- import javax.rmi.CORBA.Util;
- import org.omg.CORBA.ORB;
- import org.omg.PortableServer.POA;
-
- public class HotelServer {
- public static void main(String args[]) {
- try {
- // create and initialize the ORB and POA
- ORB orb = ORB.init(args, null);
- POA rootPOA = org.omg.PortableServer.POAHelper.narrow(
- orb.resolve_initial_references("RootPOA"));
-
- // Instantiate the Servant and activate the Tie
- HotelImpl hotelImpl = new HotelImpl(rootPOA);
- _HotelImpl_Tie tie = (_HotelImpl_Tie)Util.getTie(hotelImpl);
- byte[] id = rootPOA.activate_object(tie);
-
- // Publish the reference in the Naming Service
- // using JNDI API
- javax.naming.Context initialNamingContext = new InitialContext();
- initialNamingContext.rebind("Hotel California",
- rootPOA.id_to_reference(id) );
-
- // activate the RootPOA, and run
- rootPOA.the_POAManager().activate();
-
- System.out.println( "Ready" );
- orb.run();
- } catch (Exception e) {
- System.err.println("ERROR: " + e);
- e.printStackTrace(System.out);
- }
- }
- }
Most of the code changes are boilerplate CORBA. The first two lines initialize the ORB and get the Portable Object Adapter (POA). These two lines appear in every CORBA server. The _HotelImpl_Tie
skeleton class is generated by the rmic
compiler, as explained above. The subsequent three lines create our servant, a HotelImpl
, and wrap it in the rmic
-generated _HotelImpl_Tie
skeleton through the javax.rmi.CORBA.Util
class. The activate_object()
call is the standard CORBA mechanism for activating a distributed object. The rootPOA.the_POAManager().activate()
and orb.run()
lines are also boilerplate CORBA code; they appear in the main program of every CORBA server.
The HotelImpl.checkIn()
method creates and activates GuestRoomImpl
servants, so it too requires modification.
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- import javax.rmi.PortableRemoteObject;
- import javax.rmi.CORBA.Util;
- import org.omg.PortableServer.POA;
- import org.omg.PortableServer.POAPackage.ServantAlreadyActive;
- import org.omg.PortableServer.POAPackage.WrongPolicy;
-
- public class HotelImpl extends PortableRemoteObject
- implements HotelInterface
- {
- private static final int NUMBER_OF_ROOMS = 500;
- private GuestRoomImpl[] rooms = new GuestRoomImpl[NUMBER_OF_ROOMS];
-
- private POA guestRoomPOA;
-
- public HotelImpl(POA guestRoomPOA)
- throws RemoteException
- {
- this.guestRoomPOA = guestRoomPOA;
- }
-
- public String getName() throws RemoteException
- {
- return "Hotel California";
- }
- public short getNumberOfRooms() throws RemoteException
- {
- return NUMBER_OF_ROOMS;
- }
- public GuestRoomInterface checkIn(short numNights)
- throws RemoteException
- {
- // Determine if a room is available.
-
- GuestRoomInterface availableRoom = null;
-
- short nextRoom = 0;
- while ( nextRoom < NUMBER_OF_ROOMS
- && rooms[nextRoom] != null
- && !rooms[nextRoom].isAvailable() ) {
- ++nextRoom;
- } // end of while ()
-
- if ( nextRoom < NUMBER_OF_ROOMS ) {
- if ( rooms[nextRoom] == null ) {
- rooms[nextRoom] = new GuestRoomImpl(nextRoom);
- } // end of if ()
-
- try {
- // activate GuestRoom
- activateGuestRoom( rooms[nextRoom] );
- }
- catch (Exception e) {
- // can't activate GuestRoom
- rooms[nextRoom] = null;
- System.err.println("ERROR: " + e );
- e.printStackTrace(System.err);
- return null;
- }
-
- rooms[nextRoom].checkIn( numNights );
- availableRoom = rooms[nextRoom];
- }
- return availableRoom;
- }
-
- protected void activateGuestRoom( GuestRoomImpl roomImpl )
- throws RemoteException, ServantAlreadyActive, WrongPolicy {
-
- _GuestRoomImpl_Tie tie
- = (_GuestRoomImpl_Tie)Util.getTie( roomImpl );
- byte[] id = guestRoomPOA.activate_object(tie);
- }
- }
Each call to checkIn()
potentially triggers the activation of a GuestRoom
. The activation of the GuestRoom
looks very similar to the activation of the Hotel
. The activateGuestRoom
method creates a GuestRoomImpl
servant, wraps it in a _GuestRoomImpl_Tie
skeleton, and activates it. The _GuestRoomImpl_Tie
class is generated by the rmic
compiler, as explained above.
The CORBA client and server do not use the rmiregistry
-- instead, they use the CORBA Naming Service. Either Sun's orbd
or JacORB's Naming Service is acceptable. Both are compliant with the OMG's CORBA Naming Service specification. This example uses JacORB's Naming Service. The examples assume that the JACORB_HOME
environment variable points to the root directory of the JacORB installation (e.g. C:/Java/JacORB_2_1
).
java -Djava.endorsed.dirs=%JACORB_HOME%\lib
-cp .;classes;%JACORB_HOME%\lib\jacorb.jar;%JACORB_HOME%\lib\idl.jar;%JACORB_HOME%\lib\logkit-1.2.jar
-Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB
-Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton
-DORBInitRef.NameService=
corbaloc:iiop:localhost:12026/StandardNS/NameServer-POA/_root
-DOAPort=12026
org.jacorb.naming.NameServer ns.ior
The server registers its Hotel
with the JacORB Naming Service.
java -Djava.endorsed.dirs=%JACORB_HOME%\lib
-cp .;classes;%JACORB_HOME%\lib\jacorb.jar;%JACORB_HOME%\lib\logkit-1.2.jar
-Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB
-Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton
-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
-Djava.naming.provider.url=
corbaloc:iiop:localhost:12026/StandardNS/NameServer-POA/_root
-DORBInitRef.NameService=
corbaloc:iiop:localhost:12026/StandardNS/NameServer-POA/_root
HotelServer
The client retrieves the Hotel
from the JacORB Naming Service and invokes upon it.
java -Djava.endorsed.dirs=%JACORB_HOME%\lib
-cp .;classes;%JACORB_HOME%\lib\jacorb.jar;%JACORB_HOME%\lib\logkit-1.2.jar
-Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB
-Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton
-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
-Djava.naming.provider.url=
corbaloc:iiop:localhost:12026/StandardNS/NameServer-POA/_root
-DORBInitRef.NameService=
corbaloc:iiop:localhost:12026/StandardNS/NameServer-POA/_root
HotelClient
Figure 3 shows the RMI/IIOP client and server. The diagram also and indicates that the same server can be used from a pure CORBA client, and the same client can access a pure CORBA server.
Third Example: Dual Export
The third example modifies the server to export its object references using both the IIOP and JRMP protocols. Again, no client code changes are required. In fact, a pure RMI client can be deployed with no knowledge that the server supports the IIOP protocol. Again, This requires code changes to both the HotelServer.main()
and HotelImpl.checkIn()
methods. This modified HotelServer.main()
uses the CORBA Naming Service directly rather than using the JNDI interfaces, although that is not a requirement.
The rmic
compilation step of the build process must generate both IIOP stubs and skeletons and JRMP stubs and skeletons. Thus the build process executes the rmic
compiler twice on each *Impl
. The attached code samples illustrate this.
The RMI/IIOP server's main method follows. Changes are highlighted. The main
creates a DualHotelImpl
servant rather than a HotelImpl
servant. The DualHotelImpl
extends the HotelImpl
and overrides its protected
activation method.
- import javax.naming.Context;
- import javax.naming.InitialContext;
- import javax.rmi.CORBA.Util;
- import org.omg.CORBA.ORB;
- import org.omg.PortableServer.POA;
- import java.rmi.server.UnicastRemoteObject;
- import java.util.Properties;
- import org.omg.CosNaming.NamingContextExt;
- import org.omg.CosNaming.NamingContextExtHelper;
-
- public class DualHotelServer {
- public static void main(String args[]) {
- try {
-
- // create and initialize the ORB and POA
- ORB orb = ORB.init(args, null);
- POA rootPOA = org.omg.PortableServer.POAHelper.narrow(
- orb.resolve_initial_references("RootPOA"));
-
- // Instantiate the Servant and activate the Tie
- HotelImpl hotelImpl = new DualHotelImpl(rootPOA);
- _HotelImpl_Tie tie = (_HotelImpl_Tie)Util.getTie(hotelImpl);
-
- //HotelImpl servant = new HotelImpl();
- byte[] id = rootPOA.activate_object(tie);
-
- // Publish the reference in the Naming Service
- NamingContextExt root = NamingContextExtHelper.narrow(
- orb.resolve_initial_references("NameService") );
- root.rebind( root.to_name( "Hotel California" ),
- rootPOA.id_to_reference(id) );
-
- // Publish the reference in the RMI Registry
- Properties props = new Properties();
- props.put( "java.naming.factory.initial",
- "com.sun.jndi.rmi.registry.RegistryContextFactory" );
- InitialContext rmiContext = new InitialContext( props );
- UnicastRemoteObject.exportObject( hotelImpl );
- rmiContext.rebind( "Hotel California", hotelImpl );
-
- // activate the RootPOA, and run
- rootPOA.the_POAManager().activate();
-
- System.out.println( "Ready" );
- orb.run();
- } catch (Exception e) {
- System.err.println("ERROR: " + e);
- e.printStackTrace(System.out);
- }
- }
- }
The DualHotelImpl
overrides the activateGuestRoom()
method of its base HotelImpl
class to activate the GuestRoom
through both IIOP and JRMP. Export the GuestRoom
with UnicastRemoteObject
to export it through the RMI/JRMP protocol.
- import java.rmi.RemoteException;
- import java.rmi.server.UnicastRemoteObject;
- import org.omg.PortableServer.POA;
- import org.omg.PortableServer.POAPackage.ServantAlreadyActive;
- import org.omg.PortableServer.POAPackage.WrongPolicy;
-
- public class DualHotelImpl extends HotelImpl
- {
- public DualHotelImpl(POA guestRoomPOA) throws RemoteException
- {
- super( guestRoomPOA );
- }
-
- protected void activateGuestRoom( GuestRoomImpl roomImpl )
- throws RemoteException, ServantAlreadyActive, WrongPolicy {
-
- super.activateGuestRoom( roomImpl );
-
- // export the GuestRoom as an RMI/JRMP object
- UnicastRemoteObject.exportObject( roomImpl );
- }
- }
The server registers its remote objects with both the the rmiregistry
and the JacORB Naming Service. An RMI/JRMP client finds the Hotel
through the rmiregistry
; an IIOP client finds the Hotel
through the JacORB Naming Service.
First, run the rmiregistry
.
rmiregistry
Then, run the JacORB Naming Service.
java -Djava.endorsed.dirs=%JACORB_HOME%\lib
-cp .;classes;%JACORB_HOME%\lib\jacorb.jar;%JACORB_HOME%\lib\logkit-1.2.jar
-Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB
-Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton
-DORBInitRef.NameService=
corbaloc:iiop:localhost:12026/StandardNS/NameServer-POA/_root
-DOAPort=12026
org.jacorb.naming.NameServer ns.ior
Run the server, which registers its Hotel
with both the rmiregistry
and the JacORB Naming Service.
java -Djava.endorsed.dirs=%JACORB_HOME%\lib
-cp .;classes;%JACORB_HOME%\lib\jacorb.jar;%JACORB_HOME%\lib\logkit-1.2.jar
-Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB
-Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton
-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
-Djava.naming.provider.url=
corbaloc:iiop:localhost:12026/StandardNS/NameServer-POA/_root
-DORBInitRef.NameService=
corbaloc:iiop:localhost:12026/StandardNS/NameServer-POA/_root
HotelServer
The client can be run as an RMI/JRMP client, retrieving its Hotel
from the rmiregistry
.
java -Djava.naming.factory.initial=
com.sun.jndi.rmi.registry.RegistryContextFactory
HotelClient
The same client can also be run an an RMI/IIOP client, retrieving its Hotel
from the JacORB Naming Service.
java -Djava.endorsed.dirs=%JACORB_HOME%\lib
-cp .;classes;%JACORB_HOME%\lib\jacorb.jar;%JACORB_HOME%\lib\logkit-1.2.jar
-Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB
-Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton
-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
-Djava.naming.provider.url=
corbaloc:iiop:localhost:12026/StandardNS/NameServer-POA/_root
-DORBInitRef.NameService=
corbaloc:iiop:localhost:12026/StandardNS/NameServer-POA/_root
HotelClient
The diagram shows the flexibility gained from the dual export RMI/JRMP and RMI/IIOP server. Implementing a pure CORBA client in another language (such as C++) is left as an exercise for the reader.
A Note on J2SE 1.3.x and 1.4.x
J2SE 1.3 and 1.4 also support the RMI over IIOP protocol. However, there is a minor problem in Sun's implementation of the javax.rmi.CORBA.UtilClass
class that must be handled. Otherwise, the server throws a ClassCastException
. The solution is to override Sun's class with a class such as this one:
- package mypackage;
- public class OverrideSunDelegate
- extends com.sun.corba.se.internal.iiop.ShutdownUtilDelegate
- {
- public boolean isLocal(javax.rmi.CORBA.Stub stub)
- throws java.rmi.RemoteException
- {
- try
- {
- org.omg.CORBA.portable.Delegate delegate = stub._get_delegate();
- return delegate.is_local(stub);
- }
- catch (org.omg.CORBA.SystemException e)
- {
- throw javax.rmi.CORBA.Util.mapSystemException(e);
- }
- }
- }
Then, when running the RMI/IIOP server, override the implementation javax.rmi.CORBA.Util
class as follows:
-Djavax.rmi.CORBA.UtilClass=mypackage.OverrideSunDelegate
Summary
RMI over IIOP combines the usability benefits of RMI programming with the flexibility and power of the CORBA server-side programming model and the IIOP protocol. An RMI/IIOP server can be made available to the widest possible variety of clients without requiring client-side code changes.
References
- [1] Sample Code for this article
- [2] JacORB 2.1
http://www.jacorb.org - [3] J2SE 1.5.0 beta 1
http://java.sun.com/j2se/1.5.0 - [4] J2SE 1.5.0 RMI over IIOP documentation
http://java.sun.com/j2se/1.5.0/docs/guide/rmi-iiop - [5] Ant 1.6.1
http://ant.apache.org - [6] Java News Brief on CORBA and Java
www.ociweb.com/resources/sett/Jan-2002.html - [7] Object Management Group
http://www.omg.org
Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.