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.
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.idlThis 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).
#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.ccCORBA 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; }
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.idlOf 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();
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.idlDuring 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
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>\*.idlTo 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"); ... } } }
I have not tried these so far...