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
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.
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.
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.
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 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.
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); } } }
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.
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); } } }
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 :
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.
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).
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).
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
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.
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 |
Copyright 1998, 1999, 2000 David Reilly
|
Privacy | Legal | Linking | Advertise! |
Last updated:
Monday, June 05, 2006
|