CORBA is the ubiquitous standard for distributed computing, and Java is an ideal language for implementing CORBA implementations and delivering them to clients. Heres a thorough backgrounder and how-to.
Introduction
With the Internet becoming a critical piece of the business infrastructure, many legacy-distributed applications are getting a make over. Network and telecommunications management applications are being developed today with the Internet in mind. Companies with development labs and customers around the world are also adopting the distributed development philosophy as a mode to develop and deploy their applications to their products users. For example, a large manufacturing company may include remote diagnostics software as part of their maintenance package. Similarly, a telecommunications giant might sell operations management software as part of their switch.
All distributed applications, especially those with a large number of users, must be robust, scaleable, interoperable, mobile, and platform independent. As many would agree, Java is the object-oriented language of the Internet that provides portability, a highly creative execution environment, and a very strong and secure platform. Among the existing distributed technologies, CORBA (Common Object Request Broker Architecture) is an open standard, is object-oriented, and binds with many existing languages, including Java. It communicates across a network, consisting of different operating systems running programs written in various languages, using standard protocols. CORBA provides the infrastructure and services needed for a scaleable environment. Consequently, Java and CORBA complement each other.
In this article, I will introduce the basic features of CORBA and Java and guide you through building a simple client-server application. You will also learn about the vendors who sell CORBA middleware and how CORBA stacks up against other distributed technologies.
The CORBA Feel
You may already know about the OMG (Object Management Group), <www.omg.org>, which defined the OMA (Object Management Architecture) that specifies how components in a distributed application can interact. From the OMA, CORBA came into existence.
The CORBA specification contains all the features necessary to build a distributed application. It specifies an implementation language neutral IDL (Interface Definition Language) that you can use to write interfaces for the distributed components. IDL specifications are translated into your implementation language by means of a vendor-supplied compiler, thus providing platform independence (since the IDL can be translated into many languages, provided that you have the requisite compiler). Compiling the IDL to generate code in Java, C++, and other languages provides language independence. For communication over protocols like TCP/IP or IPX/SPX, CORBA also specifies an interface to the middleware, called the ORB (Object Request Broker). The ORB implementation handles the necessary inter-language or inter-OS translation for making disparate programs work together. Besides these basic services, CORBA provides system-level services like security, naming, transactions, etc. They are collectively named CORBAServices (see Table 1).
The Java Taste
If your application has an Internet interface, you need a language that provides simplicity and platform independence. Some of the features of Java make it the right choice for building component-based applications. They are:
- Java is object-oriented, which helps in building a component architecture framework.
- The CORBA IDL interfaces map directly to Java interfaces and classes. In fact, with some CORBA vendors, you dont need to write an IDL at all since they can generate the required code directly from a .class file that represents an interface (and often generate the IDL as well).
- A CORBA module is similar to a Java package (i.e., they both group related interfaces).
- Exceptions in Java have to be thrown explicitly and handled accordingly. You will find it similar to the exceptions in CORBA and quite unlike those in C++.
The ORB and Its Satellites, IDL and OA
The ORB is essentially a software bus and a universal translator. CORBA defines the interface to the ORB for various languages, and the ORB vendor provides the implementation. ORBs are typically implemented by a vendor as a .class library, a .so file, a .dll file, or the equivalent, along with a suite of stand-alone services. It transmits requests from the client to the target components and translates the messages according to the underlying OS and network layer. Thus the ORB is central to the CORBA architecture, and you must initialize it on both the client and the server.
A CORBA ORB does the following:
- Locate and instantiate objects on remote machines. The server ORB will prepare the server to receive requests once it is located.
- Marshal and unmarshal parameters (see sidebar, Stubs, Skeletons, and Marshalling) and their values to and from remote method calls. The server ORB unmarshals these parameters and sends them to the server component. This is how platform independence, network independence, and OS independence are achieved.
You can build the component interfaces and supporting data structures using the CORBA IDL. Of course there are vendors, like VisiBroker, who give you the facility to directly generate interfaces from a Java class. That helps if you know Java but havent worked with the IDL. But if you wish to use components developed in different languages, then you must expose the Java classs methods through an IDL interface. You can declare variables of basic types (see Table 2) or of user-defined types (struct, union, enum, and sequence). On the down side, CORBA must use a least-common-denominator strategy in determining legal parameter and return-value types. The entire typing system of the implementation language is not available to methods that will be called using CORBA. In particular, passing an object reference between two languages will be difficult at best, especially if one of the languages is not an object-oriented language (e.g, FORTRAN). Each IDL user-defined type gets compiled to a Java class, each IDL interface to a Java interface, and each IDL module to a Java package. When you compile the IDL, the compiler creates client stubs and server skeletons (see sidebar). Figure 1 shows marshaling and unmarshaling in the context of this application, with an actual call going from the client through the stub and skeleton to the server and back.
Besides the ORB and IDL, you need an interface between the user-defined component implementations and the ORB. For that the CORBA standard describes a number of OAs (object adapters). Each CORBA interface is an abstract object and its Java (or any language) implementation is concrete and is termed a servant. The object adapter provides the servant a mechanism to access the services provided by the ORB. In particular, the OA will create and manage component IORs (Interoperable Object References, discussed below), provide interfaces to register the components with the ORB, dispatch component requests from the client, and activate inactive registered components on the server if requests either come to the server or start server processes with registered components.
The CORBA Application
Interaction between CORBA Components
Interaction between CORBA Components is based on the following basic concepts:
- The IOR. This is the core of all communication between components. A component exports its IOR to a file or database. The client obtains the required IOR and uses it to then invoke operations on the component. The IOR is composed of the hostname and the port number where the component resides, and a unique object key. Figure 2 shows an example IOR.
- The client. This is an application that makes calls to functions of CORBA components and uses their services by using the components IOR. The client ORB marshals parameters from the client to the server during a method call to the server component.
- The server. This contains the CORBA components that need to be created, their IOR published, and their services made ready for the client. The application that does all these functions is called a server. It initializes the ORB, initializes the OA, and exports the IORs. The server ORB marshals any return values or exceptions from the server components to the client. The client ORB unmarshals whatever it got from the server.
- The CORBA call. When the client calls a member function of a server component, it is said to have made a CORBA call. A component can call a member function or operation of another component. All these operations have to be siphoned through the ORB. Figure 1 demonstrates one CORBA call.
Steps to Building the Application
- To build the distributed application, you first decide on an ORB and install that software. For Java, you install a JDK package, like JDK 1.3 from <http://java.sun.com/j2se/1.3/>.
- Then you decide on the core functionality of the application and separate the services from the user interface. That is, not all methods of the exposed classes (and certainly not all classes) of the application need to be accessible via CORBA.
- Your services can be grouped into one or more components (think class). Those components will be on your server.
- The user interface will be your client.
My application gets values and properties from the server and displays a simple bar chart on the client screen. The inspiration for this application comes from the Bar Chart applet included with Suns JDK 1.3 package, which I downloaded and installed. I have used IONAs ORBIX 2000 as the ORB. You can download and install the trial version from <www.iona.com>.
The Software Setup
You must install JDK first and set the system variable JAVA_HOME to the <install_path>/jdk1.3/bin directory. Installing the ORBIX software is quite easy. You follow the steps for installation and then configure. For configuring I ran the Orbix program it_configure from the command prompt and entered the default values.
The Server
Listing 1 shows the IDL interface, Chart.idl, and the functions that I developed first. To create a package, I gave chartmanager as the module name. In the interface definition, you will see that the input parameters are preceded by the in keyword, and the output parameters are preceded by the out keyword. in parameters are passed from the client to the component; out parameters are passed from the component to the client. You can also have inout parameters, which are passed in both directions.
I compiled this IDL using idl.exe (Listing 2), and it created the following class files (Figure 3):
- From the enum and struct definitions, one Java class like Style was created and a helper and holder class like StyleHelper and StyleHolder were created. You can use the holder class for marshaling data from the server to the client.
- From the interface definition if_chartServer, one stub class if_ChartServerStub was created for client marshaling; one if_ChartServerPOA was created for the component interface to interact with the OA; one if_Chartserver was created for the OA to use for calling the components member functions; one if_ChartServerOperations and similar helper and holder classes were created. Refer to Listing 3 for snippets of these classes.
- The generated classes are not to be edited. Since, the OA is the interface to the ORB, I created ChartImpl (see Listing 4) derived from the skeleton, if_ChartServerPOA. This class forms the basis and is the only point of entry and exit for all ChartServer interactions with the outside world (i.e., any remote client or remote component).
I created a ChartServer (Listing 5) class to instantiate the ORB, instantiate the OA, create the IOR of the component, and save it in chart.ior. At every stage, I am printing a message to the system, which is useful for debugging.
The Client
For the client I have one class, Chart. It has the main, init and paint member functions.
In the main function (Listing 6), I instantiate the ORB, read the IOR of the object from chart.ior, and use if_ChartServerHelper to narrow the object reference of the component to an if_ChartServer object. Narrowing involves (Listing 3) either typecasting the reference to an if_ChartServer object or retrieving the actual object through the client stub.
In the init function (Listing 6), I use this object to call its member functions like getCommonProperties and getBarProperties to get the chart values, color, scale, and shading and save them in local variables.
Finally in the paint function (Listing 6), I use these values to draw the bar chart.
The Build File and Final Execution
The build file (build.bat) has all the compile statements (Listing 2) for compiling the IDL to Java files and for compiling the IDL generated files and the server and client files. Make sure the directory containing your classes is in the classpath. After compiling, I started the server from the classes directory by typing:
D:\corba\classes it_java corba.code.ChartServerI got the messages shown in Figure 4.
I verified that chart.ior was created in the classes directory. In a separate DOS window, I ran the client by typing:
D:\corba\classes it_java corba.code.ChartI got similar messages (Figure 4), and then the chart application showed up on the screen.
The Vendors
Over the years, a number of vendors have come up with CORBA ORBs that provide some or all of the CORBAServices in addition to the basic CORBA functionality. Some of them are:
- Orbix from IONA Technologies, <www.iona.com>
- VisiBroker from Borland, <www.borland.com/products>
- e*ORB from Vertel, <www.vertel.com/products/eorb.asp>
- Component Broker from IBM, <www-4.ibm.com/software/ad/cb/>
- Suns JDK 1.2 (and above) has an ORB bundled with it
Refer to Table 3 for a list of some of the features for each of these products.
I used Orbix 2000 for the chart application for no particular reason other than the fact that my team was developing network management software with this ORB; it was quite stable and I was familiar with its environment.
The Others
Table 4 gives an overview of how other distributed technologies, such as DCOM, RMI, and EJB, stack up against CORBA.
Conclusions and Future Expansion
This application is a simple demonstration of a CORBA server serving a few requests from a client application. The data is hard coded in the server. You could read the data from a file or enhance the application further by storing the chart data in a database and use JDBC to retrieve it when needed. That way searching and manipulation of data would become much simpler. Currently every time the server is run on a different machine, you need to export the IOR to a file. To avoid that, you can use the CORBA NS (Naming Service) to locate them by a name.
The NS is like a directory tree of component naming contexts: naming contexts contain name bindings or other naming contexts. Each name binding maps to an object reference. Hence unlike the IOR, you have a machine-independent form of representing and locating objects. The components can be located anywhere on the network instead of a particular machine. After creating a name, you register it and publish it in the NS. The client attaches (binds) to the NS and uses the name, from a file or database, to resolve and get a handle of the component object instance.
To use the NS, you need to create a naming tree or graph using the following constructs from the Cosnaming module in Orbix:
module CosNaming{ typedef string Istring; struct NameComponent { Istring id; Istring kind; } typedef sequence<NameComponent> Name; ... };The id field is mandatory, and kind is optional. If you want to use the same id for different components, you can differentiate by using the kind field. Otherwise you can leave it empty. To create a naming context for the Chart server, you create the naming graph with:
org.omg.CosNaming.NameComponent[] chart_name = new org.omg.CosNaming.NameComponent[] { new NameComponent("ChartManager", ""); new NameComponent("ChartServer", "performance"); };In the server after you initialize the ORB, you get a CORBA reference to the initial naming context of the NS with:
org.omg.CORBA.Object obj = orb.resolve_initial_references("NameService");Then you obtain a reference to the naming context by narrowing it:
org.omg.CosNaming.NamingContextExt root_cxt = org.omg.CosNaming.NamingContextExtHelper.narrow(obj)) if (root_cxt = null) // show some error else {...}Now you have the root of the naming directory (or NS). To this root, you add your component-naming context by attaching (binding) to it:
// create naming context org.omg.CosNaming.NamingContext chartserver_cxt = root_cxt.new_context(); // bind the new context to the name you created above root_cxt.bind_context(chart_name, chartserver_cxt);To access the server component in the client, you get the root context and create the chart_name exactly as in the server. Then you get a handle to the server object with
org.omg.CORBA.Object obj = root_cxt.resolve(chart_name);and then narrow the CORBA object to an instance of if_chartServer with:
if_ChartServer chartSer = if_ChartServerHelper.narrow (obj);References
[1] Viswajit Aklecha. Object-Oriented Frameworks using C++ and CORBA (Coriolis Group, 2000).
[2] D. Slama, J. Garbis, and P. Russell. Enterprise CORBA (Prentice Hall, 1999).
[3] Orbix 2000, documentation.
Mrittika Ganguli is a systems analyst at Satyam Computer Services Ltd., Bangalore, India. She holds a Masters in Computer Systems from RPI, Troy, NY and has about six years of experience working with C++ and Java technologies. Her current assignment is guiding a team of engineers in designing and developing a distributed telecom application. Mrittika can be reached at gangulm@yahoo.com.