Java IDL: Java Meets CORBA - Part 2

Gopalan Suresh Raj

The TimeServer example

Now let’s go through the tasks involved in building a CORBA-distributed application using Java IDL. We can build a Time Server program as a distributed application, with both applet and application clients. The Time Server program has a single operation, which returns the current time of a server machine to any client that requests it.

Figure 5 shows a diagramatic representation of what actually goes on the wire when a client calls our TimeServer CORBA object.

Figure 5: The TimeServer example

The operations involved are as follows:

1. The client (applet or application) invokes the getTime() operation of the TimeServer.

2. The ORB transfers that invocation to the TimeServer object registered for that IDL interface.

3. The TimeServer 's getTime()method runs, returning a Java String.

4. The ORB transfers that String back to the client.

5. The client prints the value of the String.

Defining the IDL

The OMG IDL is a purely declarative language designed for specifying programming-language-independent operational interfaces for distributed applications. OMG specifies a mapping from IDL to several different programming languages, including C, C++, Smalltalk, COBOL, Ada, and Java. When mapped, each statement in OMG IDL is translated to a corresponding statement in the programming language of choice. You can use the tool idltojava to map an IDL interface to Java and implement the client class. When you map the same IDL to C++ and implement the server in that language, the Java client and C++ server interoperate through the ORB as though they were written in the same language.

As was previously mentioned, the IDL interface defines a contract between the client and server parts of your application, specifying what operations and attributes are available. When you run the idltojava compiler on your IDL code, your IDL code is mapped to equivalent Java code automatically. You can then go about writing any of your implementation code.

Our IDL definition for the TimeServer object looks like this:

module Tracker {
 interface Time {
  string getTime();
 };
};

Listing 2: Time Tracker CORBA IDL

A CORBA module is a namespace that acts as a container for related interfaces and declarations. It corresponds closely to a Java package. Each module statement in an IDL file is mapped to a Java package statement. We have defined a module called Tracker.

Like Java interfaces, CORBA interfaces declare the API contract that an object has with other objects. Each interface statement in the IDL maps to a Java interface statement when mapped. When you compile the IDL, this statement generates an interface statement in the Java code. We have defined an interface called Time. Your client and server classes may implement this Time interface in different ways.

CORBA operations are the behavior that servers promise to perform on behalf of clients that invoke them. Each operation statement in the IDL generates a corresponding method statement in the generated Java interface. In the previous IDL, getTime() is one such operation.

The tool idltojava reads OMG IDL files and creates the required Java files. The idltojava defaults are set up so if you need both client and server files, you simply enter the tool name and the name of your IDL file. Compile the IDL from the command line.

F:\>idltojava Tracker.idl

Develop the client application

You start off by importing the required packages. The package containing our stubs is in package Tracker. Because our Client is using the Naming Service to get a reference to the server object, we need org.omg.CosNaming. Since All CORBA applications need org.omg.CORBA, we import that package too.

The listing of our Client program is shown below in Listing 3:

package ex1; 
import Tracker.*; // The package containing our stubs. 
import org.omg.CosNaming.*; // Client will use the naming service. 
import org.omg.CORBA.*; // All CORBA applications need these classes. 

public class Client { 
 public static void main (String args[]) { 
  try { 
   // Create and initialize the ORB 
   ORB orb = ORB.init (args, null); 
   // Get the root naming context 
   org.omg.CORBA.Object objRef = 
                                orb.resolve_initial_references ("NameService"); 
   NamingContext ncRef = NamingContextHelper.narrow (objRef); 
   // Resolve the object reference in naming 
   NameComponent nc = new NameComponent ("TimeServer", ""); 
   NameComponent path[] = {nc}; 
   Time timeRef = TimeHelper.narrow (ncRef.resolve (path)); 
   // Call the time server object and print results 
   String time = "Time on the Server is " + timeRef.getTime (); 
   System.out.println (time); 
  } catch (Exception e) { 
   e.printStackTrace (); 
  } 
 } 
} 

Listing 3: The TimeServer CORBA client

Creating an ORB Object

A CORBA client needs a local ORB object to perform all its marshaling and IIOP work. Every client instantiates an org.omg.CORBA.ORB object and initializes it by passing to the object certain information about itself.

We declare and initialize an ORB variable:

ORB orb = ORB.init (args, null);

The call to the ORB's init method passes in your application's command line arguments, enabling you to set certain properties at runtime.

Finding the Time Server Object

Once the application has an ORB, it can ask the ORB to locate the actual service it needs, in this case, the Time Server. A number of ways exist for a CORBA client to get an initial object reference; our client application will use the COS Naming Service specified by OMG and provided with Java IDL.

The steps involved in finding a CORBA object,using the Naming Service, are as follows.

1. Obtain the Initial Naming Context

2. Narrow the object reference

3. Find the object service in Naming

Obtaining the initial naming context

The first step in using the Naming Service is to get the initial naming context. In the previous code , we call orb.resolve_initial_references to get an object reference to the name server.

org.omg.CORBA.Object objRef = orb.resolve_initial_references ("NameService");

The string "NameService" is defined for all CORBA ORBs. When you pass in that string, the ORB returns the initial naming context, an object reference to the name service.

Narrowing the object reference

As with all CORBA object references, objRef is a generic CORBA object. To use it as a NamingContext object, you must narrow it to its proper type. The call to narrow just following the previous statement does this for us.

NamingContext ncRef = NamingContextHelper.narrow (objRef);

Here you see the use of an idltojava -generated helper class, similar in function to TimeHelper. The ncRef object is now an org.omg.CosNaming.NamingContext and we use it to access the Naming Service and find other services. We do this in the next step.

Finding a Service in Naming

Names can have different structures depending upon the implementation of the Naming Service. Consequently, CORBA name servers handle complex names by way of NameComponent objects. Each NameComponent holds a single part, or element, of the name. An array of NameComponent objects can hold a fully specified path to an object on any computer file or disk system.

To find the Time server, you first need a NameComponent to hold an identifying string for the Time server. In the previous code, the call to narrow does this for us. This is discussed as we go along.

NameComponent nc = new NameComponent ("TimeServer", "");

This statement sets the id field of nc, the new NameComponent, to " TimeServer" and the kind field to an empty string.

Because the path to the Time object has just one element, we create a single-element array out of nc. The NamingContext.resolve method requires this array for its work:

NameComponent path[] = {nc};

Finally, we pass the path to the Naming Service's resolve method to get an object reference to the Time server and narrow it to a Time object:

Time timeRef = TimeHelper.narrow (ncRef.resolve(path));

Here you see the TimeHelper helper class at work. The resolve method returns a generic CORBA object as you saw previously when locating the name service itself. Therefore, we immediately narrow it to a Time object, which is the object reference needed to perform the rest of the work.

Invoking the getTime() Operation

CORBA invocations look like a method call on a local object. The complications of marshaling parameters over the wire, routing them to the server-side ORB, unmarshaling, and placing the upcall to the server method are completely transparent to the client programmer. Because so much is done for you by generated code, invocation is the easiest part of CORBA programming.

1. Continuing with the try-catch block in Client.java, the following invocation following the call to the name service's resolve method, invokes the getTime() operation on the server

String time = "Time on the Server is " + timeRef.getTime ();

2. Finally, we add code to print the results of the invocation to standard output

System.out.println (time);

Develop the server application

Once again, the steps involved are more or less similar to what we did when we developed the client application. The steps involved are

All this was explained previously when we developed our client application.

Managing the servant object

A server is a process that instantiates one or more servant objects. The servant implements the interface generated by idltojava and actually performs the work of the operations on that interface. Our Server needs a TimeServer.

Instantiating the TimeServer servant object

Inside the try-catch block, just below the call to init, we instantiate the servant object.

TimeServer timeRef = new TimeServer ();

Next, we connect the servant to the ORB, so the ORB can recognize invocations on it and pass them along to the correct servant:

orb.connect (timeRef);

Defining the servant class

We define the class for the servant object as follows.

class TimeServer extends _TimeImplBase {
 public String getTime () {
  SimpleDateFormat formatter = 
  new SimpleDateFormat ("MMMMM dd, yyyyy GGG, hh:mm:ss:SSS aaa");
  Date date = new Date ();
  return formatter.format ( date );
 } 
}

The servant is a subclass of _TimeImplBase, so it inherits the general CORBA functionality generated for it by the compiler.

Working with COS Naming

Once again, the steps involved are similar to what we had to do when we developed our client application.

  1. Obtaining the Initial Naming Context
  2. Narrowing the Object Reference

Registering the servant with the Name Server

The only interesting piece of code here is where we pass path and the servant object timeRef to the Naming Service, binding the servant object timeRef to the "TimeServer" id

NameComponent nc = new NameComponent ("TimeServer", "");
NameComponent path[] = {nc};
ncRef.rebind (path, timeRef);

Now, when the client calls resolve("TimeServer") on the initial naming context, the Naming Service returns an object reference to the Time servant.

Waiting for invocation

The server is ready; it simply needs to wait around for a client to request its service. The following piece of code achieves this for us:

Thread.currentThread ().join ();

This requires TimeServer to remain alive (though quiescent) until an invocation comes from the ORB. Because of its placement in main, after an invocation completes and getTime() returns, the server will wait again.

The complete server source listing is shown in the Listing 4.

package ex1;

// The package containing our stubs.
import Tracker.*; 
// Server will use the naming service.
import org.omg.CosNaming.*; 
// The package containing special exceptions thrown by the name
// service.
import org.omg.CosNaming.NamingContextPackage.*; 
// All CORBA applications need these classes.
import org.omg.CORBA.*; 

import java.util.*;
import java.text.*;

class TimeServer extends _TimeImplBase {
 public String getTime () {
  SimpleDateFormat formatter = 
              new SimpleDateFormat ("MMMMM dd, yyyyy GGG, hh:mm:ss:SSS aaa");
  Date date = new Date ();
  return formatter.format (date);
 } 
}

public class Server { 
 public static void main (String args[]) { 
  try {
   // Create and initialize the ORB
   ORB orb = ORB.init (args, null);
   // Create the servant and register it with the ORB
   TimeServer timeRef = new TimeServer ();
   orb.connect (timeRef);
   // Get the root naming context
   org.omg.CORBA.Object objRef = orb.resolve_initial_references ("NameService"); 
   NamingContext ncRef = NamingContextHelper.narrow (objRef);
   // Bind the object reference in naming
   NameComponent nc = new NameComponent ("TimeServer", "");
   NameComponent path[] = {nc};
   ncRef.rebind (path, timeRef);
   // Wait forever for current thread to die
   Thread.currentThread ().join (); 
  } catch (Exception e) {
   e.printStackTrace ();
  } 
 } 
}

Listing 4: The TimeServer CORBA server code

Start the name service

Java 2 ships with a compliant implementation of the COS Naming Service, called tnameserv. The command-line syntax for running tnameserv is

tnameserv [-ORBInitialPort ####]

The tnameserv runs on port 900 unless specified otherwise using the -ORBInitialPort command-line parameter

F:\>
F:\>tnameserv
Initial Naming Context:
IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67
436f6e746578743a312e30000000000100000000000000300001000000000008686f6d655f
7063000874000000000018afabcafe000000025e1c358b000000080000000000000000
TransientNameServer: setting port for initial object references to: 900

Start the CORBA TimeServer

F:\>
F:\>java ex1.Server

Execute the Time Server client

F:\>
F:\>java ex1.Client
Time on the Server is January 10, 1999 AD, 03:37:27:868 PM
F:\>

You can always refer to my Homepage at http://www.execpc.com/~gopalan for more CORBA source code. Another good resource is the comp-object-corba@omg.org mailing list from OMG. You can get the CORBA specs from http://www.omg.org.

Go to Part 3 of this article

click here to go to
My CORBA HomePage...

 

About the Author...
Gopalan Suresh Raj is a Software Architect, Developer and an active Author. He is contributing author to a couple of books "Enterprise Java Computing-Applications and Architecture" and "The Awesome Power of JavaBeans". His expertise spans enterprise component architectures and distributed object computing. Visit him at his Web Cornucopia© site (http://www.execpc.com/~gopalan) or mail him at gopalan@execpc.com.

Go to the Component Engineering Cornucopia page

This site was developed and is maintained by Gopalan Suresh Raj

This page has been visited times since September 21,1998.

Last Updated : Dec 19, '98

If you have any questions, comments, or problems regarding this site, please write to me I would love to hear from you.


Copyright (c) 1997-99, Gopalan Suresh Raj - All rights reserved. Terms of use.

All products and companies mentioned at this site,are trademarks of their respective owners.