Gopalan Suresh Raj's Web Cornucopia
An Oasis for the parched Enterprise Services Engineer/Developer

    CORBA-Java IDL-Java Meets CORBA - Part 2

    Distributed-Computing

    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 2: CORBA 2.0 architecture 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.

    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.

    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 ();
    
    1. 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

    1. Importing Required Packages
    2. Declaring the Server Class
    3. Creating an ORB Object

    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
    3. 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 webcornucopia.com 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 omg.org.

    Author Bibliography

    Gopalan Suresh Raj is a Senior Analyst, Software Architect, and Developer with expertise in multi-tiered systems development, enterprise service architectures, and distributed computing. He is also an active author, including contributions to Professional JMS Programming, Wrox Press, 2001, Enterprise Java Computing-Applications and Architecture, Cambridge University Press, 1999, and The Awesome Power of JavaBeans, Manning Publications Co., 1998. He has submitted papers at international fora, and his work has been published in numerous technical journals. Visit him at his Web Cornucopia© site (webcornucopia.com) or mail him at gopalan@webcornucopia.com.

    Back