Next: Interface Type Checking Up: No Title Previous: The omniORB2 API

# The Basic Object Adaptor (BOA)

This chapter describes the BOA implementation in omniORB2. The CORBA specification defines the Basic Object Adaptor as the entity that mediates between object implementations and the ORB. Unfortunately, the BOA specification is incomplete and does not address the multi-threading issues appropriately. The end result is that different ORB vendors implement different extensions to their BOAs. Worse, the implementation of the operations defined in the specification are different in different ORBs. Recently, a new Object Adaptor specification (the Portable Object Adaptor- POA) has been adopted and will replace the BOA as the standard Object Adaptor in CORBA. The new specification recognises the compatibility problems of BOA and recommends that all BOAs should be considered propriety extensions. OmniORB2 will support POA in future releases. Until then, you have to use the BOA to attach object implementations to the ORB.

The rest of this chapter describes the interface of the BOA in detail. It is important to recognise that the interface described below is omniORB2 specific and hence the code using this interface is unlikely to be portable to other ORBs.

Unless it is stated otherwise, the term ``object'' will be used below to refer to object implementations. This should not be confused with ``object references'' which are handles held by clients.

# BOA Initialisation

It takes two steps to put the BOA into service. The BOA has to be initialised using `BOA_init` and activated using `impl_is_ready`.

`BOA_init` is a member of the `CORBA::ORB` class. Its signature is:

    BOA_ptr BOA_init(int & argc,
                     char ** argv,
                     const char * boa_identifier);

Typically, it is used in the startup code as follows:

    CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2");   // line 1
    CORBA::BOA_ptr boa = orb->BOA_init(argc,argv,"omniORB2_BOA"); // line 2

The `argv` parameters may contain BOA options. These options will be removed from the `argv` list when `BOA_init` returns. Other parameters in `argv` will remain. The supported options are:

-BOAiiop_port `<port number (0-65535)>`
  Use the port number to receive IIOP requests. This option can be specified multiple times in the command line and the BOA would be initialised to listen on all of the ports.

-BOAid `<id (string)>`
  If this option is used the id must be ``omniORB2_BOA''.

If the third argument of `BOA_init` is non-nil, it must be the string constant ``omniORB2_BOA''. If the argument is nil, -BOAid must be present in `argv`.

If there is any problem in the initialisation process, a `CORBA::INITIALIZE` exception would be raised.

To register an object with the BOA, the method `_obj_is_ready` should be called with the return value of `BOA_init` as the argument.

`BOA_init` is thread-safe. It can be called multiple times and the same `BOA_ptr` will be returned. However, only the `argv` in the first call will be scanned, the argument is ignored in subsequent calls.

`BOA_init` returns a pseudo object of type `CORBA::BOA_ptr`. Similar to `CORBA::Object_ptr`, the pointer can be managed using `CORBA::BOA_var`, `BOA::_duplicate` and `CORBA::release`. The pointer can be tested using `CORBA::is_nil` which returns true if the pointer is equivalent to the return value of `BOA::_nil`.

After `BOA_init` is called, objects can be registered. However, incoming IIOP requests would not be despatched until `impl_is_ready` is called.

    class BOA {
    public:
       impl_is_ready(CORBA::ImplementationDef_ptr p = 0,
                     CORBA::Boolean NonBlocking = 0);
    };

One of the common pitfall in using the BOA is to forget to call impl_is_ready. Until this call returns, there is no thread listening on the port from which IIOP requests are received. The remote client may hang because of this.

When `impl_is_ready` is called with no argument. The calling thread would be blocked indefinitely in the function until `impl_shutdown` (see below) is called. The thread that is calling `impl_is_ready` is not used by the BOA to perform its internal functions. The BOA has its own set of threads to process incoming requests and general housekeeping. Therefore, it is not necessary to have a thread blocked in the call if it can be put into use elsewhere. For example, the main thread may call `impl_is_ready` once in non-blocking mode (see below) and then enter the event loop to handle the GUI frontend.

If non-blocking behaviour is needed, the `NonBlocking` argument should be set to 1. For instance, if you creates a callback object, you might call impl_is_ready in non-blocking mode to tell the BOA to start receiving IIOP requests before sending the callback object to the remote object. The first argument `ImplementationDef_ptr` is ignored by the BOA. Just set the argument to nil.

`impl_is_ready` is thread safe and can be called multiple times. Multiple threads can be blocked in `impl_is_ready`.

# Object Registration

Once the BOA is initialised, objects can be registered. The purpose of object registration is to let the BOA know of the existence of the object and to dispatch requests for the object as upcalls into the object.

To register an object, the `_obj_is_ready` function should be called. `_obj_is_ready` is a member function of the implementation skeleton class. The function should be called only once for each object. The call should be made only after the object is fully initialised.

The member function `obj_is_ready` of the BOA may also be used to register an object. However, this function has been superseded by `_obj_is_ready` and should not be used in new application code.

# Object Disposal

Once an object is registered, it is under the management of the BOA. To remove the object from the BOA and to delete it (when it is safe to do so), the `_dispose` function should be called. `_dispose` is a member function of the implementation skeleton class. The function should be called only once for each object.

Notice the asymmetry in object instantiation and destruction. To instantiate an object, the application code has to call the new operator. To remove the object, the application should never call the delete operator on the object directly.

At the time the `_dispose` call is made, there may be other threads invoking on the object, the BOA ensures that all these calls are completed before removing the object from its internal tables and calling the delete operator.

Internally, the BOA keeps a reference count on each object. Initially, the reference count is 0. After a call to `_obj_is_ready`, the reference count is 1. The BOA increases the reference count by 1 before an upcall into the object is made. The count is decreased by 1 when the upcall returns. `_dispose` decreases the reference count by 1, if the reference count is 0, the delete operator is called. If the count is non-zero, the object is marked as disposed. The object will be deleted when the reference count eventually goes to zero.

The reference count is also increased by 1 for each object reference held in the same address space. Hence, the delete operator will not be called when there are outstanding object references in the same address space. To ensure that an object is deleted, all its object references in the same address space should be released using `CORBA::release`.

Unlike colocated object references, references held by clients in other address spaces would not prevent the deletion of objects. If these clients invoke on the object after it is disposed, the system exception INV_OBJREF would be raised. The difference in semantics is an undesirable side-effect of the current BOA implementation. In future, colocated references will have the same semantics as remote references, i.e. their presence will not delay the deletion of the objects.

Instead of `_dispose`, it may be useful to have a method to deactivate the object but not deleting it. This feature is not supported in the current BOA implementation.

# BOA Shutdown

The BOA can be withdrawn from service using member functions `impl_shutdown` and `destroy`.

    class BOA {
    public:
       void impl_shutdown();
       void destroy();
    };

`impl_shutdown` and `destroy` are the inverse of `impl_is_ready` and `BOA_init` respectively.

`impl_shutdown` deactivates the BOA. When the call returns, all the internal threads and network connections will be shutdown. Any thread blocking in `impl_is_ready` would be unblocked. After the call, no request from other address spaces will be processed. In other words, the BOA will be in the same state as it was in before `impl_is_ready` was called. For example, a remote client may hang if it tries to connect to the server after `impl_shutdown` was called because no thread is listening on the IIOP port.

`impl_shutdown` does not wait for incoming requests to complete before it closes the network connections. The remote clients will see the network connections shutdown and the replies may not reach them even if the upcalls have been completed. Therefore, if the application is to define an operation in an IDL interface to shutdown the BOA, the operation should be defined as an oneway operation.

`impl_shutdown` is thread-safe and can be called multiple times. The call is silently ignored if the BOA has already been shutdown. After `impl_shutdown` is called, the BOA can be reactivated by another call to `impl_is_ready`.

It should be noted that `impl_shutdown` does not affect outgoing network connections. That is, clients in the same address space will still be able to make calls to objects in other address spaces.

While remote requests are not delivered after `impl_shutdown` is called, the current implementation does not stop colocated clients from calling the objects. In future, colocated clients will exhibit the same behaviour as remote clients.

`destroy` permanently removed the BOA. This function will call `impl_shutdown` implicitly if it has not been called. When this call returns, the IIOP port(s) held by the BOA will be freed. Remote clients will see their requests refused by the operating system when they try to open a connection to the IIOP port(s).

After `destroy` is called, the BOA should not be used. If there is any objects still registered with the BOA, the objects should not be invoked afterwards. The objects are not disposed. Invoking on the objects after `destroy` would result in undefined behaviour. Initialisation of another BOA using `BOA_init` is not supported. The behaviour of `BOA_init` after this call is undefined.

# Unsupported functions

The following member functions are not implemented. Calling these functions do not have any effect.

- `Object_ptr create(...)`
- `ReferenceData* get_id(Object_ptr)`
- `Principal_ptr get_principal(Object_ptr,Environment_ptr)`
- `void change_implementation(Object_ptr, ImplementationDef_ptr)`
- `void deactivate_impl(ImplementationDef_ptr)`
- `void deactivate_obj(Object_ptr)`

# Loading Objects On Demand

Since 2.5.0, there is limited support for loading objects on demand. An application can register a handler for loading objects dynamically. The handler should have the signature `omniORB::loader::mapKeyToObject_t`:

      namespace omniORB {
        ...
        class loader {
        public:
          typedef CORBA::Object_ptr (*mapKeyToObject_t) (const objectKey& key);
          static void set(mapKeyToObject_t NewKeyToObject);
        };
      };

When the ORB cannot locate the target object in this address space, it calls the handler with the object key of the target. The handler is expected to instantiate the object, either in this address space or in another address space, and returns the object reference to the newly instantiated object. The ORB will then reply with a LOCATION_FORWARD message to instruct the client to retry using the object reference returned by the handler. When the handler returns, the ORB assumes ownership of the returned value. It will call CORBA::release() on the returned value when it has finished with it.

The handler may be called concurrently by multi-threads. Hence it must be thread-safe.

If the handler cannot load the target object, it should return CORBA::Object::_nil(). The object will be treated as non-existing.

The application registers the handler with the ORB at runtime using omniORB::loader::set(). This function is not thread-safe. Calling this function again will replace the old handler with the new one.

Next: Interface Type Checking Up: No Title Previous: The omniORB2 API