Bridging Allegro CL Applications to Java with ORBLink

This example illustrates how to connect a simple Java GUI to Lisp server using CORBA. We will use ORBLink as the Lisp ORB and Visibroker for Java as the Java ORB.

In order to run the example, you'll need Java, which you can get from JavaSoft, or elsewhere. The example we give today uses the JavaSoft version of the Java VM version 1.1. To use Java CORBA requires a third party ORB, and we use Visibroker. (Of course, CORBA support will be bundled into the JDK 1.2, although the Visibroker ORB is nonetheless more industrial strength than JavaSoft's bundled ORB). You can get trial copies of Visibroker for free from Inprise.

The example application is borrowed from February/March Java Pro article written by Luke Andrew Cassidy-Dorion (the article and the Java code are used with permission). All code can be found here. There is a Makefile for making the Java side, but we'll actually go through the steps here.

This example is a distributed Chat application. The Chat application consists of an arbitrary number of Java based Listeners, each of which permits you to type in comments, and an Allegro CL server, which broadcasts comments to all the other Listeners.

Designing the Application
CORBA IDL (Interface Definition Language) is a specification language that describes behavior, not implementation. We give as an example a simple Chat Room server and Chat Room listeners application. ChatListenerI is implemented by the Java code and ChatServerI is implemented in Lisp. When the server calls messageReceived the actual code runs remote in the Java client. ChatServerI is implemented in the Lisp server. When a new Java client listener starts up, it calls addListener to register itself with the server. When input gets typed into a Java client listener, the client calls sendMessage to send its input to the server which, in its turn, calls messageReceived (with the text it just got from the listener that called sendMessage) on each of the registered listeners. The code for messageReceived in each listener displays the text of the message.

The interface definitions are below

module chat{
   interface ChatListenerI{
     void messageReceived (in string message);
   interface ChatServerI{
     void addListener (in ChatListenerI listener);
     void sendMessage(in string message);

The Server Implementation
Here is the server implementation (it's all in the file which is in the files you have downloaded). We define a class my-server, which will inherit from a class automatically generated by the Allegro OrbLink IDL compiler. By convention, this generated class chat:ChatServerI-servant is in a package with the same name as the IDL module and is named for the interface with -servant attached.

          (defclass my-server (chat:ChatServerI-servant)
             ((listeners :initform nil :accessor get-listeners)))

The class my-server has a slot, listeners that will contain all the registered listeners. To complete the implementation, we need to define the methods addListener and sendMessage. Adding a listener just pushes the new listener onto the slot (remember, when a Java listener instantiates itself, the first thing it does is call addListener). The method sendMessage takes the message sent by a listener and broadcasts it to all the listeners by successively calling messageReceived on each listener in turn. The code for displaying the message in each listener is the implementation code for messageReceived, which, of course, is written in Java.

          (corba:define-method addListener ((this my-server) listener)
             (push listener (get-listeners this)))
          (corba:define-method sendMessage ((this my-server) message)
             (dolist (listener (get-listeners this))
                (op:messageReceived listener message)))

The Client Implementation
On the Java side, things are a bit more complex and we won't show the full implementation, just a portion of the initialization method that shows how each listener finds and registers itself with the server.

          private void doConnect(){
                 orb = ORB.init();
                 boa = orb.BOA_init();
                 org.omg.CORBA.Object obj = IorIo.resolve(orb, filename);  // filename is the location of the IOR
                 server = ChatServerIHelper.narrow(obj);
                 ChatListenerI listener = new Listener();
                // ... more stuff ... //

The way the Java code finds the proxy for the server is to use an IOR or Interoperable Object Reference, which has the information about where the server is and how to connect to it. An IOR is an encoded string of digits containing things like the IP address, the port number, and any other information needed to connect to a server. In this example, as you will see a little later, each Java client is launched with the filename containing the IOR as an argument. The Lisp server is responsible for writing the IOR into the file when it starts up.

A diagram of the architecture illustrates how it will all work:

Chat Architecture Graphic

Starting up the Lisp Chat server
So here's how we would start up the server (assuming you have changed into the directory where you have the code) and started up an Allegro CL with Allegro OrbLink loaded in:

          (corba:idl "chat.idl")    ;; load in the IDL and define the  interfaces
          (load "")  ;; load in the chat-server implementation

          ;; start the server
          (setq server (make-instance 'my-server))
          ;; and write out the IOR
          (orblink:write-ior-to-file server "chat.ior")

Starting up the Java Chat clients
In the directory where you have put the sample code, there is a Makefile which will automatically compile the Java code, but here are the steps spelled out.

  1. Generate the Java code from chat.idl by using the command:
    idl2java chat.idl
  2. Generate the application using the Visibroker wrapping for the java CORBA compiler:

You are now ready to launch clients using: vbj ChatClient chat.ior

Your CORBA based client/server chat application is up and running!