This document discusses how to write Java Remote Method Invocation
(RMI) programs that can access remote objects by using the Internet Inter-ORB
Protocol (IIOP). By making your RMI programs conform to a small set of
restrictions, your CORBA clients
in any language can access RMI-IIOP servers. RMI-IIOP gives
you RMI ease-of-use coupled with CORBA/IIOP language interoperability.
What is RMI-IIOP?
RMI
With RMI you can write distributed programs in the Java programming language.
RMI is easy to use, you don't need to learn a separate interface definition
language (IDL), and you get Java's inherent "write once, run anywhere"
benefit. Clients, remote interfaces, and servers are written entirely in
Java. RMI uses the Java Remote Method Protocol (JRMP) for remote Java object
communication. To get a quick introduction to writing RMI programs, see
the RMI
Tutorial web page. That document describes writing a simple "Hello
World" RMI program.
RMI lacks interoperability with other languages because it does not use
CORBA-IIOP as the communication protocol.
IIOP, CORBA, and Java IDL
IIOP is CORBA's communication protocol using TCP/IP as the transport.
It specifies a standard for client and server communication. CORBA is a standard
distributed object architecture developed by the Object Management Group
(OMG). Interfaces to remote objects are described in a platform-neutral
interface definition language (IDL). Mappings from IDL to specific programming
languages are implemented, binding the language to CORBA/IIOP.
The Java(TM) 2 Platform, Standard Edition (J2SE), v.1.4 CORBA/IIOP implementation is known as Java IDL. Along with
the idlj compiler, Java IDL can be used to define, implement,
and access CORBA objects from the Java programming language.
The Java
IDL web page gives you a good, Java-centric view of CORBA/IIOP programming.
To get a quick introduction to writing Java IDL programs, see the Getting
Started: Hello World web page.
RMI-IIOP
Previously Java programmers had to choose between RMI and CORBA/IIOP (Java
IDL) for distributed programming solutions. Now, by adhering to a few
restrictions,
RMI server objects can use the IIOP protocol and communicate with CORBA client
objects written in any language.
This solution is known as RMI-IIOP. RMI-IIOP combines RMI-style ease of
use with CORBA cross-language interoperability.
Table of Contents
The rmic Compiler
The RMI-IIOP software comes with an rmic compiler that can
generate IIOP stubs and ties, and emit IDL, in accordance with the
Java Language to
OMG IDL Language Mapping Specification, in accordance with the
compliance statement.
Here are the primary rmic flags that support the CORBA/IIOP functionality:
-iiop -- Generates IIOP stubs/ties.
-iiop -poa -- Generates IIOP stubs/ties that work with a Portable Object Adapter (POA).
-idl -- Generates IDL.
The following options are used in conjunction with the -idl option.
-noValueMethods -- Stops generation of IDL for methods and
constructors within IDL valuetypes.
-always -- Forces regeneration even when existing stubs/ties/idl
are newer than the input class. Only valid when -iiop and/or -idl
flags are present.
-idlModule <fromJavaPackage<.class>> <toIDLModule>
-- Specifies IDLEntity package mapping. For example: -idlModule
foo.bar my::real::idlmod
-idlFile <fromJavaPackage<.class>> <toIDLFile> --
Specifies IDLEntity file mapping. For example: -idlFile test.pkg.X
TEST16.idl
For more detailed information on the rmic compiler, read the
rmic documentation.
The -iiop Flag
Using rmic with the -iiop option generates IIOP stub and tie
classes instead of Java Remote Method Protocol (JRMP) stub and skeleton classes.
A stub class is a local proxy for a remote object. Stub classes
are used by clients to send calls to a server. Each remote interface requires
a stub class, which implements that remote interface. The client's reference
to a remote object is actually a reference to a stub. Tie classes are used
on the server side to process incoming calls, and dispatch the calls to
the proper implementation class. Each implementation class requires a tie
class.
Stub classes are also generated for abstract interfaces. An abstract
interface is an interface that does not extend java.rmi.Remote,
but whose methods all throw either java.rmi.RemoteException or
a superclass of java.rmi.RemoteException. Interfaces that do not
extend
java.rmi.Remote and have no methods are also abstract interfaces.
The -iiop -poa Flag
New to this release of J2SE is the -iiop -poa option. Using the -iiop flag with the -poa option changes the inheritance from
org.omg.CORBA_2_3.portable.ObjectImpl to
org.omg.PortableServer.Servant. This type of mapping is nonstandard
and is not specified by the
Java Language to
OMG IDL Language Mapping Specification.
The PortableServer module for the Portable Object Adapter
(POA) defines
the native Servant type. In the Java programming language,
the Servant
type is mapped to the Java org.omg.PortableServer.Servant class. It
serves as the base class for all POA servant implementations and
provides a number of methods that may be invoked by the application
programmer, as well as methods which are invoked by the POA itself and
may be overridden by the user to control aspects of servant behavior.
The -idl Flag
Using rmic with the -idl option generates OMG IDL for
the classes specified and any classes referenced. You would want to use this
option when you have a CORBA client in another language that wants to talk to
an RMI-IIOP server.
Note: After the OMG IDL is generated using rmic -idl,
use the generated IDL with an IDL-to-C++ or other language compiler, but
not with the IDL-to-Java
language compiler. "Round tripping" is not recommended
and should not be necessary. The IDL generation facility is intended to
be used with other languages such as C++. Java clients or servers can use
the original RMI-IIOP types.
IDL provides a purely declarative, programming language independent
means for specifying the API for an object.
The IDL is used as a specification for methods and data that can be
written in and invoked from any language that provides CORBA bindings.
This includes Java and C++ among others. See the Java
Language to IDL Mapping (OMG) document for a complete description.
Note: The generated IDL can only be compiled using an IDL compiler
that supports the CORBA 2.3 extensions to IDL.
The -noValueMethods Flag
The -noValueMethods option, when used with -idl, ensures
that methods and initializers are not included in valuetypes
emitted during IDL Generation. These are optional for valuetypes
and are otherwise omitted.
See the RMIC tool page (Solaris Version/Windows version) for a complete
rmic
description.
The idlj Compiler
The RMI-IIOP software includes an IDL-to-Java compiler. This compiler
supports the CORBA Objects By Value feature, which is required for
interoperation with RMI-IIOP. It is written in Java, and so can run on
any platform. See the
IDL-to-Java Compiler
User's Guide for details of how to use this compiler.
How to Make RMI Programs Use IIOP
The following steps are a general guide to converting an RMI application
to RMI-IIOP.
-
If you are using the RMI registry for naming services, you need
to switch to CosNaming. In the createORB() method,
do not pass the org.omg.ORBClass property. Instead, use
ORB.init( args, null ). You need to do the
following:
-
In both your client and server code, you need to create an InitialContext
for JNDI using the following code:
import javax.naming.*;
...
Context ic = new InitialContext();
-
Modify all uses of RMI registry lookup() and bind() to
use JNDI lookup() and bind()instead. For example, instead
of your RMI server using:
import java.rmi.*;
...
Naming.rebind("MyObject", myObj);
use:
import javax.naming.*;
...
ic.rebind("MyObject", myObj);
-
If the client is an applet, the client applet needs to pass this
to the JNDI CosNaming plugin. Replace the above code with the
following:
import java.util.*;
import javax.naming.*;
...
Hashtable env = new Hashtable();
env.put("java.naming.applet", this);
Context ic = new InitialContext(env);
-
If you are not using the RMI registry for naming services, you have
some other way of bootstrapping your initial remote object reference. For
example, your server code may be using Java serialization to write an RMI
object reference to an ObjectOutputStream and passing this to
your client code for deserializing into an RMI stub.
On the server side, use the PortableRemoteObject.toStub()
call to obtain a stub, then use writeObject() to serialize this
stub to an ObjectOutputStream. The code to do this looks something
like:
org.omg.CORBA.ORB myORB = org.omg.CORBA.ORB.init(new String[0], null);
Wombat myWombat = new WombatImpl();
javax.rmi.CORBA.Stub myStub = (javax.rmi.CORBA.Stub)PortableRemoteObject.toStub(myWombat);
myStub.connect(myORB);
// myWombat is now connected to myORB. To connect other objects to the
// same ORB, use PortableRemoteObject.connect(nextWombat, myWombat);
FileOutputStream myFile = new FileOutputStream("t.tmp");
ObjectOutputStream myStream = new ObjectOutputStream(myFile);
myStream.writeObject(myStub);
On the client side, use readObject() to deserialize a remote reference
to the object from an ObjectInputStream, with code like:
FileInputStream myFile = new FileInputStream("t.tmp");
ObjectInputStream myStream = new ObjectInputStream(myFile);
Wombat myWombat = (Wombat)myStream.readObject();
org.omg.CORBA.ORB myORB = org.omg.CORBA.ORB.init(new String[0], null);
((javax.rmi.CORBA.Stub)myWombat).connect(myORB);
// myWombat is now connected to myORB. To connect other objects to the
// same ORB, use PortableRemoteObject.connect(nextWombat, myWombat);
-
Either change your remote implementation classes to inherit from javax.rmi.PortableRemoteObject,
or explicitly export implementation objects after creation by calling PortableRemoteObject.exportObject(). For more discussion
on this topic, read Connecting IIOP stubs to the ORB.
-
Change all the places in your code where there is a Java cast of a remote
interface to use javax.rmi.PortableRemoteObject.narrow().
-
Don't depend on Distributed Garbage Collection (DGC) or use any of the RMI DGC
facilities. Use PortableRemoteObject.unexportObject() to unexport
objects that are no longer in use.
-
Regenerate the RMI stubs and ties using the rmic command with
the -iiop option. This will produce stub and tie files with the
following names:
_<implementionName>_Tie.class
_<interfaceName>_Stub.class
-
Before starting the server, start the CosNaming server (in its
own process) using the following command:
orbd -ORBInitialPort port#
You must specify the port number when starting ORBD.
-
When starting client and server applications, specify the following system
properties:
java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
-Djava.naming.provider.url=iiop://<hostname>:1050
<appl_class>
This example uses the name service port number of 1050. If you specified
a different port in step 7, you need to use the same port number in the
provider URL here. The <hostname> in the provider URL is the host name
that was used to start the CosNaming server in step 7.
-
If the client is an applet, specify the following properties in the applet
tag:
java.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
java.naming.provider.url=iiop://<hostname>:1050
This example uses the name service port number of 1050. If you specified
a different port in step 7, you need to use the same port number in the
provider URL here. The <hostname> in the provider URL is the host name
that was used to start the CosNaming server in step 7.
For a tutorial that describes creating a new RMI-IIOP application, link to
Getting Started Using RMI-IIOP.
Connecting IIOP stubs to the ORB
When your application uses IIOP stubs, as opposed to JRMP stubs, you need to
properly connect the IIOP stubs with the ORB before invoking operations on the
IIOP stubs (this is not necessary with JRMP stubs). This section discusses
the extra 'connect' step required for the IIOP stub case.
The PortableRemoteObject.exportObject() call only creates a tie object and caches it for future usage. The created tie does not have a delegate or an ORB associated. This is known as
explicit invocation.
The PortableRemoteObject.exportObject() happens automatically when the servant instance is created. This happens when a PortableRemoteObject constructor is called as a base class. This is known as implicit invocation.
Later, when PortableRemoteObject.toStub() is called by the application, it creates the corresponding Stub object and associates it with the cached Tie object. But since the Tie is not connected and does not have a delegate, the newly created stub also does not have a delegate or ORB.
The delegate is set for the stub only when the application calls Stub.connect(orb). Thus, any operations on
the stub made before the ORB connection is made will fail.
The Java Language
to IDL Mapping Specification says the
following about the Stub.connect() method:
The connect method makes the stub ready for remote communication using the
specified ORB object orb. Connection normally happens implicitly when the stub is
received or sent as an argument on a remote method call, but it is sometimes useful to
do this by making an explicit call (e.g., following deserialization). If the stub is already
connected to orb (i.e., has a delegate set for orb), then connect takes no action. If
the stub is connected to some other ORB, then a RemoteException is thrown.
Otherwise, a delegate is created for this stub and the ORB object orb.
For servants that are not POA-activated, Stub.connect(orb) is necessary as a
required setup.
Restrictions When Running RMI Programs Over
IIOP
To make existing RMI programs run over IIOP, you need to observe the following
restrictions.
-
Make sure all constant definitions in remote interfaces are of primitive
types or String and evaluated at compile time.
-
Don't use Java names that conflict with IDL mangled names generated by
the Java to IDL mapping rules. See section 28.3.2 of the Java Language
to IDL Mapping specification for the Java to IDL name mapping rules.
-
Don't inherit the same method name into a remote interface more than once
from different base remote interfaces.
-
Be careful when using names that differ only in case. The use of a type
name and a variable of that type whose name differs from the type name
only in case is supported. Most other combinations of names that differ
only in case are not supported.
-
Don't depend on runtime sharing of object references to be preserved exactly
when transmitting object references across IIOP. Runtime sharing of other
objects is preserved correctly.
-
Don't use the following features of RMI:
-
RMISocketFactory
-
UnicastRemoteObject
-
Unreferenced
-
The Distributed Garbage Collection (DGC) interfaces
Other Things You Should Know
Servers Need to be Thread Safe
Since remote method invocation on the same remote object may execute concurrently,
a remote object implementation needs to make sure its implementation is
thread-safe.
Interoperating with Other ORBs
RMI-IIOP should interoperate with other ORBS that support the CORBA 2.3 specification.
It will not interoperate with older ORBs, because these are unable to handle
the IIOP encodings for Objects By Value. This support is needed to send
RMI value classes (including strings) over IIOP.
NOTE: Although it is true that ORBs written in
different languages should be able to talk to each other, we haven't tested the
interoperability of the Java ORB with other vendor's ORBs.
Known Problems
-
JNDI 1.1 does not support java.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
as an Applet parameter. Instead, it must be explicitly passed as
a property to the InitialContext
constructor. This capability is supported in JNDI 1.2.
-
When running the Naming Service on Solaris, you must use a port number greater
than 1024.
-
On Solaris, you may experience an "out of file descriptors" problem when
running RMI-IIOP applications. This may occur if you are using too
many file descriptors, which can happen because each JAR file in the classpath
consumes one file descriptor and the JDK 1.1.6 setup adds numerous JAR
files to the classpath. Before running applications, set the file
descriptor limit to a number greater than its default value of 64.
For example, set the ulimit number
to 90 or higher, as follows:
ulimit -n 90
Background Reading
Here are some sites to get you up to speed with this technology:
-
The RMI-IIOP
home page contains links to RMI-IIOP documentation, sample code, specifications,
news, other related web sites, and more. Check it out!
-
The RMI-IIOP
FAQ page answers many basic RMI-IIOP questions.
- The RMI-IIOP Tutorial gives a step-by-step example for creating and running a simple RMI-IIOP application.
-
The Java RMI
home page contains links to RMI documentation, examples, specification,
and more. Make sure you read this.
-
The RMI trail in the Java Tutorial.
-
The RMI API
documentation.
-
The Java
IDL web page will familiarize you with Sun's CORBA/IIOP implementation.
-
The Java
IDL Tutorial is the newest version of the IDL trail from the Java Tutorial.
-
The Java Language
to IDL Mapping, ptc/00-01-06 document is a detailed technical specification of RMI-IIOP. For further information on how the specification is implemented in J2SE v.1.4, see the compliance document.
RMI-IIOP Home
Send your comments to
rmi-iiop@sun.com.