|
|
|
|
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.
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.
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.
The diagram 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.
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.
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
The diagram shows the RMI/JRMP client and server.
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 in bold.
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
The diagram 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.
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.
Code changes are in bold. 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.
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
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.
OCI is the leading provider of Object Oriented technology training in the Midwest. More than 3,000 students participated in our training program over the last 12 months. Targeted toward Software Engineers and the development community, our extensive program of over 50 hands-on workshops is delivered to corporations and individuals throughout the U.S. and internationally. OCI's Educational Services include Group Training events and Open Enrollment classes.
For further information regarding OCI's Educational Services programs, please visit our Educational Services section on the web or contact us at training@ociweb.com.
|
|
|