Add the power of CORBA to our distributed whiteboard

Find out how to transport serialized Java objects to and from an applet client and a Java server using CORBA

You gotta be Java -- and 1.1-patched at that.

Play with the CORBAtized whiteboard!

Note: You must use Appletviewer 1.1 or an AWT 1.1-enabled version of Netscape to view and manipulate the whiteboard (see Resources for a link to the patch required to bring Netscape up to speed).

Whiteboard history -- from sockets, RMI, and servlets to CORBA The last installment of Step by Step illustrated how to use a servlet as the server element of the collaborative whiteboard system that we've been examining of late. Because the most natural and handy use for servlets is as Web server extensions, we developed a whiteboard communications layer that used the HTTP protocol to communicate between clients and an HTTP servlet.

The client, implemented as an applet, used URLs to HTTP POST a message to the servlet, which maintained the shared whiteboard state. The servlet responded with a message of its own during the response portion of the same HTTP POST. The messages passed over HTTP were actual Java objects, serialized using the 1.1 serialization mechanism.

The interesting thing about this communications implementation is that it's hidden within ObservableList, which is the client data structure that contains the actual list of whiteboard objects to be displayed. As far as the whiteboard is concerned, it doesn't matter whether we're using RMI, Berkeley sockets, or HTTP to effect communications. In fact, the servlet-based communications layer was substituted in place of a similar one using RMI, which in turn replaced a layer that used sockets.

How do these different communication mechanisms measure up? Sockets and HTTP have the advantage of being simple, flexible, and fast. Sockets allow realtime communication. HTTP solves the problem of communicating through firewalls. On the other hand, they share the disadvantage of being low-level mechanisms, which means that you -- the developer -- have to provide a lot of application-specific code if you want to use them to enable communications. In fact, you must provide a messaging protocol as well as the message transport.

What we're really looking for is a direct object-to-object communication mechanism that would allow objects to call others' methods directly. RMI provides this, and hides the mechanics of it from the developer. But what if we want to implement the server in C++ or some other non-Java language, or we want to connect to a legacy system of some kind? We'd be out of luck because RMI is for JVM-to-JVM communications only.

That leaves us with only one alternative: CORBA.

It turns out that CORBA solves these problems for us. CORBA provides a generalized way to "glue" objects together even if they're implemented in different languages and/or are physically distributed. A word of warning: CORBA is a very powerful and complicated beast. We'll only scratch the surface here, but with the techniques you learn in this column you'll have the fundamentals for using CORBA as a distributed computing mechanism for Java.

Crash course on CORBA

Common Object Request Broker Architecture, or simply CORBA, is a high-level integration technology for distributed applications. The CORBA standard is maintained by the Object Management Group (OMG), is currently in version 2, and enjoys widespread industry support.

As an integration technology, CORBA is used to allow distributed objects to communicate with each other. CORBA is interesting because it isn't language-dependent and it's (mostly) not even vendor-dependent.

The components of CORBA

There are a number of technologies associated with CORBA. We'll cover only the core ones here.

  • The Object Request Broker (ORB) is the "software bus" between distributed objects. The ORB keeps track of the location of remote server objects and makes local objects available remotely as servers. All the developer has to do to obtain (or provide) object access to remote systems is interface with the local ORB. An ORB doesn't run in a process as a standard service does. Instead, it exists as a middleware layer that is located partially at the server (the "skeleton") and partially at the client (the "stub"). Of course, a service that listens for incoming requests from other ORBs has to run on any machine that wishes to provide server objects. ORBs are often integrated into Web browsers and OSs, and an ORB can function as both client and server, depending on the needs of the applications that make use of it. A typical client/server application would use a server application that only handles server responsibilities and a client that handles only client responsibilities. This is how we'll architect our communications layer in this installment.
  • Internet Inter-ORB Protocol (IIOP) is the protocol that ORBs use to communicate over TCP/IP networks. As applications developers, we don't have to worry too much about its internal workings -- we just need to verify that support for it is present in the ORB we choose. IIOP implementation is required for an ORB to be fully compliant with CORBA 2, the latest version.
  • Interface Description Language (IDL) is used to specify the interface between the client ORB and the server ORB. The IDL interface specification is then compiled via an IDL-to-implementation language compiler to produce stub and a skeleton code. Ideally, the IDL will enable your ORB to communicate with other vendors' ORBs. In practice, this isn't always the case, but between language-specific versions of a single vendor's ORB (for example, Visigenic's C++ ORB and Visigenic's Java ORB) the vendor's IDL can be expected to be 100 percent compatible.
  • Business Objects are what you as the applications developer produce. OMG defines business objects as high-level representations of things that exist in a business domain. Examples would be a customers, orders, and employees.

CORBA development steps

Here's the general process to follow to get your Java CORBAtized distributed app up and running. We're going to go for the gusto and use CORBA over the Web with an applet-based client instead of using a standalone client.

  1. Get and set up an ORB on your Web server. You can use ORBs from Visigenic, IONA, Expersoft, or any of a number of other vendors. This installation will be used by the CORBA server. The installation instructions should be provided with the ORB. Make its classes available under your Web tree so that clients can load them. This is not necessary if you plan to use Netscape's built-in Visigenic ORB, which is preferable if your clients will all access using Netscape.
  2. Define the server interface for your application in an IDL file.
  3. Compile the interface for the server object from IDL into (in this case) Java using the idl2java compiler. This tool will generate a set of helper classes, including the Java interface class itself.
  4. Write the server class -- for example, CorbaListServerImpl. The server will extend the class _server interface nameImplBase. It will implement the exact method signatures found in the Java server interface class that idl2java generated from the IDL definitions. In our case, this interface class is called CorbaListServer. In our case, the server will also have its own main () method so that it can export itself to the world when you start it. Generally speaking, the server object doesn't have to have its own main () method and export itself; instead, it could be exported by some other object that provides a thread for it.
  5. Write the client class. The client should register with its local ORB and create a reference to the remote server object. It can then use this reference to make calls directly on the remote server object.
  6. Compile all the code.
  7. Start the listening services for the server side. Visibroker's osagent and gatekeeper are the relevant services for our purposes. Osagent's default port is TCP 14000 and gatekeeper's is TCP 15000, so if you want to allow access behind a firewall you'll have to punch holes in the firewall at those ports.
  8. If your client is an applet, add the appropriate parameters to your HTML tag.
  9. Start the server object with java CorbaListServerImpl. Clients can now connect to your server!

Now it's time to put theory into practice and apply these steps to our whiteboard app.

Getting and setting up the ORB

The first thing to do to get up and running with CORBA is to acquire and install an ORB. We're going to use the Visigenic ORB for our project, because it is embedded in Netscape Navigator (use NS version 4.0.4 or later). Check the Resources section for the link to Visigenic's download site. We're going to use the Visigenic Java ORB, which is called VisiBroker 3.1 for Java, evaluation version.

After you've set up the VisiBroker for Java on your system, make sure you're using JDK 1.1.3 or later and that your JDK bin directory is included in your PATH. Also, your CLASSPATH must include the Visigenic classes, which are probably in c:\visigenic\vbroker\lib\ (under Windows). Create a separate entry in your CLASSPATH for each of the three JAR files in that directory. If you like, you can use the Visigenic Java compiler (vjc) instead of javac and you don't have to set your CLASSPATH to include the Visigenic classes.

Now it's time to put the classes up on the Web server. Copy the com subdirectory to the directory in your Web tree from which the applet will launch.

Now you're ready to define an IDL interface for the server.

CORBAtized whiteboard system architecture

Defining the interface with IDL

IDL is not a programming language; rather, it's an interface specification "language" for CORBA objects. A comprehensive (or even remotely complete) treatment of IDL is beyond the scope of this article, but the Resources section provides links to more information about IDL and the Visigenic mapping of IDL to Java.

The following segment is the IDL that defines the server interface.

module corbawb { typedef sequence<octet> SerializedObject; interface CorbaListServer { SerializedObject addElement (in SerializedObject element); SerializedObject replaceElement (in SerializedObject id, in serializedObject element); SerializedObject getUpdate (in long updateCount); };

};

The first thing this IDL file does is define the CORBA module, which maps to the Java package mechanism. The Java versions of interfaces defined in the module, as well as all helper classes generated by idl2java, will be placed into the package corbawb. We're putting them in corbawb with the other classes for the application.

Note that IDL provides a C++ -like syntax. This explains the typedef construct, which maps the CORBA type sequence<octet> to our name SerializedObject. A SerializedObject is what we will use to pass Java byte arrays that contain objects being passed by value. These objects will be passed from clients to server and vice versa.

The server The server consists of a Java server interface produced from IDL and a server implementation that implements it. The server implementation is assisted by a skeleton class and some other helpers produced by the IDL compiler. The skeleton class knows how to do the networking functions of CORBA (which you would have to write yourself with a sockets-based approach).

Class CorbaListServer

This is the code produced by idl2java, and it is the interface that we must implement in CorbaListServerImpl. It's shown here minus the comments that idl2java includes.

 
  package corbawb;
public interface CorbaListServer extends org.omg.CORBA.Object {
  public byte[] addElement (byte[] element);
  public byte[] replaceElement (byte[] id, byte[] element);
  public byte[] getUpdate (int updateCount);
}

Class CorbaListServerImpl

Here we see the imports for the ORB and BOA classes, which together manage the process of making the server available to clients and handling their requests.

package corbawb;

import org.omg.CORBA.ORB; import org.omg.CORBA.BOA; import org.merlin.step.dec.IDList;

public class CorbaListServerImpl extends _CorbaListServerImplBase {

It's highly important to note that our server implementation extends _CorbaListServerImplBase, which is produced by the IDL compiler. _CorbaListServerImplBase itself is declared to implement the CorbaListServer interface, which, of course, means that we have to provide the implementations of those methods in CorbaListServerImpl.

protected IDList list = new IDList ();

public CorbaListServerImpl (String name) { super (name); }

// CorbaListServer interface's method implementations public byte [] addElement (byte [] elementBytes) { Object element = CorbaWBUtil.deserialize (elementBytes); return CorbaWBUtil.serialize (list.addElement (element)); }

public byte [] replaceElement (byte [] idBytes, byte [] elementBytes) { Object id = CorbaWBUtil.deserialize (idBytes); Object element = CorbaWBUtil.deserialize (elementBytes); return CorbaWBUtil.serialize (list.replaceElement (id, element)); }

public byte [] getUpdate (int updateCount) { return CorbaWBUtil.serialize ((list.getUpdateCount () == updateCount) ? null : list.clone ()); }

Here we see details of the constructor and CorbaListServer interface method implementations. Notice that we're passing in and returning byte arrays. This is the interface that the IDL compiler generated for us as compatible with CORBA.

Each use of an object is wrapped in a call to a method of CorbaWBUtil, which serializes the object to a byte array or deserializes the object from a byte array as needed.

  
  public static void main (String[] args) {
    ORB orb = ORB.init (args, null);
    BOA boa = orb.BOA_init ();
    boa.obj_is_ready (new CorbaListServerImpl ("CorbaListServer"));
    System.out.println ("CorbaListServerImpl is ready.");
    boa.impl_is_ready ();
  }
}

The main () method provides the code that actually makes the server object available to clients. The call boa.impl_is_ready () listens for requests.

Now let's turn our attention to the client side.

The client The whiteboard, as you recall, uses a distributed data structure called ObservableList to store elements. Distributed implementations using sockets and RMI simply subclassed ObservableList and overrode method implementations to change the type of networking used. In both cases, a server provided a central IDList to keep track of elements shared among the clients.

This month we'll extend ObservableList to provide a CORBA-based implementation that will work with the server developed above.

Class CorbaList

package corbawb;

import java.io.*; import java.util.*; import java.net.*; import java.applet.*;

import org.merlin.step.dec.IDList; import org.merlin.step.dec.ObservableList;

public class CorbaList extends ObservableList implements Runnable {

public static final int UPDATE_DELAY = 10 * 1000; Applet parent;

IDList list; Thread processor; CorbaListAdapter adapter;

public CorbaList (Applet parent) { list = new IDList (); adapter = new CorbaListAdapter (parent); update (); processor = new Thread (this); processor.start (); }

This code just extends ObservableList and sets up a thread to do update calls in the background. It also instantiates one copy of CorbaListAdapter, which is the actual CORBA client. CorbaList makes calls to the adapter which translates them to CORBA format and calls the CORBA server.

The adapter class allows us to use CORBA's byte arrays to transfer objects around. The adapter class deals with the mechanics of the byte arrays and returns regular objects to CorbaList.

public Enumeration elements () { return list.elements (); }

public synchronized void addElement (Object element) { Object id = adapter.addElement (element); list.addElementWithID (id, element); }

public synchronized void replaceElementAtEnd (Object oldElement, Object element) { Object oldID = list.getIDOfElement (oldElement); if (oldID != null) { Object id = adapter.replaceElement (oldID, element); if (id != null) list.replaceElementWithID (oldID, id, element); else System.out.println ("Unknown id:" + oldID); } }

protected synchronized void update () { IDList newList = adapter.getUpdate (list.getUpdateCount ()); if (newList != null) { list = newList; fireUpdate (); } }

These are the methods from last time that specialize ObservableList for use as a distributed datastructure. Notice that we call adapter's methods to access the server. All methods are synchronized to avoid corrupting list.

public void run () { while (processor == Thread.currentThread ()) { try { Thread.sleep (UPDATE_DELAY); update (); } catch (Exception ignored) { ignored.printStackTrace (); } } }

public void stop () { processor = null; } }

This segment shows the update thread, which polls the server periodically for updates. Note that the thread-stopping semantics are in line with the new JavaSoft recommendations for stopping threads without using the Thread.stop () call, which has been deprecated in JDK 1.2.

Class CORBAListAdapter

The CorbaListAdapter is the actual CORBA client. Its constructor sets up the client-side ORB, which may be loaded over the wire or be located already in the client Web browser. The HTML applet parameters specify which of these options will actually happen.

package corbawb;

import java.applet.*;

import org.omg.CORBA.SystemException; import org.omg.CORBA.ORB; import org.merlin.step.dec.IDList;

public class CorbaListAdapter {

CorbaListServer server;

public CorbaListAdapter (Applet parent) { ORB orb = ORB.init (parent, null); server = CorbaListServerHelper.bind (orb, "CorbaListServer"); System.out.println ("Client ORB initialized."); }

Notice the call to CorbaListServerHelper.bind (). This class was created by idl2java when we did the IDL to Java compile earlier.

public Object addElement (Object element) { try { byte [] elementBytes = CorbaWBUtil.serialize (element); byte [] idBytes = server.addElement (elementBytes); return CorbaWBUtil.deserialize (idBytes); } catch (SystemException ignored) { ignored.printStackTrace (); return null; } }

public Object replaceElement (Object oldID, Object element) { try { byte [] oldIDbytes = CorbaWBUtil.serialize (oldID); byte [] elementBytes = CorbaWBUtil.serialize (element); byte [] newIDbytes = server.replaceElement (oldIDbytes, elementBytes); return CorbaWBUtil.deserialize (newIDbytes); } catch (SystemException ignored) { ignored.printStackTrace (); return null; } }

public IDList getUpdate (int updateCount) { try { byte [] newIDListBytes = server.getUpdate (updateCount); Object newIDList = CorbaWBUtil.deserialize (newIDListBytes); return (newIDList != null) ? (IDList) newIDList : null; } catch (SystemException ignored) { ignored.printStackTrace (); return null; } } }

These are the wrapper methods that adapt Java objects to CORBA datastructures. These methods use the CorbaWBUtil methods to serialize objects into byte arrays and recover objects from byte arrays.

The HTML applet tag

<html><head><title>CORBA-based Whiteboard</title></head>
<body bgcolor="#ffffff">
<applet code="corbawb.CorbaWBLauncher" width=96 height=96>
<param name=org.omg.CORBA.ORBClass value=com.visigenic.vbroker.orb.ORB>
<PARAM name=ORBgatekeeperIOR value="http://yourserver:15000/gatekeeper.ior">
<PARAM name=USE_ORB_LOCATOR value=true>
You gotta be Java.
</applet>
</body></html>

This is the applet tag that enables us to use the client in a Web browser. Most of the parameters are Visigenic-specific directives. <yourserver> should be replaced with the name of the machine that you will be using as the CORBA/Web server.

Class CorbaWBUtil

This class contains the static methods that serialize and deserialize objects into and from byte arrays. These methods are used by both the client and the server.

package corbawb;
import java.io.*;
public class CorbaWBUtil {
  public static Object deserialize (byte b []) {
    try {
      ByteArrayInputStream bais = new ByteArrayInputStream (b);
      ObjectInputStream ois = new ObjectInputStream (bais);
      return ois.readObject ();
    } catch (IOException ex) {
      ex.printStackTrace ();
      return null;
    } catch (ClassNotFoundException ex) {
      ex.printStackTrace ();
      return null;
    }
  }

The deserialize () method returns an object constructed from an array of bytes. The purpose of this operation is to adapt the CorbaList objects to the actual data that has to be sent using CORBA.

  public static byte [] serialize (Object o) {
    try {
      ByteArrayOutputStream baos = new ByteArrayOutputStream ();
      ObjectOutputStream oos = new ObjectOutputStream (baos);
      oos.writeObject (o);
      oos.flush ();
      return baos.toByteArray ();
    } catch (IOException ex) {
      ex.printStackTrace ();
      return null;
    }
  }
}

The serialize () method returns a byte array constructed from an object. Like deserialize (), this method is used to adapt CorbaList objects to the actual data sent using CORBA.

Using the code

  1. Download the complete source (jw-03-step.tgz or jw-03-step.zip), which is located in the Resources section of the article, and untar (or unzip) the file.
  2. If you have not yet installed Visigenic's Visibroker for Java on your Web server, do so now, setting your CLASSPATH and PATH to the values discussed earlier.
  3. Launch osagent and gatekeeper. Launch gatekeeper from the step3-98 directory.
  4. Copy the Visigenic com hierarchy to the step3-98 directory and the org\omg directory to step3-98\org.
  5. Change to the step3-98 directory.
  6. Compile using the following command: javac corbawb\*.java

(If you're using Unix, be sure to use the forward slash and not the backslash in the command sequencee and paths above.)

If you customize the code, you'll have to change the corbawb.idl file, run idl2java, and use the new classes that it generates.

Notes

VisiBroker for Java has the capability to pass Java objects by value. This isn't supported yet under Netscape. More importantly, the tool for generating the support classes (java2iiop) creates helper classes in the core packages such as java.lang and so on. An applet client will refuse to load classes from the network that claim to belong to these system classes.

Also, we're pretty Java-specific with our IDL here. In a more general approach, the whiteboard would use more CORBA-friendly elements that would map directly onto a CORBA struct. This would be important if we were trying to tie in a C++ server (for instance), as it would be unable to make heads or tails of the Java objects we are sending as octet sequences/byte arrays.

Conclusion

CORBA is a useful alternative to RMI, sockets, and servlets when cross-platform client/server integration is necessary. Plus, CORBA is industrial strength in the sense that it is scalable and widely supported. You can use it for your projects when you need to make use of its powerful features.

Michael Shoffner is an official purveyor of fine foods with a wholly owned subsidiary of the RAMJAC corporation. Our motto: "I will not be pushed, filed, stamped, indexed, briefed, debriefed, or numbered. My life is my own."

Learn more about this topic

  • Step-by-step instructions on how to apply the Netscape 4.03 patch for Windows and some flavors of Unix http://developer.netscape.com/software/jdk/download.html#NT_INSTALL
  • Download the complete source as a gzipped tar file http://www.javaworld.com/javaworld/jw-03-1998/step/jw-03-step.tar.gz
  • Download the complete source as a zip file http://www.javaworld.com/javaworld/jw-03-1998/step/jw-03-step.zip
  • CORBA
  • The CORBA FAQ http://www.cerfnet.com/~mpcline/Corba-FAQ/
  • Java/CORBA whitepaper (OMG site) http://www.omg.org/news/wpjava.htm
  • Javasoft's CORBA (JAVA IDL) http://www.javasoft.com/products/jdk/idl/index.html
  • Bryan Morgan's article "CORBA meets Java" (JavaWorld, October 1997) provides a detailed introduction to CORBA and its use for developing industrial-strength applications with Java http://www.javaworld.com/javaworld/jw-10-1997/jw-10-corbajava.html
  • IIOP http://www.omg.org/news/iiop4.htm
  • Business objects http://www.omg.org/docs/1994/94-12-01.txt
  • Visigenic's Visibroker for Java
  • Visibroker for Java installation http://www.visigenic.com/techpubs/#VBJLink
  • Visibroker for Java programmer's guide http://www.visigenic.com/techpubs/htmlhelp/vbj30/pg/frames/vbj_toc.htm
  • Visbroker for Java reference guide (incl IDL->Java) http://www.visigenic.com/techpubs/htmlhelp/vbj30/ref/frames/vbj_toc.htm
  • Previous Step by Step articles

Join the discussion
Be the first to comment on this article. Our Commenting Policies