The CORBA mechanics all start with an interface. The best way to think of an interface is to envision my car. That's right -- my car. You're not familiar with it, but if I said to you, "Take my car and pick up some sandwiches for lunch," you probably wouldn't question whether you're capable of driving my car. You may wonder where it's parked or whether it's safe to drive, but you should be confident that driving my car will be similar to driving your car. That's because the person-to-car interface is highly standardized among cars. There may be some implementation differences between my sedan and your sports car, but with the standard placement of the gas pedal, brakes, and steering, you should be down the road quickly and effortlessly.
Because CORBA is language independent, it relies on an interface definition
language (IDL) to express how clients will make a request to the service
that implements the interface. Our interface is one method: add().
This method takes two numbers (IDL longs) and returns the sum of those numbers. Here
is our interface calculator:
Listing 1. calcsimpl.idl
module corbasem {
module gen {
module calcsimpl {
interface calculator {
long add(in long x, in long y);
};
};
};
}; |
The IDL keywords in this interface are: module, interface, long, and
in. IDL uses the module keyword to create
namespaces and it maps very cleanly to the Java keyword package. When we
run the IDL-to-Java compiler, the generated Java files will be placed into
a subdirectory with the name calcsimpl. IDL keyword interface
maps perfectly to Java interface and represents an abstract type in that
both define only how you communicate with an object; it says nothing about
the object's implementation. The IDL keyword long
is a fundamental integer type that is mapped to at least a 4-byte type,
which, in Java code, is an int.
If you think about the mechanics of making a remote method call, it
makes perfect sense to define the direction that the parameters will be
moving in -- client-to-server, server-to-client, or both. In IDL operations
these are declared with the in, out,
and inout keywords. Every parameter must
have its direction declared so that the object request broker (ORB) knows in which direction the
parameter is going. This impacts packaging up the parameters for sending,
unpacking parameters, and memory management. The more the ORB understands
about parameters, the more efficient it can be. The keyword in
signifies that the long x and long
y are passed from client to server.
Figure 1. Participants in a CORBA request

After the interface is defined, it has to run through an IDL-to-Java compiler provided by your ORB vendor. The IDL compiler is a neat utility that produces the IDL stubs and skeletons, as well as other support files. Most of these generated source files will faciliate some portion of the marshalling of the specified IDL types as defined in the CORBA standard. The compiler produces most of the network plumbing, which in a distributed system is not trivial. On the most fundamental level, the IDL-to-Java compiler is simply a program that implements the IDL-to-Java language mapping as defined by the CORBA 2.3 specification. If we had to produce this code by hand it would be tedious, time-consuming, and prone to errors; the IDL-to-Java compiler takes care of all this so you don't have to. At the same time, it binds you to certain laws and forces encapsulation upon you. The IDL-to-Java compiler will force upon your system the laws of CORBA-land.
I entered the following command to execute the IDL-to-Java compiler from Orbacus. I target all the generated files to an output directory that is in my CLASSPATH.
Listing 2. Calling the IDL-to-Java compiler
What has been produced? All the Java source files on which the implementation will be built are generated by this command. The IDL-to-Java compiler ensures that the defined interface will abide by the laws set forth by the CORBA specification.
Figure 2. IDL-to-Java compiler file generation

Here are the files:
- calculator.java - This file is called the signature interface file. The CORBA specification states that this file must extend IDLEntity and have the same name as the IDL interface name. This file provides the type signature so that this interface can be used in method declarations for other interfaces.
-
calculatorOperations.java - This file contains the Java public
interface -- calculatorOperations. The specification states that this file
should have the same name as the IDL interface name with the
Operationssuffix added and contains the mapped operations signatures for the interface. The signature interface defined above (calculator.java) extends this interface. -
calculatorHelper.java - The helper class is designed to keep
many of the needed housekeeping functions out of our interfaces but readily
available to our implementations. The helper file contains the important
static narrow method that allows an
org.omg.CORBA.Objectto be narrowed to an object reference of a more specific type, which in this case will be a calculator type. - calculatorHolder.java - The holder class is a specialized class that is generated for any type that may need to be passed by reference. The holder class will not be used in this example but we will see it often in future columns.
-
calculatorPOA.java - The skeleton class provides much of the request-response plumbing for CORBA functionality.
calculatorPOA.javais generated because our default implementation is inheritance based. Our output would be different if we chose to have a delegation-based implementation. Future columns will present these topics in detail. - _calculatorStub.java - As the name implies, this is the stub class. Your client will need this class to make it all work.
The generated files must now be put to work in a server that implements our interface. Thankfully, much of the plumbing is in place for us, but don't celebrate too soon -- there is still a lot of work to do; namely, all these files have be used in the correct places.
Let's start with an implementation of our method add(). (You can download the complete SimpleCalcSvr.java file.)
SimpleCalcServant extends calculatorPOA {
public int add(intx, inty) {
return x + y;
}
} |
Notice that our implementation class extends the generated class calculatorPOA.
When a request comes in from a client it comes up through the ORB into
the skeleton. The skeleton will eventually call into SimpleCalcServant
to complete the request and start the response. Our interface was simple,
therefore our implementation is simple as well.
Implementing the rest of the server involves setting up the CORBA architecture around this interface implementation. For reasons of portability and flexibility, many of these calls are mandated by the CORBA specification.
The first task we need to complete is detailing which ORB we would like to use, and then initializing it. The following code (lines 18 thru 29 in file SimpleCalcSvr.java) handle this task:
java.util.Propertiesprops = System.getProperties();
props.put("org.omg.CORBA.ORBClass",
"com.ooc.CORBA.ORG");
props.put("org.omg.CORBA.ORBSingletonClass",
"com.ooc.CORBA.ORBSingleton");
org.omg.CORBA.ORBorb =null;
// initialize the ORB
orb = org.omg.CORBA.ORB.init(args, props); |
When we initialize the ORB we need to tell it exactly which class will
be acting as the ORBClass and which will be acting as the ORBSingleton
class. Our implementation should not care, but all of our associated plumbing
will. As I said before, in this case I am using Object Oriented Concepts,
Inc. Orbacus ORB and the OOC classes are given in those two props.put()
calls. Once the properties are filled in, props is simply passed as a parameter
to the ORB.init() method. The fact is, things
change and if we wanted to move this server to another ORB we would not
want to have to recode our server. Therefore, we would ideally like to
alter a configuration file to point to another ORB class and simply restart.
The ORB is now in place and initialized, and the implementation is in place
but not yet created. At this point we need to create the perfect place
for our implementation to live in, and this is not as easy as it sounds.
In a distributed environment, each of our implementations may require slight
environmental differences. There are a lot of characteristics that we can
give our implementations. Implementations can be single-threaded or multi-threaded,
they can be highly scalable pools of objects, or they can be singletons.
These many different server characteristics have given rise to the Portable
Object Adapter (POA). The POA allows us to create the perfect environment
for our implementation to reside in. All 2.3-compliant ORBs will have a
root POA from which all other POAs are created. In this simple example
I have split off the implementation-specific code into its own method: runcalc().
Creating an environment for our implementation will be our first task, so we'll have to set up a POA. Originally, CORBA servers worked with Basic Object Adapters (BOA), but every vendor's BOA was different. With the latest version of the CORBA specification, the BOA is completely replaced by the POA.
// setup the Portable Object Adapter
// from the always present rootPOA
org.omg.PortableServer.POArootPOA =
org.omg.PortableServer.POAHelper.narrow(
orb.resolve_initial_references("RootPOA"));
org.omg.PortableServer.POAManagermanager =
rootPOA.the_POAManager(); |
By title and definition, this is a simple example. We will keep things simple by using the root POA rather than creating a new one. The POA manager is an object that encapsulates the processing state of the POA. So, we will use the POA manager to start the queuing of requests to our servants.
We still need to instantiate our implementation:
// create servant for calculator interface
SimpleCalcServantcalcSvt =new SimpleCalcServant();
calculatorcalc = calcSvt._this(orb); |
Per the CORBA 2.3 specification, all skeletons provide a _this()
method, which allows the servant to obtain
the object reference for the target CORBA object it is associating for
those requests.
After we have instantiated the implementation, the mechanics must be put into place for our clients to be able to locate it. There are many different methods and services available to locate objects that will satisfy the request to an interface. The CORBA Services define the Naming Service and the Trader Service specifically to help clients find objects to handle requests. An object could also be passed in on a method call.
In this example, we will use the most straightforward method of all -- writing our object reference to a file that will be picked up by our client. The creation of a string representation of an object reference and the reverse, string-to-object reference, is required functionality for all ORBs.
// write the object reference to a file
PrintWriterrefstr =new PrintWriter(
new FileWriter("calcref.ior"));
refstr.println(orb.object_to_string(calc));
refstr.close(); |
The last thing we need to do is activate our POA to start queuing client requests and force the server to enter its event loop to receive those incoming requests.
Listing 8. SimpleCalcSvr.java -- Activate POA
// make the implementation available
manager.activate();
System.out.println("SimpleCalcSvr is running!");
orb.run(); |
If you think about the mechanics of what is happening, you should realize that the client and server are really just mirror images of each other. The client creates the request by packaging up all the parameters and sending the request on its way. The server simply unpacks the parameters of the request, performs the operation, packages up the return value and out parameters, and sends the response back to the client. The client unpacks the return value and out parameters, then continues processing. Therefore, what the client packages, the server unpacks and vice versa.
This means that you should expect to see similar structures between the client and the server. The client must also create and initialize an ORB. It could be the same ORB we were using, or it could be an ORB from a different vendor. However, it cannot be just any ORB. It should be an ORB that supports IIOP, the TCP/IP-based interoperability protocol defined by the Object Management Group (OMG). If you have an older ORB, beware -- it may not speak with other ORBs.
First, we create our ORB in the same manner as the server. (You can download the complete SimpleCalcClient.java file.)
Listing 9. SimpleCalcClient.java -- Initialize ORB
java.util.Propertiesprops = System.getProperties();
props.put("org.omg.CORBA.ORBClass",
"com.ooc.CORBA.ORG");
props.put("org.omg.CORBA.ORBSingletonClass",
"com.ooc.CORBA.ORBSingleton");
org.omg.CORBA.ORBorb =null;
// initialize the ORB
orb = ORB.init(args, props);
|
Look familiar? It should, it is exactly like the server. Our client is now connected to an ORB, but our goal is to call to a service that is provided elsewhere in our system. We need to locate the object that will respond to our request. In this example, that means obtaining an object reference from the file we created in the server. To locate the calculator server we need to take the string version of the object reference stored in the file and turn it into an object reference we can use to call through.
Listing 10. SimpleCalcClient.java -- Get object reference
System.out.println("Getting reference from string...");
BufferedReaderin = new BufferedReader(
new FileReader("calcref.ior"));
Stringior = in.readLine();
in.close();
calculatorcalc = calculatorHelper.narrow(
orb.string_to_object(ior));
|
Notice the use of the calculatorHelper
class generated by our IDL-to-Java compiler. The calcref.ior
file contains an object reference rather than a calculator reference. The calculatorHelper
class's narrow method is used to focus the abstract type to the specific
calculator type.
Look closely at calculator calc. That
represents the server somewhere out there in cyberspace. The last thing
we have to do is invoke the method add()
on calc.
Listing 11. SimpleCalcClient.java -- Invoke add()
System.out.println( calc.add(2,3) ); |
I know we've covered a lot, but think of what we've gained. Our
client is completely isolated from our server. It does not know
what hardware it is running on, what operating system it is using,
what language it is written in, if it is multi-threaded, or where it is
located -- be it in the next room or halfway around the world. All it knows is that if it calls add() on calc it will get a response it can count on.
This is all about providing a service like the telephone or the electric company. When you pick up your phone you expect to get a dial tone and a clear connection for your call. You don't care if the call is being routed through fiber optic cables or bounced off a satellite. The same is now coming true in the information industry, thanks to the OMG and the infrastructure we put into our simple, yet extraordinarily powerful, example.
Next month we will dig a little deeper and look at the magic of IIOP that is happening just below the surface.
- Find out more about CORBA at the OMG Web site.
- Learn about Java programming from Bruce Eckel at Mindview.net
and read the second edition of
Thinking
in Java
- Read the previous CORBA Junction column, "Laws and liberties: Why choose CORBA and Java technology?"

Residing in Berwyn, Pennsylvania, Dave Bartlett is an independent consultant and freelance author and instructor. He is the author of Hands-On CORBA with Java, a 5-day course presented via public sessions or in-house to organizations. Presently, Dave is working to turn the course material into a book entitled Thinking in CORBA with Java. Dave has Masters degrees in Engineering and Business from Penn State. He can be reached at dbartlett@pobox.com.




