Java and CORBA - a smooth blend

By David Reilly

With the introduction of support for CORBA into Java 1.2, developers can now create distributed systems that run in a hybrid mix of Java and other CORBA-friendly languages. Java 1.2 includes everything you need to start writing CORBA services. In this article, I'll show you how to get started with Java IDL.

CORBA, for those who haven't heard of it before, stands for Common Object Request Broker Architecture. This architecture allows clients to invoke methods of objects running on remote servers. The idea is nothing new - many developers will have come across similar systems such as RPC (remote procedure call), or Java's own RMI (remote method invocation). The difference between these and CORBA is that CORBA is much more interoperable with other platforms - a C++ object running on a Wintel system can communicate with a Java object on a Unix box. Developers don't need to know what language a CORBA service is written in, or even where it is physically located. This makes CORBA systems very versatile - one can change the location of a CORBA object, and then re-register with a nameserver. Clients that look up the service can then find its new location, and then continue to make requests. Java makes it easy to integrate support for CORBA into applications and applets, thanks to the introduction of Java IDL in JDK1.2

Interface Definition Language (IDL)

Developers use the Interface Definition Language (IDL) to describe the interface to a CORBA object. An IDL schema can then be used to generate Java code for the client and server that will use the object. The same IDL schema could be used to generate either a client or server in C++, Ada, or any other language that supports CORBA. You don't write your implementation of a CORBA service in IDL - so you can continue to write in pure Java code if you so wish.

Let's start by taking a look at a sample IDL schema, to give you an idea what I'm talking about. The following schema shows a very simple CORBA service, which allows clients to get and store the name associated with an email address. For the purposes of this example, we won't worry about modifying or deleting an existing user.

// Address book system module
module address_book_system
{
    // Specify interface to our address book
    interface address_book
    {
	// Unknown user exception
	exception unknown_user {};

	// User already exists exception
	exception user_exists {};

	// Lookup name from email address
	string name_from_email(in string email)
               raises (unknown_user);

	// Lookup email from full name
	string email_from_name(in string name)
               raises (unknown_user);

	// Record a new name and email
	void record_user(in string name, in string email)
	     raises user_exists;
	};
};

Listing 1-0 AddressBook.idl

We start by declaring a module for our address book system (modules can be mapped to Java packages). Inside the module, we declare our interfaces and the exceptions they can raise. Since its such as simple system, the methods of the interface will only return a string, but we can create more complex return values if needed. As well as returning values, we accept as input two strings for our record_user method. As you can see, IDL really isn't to hard to pick up.

Once you've written your interface using IDL, you can then begin to write client and servers in the language of the choice. We'll choose Java for now, but there's no reason why the client or the server couldn't be written in another language. Provided you have an interface in IDL format, you could write code that interacts with the object it describes.

From IDL to Java

Converting an IDL schema into Java source code is quite straightforward. Sun provides a free tool, idltojava, that creates the source code for you. This tool is currently distributed separately to JDK 1.2, but is freely available for members of the Java Developer Connection.

idltojava -fno-cpp AddressBook.idl

Based on the name of your IDL schema's module, a package will be generated that contains skeleton source code for your CORBA client and server, as well a second package to cover our two exceptions. The following is a list of the files it creates for us

\address_book_system\
  _address_bookStub.java
  address_book.java
  address_bookHolder.java
  address_bookHelper.java
  address_bookPackage
  _address_bookImplBase.java

  address_bookPackage\
    unknown_user.java
    unknown_userHelper.java
    unknown_userHolder.java
    user_exists.java
    user_existsHelper.java
    user_existsHolder.java

As you can see, there are a lot of files! Fortunately, we only need three to implement a complete CORBA client and server. The first file is a servant, which serves the requests made by clients. The second is a server, which will accept requests, and the third is a standalone application which will issue requests.

Implementing a CORBA Servant

Under CORBA, a servant is responsible for handling service requests. When we ran the idltojava tool, an abstract class was created that handles most of the hard work for us. All we need to do is create an AddressBookServant class, that implements the _address_bookImplBase. It will contain the code for our three methods described in the IDL schema.

skip to next section

package address_book_system;
import  address_book_system.address_bookPackage.*;

import  java.util.Hashtable;
import  java.util.Enumeration;
//
//
// AddressBookServant
//
// This servant class is responsible for implementing
// three methods
//
//   * String name_from_email ( String email );
//   * String email_from_name ( String name  );
//   * void   record_user ( String name, String email ); 
//
//
class AddressBookServant extends _address_bookImplBase
{
    private Hashtable name2email;

    public AddressBookServant()
    {
	// Create a new hashtable to store name & email
	name2email = new Hashtable();
    }

    // Get the name of this email user
    public String name_from_email ( String email )
           throws unknown_user
    {
	if (name2email.contains(email))
	{
 	    // Begin search for that name
	    for (Enumeration e = name2email.keys(); 
                 e.hasMoreElements();)
	    {
		String name = (String) e.nextElement();
		String e_mail = (String) name2email.get(name);

		// Match on email ?
		if (email.compareTo(e_mail) == 0)
		{
			return name;
		}
	    }
	}

	// User not found - throw unknown user exception
	throw new unknown_user();
    }

    // Get the email of this person
    public String email_from_name ( String name  ) 
           throws unknown_user
    {
	// If user exists
	if (name2email.containsKey(name))
	{
		// Return email address
		return (String) name2email.get(name);
	}

	// User doesn't exist
	throw new unknown_user();
    }

    // Add a new user to the system
    public void record_user ( String name, String email )
           throws user_exists
    {
	// Is the user already listed
	if (name2email.containsKey( name ) ||
	    name2email.contains( email ) )
	{
		// If so, throw exception
		throw new user_exists();
	}

	// Add to our hash table
	name2email.put (name, email);
    }
}

The servant class maintains a list of people's email addresses, and stores this information inside a Hashtable. When the CORBA server receives a request, it will call upon the name_from_email, email_from_name, and record_user methods of the servant to fulfill the request.

Writing a CORBA server

Writing a CORBA server is pretty straightforward. We need to create an ORB (object-request-broker), and register our servant with it. We'll also notify a nameserver of our ORB, so that CORBA clients can access our address book. Once we've registered, CORBA clients will be able to look for our interface, and send requests through our ORB to the servant object.

skip to next section

package address_book_system;
import org.omg.CORBA.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;

//
//
// AddressBookServer
//
// This server is responsible for creating an object
// request broker (ORB), and registering an AddressBookServant
// with a naming service
//
//
public class AddressBookServer 
{
    public static void main(String args[])
    {
	try
	{
		// Create a new object request broker
		ORB orb = ORB.init(args, null);

		// Create a new address book ...
		AddressBookServant servant = new AddressBookServant();

		// ... and connect it to our orb
		orb.connect(servant);

		// Object Request Broker Initialised
		System.out.println ("Address book ORB initialised");

		// Obtain reference for our nameservice
		org.omg.CORBA.Object object = 
		orb.resolve_initial_references("NameService");

		// Since we have only an object reference, we must
		// cast it to a NamingContext. We use a helper
		// class for this purpose
		NamingContext namingContext = 
		NamingContextHelper.narrow(object);

		// Add a new naming component for our interface
		NameComponent list[] = 
		{ new NameComponent("address_book", "") };

		// Now notify naming service of our new interface
		namingContext.rebind(list, servant);

		// Wait for clients
		for (;;){}
	}
	catch (Exception e)
	{
		System.err.println ("ORB Error - " + e);
		e.printStackTrace(System.out);
		System.exit(0);
	}
    }
}

Writing a CORBA client

Now that we have a finished server, we need to write a client to show that the server works. Our client is very simple. It looks up our AddressBook service through a naming service, and then sends requests in response to commands made by the user. The client also traps exceptions thrown by the AddressBook service, in the event that a user's details doesn't exist or that a duplicate is being inserted into the system.

skip to next section

package address_book_system;
import address_book_system.address_bookPackage.*;

import org.omg.CORBA.*;
import org.omg.CosNaming.*;
import java.io.*;

//
//
// AddressBookClient
//
// This client demonstrates the AddressBook server and servant.
// A menu is presented to the user, allow he or she to add
// users, and look up their names & email addresses
//
//
public class AddressBookClient 
{
    public static void main(String args[]) throws IOException
    {
        try
	{
	    // Create an object request broker
	    ORB orb = ORB.init(args, null);

	    // Obtain object reference for name service ...
            org.omg.CORBA.Object object = 
            orb.resolve_initial_references("NameService");
			
            // ... and narrow it to a NameContext
            NamingContext namingContext = 
            NamingContextHelper.narrow(object);

            // Create a name component array
            NameComponent nc_array[] =
            { new NameComponent("address_book","") };

            // Get an address book object reference ...
            org.omg.CORBA.Object objectReference = 
            namingContext.resolve(nc_array);

            // ... and narrow it to get an address book
            address_book AddressBook = 
            address_bookHelper.narrow(objectReference);

            // DataInputStream for system.in
            DataInputStream din = new DataInputStream 
            (System.in);

            for (;;)
            {
                try
                {
		    // Print menu
                    System.out.println ("1- Add user");
                    System.out.println ("2- Look up email");
                    System.out.println ("3- Look up name");
                    System.out.println ("4- Exit");

                    System.out.print ("Command :");

                    // Read a line from user
                    String command = din.readLine();

                    // Convert to number
                    Integer num = new Integer (command);
                    int choice = num.intValue();

                    // Variables we'll need for service calls
                    String name;
                    String email;

                    switch (choice)
                    {
                        case 1:
			System.out.print ("Name:");
			name  = din.readLine();
			System.out.print ("Email:");
			email = din.readLine();

			// Call AddressBook service
			AddressBook.record_user(name,email);
			break;
			
			case 2:
			System.out.println ("Name:");
			name  = din.readLine();

			// Call AddressBook service
			email = AddressBook.email_from_name(name);
			System.out.println ("Email of " + name + " is " + email);
			break;
			
			case 3:
			System.out.println ("Email:");
			email  = din.readLine();

        		// Call AddressBook service
			name = AddressBook.name_from_email(email);
			System.out.println ("Name of " + email + " is " + name);		
			break;
	
			case 4:
			System.exit(0);
                    }
                }
		catch (user_exists  already_there)
		{
		    System.out.println ("User already exists - cannot be added to address book");
		}
		catch (unknown_user bad_user)
		{
		    System.out.println ("User doesn't exist");
		}
            }
        }
	catch (Exception e)
	{
            System.err.println ("CORBA error - " + e);
	}
    }
}

Putting it all together

Now that we have a CORBA server and a CORBA client, the next step is to have the client connect to the server. If you've already compiled the source code, then you're ready to begin. Otherwise, download the source code (available as a .ZIP file), and extract it to its own directory.

Next, you need to do the following :

  1. Start the nameservice that comes with JDK1.2
  2. Start the server
  3. Start the client

Start the nameservice that comes with JDK1.2

If your Java installation directory is in the path, you can type the following from your prompt

tnameserv -ORBInitialPort 2000

The port parameter is necessary for Unix users who aren't logged it as root. Windows users can omit the parameter. If you do specify a port parameter, make sure you do so for the client and server as well.

Start the server

All of the examples are part of the address_book_system package. Some users have reported problems running the examples. Make sure that the current directory is in the classpath, and then go to the directory where you've compiled the examples, or where you've unzipped them. Next, start the server

java address_book_system.AddressBookServer 
     -ORBInitialPort 2000 
     -ORBInitialHost myhost

The port parameter is optional (but needed if you've specified a port for your nameservice). You'll also need to specify the hostname if you run the server on a different machine to the nameservice (omit it if running everything locally).

Start the client

You'll need to go to the directory where the examples are stored. Make sure that the nameservice and server are already running, and then start the client

java address_book_system.AddressBookClient -ORBInitialPort 2000
     -ORBInitialHost myhost

The port parameter is optional (but needed if you've specified a port for your nameservice). You'll also need to specify the hostname if you run the client on a different machine to the nameservice (omit it if running everything locally).

Running the client

The client is capable of invoking all the methods defined in our IDL. It can record new users, look up an email address, and look up a name. While the demonstration is relatively simple, it does show how easy it is to write clients that interact with services on remote systems. This demo is a standalone application, but you could just as easily write an applet that implemented the same functionality.

1- Add user
2- Look up email
3- Look up name
4- Exit
Command :1
Name: David Reilly
Email: dodo@fan.net.au
1- Add user
2- Look up email
3- Look up name
4- Exit
Command :2
Name:
David Reilly
Email of David Reilly is dodo@fan.net.au

Summary

CORBA technology offers developers many benefits over other techniques for distributed computing, but its most compelling is the ease with which developers can create distributed services that are platform independent. Once a service is described by an IDL schema, the developer can choose to implement either client or server (or both) in Java. Being able to use Java is an attractive option, because Java brings platform portability - but its nice to know that one can also connect a C++ module to Java. CORBA allows developers to connect objects together, and developers can still choose the versatility and familiarity of Java.

A further advantage is that CORBA services can be moved throughout the network or intranet. Java 1.2 includes a nameservice, which allows ORBs to register (or bind) their services. This means that services can be moved from system to system as needed, and that clients don't need to be aware of the physical location of the service. CORBA is a powerful technology for distributed computing, and the introduction of support for CORBA in JDK1.2 offers developers  greater freedom when they create distributed systems.

Resources

CORBA Case Studies, http://www.corba.org/

Object Management Group (OMG), http://www.omg.org/

Java System Development Kit 1.2, http://java.sun.com/products/jdk/1.2/

Don't wear out your fingers typing in code!

All of the source code, examples, and demonstrations for this article are available from

ftp://ftp.davidreilly.com/pub/jcb/code/javaidl.zip

Back to main


Copyright 1998, 1999, 2000 David Reilly

Privacy | Legal | Linking | Advertise!

Last updated: Monday, June 05, 2006