Using Multiple Vendor's ORBs with Name Services

by Eric Y. Theriault
July 22, 2004

Introduction

When you look at available CORBA ORB's, the majority indicate that they should be able to work with other vendor's ORB's and name services, because they support the IIOP. In some cases, there are little guarantees that this will work (such as the case with Java).

This article will first make a simple application work with multiple ORB's via IOR files, and then add support for a name service to the application. The ORB's used in this article are Mico, TAO, Java IDL (the default implementation from Sun's JDK 1.4.2), and IIOP.NET for C#. I assume some exposure to CORBA.

The full source code presented herein is available for Linux and Windows: nameservice.zip.

The Application

As the goal of this article is to explore the interoperability of name services, the actual application is not that important. Because of this, I have selected a very simple example inspired by Advanced CORBA Programming with C++1.

The interface is basically to acquire the server's local time and is marginally more complex than the Hello World application. The IDL for this is as follows2:

IOR-File Based Implementation

At the heart of CORBA objects is the Interoperable Object Reference (IOR), which provides all the necessary address information for a client to communicate with a server. Because of its importance, some ORB's provide utilities like Mico's iordump, which displays the information encoded in the IOR.

Because the IOR is at the heart, most compatibility issues will also occur with simple testing at this level. Before addressing the name services, I will first demonstrate a basic client and server implementation of the above IDL, which will first ensure that there is some level of compatibility3.

The server application will initialize the ORB, create the implementation to the TimeTools interface, and add it to the ORB. Once the TimeTools implementation is created, it will create a time.ior file in the current directory. Following this, the application displays that it is ready, and the ORB's implementation will take over.

The client application will use the time.ior file created by the server to communicate with the server. The client will start by initializing the ORB. It will then read in the time.ior file and resolve the reference into an object. Once the object is bound, it will use the getTime() method of the interface, and display the server's current local time.

It is important to note that the following implementations are all the same, even if they are compiled with different languages and ORB's. This highlights the advantages of standardized components.

Java Implementation

The Java SDK provides an interface to CORBA since JDK version 1.2. The essential package is org.omb.CORBA, which provides all the required inferences for a CORBA application to utilize it.

Underneath the hood, however, you have a variety of choices for CORBA implementations. The default implementation is provided by Sun, but other ORB's can seamlessly plug-in, and in some cases without any necessary modification to your implementation. There are three ways to plug-in the new implementation (This assumes the additional jars are in your classpath).

  1. When starting the application, you can specify the -Dorg.omg.CORBA.ORBClass=implementation -Dorg.omg.CORBA.ORBSingletonClass=singletonImplementation The implementation and singletonImplementation parameters will be specified by your ORB vendor, such as JacORB's values are org.jacorb.orb.ORB and org.jacorb.orb.ORBSingleton.

  2. If modifying your application is an option, the second parameter to the org.omg.CORBA.ORB.init() method can be populated with the above properties, viz. org.omg.CORBA.ORBClass and org.omg.CORBA.ORBSingletonClass.

  3. Create or copy a orb.properties file into your JDK's lib directory which specifies the above properties. I would personally avoid this as every application on the machine is effected, and when upgrading the JDK, this type of modification can be lost and forgotten.

Provided with the JDK is an application called idlj, which will compile the IDL into Java source. I usually use the idlj -fall -td sourceCodeDirectory -pkgTranslate module packageName idl-Files command line. The -fall parameter will generate both the client and server components. The -td parameter specifies the location where the new packages will be created. By default, idlj will do a direct module to package mapping, however, I generally like to create a sub-package; the -pkgTranslate does the conversion from the module name to the package name.

If you are using another Vendor's implementation, some also provide IDL compilers themselves. For example, JacORB provides an idl compiler that is comparable to the above, and is documented in the JacORB Utilities section of its documentation. In my tests, I was using the JacORB implementation with Sun's IDL compiler and it was working, so this may not be absolutely required.

Once the IDL is compiled into Java source, you can then implement your server and client. The server itself is rather simple. The server is implemented in TimeServer.java, which uses TimeImpl.java for its interface implementation.

The client application is even more straight forward, and is implemented in TimeClient.java.

The ant build file is available here, and the project is compiled by simply typing in ant in the root directory.

Once all the source is built, the server is easily executed by java -classpath classes ca.eyt.corba.TimeServer and the client is executed by java -classpath classes ca.eyt.corba.TimeClient, and it should display the time.

C++ ORB's Overview

Similar to Java, the implementation of CORBA services is oblivious to the underlying ORB. One thing that is not so seamless, however, is the fact that different IDL compilers will name the header files differently, so if your project intends to have multiple vendors, this may require some #ifdef'ing or an abstraction header file.

Some vendors provide some additional information in a CORBA::Exception that can be quite useful when debugging. This is vendor-specific, and therefore, you will need an abstraction layer for this also.

For this project, I have chosen #ifdef since this is meant to highlight the differences in ORB's.

Because of the code is the same, the server and client implementation will be discussed here, and the vendor specific information will follow.

The server implementation is simple. In Java I used an external class for the actual implementation, but for the C++ implementation, I implemented the class in the unnamed namespace. It is available as TimeServer.cpp.

Again, the client implementation is even simpler, and is available as TimeClient.cpp.

Mico

Mico is an Open Source ORB for C++, which can be downloaded from www.mico.org.

On UNIX-based systems, you essentially extract the sources, change directories into the extracted directory, ./configure, gmake, and gmake install. I tend to avoid the install part though.

On Windows, I compiled it with Visual Studio. You basically extract the sources, change directories, and nmake -f Makefile.win32.

Once compiled, you can use its IDL compiler to compile our IDL into C++4. Its IDL compiler is called idl, and is located in the idl directory of the Mico source tree. I have selected the --poa (Portable Object Adapter) option; POA was introduced in CORBA 2.2 to extend the services of the Basic Object Adapter (BOA), which had insufficient services, to which vendors extended the services in incompatible fashions. I have also selected the --c++-suffix cpp parameter to name the C++ implementation files with a .cpp.

Once the IDL compiler runs, it generates the necessary files, and from this, you are ready to build the tools. The Makefile I used for Linux is available here. The Visual Studio Project for the Client is TimeClientMico.vcproj and the Server is TimeServerMico.vcproj.

This will create a TimeServerMico and a TimeClientMico. Similar to the Java above, the server is started first, and then the client is executed afterwards, and the client should display the time.

TAO

TAO is part of the ACE toolkit. It can be downloaded from http://deuce.doc.wustl.edu/Download.html.

Building ACE is described http://www.cs.wustl.edu/~schmidt/ACE_wrappers/ACE-INSTALL.html. It is presented a little more complicated than it actually is. It is important to remember that after $ACE_ROOT/ACE is built, you must also build $ACE_ROOT/TAO.

Once that the TAO is built, you can use its IDL compiler, which is called tao_idl in $ACE_ROOT/TAO/TAO_IDL on most operating systems and in %ACE_ROOT%\bin\ on Windows. For this IDL compiler, I used no options. It generated a few more files than the Mico implementation, however. Once this is done, you can then build it. The Makefile I used for Linux is available here. The Visual Studio Project for the Client is TimeClientTAO.vcproj and the Server is TimeServerTAO.vcproj.

This will create a TimeServerTAO and a TimeClientTAO. Exactly like Java and Mico above, the server is started first, and then the client afterwards, and the client should display the time.

IIOP.NET

While the .NET platform is optimized for SOAP, it is sometimes necessary to connect to CORBA services. There are a few CORBA vendors available for the .NET framework, including Borland's Jeneva, MiddSol's MiddCor.NET, and Remoting.Corba, but this article focuses on IIOP.NET.

IIOP.NET is an Open Source ORB for .NET, which can be downloaded from http://iiop-net.sourceforge.net/. Once that you download the source, you can simply use nmake in the root directory, which I will refer to as %IIOPNET_HOME%.

Once compiled, you can use its IDL compiler to compile our IDL file directly into a DLL. The IDL compiler is called IdlToCSCompiler; it offers a number of options, however, I have used only the required options. The first option is the name of the assembly name (or the DLL name) to create; this name must not have the extension and there are a few other minor requirements. The second option is the actual IDL file. In my case, this is IdlToCSCompiler TimeService Time.idl (this assumes that %IIOPNET_HOME%\IdlToCLSCompiler\IDLCompiler\bin is in your path).

You will need to add a reference to library created above to your project, to the IIOP.NET supplied library, IIOPChannel.dll, which can be found in %IIOPNET_HOME%\IIOPChannel\BIN, and the System.Runtime.Remoting.dll, which is part of the .NET Framework.

Whereas the Java, Mico, and TAO all looked similar, the C# source for the client and server is considerably different, and arguably much more simple. I would think that other .NET vendors' implementation may have a different implementation that would be closer to the standard, as I would imagine that this implementation is not portable. This made porting the application a little harder than it needed to be.

Still, the implementation is similar to Java's, and is rather simple. The server is implemented in TimeServer.cs, which uses TimeImpl.cs for its interface implementation.

Just as with the others, the client application is even more straight forward, and is implemented in TimeClient.cs.

Once that the TimeServices.dll is created from the IDL compiler, everything is ready to build. My Visual Studio project for the Client is TimeClient.csproj and TimeClient.csproj.user, and for the Server uses TimeServer.csproj and TimeServer.csproj.user.

Testing Interoperability

Now that all the IOR-based applications are created, we can begin testing interoperability. This is done by starting an implementation of the Server, then starting each of the Client implementations to ensure that it can acquire the time from the server. The Server is then terminated and another implementation's server is started and tested in the same fashion.

The key for this is to properly distribute the time.ior file. In my case, I simply created a hard-link in each of the executable's directories.

In my case, everything worked right out of the box.

Attaching the Naming Services

Naming services is a CORBA standard that is part of the Common Object Services Specification (COSS). This service creates a mapping (known as a binding) between a name (referred to as a naming context) and an instance of an IDL Object. This mapping is roughly analogous to a directory tree or a directed graph (referred to as a naming graph).

The key to the above is that it produces a mapping between a name and an object. This means that instead of our client application requiring an IOR file from the server, it can now simply connect to a Naming Server and acquire the IOR string to connect to the server. This mapping facilitates the distribution of IOR's around the network.

It is important to note that the naming service is a CORBA application, and the entire interface is defined using IDL. As such, in order to use the naming service, you simply need to tell your ORB where the name server is located, which can be specified by command-line parameters passed into the CORBA::init() function, or via an IDL type string or IOR directly.

Overview

Just like the IOR section, I will modify each language independently, and then discuss how to make them interoperate.

The first thing to do is to modify the server to bind the IDL type string with the name server. To be backwards compatible, our server will continue to create the IOR file. This allows you to use the tools created above with the new server, as well as to see the difference between the original server above and the new server easily (such as by using xxdiff).

On the client side, however, we will completely remove the IOR file support, and only use the name service support. At the start of the application, we will attempt to resolve the name server, and from this, we will attempt to lookup the Time mapping, resolve the Time object, and display the time.

The Java Version

For both the client and server, there is really little to do to add support for the name services. The client is available as TimeClient2.java and the server is available as TimeServer2.java.

The Java name service is both the tname serv daemon and in the orbd application. Both are suitable, but the latter offers slightly more services. For them to work, you must pass in the TCP port number to allow clients to connect via the -ORBInitialPort parameter.

Once this is done, when you start the server or client, you must tell it where this name server is running, via the -ORBInitialHost and -ORBInitialPort parameters, or the org.omg.CORBA.ORBInitialHost and org.omg.CORBA.ORBInitialPort properties, respectively.

For example, to run our server, you will now type java -classpath classes ca.eyt.corba.TimeServer2 -ORBInitialPort 1050 -ORBInitialHost localhost, and then start the client in a similar fashion. If the orbd is not running on the indicated port, Java will raise a org.omg.CORBA.COMM_FAILURE exception on startup.

As you will see in the C++ section, you can also use the -ORBInitRef NameService=location command line parameter. Here the location can either be the IOR string or the corbaloc object URL that can be acquired by an iordump utility. This will become more important when dealing with a non-JDK name service.

C++ ORB's

Although the name service is a standard extension, ORB's have different header files for it. This will require a #ifdef or some abstraction layer to choose the correct file. In our case, Mico uses coss/CosNaming.h whereas TAO uses orbsvcs/Naming/Naming_Utils.h.

Once the proper include file is included, however, the remaining changes are the same and are very similar to the Java version.

The client is TimeClient2.cpp and the server is TimeServer2.cpp. This is fairly straight forward.

Once that these are compiled, the next part is joining them with the ORB's.

Mico

To run our server, you must first startup Mico's name service daemon, which is known as nsd. The nsd daemon will choose a random port number, so in this case, you will want to use the --ior filename parameter, which will create the named IOR file that can be used directly or in conjunction with Mico's iordump utility, you can acquire the corbaloc string.

To specify the name server, you must use the -ORBInitRef NameService=location command line option, where the location can be either the IOR string from the file or, which is generally more useful, the corbaloc object URL, which you could acquire via Mico's iordump utility. The string is generally in the form of corbaloc::hostname:port/NameService, so it could also be generated.

To run our server, you simply use the TimeServer2Mico followed by the above command-line option for your configuration. The client is also run in a similar fashion.

TAO

The TAO Name Service is called Naming_Service, and also works by the IOR file described in the Mico section. The -o file option provides the IOR file, which can be used exactly like the Mico section indicates.

As an added benefit, the TAO Name Service also comes with the discover name services by multicasting for the server. To enable this, you must provide the Naming_Service with the -m 1 command line option. When enabled, you no longer need to supply the -ORBInitRef parameter for applications built with TAO. For all other applications, however, you will still need the initial reference.

If the multicasting is enabled, the TAO applications work out of the box. Without the multicast option, however, you will need to specify the -ORBInitRef NameService=location parameter, where the location is either the corbaloc object URL or the IOR string, as the Mico section describes.

IIOP.NET

IIOP.NET does not come with a nameservice, and is intended to reuse other nameservices, however, it does not use the same default parameters as the other ORBs. In light of this, I have created a the ORBHelper class to provide this functionality, and the command-line arguments will now match Java's.

To try the server, simply run a name service, start the server by TimeServer2 -ORBInitialHost localhost -ORBInitialPort 1050, and use the client in a similar fashion.

If you use the -ORBInitRef command line option, there are some problems with certain initial references; specifically, the string created by Mico's iordump for the TAO nameservice IOR does not work. It prefers a simple corbaloc string.

Aside from this minor change, the remaining code in almost untouched. The client is available as TimeClient2.cs, the server is available as TimeServer2.cs, and the ORBHelper utility class is available as ORBHelper.cs.

Interoperability

When it comes to interoperability with the information presented above, the Java name service interoperates correctly with all clients and servers combinations, however, when using either Mico's or TAO's name service will provide interoperability with only Mico's and TAO's clients and servers.

As briefly mentioned at the end of the Java section, to allow Java to use a non-JDK name service, you must provide the same -ORBInitRef command line parameter as used with Mico and TAO. By using the IOR command line, such as java -classpath . ca.eyt.corba.TimeClient2 -ORBInitRef NameService=IOR:..., each server will work, but you will quickly note that the corbaloc: object URL will work with the TAO name service, but not with the Mico name service.

The Mico name service uses the 1.0 standard, whereas the TAO, JDK, and IIOP.NET's standard is 1.2. Whereas TAO automatically assumes the 1.0 standard, it seems that the JDK does not even attempt it. As such, the corbaloc:... string must include the version, such as corbaloc::1.0@host:port/NameService (Note the 1.0@).

Once this is completed, every combination works out of the box!

Multicasting for the Name Server

In order for a name service to be used, the location of the name server must be configured. Instead of specifying this via the command-line, an external configuration could be used. One scheme that is mentioned in the JacORB documentation to manage IOR's is that you could post the IOR file on a web server, and have your clients acquire it from that server. The IOR on the web server can either be the name service itself or your server could create the IOR file for the individual services. This can be less configuration for some environments.

The TAO idea for multicasting for a name service can be easily implemented, and arguably requires less configuration. The idea is simply to configure the multicast server to properly resolve the name service. Once that it is resolved, the server waits for client multicast requests. Upon receiving this multicast, the server can unicast the IOR over to the client. The client will require a slight modification to multicast a message to discover the name service and to acquire this IOR from the server. The sequence diagram for this interaction is as follows:

Multicasting in Java can simply be done via the java.net.MulticastSocket class. More information surrounding Multicasts can be found in STEVENS+. For the sake of an example, TimeServer3.java implements the server component by implementing a lightweight multicast server thread. TimeClient3.java implements the client by implementing a multicast client, that eventually stops looking for a server.

The C++ code to do the multicast is complicated a bit by the fact that you must create a class to abstract out the multicast functionality (or do all the raw-socket commands), and then you need to use a thread library. For the sake of this article, I have implemented a quick class based on what I used of the Java MulticastSocket class and I will use the pthreads library on Linux and the native Windows threads under Windows.

Using this MulticastSocket.cpp and MulticastSocket.h, I am able to recreate TimeClient3.cpp easily. The Server implementation requires some extra indirections for PThreads and Windows' Threads, but in the end, TimeServer3.cpp is very much the same as the Java version.

Multicasting in C# is more like C++ than Java, as it does not provide a working multicast class, which results in more complexities. Again, I have implemented only the required functionality, and using MulticastSocket.cs's interface, TimeServer3.cs and TimeClient3.cs are easy to create.

Once these classes are all built, the server is started via the same command-line as before to specify the name server's location. The client, however, is command-line free and automagically detects the name service, and works out of the box.

Summary

Of the ORB's surveyed, the interoperability at the IOR level was fairly simple. Adding the name service changed the host application slightly, but changed the mechanisms of program invocation and in some cases, this changed when changing the name service.

For a simple application with one or two interfaces, the complexity of name services is probably not worth it, because you still need to distribute either an IOR or a corbaloc: string to all your client and servers, similar to the way that you are currently managing this with a single interface.

For an application for five or more interfaces, the name service route is definitely the way to go. Managing the distribution of the files would definitely outweigt the arguably lesser configuration management with the name service. Besides, once you figure out the above, techniques, everything works out of the box!

Adding the ability to multicast the name service configuration is quite easy, and can avoid some difficulties if most of the machines are on the same network.

The full source code presented above is available for Linux and Windows: nameservice.zip.


[1] Henning, Michi and Steve Vinoski. "Advanced CORBA Programming with C++." Addison-Wesley-Longman, 1999.

[2] Also available here.

[3] Mico's distribution provides similar examples in the demo/interop directory.

[4] The IDL originally had the module name Time, but the Mico IDL compiler did not like a module and interface name having the same name, and this caused me to change the module name to TimeTools.

[5] Reilly, David and Michael Reilly. "Java Network Programming and Distributed Computing." Addison-Wesley-Longman, 2002.

[6] Geihs, Kurt, Arno Puder, and Kay Romer. "Mico: An Open Source CORBA Implementation with CDROM." Morgan Kaufmaa, 2000.

[7] Stevens, W. Richard., Bill Fenner, and Andrew M. Rudoff. "Unix Network Programming, Volume 1: The Sockets Networking API, Third Edition." Addison-Wesley, 2004.

[8] Brewer, Gary. "IP Multicasting in C#"

eyt*