2.1. CORBA

CORBA (Common Object Request Broker Architecture) is a standard architecture of a remote procedure call framework that supports heterogeneous object oriented applications. The CORBA standard has evolved through several major revisions, the text in this section is mostly relevant for the later 2.x and early 3.x versions.

2.1.1. Interface Definition Language

The interface definition language is used to describe types used by CORBA, from the basic types of individual arguments to the complex types of interfaces and objects. The language is similar in syntax to C++.

2.1.1.1. Basic Types

The integer types are short, long and long long for signed integer numbers of 16, 32 and 64 bits and unsigned short, unsigned long and unsigned long long for their unsigned counterparts.

Integer Types
short

16 bit signed integer

long

32 bit signed integer

long long

64 bit signed integer

unsigned short

16 bit unsigned integer

unsigned long

32 bit unsigned integer

unsigned long long

64 bit unsigned integer

Values. 

18, 022, 0x12, 0X12

Constants. 

const short aShortConstant = 6 * 7;

The floating point types are float, double and long double for ANSI/IEEE 754-1985 single precision, double precision and double extended precision floating point numbers.

Floating Point Types
float

24 bit signed fraction, 8 bit signed exponent

double

53 bit signed fraction, 11 bit signed exponent

long double

113 bit signed fraction, 15 bit signed exponent

Values. 

3.14, 12.34e5, 1.2E-4

Constants. 

const float aFloatConstant = 3.141593;

The character types are char for a single character in a single-byte character set and wchar for a single character in a multiple-byte character set. The interface definition language itself uses ISO 8859-1 Latin 1.

Character Types
char

character in single-byte character set

wchar

character in multiple-byte character set

Values. 

'a', '\n', '\000', '\x12'

Constants. 

const char aTab = '\t';
const wchar aWideTab = L'\t';

The logical type is boolean with values of true and false.

Logical Types
boolean

logical value

Values. 

TRUE, FALSE

Constants. 

const boolean aTrueValue = TRUE;
const boolean aFalseValue = FALSE;

The special types are octet for 8 bits of raw data and any for a container of another arbitrary type.

Special Types
octet

8 bits of raw data

any

container of another arbitrary type

2.1.1.2. Constructed Data Types

A structure represents a classical compound type with named members that all contain a value.

Structures

Declaration. 

struct aPerson
{
  string firstName;
  string lastName;
  short  age;
};

An exception is a structure that can be returned as an exceptional result of an operation. A number of standard exceptions is defined. Note there is no inheritance in exception declarations, however, language mappings do add inheritance to make it easier to catch standard exceptions.

Exceptions

Declaration. 

exception anException
{
  string reason;
  string severity;
};

Standard System Exception. 

exception COMM_FAILURE
{
  unsigned long minor;
  completion_status completed;
};

A union represents a classical compound type with named members out of which one contains a value. A discriminator is used to determine which of the members contaisn the value.

Unions

Declaration. 

union aSillyUnion switch (short)
{
  case 1  : long aLongValue;
  case 2  : float aFloatValue;
  default : string aStringValue;
};

An enum represents a classical enumerated type with distinct identifiers stored as 32 bit unsigned integers.

Enums

Declaration. 

enum aBaseColor { red, green, blue }

An array is a container for a fixed number of items of the same type addressed by integer indices.

Arrays

Declaration. 

typedef long aLongArray [10];

A sequence is a container for a variable number of items of the same type addressed by integer indices. The maximum number of items in the container can be limited explicitly.

Sequences

Declaration. 

typedef sequence<long,10> aBoundedVector;
typedef sequence<long> anUnboundedVector;

A string is a sequence of char items. A wstring is a sequence of wchar items.

Strings

Declaration. 

typedef string<10> aBoundedString;
typedef string anUnboundedString;

Constants. 

const string aHello = "Hello\n";
const wstring aWideHello = L"Hello\n";

A fixed point type represents a fixed point number of upto 31 significant digits.

Fixed Point Types

Declaration. 

typedef fixed<10,2> aPrice;

Constants. 

const fixed aPrice = 12.34D;

2.1.1.3. Constructed Object Types

An interface type represents an object that is passed by reference and accessed remotely. The declaration of an interface type can specify multiple interface inheritance, attributes and operations. Apart from this, the declaration also creates a lexical scope within which other declarations can appear.

Interface Types

Declaration. 

abstract interface aParentInterface
{
  attribute string aStringAttribute;
  short aMethod (in long aLongArgument, inout float aFloatArgument);
}

interface aChildInterface : aParentInterface
{
  readonly attribute short aShortAttribute;
  oneway void aOnewayMethod (in long anArgument);
  void aTwowayMethod () raises anException;
}

Keywords. 

local

interface not invoked remotely

abstract

runtime determines passing semantics

oneway

best effort delivery

readonly

attribute without setter

In some situations, it might be useful to have an interface type that can represent both an object passed by reference and an object passed by value. This is possible when the interface is denoted as abstract.

It is also possible to use interface types to describe objects that are not invoked through CORBA, the interface types are then denoted as local.

A value type represents an object that is passed by value and accessed locally. The declaration of a value type can specify single value type inheritance, single interface and multiple abstract interface support, attributes with private or public visibility, operations and initializers. Apart from this, the declaration also creates a lexical scope within which other declarations can appear.

Value Types

Declaration. 

valuetype aChildValue : truncatable aParentValue, supports anInterface
{
  private short aShortMember;
  public aParentValue aValueMember;
  factory aFactory (in string anArgument);
  short aLocalMethod (in long aLongArgument, in float aFloatArgument);
}

Keywords. 

custom

custom marshalling

abstract

base type not instantiated

truncatable

state compatible with parent

public

value used by clients

private

value used by implementation

factory

portable initializer

A value type can support multiple abstract interfaces but only a single interface that is not abstract. When used as an instance of one of the supported abstract interfaces, the value type is passed by value. When used as an instance of the supported interface that is not abstract, the value type is passed by reference.

When an object is passed by value, it might happen that an implementation of its type is not available to the receiver, but an implementation of its parent type is. When a value type is denoted as truncatable, its implementation is considered compatible with the implementation of its parent to the degree that the state of the type can be truncated to the portion inherited from its parent and used by its parent.

A value type that is declared custom will rely on user defined marshalling implementation. A custom value type may not be truncatable.

2.1.2. Language Mapping

The section on language mapping discusses C++ and Java as two major examples. For mostly historical reasons, some mapping constructs do not rely on all the latest features of the target languages, making the language mapping more portable but perhaps potentially less elegant.

Since the goal of the text is to illustrate the issues encountered in language mapping, it outlines the mapping for selected representative types only. Mapping of other types is roughly analogous. Note how in C++, the mapping can use overloading to achieve syntactically simple constructs, but struggles to cope with memory management. In contrast, the mapping to Java sometimes struggles to map types without native counterparts, but memory management is completely transparent.

2.1.2.1. Integer And Floating Point Types

The goal of the integer types mapping is to use native types with matching precision. The use of native types means no conversion is necessary during argument passing. The requirement of matching precision is obviously necessary for correctness.

C++.  Because the early versions of the language do not standardize the precision of native integer types, the mapping introduces CORBA integer types that the implementation should use. These types are mapped to native integer types using typedef.

The mapping for C++11 uses standard integer types with explicit precision.

Java.  Because the language does not provide unsigned integer types, the mapping uses signed integer types and indicates conversion errors by throwing an exception.

Because the language lacks the ability to pass mutable integer types by reference, special Holder classes are defined for all integer types.

Holder Class Example
public final class IntHolder
implements org.omg.CORBA.portable.Streamable {
    public int value;

    public IntHolder () { }
    public IntHolder (int o) { value = o; }

    public TypeCode _type () {
        return ORB.init ().get_primitive_tc (TCKind.tk_long);
    }

    public void _read (org.omg.CORBA.portable.InputStream in) {
        value = in.read_long ();
    }

    public void _write (org.omg.CORBA.portable.OutputStream out) {
        out.write_long (value);
    }
}

The mapping of floating point types encounters similar problems as the mapping of integer types. These problems are also solved in a similar manner in both C++ and Java.

2.1.2.2. Character And String Types

Besides the usual goal of using native types, mapping of character types also attempts to preserve the meaning of characters in presence of multiple potential encodings.

C++.  Because the language does not standardize the encoding of native character types, the mapping assumes that platform specific information will be used to derive the appropriate encoding as necessary.

The language also lacks automated memory management. Special var classes and allocator methods are introduced.

Var Class Example
class String_var {
private:

    char *data;

public:

    inline String_var ()        { data = 0; }
    inline String_var (char *p) { data = p; }

    inline String_var (const char *p) {
        if (p) data = CORBA::string_dup (p);
        else   data = 0;
    }

    inline ~String_var () {
        CORBA::string_free (data);
    }

    inline String_var &operator = (char *p) {
        CORBA::string_free (data);
        data = p;
        return (*this);
    }

    inline operator char * () { return (data); }

    inline char &operator [] (CORBA::ULong index) {
        return (data [index]);
    }

    ...
}

The var classes and allocator methods help prevent memory management errors in common programming constructs.

Var Class Usage
void FunctionWithoutLeaks (void) {
    // All strings must be allocated using specific functions
    String_var vSmartPointer = string_dup ("A string ...");

    // Except assignment from const string which copies
    const char *pConstPointer = "A const string ...";
    vSmartPointer = pConstPointer;

    // Assignment releases rather than overwrites
    vSmartPointer = string_dup ("Another string ...");

    // Going out of scope releases too
    throw (0);
}

The mapping for C++11 provides reference types whose semantics is equal to that of std::shared_ptr and std::weak_ptr, available through the IDL::traits<T>::ref_type and IDL::traits<T>::weak_ref_type traits. The basic string type is std::string.

2.1.2.3. Any Type

The paramount concern of the any type mapping is making it type safe, that is, making sure the type of the content is always known.

C++.  The mapping relies on operator overloading and defines a class with accessor operators for all types that can be stored inside any. This includes accessors for user defined types.

Any Class Example
class Any {
public:

    // Types passed by value are easy
    void operator <<= (Any &, Short);
    Boolean operator >>= (const Any &, Short &);
    ...

    // Types passed by reference introduce ownership issues
    void operator <<= (Any &, const Any &);
    void operator <<= (Any &, Any *);
    ...

    // Types where overloading fails introduce resolution issues
    struct from_boolean { from_boolean (Boolean b) : val (b) { } Boolean val; };
    struct from_octet { from_octet (Octet o) : val (o) { } Octet val; };
    struct from_char { from_char (Char c) : val (c) { } Char val; };
    ...

    void operator <<= (from_boolean);
    void operator <<= (from_octet);
    void operator <<= (from_char);
    ...

    struct to_boolean { to_boolean (Boolean &b) : ref (b) { } Boolean &ref; };
    ...

    Boolean operator >>= (to_boolean) const;
    ...

private:

    // Private operators can detect resolution issues
    unsigned char void operator <<= (unsigned char);
    Boolean operator >>= (unsigned char &) const;
}

Operator overloading fails to distinguish IDL types that map to the same native type. This is true for example with the char and octet IDL types, which both map to the char native type. In such situations, wrapping in a distinct type is used.

The any type is assumed to own its content.

Any Class Insertion
Any oContainer;

// Small types can be stored easily
Long iLongValue = 1234;
Float fFloatValue = 12.34;
oContainer <<= iLongValue;
oContainer <<= fFloatValue;

// Constant references have copying semantics
const char *pConstString = "A string ...";
oContainer <<= pConstString;

// Non constant references have adoption semantics
String_var vString = string_dup ("A string ...");
oContainer <<= Any::from_string (vString, 0, FALSE);
oContainer <<= Any::from_string (vString._retn (), 0, TRUE);

// Some types need to be resolved explicitly
Char cChar = 'X';
Octet bOctet = 0x55;
oContainer <<= Any::from_char (cChar);
oContainer <<= Any::from_octet (bOctet);
Any Class Extraction
Any oContainer;

// Small types can be retrieved easily
Long iLongValue;
Float fFloatValue;
if (oContainer >>= iLongValue) ...;
if (oContainer >>= fFloatValue) ...;

// References remain owned by container
const char *pConstString;
if (oContainer >>= Any::to_string (pConstString, 0)) ...;

// Some types need to be resolved explicitly
Char cChar;
Octet bOctet;
if (oContainer >>= Any::to_char (cChar)) ...;
if (oContainer >>= Any::to_octet (bOctet)) ...;

Java.  The mapping defines a class with accessor methods for all standard types. To keep the any class independent of user defined types, methods for inserting and extracting a user defined type are implemented by helper classes associated with that type.

2.1.2.4. Structures And Exceptions

The mapping of structures and exceptions uses the corresponding object types.

C++.  A structure is assumed to own its content.

An exception is equipped with a method to throw its most derived type.

Exception Class Example
class Exception {
public:

    // Method for throwing most derived type
    virtual void _raise () const = 0;
    ...
}

2.1.2.5. Unions

The paramount concern of the union type mapping is making it type safe, that is, making sure the type of the content is always known.

C++.  The mapping defines a class with accessor methods for all types that can be stored inside the union. Each setter method also sets the discriminator as appropriate. Each getter method also tests the discriminator.

Union Class Example
class AUnion {
public:

    ...

    void _d (Short);    // Set discriminator
    Short _d() const;   // Get discriminator

    void ShortItem (Short);     // Store ShortItem and set discriminator
    Short ShortItem () const;   // Read ShortItem if stored

    void LongItem (Long);       // Store LongItem and set discriminator
    Long LongItem () const;     // Read LongItem if stored

    ...
}
Union Class Usage
AUnion oUnion;
Short iShortValue = 1234;
Long iLongValue = 5678;

// Storing sets discriminator
oUnion.ShortItem (iShortValue);
oUnion.LongItem (iLongValue);

// Retrieving must check discriminator
if (oUnion._d () == 1) iShortValue = oUnion.ShortItem ();
if (oUnion._d () == 2) iLongValue = oUnion.LongItem ();

Java.  The mapping defines a class with accessor methods for all types that can be stored inside the union. Each setter method also sets the discriminator as appropriate. Each getter method also tests the discriminator.

2.1.2.6. Enum Types

C++.  The only catch to mapping the enum type is making sure of its size. This is achieved by defining an extra enum member that dictates the size.

Java.  The mapping of the enum type should be type safe, that is, instances of different enum types should not be interchangeable among themselves or interchangeable with integer types. This, however, would prevent using instances of enum types in the switch statement. That is why the mapping uses a class to represent an enum but also defines integer constants corresponding to enum instances.

Enum Class Example
public class AnEnum {
    public static final int _red = 0;
    public static final AnEnum red = new AnEnum (_red);

    public static final int _green = 1;
    public static final AnEnum green = new AnEnum (_green);

    ...

    public int value () {...};
    public static AnEnum from_int (int value) {...};
}
Enum Class Usage
AnEnum oEnum;

// Assignments are type safe
oEnum = AnEnum.red;
oEnum = AnEnum.green;

// Switch statements use ordinal values
switch (oEnum.value ()) {
    case AnEnum._red: ...;
    case AnEmum._green: ...;
}

2.1.2.7. Sequences

C++.  Because the language lacks variable length arrays, sequences are mapped to classes with an overloaded indexing operator. Special var classes and allocator methods are introduced.

Sequence Class Example
class ASequence {
public:

    ASequence ();
    ASequence (ULong max);
    ASequence (ULong max, ULong length, Short *data, Boolean release = FALSE);

    ...

    ULong maximum () const;
    Boolean release () const;

    void length (ULong);
    ULong length () const;

    T &operator [] (ULong index);
    const T &operator [] (ULong index) const;

    ...
}

The mapping for C++11 provides reference types whose semantics is equal to that of std::shared_ptr and std::weak_ptr, available through the IDL::traits<T>::ref_type and IDL::traits<T>::weak_ref_type traits. The basic sequence type is std::vector.

2.1.2.8. Fixed Point Types

C++.  The mapping relies on operator overloading and defines a class with common arithmetic operators. Because the language does not support fixed point constants, the mapping also adds a conversion from a string.

Fixed Class Example
class Fixed {
public:

    // Constructors

    Fixed (Long val);
    Fixed (ULong val);
    Fixed (LongLong val);
    Fixed (ULongLong val);
    ...
    Fixed (const char *);

    // Conversions

    operator LongLong () const;
    operator LongDouble () const;
    Fixed round (UShort scale) const;
    Fixed truncate (UShort scale) const;

    // Operators

    Fixed &operator = (const Fixed &val);
    Fixed &operator += (const Fixed &val);
    Fixed &operator -= (const Fixed &val);
    ...
}

Fixed operator + (const Fixed &val1, const Fixed &val2);
Fixed operator - (const Fixed &val1, const Fixed &val2);
...

Java.  The mapping simply uses the BigDecimal class.

2.1.2.9. Proxies

Since the proxy should resemble an implementation of the interface that it represents, the mapping will generally use the native interface and object constructs of the target language in a straightforward manner. What makes proxies interesting are the subtle typing issues that arise.

C++.  The IDL interface is represented by a C++ class with virtual methods for IDL operations. The proxy is a platform specific class that inherits from the interface class. Safe type casting over remote types requires the addition of the narrow method.

Proxy Interface Class Example
class AnInterface;
typedef AnInterface *AnInterface_ptr;
class AnInterface_var;


class AnInterface : public virtual Object {
public:

    typedef AnInterface_ptr _ptr_type;
    typedef AnInterface_var _var_type;

    static AnInterface_ptr _duplicate (AnInterface_ptr obj);
    static AnInterface_ptr _narrow (Object_ptr obj);
    static AnInterface_ptr _nil ();

    virtual ... AnOperation (...) = 0;

protected:

    AnInterface ();
    virtual ~AnInterface ();

    ...
}

Memory management issues are solved by introducing reference counting and var classes.

Proxy Var Class Example
class AnInterface_var : public _var {
protected:

    AnInterface_ptr ptr;

public:

    AnInterface_var () { ptr = AnInterface::_nil (); }
    AnInterface_var (AnInterface_ptr p) { ptr = p; }

    ...

    ~AnInterface_var () {
        release (ptr);
    }

    AnInterface_var &operator = (AnInterface_ptr p) {
        release (ptr);
        ptr = p;
        return (*this);
    }

    AnInterface_var &operator = (const AnInterface_var &var) {
        if (this != &var) {
            release (ptr);
            ptr = AnInterface::_duplicate (AnInterface_ptr (var));
        }
        return (*this);
    }

    operator AnInterface_ptr & () { return (ptr); }
    AnInterface _ptr operator -> () const { return (ptr); }

    ...
}

The mapping for C++11 provides reference types whose semantics is equal to that of std::shared_ptr and std::weak_ptr, available through the IDL::traits<T>::ref_type and IDL::traits<T>::weak_ref_type traits. Casting to derived interfaces is supported through a IDL::traits<T>::narrow method.

Java.  The IDL interface is represented by a Java interface with methods for IDL operations. The proxy is a platform specific class that implements the Java interface. Safe type casting over remote types requires the addition of the narrow method. Still more methods are present in a helper class that facilitates insertion and extraction to and from the any type together with the marshalling operations. The standardization of the marshalling operations makes it possible to use proxy classes in a platform independent manner.

Proxy Class Example
public interface AnInterfaceOperations {
    ... AnOperation (...) throws ...;
}

public interface AnInterface extends AnInterfaceOperations ... { }

abstract public class AnInterfaceHelper {
    public static void insert (Any a, AnInterface t) {...}
    public static AnInterface extract (Any a) {...}
    public static AnInterface read (InputStream is) {...}
    public static void write (OutputStream os, AnInterface val) {...}
    ...

    public static AnInterface narrow (org.omg.CORBA.Object obj) {...}
    public static AnInterface narrow (java.lang.Object obj) {...}
}

final public class AnInterfaceHolder implements Streamable {
    public AnInterface value;
    public AnInterfaceHolder () { }
    public AnInterfaceHolder (AnInterface initial) {...}

    ...
}

2.1.2.10. Servants

Where the mapping of the proxy selects the target type with transparency in mind, the mapping of the servant provides enough freedom in situations where strict typing constraints are not desirable. This is achieved by coupling servants to interfaces either by inheritance or by delegation.

C++.  The servant mapping starts with a reference counted servant base class. The reference counting of servants is distinct from the reference counting of proxies.

Servant Base Class
class ServantBase {
public:

    virtual ~ServantBase ();

    virtual InterfaceDef_ptr _get_interface () throw (SystemException);
    virtual Boolean _is_a (const char *logical_type_id) throw (SystemException);
    virtual Boolean _non_existent () throw (SystemException);

    virtual void _add_ref ();
    virtual void _remove_ref ();

    ...
}

An abstract C++ class is generated for each IDL interface, the servant implementation can inherit from this abstract class and implement its methods as necessary. Alternatively, templates can be used to tie the servant implementation to a type that inherits from the abstract class.

Servant Class Example
class POA_AnInterface : public virtual ServantBase {
public:

    virtual ... AnOperation (...) = 0;

    ...
}

template <class T> class POA_AnInterface_tie : public POA_AnInterface {
public:

    POA_AnInterface_tie (T &t) : _ptr (t) { }

    ...

    ... AnOperation (...) { return (_ptr->AnOperation (...); }
}

C++11.  The servant mapping starts with a servant base class.

Servant Base Class
class Servant {
public:

    virtual IDL::traits<CORBA::InterfaceDef>::ref_type _get_interface ();
    virtual bool _is_a (const std::string &logical_type_id);
    virtual bool _non_existent ();

    ...

protected:

    virtual ~Servant ();
}

An abstract C++ class is generated for each IDL interface, the servant implementation can inherit from this abstract class and implement its methods as necessary.

Servant Class Example
class _AnInterface_Servant_Base : public virtual Servant {
public:

    virtual ... AnOperation (...) = 0;

    ...
}

class AnInterface_Servant : public virtual CORBA::servant_traits<AnInterface>::base_type {
public:

    virtual ... AnOperation (...) override;
}

Java.  The servant mapping starts with a servant base class.

Servant Base Class
abstract public class Servant {
    final public Delegate _get_delegate () { ... }
    final public void _set_delegate (Delegate delegate) { ... }
    ...
}

An abstract Java class is generated for each IDL interface, the servant implementation can inherit from this class and implement its methods as necessary. Alternatively, delegation can be used to tie the servant implementation to a type that inherits from the abstract class.

Servant Class Example
abstract public class AnInterfacePOA implements AnInterfaceOperations {
    public AnInterface _this () { ... }
    ...
}

public class AnInterfacePOATie extends AnInterfacePOA {
    private AnInterfaceOperations _delegate;

    public AnInterfacePOATie (AnInterfaceOperations delegate)
    { _delegate = delegate; }

    public AnInterfaceOperations _delegate ()
    { return (_delegate); }

    public void _delegate (AnInterfaceOperations delegate)
    { _delegate = delegate; }

    public ... AnOperation (...) { return (_delegate.AnOperation (...); }
}

2.1.2.11. Value Types

C++.  The language lacks both dynamic type creation and instance state access. The mapping therefore implements both, the type creation by factories and the state access by accessor methods. Custom marshalling interface is available for situations where generated marshalling code based on accessor methods is not appropriate.

Value Mapping Example
class AValue : public virtual ValueBase {
public:

    virtual void ShortItem (Short) = 0;
    virtual Short ShortItem () const = 0;

    virtual void LongItem (Long) = 0;
    virtual Long LongItem () const = 0;

    ...

    virtual ... AnOperation (...) = 0;
}

class OBV_AValue : public virtual AValue {
public:

    virtual void ShortItem (Short) { ... };
    virtual Short ShortItem () const { ... };

    virtual void LongItem (Long) { ... };
    virtual Long LongItem () const { ... };

    ...

    virtual ... AnOperation (...) = 0;
}

class ValueFactoryBase {
private:

    virtual ValueBase *create_for_unmarshal () = 0;

    ...
}

class AValue_init : public ValueFactoryBase {
public:

    virtual AValue *AConstructor (...) = 0;

    ...
}

Java.  The language provides both dynamic type creation and instance state access. The mapping therefore only provides a custom marshalling interface for situations where generated marshalling code based on serialization is not appropriate.

2.1.2.12. Argument Passing

It is also worth noting some broader aspects of argument passing.

C++.  The language mapping attempts to minimize copying by preferring stack allocation to heap allocation whenever possible. The caller often allocates memory for values returned by the callee, otherwise stack allocation would not be possible. As an unfortunate complication, fixed size types and variable size types have to be distinguished.

The mapping for C++11 simplifies the argument passing rules. All primitive types are passed by value when input and by reference when output. All other types are passed by constant reference when input and by reference when output.

Java.  Since the language does not allow passing some types by reference, holder classes are generated to solve the need for mapping output arguments.

2.1.3. Object Adapter

The object adapter delivers requests to servants using a mapping from object ID values to servant references. An object ID is an opaque sequence of octets assigned to each object by the server. Incoming requests identify the target objects using their object ID.

The object adapter specification supports multiple configurations that govern the process of delivering requests to servants. Some configurations use an active object map to map object ID values to servant references. Other configurations use custom servant managers to determine the mapping. It is also possible to configure the threading model used to invoke servants. The configuration is set using policies.

Object Adapter Configuration

local interface POA {
  POA create_POA (in string adapter_name,
                  in POAManager manager,
                  in CORBA::PolicyList policies);

    ThreadPolicy create_thread_policy (in ThreadPolicyValue value);
    LifespanPolicy create_lifespan_policy (in LifespanPolicyValue value);
    ServantRetentionPolicy create_servant_retention_policy (in ServantRetentionPolicyValue value);
    RequestProcessingPolicy create_request_processing_policy (in RequestProcessingPolicyValue value);

    ...
};

local interface POAManager {
    enum State { HOLDING, ACTIVE, DISCARDING, INACTIVE };
    State get_state ();

    void activate () raises (AdapterInactive);
    void hold_requests (in boolean wait_for_completion) raises (AdapterInactive);
    void discard_requests (in boolean wait_for_completion) raises (AdapterInactive);

    void deactivate (in boolean etherealize_objects,
                     in boolean wait_for_completion);
};

The threading model configuration is restricted to general categories. A particular object adapter implementation can provide more detailed threading model configuration. Typical configurations include the single threaded model and the leader-follower thread pool model.

Thread Policy

Thread Policy Values. 

SINGLE_THREAD_MODEL

calls to servants and managers are serialized

MAIN_THREAD_MODEL

calls to servants are using single main thread

ORB_CTRL_MODEL

calls use arbitrary threading model

The object identity policies default to an automatically assigned system identity. Explicit configuration allows for custom identities, useful especially when object state is external, rather than encapsulated in the servant. Each servant can query the object identity associated with current request.

Object Identity Policies

ID Uniqueness Policy Values. 

UNIQUE_ID

servants have exactly one object ID

MULTIPLE_ID

servants have at least one object ID

ID Assignment Policy Values. 

USER_ID

object ID is assigned by application

SYSTEM_ID

object ID is assigned by object adapter

Implicit Activation Policy Values. 

IMPLICIT_ACTIVATION

assign object ID on demand

NO_IMPLICIT_ACTIVATION

do not assign object ID on demand

Object Activation

ObjectId activate_object (in Servant servant) raises (ServantAlreadyActive, WrongPolicy);

void activate_object_with_id (in ObjectId oid, in Servant servant)
raises (ObjectAlreadyActive, ServantAlreadyActive, WrongPolicy);

void deactivate_object (in ObjectId oid) raises (ObjectNotActive, WrongPolicy);

Object create_reference (in CORBA::RepositoryId ifc) raises (WrongPolicy);
Object create_reference_with_id (in ObjectId oid, in CORBA::RepositoryId ifc);

Object servant_to_reference (in Servant servant) raises (ServantNotActive, WrongPolicy);
Servant reference_to_servant (in Object reference) raises (ObjectNotActive, WrongAdapter, WrongPolicy);

Current Object Interface

local interface Current {
    POA get_POA () raises (NoContext);
    ObjectId get_object_id () raises (NoContext);
    Object get_reference () raises (NoContext);
    Servant get_servant () raises (NoContext);
};

A request can be delivered to a servant tracked in the active object map, a servant identified by one of the two servant manager types, or a default servant.

Servant Lookup Policies

Servant Retention Policy Values. 

RETAIN

keep track of active servants

NON_RETAIN

do not keep track of active servants

Request Processing Policy Values. 

USE_ACTIVE_OBJECT_MAP_ONLY

only deliver to tracked servants

USE_DEFAULT_SERVANT

alternatively deliver to default servant

USE_SERVANT_MANAGER

alternatively activate servants on demand

Servant Activator Interface

local interface ServantActivator : ServantManager {

    Servant incarnate (in ObjectId oid,
                       in POA adapter)
    raises (ForwardRequest);

    void etherealize (in ObjectId oid,
                      in POA adapter,
                      in Servant servant,
                      in boolean cleanup_in_progress,
                      in boolean remaining_activations};
};

Servant Locator Interface

local interface ServantLocator : ServantManager {

    native Cookie;

    Servant preinvoke (in ObjectId oid,
                       in POA adapter,
                       in CORBA::Identifier operation,
                       out Cookie cookie)
    raises (ForwardRequest);

    void postinvoke (in ObjectId oid,
                     in POA adapter,
                     in CORBA::Identifier operation,
                     in Cookie cookie,
                     in Servant servant);
};

A request forwarding mechanism supports creating object references whose lifetime exceeds that of the server.

Lifespan Policy

Lifespan Policy Values. 

TRANSIENT

object references have lifetime of object adapter

PERSISTENT

object references have potentially unlimited lifetime

Request Forward Exception

exception ForwardRequest {
    Object forward_reference;
};

2.1.4. Network Protocol

The network protocol is defined in two layers. The lower layer introduces the General Inter-ORB Protocol (GIOP), which defines the Common Data Representation (CDR), the message formats and the transport assumptions. The upper layer introduces the Internet Inter-ORB Protocol (IIOP), which specializes the lower layer for IP networks.

The Common Data Representation supports both byte orderings. Among interesting features are type codes, which serve to recursively describe the transported types where needed, and encapsulations, which serve to wrap already encoded data. Object references support multiple profiles, each profile describes one way to access the remote object.

2.1.5. Messaging

Synchronization Scope Policy

SYNC_NONE

SYNC_WITH_TRANSPORT

SYNC_WITH_SERVER

SYNC_WITH_TARGET

Routing Policy

ROUTE_NONE

ROUTE_FORWARD

ROUTE_STORE_AND_FORWARD

Asynchronous Messaging Mapping Example

Interface. 

interface StockManager {
    attribute string stock_exchange_name;
    boolean add_stock (in string symbol, in double quote);
    void remove_stock (in string symbol, out double quote) raises (InvalidStock);
};

Callback Mapping. 

void sendc_get_stock_exchange_name (
    in AMI_StockManagerHandler ami_handler);
void sendc_set_stock_exchange_name (
    in AMI_StockManagerHandler ami_handler,
    in string attr_stock_exchange_name);

void sendc_add_stock (
    in AMI_StockManagerHandler ami_handler,
    in string symbol,
    in double quote);

void sendc_remove_stock (
    in AMI_StockManagerHandler ami_handler,
    in string symbol);


interface AMI_StockManagerHandler : Messaging::ReplyHandler {
    void get_stock_exchange_name (
        in string ami_return_val);
    void get_stock_exchange_name_excep (
        in Messaging::ExceptionHolder excep_holder);

    void set_stock_exchange_name ();
    void set_stock_exchange_name_excep (
        in Messaging::ExceptionHolder excep_holder);

    void add_stock (in boolean ami_return_val);
    void add_stock_excep (
        in Messaging::ExceptionHolder excep_holder);

    void remove_stock (in double quote);
    void remove_stock_excep (
        in Messaging::ExceptionHolder excep_holder);
};

Poller Mapping. 

AMI_StockManagerPoller sendp_get_stock_exchange_name ();
AMI_StockManagerPoller sendp_set_stock_exchange_name (
    in string attr_stock_exchange_name);

AMI_StockManagerPoller sendp_add_stock (
    in string symbol, in double quote);
AMI_StockManagerPoller sendp_remove_stock (
    in string symbol);


valuetype AMI_StockManagerPoller : Messaging::Poller {
    void get_stock_exchange_name (
        in unsigned long timeout,
        out string ami_return_val);
    void set_stock_exchange_name (
        in unsigned long timeout);

    void add_stock (
        in unsigned long timeout,
        out boolean ami_return_val);

    void remove_stock (
        in unsigned long timeout,
        out double quote) raises (InvalidStock);
};

2.1.6. Components

Component Features

attributes

denote configurable properties

supported interface

inherited in all interfaces

facets

interfaces provided to the outside

receptacles

interfaces required from the outside

sources

events produced to the outside

sinks

events consumed from the outside

Component Definition Example

module DiningPhilosophers {
  interface IFork {
    void pick_up () raises (ForkNotAvailable);
    void release ();
  };

  component AFork {
    provides IFork fork;
  };

  eventtype PhilosopherStatus {
      public string name;
      public PhilosopherState state;
      public boolean has_left_fork;
      public boolean has_right_fork;
  };

  component APhilosopher {

    attribute string name;

    // Receptacles for forks
    uses Fork left;
    uses Fork right;

    // Source for status
    publishes PhilosopherStatus status;
  };

  component AnObserver {
    // Sink for status
    consumes PhilosopherStatus status;
  };

  ...
};

Navigation Interfaces

module Components {
    typedef string FeatureName;
    typedef sequence<FeatureName> NameList;

    valuetype PortDescription {
        public FeatureName name;
        public CORBA::RepositoryId type_id;
    };

  ...
    valuetype FacetDescription : PortDescription {
        public Object facet_ref;
    };
    typedef sequence<FacetDescription> FacetDescriptions;

    interface Navigation {
        FacetDescriptions get_all_facets ();
        Object provide_facet (in FeatureName name) raises (InvalidName);
        FacetDescriptions get_named_facets (in NameList names) raises (InvalidName);

    ...
    };

    ...
    valuetype PublisherDescription : PortDescription {
        public SubscriberDescriptions consumers;
    };
    typedef sequence<PublisherDescription> PublisherDescriptions;

    valuetype ConsumerDescription : PortDescription {
        public EventConsumerBase consumer;
    };
    typedef sequence<ConsumerDescription> ConsumerDescriptions;

    PublisherDescriptions get_all_publishers ();
    PublisherDescriptions get_named_publishers (in NameList names) raises (InvalidName);

    ConsumerDescriptions get_all_consumers ();
    ConsumerDescriptions get_named_consumers (in NameList names) raises (InvalidName);

    ...
};

Assembly Interfaces

uses AnInterface AReceptacle;
consumes AnEvent ASink;
void connect_AReceptacle (in AnInterface connection)
    raises (AlreadyConnected, InvalidConnection);
AnInterface disconnect_AReceptacle ()
    raises (NoConnection);
AnInterface get_connection_AReceptacle ();
AnEventConsumer get_consumer_ASink ();
module Components {
    ...

    interface Receptacles {
        Cookie connect (in FeatureName name, in Object connection)
            raises (InvalidName, InvalidConnection, AlreadyConnected, ExceededConnectionLimit);
        Object disconnect (in FeatureName name, in Cookie ck)
            raises (InvalidName, InvalidConnection, CookieRequired, NoConnection);
        ConnectionDescriptions get_connections (in FeatureName name)
            raises (InvalidName);

        ...
    };

    ...
    valuetype Cookie {
        private CORBA::OctetSeq cookieValue;
    };

    valuetype SubscriberDescription {
        public Cookie ck;
        public EventConsumerBase consumer;
    };
    typedef sequence<SubscriberDescription> SubscriberDescriptions;

    interface Events {
        void connect_consumer (in FeatureName emitter_name, in EventConsumerBase consumer)
            raises (InvalidName, AlreadyConnected, InvalidConnection);
        EventConsumerBase disconnect_consumer (in FeatureName source_name)
            raises (InvalidName, NoConnection);
        EventConsumerBase get_consumer (in FeatureName sink_name) raises (InvalidName);

        Cookie subscribe (in FeatureName publisher_name, in EventConsumerBase subscriber)
            raises (InvalidName, InvalidConnection, ExceededConnectionLimit);
        EventConsumerBase unsubscribe (in FeatureName publisher_name, in Cookie ck)
            raises (InvalidName, InvalidConnection);

        ...
    };

    ...
};

2.1.7. References

  1. Remedy IT: CORBA Programmers Guide. http://www.remedy.nl/opensource/corbapg.html

  2. OMG: CORBA Interface Definition Language Specifications. https://www.omg.org/spec/category/interface-definition-language

  3. OMG: CORBA Language Mapping Specifications. https://www.omg.org/spec/category/language-mapping

  4. OMG: CORBA Platform Specifications. https://www.omg.org/spec/category/corba-platform