The Common Object Request Broker Architecture (CORBA) as defined by the OMG spec, allows clients to invoke operations on distributed objects (as defined by their IDL) without concern for their location, the programming language of the remote service, its OS, hardware platform (32 bit or 64 bit word size) or communications protocols (TCP/IP, IPX, FDDI, ATM,...).
CORBA is a support framework of applications, libraries and services for making distributed procedure calls. A program on computer "C" (CORBA client) calls a function which is computed and processed on computer node "S" (CORBA server) and passes to it function arguments. The function/method passes arguments and returns values as with any other C/C++ call except that it may be distributed across the network so that portions of the program may be executed on a remote machine.
CORBA's strength is that it allows platform and programming language interoperability. The interface between the client and server is defined by the CORBA IDL language which can be processed to produce code to support a variety of languages and platforms. The CORBA communication protocol, the language mappings and object model are standardized to allow this general inter-operability.
The remote function/method may start programs (fork/exec) on the remote computer or run remote services uniquely available to the remote computer and then return data.
In this example we will run a CORBA name service which acts as the glue between the client and server.
Sequence:
- Application calls Function_1a()
- CORBA name service locates Function_1a() on "server 1"
- Application requests a call to Function_1a() on "server 1"
- Execution of Function_1a() on "server 1" returns a function return value and returns an argument list.
Various CORBA implementations are available for Linux:
- Open Source:
- OmniORB: C++, AT&T of England
Small footprint and modern implementation. This ORB is covered in this tutorial.
Debian:- C++: omniorb4
- Python: python-omniorb2
- TAO: [TheAceORb.com]: C++ based on the ACE framework
Debian: ace - JACOrb: Java Implementation
- MICO: C++
- ORBit: Gnome desktop implementation
Debian:- C: orbit2
- C++: orbit2spp
- Fnorb: Python
Debian: fnorb
- OmniORB: C++, AT&T of England
- Commercial:
- Borland Visibroker:
- IONA Orbix:
Links:
OmniORB is a fast and standards compliant CORBA ORB. It provides the IDL compiler which creates C++ source routines (.hh and .cc files) which one calls and links with. It also provides the CORBA libraries and include files.
OmniORB requires Packages python and python-devel 2.2 or better. The IDL compiler is written in Python.
Install from source:
Download from: sourceforge
Compile and install OmniORB:
- tar xzf omniORB-4.X.X.tar.gz
- cd omniORB-4.X.X
- Configure:
- Linux: ./configure --prefix=/opt --enable-threads --enable-shared
- Solaris: ./configure --prefix=/opt --enable-threads --enable-shared PYTHON=/usr/local/bin/python CC=/usr/local/bin/gcc CXX=/usr/local/bin/g++
- SGI/IRIX: ./configure --prefix=/opt --enable-threads --enable-shared PYTHON=/usr/freeware/bin/python CC=/bin/cc CXX=/bin/CC
Note: Edit file src/lib/omniORB/orbcore/proxyFactory.CC
Change from: To: if(ofl)return;
...
if(!ofl)
{
...
}
- make
- make install
- --disable-static: speeds up build, but so static libs are produced
- --enable-threads: Use pthreads
- --enable-shared: default - shared libraries generated
Excellent documentation can be found in the directory: omniORB-4.X.X/doc/...
Packages:
Red Hat RPM: [download from EPEL]rpm -ivh omniORB-4.X.X
Debian package: omniORB4 (C++)
Edit config file: /etc/omniORB.cfg
On Linux and Cygwin platforms comment out the following two directives:
- supportPerThreadTimeOut
- poaUniquePersistentSystemIds
The following steps will create an operational CORBA transaction:
- Server Set-up:
- Start name service. (This will also define the IOR.) Edit /etc/omniORB.cfg and update with IOR generated by the omniNames CORBA name server.
- Set environment variable:
[OmniTest]$ export OMNIORB_CONFIG=/etc/omniORB.cfg
- Start CORBA server.
- Remote CORBA service "A" now makes available functions/methods "A" and "B" to CORBA
clients.
- CORBA usage:
- Set environment variable:
[OmniTest]$ export OMNIORB_CONFIG=/etc/omniORB.cfg
- Run CORBA client.
- Set environment variable:
OmniORB config file: omniORB.cfg (based on sample.cfg)
Generate an IOR:
- omniNames --help
- Generated in log file as defined during start-up:
omniNames -start -logdir /opt/omni/omniNamesLogdir -errlog /opt/omni/omniNamesError.txt
export LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATH
Use/edit URI:
- InitRef = NameService=corbaname::my.host.name
or - InitRef = NameService=IOR:01000000280000004944...
or - InitRef = TradingService=corbaloc:iiop:marrow:5009/MyObjectKey
(corbaloc)
Notes:
- The default port is 2809 which complies with OMG's COS naming service protocol.
- One may specify the log file directory:
- Command line argument: -logdir /opt/omni/omniNamesLogdir
or - Environment variable: export OMNINAMES_LOGDIR=/opt/omni/omniNamesLogdir
- Command line argument: -logdir /opt/omni/omniNamesLogdir
The IDL (Interface Definition Language) is a programming language neutral function/method definition and data definition language used to define the interface between the client and server. In this example we will be using C++ for both the client and server.
File: Data.idl
ifndef __DATADIST_IDL__ #define __DATADIST_IDL__ module Data { interface ServiceA{ boolean CallServiceRoutineA ( in long num1, inout long num2, out long retNum ); boolean CallServiceRoutineB ( inout long num1, inout long num2); }; }; #endif
This results in the generation of an include file Data.hh and a C++ source file DataSK.cc. The file DataSK.cc is to be compiled and linked with your program and provides the CORBA communications infrastructure classes and functions which are called by the application. The include file is to be included by both the CORBA client and CORBA server.
CORBA IDL Data types:
IDL | C++ | Description: Word size and range |
---|---|---|
short | CORBA::Short | 16 bit: -2^15 ... +2^15 - 1 |
long | CORBA::Long | 32 bit: -2^31 ... +2^31 - 1 |
long long | CORBA::LongLong | 64 bit: -2^63 ... +2^63 - 1 |
unsigned short | CORBA::UShort | 16 bit: 0 ... 2^16 - 1 |
unsigned long | CORBA::ULong | 32 bit: 0 ... 2^32 - 1 |
unsigned long long | CORBA::ULongLong | 64 bit: 0 ... 2^64 |
float | CORBA::Float | 32 bit IEEE single precision floating point number |
double | CORBA::Double | 64 bit IEEE double precision floating point number |
long double | CORBA::LongDouble | |
char | CORBA::Char | 8 bit |
wchar | CORBA::WChar (Wide Char) |
|
string | CORBA::char * | |
wstring | CORBA::WChar * | |
boolean | CORBA::Boolean | true/false |
octet | CORBA::Octet (unsigned char) | 8 bit raw. No conversion. |
any | CORBA::Any | Arbitrary |
This will remain the same for most CORBA servers. The only difference will be the name of the service (OmniNameService) and the implementation of the server functions/methods (CServiceA_i()).
File: Server.cpp
#include <stdlib.h> #include <iostream> #include <string> #include <assert.h> #include <signal.h> #include <unistd.h> #include "CServiceA.h" #include "Data.hh" using namespace std; int main(int argc, char** argv) { // -------------------------------------------------------------------------- // Start CORBA server: // -------------------------------------------------------------------------- try { //------------------------------------------------------------------------ // 1) Initialize ORB // 2) Get reference to root POA // 3) Bind to name service // 4) Initialize servant object //------------------------------------------------------------------------ //------------------------------------------------------------------------ // Initialize CORBA ORB - "orb" //------------------------------------------------------------------------ CORBA::ORB_var orb = CORBA::ORB_init(argc, argv); //------------------------------------------------------------------------ // Servant must register with POA in order to be made available for client // Get reference to the RootPOA. //------------------------------------------------------------------------ CORBA::Object_var obj = orb->resolve_initial_references("RootPOA"); PortableServer::POA_var _poa = PortableServer::POA::_narrow(obj.in()); //------------------------------------------------------------------------ // Operations defined in object interface invoked via an object reference. // Instance of CRequestSocketStream_i servant is initialized. //------------------------------------------------------------------------ CServiceA_i* myRequestServiceA = new CServiceA_i(); //------------------------------------------------------------------------ // ObjectId_var class defined in poa.h // typedef String_var ObjectId_var; CORBA_ORB.h // ??????? //------------------------------------------------------------------------ // Servant object activated in RootPOA. // (Object id used for various POA operations.) //------------------------------------------------------------------------ PortableServer::ObjectId_var myRequestServiceA_oid = _poa->activate_object(myRequestServiceA); //------------------------------------------------------------------------ // Obtain object reference from servant and register in naming service(??) //------------------------------------------------------------------------ CORBA::Object_var SA_obj = myRequestServiceA->_this(); //------------------------------------------------------------------------ // Obtain a reference to the object, and print it out as string IOR. //------------------------------------------------------------------------ CORBA::String_var sior(orb->object_to_string(SA_obj.in())); cerr << "'" << (char*)sior << "'" << endl; //======================================================================== // Bind (rebind) object (orb) to name (SA_obj) //======================================================================== //------------------------------------------------------------------------ // Bind object to name service as defined by directive InitRef // and identifier "OmniNameService" in config file omniORB.cfg. //------------------------------------------------------------------------ CORBA::Object_var obj1=orb->resolve_initial_references("OmniNameService"); assert(!CORBA::is_nil(obj1.in())); //------------------------------------------------------------------------ // narrow this to the naming context //------------------------------------------------------------------------ CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow(obj1.in()); assert(!CORBA::is_nil(nc.in())); //------------------------------------------------------------------------ // Bind to CORBA name service. Same name to be requested by client. //------------------------------------------------------------------------ CosNaming::Name name; name.length(1); name[0].id=CORBA::string_dup("DataServiceName1"); nc->rebind (name,SA_obj.in()); //======================================================================== myRequestServiceA->_remove_ref(); //------------------------------------------------------------------------ // Activate the POA manager //------------------------------------------------------------------------ PortableServer::POAManager_var pmgr = _poa->the_POAManager(); pmgr->activate(); //------------------------------------------------------------------------ // Accept requests from clients //------------------------------------------------------------------------ orb->run(); //------------------------------------------------------------------------ // If orb leaves event handling loop. // - currently configured never to time out (??) //------------------------------------------------------------------------ orb->destroy(); free(name[0].id); // str_dup does a malloc internally } catch(CORBA::SystemException&) { cerr << "Caught CORBA::SystemException." << endl; } catch(CORBA::Exception&) { cerr << "Caught CORBA::Exception." << endl; } catch(omniORB::fatalException& fe) { cerr << "Caught omniORB::fatalException:" << endl; cerr << " file: " << fe.file() << endl; cerr << " line: " << fe.line() << endl; cerr << " mesg: " << fe.errmsg() << endl; } catch(...) { cerr << "Caught unknown exception." << endl; } return 0; }
Bold text points out your customizations. The rest is cut-and-paste.
The POA (Portable Object Adapter) assists the CORBA ORB (Object Request Broker) in passing CORBA client requests to the server object implementations (servants). The POA will interpret the request, marshal the exchange of parameters then locate and activate the servant and handle error recovery and security. This allows for portable, vendor independent and extensible servant software which comply with the OMG (Object Management Group) CORBA POA specification. The POA also allows for persistence of objects so that it can support a service lifespan.Also see: POA description.
The "servant" implementation: (denoted by "_i")
This is the routine which performs the duties of the remote service on the server.
File: CServiceA.h
#include "Data.hh" class CServiceA_i : public POA_Data::ServiceA, public PortableServer::RefCountServantBase { public: CServiceA_i(); virtual ~CServiceA_i(); virtual CORBA::Boolean CallServiceRoutineA( CORBA::Long num1, CORBA::Long& num2, CORBA::Long& retNum); virtual CORBA::Boolean CallServiceRoutineB( CORBA::Long& num1, CORBA::Long& num2); };
File: CServiceA.cpp
#include <vector> #include <string> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <iostream> #include <fstream> #include <stdio.h> #include "CServiceA.h" using namespace Data; #include <sys/wait.h> CServiceA_i::CServiceA_i() { } CServiceA_i::~CServiceA_i(void) { } CORBA::Boolean CServiceA_i::CallServiceRoutineA( CORBA::Long num1, CORBA::Long& num2, CORBA::Long& retNum) { num2 = num2 + num1; retNum = 10; return true; } CORBA::Boolean CServiceA_i::CallServiceRoutineB( CORBA::Long& num1, CORBA::Long& num2) { num1++; num2++; return true; }
File: MakeServer
CC = /usr/bin/g++ CPPFLAGS = -g -c LDFLAGS = -g OMNI_HOME = /opt OMNI_INCLUDES = -I$(OMNI_HOME)/include OMNI_LIB_DIR = $(OMNI_HOME)/lib OMNIIDL = $(OMNI_HOME)/bin/omniidl INCLUDES = $(OMNI_INCLUDES) LIBS = -lomniORB4 -lomnithread -lomniDynamic4 OBJECTS = Data.o CServiceA.o Server.o all Server: $(OBJECTS) $(CC) $(LDFLAGS) -o Server -L$(OMNI_HOME)/lib $(OBJECTS) $(LIBPATH) $(LIBS) Data.o: DataSK.cc Data.hh $(CC) $(CPPFLAGS) $(INCLUDES) DataSK.cc Server.o: Server.cpp Data.hh $(CC) $(CPPFLAGS) $(INCLUDES) Server.cpp CServiceA.o: CServiceA.cpp CServiceA.h Data.hh $(CC) $(CPPFLAGS) $(INCLUDES) CServiceA.cpp DataSK.cc: Data.idl $(OMNI_HOME)/bin/omniidl -bcxx Data.idl clean clean_all: rm -fr *.o rm -fr core rm -fr *.hh rm -fr *SK.cc rm -fr Server
File: CRequestServiceA.h
#include <iostream> #include <fstream> #include "Data.hh" using namespace std; class CRequestServiceA { public: CRequestServiceA(); ~CRequestServiceA(); bool RequestServiceARoutineA(); bool RequestServiceARoutineB(); CosNaming::Name m_corbaCosName; // CORBA ORB CORBA::ORB_var m_orb; CORBA::Object_var m_obj; // ORB Object CORBA::Object_var m_obj1; // Resolved id to object reference // Resolved and narrowed CORBA object for proxy calls Data::ServiceA_var m_Data; }; class DS_ServerConnectionException{ public: DS_ServerConnectionException() { cerr << "CORBA COMM_FAILURE" << endl; }; }; class DS_SystemException{ public: DS_SystemException() { cerr << "CORBA Exception" << endl; }; }; class DS_FatalException{ public: DS_FatalException() { cerr << "CORBA Fatal Exception" << endl; }; }; class DS_Exception{ public: DS_Exception() { cerr << "Exception" << endl; }; };
File: CRequestServiceA.cpp
The constructor is fairly generic and will be edited to match the names of your name server and service names. The constructor establishes the connection with the CORBA server but does not invoke any of the remote procedures. The routines "A" and "B" are called locally but executed remotely on the CORBA server.
#include "Data.hh" #include "CRequestServiceA.h" using namespace Data; CRequestServiceA::CRequestServiceA() { try { //------------------------------------------------------------------------ // Initialize ORB object. //------------------------------------------------------------------------ int argc=0; // Dummy variables to support following call. char** argv=0; CORBA::ORB_var orb = CORBA::ORB_init(argc, argv); //------------------------------------------------------------------------ // Bind ORB object to name service object. // (Reference to Name service root context.) //------------------------------------------------------------------------ CORBA::Object_var obj = orb->resolve_initial_references("OmniNameService"); assert (!CORBA::is_nil(obj.in())); //------------------------------------------------------------------------ // Narrow this to the naming context (Narrowed reference to root context.) //------------------------------------------------------------------------ CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow(obj.in()); assert (!CORBA::is_nil(nc.in())); //------------------------------------------------------------------------ // The "name text" put forth by CORBA server in name service. // This same name ("DataServiceName1") is used by the CORBA server when // binding to the name server (CosNaming::Name). //------------------------------------------------------------------------ CosNaming::Name _corbaCosName; _corbaCosName.length(1); _corbaCosName[0].id=CORBA::string_dup("DataServiceName1"); //------------------------------------------------------------------------ // Resolve "name text" identifier to an object reference. //------------------------------------------------------------------------ CORBA::Object_var obj1 = nc->resolve(_corbaCosName); assert(!CORBA::is_nil(obj1.in())); m_Data = ServiceA::_narrow(obj1.in()); if (CORBA::is_nil(m_Data.in())) { cerr << "IOR is not an SA object reference." << endl; } } catch(CORBA::COMM_FAILURE& ex) { cerr << "Caught system exception COMM_FAILURE -- unable to contact the " << "object." << endl; throw DS_ServerConnectionException(); return; } catch(CORBA::SystemException& ) { cerr << "Caught a CORBA::SystemException." << endl; throw DS_SystemException(); return; } catch(CORBA::Exception& ) { cerr << "Caught CORBA::Exception." << endl; throw DS_Exception(); return; } catch(omniORB::fatalException& fe) { cerr << "Caught omniORB::fatalException:" << endl; cerr << " file: " << fe.file() << endl; cerr << " line: " << fe.line() << endl; cerr << " mesg: " << fe.errmsg() << endl; throw DS_FatalException(); return; } catch(...) { cerr << "Caught unknown exception." << endl; throw DS_Exception(); return; } return; } CRequestServiceA::~CRequestServiceA() { // ... } bool CRequestServiceA::RequestServiceARoutineA() { CORBA::Long num1=4; CORBA::Long num2=5; CORBA::Long retNum; cout << "Values input to Service Routine A: " << num1 << " " << num2 << " " << retNum << endl; if( m_Data->CallServiceRoutineA( num1, num2, retNum)) // This is the CORBA call which is to be executed remotely { // Ok cout << "Values returned by Service Routine A: " << num1 << " " << num2 << " " << retNum << endl; return true; } else // fail { return false; } return true; } bool CRequestServiceA::RequestServiceARoutineB() { CORBA::Long num1=0; CORBA::Long num2=50; cout << "Values input to Service Routine B: " << num1 << " " << num2 << endl; if( m_Data->CallServiceRoutineB( num1, num2)) // This is the CORBA call which is to be executed remotely { // Ok cout << "Values returned by Service Routine B: " << num1 << " " << num2 << endl; return true; } else // fail { return false; } return true; }
File: Client.cpp
#include "CRequestServiceA.h" int main(int argc, char** argv) { CRequestServiceA requestServiceA; // Constructor establishes the link with the CORBA server. if(requestServiceA.RequestServiceARoutineA()) cout << "ServiceA RoutineA: True" << endl; if(requestServiceA.RequestServiceARoutineB()) cout << "ServiceA RoutineB: True" << endl; return 0; }
File: MakeClient
CC = /usr/bin/g++ CPPFLAGS = -g -c LDFLAGS = -g OMNI_HOME = /opt OMNI_INCLUDES = -I$(OMNI_HOME)/include OMNI_LIB_DIR = $(OMNI_HOME)/lib OMNIIDL = $(OMNI_HOME)/bin/omniidl INCLUDES = $(OMNI_INCLUDES) LIBS = -lomniORB4 -lomnithread -lomniDynamic4 OBJECTS = Data.o Client.o CRequestServiceA.o all Client: $(OBJECTS) $(CC) $(LDFLAGS) -o Client Client.o CRequestServiceA.o Data.o $(LIBS) Client.o: Client.cpp CRequestServiceA.h $(CC) $(CPPFLAGS) Client.cpp CRequestServiceA.o: CRequestServiceA.cpp CRequestServiceA.h Data.hh $(CC) $(CPPFLAGS) CRequestServiceA.cpp Data.o: DataSK.cc Data.hh $(CC) $(CPPFLAGS) DataSK.cc DataSK.cc: Data.idl $(OMNI_HOME)/bin/omniidl -bcxx Data.idl clean clean_all: rm -fr *.o rm -fr core rm -fr *.hh rm -fr *SK.cc rm -fr Client
Set the OMNIORB_CONFIG environment variable and then run the client:
[OmniTest]$ export OMNIORB_CONFIG=/etc/omniORB.cfg [OmniTest]$ ./Client omniORB: Distribution date: Sun Sep 22 22:06:56 BST 2002 dgrisby omniORB: Initialising omniDynamic library. omniORB: Creating ref to remote: key<0x4e616d6553657276696365> target id : IDL:omg.org/CORBA/Object:1.0 most derived id: IDL:omg.org/CosNaming/NamingContextExt:1.0 omniORB: Initial reference `OmniNameService' resolved from configuration file. omniORB: LocateRequest to remote: key<0x4e616d6553657276696365> omniORB: AsyncInvoker: thread id = 1 has started. Total threads = 1 omniORB: Invoke 'resolve' on remote: key<0x4e616d6553657276696365> omniORB: Creating ref to remote: root<0> target id : IDL:omg.org/CORBA/Object:1.0 most derived id: IDL:Data/ServiceA:1.0 Values input to Service Routine A: 4 5 0 omniORB: LocateRequest to remote: root<0> omniORB: Invoke 'CallServiceRoutineA' on remote: root<0> Values returned by Service Routine B: 4 9 10 ServiceA RoutineA: True Values input to Service Routine B: 0 50 omniORB: Invoke 'CallServiceRoutineB' on remote: root<0> Values returned by Service Routine B: 1 51 ServiceA RoutineB: True
[Potential Pitfall]:
[OmniTest]$ ./Client Caught CORBA::Exception. Exception AbortedYou must set your environment before running the client program:
export OMNIORB_CONFIG=/etc/omniORB.cfg
[Potential Pitfall]: The OmniORB libraries must be included in existing library path or added for the use of the IDL compiler, running the name server, the CORBA server and the CORBA client. To set the environment variable LD_LIBRARY_PATH:
export LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATH
What happened?
- The omniNames service must be up and running first.
- The CORBA server is started next. The server will use the environemnt variable (OMNIORB_CONFIG) to locate the config file omniORB.cfg
- The CORBA server will use the IOR in the config file to locate the name server.
- The CORBA server will register it's services with the name server and where to find them (IP and port)
- CORBA client starts and uses the environemnt variable (OMNIORB_CONFIG) to locate the config file omniORB.cfg
- The CORBA client will use the IOR in the config file to locate the name server.
- The CORBA client will ask the name server about services it is requesting.
- The name server will supply the client with the location of the services.
- The CORBA client will request services from the CORBA server.
This process can be streamlined by eliminating the name service and hard coding the client with the connection details of the service. This is less flexible as the service is then tied to the IP address of a single system. All clients would have to be recompiled with the new information if the service moved.
A CORBA sequence is a CORBA version of a variable length one-dimensional array. Like container classes (i.e. STL vector) it allows for an array of any data type including complex data types like structures. The following are additions to the above example.
Add to file: Data.idl
typedef sequence<long> NumberSeq; boolean CallServiceRoutineC ( inout long sum, in NumberSeq numberList);
Add to file: CServiceA.h:
virtual CORBA::Boolean CallServiceRoutineC( CORBA::Long& sum, const Data::NumberSeq& numberList);
Add to file: CServiceA.cpp
CORBA::Boolean CServiceA_i::CallServiceRoutineC( CORBA::Long& sum, const Data::NumberSeq& numberList) { int ii; for(ii=0; ii<numberList.length(); ii++) { sum += numberList[ii]; } return true; }
Add to file: Client.cpp
if(requestServiceA.RequestServiceARoutineC()) cout << "ServiceA RoutineC: True" << endl;
Add to file: CRequestServiceA.h
bool RequestServiceARoutineC();
Add to file: CRequestServiceA.cpp
bool CRequestServiceA::RequestServiceARoutineC() { CORBA::Long sum=0; NumberSeq numbersToAdd; int ii; numbersToAdd.length(4); numbersToAdd[0] = 10; numbersToAdd[1] = 20; numbersToAdd[2] = 30; numbersToAdd[3] = 40; cout << "Values input to Service Routine C: " << endl; for(ii=0; ii<4; ii++) cout << ii << ": " << numbersToAdd[ii] << endl; if( m_Data->CallServiceRoutineC( sum, numbersToAdd)) { // Ok cout << "Sum of values returned by Service Routine C: " << sum << endl; return true; } else // fail { return false; } return true; }
Results:
Values input to Service Routine C: 0: 10 1: 20 2: 30 3: 40 omniORB: Invoke 'CallServiceRoutineC' on remote: root<0> Sum of values returned by Service Routine C: 100 ServiceA RoutineC: True
- OmniORB - Home page
- SourceForge OmniORB project downloads
- OmniORB-Support.com
- ApaSphere.com (Commercial support)
- JacORB.org - Java. Will interface with OmniORB.
- OMG: CORBA specs: Part 1, Part 2, Part 3
- Linux Journal CORBA article