J2SE 1.4 breathes new life into the CORBA community, Part 1
Get started developing enterprise CORBA applications with the latest version of J2SE
Over the past decade, CORBA, or the Common Object Request Broker Architecture, has lived up to its initial promise of providing developers an open, standard, and vendor-, platform-, and language-neutral specification for heterogeneous computing. Using the Internet Inter-ORB Protocol (IIOP), a CORBA-based program from any vendor, on most computers, operating systems, programming languages, and networks, can interoperate with a CORBA-based program from the same or different vendor, on most other computers, operating systems, programming languages, and networks. Similarly, Java has lived up equally well to its promise of providing Write Once, Run Anywhere portability, a highly productive implementation environment, and a robust platform. Is it any surprise then that the marriage between CORBA and Java is a match made in heaven?
This article, the first in a series of four, will begin with a brief history on the evolution of CORBA. I will then summarize the major additions to CORBA 2.3.1, the version that J2SE 1.4 supports. Finally, I will end the article with a whirlwind tour in which we’ll create an application using CORBA and J2SE.
Read the whole series, “J2SE 1.4 Breathes New Life into the CORBA Community:”
- Part 1: Get started developing enterprise CORBA applications with the latest version of J2SE
- Part 2: Gain code portability with the Portable Object Adapter
- Part 3: Create enterprise-level apps with the POA
- Part 4: Portable interceptors and the Interoperable Naming Service
A quick history lesson
In 1991, the Object Management Group (OMG) introduced CORBA 1.1, which defined the interface definition language (IDL) and APIs that enabled client/server object interaction within a specific implementation of an object request broker (ORB). CORBA 2.0, adopted in December 1994, took a major step forward and defined true interoperability between ORBs from different vendors via IIOP, which is the TCP/IP mapping of the General Inter-ORB Protocol (GIOP). CORBA 2.0 mandates that all compliant ORBs support IIOP to ensure interoperability.
Since then, CORBA has evolved slowly (the current version is 2.6.1), but each stage in that evolution has seen the addition of solid features geared towards creating scalable enterprise systems. Sun Microsystems started supporting CORBA in 1998 with the release of JDK 1.2 and the addition of Java IDL and a simple ORB. That began the long-lasting relationship between Java and CORBA. Today, four years later, J2SE 1.4 continues this marriage with support for CORBA 2.3.1. (As mentioned previously, the most recent version of CORBA is 2.6.1; its only significant difference from 2.3.1 is CORBA messaging, added in version 2.4.)
The basic CORBA architecture
Figure 1 shows the essence of a CORBA method invocation. As in most distributed systems, the client references a server application’s (or object’s) proxy. In CORBA, that proxy is called a stub. The client interacts with this stub as if it were the server and makes method invocations on it. The stub passes the request to the ORB, which, in turn, locates the server application, activating it if necessary. The ORB interacts with the server application through yet another proxy called a skeleton. Ultimately, the server application processes the client request, and the results return to the client through the same chain.
Yes, I glossed over several details above. First, I described a synchronous request; that is, the client waits for its request’s response before proceeding further. CORBA 2.3.1 also supports a deferred synchronous request—also called polling—in which the client can return periodically and ask the ORB if it has received a response to its prior request(s). Another request type is the one-way request, the CORBA name for fire and forget. Here the client makes the request and then forgets about it. CORBA does not mandate any guarantees about how ORBs implement these one-way requests. For example, an ORB that never delivers any one-way requests would be completely CORBA compliant (eeks!). Finally, CORBA 2.4 introduces a sophisticated asynchronous messaging architecture, which stretches beyond the scope of this series since it is not part of J2SE 1.4 (which, as you should have memorized by now, supports CORBA 2.3.1).
Other points that I glossed over include the communication protocol(s) involved in the method invocations and the ORB organization. Conceptually, the ORB is composed of two main parts: a client-side runtime and a server-side runtime. The stubs are part of the client-side runtime, and the skeletons are part of the server-side runtime. When only one ORB is involved in the picture, as in our example so far, the communication protocol used between the client- and server-side runtimes may be IIOP or any other protocol of the vendor’s choice. Figure 2 illustrates that. Many commercial ORBs use a highly optimized and efficient protocol in such cases. But remember: All ORBs compliant with versions 2.0 and higher of CORBA must support communication using IIOP as well, to ensure interoperability across ORBs.
Now look at Figure 3, which shows the same scenario as Figure 1, with one major difference: the client application is created to work with Vendor 1’s ORB, and the server application works with Vendor 2’s ORB. Luckily both ORBs are at least CORBA 2.0 compliant and can communicate via IIOP; otherwise (consistent) communications between them could not be guaranteed.
Now let’s go into more detail about the client-side ORB runtime. As mentioned above, the stub is part of the client-side ORB. A stub is precompiled and tailor-made (and most likely compiler-generated) for a specific server application. But clients do not have to interact with a server application using these stubs. CORBA defines the dynamic invocation interface (DII), which is a general-purpose API for invoking methods on a remote server application. In most cases, the DII proves harder to use than stubs and slightly less efficient as well. However, you need to use DII sometimes—for the development of general-purpose tools, for example, or to make deferred synchronous method calls.
Note: Depending on the CORBA implementation you use, the ORB may be a single executable, multiple executables, or some combination of executables and shared libraries (such as DLLs on Windows). CORBA does not specify how an ORB is implemented. That makes sense: First, how a vendor implements the ORB depends highly on the capabilities of the device (hardware and operating system) upon which the ORB will run. Second, the implementation also depends on other value-adding services, such as load balancing and fault tolerance, that the vendor provides as competitive differentiators. Since the OMG cannot possibly know and control both these aspects, it has refrained from defining an ORB’s physical implementation details.
What’s new in J2SE 1.4?
As I mentioned earlier, the relationship between Java and CORBA started in 1998 with JDK 1.2, when Sun introduced Java IDL, a Java API for interoperability and integration with CORBA. Java IDL included both a Java-based ORB, which supported IIOP, and the IDL-to-Java compiler, idltojava
, for generating client-side stubs and server-side code skeletons. In 1999, Sun released JDK 1.3, newly rechristened as J2SE, and introduced a new, 100% Pure Java IDL-to-Java compiler, idlj
, along with support for IDL abstract interfaces and value types. Today, with J2SE 1.4, Sun completely supports the Portable Object Adapter (POA), portable interceptors, Interoperable Naming Service (INS), GIOP 1.2, and dynamic anys. I’ll briefly touch upon all of these components in subsequent sections of this article.
J2SE 1.4 also includes a new naming service implementation called Object Request Broker Daemon (ORBD), which lets clients transparently locate and invoke persistent objects on servers in the CORBA environment. ORBD is a daemon process containing a bootstrap service, a transient naming service, a persistent naming service, and a server manager. J2SE 1.4 also supports (for backwards compatibility) tnameserv
, which is a transient naming service implementation that originated in JDK 1.2. I recommend using ORBD in all your new applications. We’ll be using it in our Hello World example later in this article. I’ll talk more about transient and persistent object references in Parts 2 and 3 of this series.
In addition, J2SE 1.4 includes a new tool called servertool
, which provides a command-line interface for application programmers to register, unregister, start up, and shut down a persistent server with ORBD. I’ll use this tool in Parts 2 and 3 of this series.
Portable Object Adapter
An object adapter in CORBA glues the server applications (objects) and the ORB together. An object adapter creates object references, managing objects’ life cycles, dispatching client requests from the ORB to the correct object, and returning request results to the ORB. Until version 2.2, the CORBA specification only described a simple and vague object adapter, appropriately called the Basic Object Adapter (BOA). Since the BOA as described by the specification proved inadequate for creating enterprise applications, each ORB vendor added its own proprietary extensions, sometimes even using the same names as other vendors to create an illusion of interoperability and code portability. CORBA 2.2 introduced the POA, which addressed many of the BOA’s deficiencies. True to its name, the POA guarantees code portability across ORBs. The POA has also introduced new terms to CORBA, such as servants and servant managers. Parts 2 and 3 of this series will cover the POA in detail with examples.
Portable interceptors
Portable interceptors (PIs) are hooks into the ORB through which ORB services can intercept the ORB’s normal execution flow. I’ll discuss the several types of PIs in Part 4.
Interoperable Naming Service
The INS attempts to ease multiple sore points with CORBA object references and the naming device. One of the problems that developers have had with IORs (interoperable object references) is that they are just too hard to read and even harder to remember. The INS supports simpler, human-readable object URLs, similar to the URLs that locate RMI (remote method invocation) objects. Developers have also found fault with the way names must be created for use with the naming service. The INS supports stringified names such as Acme/Finance/AccountsPayable
. In Part 4, I’ll cover the INS, and I’ll also create a GUI-based naming service browser.
Dynamic anys
For those of you not familiar with CORBA, an any is a universal data type that can hold any other data type. Until CORBA 2.2, an any could not be created dynamically, a severe drawback for some applications. For example, debuggers, generic user interfaces for objects, and services such as the OMG Notification Service all require the ability to interpret values without knowing the values’ IDL types at compile time. The DynAny
interface was added to CORBA with the 2.2 revision to let applications dynamically compose and decompose any values. DynAny
permits applications to compose at runtime a value whose type was unknown when the application compiled, and to transmit that value as an any. Similarly, DynAny
allows applications to receive a value of type any from an operation invocation, and both to interpret the any’s type (using the TypeCode
interface) and to extract its value (using the DynAny
interface) without compile-time knowledge of the IDL types involved. I will not cover dynamic anys in this series.
A quick example: Hello World
Now that you know what J2SE 1.4 offers, I’ll quickly examine the fundamental steps required to build a CORBA application. I won’t spend too much time explaining the code, since I’ll cover many of the details in the remaining three parts of this series.
To create a CORBA application, follow these essential steps:
- Define the methods using the IDL
- Compile the IDL using a compiler, thus creating the programming-language-specific constructs
- Implement the server application, which consists of the method implementations and a host that starts up and registers the implementation with the ORB runtime
- Create client applications to use the server
As I mentioned above, I rush through these steps in this article. The later parts of this series will explain the important concepts with better examples. Without further ado, let’s get started with the Hello World example.
Step 1: Define the methods using IDL
Here is the IDL for our simple Hello World server:
module hello
+{
interface Hello
{
string sayHello();
oneway void shutdown();
};
};
Step 2: Compile the IDL using the idlj compiler
Here’s the command that compiles the above IDL to create the Java interfaces and abstract classes:
idlj -fall Hello.idl
idlj
is located in your J2SE 1.4 installation’s bin
directory. This compiler implements the CORBA IDL-to-Java mapping specifications. It produces some Java files as output, which I briefly describe below:
HelloPOA.java:
The abstract class that our server object will extendHelloHelper.java:
Provides auxiliary functionality, notably thenarrow()
method required to cast CORBA object references to their proper typesHelloHolder.java:
Used whenever theHello
interface (type) is anout
or aninout
parameter to a method defined in IDL_HelloStub.java:
The client stub, providing CORBA functionality for the clientHello.java
andHelloOperations.java:
Together provide interfaces that contain the Java version of our IDL interface
Step 3: Implement the server application
As mentioned above, the implementation of the server application consists of two parts: one class that implements the methods from the server interface, and another class that hosts the server object and registers it with the ORB runtime and naming service:
package hello;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import org.omg.PortableServer.POA;
import java.util.Properties;
class HelloImpl extends HelloPOA {
private ORB orb;
public void setORB(ORB orb_val) {
orb = orb_val;
}
// Implement sayHello() method
public String sayHello() {
return "nHello world !!n";
}
// Implement shutdown() method
public void shutdown() {
orb.shutdown(false);
}
}
public class HelloServer {
public static void main(String args[]) {
try{
// Create and initialize the ORB
ORB orb = ORB.init(args, System.getProperties());
// Get reference to rootpoa
POA rootPOA = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
// Create servant and register it with the ORB
HelloImpl helloImpl = new HelloImpl();
helloImpl.setORB(orb);
// Get object reference from the servant
org.omg.CORBA.Object ref = rootPOA.servant_to_reference(helloImpl);
Hello href = HelloHelper.narrow(ref);
// Get the root naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
// Use NamingContextExt, which is part of the Interoperable
// Naming Service (INS) specification
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
// Bind the Object reference in Naming
String name = "Hello";
NameComponent path[] = ncRef.to_name( name );
ncRef.rebind(path, href);
// Activate the POAManager
rootPOA.the_POAManager().activate();
System.out.println("HelloServer ready and waiting ...");
System.out.println(orb.object_to_string(href));
// Wait for invocations from clients
orb.run();
}
catch (Exception e) {
System.err.println("ERROR: " + e);
e.printStackTrace(System.out);
}
System.out.println("HelloServer Exiting ...");
}
}
Step 4: Create client applications to use the server
Here’s a sample client to test the server:
package hello;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
import java.util.*;
public class HelloClient
{
static Hello helloImpl;
public static void main(String args[])
{
try{
// Create and initialize the ORB.
ORB orb = ORB.init(args, System.getProperties());
// Get the root naming context.
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
// Use NamingContextExt instead of NamingContext. This is
// part of the Interoperable Naming Service.
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
// Resolve the Object reference in Naming.
String name = "Hello";
helloImpl = HelloHelper.narrow(ncRef.resolve_str(name));
System.out.println("Obtained a handle on server object: " + helloImpl);
System.out.println(helloImpl.sayHello());
helloImpl.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Compile the server and client Java source files. Start the naming service as follows:
orbd -ORBInitialPort 1050 -ORBInitialHost localhost
Now run the server:
java HelloServer -ORBInitialPort 1050 -ORBInitialHost localhost
Then run the client:
java HelloClient -ORBInitialPort 1050 -ORBInitialHost localhost
Until next time
In this introductory article, I’ve set the stage for the more advanced and fun topics to come in the remaining parts of this series. In Part 2, I will introduce you to the POA released in CORBA 2.2, which offers one of the most important portability-enhancing additions to CORBA after IDL. Part 3 will go into even more detail about the POA. Finally, Part 4 will cover knickknacks such as portable interceptors and the INS.