RMI-IIOP Programmer's Guide
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) 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.
QUESTION: I have a RMI-IIOP application which
was running on J2SE 1.3. When I generate new stubs and skeletons
using rmic -iiop -poa <RMI Interface>, it does not
work. Why ?
ANSWER: There are 2 different ways to compile
and run RMI-IIOP programs, which are listed below. We do not
recommend that you combine these.
- The default RMI-IIOP behavior. An example of an application
that uses the default RMI-IIOP behavior can be found at Tutorial: Getting Started Using
RMI-IIOP.
- POA-based RMI-IIOP. An example of an application that uses the
non-standard POA-based RMI-IIOP behavior can be found at Getting Started Using RMI-IIOP: Example
Using POA-based server-side model.
The -iiop -poa Flag
New to this release of Java SE 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 tutorials that describe creating a new RMI-IIOP application,
link to Getting Started Using
RMI-IIOP: Example Using POA-based server-side model or
Tutorial: 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.
When do I use UnicastRemoteObject vs PortableRemoteObject?
UnicastRemoteObject should be used as the superclass
for the object implementation in RMI programming.
PortableRemoteObject should be used in RMI-IIOP
programming. If PortableRemoteObject is used, you can
switch the transport protocol to either JRMP or IIOP during
runtime.
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 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 this version of Java SE, see
the compliance document.