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

    CORBA-Java IDL-Java Meets CORBA - Part 4

    Distributed-Computing

    Common Object Services —The Naming Service

    The COS Naming Service is an OMG-specified extension to the core CORBA standard, where COS stands for Common Object Service. This Naming Service allows an object to be published using a symbolic name and it allows client applications to obtain references to the object using a standard API. The COS Naming Service can reside on any host accessible within the network and enables applications to publish, lookup, and list CORBA object references from any network host.

    A namespace is the collection of all names bound to a Naming Service. A namespace may contain naming context bindings to contexts located in another server. In such a case, the namespace is said to be a federated namespace because it is a collection of namespaces from multiple servers. An interesting point is the location of each context is transparent to the client applications; they have no knowledge that multiple servers are handling the resolution requests for an object.

    Java 2 ships with a compliant implementation of the COS Naming Service, called tnameserv. The Java IDL COS Naming Service implementation supports transient bindings only, which means objects must be reregistered each time the Naming Service is restarted. The COS Naming Service implementations for Iona and Inprise are much more sophisticated and scalable because they support persistent bindings, load balancing, and customization. The JDK tnameserv Naming Service is more than adequate to get developers started on a project. When rolling out your applications, you want to purchase a commercial implementation to gain persistent naming and load-balancing capabilities.

    A COS Naming Service stores object references in a hierarchical name space; much like a file system uses a directory structure to store files. Specifically, bindings maintain the association between a symbolic name and an object reference, while naming contexts are objects that organize the bindings into a naming hierarchy. Like the root directory in a file system, the initial naming context provides a known point to build binding hierarchies. To complete the file system analogy, a binding maps to a file and a naming context maps to a directory. Thus, a naming context can contain multiple bindings and other naming contexts.

    Common Object Services—The Event Service

    The Event Service package provides a facility that decouples the communication between objects. It provides a supplier-consumer communication model that allows multiple supplier objects to send data asynchronously to multiple consumer objects through an event channel. The supplier-consumer communication model allows an object to communicate an important change in state, such as a disk running out of free space, to any other objects that might be interested in such an event.

    The flow of data into the event channel is handled by the supplier objects, while the flow of data out of the event channel is handled by the consumer objects. The event channel is both a consumer of events and a supplier of events. The data communicated between suppliers and consumers are represented by the Any class, allowing any CORBA type to be passed in a type-safe manner. Supplier and consumer objects communicate through the event channel using standard CORBA requests.

    Proxy consumers and suppliers

    Consumers and suppliers are completely decoupled from one another through the use of proxy objects. Instead of directly interacting with each other, they obtain a proxy object from the EventChannel and communicate with it. Supplier objects obtain a consumer proxy and consumer objects obtain a supplier proxy. The EventChannel facilitates the data transfer between consumer and supplier proxy objects.

    Communication models

    The Event Service provides both a pull and a push communication model for suppliers and consumers. In the push model, supplier objects control the flow of data by pushing it to consumers. In the pull model, consumer objects control the flow of data by pulling data from the supplier.

    The EventChannel insulates suppliers and consumers from having to know which model is being used by other objects on the channel. This means a pull supplier can provide data to a push consumer and a push supplier can provide data to a pull consumer.

    Push model

    The push model is the more common of the two communication models. An example use of the push model is a supplier that monitors available free space on a disk and notifies interested consumers when the disk is filling up. The push supplier sends data to its ProxyPushConsumer in response to events it is monitoring.

    The push consumer spends most of its time in an event loop, waiting for data to arrive from the ProxyPushSupplier. The EventChannel facilitates the transfer of data from the ProxyPushSupplier to the ProxyPushConsumer.

    Pull model

    In the pull model, the event channel regularly pulls data from a supplier object, puts the data in a queue, and makes it available to be pulled by a consumer object. An example of a pull consumer would be one or more network monitors that periodically poll a network router for statistics.

    The pull supplier spends most of its time in an event loop, waiting for data requests to be received from the ProxyPullConsumer. The pull consumer requests data from the ProxyPullSupplier when it is ready for more data. The EventChannel pulls data from the supplier to a queue and makes it available to the ProxyPullSupplier.

    Putting It All Together—The Bank Example

    In this section, we build a sample client application that can query and perform operations on the balance in a bank checking account.

    Define the IDL

    The Bank.idl file defines the Bank module that contains two interfaces: The Account interface provides methods for setting and obtaining the current balance; the CheckingsAccount interface provides a method that opens, credits to, debits from, and creates accounts with a specified name, and returns an Account object reference. The Account object reference can then be used to obtain the balance in the account.

    User-defined exceptions

    The Bank.idl defines a user exception called InvalidTransaction. This exception is raised by many of the methods defined in this file in various interfaces. User-defined exceptions map to final Java classes that extend org.omg.CORBA.UserException. The idltojava compiler automatically generates a Java class for each exception you define in the IDL. The class generated by the idltojava compiler provides instance variables for the fields of exception, as well as constructors. The IDL is shown in Listing

    module Bank {
    
     exception InvalidTransaction {
      string explanation;
     };
    
     interface Account {
      float getBalance ();
      void setBalance (in float balance) 
             raises (InvalidTransaction);
     };
    
     interface CheckingsAccount {
      Account open(in string name);
      Account create(in string name, in float balance);
      Account credit(in string name, in float amount)
                          raises (InvalidTransaction);
      Account debit(in string name, in float amount)
                         raises (InvalidTransaction);
     };
    };
    

    Listing 8: The Bank Account CORBA IDL

    Because the Bank.idl file requires no special handling, you can compile it by typing the following command:

    F:\>idltojava Bank.idl
    

    The generated code

    Because Java allows only one public interface or class per file, compiling the Bank.idl file generates several java files. These files are stored in a generated subdirectory named Bank, which is the module name specified in the IDL file. IDL modules are mapped to Java packages, so all the files listed in the following will be part of the Bank package:

    • Account.java—The Account interface declaration.
    • CheckingsAccount.java—The CheckingsAccount interface declaration.
    • InvalidTransaction.java—The InvalidTransaction interface declaration.
    • AccountHelper.java—Declares the AccountHelper class, which contains an AccountHelper. This class defines helpful utility functions.
    • CheckingsAccountHelper.java—Declares the CheckingsAccountHelper class. This class defines helpful utility functions.
    • InvalidTransactionHelper.java—Declares the InvalidTransactionHelper class. This class defines helpful utility functions.
    • AccountHolder.java—Declares the AccountHolder class, which provides a holder for passing parameters.
    • CheckingsAccountHolder.java—Declares the CheckingsAccountHolder class, which provides a holder for passing parameters.
    • InvalidTransactionHolder.java—Declares the InvalidTransactionHolder class, which provides a holder for passing parameters.
    • _ AccountStub.java—Stub code for the Account object on the client side.
    • _CheckingsAccountStub.java—Stub code for the CheckingsAccount object on the client side.
    • _AccountImplBase.java—Skeleton code (implementation base code) for the Account object implementation on the server side.
    • _CheckingsAccountImplBase.java—Skeleton code (implementation base code) for the CheckingsAccount object implementation on the server side.

    Develop the client

    The Client class implements the client application that obtains the current balance of a bank account. The program performs the following steps:

    1. Initializes the Object Request Broker.
    2. Binds to a CheckingsAccount.
    3. Requests the CheckingsAccount to open the Account with the specified name. If no account name is supplied, a default name is used.
    4. Gets the balance of the Account using the object reference returned by the methods of the server.
    5. Prints the balance. Listing 9 below, shows the Bank Account CORBA client implementation.
    // Client.java
    package ex3;
    
    public class Client {
     
     public static void main (String[] args) {
      try {
       // Initialize the ORB.
       org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init (args,null);
       // Get a reference to the Naming Service
       org.omg.CORBA.Object nameServiceObj = orb.resolve_initial_references 
        ("NameService");
       
       if ( nameServiceObj == null ) {
        System.out.println ("nameServiceObj = null");
        return;
       }
       // Narrow the object
       org.omg.CosNaming.NamingContext nameService = org.omg.CosNaming.NamingContextHelper.narrow 
        (nameServiceObj);
       
       if( nameService == null ) {
        System.out.println ("nameService = null");
        return;
       }
       // Locate an object called CheckingsAccount
       // relative to the initial namecontext
       org.omg.CosNaming.NameComponent[] accountName = { 
        new org.omg.CosNaming.NameComponent ("CheckingsAccount", "")
       };
       Bank.CheckingsAccount checkingsAccount = Bank.CheckingsAccountHelper.narrow 
        (nameService.resolve (accountName));
       
       nameService.rebind (accountName, checkingsAccount);
       // use args[1] as the account name, or a default.
       String op = args.length > 0 ? args[0] : "-c";
       String name = args.length > 1 ? args[1] : "Gopalan";
       float amount = args.length > 2 ? 
                      (new Float (args[2])).floatValue () : 1000;
       // Request the CheckingsAccount to open a named account.
       Bank.Account account = null;
       try {
        if (op.equals("-c")) 
         account = checkingsAccount.create (name, amount);
        if (op.equals("-o")) 
         account = checkingsAccount.open (name);
        if (op.equals("-cr")) 
         account = checkingsAccount.credit (name, amount);
        if (op.equals("-db")) 
         account = checkingsAccount.debit (name, amount);
       } catch (Exception e) { e.printStackTrace (); }
       // Get the balance of the account.
       float balance = account.getBalance ();
       // Print out the balance.
       System.out.println ("The balance in " + name + "'s account is $" 
                           + balance);
      } catch (Exception e) { e.printStackTrace (); }
     }
    }
    

    Listing 9: The Bank Account CORBA client

    Code the AccountImpl servant

    The AccountImpl class in this file extends Bank._AccountImplBase class generated in the Bank package. This class contains methods to set or get an account Balance. If the Balance goes below zero, it throws an InvalidTransaction Exception. Listing 10 below, shows the AccountImpl servant class definition.

    // AccountImpl.java
    package ex3;
    import java.util.*;
    
    public class AccountImpl extends Bank._AccountImplBase {
     private float _balance;
    
     public AccountImpl (float balance) {
      super ();
      _balance = balance;
      System.out.println ("Created Account Server");
     }
    
     public float getBalance () { return _balance; }
    
     public void setBalance (float amount) 
      throws Bank.InvalidTransaction { 
      if (amount < 0)
       throw new Bank.InvalidTransaction ();
      else
       _balance = amount; 
     }
    }
    

    Listing 10: The AccountImpl class definiton

    Code the CheckingAccountImpl servant

    The CheckingsAccountImpl class in this file extends Bank._CheckingsAccountImplBase class generated in the Bank package. This class provides the actual implementation of the open, create, credit, and debit method used to obtain, create, credit, or debit an Account. The CheckingsAccountImpl uses a Dictionary to contain all the accounts.

    When a request to create an account is received, the create method does the following:

    1. Looks for an Account with a matching name.
    2. If a match is not found, a new Account is created with that name and balance. A new object is activated.
    3. An object reference to the new or existing Account is returned.
    4. All the other code is straightforward and needs no explanation.

    Listing 11 below shows the CheckingsAccountImpl class definition

    // CheckingsAccountImpl.java
    package ex3;
    import java.util.*;
    
    public class CheckingsAccountImpl 
     extends Bank._CheckingsAccountImplBase {
     
     private Dictionary _accountsTable = new Hashtable ();
     private Random _random = new Random ();
    
     public CheckingsAccountImpl () {
      super ();
      System.out.println ("Created CheckingsAccount Server");
     }
    
     public synchronized Bank.Account open(String name) {
      // Lookup the account in the account dictionary.
      Bank.Account account = (Bank.Account) _accountsTable.get (name);
      // Return the account. 
      return account; 
     }
    
     public synchronized Bank.Account credit(String name, float amount) 
      throws Bank.InvalidTransaction {
      // Lookup the account in the account dictionary.
      Bank.Account account = (Bank.Account) _accountsTable.get (name);
      if (amount > 0)
       account.setBalance (account.getBalance ()+amount);
      // Return the account. 
      return account; 
     }
    
     public synchronized Bank.Account debit(String name, float amount)
      throws Bank.InvalidTransaction {
      // Lookup the account in the account dictionary.
      Bank.Account account = (Bank.Account) _accountsTable.get (name);
      if (amount > 0)
       account.setBalance (account.getBalance ()-amount);
      // Return the account. 
      return account; 
     }
    
     public synchronized Bank.Account create(String name, float balance) {
      // Lookup the account in the account dictionary.
      Bank.Account account = (Bank.Account) _accountsTable.get (name);
      // If the account is not in the dictionary, create an account.
      if (account == null) {
       // Create the account implementation, given the balance.
       account = new AccountImpl (balance);
       // Make the object available to the ORB. 
       // Export the newly created object 
       _orb ().connect (account);
       System.out.println ("Created " + name + "'s account: " +
                           account);
       // Save the account in the account dictionary.
       _accountsTable.put (name, account); 
      } 
      // Return the account. 
      return account; 
     }
    }
    

    Listing 11: The CheckingAccountsImpl CORBA servant class definition

    Develop the server class

    Just as with the client, many of the files used in implementing the bank server are contained in the Bank package generated by the idl2java compiler. The program performs the following steps:

    1. Initializes the Object Request Broker.
    2. Initializes the Portable Object Adaptor.
    3. Creates an CheckingsAccount object.
    4. Activates the newly created object.
    5. Prints a status message.
    6. Waits for incoming client requests. Listing 12 below, shows the Bank Account CORBA server:
    // Server.java
    package ex3;
    import org.omg.CosNaming.*;
    
    public class Server { 
     
     public static void main (String[] args) {
      try {
       // Initialize the ORB.
       org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init (args,null);
       System.out.println ("Orb Initialized.");
       // Create the account CheckingsAccount object. 
       Bank.CheckingsAccount checkingsAccount = new CheckingsAccountImpl();
       if (checkingsAccount == null) {
        System.out.println ("CheckingsAccount = null");
        return;
       }
       // Export the newly created object 
       orb.connect (checkingsAccount);
       System.out.println ("CheckingsAccountServer is connected.");
       // Get a reference to the Naming Service
       org.omg.CORBA.Object nameServiceObj = orb.resolve_initial_references 
        ("NameService");
       
       if (nameServiceObj == null) {
        System.out.println ("nameServiceObj = null");
        return;
       }
       // Narrow the object
       org.omg.CosNaming.NamingContext nameService = org.omg.CosNaming.NamingContextHelper.narrow 
        (nameServiceObj);
       
       if (nameService == null) {
        System.out.println ("nameService = null");
        return;
       }
       // bind the CheckingsAccount object in the Naming Service
       org.omg.CosNaming.NameComponent[] accountName = { 
        new org.omg.CosNaming.NameComponent ("CheckingsAccount", "")};
       nameService.rebind (accountName, checkingsAccount);
       // Wait forever for current thread to die
       Thread.currentThread ().join (); 
      } catch (Exception e) { e.printStackTrace (); }
     }
    }
    

    Listing 12: The Bank Account CORBA server application

    Run the Naming Service

    F:\>
    F:\>tnameserv
    Initial Naming Context:
    IOR:000000000000002849444c3a6f6d672e6f72672f436f734e61
    6d696e672f4e616d696e67436f6e746578743a312e30000000000
    100000000000000300001000000000008686f6d655f706300108c
    000000000018afabcafe000000025ddcc3cd000000080000000000000000
    TransientNameServer: setting port for initial object references to: 900
    

    Run the CheckingAccount server

    When the Checkings Account server is up and running, a typical session shows up like this on the window:

    F:\>
    F:\>java ex3.Server
    Orb Initialized.
    Created CheckingsAccount Server
    CheckingsAccountServer is connected.
    Created Account Server
    Created Gopalan's account: ex3.AccountImpl:com.sun.CORBA.idl.GenericCORBAClientSC@dc47ae34
    Created Account Server
    Created Athul's account: ex3.AccountImpl:com.sun.CORBA.idl.GenericCORBAClientSC@d08fae34
    

    Run the client

    When the client is executed, typical sessions show up like this on the window:

    F:\>
    F:\>java ex3.Client
    The balance in Gopalan's account is $1000.0
    F:\>java ex3.Client -c Athul 2000
    The balance in Athul's account is $2000.0
    F:\>e:\java\jdk1.2\bin\java ex3.Client -o Athul
    The balance in Athul's account is $2000.0
    F:\>e:\java\jdk1.2\bin\java ex3.Client -cr Athul 100
    The balance in Athul's account is $2100.0
    F:\>e:\java\jdk1.2\bin\java ex3.Client -db Athul 100
    The balance in Athul's account is $2000.0
    F:\>
    

    This concludes this 4 part article on Java and CORBA. I hope you enjoyed reading this as much as I did writing it.

    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