RMI over IIOP with JacORB

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:

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. RMI/IIOP

Figure 1. RMI/IIOP

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 1Ant 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:

  1. import java.rmi.Remote;
  2. import java.rmi.RemoteException;
  3. public interface HotelInterface extends Remote {
  4. public String getName() throws RemoteException;
  5. public short getNumberOfRooms() throws RemoteException;
  6. public GuestRoomInterface checkIn(short numNights)
  7. throws RemoteException;
  8. }
  9. import java.rmi.Remote;
  10. import java.rmi.RemoteException;
  11.  
  12. public interface GuestRoomInterface extends Remote {
  13. public short getRoomNumber() throws RemoteException;
  14. public float getRate() throws RemoteException;
  15. public float getBalance() throws RemoteException;
  16. public void checkOut() throws RemoteException
  17. }

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.

  1. import java.rmi.Remote;
  2. import java.rmi.RemoteException;
  3. import javax.rmi.PortableRemoteObject;
  4.  
  5. public class HotelImpl extends PortableRemoteObject
  6. implements HotelInterface
  7. {
  8. private static final int NUMBER_OF_ROOMS = 500;
  9. private GuestRoomImpl[] rooms = new GuestRoomImpl[NUMBER_OF_ROOMS];
  10.  
  11. public HotelImpl() throws RemoteException
  12. {
  13. }
  14.  
  15. public String getName() throws RemoteException
  16. {
  17. return "Hotel California";
  18. }
  19. public short getNumberOfRooms() throws RemoteException
  20. {
  21. return NUMBER_OF_ROOMS;
  22. }
  23. public GuestRoomInterface checkIn(short numNights)
  24. throws RemoteException
  25. {
  26. // Determine if a room is available.
  27. GuestRoomInterface availableRoom = null;
  28.  
  29. short nextRoom = 0;
  30. while ( nextRoom < NUMBER_OF_ROOMS
  31. && rooms[nextRoom] != null
  32. && !rooms[nextRoom].isAvailable() ) {
  33. ++nextRoom;
  34. } // end of while ()
  35.  
  36. if ( nextRoom < NUMBER_OF_ROOMS ) {
  37. if ( rooms[nextRoom] == null ) {
  38. rooms[nextRoom] = new GuestRoomImpl(nextRoom);
  39. } // end of if ()
  40.  
  41. rooms[nextRoom].checkIn( numNights );
  42. availableRoom = rooms[nextRoom];
  43. } // end of else
  44.  
  45. return availableRoom;
  46. }
  47. }

The implementation of the GuestRoom interface also extends PortableRemoteObject. The implementation itself is not important.

  1. import java.rmi.Remote;
  2. import java.rmi.RemoteException;
  3. import javax.rmi.PortableRemoteObject;
  4.  
  5. public class GuestRoomImpl extends PortableRemoteObject
  6. implements GuestRoomInterface
  7. {
  8. private boolean isAvailable = true;
  9. private short roomNumber;
  10. private float rate = 100.0f;
  11. private short numNights;
  12. private float balance = 0.0f;
  13.  
  14. GuestRoomImpl( short roomNumber ) throws RemoteException
  15. {
  16. this.roomNumber = roomNumber;
  17. }
  18.  
  19. public void checkIn( short numNights )
  20. {
  21. System.out.println( "Checking in to room " + this.roomNumber );
  22. this.isAvailable = false;
  23. this.numNights = numNights;
  24.  
  25. this.balance = this.numNights * this.rate;
  26. }
  27.  
  28. public short getRoomNumber() throws RemoteException
  29. {
  30. return this.roomNumber;
  31. }
  32. public static float getDefaultRate()
  33. {
  34. return 100.0f;
  35. }
  36. public float getRate() throws RemoteException
  37. {
  38. return this.rate;
  39. }
  40. public short getNumNights() throws RemoteException
  41. {
  42. return this.numNights;
  43. }
  44. public float getBalance() throws RemoteException
  45. {
  46. System.out.println( "Getting balance of room " + this.roomNumber );
  47. return this.balance;
  48. }
  49. public void checkOut() throws RemoteException
  50. {
  51. System.out.println( "Checking out of room " + this.roomNumber );
  52. this.isAvailable = true;
  53. this.numNights = 0;
  54. this.balance = 0.0f;
  55. }
  56. public boolean isAvailable() {
  57. return this.isAvailable;
  58. }
  59. }

The HotelServer creates a HotelImpl and binds it into the JNDI naming service. The JNDI naming service is bound to the rmiregistry at runtime.

  1. import javax.naming.Context;
  2. import javax.naming.InitialContext;
  3.  
  4. public class HotelServer {
  5. public static void main(String args[]) {
  6. try {
  7.  
  8. // Instantiate the Servant
  9. HotelImpl hotelImpl = new HotelImpl();
  10.  
  11. // Publish the reference in the Naming Service
  12. // using JNDI API
  13. javax.naming.Context initialCtx = new InitialContext();
  14. initialCtx.rebind("Hotel California", hotelImpl );
  15.  
  16. System.out.println( "Ready" );
  17. } catch (Exception e) {
  18. System.err.println("ERROR: " + e);
  19. e.printStackTrace(System.out);
  20. }
  21. }
  22. }

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.

  1. import javax.rmi.PortableRemoteObject;
  2. import javax.naming.Context;
  3. import javax.naming.InitialContext;
  4. import javax.naming.NamingException;
  5.  
  6. public class HotelClient
  7. {
  8. public static void main( String[] args )
  9. {
  10. Context ic;
  11. Object objref;
  12. HotelInterface hi;
  13.  
  14. try {
  15. ic = new InitialContext();
  16.  
  17. // Get the Object reference from the Name Service
  18. // using JNDI call.
  19. objref = ic.lookup("Hotel California");
  20.  
  21. // Narrow the object reference to the concrete type and
  22. // invoke the method.
  23. hi = (HotelInterface) PortableRemoteObject.narrow(
  24. objref, HotelInterface.class);
  25.  
  26. // Invoke operations on the Hotel as if it were a local object.
  27. // Note that "name" attribute is accessed via "name()" method
  28. String hotelName = hi.getName();
  29. System.out.println("This is the Hotel " + hotelName );
  30.  
  31. // Check into a room for 5 nights.
  32. short numNights = 5;
  33. GuestRoomInterface room = hi.checkIn(numNights);
  34. System.out.println( "Balance at checkout for room "
  35. + room.getRoomNumber()
  36. + " will be: "
  37. + room.getBalance() );
  38.  
  39. } catch( Exception e ) {
  40. System.err.println( "Exception " + e + "Caught" );
  41. e.printStackTrace( );
  42. return;
  43. }
  44. }
  45. }

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.

Figure 2. RMI / JRMP Client and Server

Figure 2. 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.

  1. import javax.naming.Context;
  2. import javax.naming.InitialContext;
  3. import javax.rmi.CORBA.Util;
  4. import org.omg.CORBA.ORB;
  5. import org.omg.PortableServer.POA;
  6.  
  7. public class HotelServer {
  8. public static void main(String args[]) {
  9. try {
  10. // create and initialize the ORB and POA
  11. ORB orb = ORB.init(args, null);
  12. POA rootPOA = org.omg.PortableServer.POAHelper.narrow(
  13. orb.resolve_initial_references("RootPOA"));
  14.  
  15. // Instantiate the Servant and activate the Tie
  16. HotelImpl hotelImpl = new HotelImpl(rootPOA);
  17. _HotelImpl_Tie tie = (_HotelImpl_Tie)Util.getTie(hotelImpl);
  18. byte[] id = rootPOA.activate_object(tie);
  19.  
  20. // Publish the reference in the Naming Service
  21. // using JNDI API
  22. javax.naming.Context initialNamingContext = new InitialContext();
  23. initialNamingContext.rebind("Hotel California",
  24. rootPOA.id_to_reference(id) );
  25.  
  26. // activate the RootPOA, and run
  27. rootPOA.the_POAManager().activate();
  28.  
  29. System.out.println( "Ready" );
  30. orb.run();
  31. } catch (Exception e) {
  32. System.err.println("ERROR: " + e);
  33. e.printStackTrace(System.out);
  34. }
  35. }
  36. }

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.

  1. import java.rmi.Remote;
  2. import java.rmi.RemoteException;
  3. import javax.rmi.PortableRemoteObject;
  4. import javax.rmi.CORBA.Util;
  5. import org.omg.PortableServer.POA;
  6. import org.omg.PortableServer.POAPackage.ServantAlreadyActive;
  7. import org.omg.PortableServer.POAPackage.WrongPolicy;
  8.  
  9. public class HotelImpl extends PortableRemoteObject
  10. implements HotelInterface
  11. {
  12. private static final int NUMBER_OF_ROOMS = 500;
  13. private GuestRoomImpl[] rooms = new GuestRoomImpl[NUMBER_OF_ROOMS];
  14.  
  15. private POA guestRoomPOA;
  16.  
  17. public HotelImpl(POA guestRoomPOA)
  18. throws RemoteException
  19. {
  20. this.guestRoomPOA = guestRoomPOA;
  21. }
  22.  
  23. public String getName() throws RemoteException
  24. {
  25. return "Hotel California";
  26. }
  27. public short getNumberOfRooms() throws RemoteException
  28. {
  29. return NUMBER_OF_ROOMS;
  30. }
  31. public GuestRoomInterface checkIn(short numNights)
  32. throws RemoteException
  33. {
  34. // Determine if a room is available.
  35.  
  36. GuestRoomInterface availableRoom = null;
  37.  
  38. short nextRoom = 0;
  39. while ( nextRoom < NUMBER_OF_ROOMS
  40. && rooms[nextRoom] != null
  41. && !rooms[nextRoom].isAvailable() ) {
  42. ++nextRoom;
  43. } // end of while ()
  44.  
  45. if ( nextRoom < NUMBER_OF_ROOMS ) {
  46. if ( rooms[nextRoom] == null ) {
  47. rooms[nextRoom] = new GuestRoomImpl(nextRoom);
  48. } // end of if ()
  49.  
  50. try {
  51. // activate GuestRoom
  52. activateGuestRoom( rooms[nextRoom] );
  53. }
  54. catch (Exception e) {
  55. // can't activate GuestRoom
  56. rooms[nextRoom] = null;
  57. System.err.println("ERROR: " + e );
  58. e.printStackTrace(System.err);
  59. return null;
  60. }
  61.  
  62. rooms[nextRoom].checkIn( numNights );
  63. availableRoom = rooms[nextRoom];
  64. }
  65. return availableRoom;
  66. }
  67.  
  68. protected void activateGuestRoom( GuestRoomImpl roomImpl )
  69. throws RemoteException, ServantAlreadyActive, WrongPolicy {
  70.  
  71. _GuestRoomImpl_Tie tie
  72. = (_GuestRoomImpl_Tie)Util.getTie( roomImpl );
  73. byte[] id = guestRoomPOA.activate_object(tie);
  74. }
  75. }

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.

Figure 3. RMI / IIOP Client and Server, CORBA

Figure 3. RMI / IIOP Client and Server, CORBA

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.

  1. import javax.naming.Context;
  2. import javax.naming.InitialContext;
  3. import javax.rmi.CORBA.Util;
  4. import org.omg.CORBA.ORB;
  5. import org.omg.PortableServer.POA;
  6. import java.rmi.server.UnicastRemoteObject;
  7. import java.util.Properties;
  8. import org.omg.CosNaming.NamingContextExt;
  9. import org.omg.CosNaming.NamingContextExtHelper;
  10.  
  11. public class DualHotelServer {
  12. public static void main(String args[]) {
  13. try {
  14.  
  15. // create and initialize the ORB and POA
  16. ORB orb = ORB.init(args, null);
  17. POA rootPOA = org.omg.PortableServer.POAHelper.narrow(
  18. orb.resolve_initial_references("RootPOA"));
  19.  
  20. // Instantiate the Servant and activate the Tie
  21. HotelImpl hotelImpl = new DualHotelImpl(rootPOA);
  22. _HotelImpl_Tie tie = (_HotelImpl_Tie)Util.getTie(hotelImpl);
  23.  
  24. //HotelImpl servant = new HotelImpl();
  25. byte[] id = rootPOA.activate_object(tie);
  26.  
  27. // Publish the reference in the Naming Service
  28. NamingContextExt root = NamingContextExtHelper.narrow(
  29. orb.resolve_initial_references("NameService") );
  30. root.rebind( root.to_name( "Hotel California" ),
  31. rootPOA.id_to_reference(id) );
  32.  
  33. // Publish the reference in the RMI Registry
  34. Properties props = new Properties();
  35. props.put( "java.naming.factory.initial",
  36. "com.sun.jndi.rmi.registry.RegistryContextFactory" );
  37. InitialContext rmiContext = new InitialContext( props );
  38. UnicastRemoteObject.exportObject( hotelImpl );
  39. rmiContext.rebind( "Hotel California", hotelImpl );
  40.  
  41. // activate the RootPOA, and run
  42. rootPOA.the_POAManager().activate();
  43.  
  44. System.out.println( "Ready" );
  45. orb.run();
  46. } catch (Exception e) {
  47. System.err.println("ERROR: " + e);
  48. e.printStackTrace(System.out);
  49. }
  50. }
  51. }

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.

  1. import java.rmi.RemoteException;
  2. import java.rmi.server.UnicastRemoteObject;
  3. import org.omg.PortableServer.POA;
  4. import org.omg.PortableServer.POAPackage.ServantAlreadyActive;
  5. import org.omg.PortableServer.POAPackage.WrongPolicy;
  6.  
  7. public class DualHotelImpl extends HotelImpl
  8. {
  9. public DualHotelImpl(POA guestRoomPOA) throws RemoteException
  10. {
  11. super( guestRoomPOA );
  12. }
  13.  
  14. protected void activateGuestRoom( GuestRoomImpl roomImpl )
  15. throws RemoteException, ServantAlreadyActive, WrongPolicy {
  16.  
  17. super.activateGuestRoom( roomImpl );
  18.  
  19. // export the GuestRoom as an RMI/JRMP object
  20. UnicastRemoteObject.exportObject( roomImpl );
  21. }
  22. }

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.

Figure 4. Flexibility of Dual Export RMI / JRMP and RMI / IIOP Server

Figure 4. Flexibility of Dual Export RMI / JRMP and RMI / IIOP Server

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:

  1. package mypackage;
  2. public class OverrideSunDelegate
  3. extends com.sun.corba.se.internal.iiop.ShutdownUtilDelegate
  4. {
  5. public boolean isLocal(javax.rmi.CORBA.Stub stub)
  6. throws java.rmi.RemoteException
  7. {
  8. try
  9. {
  10. org.omg.CORBA.portable.Delegate delegate = stub._get_delegate();
  11. return delegate.is_local(stub);
  12. }
  13. catch (org.omg.CORBA.SystemException e)
  14. {
  15. throw javax.rmi.CORBA.Util.mapSystemException(e);
  16. }
  17. }
  18. }

Then, when running the RMI/IIOP server, override the implementation javax.rmi.CORBA.Utilclass 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

Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.