Contents
CORBA is also an infrastructure for distributed systems. It is targeted at a slightly different ``space'' to Jini, and has some minor and major differences.
IDL is a language that allows the programmer to specify the interfaces of a distributed object system. The syntax is similar to C++, but does not include any implementation-level constructs. So it allows definitions of data types (such as structures and unions), constants, enumerated types, exceptions and interfaces. Within interfaces it allows declaration of attributes and operations (methods). The complete IDL specification can be found on the Object Management Group (OMG) site http://www.omg.org.
The book ``Java Programming with CORBA'' by Andreas Vogel and Keith Duddy
(http://www.wiley.com/compbooks/vogel)
contains an example of a room booking service specified in CORBA IDL and
implemented in Java. This defines interfaces for Meeting
, a
factory to produce them, MeetingFactory
and a Room
.
A room may have a number of meetings in slots (hourly throughout the day),
and there are support constants, enumerations and typedefs to support this.
In addition, exceptions may be thrown under various error conditions.
The IDL that follows differs slightly from that given in the book, in that
definitions of some data types that occur within interfaces have been
``lifted'' to a more global level, since the mapping from IDL to
Java has changed slightly for elements nested within interfaces
since that book was written.
module corba {
module RoomBooking {
interface Meeting {
// A meeting has two read-only attributes which describes
// the purpose and the participants of that meeting.
readonly attribute string purpose;
readonly attribute string participants;
oneway void destroy();
};
interface MeetingFactory {
// A meeting factory creates meeting objects.
Meeting CreateMeeting( in string purpose, in string participants);
};
// Meetings can be held between the usual business hours.
// For the sake of simplity there are 8 slots at which meetings
// can take place.
enum Slot { am9, am10, am11, pm12, pm1, pm2, pm3, pm4 };
// since IDL does not provide means to determine the cardinality
// of an enum, a corresponding constant MaxSlots is defined.
const short MaxSlots = 8;
exception NoMeetingInThisSlot {};
exception SlotAlreadyTaken {};
interface Room {
// A Room provides operations to view, make and cancel bookings.
// Making a booking means associating a meeting with a time-slot
// (for this particular room).
// Meetings associates all meetings (of a day) with time slots
// for a room.
typedef Meeting Meetings[ MaxSlots ];
// The attribute name names a room.
readonly attribute string name;
// View returns the bookings of a room.
// For simplicity, the implementation handles only bookings
// for one day.
Meetings View();
void Book( in Slot a_slot, in Meeting a_meeting )
raises(SlotAlreadyTaken);
void Cancel( in Slot a_slot )
raises(NoMeetingInThisSlot);
};
};
};
CORBA has bindings to a number of languages. The most recent addition is Java, and this binding is still under active development (that is, the core is basically settled, but some parts are still changing). This binding must cover all elements of IDL. A horribly brief summary is:
int
becomes a Java int
, a CORBA string
becomes a Java
java.lang.String
, and so on. Some are a little tricky, such as
the unsigned types which have no Java equivalent.
readonly string purpose
becomes the Java accessor method String purpose()
rather than
String getPurpose()
. Where Java code is generated, the generated
names will be used. But in ``ordinary'' methods, I shall use the more accepted
naming forms.
A Jini service exports a proxy object that acts within the client, on behalf
of the service. The proxy may be ``fat'' or ``thin'', depending on circumstances.
In the chapter on MindStorms, the proxy has to be thin, since all it does
is pass on requests to the real service, which is linked to the hardware device.
The service cannot move since it has to talk to a particular serial port, so the
proxy is thin. (The proxy may have an extensive user interface, but the Jini
community seems to feel that any user interface should be in Entry
objects rather than in the proxy itself.) Proxy objects created as RMI proxies are
similarly thin, just passing on method calls to the service.
CORBA services can be delivered to any accessible client. Each service is limited to the server on which it is running, so they are essentially immobile. They can be found by a variety of methods, such as a CORBA naming or trading service. These search methods can be run by any client, anywhere. A search will return a reference to a remote object, which is essentially a thin proxy to the CORBA service. Similarly, if a CORBA method call creates and returns an object, then it will return a remote reference to that object which will continue to exist on the server where it was created. (The new CORBA standards will allow objects to be returned by value. This is not yet commonplace, and will probably be restricted to a few languages such as C++ and Java.)
The simplest way to make a CORBA object available to a Jini federation is to build a Jini service that is at the same time a CORBA client. The service acts as a bridge between the two protocols. Really, this is just the same as MindStorms - anything that requires talking a different protocol (hardware or software) will require a bridge between the two.
Most CORBA implementations use IIOP - that is, they are TCP based. The current Jini implementation is also TCP based. So there is a confluence of transport methods, which normally would not occur. A bridge would usually be fixed to a particular piece of hardware, but here it is not necessary due to this confluence. A Jini proxy need not report back to its server since it can negotiate directly with the CORBA service over the TCP network. In many instances it can be a ``fat'' proxy. This is similar in concept to the fat proxy in the chapter on Service Choices: there the fat proxy used HTTP to talk to a service within another protocol, but this can also be done with the CORBA protocols. A proxy may need to be thin in some cases such as when it is used by an applet: security restrictions may mean that it cannot talk to any host but only to the one the applet came from. In such a case it would have to perform all CORBA client activities back on its `home' server.
The CORBA IDL tutorial at http://java.sun.com/products/jdk/1.2/docs/guide/idl/ gives a ``hello world'' example. This is defined by the IDL
module corba {
module HelloApp {
interface Hello {
string sayHello();
};
};
};
This can be compiled into Java using a compiler such as Sun's
idltojava
(or other Corba 2.2 compliant compiler).
This results in a package corba.HelloApp
containing a number of
classes and interfaces. Hello
is an interface which is used
by a CORBA client (in Java).
/*
* File: ./corba/HelloApp/Hello.java
* From: Hello.idl
* Date: Tue Aug 24 11:30:21 1999
* By: idltojava Java IDL 1.2 Aug 11 1998 02:00:18
*/
package corba.HelloApp;
public interface Hello
extends org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity {
String sayHello()
;
}
A server for the hello IDL can be written in any language with a CORBA binding, such as C++. Rather than get diverted into other languages, though, we shall stick to a Java implementation. However, this language choice is not forced on us by CORBA.
The server must create an object that implements the Hello
interface. This is done by creating a servant that
inherits from the _HelloImplBase
, and then registering
it with the CORBA ORB (Object Request Broker - the CORBA ``backplane'').
The server must also find a name server and register the name and the
implementation. The servant implements the Hello
interface.
The server can just sleep to continue existence after registering
the servant.
/**
* CorbaHelloServer.java
*/
package corba;
import corba.HelloApp.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
public class CorbaHelloServer {
public CorbaHelloServer() {
}
public static void main(String[] args) {
try {
// create a Hello implementation object
ORB orb = ORB.init(args, null);
HelloServant helloRef = new HelloServant();
orb.connect(helloRef);
// find the name server
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
// bind the Hello service to the name server
NameComponent nc = new NameComponent("Hello", "");
NameComponent path[] = {nc};
ncRef.rebind(path, helloRef);
// sleep
java.lang.Object sleep = new java.lang.Object();
synchronized(sleep) {
sleep.wait();
}
} catch(Exception e) {
e.printStackTrace();
}
}
} // CorbaHelloServer
class HelloServant extends _HelloImplBase {
public String sayHello() {
return("hello world");
}
}
A standalone client finds a proxy implementing the Hello
interface by methods such as looking up a CORBA name server. The name server
returns a org.omg.CORBA.Object
which is cast to the interface type by
the HelloHelper
method narrow()
(the Java cast
method is not used).
This proxy object can then be used to call methods back in the CORBA server
/**
* CorbaHelloClient.java
*/
package corba;
import corba.HelloApp.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
public class CorbaHelloClient {
public CorbaHelloClient() {
}
public static void main(String[] args) {
try {
ORB orb = ORB.init(args, null);
// find the name server
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
// find the Hello proxy
NameComponent nc = new NameComponent("Hello", "");
NameComponent path[] = {nc};
org.omg.CORBA.Object obj = ncRef.resolve(path);
Hello helloRef = HelloHelper.narrow(obj);
// now invoke methods on the CORBA proxy
String hello = helloRef.sayHello();
System.out.println(hello);
} catch(Exception e) {
e.printStackTrace();
}
}
} // CorbaHelloClient
In order to make the CORBA object accessible to the Jini world, it must be turned into a Jini service. At the same time it must remain in a CORBA server, so that it can be used by ordinary CORBA clients. So we do nothing to the CORBA server. Instead, we need to build a Jini service that will act as a CORBA client. This service will then be able to deliver the CORBA service to Jini clients.
The Jini service can be implemented as a fat proxy delivered to a Jini client and performing most if its activity within this Jini client. The processes that run in this, with their associated Jini and CORBA objects, look like:
The Java interface for this service is quite simple, and basically just copies the interface for the CORBA service, as
/**
* JavaHello.java
*/
package corba;
import java.io.Serializable;
public interface JavaHello extends Serializable {
public String sayHello();
} // JavaHello
The method sayHello()
for the CORBA IDL returns a string
.
In the Java binding this becomes an ordinary Java String
, and the
Jini service can just use this type. The next example will show a more complex
case where CORBA objects may get returned. Note that as a ``fat'' service,
any implementation will get moved across to a Jini client and will run there.
So the service only needs to implement Serializable
, and its methods do
not need to throw Remote
exceptions since they will run locally
in the client.
The implementation of this Jini interface will basically act as a CORBA client.
Its sayHello()
method
will contact the CORBA naming service, find a reference to the CORBA
Hello
object and call its sayHello()
method. The Jini service can just return the string it gets from the CORBA service.
/**
* JavaHelloImpl.java
*/
package corba;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
import corba.HelloApp.*;
public class JavaHelloImpl implements JavaHello {
protected Hello helloRef = null;
protected String[] argv;
public JavaHelloImpl(String[] argv) {
this.argv = argv;
}
public String sayHello() {
// Hello helloRef = null;
if (helloRef == null) {
helloRef = getHelloRef();
}
// now invoke methods on the CORBA proxy
String hello = helloRef.sayHello();
return hello;
}
protected Hello getHelloRef() {
ORB orb = null;
// Act like a CORBA client
try {
orb = ORB.init(argv, null);
// find the CORBA name server
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
// find the CORBA Hello proxy
NameComponent nc = new NameComponent("Hello", "");
NameComponent path[] = {nc};
org.omg.CORBA.Object obj = ncRef.resolve(path);
Hello helloRef = HelloHelper.narrow(obj);
return helloRef;
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
} // JavaHelloImpl
The Jini server which exports the service doesn't contain anything new
package corba;
// import com.sun.jini.lookup.JoinManager;
import net.jini.lookup.JoinManager;
import net.jini.core.lookup.ServiceID;
// import com.sun.jini.lookup.ServiceIDListener;
// import com.sun.jini.lease.LeaseRenewalManager;
import net.jini.lookup.ServiceIDListener;
import net.jini.lease.LeaseRenewalManager;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.LookupDiscoveryManager;
/**
* JavaHelloServer.java
*/
public class JavaHelloServer implements ServiceIDListener {
public static void main(String argv[]) {
new JavaHelloServer(argv);
// keep server running forever to
// - allow time for locator discovery and
// - keep re-registering the lease
Object keepAlive = new Object();
synchronized(keepAlive) {
try {
keepAlive.wait();
} catch(java.lang.InterruptedException e) {
// do nothing
}
}
}
public JavaHelloServer(String[] argv) {
JoinManager joinMgr = null;
try {
LookupDiscoveryManager mgr =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null /* unicast locators */,
null /* DiscoveryListener */);
joinMgr = new JoinManager(new JavaHelloImpl(argv),
null,
this,
mgr,
new LeaseRenewalManager());
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public void serviceIDNotify(ServiceID serviceID) {
System.out.println("got service ID " + serviceID.toString());
}
} // JavaHelloServer
Similarly, the Jini client doesn't contain anything new
package client;
import corba.JavaHello;
import java.rmi.RMISecurityManager;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
/**
* TestCorbaHello.java
*/
public class TestCorbaHello implements DiscoveryListener {
public static void main(String argv[]) {
new TestCorbaHello();
// stay around long enough to receive replies
try {
Thread.currentThread().sleep(10000L);
} catch(java.lang.InterruptedException e) {
// do nothing
}
}
public TestCorbaHello() {
System.setSecurityManager(new RMISecurityManager());
LookupDiscovery discover = null;
try {
discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
} catch(Exception e) {
System.err.println(e.toString());
System.exit(1);
}
discover.addDiscoveryListener(this);
}
public void discovered(DiscoveryEvent evt) {
ServiceRegistrar[] registrars = evt.getRegistrars();
for (int n = 0; n < registrars.length; n++) {
System.out.println("Service found");
ServiceRegistrar registrar = registrars[n];
new LookupThread(registrar).start();
}
// System.exit(0);
}
public void discarded(DiscoveryEvent evt) {
// empty
}
class LookupThread extends Thread {
ServiceRegistrar registrar;
LookupThread(ServiceRegistrar registrar) {
this.registrar = registrar;
}
public void run() {
Class[] classes = new Class[] {JavaHello.class};
JavaHello hello = null;
ServiceTemplate template = new ServiceTemplate(null, classes,
null);
try {
hello = (JavaHello) registrar.lookup(template);
} catch(java.rmi.RemoteException e) {
e.printStackTrace();
System.exit(2);
}
if (hello == null) {
System.out.println("hello null");
return;
}
String msg;
try {
msg = hello.sayHello();
System.out.println(msg);
} catch(Exception e) {
// we may get a CORBA runtime error
System.err.println(e.toString());
}
}
}
} // TestCorbaHello
The major additional step is to build the Java classes from the IDL specification.
There are a number of CORBA IDL to Java compilers. One of these
is the Sun compiler idltojava
which is available from
java.sun.com
. This (or another compiler) needs to be run on the
IDL file to produce the Java files in the package corba.HelloApp
.
These are standard Java files, and can be compiled using your normal Java
compiler. They may need some CORBA files in the classpath if required by your
vendor's implementation of CORBA. Files produced by idltojava
do
not need any extra classes.
The Jini server, service and client are also normal Java files, and can be compiled like earlier Jini files, with the classpath set to include the Jini libraries.
There are a large number of elements and processes that must be set running to get this running satisfactorily
tnameserv
. By default this runs on TCP port 900. Under Unix access
to this port is restricted to system supervisors. It can be set running on this
port by a supervisor, or started during boot time. An ordinary user will need
to use the option -ORBInitialPort port
to run it on a port above 1024.
For example,
tnameserv -ORBInitialPort 1055
All CORBA services and clients should also use this port number
java corba.CorbaHelloServer -ORBInitialPort 1055
rmid
, and HTTP servers to move class definitions
around
java corba.JavaHelloServer -ORBInitialPort 1055
java client.TestCorbaHello -ORBInitialPort 1055
The IDL for a room booking problem was briefly discussed in an earlier section.
This has a few more complexities over the previous example.
The problem is to have a set of rooms, and for each room a set of bookings
that can be made on that room. The bookings may be made on the hour, from
9am till 4pm (this only covers the bookings for one day). Bookings once made
may be cancelled. A room can be queried for the set of bookings it has: it
returns an array of meetings, which are null
if no booking has
been made, or a non-null meeting which has details of the participants and
the purpose of the meeting.
The additional features of this example are
CORBA defines a set of ``primitive'' types such as integers of various sizes,
chars, etc. The language bindings specify the primitive
types they turn into for each language. For example, the CORBA wide
character wchar
becomes a Java Unicode char
.
Things are different for non-primitive objects, though.
The IDL for the room booking defines CORBA interfaces for Meeting
,
MeetingFactory
and Room
. The implementation of these
can be in any suitable language, and need not be in Java. The Java binding will
convert these into Java interfaces. A CORBA client written in Java will get objects
that implement these interfaces, but these will essentially be just references to
remote CORBA objects. Two things are certain about these references:
Hello
.
These inherit from org.omg.CORBA.portable.IDLEntity
,
which implements Serializable
.
So the references can be moved around like Jini objects. But they lose their link to
the CORBA ORB that created them, and may end up in a different ``namespace''
where the reference makes no sense. So CORBA references cannot be usefully
moved around.
The closest one can come at present is to convert them to
``string-ified'' form and move that around. This may change when CORBA pass-by-value
objects become common. Note that the serialization method that gives a string
representation of a CORBA object is not the same as the Java one: the CORBA method
serializes the remote reference, the Java method serializes the object's instance
data
UnicastRemoteObject
or
Activatable
. The Java runtime will not use an RMI stub for them
If a Jini client gets local references to these objects, and keeps them local,
then it can use them via their Java interfaces. If they
need to be moved around the network, then appropriate ``mobile'' classes will
need to be defined and the information copied across to them from the local
objects. For example, the CORBA Meeting
interface generates the
Java interface
/*
* File: ./corba/RoomBooking/Meeting.java
* From: RoomBooking.idl
* Date: Wed Aug 25 11:30:25 1999
* By: idltojava Java IDL 1.2 Aug 11 1998 02:00:18
*/
package corba.RoomBooking;
public interface Meeting
extends org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity {
String purpose();
String participants();
void destroy()
;
}
To make the information from a CORBA Meeting
available as
a mobile Jini object, we would need an interface
/**
* JavaMeeting.java
*/
package corba.common;
import java.io.Serializable;
import org.omg.CORBA.*;
import corba.RoomBooking.*;
import java.rmi.RemoteException;
public interface JavaMeeting extends Serializable {
String getPurpose();
String getParticipants();
Meeting getMeeting(ORB orb);
} // JavaMeeting
The first two methods allow information about a meeting to be accessible to applications
that do not want to contact the CORBA service, and the third allows a CORBA object
reference to be reconstructed within a new ORB.
A suitable implementation is
/**
* JavaMeetingImpl.java
*/
package corba.RoomBookingImpl;
import corba.RoomBooking.*;
import org.omg.CORBA.*;
import corba.common.*;
/**
* A portable Java object representing a CORBA object.
*/
public class JavaMeetingImpl implements JavaMeeting {
protected String purpose;
protected String participants;
protected String corbaObj;
/**
* get the purpose of a meeting for a Java client
* unaware of CORBA
*/
public String getPurpose() {
return purpose;
}
/**
* get the participants of a meeting for a Java client
* unaware of CORBA
*/
public String getParticipants() {
return participants;
}
/**
* reconstruct a meeting using a CORBA orb in the target JVM
*/
public Meeting getMeeting(ORB orb) {
org.omg.CORBA.Object obj = orb.string_to_object(corbaObj);
Meeting m = MeetingHelper.narrow(obj);
return m;
}
/**
* construct a portable Java representation of the CORBA
* Meeting, using the CORBA orb on the source JVM
*/
public JavaMeetingImpl(Meeting m, ORB orb) {
purpose = m.purpose();
participants = m.participants();
corbaObj = orb.object_to_string(m);
}
} // JavaMeetingImpl
might be appropriate.
The implementation of the room booking problem in the Vogel and Duddy book runs each room as a separate CORBA object, each with its own server. A meeting factory creates meeting objects which are kept within the factory server, and passed around by reference. So for a distributed application with ten rooms, there will be eleven CORBA servers running.
There are several possible ways of bringing this set of objects into the Jini world so that they are accessible to a Jini client, including
We can have one Jini server for each of the CORBA servers. The Jini servers can be running on the same machines as the CORBA ones, but there is no necessity to do so. Applet security restrictions, for example, may mean that all the Jini servers are running on a single machine, the same one as an applet's HTTP server.
The Jini proxy objects exported by each Jini server may be fat ones, which connect directly to the CORBA server. Thus each proxy becomes a CORBA client, as was dealt with in the ``hello world'' example. Within the Jini client, we do not just have one proxy, but many proxies. Because they are all running within the same address space, they can share CORBA references - there is no need to package a CORBA reference as a portable Jini object. In addition, the Jini client can just use all of these CORBA references directly, as instance objects of interfaces. Diagramatically, this appears as
The CORBA servers are all accessed from within the Jini client. This may rule out this arrangement if the client is an applet, and the servers are on different machines.
The proxies exported can be thin, such as RMI stubs. In this case each Jini server is acting as a CORBA client.
If all the Jini servers are collocated on the same machine, then this becomes a possible architecture suitable for applets. The downside of this approach is that all the CORBA references are within different JVMs. In order to move the reference for a meeting from the Jini meeting factory to one of the Jini rooms, it may be necessary to wrap it in a portable Jini object, as discussed above. The Jini client will also need to get information about the CORBA objects, which can be gained from these portable Jini objects.
An alternative to Jini servers for each CORBA server is to have a single Jini server into the CORBA federation. This can be a feasible alternative when the set of CORBA objects form a complete system or module, and it makes sense to treat them as a unit. There are then the choices again of where to locate the CORBA references, either in the Jini server or in a proxy. Placing them in a fat proxy looks like
Placing all the CORBA references on the server side of a Jini service means that a Jini client only needs to make one network connection to the service. This is probably the best option from a security viewpoint of a Jini client.
CORBA methods can throw exceptions of two types: system exceptions and user
exceptions. System exceptions subclass from RuntimeException
,
and so are unchecked.
These do not need to have explicit try...catch
clauses around them.
If an exception is thrown, it will be caught by the Java runtime, and generally
halt the process with an error message.
This would result in a CORBA client dying, which would generally be undesirable.
Many of these system exceptions will be caused by the distributed
nature of CORBA objects, and probably should be caught explicitly.
If they cannot be handled directly, then to bring them
into line with the Jini world, they can be wrapped as ``nested exceptions'' within
a Remote
exception and thrown again.
User exceptions are declared in the IDL for the CORBA interfaces and methods.
These exceptions are checked, and need to be explicitly
caught (or re-thrown) by Java methods. If a user exception is thrown, this will
be because of some semantic error within one of the objects, and will be
unrelated to any networking or remote issues. They should be treated as they are,
without wrapping them in Remote
exceptions.
This and the following sections consider issues in building a single thin proxy for a federation of CORBA objects. The Vogel and Duddy book gives a CORBA client to interact with the CORBA federation, and this is used as the basis for the Jini services and clients.
Using a thin proxy means that all CORBA-related calls will be placed in the service object, and made available to Jini clients only by means of portable Jini versions of the CORBA objects. These portable objects are defined by the interfaces
/**
* JavaRoom.java
*/
package corba.common;
import corba.RoomBooking.*;
import java.io.Serializable;
import org.omg.CORBA.*;
import java.rmi.RemoteException;
public interface JavaRoom extends Serializable {
String getName();
Room getRoom(ORB orb);
} // JavaRoom
and
/**
* JavaMeeting.java
*/
package corba.common;
import java.io.Serializable;
import org.omg.CORBA.*;
import corba.RoomBooking.*;
import java.rmi.RemoteException;
public interface JavaMeeting extends Serializable {
String getPurpose();
String getParticipants();
Meeting getMeeting(ORB orb);
} // JavaMeeting
The bridge interface between the CORBA federation and the Jini clients has to provide methods for making changes to objects within the CORBA federation, and to obtain information from them. For the room booking system this requires the ability to book and cancel meetings within rooms, and also the ability to view the current state of the system. Viewing is accomplished by three methods: updating the current state, and getting the list of rooms and of bookings for a room.
/**
* RoomBookingBridge.java
*/
package corba.common;
import java.rmi.RemoteException;
import corba.RoomBooking.*;
import org.omg.CORBA.*;
public interface RoomBookingBridge extends java.io.Serializable {
public void cancel(int selected_room, int selected_slot)
throws RemoteException, NoMeetingInThisSlot;
public void book(String purpose, String participants,
int selected_room, int selected_slot)
throws RemoteException, SlotAlreadyTaken;
public void update()
throws RemoteException, UserException;
public JavaRoom[] getRooms()
throws RemoteException;
public JavaMeeting[] getMeetings(int room_index)
throws RemoteException;
} // RoomBookingBridge
There is a slight legacy in this that comes from the original ``monoblock'' CORBA client.
In there, because the GUI interface elements and the CORBA references were all
in the one client, simple shareable structures such as arrays of rooms
and arrays of meetings were used. Meetings and rooms could be indentified simply
by their index into the appropriate array. In splitting the client apart
into multiple (and remote) classes this is not
really a good idea anymore as it assumes a commonality of implementation
across objects which may not occur. It doesn't seem worthwhile being too
fussy about that here, though.
The room booking Jini bridge has to perform all CORBA activities, and to wrap these
up as portable Jini objects. A major part of this is locating the CORBA services,
which here are the meeting factory and all of the rooms. We do not want to get too
involved in these issues here. The meeting factory can be found in essentially
the same way as the hello server was earlier, by looking up its name.
Finding the rooms is harder as these are not known in advance. Essentially, the
equivalent of a ``directory'' has to be set up on the name server, which is
known as a ``naming context''. Rooms are registered within this naming context
by their servers, and the client gets this context and then does a search
for its contents. The Jini component of this object is that it subclasses from
UnicastRemoteObject
, and implements a
RemoteRoomBookingBridge
which is a remote version of
RoomBookingBridge
. It is also worthwhile noting how CORBA exceptions
are caught and wrapped in Remote
exceptions.
/**
* RoomBookingBridgeImpl.java
*/
package corba.RoomBookingImpl;
import org.omg.CORBA.*;
import org.omg.CosNaming.*;
import corba.RoomBooking.*;
import corba.common.*;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Vector;
public class RoomBookingBridgeImpl extends UnicastRemoteObject implements RemoteRoomBookingBridge {
private MeetingFactory meeting_factory;
private Room[] rooms;
private Meeting[] meetings;
private ORB orb;
private NamingContext room_context;
public RoomBookingBridgeImpl(String[] args)
throws RemoteException, UserException {
try {
// initialise the ORB
orb = ORB.init(args, null);
}
catch(SystemException system_exception ) {
throw new RemoteException("constructor RoomBookingBridge: ",
system_exception);
}
init_from_ns();
update();
}
public void init_from_ns()
throws RemoteException, UserException {
// initialise from Naming Service
try {
// get room context
String str_name = "/BuildingApplications/Rooms/";
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
NameComponent nc = new NameComponent(str_name, " ");
NameComponent path[] = {nc};
org.omg.CORBA.Object roomRef = ncRef.resolve(path);
room_context = NamingContextHelper.narrow(roomRef);
if( room_context == null ) {
System.err.println( "Room context is null," );
System.err.println( "exiting ..." );
System.exit( 1 );
}
// get MeetingFactory from Naming Service
str_name = "/BuildingApplications/MeetingFactories/MeetingFactory";
nc = new NameComponent(str_name, " ");
path[0] = nc;
meeting_factory = MeetingFactoryHelper.narrow(ncRef.resolve(path));
if( meeting_factory == null ) {
System.err.println(
"No Meeting Factory registred at Naming Service" );
System.err.println( "exiting ..." );
System.exit( 1 );
}
}
catch(SystemException system_exception ) {
throw new RemoteException("Initialise ORB", system_exception);
}
}
public void update()
throws RemoteException, UserException {
try {
// list rooms
// initialise binding list and binding iterator
// Holder objects for out parameter
BindingListHolder blHolder = new BindingListHolder();
BindingIteratorHolder biHolder = new BindingIteratorHolder();
BindingHolder bHolder = new BindingHolder();
Vector roomVector = new Vector();
Room aRoom;
// we are 2 rooms via the room list
// more rooms are available from the binding iterator
room_context.list( 2, blHolder, biHolder );
// get rooms from Room context of the Naming Service
// and put them into the roomVector
for(int i = 0; i < blHolder.value.length; i++ ) {
aRoom = RoomHelper.narrow(
room_context.resolve( blHolder.value[i].binding_name ));
roomVector.addElement( aRoom );
}
// get remaining rooms from the iterator
if( biHolder.value != null ) {
while( biHolder.value.next_one( bHolder ) ) {
aRoom = RoomHelper.narrow(
room_context.resolve( bHolder.value.binding_name ) );
if( aRoom != null ) {
roomVector.addElement( aRoom );
}
}
}
// convert the roomVector into a room array
rooms = new Room[ roomVector.size() ];
roomVector.copyInto( rooms );
// be fiendly with system resources
if( biHolder.value != null )
biHolder.value.destroy();
}
catch(SystemException system_exception) {
throw new RemoteException("View", system_exception);
// System.err.println("View: " + system_exception);
}
}
public void cancel(int selected_room, int selected_slot)
throws RemoteException, NoMeetingInThisSlot {
try {
rooms[selected_room].Cancel(
Slot.from_int(selected_slot) );
System.out.println("Cancel called" );
}
catch(SystemException system_exception) {
throw new RemoteException("Cancel", system_exception);
}
}
public void book(String purpose, String participants,
int selected_room, int selected_slot)
throws RemoteException, SlotAlreadyTaken {
try {
Meeting meeting =
meeting_factory.CreateMeeting(purpose, participants);
System.out.println( "meeting created" );
String p = meeting.purpose();
System.out.println("Purpose: "+p);
rooms[selected_room].Book(
Slot.from_int(selected_slot), meeting );
System.out.println( "room is booked" );
}
catch(SystemException system_exception ) {
throw new RemoteException("Booking system exception", system_exception);
}
}
/**
* return a list of the rooms as portable JavaRooms
*/
public JavaRoom[] getRooms() {
int len = rooms.length;
JavaRoom[] jrooms = new JavaRoom[len];
for (int n = 0; n < len; n++) {
jrooms[n] = new JavaRoomImpl(rooms[n]);
}
return jrooms;
}
public JavaMeeting[] getMeetings(int room_index) {
Meeting[] meetings = rooms[room_index].View();
int len = meetings.length;
JavaMeeting[] jmeetings = new JavaMeeting[len];
for (int n = 0; n < len; n++) {
if (meetings[n] == null) {
jmeetings[n] = null;
} else {
jmeetings[n] = new JavaMeetingImpl(meetings[n], orb);
}
}
return jmeetings;
}
} // RoomBookingBridgeImpl
The Java classes and servers implementing the CORBA objects are mainly unchanged from
the implementations given in the Vogel and Duddy book. They can continue to act
as CORBA servers to the original clients. I replaced the ``easy naming'' naming service
by a later one with the slightly more complex standard mechanism for creating
contexts and placing names within this context. This can use the tnameserv
CORBA naming server, for example.
The Vogel and Duddy room booking client was messed around a little bit, but its essential structure remained unchanged. The GUI elements, for example, were not altered. All CORBA-related code was hived off into the bridge classes.
The Vogel and Duddy code samples can all be downloaded from a public Web site
(http://www.wiley.com/compbooks/vogel),
and come with no author attribution or copyright claim. The client is also
quite lengthy since it has plenty of GUI inside. So I won't complete the
code listing here.
The code for all my classes, and the modified code of the Vogel and Duddy classes,
is given in the subdirectory corba
of the programs.zip
file.
The IDL interface RoomBooking.idl
needs to have an IDL to Java run
on it. This produces classes in the package corba.RoomBooking
.
These can all be compiled using the standard Java classes and any CORBA classes
needed.
The Jini server, service and client are also normal Java files, and can be compiled like earlier Jini files, with the classpath set to include the Jini libraries.
There are a large number of elements and processes that must be set running to get this running satisfactorily
tnameserv -ORBInitialPort 1055
All CORBA services and clients should also use this port number
java corba.RoomBookingImpl.RoomServer "freds room" -ORBInitialPort 1055
java corba.RoomBookingImpl.MeetingFactoryServer -ORBInitialPort 1055
rmid
, and HTTP servers to move class definitions
around
java corba.RoomBookingImpl.RoomBookingBridgeServer -ORBInitialPort 1055
java corba.RoomBookingImpl.RoomBookingClientApplication -ORBInitialPort 1055
Both of the examples in this chapter started life as pure CORBA systems written by other authors, with CORBA objects delivered by servers to a CORBA client. The clients were both migrated to Jini clients of a Jini service acting as a front-end to CORBA objects, in a series of steps. For those in a similar situation, it may be worthwhile to spell out the steps I went through in doing this for the room booking problem.
RoomBookingBridgeImpl
. At this stage, the
RoomBookingBridge
interface was not defined:
that came after the implementation was completed (ok, I hang my head in shame,
but I was trying to adapt existing code rather than starting from scratch).
At this time, the client was still running as a pure CORBA client - no Jini
mechanisms had been introduced.
JavaRoom
and the implementations as
JavaRoomImpl
. The GUI code in the client had no need to directly
modify fields in these objects, so they ended up as ``readonly''
versions of their CORBA sources.
(If a fat proxy had been used, this step of creating portable Jini objects
would not have been necessary.)
RoomBookingBridgeImpl
was changed to return these from its methods.
Again, this was all still done within the CORBA world, and no Jini
services were yet involved. This looked like a good time to define the
RoomBookingBridge
interface, when everything had settled down.
RoomBookingBridgeImpl
was turned into a
UnicastRemoteObject
and placed into a Jini server.
The client was changed to lookup a RoomBookingBridge
service rather than create a RoomBookingBridgeImpl
object.
At the end of this, I had an implementation of a Jini service, with a thin RMI proxy. The CORBA objects and servers had not been changed at all. The original CORBA client had been split into two, with the Jini service implementing all of the CORBA lookups. These were exposed to the client through a set of ``facades'' that gave it the information it needed.
The client was still responsible for all of the GUI aspects, and so was acting
as a ``knowledgeable'' client. If needed, these GUI elements could be placed
into Entry
objects, and also exported as part of the service.
We have looked at making CORBA objects into Jini services. Is it possible to go the other way, and make a Jini service appear as a CORBA object in a CORBA federation? Well, it should be. Just as there is a mapping from CORBA IDL to Java, there is also a mapping of a suitable subset of Java into CORBA IDL. So a Jini service interface can be written as a CORBA interface. A Jini client could then be written as the implementation of a CORBA serve to this IDL.
At present, with a paucity of Jini services, it does not seem worthwhile yet to explore this in detail. This may change in the future, though.
CORBA is a separate distributed system to Jini. However, it is quite straightforward to build bridges between the two systems, and there are a number of different possible architectures. This makes it possible for CORBA services to be used by Jini clients.