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
|
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
|
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 higherjava2idl
, bundled with VisiBrokerrmic
andejbc
(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) {Page 2 of 3
Then we initialize the ORB, connect to the naming service, and obtain the initial context:
try { CORBA::ORB_var orb = CORBA::ORB_init(argc, argv); cout << "Accessing the naming service" << endl; CORBA::Object_ptr o = orb->resolve_initial_references("NameService"); cout << "Getting the naming context" << endl; CosNaming::NamingContext_var context = CosNaming::NamingContext::_narrow(o);
Next, we create a name in an appropriate format for the CORBA naming service, under which our EJB home interface is stored in the JNDI. Note that we use the JNDI name corba-eai
:
CosNaming::Name name; name.length(1); name[0].id = CORBA::string_dup("corba-eai"); name[0].kind = CORBA::string_dup("");
Next, we resolve the name and narrow the object reference to our session bean's CorbaEaiHome
home interface:
cout << "Resolving the home interface" << endl; CORBA::Object_var object = context->resolve(name); eai::CorbaEaiHome_var ceaihome = eai::CorbaEaiHome::_narrow(object);
We now have a reference to the home interface, so we can create a new instance of the session bean and invoke a method on it:
cout << "Creating a new instance" << endl; eai::CorbaEai_ptr ceai = ceaihome->create(); CORBA::Long r = ceai->sum((CORBA::Long)15,(CORBA::Long)20); cout << "Result: " << r << endl; } catch(const CORBA::Exception& e) { cout << "Exception: " << e << endl; } return 0; }
Build and run the client
To build the C++ client, we use Microsoft Visual C++ 5.0 (or higher) command-line tools. Before we start, be sure you have set the environment variables for Visual C++. The easiest way is to use the vcvars32.bat
batch file, which you can find in the Visual C++ installation bin
subdirectory. Below is the relevant command line to compile and link the C++ client. Again, VisiBroker should be installed in the /prg/vbroker
folder:
CL /MD /DTHREAD -DWIN32 /GX /DSTRICT /DALIGNED /DVISIBROKER /DMSVCUSING_BUG /DMSVCNESTEDUSING_BUG -I/prg/vbroker/include - I/prg/vbroker/include/stubs CorbaEai_c.cpp Client.cpp /link /out:Client.exe /LIBPATH:/prg/vbroker/lib
To run the example, do the following:
- Start the application server (WebLogic, in our case)
- Deploy the session bean
CorbaEai
to the application server - Start the C++ EJB client, using the command line shown below:
Client -ORBInitRef NameService=iioploc://localhost:7001/NameService
Notice that in this example the EJB application server resides on the same computer as the client. To enable remote communication, specify an actual name or IP address instead of localhost
.
Value types
This example is far from perfect. It doesn't implement the value types for Java serializable objects, which means that we cannot reliably catch remote exceptions (because they are mapped as value types, as the IDL shows). In addition, I limited the first example to methods that use primitive data types only.
To understand how CORBA handles Java serializable classes, you must look at CORBA value types. Java serializables namely map to value types. Every Java serializable object passed as a parameter, return value, or exception to/from a CORBA client has to be re-implemented in the client's programming language, C++ in our case. This requires significant effort. Unfortunately, I am not aware of any CORBA implementations that provide full implementations for Java built-in types, such as Remote
, Throwable
, EJBObject
, and EJBHome
. As far as I know, only the IBM WebSphere Application Server provides a value type library that contains C++ value type implementations for some commonly used Java types, such as Integer
, Float
, Vector
, Exception
, and OutputStream
. According to the WebSphere Application Server 4.0 documentation, the value type library supports the WebSphere C++ ORB only.
For each value type, the IDL to C++ (idl2cpp
) compiler generates a pointer type definition _ptr
and a _var
class for each type. The _var
class automatically manages the memory for the dynamically allocated object reference. A casting operator also lets you assign a _var
to a _ptr
. Also note that the original Java constructors do not map to IDL. However, when IDL maps to C++, constructors become the init
methods on the factory classes.
To implement a value type (in C++, for example), you:
- Implement a value type class, which should inherit from the value type base class (generated by the IDL compiler) and the default value reference count base class. For value types, you must implement reference counting manually.
- Implement the factory class with factory methods.
- Register the factory with the ORB.
Let's look at the example for the CreateException
value type. First, we declare the implementation class CreateExceptionImpl
:
class CreateExceptionImpl : public virtual ::javax::ejb::OBV_CreateException, public virtual CORBA::DefaultValueRefCountBase {
We must provide at least the implementation for the constructor and the _copy_value()
method, which simply returns a new value type class instance:
public: CreateExceptionImpl () : OBV_CreateException () {} virtual ~CreateExceptionImpl () {} CORBA::ValueBase* _copy_value(){ return new CreateExceptionImpl(); }
We can also provide the implementation for other methods, such as message()
, localizedMessage()
, and toString()
:
CORBA::WStringValue* message() { return new CORBA::WStringValue(CORBA::wstring_dup(L"javax::ejb::CreateException")) ; } CORBA::WStringValue* localizedMessage() { return new CORBA::WStringValue(CORBA::wstring_dup(L"javax::ejb::CreateException")) ; } CORBA::WStringValue* toString() { return new CORBA::WStringValue(CORBA::wstring_dup(L"javax::ejb::CreateException")) ; } };
Next, we define the factory class CreateExceptionFactory
. We should provide at least the implementation for create_for_unmarshal()
methods; however, we should also implement the create__()
method. Both methods simply return a new implementation class instance:
class CreateExceptionFactory : public ::javax::ejb::CreateException_init { public: CreateExceptionFactory() {} virtual ~CreateExceptionFactory() {} javax::ejb::CreateException* create__() { return new CreateExceptionImpl(); } CORBA::ValueBase* create_for_unmarshal() { return new CreateExceptionImpl(); } };
Finally, we register the factory with the ORB. For this, the ORB interface provides a method register_value_factory()
, which accepts the repository ID and the factory instance. We can get the repository ID from the IDL (it is defined using the #pragma
directive). The example below shows how to register the CreateException
:
orb->register_value_factory("RMI:javax.ejb.CreateException: FD98A9711F66DF7F:575FB6C03D49AD6A", (CORBA::ValueFactory)new CreateExceptionFactory);
Similarly, we implement all value types, which we use in the CORBA client. Although this looks complicated, you'll see that the procedure is not difficult and you can define template classes to automate it.
Develop a more advanced CORBA client
Now that you are familiar with CORBA value types, you can develop a CORBA client for EJBs with more complicated interfaces.
In this example, we extend our CorbaEai
session bean's interface with several methods that use user-defined and built-in Java objects. First, we add a method sumObj()
to sum up two Integer
objects:
public Integer sumObj(Integer num1, Integer num2) throws RemoteException;
Then we add a getter/setter pair for a user-defined serializable class, called MyType
:
public MyType getMyType() throws RemoteException; public void setMyType(MyType mt) throws RemoteException;
The MyType
class is simple; it contains two attributes (for simplicity, I have not shown the corresponding getter/setter methods):
package eai; import java.io.Serializable; public final class MyType implements Serializable { public int a; public double b; }
Finally, we add a method that returns a Java vector (getVector()
) and a method that returns a Java collection (getCollection()
):
public Vector getVector() throws RemoteException; public Collection getCollection() throws RemoteException;
These methods represent typical methods found in most EJB-based applications. The complete example can be found in jw-0329-corba.zip
.
The process of developing the CORBA client resembles that of the first example. First, we generate the IDL interfaces. We can use the same command as before; however, we won't simplify the IDL this time.
Please notice that the generated IDL interface for the CorbaEai
session-bean remote component interface follows the RMI/IDL rule for mapping property accessor methods. Methods that use the JavaBeans naming for read-write and read-only properties do not map to IDL operations, but rather to IDL attributes. In our example, we have the getMyType()
and setMyType()
read-write property methods and the getVector()
and getCollection()
read-only methods. These methods map to the following IDL attributes:
attribute ::eai::MyType myType; readonly attribute ::java::util::Vector vector; readonly attribute ::java::util::Collection collection;
To access these attributes from C++ (or some other language), you must know how they map from IDL to C++ (or whatever language you choose). For C++, each attribute maps to two overloaded C++ functions, one to set and one to get the attribute value, with the same name as the attribute. A read-only attribute maps to the get function only. For more information on IDL-to-C++ mapping, please refer to OMG's C++ Language Mapping.
Other methods, such as sum()
and sumObj()
, map on a one-to-one basis:
long sum( in long arg0, in long arg1); ::java::lang::Integer sumObj( in ::java::lang::Integer arg0, in ::java::lang::Integer arg1);
Next, we compile the IDL interfaces to C++ using the VisiBroker idl2cpp
compiler. You can compile all the generated IDL files one by one, or you can gather all generated IDL files into one file and then compile that file.
Now we can code the C++ client. First, we provide the value type implementations (value type classes and factory classes), as I explained before, for all relevant value types. In our example, we define the implementations for the following value types:
MyType
Integer
Vector
CreateException
RemoveException
We also register those value types with the ORB.
You might wonder why we didn't define a value type for the Java Collection
. As you know, Collection
is an interface, implemented by the AbstractCollection
class, which provides a skeletal implementation. The Java SDK provides implementations for more specific interfaces, such as List
. To understand the class and interface hierarchy, please examine the following UML diagram.

You can see that Vector
is a possible implementation for the Collection
interface. Therefore, we use it to access the Collection
.
The actual C++ code to invoke the CorbaEai
session bean methods is relatively simple. To invoke the sumObj()
method, we first create two Integer
value types and insert the values. Then we invoke the method and output the result:
::java::lang::Integer_var javaInt1 = new IntegerImpl(); ::java::lang::Integer_var javaInt2 = new IntegerImpl(); javaInt1->value(15); javaInt2->value(20); ::java::lang::Integer_var javaIntR = ceai->sumObj(javaInt1, javaInt2); cout << "sumObj(): " << javaIntR->value() << endl;
To invoke the getMyType()
and setMyType()
methods, recall that we mapped them to the IDL attribute. Therefore, we use the C++ overloaded method myType()
. The following code invokes the getMyType()
method on our session bean:
::eai::MyType_var mt = ceai->myType(); cout << "getMyType(): " << mt << endl;
To invoke the setMyType()
method, we first create a MyType
instance and fill in the necessary values; only then do we invoke it:
cout << "setMyType()... " << endl; ::eai::MyType_var mt2 = new MyTypeImpl(); mt2->a((CORBA::Long)9); mt2->b((CORBA::Double)9.9); ceai->myType(mt2);
Notice that the method names have changed because we used JavaBeans naming (get/set methods). Otherwise, the methods would map without changes.
The same is true for the getVector()
method, which we mapped to a read-only IDL attribute. Therefore, we access it from C++ as vector()
:
::java::util::Vector_ptr vec = ceai->vector(); cout << "getVector(): " << vec << endl;
Perhaps the most interesting part of this whole process is how we access the Collection
. After invoking the getCollection()
session bean method (which in C++ is called collection()
), we explicitly downcast it to a Vector
:
::java::util::Collection_ptr coll = ceai->collection(); ::java::util::Vector_ptr cvec = ::java::util::Vector::_downcast(coll); cout << "Collection: " << cvec << endl;
Finally, notice that we can use the remote component interface methods, inherited from EJBObject
. The most important such method is remove()
. Therefore, we can conclude our example with the following C++ line:
ceai->remove();Page 3 of 3
Interoperability issues
To actually run the described example with VisiBroker for C++ 4.5 and BEA WebLogic 6.1, you unfortunately need a small workaround. Due to incompatibilities between VisiBroker and WebLogic, which both use GIOP/IIOP 1.2 by default, you must force them to use GIOP/IIOP 1.1. You can achieve this by adding the DefaultGIOPMinorVersion="1"
attribute to the <Server>
tag in the WebLogic config.xml
file.
Unfortunately, this example once again shows that the interoperability between CORBA products from different vendors in practice still needs improvement.
Ideal integration
In this article, I've shown that interoperability between CORBA and EJBs is possible. However, developing an EJB client in a programming language other than Java is not as easy as you might hope, particularly if you use methods that accept or return objects and user-defined types. This is the case for most real-world EJB components, so you must use CORBA value types. The major problem is that you must implement Java built-in types as well. I hope CORBA vendors provide value-type implementations soon, as IBM WebSphere Application Server does. I also hope interoperability issues between different COBRA products resolve soon.
At any rate, the interoperability between EJBs and CORBA is important and useful in most cases where you need to integrate existing non-Java-based applications with newly developed J2EE-based solutions.
Learn more about this topic
- Download the source code for this article
http://images.techhive.com/downloads/idge/imported/article/jvw/2002/03/jw-0329-corba.zip - Free trial version of VisiBroker for C++
http://www.inprise.com/visibroker/download/ - Free trial version of WebLogic 6.1
http://commerce.bea.com/downloads/weblogic_server.jsp - You can find more on integration with J2EE in Professional J2EE EAI, Matjaz B. Juric et al. (Wrox Press, December 2001; ISBN186100544X)
http://www.amazon.com/exec/obidos/ASIN/186100544X/javaworld - CORBA/IIOP specification
http://www.omg.org/technology/documents/formal/corba_iiop.htm - Java Language Mapping to OMG IDL
http://www.omg.org/technology/documents/formal/java_language_mapping_to_omg_idl.htm - Professional EJB, Wrox author team (including Matjaz B. Juric) (Wrox Press, July 2001; ISBN1861005083)
http://www.amazon.com/exec/obidos/ASIN/1861005083/javaworld - BEA WebLogic Server 6.1 documentation
http://edocs.bea.com/wls/docs61/index.html - IBM WebSphere Application Server 4.0 documentation
http://www-4.ibm.com/software/webservers/appserv/infocenter.html - OMG C++ Language Mapping
http://www.omg.org/technology/documents/formal/c++.htm - Browse the Enterprise JavaBeans section of JavaWorld's Topical Index
http://www.javaworld.com/channel_content/jw-ejbs-index.shtml - Browse the Java Naming and Directory Interface section of JavaWorld's Topical Index
http://www.javaworld.com/channel_content/jw-jndi-index.shtml - Subscribe to JavaWorld's free Enterprise Java email newsletter
http://www.javaworld.com/subscribe - Get under the hood of the technologies shaping the future in JavaWorld's Enterprise Java discussion
http://forums.idg.net/webx?50@@.ee6b80a - You'll find a wealth of IT-related articles from our sister publications at IDG.net