Integrate EJBs with CORBA

Access EJBs from non-Java-based applications

Enterprise JavaBeans (EJBs) are important for developing mission-critical business applications in Java. However, business applications don't exist in isolation; today, companies require integrated applications. In addition, most companies already have existing applications written in programming languages other than Java. Therefore, integrating EJB-based solutions with existing applications is becoming increasingly important.

In this article, I show how to access EJBs from applications written in programming languages other than Java. More specifically, I discuss access to session and entity beans (which use the synchronous IIOP, or Internet Inter-ORB Protocol, communication) from a CORBA C++ client. I won't address message-driven beans, although you could access them too from other programming languages using MOM (message-oriented middleware)-compliant products.

RMI-IIOP

Session and entity beans use synchronous communication with remote method invocation (RMI). Java 2 Platform, Enterprise Edition (J2EE) 1.3 requires that Java clients use RMI-IIOP. RMI-IIOP uses CORBA's IIOP protocol, which makes RMI-IIOP CORBA compatible. In other words, clients not based on Java can use CORBA to communicate with EJBs.

To do this, you must use a J2EE 1.3-compliant application server. Previous EJB specifications did not require you to use RMI-IIOP. Instead, the application server used RMI-JRMP (Java Remote Method Protocol) or some proprietary protocol. You must also use an ORB (Object Request Broker) compliant with CORBA 2.3.1 or higher. Older CORBA versions did not implement the necessary specifications for RMI-IIOP interoperability, particularly the Objects by Value specification later integrated into the CORBA specification (see the Object Management Group's (OMG) CORBA/IIOP Specification 2.6, under the "Value Type Semantics" chapter) and the Java Language Mapping to OMG IDL specification.

Value-type semantics added the concept of transferring objects by value, introduced by RMI, to CORBA. CORBA initially didn't support this functionality; however, that concept is crucial for achieving Java/CORBA interoperability.

The Java Language Mapping to OMG IDL specification defines how Java interfaces map to the CORBA Interface Definition Language (IDL). This definition lets CORBA distributed objects access EJBs (and also RMI-IIOP distributed objects) that originally didn't have CORBA IDL. Specifically, the specification defines a Java RMI subset, called RMI/IDL, which you can map to IDL using IIOP (or, more generally, General Inter-ORB Protocol) as the underlying protocol for communication.

RMI/IDL

Many RMI/IDL data types conform to certain restrictions; we'll look at the most important ones. For more information, please see the Java Language Mapping to OMG IDL specification.

Table 1 shows the mapping of Java primitive data types to IDL.

Table 1. Java-to-IDL mapping

JavaOMG IDL
voidvoid
booleanboolean
charwchar
byteoctet
shortshort
intlong
longlong long
floatfloat
doubledouble

Java packages map to IDL modules. RMI/IDL remote interfaces map to the IDL interfaces with corresponding names. However, methods that use JavaBeans naming for read-write and read-only properties map to IDL attributes. I'll return to this later.

Java serializable objects map to CORBA value types. Value types provide pass-by-value semantics to CORBA. Value types are local and cannot be called remotely. They aren't registered with the ORB and don't require an identity, as their value is their identity. For more information, please refer to Professional J2EE EAI and the CORBA/IIOP Specification 2.6.

As I mentioned, all Java serializable objects, built-in and user-defined, map to value types. However, there are certain exceptions to the rule -- you'll encounter one when attempting to map java.lang.String, for example. If defined as a constant (final static), the object maps to IDL wstring. In all other cases, including method parameters and return values, the object maps to the CORBA::WStringValue value type. This value type is defined as a part of the CORBA module; the IDL definition is as follows:

valuetype WStringValue wstring;

This is equivalent to the following IDL definition:

valuetype WStringValue {
   public wstring data;
};

Keep in mind, however, that the first definition maps more cleanly to Java. Table 2 shows other special case mappings.

Table 2. Other important special case mappings

JavaOMG IDL
java.lang.Object::java::lang::_Object
java.lang.String::CORBA::WStringValue or wstring
java.lang.Class::javax::rmi::CORBA::ClassDesc
java.io.Serializable::java::io::Serializable
java.io.Externalizable::java::io::Externalizable
java.rmi.Remote::java::rmi::Remote
org.omg.CORBA.ObjectObject

Achieve integration

I'll return to value types later for user-defined classes and then for built-in classes, such as Vectors, Collections, and Enumerations. For now, let's look at the basic approach for CORBA and EJB integration. First, we'll need an EJB. In this first example, we use a simple session bean that only uses primitive data types as method parameters and return values. That way, we aren't forced to use value types. (Note: Accessing entity beans from CORBA clients mirrors the process of accessing session beans.)

This approach is the simplest; however, you cannot use it for more complex interfaces. Its benefit: you can use CORBA ORBs, which don't support value types. This is the case for some CORBA products, particularly for those based on languages other than C++.

For the examples, I'll use Inprise/Borland's VisiBroker for C++ 4.5 as the CORBA ORB and Microsoft's Visual C++ 5.0 (or higher) C++ compiler to compile the C++ client code. To deploy the example EJBs, I'll use BEA's WebLogic 6.1. You can download free trial versions of VisiBroker for C++ and WebLogic 6.1 from Resources.

You can use any CORBA 2.3.1 (or higher)-compliant ORB product that provides C++ mapping, a corresponding C++ compiler, and a J2EE 1.3-compliant application server. Theoretically, no code modifications are needed; however, minor modifications might be necessary if you use other products.

EJB session bean

Let's look briefly at a simple session bean, called CorbaEai. For our first example, the simple remote component interface contains a single method that sums two integer numbers:

package eai;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface CorbaEai extends EJBObject {
  public int sum(int num1, int num2) throws RemoteException;
}

After you become familiar with the integration process, I'll show you how to extend the interface to include methods with user-defined and built-in objects.

To run these examples, you must deploy the session bean to an application server. This requires that you define the home interface, implement the bean implementation class, define the deployment descriptor, build the jar file, and finally deploy the EJB. I won't show these steps; however, you can download the file containing the full code example for WebLogic 6.1, jw-0329-corba.zip, from Resources below.

Develop the CORBA client

To develop the CORBA client, we'll perform the following steps:

  • Generate the IDL from session bean home and component remote interfaces
  • Simplify the generated IDL
  • Compile the IDL interfaces to the corresponding programming language -- C++ in our case -- to generate stubs and other necessary mappings
  • Find out how to use JNDI (Java Naming and Directory Interface) as a CORBA naming service
  • Develop the C++ client and the support for value types
  • Build the client

Generate the IDL

To generate the IDL from Java interfaces, you can use any tool that supports the Java Language Mapping to OMG IDL specification. Examples include:

  • rmic compiler, using the -idl option, bundled with Java SDK 1.3 and higher
  • java2idl, bundled with VisiBroker
  • rmic and ejbc (using -idl) compilers, bundled with BEA WebLogic

Other application servers provide similar tools. Be aware that generating the IDL interfaces from EJB interfaces is more complicated than generating them from RMI-IIOP interfaces. First, the EJB interfaces inherit from the EJBObject and EJBHome interfaces that define some base methods. Because the IDL interfaces also have to inherit from those interfaces, the tool must generate the IDL interfaces for the EJB interfaces as well. Second, the methods in EJB home interfaces throw at least CreateException, so the tool must map this exception, as well as any other user-defined exceptions, to the IDL.

You can use WebLogic's ejbc compiler, which provides all necessary IDL definitions for the enterprise bean. Suppose we built our CorbaEai session bean and generated the eai.jar file. To use ejbc to generate IDL files, we must use an additional -idl switch, which we use to tell the ejbc to generate the IDL interfaces. We also use the -idlDirectory switch to specify the directory in which to store the generated IDL files. First, we have to set WebLogic's environment variables. Then, we can use the following command:

java weblogic.ejbc eai.jar eaiC.jar -idl -idlDirectory ./idl

The ejbc compiler creates IDL files in the ./idl directory, generating 17 IDL files.

Simplify the generated IDL

Compiling and implementing all the generated interfaces and value types in C++ is time consuming. However, we won't need all the generated interfaces because we won't use certain methods. Therefore, we can save a lot of work by manually simplifying the generated IDLs.

For this first example, suppose we don't invoke methods on the EJBObject and EJBHome interfaces, and we don't need the EJBMetaData. We won't need a specialized RemoveException, so we can leave out the RemoteException and RemoveEx IDL interfaces. We can also eliminate all the IDL interfaces: java.io, java.lang, and java.rmi.

To simplify the IDL even further, we can merge the IDL interfaces into one file. Therefore, after simplifying the IDL, we get the following file, named CorbaEai.idl:

#include "orb.idl"
module javax {
  module ejb {
    valuetype CreateException {
       #pragma ID CreateException "RMI:javax.ejb.CreateException:FD98A9711F66DF7F:575FB6C03D49AD6A"
    }; 
  };
};
module javax {
  module ejb {
    exception CreateEx {
       CreateException value;
    };
  };
};
module eai {
  interface CorbaEai {
    long sum( in long arg0,  in long arg1);
    #pragma ID CorbaEai "RMI:eai.CorbaEai:0000000000000000"
   };
};
module eai {
  interface CorbaEaiHome {
    ::eai::CorbaEai create() raises (::javax::ejb::CreateEx);
    #pragma ID CorbaEaiHome "RMI:eai.CorbaEaiHome:0000000000000000"
   };
};

Compile the IDL interfaces

Next, we map the IDL interfaces to the client's programming language. We use C++ for the client, so let's use the idl2cpp compiler included with VisiBroker for C++ 4.5. (You can use any IDL-to-C++ compiler, as long as it complies with CORBA 2.3.1 or higher.)

If you look closer at the IDL definition, you'll see that it includes orb.idl. In our example, we will use the orb.idl file that comes with VisiBroker. Note that, in this case, VisiBroker is installed in the /prg/vbroker folder; therefore, we must specify the /prg/vbroker/idl directory in which the orb.idl file resides in our command. We use the -I switch for the idl2cpp compiler.

We force the IDL compiler to use strict code generation with the -strict switch. Such code corresponds to the CORBA specification; otherwise, VisiBroker generates additional methods. We don't need the server side, so we suppress its generation (-no_servant switch). Additionally, we direct that the idl2cpp compiler should implement IDL modules as C++ namespaces (-namespace switch). Finally, we define that the file suffix should be cpp instead of cc, which is VisiBroker's default (-src_suffix switch).

The command line looks like this:

idl2cpp -strict -no_servant -namespace -I/prg/vbroker/idl -src_suffix cpp CorbaEai.idl

Use JNDI as a CORBA naming service

Before we develop the C++ client, think about how it obtains the initial reference to the session bean home interface. CorbaEaiHome is registered with the application server's (WebLogic in our case) JNDI, so the C++ client must access the JNDI naming service.

Some application servers, like BEA WebLogic, provide an interface compliant with the CORBA naming service to access JNDI. To test this, we use the WebLogic host2ior utility, which outputs the IORs (Interoperable Object References) for the naming service used with IIOP and IIOP/SSL (Secure Socket Layer). For our example's purposes, we are only interested in the nonsecure IIOP.

The CORBA naming service is realized as a CORBA distributed object with a standardized interface, technically the same as other CORBA objects. Therefore, we could use the IOR directly to connect to the JNDI.

A better way, however, gets a reference by resolving the initial references with a keyword NameService. In this case, we specify at the start of the client the service provider that client should use. We do this with a command-line switch that I'll show later.

Develop the C++ client

We develop a simple C++ client that invokes the sum() method on the stateless session bean CorbaEai. I assume you are familiar with CORBA. You can find details of developing CORBA C++ clients in Professional J2EE EAI.

First, we declare the necessary includes, followed by the main() method:

#include "CosNaming_c.hh"
#include "CorbaEai_c.hh"
USE_STD_NS
int main(int argc, char* const* argv)
{

Then we initialize the ORB, connect to the naming service, and obtain the initial context:

1 2 3 Page 1