CORBA and Java

by
Don Busch, Principal Software Engineer
Object Computing, Inc. (OCI)

Introduction

Common Object Request Broker Architecture (CORBA) is an open specification for the design and implementation of distributed, object-oriented computing systems.

The CORBA specification is a product of the Object Management Group (OMG), an international consortium of over 800 companies. The OMG was founded in 1989 with the goal of providing open standards to enable the development of extensible, reusable and less costly computing systems. All specifications published by the OMG are freely available to the public.

CORBA has similarities to Java Remote Method Invocation (RMI), in that both are technologies for building distributed computing systems. The greatest difference between the two is that RMI is designed for distributed applications, which are now and will always be pure Java, whereas CORBA allows interoperability among distributed application components written in various programming languages. A more complete discussion of CORBA and RMI can be found in this 1997 article.

Object Request Broker

The Object Request Broker (ORB) mediates communication between a client and an object implementation. The object implementation may be in the same process as the client, in a different process on the same host or on a different host. The ORB is responsible for finding the object implementation to carry out the request, preparing the object implementation to receive the request and communicating the data that makes up the request.

The client and object implementation may differ in matters of hardware platform, operating system and/or programming language. The OMG defines standardized language bindings for C, C++, Java, Ada95, Smalltalk, Python and COBOL.

Object Management Architecture

The CORBA specification defines an architecture for component software integration called the Object Management Architecture (OMA). The OMA is built on top of the ORB and defines higher-level CORBAservices and CORBAfacilities, which can carry out tasks common to many distributed applications. Individual domains such as Finance, Healthcare and Telecommunications have also defined specifications for Services and Facilities specific to their domains.

Object Management Architecture

The OMG and, by extension, its members define specifications for CORBAservices and CORBAfacilities; individual vendors implement those Services and Facilities. Different vendors' implementations of the Services and Facilities are interoperable. A typical CORBA application will utilize a small subset of available CORBAservices and/or CORBAfacilities.

Interface Definition Language

The contract between a client and server is described via Interface Definition Language (IDL). IDL completely defines the interface and fully identifies each operation's parameters. IDL is independent of the language(s) used to implement the client and server.

The syntax of IDL is similar to the syntax of Java or C++. IDL contains no implementation details, however, as IDL interfaces are implemented in conventional programming languages.

The IDL interfaces for our example are as follows:

    interface GuestRoom; // forward declaration

    interface Hotel {
        readonly attribute string name;
        readonly attribute short numberOfRooms;
        GuestRoom checkIn(in short numNights);
    };

    interface GuestRoom {
        readonly attribute short roomNumber;
        readonly attribute float rate;
        readonly attribute short numNights;
        void chargeMeal(in float amount);
        readonly attribute float balance;
        void checkOut();
    };
    

This example has two interfaces, Hotel and GuestRoom. A client retrieves a GuestRoom by checking into the Hotel. Once checked in, the client can charge meals, retrieve the room's current balance and check out.

The IDL code is passed through an IDL Compiler, which generates stub code for the client and skeleton code for the server in the target language. The stub code shields the client from the low-level details of invoking a remote operation. Similarly, the skeleton code shields the server from the low-level details of making a server-side implementation object available to process requests from clients.

In our example, the IDL compiler will generate Java. It's possible that the client and server would be implemented in different languages, in which case we'd pass the IDL code through an IDL Compiler for each target language.

Most IDL compilers allow the generated Java code to be placed in a Java package of the programmer's choosing. There is no standard for how each IDL compiler accomplishes this. One example comes from JacORB, an open-source Java ORB. JacORB's IDL compiler uses a "-p" command-line switch to specify a package. For example, the following would put all of the generated Java code into the com.ociweb package:

    java org.jacorb.idl.parser -p com.ociweb Hotel.idl
    

Clients and Servers

In our example, we'll write a client and server, which use the Hotel and GuestRoom interfaces. First, the client retrieves the Hotel's object reference. An object reference is a location-independent handle, which a client uses to invoke operations on a server object.

The client and server use the CORBA Naming Service to publish and retrieve the object reference. The Naming Service is a registry where human-readable names are associated with object references. Typically, a server binds some, but not all, of its objects into the Naming Service. For example, our server registers its Hotel in the Naming Service, but not its GuestRooms. The checkIn() operation is used as a factory method to obtain GuestRooms.

The Client

The client has two main duties: to find the remote objects with which it wishes to communicate and to invoke operations on them. The client uses the Naming Service to find the Hotel and uses the Hotel to find a GuestRoom. It invokes operations on the Hotel and GuestRoom as though they are local Java references.

    import org.omg.CosNaming.*;
    import Hotel;
    import HotelHelper;
    import GuestRoom;
    
    public class HotelClient {
        public static void main(String[] args) {
            try {
                // Initialize the ORB.
                org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);

                // Get the Naming Service's root naming context
                org.omg.CORBA.Object obj =
                        orb.resolve_initial_references("NameService");

                NamingContextExt rootContext = NamingContextExtHelper.narrow(obj);

                // Get the Hotel's object reference from the Naming Service
                obj = rootContext.resolve_str("Hotels/Hotel California");
                Hotel theHotel = HotelHelper.narrow(obj);
                if (theHotel == null) {
                    System.err.println(
                            "ERROR: Object reference does not refer to a Hotel");
                    System.exit(1);
                }

                // Invoke operations on the Hotel as if it were a local object.
                // Note that "name" attribute is accessed via "name()" method
                String hotelName = theHotel.name();
                System.out.println("This is the Hotel " + hotelName);

                // Check into a room for 5 nights.
                short numNights = 5;
                GuestRoom room = theHotel.checkIn(numNights);
                System.out.println("Balance at checkout for room "
                        + room.roomNumber() + " will be: " + room.balance() );
            } catch (org.omg.CORBA.SystemException ex) { // Catch exceptions
                System.err.println("ERROR: " + ex);
                ex.printStackTrace(System.err);
            } catch (org.omg.CORBA.UserException ex) {
                System.err.println("ERROR: " + ex);
                ex.printStackTrace(System.err);
            }
        }
    }
    

The Server

The server implements the IDL interfaces used by the client. Our server has two interfaces to implement: Hotel and GuestRoom. We write an implementation, or servant, class for the Hotel. The servant class extends the HotelPOA class generated by the IDL compiler and implements all of the IDL attributes and operations in the Hotel interface. The HotelPOA class is the skeleton class generated by the IDL compiler. The full discussion of the skeleton class is beyond the scope of this article.

    import Hotel;
    import HotelHelper;
    import HotelPOA;
    import GuestRoom;
    import GuestRoomHelper;
    import GuestRoomImpl;

    import org.omg.PortableServer.POA;
    import org.omg.PortableServer.POAPackage.*;

    public class HotelImpl extends HotelPOA {
        private static final int NUMBER_OF_ROOMS = 500;

        private GuestRoomImpl[] rooms = new GuestRoomImpl[NUMBER_OF_ROOMS];
        private boolean[] available = new boolean[NUMBER_OF_ROOMS];

        private POA guestRoomPOA;

        public HotelImpl(POA guestRoomPOA) {
            this.guestRoomPOA = guestRoomPOA;

            for (int i = 0; i < available.length; ++i) {
                available[i] = true;
            } // end of for ()
        }

        public String name() {
            return "Hotel California";
        }

        public short numberOfRooms() {
            return NUMBER_OF_ROOMS;
        }

        public GuestRoom checkIn(short numNights) {
            // Determine if a room is available.
            GuestRoom availableRoom = null;

            short nextRoom = 0;
            while (nextRoom < NUMBER_OF_ROOMS && !available[nextRoom]) {
                ++nextRoom;
            } // end of while ()

            if (nextRoom < NUMBER_OF_ROOMS) {
                available[nextRoom] = false;

                if (rooms[nextRoom] == null) {
                    rooms[nextRoom] = new GuestRoomImpl(nextRoom);
                } // end of if ()

                rooms[nextRoom].checkIn(numNights);

                try {
                    availableRoom = GuestRoomHelper.narrow(
                            this.guestRoomPOA.servant_to_reference(rooms[nextRoom]));
                } catch (ServantNotActive ex) {
                    // Will not be thrown in this application
                    System.err.println("ERROR: " + ex);
                    ex.printStackTrace(System.err);
                    return null;
                } catch (WrongPolicy ex) {
                    // Will not be thrown in this application
                    System.err.println("ERROR: " + ex);
                    ex.printStackTrace(System.err);
                    return null;
                }
            } // end of else

            return availableRoom;
        }
    }
    

and a servant class for GuestRoom:

    import GuestRoom;
    import GuestRoomHelper;
    import GuestRoomImpl;

    import org.omg.PortableServer.POA;

    public class GuestRoomImpl extends GuestRoomPOA {
        private boolean checkedIn = false;
        private short roomNumber;
        private float rate = 100.0f;
        private short numNights;
        private float balance = 0.0f;

        GuestRoomImpl(short roomNumber) {
            this.roomNumber = roomNumber;
        }

        public void checkIn(short numNights) {
            this.checkedIn = true;
            this.numNights = numNights;
            this.balance = this.numNights * this.rate;
        }

        public short roomNumber() {
            return this.roomNumber;
        }
        
        public float rate() {
            return this.rate;
        }
        
        public short numNights() {
            return this.numNights;
        }
        
        public float balance() {
            return this.balance;
        }

        public void chargeMeal(float amount) {
            this.balance += amount;
        }
        
        public void checkOut() {
            this.checkedIn = false;
            this.numNights = 0;
            this.balance = 0.0f;
        }
    }
    

Then, our server needs a main program. The main program initializes the ORB, creates a Hotel servant, registers the Hotel servant with the object adapter and binds the Hotel into the Naming Service:

    import Hotel;
    import HotelHelper;

    import org.omg.CORBA.*;
    import org.omg.PortableServer.*;
    import org.omg.CosNaming.*;
    import org.omg.CosNaming.NamingContextPackage.*;

    public class HotelServer {
        public static void main(String args[]) {
            try {
                // create and initialize the ORB and POA
                ORB orb = ORB.init(args, null);
                POA poa = org.omg.PortableServer.POAHelper.narrow(
                        orb.resolve_initial_references("RootPOA"));

                // create servant and register it with the POA
                HotelImpl servant = new HotelImpl(poa);
                byte[] hotelId = poa.activate_object(servant);

                // Get the Naming Service's root naming context
                org.omg.CORBA.Object obj =
                        orb.resolve_initial_references("NameService");
                NamingContextExt rootContext =
                        NamingContextExtHelper.narrow(obj);

                // Create a "Hotels" naming context
                try {
                    rootContext.bind_new_context(
                            rootContext.to_name("Hotels"));
                } catch (AlreadyBound e) {
                    // consume exception
                }

                // Publish the Hotel's object reference to the Naming Service
                org.omg.CORBA.Object hotelObj =
                        poa.id_to_reference(hotelId);
                rootContext.rebind(rootContext.to_name(
                        "Hotels/Hotel California"), hotelObj);

                // activate the RootPOA, and run
                poa.the_POAManager().activate();
                orb.run();
            } catch (org.omg.CORBA.SystemException ex) {
                System.err.println("ERROR: " + ex);
                ex.printStackTrace(System.err);
            } catch (org.omg.CORBA.UserException ex) {
                System.err.println("ERROR: " + ex);
                ex.printStackTrace(System.err);
            }
        }
    }
    

Summary

There are many CORBA resources available that provide more detail than the introduction presented here. Two useful books are Henning and Vinoski's Advanced CORBA Programming with C++ and Brose, Vogel, and Duddy's Java Programming with CORBA .

A fully functional client-server example that is implemented with JacORB, an open-source Java ORB, can be found here. The link contains a PDF presentation and an example built with Ant. JacORB is a high-performance ORB that stays current with the CORBA specification, supports many CORBAservices, and releases frequent updates. All JacORB source code is freely available.

References


OCI Educational Services

Object Computing, Inc (OCI) has been providing educational services to clients, industries and universities since 1993. We offer one of the most comprehensive distributed Object Oriented training curricula in the country. These curricula focus on the fundamentals of OO technology; with close to 40 workshops in OOAD, Java, XML, C++/CORBA and Unix/Linux.

Java C/C++ .NET/C# Real-Time Systems Object Oriented Software Engineering Distributed Computing Wireless Enterprise Unix/Linux XML

For further information regarding OCI's Educational Services programs, please visit our Educational Services section on the web or contact us at training.