CORBA

I wanted to controll our new telephone switch which came with a CORBA interface and appropriate IDLs. So I played around with some CORBA implementations, namely omniORB, JacORB, IIOP.NET, and the one that comes with the Java platform. For some theoretical background I read some chapters from Advanced CORBA Programming with C++ and googled the web for some code snippets. I provide my own code examples and CORBA experience on this page.
Each CORBA application is based on a language independent interface description. This interface has to be written in its own language called interface description language (IDL). Each CORBA implementation comes with an idl compiler which translates those idl files to source code, so called stubs. You can translate each idl file to its client side or server side stub. A CORBA application without idl files is pretty much senseless.

omniORB

To generate client stubs, you have to invoke omniidl for each of your IDL files, eg.:

omniidl -I<INPUT DIR> -bcxx -C<OUTPUT DIR> <INPUT DIR>/LoginService.idl
This will create two files LoginServiceSK.cc and LoginService.hh in <OUTPUT DIR>. Compile each *SK.cc into an object file (eg. LoginService.o). If you have multiple object files, place them into an archive (eg. libmyclient.a).
A typical CORBA client looks like this:
#include <iostream>
#include <omniORB4/CORBA.h>
#include <LoginService.hh>

try {
	// initialize the ORB stack
	CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv, "omniORB4");

	// get the Root POA
	CORBA::Object_ptr obj = orb->resolve_initial_references("RootPOA");
	PortableServer::POA_ptr rootPOA = PortableServer::POA::_narrow(obj);

	// this is application specific: get the LoginService at the specified
	// host (192.168.0.23)
	obj = orb->string_to_object("corbaloc::192.168.0.23:2809/Login");
	LoginService::ILogin_ptr login = LoginService::ILogin::_narrow(obj);

	...

	// shutdown the ORB stack
	orb->destroy();

	return 0;
} catch (CORBA::Exception& e) {
	std::cout << "corba exception thrown: " << e._name() << "\n";
	return -1;
} catch (...) {
	std::cout << "exception thrown\n";
	return -1;
}
When you compile your program, you have to link with libomniORB4, libomniDynamic4, and libmyclient, eg.:
c++ -lomniORB4 -lomniDynamic4 -lmyclient -o program program.cc
CORBA is quite firewall friendly. It uses a random TCP high port on the client side, and TCP port 2809 as destination port, usually. But if you use callbacks, then the server opens a reverse connection to a random high port on the client. I don't know of any firewall that can handle this correctly. Luckily, CORBA supports a so called bidirectional policy which multiplexes any callbacks into the connection initiated by the client. You have to extend your program accordingly:
#include <iostream>
#include <omniORB4/CORBA.h>
#include <LoginService.hh>

try {
	// initialize the ORB stack
	CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv, "omniORB4");

	// get the Root POA
	CORBA::Object_ptr obj = orb->resolve_initial_references("RootPOA");
	PortableServer::POA_ptr rootPOA = PortableServer::POA::_narrow(obj);

	// create the bidirectional policy
	CORBA::Any any;
	any <<= BiDirPolicy::BOTH;
	CORBA::PolicyList policy;
	policy.length(1);
	policy[0] = orb->create_policy(BiDirPolicy::BIDIRECTIONAL_POLICY_TYPE, any);

	// activate the bidirectional policy
	PortableServer::POAManager_ptr poaMgr = rootPOA->the_POAManager();
	poaMgr->activate();
	PortableServer::POA_ptr bidirPOA = rootPOA->create_POA("bidirPOA", poaMgr, policy);

	// this is application specific: get the LoginService at the specified
	// host (192.168.0.23)
	obj = orb->string_to_object("corbaloc::192.168.0.23:2809/Login");
	LoginService::ILogin_ptr login = LoginService::ILogin::_narrow(obj);

	...

	// shutdown the ORB stack
	orb->destroy();

	return 0;
} catch (CORBA::Exception& e) {
	std::cout << "corba exception thrown: " << e._name() << "\n";
	return -1;
} catch (...) {
	std::cout << "exception thrown\n";
	return -1;
}

Plain Java

Recent versions of Sun's Oracle's Java come with a builtin CORBA stack. Unfortunately, it doesn't support bidirectional operations. But you can optionally provide an ip address which the callback functions connect to. Compiling the idl files is quite simple, eg.:

idlj -fclient -fallTIE -i <INPUT DIR> -td <OUTPUT DIR> <INPUT DIR>/LoginService.idl
Of course you have to reference all files below <OUTPUT DIR> in your project. A sample client looks like this:
import java.util.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import LoginService.*;

// optional hint for callback functions
String myIpAddress = ...;
Properties props = new Properties();
props.put("com.sun.CORBA.ORBServerHost", myIpAddress);

// initialize the ORB stack
ORB orb = ORB.init(args, props);

// get the Root POA
POA poa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));

// this is application specific: get the LoginService at the specified
// host (192.168.0.23)
org.omg.CORBA.Object obj = orb.string_to_object("corbaloc::192.168.0.23:2809/Login");
ILogin login = ILoginHelper.narrow(obj);

...

// shutdown the ORB stack
orb.destroy();

JacORB

As of JacORB 2.3.1, you have to adjust the shell script that calls the idl compiler. Change into the bin directory of JacORB, and copy the file idl.tpl to idl.sh. Edit this file to match your environment, eg.:

!/bin/sh

JACORB_HOME=~/Downloads/jacorb-2.3.1
java -classpath ${JACORB_HOME}/lib/idl.jar:${JACORB_HOME}/lib/logkit-1.2.jar:${CLASSPATH} org.jacorb.idl.parser  "$@"
Now you can compile your idl files:
idl.sh -d <OUTPUT DIR> -I <INPUT DIR> -td <OUTPUT DIR> <INPUT DIR>/LoginService.idl
During the initialization of the ORB stack you have to switch to JacORB:
import java.util.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import LoginService.*;

// use JacORB
Properties props = new Properties();
props.put("org.omg.CORBA.ORBClass", "org.jacorb.orb.ORB");
props.put("org.omg.CORBA.ORBSingletonClass", "org.jacorb.orb.ORBSingleton");

// initialize the ORB stack
ORB orb = ORB.init(args, props);

// get the Root POA
POA poa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));

// this is application specific: get the LoginService at the specified
// host (192.168.0.23)
org.omg.CORBA.Object obj = orb.string_to_object("corbaloc::192.168.0.23:2809/Login");
ILogin login = ILoginHelper.narrow(obj);

...

// shutdown the ORB stack
orb.destroy();
Of course you have to import the jacorb.jar into your project. JacORB support bidirectional operations. A sample program looks like its C++ counterpart:
import java.util.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import org.omg.BiDirPolicy.*;
import LoginService.*;

// use JacORB
Properties props = new Properties();
props.put("org.omg.CORBA.ORBClass", "org.jacorb.orb.ORB");
props.put("org.omg.CORBA.ORBSingletonClass", "org.jacorb.orb.ORBSingleton");

// initialize the ORB stack
ORB orb = ORB.init(args, props);

// get the Root POA
POA poa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));

// create the bidirectional policy
Any any = orb.create_any();
BidirectionalPolicyValueHelper.insert(any, BOTH.value);
Policy[] policies = {orb.create_policy(BIDIRECTIONAL_POLICY_TYPE.value, any)};

// activate the bidirectional policy
POAManager mgr = poa.the_POAManager();
mgr.activate();
POA bidirPoa = poa.create_POA("bidirPOA", mgr, policies);

// this is application specific: get the LoginService at the specified
// host (192.168.0.23)
org.omg.CORBA.Object obj = orb.string_to_object("corbaloc::192.168.0.23:2809/Login");
ILogin login = ILoginHelper.narrow(obj);

...

// shutdown the ORB stack
orb.destroy();
Additionally, you have to create a jacorb.properties:
jacorb.log.default.verbosity=1
org.omg.PortableInterceptor.ORBInitializerClass.bidir_init=org.jacorb.orb.giop.BiDirConnectionInitializer

IIOP.NET

As C# / .NET comes without support for CORBA, I tried IIOP.NET. Unfortunately, version 1.9.0 SP1 has a minor problem with strings. My patch fixes that. If you have installed a recent version of Visual Studio, then you won't be able to load the IIOP.NET project. Instead, you have to open a Visual Studio command prompt, change to the IIOP.NET folder (eg. IIOPNet.src.1.9.0.sp1), apply my patch, and call nmake build-base. You will then find the idl compiler idltoclscompiler.exe in IDLToCLSCompiler\IDLCompiler\bin. It compiles all idl files to one dll:

IDLToCLSCompiler\IDLCompiler\bin\idltoclscompiler.exe" -idir <INPUT DIR> MyClient <INPUT DIR>\*.idl
To compile your corba client, you have to reference MyClient.dll and IIOPNet.src.1.9.0.sp1\IDLToCLSCompiler\IDLCompiler\bin\IIOPChannel.dll in your Visual Studio project. The source code differs from Java and C++:
using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Threading;
using omg.org.CORBA;         // provided by IIOPChannel.dll
using Ch.Elca.Iiop;          // provided by IIOPChannel.dll
using Ch.Elca.Iiop.Services; // provided by IIOPChannel.dll
using LoginService;          // provided by MyClient.dll

namespace CorbaTest.Net
{
    public class Program
    {        
        static void Main(string[] args)
        {
            // register corba services
            IiopChannel channel = new IiopChannel();
            ChannelServices.RegisterChannel(channel, false);

            // login returns location of the userservices
            ILogin login = (ILogin)RemotingServices.Connect(typeof(ILogin),
                "corbaloc::192.168.0.23:2809/Login");

            ...
        }
    }
}
It is even possible to use a bidirectional policy:
using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Threading;
using omg.org.CORBA;         // provided by IIOPChannel.dll
using Ch.Elca.Iiop;          // provided by IIOPChannel.dll
using Ch.Elca.Iiop.Services; // provided by IIOPChannel.dll
using LoginService;          // provided by MyClient.dll

namespace CorbaTest.Net
{
    public class Program
    {        
        static void Main(string[] args)
        {
            // create a bidir policy
            Hashtable props = new Hashtable();
            props[IiopChannel.BIDIR_KEY] = true;
            props[IiopServerChannel.PORT_KEY] = 0;

            // register corba services
            IiopChannel channel = new IiopChannel(props);
            ChannelServices.RegisterChannel(channel, false);

            // login returns location of the userservices
            ILogin login = (ILogin)RemotingServices.Connect(typeof(ILogin),
                "corbaloc::192.168.0.23:2809/Login");

            ...
        }
    }
}

Other ORBs

I have not tried these so far...