rmic can now generate stubs for the Internet Inter-ORB protocol (IIOP) which is normally used by CORBA:
$ cd iiop/time $ CLASSPATH=../.. javac TimeService.java $ rmic -classpath ../.. -keepgenerated -iiop -d ../.. iiop.time.TimeService
Time and TimeService are unchanged except that the package is now iiop.time .
The proxy classes are _TimeService_Tie , required by the server, and _Time_Stub , required by server and client.
Client is changed because a reference to the service must now be obtained from a CORBA compliant name service:
package iiop.time;
import iiop.rmi.Server;
/** <tt>java iiop.time.Client</tt><br>
connects to time service.
*/
public class Client {
public static void main (String args []) {
try {
System.out.println(((Time)Server.lookup(Time.id, Time.class)).getTime());
} catch (Exception e) { System.err.println(e); System.exit(1); }
}
}
A new, reasonably generic rmi.Server class is implemented to support the lookup:
package iiop.rmi;
import java.rmi.Remote;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
/** <tt>java iiop.rmi.Server service</tt><br>
creates service(), binds it for service.id.
Set <tt>rmi.log</tt> to display threads.
*/
public class Server {
/** locates a service using JNDI.
@param id service's registry name.
*/
public static Object lookup (String id, Class to) throws NamingException {
return PortableRemoteObject.narrow(new InitialContext().lookup(id), to);
}
This lookup uses the Java Naming and Directory Interface (JNDI) with a plugin for the CORBA naming service.
rmi.Server is designed to register and start a service:
/** starts and registers a service using JNDI.
Set <tt>rmi.log</tt> to show threads.
@param args [0] classname of service; class must contain static String id.
*/
public static void main (String args []) throws Exception {
Class serviceClass = null;
try {
if (args != null && args.length > 0) {
serviceClass = Class.forName(args[0]);
Remote service = (Remote)serviceClass.newInstance();
PortableRemoteObject.exportObject(service);
new InitialContext().rebind((String)serviceClass.getField("id").get(null),
service);
if (Boolean.getBoolean("rmi.log"))
dumpThreads(true);
}
} finally {
if (serviceClass == null)
System.err.println("usage: java iiop.rmi.Server service");
}
}
It is easy to see that InitialContext does for JNDI what Naming does for RMI. dumpThreads() is unchanged from before.
tnameserv implements a transient CORBA-compliant name service:
$ tnameserv -ORBInitialPort 1111 & Initial Naming Context: IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f6e746578743a312e3000000000010000000000000050000101000000000a3132372e302e302e3200042b00000018afabcafe00000002135175b00000000800000000000000000000000100000001000000140000000000010020000000000001010000000000 TransientNameServer: setting port for initial object references to: 1111 Ready.
The output could be used to provide a CORBA client with access to the name service. JNDI, however, can also deal with a URL-style reference:
$ java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \ > -Djava.naming.provider.url=iiop://localhost:1111 \ > iiop.rmi.Server iiop.time.TimeService & $ java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \ > -Djava.naming.provider.url=iiop://localhost:1111 \ > iiop.time.Client Mon May 28 22:58:58 CEST 2001
Server and client need suitable JNDI plugins to reach tnameserv .
Both, the arithmetic service and the chat service, can be run over IIOP. However, the client has to do a little bit of extra work to set up the callback:
package iiop.cpu; import iiop.rmi.Server; import java.rmi.Remote; import java.rmi.RemoteException; import javax.rmi.PortableRemoteObject; /** <tt>java iiop.cpu.Client</tt><br> connects to cpu service, provides terms. */ public class Client implements Terms, Remote { // implementation of Terms as before public static void main (String args []) { try { Cpu cpu = (Cpu)Server.lookup(Cpu.id, Cpu.class); Client client = new Client(); PortableRemoteObject.exportObject(client); client.run(cpu); PortableRemoteObject.unexportObject(client); } catch (Exception e) { System.out.println(e); System.exit(1); } if (Boolean.getBoolean("rmi.log")) Server.dumpThreads(true); } }
Client implements Terms and needs to be exported by proxy to the arithmetic service. This essentially requires a delegate pattern which is managed using exportObject() and unexportObject() from PortableRemoteObject .
The threads are implemented correctly, the client terminates properly. However, the arithmetic service once again receives a stub rather than itself in result() :
$ java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \ > -Djava.naming.provider.url=iiop://localhost:1111 iiop.cpu.Client service: iiop.cpu.CpuService@4b222f != IOR:0000000000000029524d493a69696f702e6370752e437075536572766963653a3030303030303030303030303030303000000000000000010000000000000050000101000000000a3132372e302e302e3200043500000018afabcafe0000000213531d870000000800000001000000000000000100000001000000140000000000010020000000000001010000000000 5
Why should RMI be run over IIOP?
If RMI uses it's own protocol, it is more transparently integrated with Java.
IIOP must be used if a Java program wants to interact with another CORBA program, e.g., a program written in C++. This is pursued further in the next chapter.
RMI over IIOP is simpler to program than a CORBA application; therefore, this might be the best approach as long as there is no specific CORBA interface definition to fulfill.
[However, I have only managed to let the JDK 1.3.1 RMI over IIOP use the ORBacus C++ nameserv ; none of the other interoperations has worked.]