CORBA

Background

CORBA

The Common Object Request Broker Architecture is a middleware system for building distributed applications. It is looked after by the Object Management Group (OMG) which has been in existence for over 15 years.

CORBA supports distributed objects. The "objects" may be written in C, C++, Ada, Java, SmallTalk, Python, etc. Some of these are not O/O languages. A single distributed application may use a mixture of languages.

Objects communicate by making method calls on one another. The CORBA "backplane" or Object Request Broker (ORB) looks after marshalling parameters, transferring the data, making the remote call and returning the results.

The ORB structure is

Objects may be located in a variety of ways. These include

CORBA IDL

To achieve language independence, CORBA applications are specified by interfaces written in an IDL (Interface Definition Language). CORBA IDL is different to Sun's RPC/ONC IDL and to DCE IDL. It is based on C++, with changes to accomodate distributed objects, such as in/out parameters instead of reference/value parameters.

A simple IDL is

    module HelloApp {
        interface Hello {
            string sayHello();
        };
    };
A more complex one is
module RoomBooking {
 
    interface Meeting {
         readonly attribute string purpose;
         readonly attribute string participants;
    };
 
    interface MeetingFactory {
         Meeting CreateMeeting(in string purpose, in string participants);
    };
 
    enum Slot {am9, am10, am11, pm12, pm1, pm2, pm3, pm4};
 
    const short MaxSlots = 8;
 
    exception NoMeetingInThisSlot {};
    exception SlotAlreadyTaken {};
 
    interface Room {
        typedef Meeting Meetings[MaxSlots];
 
        readonly attribute string name;
 
        Meetings View();
 
        void Book(in Slot a_slot, in Meeting  a_meeting)
            raises(SlotAlreadyTaken);
 
        void Cancel(in Slot  a_slot)
            raises(NoMeetingInThisSlot);
    };
};               

The IDL is used to generate stubs and skeletons for the client and server.

Primitive types

short 16 bits signed
unsigned short 16 bits unsigned
long 32 bits signed
unsigned long 32 bits unsigned
long long 64 bits signed
unsigned long long 64 bits unsigned
char 8 bits
wchar "wide enough" char
boolean
octet 8 bits
any

There are mappings defined to types in all of the CORBA supported languages

IDL C/C++ (no name spaces) C++ (with namespaces) Java
short CORBA_Short CORBA::Short short
unsigned short CORBA_UShort CORBA::UShort short
long CORBA_Long CORBA::Long int
unsigned long CORBA_ULong CORBA::ULong int
long long CORBA_LongLong CORBA::LongLong long
unsigned long long CORBA_ULongLong CORBA::ULongLong long
char CORBA_Char CORBA::Char char
wchar CORBA_WChar CORBA::WChar char
boolean CORBA_Boolean CORBA::Boolean boolean
octet CORBA_Octet CORBA::Octet byte

Modules and Interfaces

An appplication may be made up from specifications from different sources. There may be a possibility of name clashes. CORBA uses "modules" to create separate "name spaces" so that the possibility of clashes is reduced.

e.g.

module a {
  module b {
    ...
  };
};

Classes are defined by "interfaces". For example,

module gui {
  interface pushButton {
    ...
  };

  interface textArea {
    ...
  };
};

C++ mapping

C++ was still evolving while CORBA was being developed. There are a number of possible mappings, depending on which version of the language/which compiler you target.

Java mapping

Modules map to Java packages. Interfaces map to Java interfaces

package gui;

public interface pushButton {
  ...
};

Inheritance

IDL supports multiple inheritance of interfaces

interface Rectangle: Shape {...};
These map directly onto the inheritance mechanisms of classes for C++ and interfaces for Java.

C++:

    class Rectangle: Shape {...};

Java

    interface Rectangle extends Shape {...};

Attributes

Attributes are fields of objects. They may be readonly or read/write fields. e.g.

attribute long x;
readonly attribute long y; 

Mapping to C++

Attributes are hidden. They are accessible by "set" and "get" methods

CORBA_Long x();      // get
void x(CORBA_Long);  // set

CORBA_Long y();      // get only

Mapping to Java

Attributes map to protected fields of objects. There are "set" and "get" methods for attributes that are both readable and writable, but only "get" methods for readonly attributes.

Java Beans (and most style guides) use getX() and setX() methods to access an attribute. CORBA doesn't follow this. It uses X() for get methods and X(...) for set methods

int x();       // get
void x(int i); // set

int y();       // get only

Operations

CORBA uses the word operation for methods. These translate into operations in C++ and methods in Java. Parameters can be in, inout or out. in parameters translate according to the earlier tables and are passed by value. inout and out parameters translate differently because they have to carry return values

mapping to C++

Each out or inout basic type maps to a CORBA type followed by _out

short CORBA_Short_out
long CORBA_Long_out
boolean CORBA_Boolean_out
In addition, each of these is typedef'ed to a reference to the corresponding CORBA type
typedef CORBA_Short& CORBA_Short_out;

e.g.

    long f(in short x, out short y, inout short z);
maps to
    virtual CORBA_Long f(CORBA_Short x,
                         CORBA_Short& y,
                         CORBA_Short& z);                                     

Mapping to Java

Java has a set of "helper" classes such as org.omg.CORBA.ShortHolder. These have a field value. An instance of these classes can be passed as an out parameter, and its value field can be changed

    public int
    f(short x,
      org.omg.CORBA.ShortHolder y,
      org.omg.CORBA.ShortHolder z); 

Similar helper classes are defined for non-primitive types (objects).


CORBA protocols

Two CORBA objects must communicate using a standardised format. CORBA 1.0 failed to define this, so objects from different vendors could not communicate (or even find each other). In CORBA 2.0 this was fixed by a number of different specification (see http://www.blackmagic.com/people/gabe/iiop.html)

CORBA and C++

Idl compiler

Given an IDL file X.idl, an "idl to C++" compiler will generate 4 files

e.g. Hello.idl results in

Simple C++ Client

The client is an application starting from main(). The pseudocode is

    initialize CORBA ORB
    find a reference to the service
    cast the service to the right type
    call methods on the service

For the IDL

interface Hello {
    void sayHello();
};
the client can use the method
  void sayHello(); 

The client is

// for Orbacus

#include <OB/CORBA.h>
#include "Hello.h"

#include <fstream.h>

int main(int argc, char **argv)
{
    // initialize CORBA ORB
    CORBA_ORB_var orb = CORBA_ORB_init(argc, argv);

    // find service - here pick a string form out of a file
    const char* refFile = "Hello.ref";
    ifstream in(refFile);
    char s[1000]; // must be big enough!
    in >> s;
    CORBA_Object_var obj = orb -> string_to_object(s);

    // cast to right type
    Hello_var hello = Hello::_narrow(obj);

    // invoke methods
    // NB: Prints on the server!
    hello -> sayHello();

    exit(0);
}

Simple C++ Server

Much of the server code is supplied by the IDL compiler and the CORBA library calls. Pseudocode for a server is

    initialize CORBA ORB
    initialize the BOA (Basic Object Adapter)
    create a reference to the service
    export the reference (so clients can find it)
    enter client request loop

For the hello example, the server is

// Orbacus server

#include <OB/CORBA.h>
#include <Hello_impl.h>

#include <fstream.h>

int main(int argc, char** argv) 
{
    // initialize CORBA ORB
    CORBA_ORB_var orb = CORBA_ORB_init(argc, argv);

    // initialize the BOA
    CORBA_BOA_var boa = orb -> BOA_init(argc, argv);

    // create a reference to the service
    Hello_var p = new Hello_impl;

    // export the reference
    CORBA_String_var s = orb -> object_to_string(p);
    const char* refFile = "Hello.ref";
    ofstream out(refFile);
    out << s << endl;
    out.close();

    // enter client request loop
    boa -> impl_is_ready(CORBA_ImplementationDef::_nil());
}

The Hello_impl class is defined in two files: a header file and an implementation file. The header file Hello_impl.h is

#include <Hello_skel.h>

class Hello_impl: public Hello_skel
{
 public:
    virtual void sayHello();
};
and the implementation file Hello_impl.cpp is
// Orbacus

#include <OB/CORBA.h>
#include <Hello_impl.h>

void Hello_impl::sayHello()
{
    cout << "Hello world" << endl;
}

CORBA and Java

Idl compiler

Given an IDL file Hello.idl, an "idl to Java" compiler will generate 5 files

Under jdk1.4, the command to generate all of these is
   idlj -f all -oldImplBase *.java

From the Hello.idl

interface Hello {
    void sayHello();
};
the Java interface Hello.java is generated


/**
* Hello.java .
* Generated by the IDL-to-Java compiler (portable), version "3.2"
* from Hello.idl
* Tuesday, September 5, 2006 9:58:20 AM EST
*/

public interface Hello extends HelloOperations, org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity 
{
} // interface Hello
with


/**
* HelloOperations.java .
* Generated by the IDL-to-Java compiler (portable), version "3.2"
* from Hello.idl
* Tuesday, September 5, 2006 9:58:20 AM EST
*/

public interface HelloOperations 
{
  void sayHello ();
  String sayHello2 ();
} // interface HelloOperations

Simple Java client

The client starts from main(), and obtains a reference to a CORBA Object. It does this by reading the string version of the reference from a file, like the C++ case. It then casts it to an implementation of the Hello interface.


import java.io.*;

public class JClient {

    public static void main(String[] argv) {
        /*
	java.util.Properties props = System.getProperties();
	props.put("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORB");
	props.put("org.omg.CORBA.ORBSingletonClass",
		  "com.ooc.CORBA.ORBSingleton");
	System.setProperties(props);
        */

	org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(argv, null);

	String ref = null;
	try {
	    String refFile = "Hello.ref";
	    BufferedReader in = new BufferedReader(new FileReader(refFile));
	    ref = in.readLine();
	} catch(IOException e) {
	    System.out.println("Cant read from file");
	    System.exit(1);
	}

	org.omg.CORBA.Object obj = orb.string_to_object(ref);

	Hello p = HelloHelper.narrow(obj);

	String res = p.sayHello2();
	System.out.println(res);
    }
}

Simple Java server

The implementation of the service is


public class Hello_impl extends _HelloImplBase {

    public void sayHello() {
	System.out.println("Hello World");
    }

    public String sayHello2() {
	return "Hello 2 world";
    }
}

The server is


import java.io.*;

public class JServer {

    static public void main(String[] argv) {

	org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(argv, null);

	Hello_impl p = new Hello_impl();

	try {
	    String ref = orb.object_to_string(p);
	    String refFile = "Hello.ref";
	    PrintWriter out = new PrintWriter(new FileOutputStream(refFile));
	    out.println(ref);
	    out.flush();
	} catch(IOException e) {
	    System.out.println("Cant write to file");
	    System.exit(1);
	}

	orb.connect(p);
        java.lang.Object sync = new java.lang.Object();
        synchronized(sync) {
            try {
                sync.wait();
            } catch(InterruptedException e) {
                // do nothing
            }
        }
    }
}

Different ORBs

Different vendors will need their own classes loaded in. This is generally done by setting properties to label the class files. For example, the Orbacus ORB requires this in the client

public class JClient {

    public static void main(String[] argv) {
        // Setup info about the CORBA implementation used
	java.util.Properties props = System.getProperties();
	props.put("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORB");
	props.put("org.omg.CORBA.ORBSingletonClass",
		  "com.ooc.CORBA.ORBSingleton");
	System.setProperties(props);

	org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(argv, props);
        // etc

The server changes are

public class JServer {

    static public void main(String[] argv) {
        // setup info about CORBA implementation
	java.util.Properties props = System.getProperties();
	props.put("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORB");
	props.put("org.omg.CORBA.ORBSingletonClass",
		  "com.ooc.CORBA.ORBSingleton");
	System.setProperties(props);

        // initialise ORB and BOA
	org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(argv, props);
	org.omg.CORBA.BOA boa = ((com.ooc.CORBA.ORB) orb).BOA_init(argv,props);
        // etc


Getting object references

Strings

The methods object_to_string() and string_to_object() can be used to store references to objects outside of a server so that clients can reconstruct them. These "stringified" references are portable to other machines - they contain location information about the service. For example

Naming service

A naming service is a process running independently of any other clients and servers. A service will register itself with the naming service, passing both a simple string as its name and an object reference to itself. A client will then ask the naming service for the service by using its name, and will get back an object reference.

Sun supply a naming service as part of JDK

tnameserv -ORBInitialPort nameserverport

All servers and clients will need to know the location of the naming service in order to find it and then register/query it. This is typically done by giving a port number or URL. e.g.

JClient -ORBInitialHost nameserverhost
        -ORBInitialPort nameserverport

Java resolution

The Naming Service is found this way in Java

org.omg.CORBA.Object obj;
org.omg.CosNaming.NamingContext ctx;

obj = orb.resolve_initial_references("NameService");
ctx = org.omg.CosNaming.NamingContextHelper.narrow(obj);

Java service

The service registers itself with the naming service by

// bind the Hello service to the name server
NameComponent nc = new NameComponent("Hello", "");
NameComponent path[] = {nc};
ctx.rebind(path, helloRef);

The Java Hello service using this is

import java.io.*;
import org.omg.CosNaming.*;

public class JServer {

    static public void main(String[] argv) {

	org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(argv, null);

	Hello_impl helloRef = new Hello_impl();

	// find the naming service
	org.omg.CORBA.Object obj = null;
	org.omg.CosNaming.NamingContext ctx;

	try {
	    obj = orb.resolve_initial_references("NameService");
	} catch(org.omg.CORBA.ORBPackage.InvalidName e) {
	    e.printStackTrace();
	    System.exit(1);
	}
	ctx = org.omg.CosNaming.NamingContextHelper.narrow(obj);

	// bind the Hello service to the naming service
	NameComponent nc = new NameComponent("Hello", "");
	NameComponent path[] = {nc};
	try {
	    ctx.rebind(path, helloRef);
	} catch(Exception e) {
	    e.printStackTrace();
	    System.exit(1);
	}

        orb.connect(p);

        java.lang.Object sync = new java.lang.Object();
        synchronized(sync) {
            try {
                sync.wait();
            } catch(InterruptedException e) {
                // do nothing
            }
        }
    }
}

Java client

The client uses the Naming Service to get a reference to the Hello object by

NameComponent nc = new NameComponent("Hello", "");
NameComponent path[] = {nc};
org.omg.CORBA.Object obj = ctx.resolve(path);
Hello helloRef = HelloHelper.narrow(obj);

The Hello client using this is

import java.io.*;
import org.omg.CosNaming.*;

public class JClient {

    public static void main(String[] argv) {

	org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(argv, null);

	// find the naming service
	org.omg.CORBA.Object obj = null;
	org.omg.CosNaming.NamingContext ctx;
	
	try {
	    obj = orb.resolve_initial_references("NameService");
	} catch(org.omg.CORBA.ORBPackage.InvalidName e) {
	    e.printStackTrace();
	    System.exit(1);
	}
	ctx = org.omg.CosNaming.NamingContextHelper.narrow(obj);

	// find the object reference
	NameComponent nc = new NameComponent("Hello", "");
	NameComponent path[] = {nc};
	org.omg.CORBA.Object helloObj = null;
	try {
	    helloObj = ctx.resolve(path);
	} catch(Exception e) {
	    e.printStackTrace();
	    System.exit(1);
	}
	Hello helloRef = HelloHelper.narrow(helloObj);

	helloRef.sayHello();
    }
}

The naming service also supports a directory structure. For example, a list of shops coud be stored under the directory "shops", and the naming context can be asked to list the directory.

Trader

A Trader is a more advanced CORBA service than just Naming. A Naming Service associates a single string with a service. A Trader allows a set of properties to be associated with a service. A client can then search (using boolean operators) for services matching required properties. For example, a printer can register itself as a printer capable of printing Postscript and PDF files, with a speed of 24 ppm. A client can then search for a printer capable of printing PDF files with a speed of more than 20 ppm.

References


Jan Newmarch (http://jan.newmarch.name)
jan@newmarch.name
Last modified: Mon Sep 4 19:48:16 EST 2006
Copyright ©Jan Newmarch
Copyright © Jan Newmarch, Monash University, 2007
Creative Commons License This work is licensed under a Creative Commons License
The moral right of Jan Newmarch to be identified as the author of this page has been asserted.