CORBA and Java

CORBA and Java

By Don Busch, OCI Principal Software Engineer

January 2002


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 enabling the development of extensible, reusable, and less costly computing systems through open standards. 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 OMG 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.

Figure 1. Specifications

Figure 1. Specifications

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:

  1. interface GuestRoom; // forward declaration
  2.  
  3. interface Hotel {
  4. readonly attribute string name;
  5. readonly attribute short numberOfRooms;
  6. GuestRoom checkIn(in short numNights);
  7. };
  8.  
  9. interface GuestRoom {
  10. readonly attribute short roomNumber;
  11. readonly attribute float rate;
  12. readonly attribute short numNights;
  13. void chargeMeal(in float amount);
  14. readonly attribute float balance;
  15. void checkOut();
  16. };

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 a server that 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 GuestRoom.

The checkIn() operation is used as a factory method to obtain GuestRoom.

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.

  1. import org.omg.CosNaming.*;
  2. import Hotel;
  3. import HotelHelper;
  4. import GuestRoom;
  5.  
  6. public class HotelClient {
  7. public static void main(String[] args) {
  8. try {
  9. // Initialize the ORB.
  10. org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
  11.  
  12. // Get the Naming Service's root naming context
  13. org.omg.CORBA.Object obj =
  14. orb.resolve_initial_references("NameService");
  15.  
  16. NamingContextExt rootContext = NamingContextExtHelper.narrow(obj);
  17.  
  18. // Get the Hotel's object reference from the Naming Service
  19. obj = rootContext.resolve_str("Hotels/Hotel California");
  20. Hotel theHotel = HotelHelper.narrow(obj);
  21. if (theHotel == null) {
  22. System.err.println(
  23. "ERROR: Object reference does not refer to a Hotel");
  24. System.exit(1);
  25. }
  26.  
  27. // Invoke operations on the Hotel as if it were a local object.
  28. // Note that "name" attribute is accessed via "name()" method
  29. String hotelName = theHotel.name();
  30. System.out.println("This is the Hotel " + hotelName);
  31.  
  32. // Check into a room for 5 nights.
  33. short numNights = 5;
  34. GuestRoom room = theHotel.checkIn(numNights);
  35. System.out.println("Balance at checkout for room "
  36. + room.roomNumber() + " will be: " + room.balance() );
  37. } catch (org.omg.CORBA.SystemException ex) { // Catch exceptions
  38. System.err.println("ERROR: " + ex);
  39. ex.printStackTrace(System.err);
  40. } catch (org.omg.CORBA.UserException ex) {
  41. System.err.println("ERROR: " + ex);
  42. ex.printStackTrace(System.err);
  43. }
  44. }
  45. }

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 (a full discussion of the skeleton class is beyond the scope of this article).

  1. import Hotel;
  2. import HotelHelper;
  3. import HotelPOA;
  4. import GuestRoom;
  5. import GuestRoomHelper;
  6. import GuestRoomImpl;
  7.  
  8. import org.omg.PortableServer.POA;
  9. import org.omg.PortableServer.POAPackage.*;
  10.  
  11. public class HotelImpl extends HotelPOA {
  12. private static final int NUMBER_OF_ROOMS = 500;
  13.  
  14. private GuestRoomImpl[] rooms = new GuestRoomImpl[NUMBER_OF_ROOMS];
  15. private boolean[] available = new boolean[NUMBER_OF_ROOMS];
  16.  
  17. private POA guestRoomPOA;
  18.  
  19. public HotelImpl(POA guestRoomPOA) {
  20. this.guestRoomPOA = guestRoomPOA;
  21.  
  22. for (int i = 0; i < available.length; ++i) {
  23. available[i] = true;
  24. } // end of for ()
  25. }
  26.  
  27. public String name() {
  28. return "Hotel California";
  29. }
  30.  
  31. public short numberOfRooms() {
  32. return NUMBER_OF_ROOMS;
  33. }
  34.  
  35. public GuestRoom checkIn(short numNights) {
  36. // Determine if a room is available.
  37. GuestRoom availableRoom = null;
  38.  
  39. short nextRoom = 0;
  40. while (nextRoom < NUMBER_OF_ROOMS && !available[nextRoom]) {
  41. ++nextRoom;
  42. } // end of while ()
  43.  
  44. if (nextRoom < NUMBER_OF_ROOMS) {
  45. available[nextRoom] = false;
  46.  
  47. if (rooms[nextRoom] == null) {
  48. rooms[nextRoom] = new GuestRoomImpl(nextRoom);
  49. } // end of if ()
  50.  
  51. rooms[nextRoom].checkIn(numNights);
  52.  
  53. try {
  54. availableRoom = GuestRoomHelper.narrow(
  55. this.guestRoomPOA.servant_to_reference(rooms[nextRoom]));
  56. } catch (ServantNotActive ex) {
  57. // Will not be thrown in this application
  58. System.err.println("ERROR: " + ex);
  59. ex.printStackTrace(System.err);
  60. return null;
  61. } catch (WrongPolicy ex) {
  62. // Will not be thrown in this application
  63. System.err.println("ERROR: " + ex);
  64. ex.printStackTrace(System.err);
  65. return null;
  66. }
  67. } // end of else
  68.  
  69. return availableRoom;
  70. }
  71. }

And here's a servant class for GuestRoom:

  1. import GuestRoom;
  2. import GuestRoomHelper;
  3. import GuestRoomImpl;
  4.  
  5. import org.omg.PortableServer.POA;
  6.  
  7. public class GuestRoomImpl extends GuestRoomPOA {
  8. private boolean checkedIn = false;
  9. private short roomNumber;
  10. private float rate = 100.0f;
  11. private short numNights;
  12. private float balance = 0.0f;
  13.  
  14. GuestRoomImpl(short roomNumber) {
  15. this.roomNumber = roomNumber;
  16. }
  17.  
  18. public void checkIn(short numNights) {
  19. this.checkedIn = true;
  20. this.numNights = numNights;
  21. this.balance = this.numNights * this.rate;
  22. }
  23.  
  24. public short roomNumber() {
  25. return this.roomNumber;
  26. }
  27.  
  28. public float rate() {
  29. return this.rate;
  30. }
  31.  
  32. public short numNights() {
  33. return this.numNights;
  34. }
  35.  
  36. public float balance() {
  37. return this.balance;
  38. }
  39.  
  40. public void chargeMeal(float amount) {
  41. this.balance += amount;
  42. }
  43.  
  44. public void checkOut() {
  45. this.checkedIn = false;
  46. this.numNights = 0;
  47. this.balance = 0.0f;
  48. }
  49. }

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:

  1. import Hotel;
  2. import HotelHelper;
  3.  
  4. import org.omg.CORBA.*;
  5. import org.omg.PortableServer.*;
  6. import org.omg.CosNaming.*;
  7. import org.omg.CosNaming.NamingContextPackage.*;
  8.  
  9. public class HotelServer {
  10. public static void main(String args[]) {
  11. try {
  12. // create and initialize the ORB and POA
  13. ORB orb = ORB.init(args, null);
  14. POA poa = org.omg.PortableServer.POAHelper.narrow(
  15. orb.resolve_initial_references("RootPOA"));
  16.  
  17. // create servant and register it with the POA
  18. HotelImpl servant = new HotelImpl(poa);
  19. byte[] hotelId = poa.activate_object(servant);
  20.  
  21. // Get the Naming Service's root naming context
  22. org.omg.CORBA.Object obj =
  23. orb.resolve_initial_references("NameService");
  24. NamingContextExt rootContext =
  25. NamingContextExtHelper.narrow(obj);
  26.  
  27. // Create a "Hotels" naming context
  28. try {
  29. rootContext.bind_new_context(
  30. rootContext.to_name("Hotels"));
  31. } catch (AlreadyBound e) {
  32. // consume exception
  33. }
  34.  
  35. // Publish the Hotel's object reference to the Naming Service
  36. org.omg.CORBA.Object hotelObj =
  37. poa.id_to_reference(hotelId);
  38. rootContext.rebind(rootContext.to_name(
  39. "Hotels/Hotel California"), hotelObj);
  40.  
  41. // activate the RootPOA, and run
  42. poa.the_POAManager().activate();
  43. orb.run();
  44. } catch (org.omg.CORBA.SystemException ex) {
  45. System.err.println("ERROR: " + ex);
  46. ex.printStackTrace(System.err);
  47. } catch (org.omg.CORBA.UserException ex) {
  48. System.err.println("ERROR: " + ex);
  49. ex.printStackTrace(System.err);
  50. }
  51. }
  52. }

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.

References



Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.


secret