4.2. Creating CORBA Objects

Now that you understand the various parts of the CORBA architecture,
let's walk through the creation of CORBA objects using Java
IDL. In order to distribute a Java object over the network using
CORBA, you have to define your own CORBA-enabled interface and it
implementation. This involves doing the following:

Writing an interface in the CORBA Interface Definition Language

Generating a Java base interface, plus a Java stub and skeleton class, using an IDL-to-Java compiler

Writing a server-side implementation of the Java interface in Java

4.2.1. An IDL Primer

This section provides a quick overview of writing a CORBA interface in IDL. A full reference on IDL syntax is provided in Chapter 10, "IDL Reference", if you need more details.

The syntax of both Java and IDL were modeled to some extent on C++, so there are a lot of similarities between the two in terms of syntax. Interfaces in IDL are declared much like classes in C++ and, thus, classes or interfaces in Java. The major differences between IDL and Java are:

IDL is a declaration language. In IDL, you declare only the names and types for interfaces, data members, methods, method parameters, etc. Method implementations are created in the implementation language you choose (in this case Java), after you've used an IDL compiler to convert your IDL interface to your target language.

IDL, like C++, includes non-class data structure definitions, like structs, unions, and enumerations.

Method parameters in IDL include modifiers that specify whether they are input, output, or input/output variables. In Java, all primitive data types are passed by value, and all object data types are passed by reference.

An IDL file can include multiple public interfaces. Java allows multiple inner classes within a single public class definition and multiple nonpublic classes per file, but only a single public class can be defined in a given Java file.

Modules, which are similar to Java packages, can be nested within other modules in the same IDL file, and interfaces in multiple distinct modules can be defined in the same IDL file. In Java, you can define a class only within a single package in a single Java file.

4.2.1.1. Modules

Modules are declared in IDL using the module keyword, followed by a name for the module and an opening brace that starts the module scope. Everything defined within the scope of this module (interfaces, constants, other modules) falls within the module and is referenced in other IDL modules using the syntax modulename::x. Suppose that you want all your classes to be contained in a module called corba, which is part of a larger module called jen (an acronym for the title of this book). In IDL this is declared as follows:

// IDL
module jen {
module corba {
interface NeatExample ...
};
};

If you want to reference the NeatExample interface in other IDL files, use the syntax jen::corba::NeatExample, which may look familiar to readers who have done C++ programming. Java programmers should note the semicolons following the closing braces on the module definitions, which are required in IDL but not in Java. A semicolon is also required after the close of an interface definition.

4.2.1.2. Interfaces

Interfaces declared in IDL are mapped into classes or interfaces in Java. As I mentioned before, IDL is used only to declare modules, interfaces, and their methods. Methods on IDL interfaces are always left abstract, to be defined in the programming language you use to implement the interfaces.

The declaration of an interface includes an interface header and an interface body. The header specifies the name of the interface and the interfaces it inherits from (if any). Here is an IDL interface header:

interface PrintServer : Server { ...

This header starts the declaration of an interface called PrintServer that inherits all the methods and data members from the Server interface. An IDL interface can inherit from multiple interfaces; simply separate the interface names with commas in the inheritance part of the header.

4.2.1.3. Data members and methods

The interface body declares all the data members (or attributes) and methods of an interface. Data members are declared using the attribute keyword. At a minimum, the declaration includes a name and a type (see Chapter 10, "IDL Reference" for a complete list of the basic data types available in IDL and the mapping to Java types). The declaration can optionally specify whether the attribute is read-only or not, using the readonly keyword. By default,
every attribute you declare is readable and writable (for Java, this means that the IDL compiler generates public read and write methods for it). Here is an example declaration for a read-only string attribute:

readonly attribute string myString;

You declare a method by specifying its name, return type, and parameters,
at a minimum. You can also optionally declare exceptions the method might raise, the invocation semantics of the method, and the context for the method call (see Chapter 10, "IDL Reference" for more details). Here is the declaration for a simple method that returns a string:

string parseString(in string buffer);

This declares a method called parseString() that accepts a single string argument and returns a string value.

4.2.1.4. A complete IDL example

Now let's tie all these basic elements together. Here's a complete IDL example that declares a module within another module, which itself contains several interfaces:

The first interface, Server, has a single read-only string attribute and an init() method that accepts a string and returns a boolean. The Printable interface has a single print() method that accepts a string header. Finally, the PrintServer interface extends the Server interface (hence inheriting all its methods and attributes) and adds a printThis() method that accepts a Printable object and returns a boolean. In all cases, we've declared our method arguments as input-only (i.e., pass-by-value), using the in keyword.

4.2.2. Turning IDL Into Java

Once you've described your remote interfaces in IDL, you need to generate Java classes that act as a starting point for implementing those remote interfaces in Java using an IDL-to-Java compiler. Every standard IDL-to-Java compiler generates the following Java classes from an IDL interface:

A Java interface with the same name as the IDL interface. This can act as the basis for a Java implementation of the interface (but you have to write it, since IDL doesn't provide any details about method implementations).

A helper class whose name is the name of the IDL interface with "Helper" appended to it (e.g., ServerHelper). The primary purpose of this class is to provide a static narrow() method that can safely cast CORBA Object references to the Java interface type. The helper class also provides other useful static methods, such as read() and write() methods that allow you to read and write an object of the corresponding type using I/O streams.

A holder class whose name is the name of the IDL interface with "Holder" appended to it (e.g., ServerHolder). This class is used when objects with this interface are used as out or inout arguments in remote CORBA methods. Instead of being passed directly into the remote method, the object is wrapped with its holder before being passed. When a remote method has parameters that are declared as out or inout, the method has to be able to update the argument it is passed and return the updated value. The only way to guarantee this, even for primitive Java data types, is to force out and inout arguments to be wrapped in Java holder classes, which are filled with the output value of the argument when the method returns.

The idltojava tool provided by Sun[3] can also generate two other classes:

A client stub class, called _interface-nameStub, that acts as a client-side implementation of the interface and knows how to convert method requests into ORB requests that are forwarded to the actual remote object. The stub class for an interface named Server is called _ServerStub.

A server skeleton class, called _interface-nameImplBase, that is a base class for a server-side implementation of the interface. The base class can accept requests for the object from the ORB and channel return values back through the ORB to the remote client. The skeleton class for an interface named Server is called _ServerImplBase.

So, in addition to generating a Java mapping of the IDL interface and some helper classes for the Java interface, the idltojava compiler also creates subclasses that act as an interface between a CORBA client and the ORB and between the server-side implementation and the ORB. Chapter 12, "Java IDL Tools", provides a complete reference for Sun's idltojava compiler. We use this IDL-to-Java tool in the examples in this chapter. Remember, though, that any Java mapping of the CORBA standard should include its own IDL-to-Java compiler to generate these Java classes from the IDL interfaces you write. In addition, the Java that these tools generate should be compliant with the standard IDL mapping for Java, published by the OMG in the CORBA standard.

4.2.2.1. A simple server class

The IDL interface shown in Example 4-1 is the IDL
equivalent of the Java class we defined in Example 4-3 in the RMI
chapter. The interface, named ThisOrThatServer,
declares two methods, doThis() and
doThat(). As in the earlier RMI example, each
method accepts a string that specifies what to do and returns a string
that indicates what was done. Since this is IDL, the string data type
is string, and the parameters are declared as
in arguments, since we want them to be passed into
the remote method by value.

Example 4-1. A ThisOrThatServer Declared in IDL

We can run the idltojava compiler on this IDL interface using the following command line (Windows version):

D:\>idltojava -fno-cpp ThisOrThatServer.idl

This command creates the five Java classes I just described: a Java version of the interface, a helper class, a holder class, a client stub, and a server skeleton. I had to use the -fno-cpp option on my machine because I don't have a C preprocessor installed for idltojava to use; this option tells the IDL compiler to use an alternate parsing scheme while it converts the IDL to Java (see Chapter 12, "Java IDL Tools" for complete details on the command-line arguments for idltojava).

The compiler creates the Java interface shown in Example 4-2, in a file named ThisOrThatServer.java. The mapping is fairly straightforward for this simple example. The interface declaration is mapped directly to a Java interface declaration, with the interface extending the org.omg.CORBA.Object interface. If we had included any module definitions in our IDL specification, they would have been mapped into a package statement at the beginning of the Java file. The IDL string type is converted into the Java String type, and, since they don't require any special handling in a remote method call, the in method parameters in IDL are mapped into regular Java input arguments.

You might notice that the IDL compiler has put the semicolons following the method declarations on separate lines. To my knowledge, there's no good reason for this; it's just a quirk of the idltojava tool provided by Sun.

4.2.2.2. The helper class

The compiler also generates a helper class, called
ThisOrThatServerHelper, as shown in Example 4-3. As I mentioned earlier, the helper
class has methods that let you read and write
ThisOrThatServer objects to and from CORBA I/O
streams, get the TypeCode for a
ThisOrThatServer object,
and, most importantly, safely narrow a CORBA Object reference into a ThisOrThatServer reference.

In the implementation of the narrow() method, we
can see how the helper class converts a CORBA
Object reference to a reference to a specific
type. First, the narrow() method checks to see if
the Object parameter is already a
ThisOrThatServer object (using the Java
instanceof operator), then it checks to see if
the object passed in is a null pointer. If
neither case is true, the Object should
contain a delegate of a
ThisOrThatServer object. Every CORBA stub for a
remote object contains an internal Delegate object
(from the org.omg.CORBA.portable package) that's
used by the stub to invoke remote requests. If the object's
delegate is a ThisOrThatServer (checked using the
objects's _is_a() method), the delegate is
used to create a new ThisOrThatServer
stub. We'll take a look at the
ThisOrThatServer stub class in a bit. If the
object doesn't contain a delegate, the is_a()
method returns false, and the
narrow() method throws a
BAD_PARAM exception.

4.2.2.3. The holder class

The compiler generates a holder class for the
ThisOrThatServer class, as shown in Example 4-4. The holder class, called
ThisOrThatServerHolder, is a wrapper used when
ThisOrThatServer objects are called for as
out or inout arguments in an IDL
method. All holder classes implement the Streamable
interface from the org.omg.CORBA.portable
package. An ORB knows to pass Streamable objects in
method calls using the _read() and
_write() methods of the
Streamable object; these methods handle whatever
serialization the object needs.

A holder contains a single instance of a CORBA object (a
ThisOrThatServer, in this example). When a holder
object is passed into a remote method call as an
inout argument, its _write()
method is invoked. This method takes the object contained by the
holder class, serializes it, and streams it through the ORB to the
remote object server. When the remote method call returns, the
holder's _read() method is invoked to read
the (possibly updated) object from the remote object server, and the
holder object replaces its internal value with the updated object.

As an example of using the holder class, let's define another IDL interface that includes a method that uses a ThisOrThatServer as an inout parameter:

The ThisOrThatServerHolder class has public constructors that let you create a holder from an existing ThisOrThatServer object, so that you can easily pass the object into this kind of method.

4.2.2.4. The client and server stubs

The idltojava compiler generates two more classes
from our interface definition: a client stub
(_ThisOrThatServerStub) and a base class for a
server implementation
(_ThisOrThatServerImplBase). The client stub, shown
in Example 4-5, implements the generated
ThisOrThatServer Java interface and acts as a
client-side proxy for a remote ThisOrThatServer
object. The stub has implementations of the
doThis() and doThat() methods
from the interface. Each implementation just generates a request to
the ORB to make a remote method call on the server-side object that
this stub is a proxy for. The method arguments are bundled up and
passed along with the request to the ORB. I'm not going to go
into the details of the stub's method implementations because
you shouldn't have to worry much about them, but it is
enlightening to look at the source code to see how your remote objects
do what they do in detail, using the core CORBA functions.

When a Java client gets a reference to a remote ThisOrThatServer object, it is given one of these stub objects. The client can make method calls on the stub object, and the stub converts these calls into corresponding requests to the ORB to invoke the methods on the remote object and send back the results.

The base class for the server implementation, shown in Example 4-6, accepts requests that are intended for the server implementation from the ORB. The base class converts a request into a method call on the server object and then takes the result of the call and gives it back to the ORB to send to the client stub. All this work is done in the server skeleton's invoke() method. The invoke() method figures out which method is being called, unpacks the method arguments (if any) from the request, and calls the method directly on itself.

Note that the server skeleton doesn't have implementations of the doThis() or doThat() methods declared in the interface. The idltojava compiler doesn't do everything for you; you still need to create a server implementation for your interface.

4.2.3. Writing the Implementation

So, we've written an IDL interface and generated the Java interface and support classes for it, including the client stub and the server skeleton. Now we need to create concrete server-side implementations of all of the methods on your interface. We do this by subclassing from the _xxxImplBase class generated by the idltojava compiler. For our example, we need to subclass _ThisOrThatServerImplBase and implement the doThis() and doThat() methods. The ThisOrThatServerImpl class in Example 4-7 does just that. Note that we've mimicked the method implementations from the RMI example in Chapter 3, "Remote Method Invocation". The only real difference is that this ThisOrThatServerImpl class extends _ThisOrThatServerImplBase, while the one in Chapter 3, "Remote Method Invocation" extends the UnicastRemoteObject.