[Up: Implementation Overview]
[Previous: BOA] [Next: IDL Compiler]

Subsections

- Architecture
- Policies
- Example
- Using a Servant Manager
- Persistent Objects
- Reference Counting

#
POA

The Basic Object Adapter provides a bare minimum of functionality to server applications. As a consequence, many ORBs added custom extensions to the BOA to support more complex demands upon an object adapter, making server implementations incompatible among different ORB vendors. In CORBA 2.2, the new Portable Object Adapter was introduced. It provides a much-extended interface that addresses many needs that were wished for, but not available with the original BOA specification. POA features include:

- Support for transparent activation of objects. Servers can export object references for not-yet-active servants that will be incarnated on demand.
- Allow a single servant to support many object identities.
- Allow many POAs in a single server, each governed by its own set of policies.
- Delegate requests for non-existent servants either to a default servant, or ask a servant manager for an appropriate servant.

These features, make the POA much more powerful than the BOA and should fulfill most server applications' needs. As an example, object references for some million entries in a database can be generated, which are all implemented by a single default servant.

##
Architecture

The general idea is to have each server contain a hierarchy of POAs. Only the Root POA is created by default; a reference to the Root POA is obtained using the `resolve_initial_references()` operation on the ORB. New POAs can be created as the child of an existing POA, each with its own set of policies.

Each POA maintains an Active Object Map that maps all objects that have been activated in the POA to a servant. For each incoming request, the POA looks up the object reference in the Active Object Map and tries to find the responsible servant. If none is found, the request is either delegated to a default servant, or a servant manager is invoked to activate or locate an appropriate servant.

Associated with each POA is a POA Manager object. A POA Manager can control one or many POAs. For each incoming request to an object, the POA Manager's state is checked, which can be one of the following:

Active
  Requests are performed immediately.
Holding
  Incoming requests are queued. This is the initial state of a POA Manager; to perform requests, the POA Manager must be explicitely set to the Active state.
Discarding
  Requests are discarded. Clients receive a `TRANSIENT` exception.
Inactive
  This is the ``final'' state of a POA Manager, which is entered prior to destruction of the associated POAs. Clients receive an `OBJ_ADAPTER` exception.

Before continuing, we should more precisely define a few terms that have already been freely used.

Object Reference
  On the client side, an object reference encapsulates the identity of a distinct abstract object. On the server side, an object reference is composed of the POA identity in which the object is realized, and a Object Id that uniquely identifies the object within the POA.
Object Id
  An Object Id is an opaque sequence of octets. Object Ids can be either system generated (the POA assigns a unique Id upon object activation), or user generated (the user must provide an Id upon object activation). The object's Object Id cannot be changed through the object's lifetime.

  In many cases, object references and Object Id can be used synonymously, since an object reference is just an Object Id with opaque POA-added ``internal'' information.

Servant
  A servant provides the implementation for one or more object references. In the C++ language mapping, a servant is an instance of a C++ class that inherits from `PortableServer::ServantBase`. This is true for dynamic skeleton implementations (DSI), or for classes that inherit from IDL-generated skeletons.

  The process of associating a servant with an Object Id is called activation and is performed using POA methods. A servant can be activated more than once (to serve many different Object Ids) and can be activated in many POAs. After activation, object references can be obtained using other POA methods.

  Servants are not objects and do not inherit from `CORBA::Object`. It is illegal to perform operations directly upon a servant - all invocations must be routed through the ORB. Also, memory management of servants is entirely left to the user. POAs keep only a pointer to a servant, so they must not be deleted while being activated.

Server
  ``Server'' refers to a complete process in which servants exist. A server can contain one or more POAs, each of which can provide zero, one or more active servants. Each active servant can then serve one or more object references.

##
Policies

We have already mentioned the policies that control various aspects of POA behaviour. POA policies do not change over the POA's lifetime. When creating a new POA as a child of an existing POA, policies are not inherited from the parent, but instead each POA is assigned a set of default policies if not explicitely defined.

Thread Policy
  `ORB_CTRL_MODEL`
    (default)
    Invocations are performed as scheduled by the ORB. Potentially, many upcalls are perfomed simultaneously.
  `SINGLE_THREAD_MODEL`
    Invocations are serialized. At most a single upcall is performed at any time.
  Non-reentrant servants should only be activated in POAs with the `SINGLE_THREAD_MODEL` policy.

  As the current version of MICO is not multithreaded, this policy is not yet evaluated.

Lifespan Policy
  `TRANSIENT`
    (default)
    Objects activated in this POA cannot outlive the server process.
  `PERSISTENT`
    Objects can outlive the server process
Id Uniqueness Policy
  `UNIQUE_ID`
    (default)
    Servants can be activated at most once in this POA.
  `MULTIPLE_ID`
    Servants can be activated more than once in this POA and can therefore serve more than one object reference.
Id Assignment Policy
  `SYSTEM_ID`
    (default)
    Object Ids are assigned by the POA upon object activation.
  `USER_ID`
    Upon activation, each servant must be provided with a unique Id by the user.
Servant Retention Policy
  `RETAIN`
    (default)
    The POA maintains a map of active servants (the Active Object Map).
  `NON_RETAIN`
    The POA does not maintain an Active Object Map.
Request Processing Policy
  `USE_ACTIVE_OBJECT_MAP_ONLY`
    (default)
    To process an incoming request, the object reference is looked up in the Active Object Map only. If no active servant serving the reference is found, the request is rejected, and an `OBJECT_NOT_EXIST` exception is returned.
  `USE_DEFAULT_SERVANT`
    The object reference is looked up in the Active Object Map first. If no active servant is found to serve the reference, the request is delegated to a default servant.
  `USE_SERVANT_MANAGER`
    The object reference is looked up in the Active Object Map first. If no active servant is found to serve the reference, a servant manager is invoked to locate or incarnate an appropriate servant.
Implicit Activation Policy
  `IMPLICIT_ACTIVATION`
    If an inactive servant is used in a context that requires the servant to be active, the servant is implicitly activated.
  `NO_IMPLICIT_ACTIVATION`
    (default)
    It is an error to use an inactive servant in a context that requires an active servant.

The Root POA has the `ORB_CTRL_MODEL`, `TRANSIENT`, `UNIQUE_ID`, `SYSTEM_ID`, `RETAIN`, `USE_ACTIVE_OBJECT_MAP_ONLY` and `IMPLICIT_ACTIVATION` policies.

## Example

As an example, let's write a simple POA-based server. You can find the full code in the `demo/poa/hello-1` directory in the MICO distribution. Imagine a simple IDL description in the file ``hello.idl'':

      interface HelloWorld {
        void hello ();
      };

The first step is to invoke the IDL to C++ compiler in a way to produce skeleton classes that use the POA:

      idl hello.idl

The IDL compiler will generate POA-baed skeletons by default. Next, we rewrite the server.

     1: // file server.cc
     2:
     3: #include "hello.h"
     4:
     5: class HelloWorld_impl : virtual public POA_HelloWorld
     6: {
     7:   public:
     8:     void hello() { printf ("Hello World!\n"); };
     9: };
    10:
    11:
    12: int main( int argc, char *argv[] )
    13: {
    14:   CORBA::ORB_var orb = CORBA::ORB_init (argc, argv, "mico-local-orb");
    15:   CORBA::Object_var poaobj = orb->resolve_initial_references ("RootPOA");
    16:   PortableServer::POA_var poa = PortableServer::POA::_narrow (poaobj);
    17:   PortableServer::POAManager_var mgr = poa->the_POAManager();
    18:
    19:   HelloWorld_impl * servant = new HelloWorld_impl;
    20:
    21:   PortableServer::ObjectId_var oid = poa->activate_object (servant);
    22:
    23:   mgr->activate ();
    24:   orb->run();
    25:
    26:   poa->destroy (TRUE, TRUE);
    27:   delete servant;
    28:   return 0;
    29: }

The object implementation does not change much with respect to a BOA-based one, the only difference is that `HelloWorld_impl` does not inherit from the BOA-based skeleton `HelloWorld_skel` any more, but from the POA-based skeleton `POA_HelloWorld`.

In `main()`, we first initialize the ORB, then we obtain a reference to the Root POA (lines 15-16) and to its POA Manager (line 17).

Then, we create an instance of our server object. In line 21, the servant is activated. Since the Root POA has the `SYSTEM_ID` policy, a unique Object Id is generated automatically and returned. At this point, clients can use the MICO binder to connect to the HelloWorld object.

However, client invocations upon the HelloWorld object are not yet processed. The Root POA's POA Manager is created in the holding state, so in line 23, we transition the POA Manager, and therefore the Root POA, to the active state. We then enter the ORB's event loop in 24.

In this example, `run()` never returns, because we don't provide a means to shut down the ORB. If that ever happened, lines 26-27 would first destroy the Root POA. Since that deactivates our active HelloWorld object, we can then safely delete the servant.

Since the Root POA has the `IMPLICIT_ACTIVATION` policy, we can also use several other methods to activate the servant instead of `activate_object()`. We could, for example, use `servant_to_reference()`, which first implicitly activates the inactive servant and then returns an object reference pointing to the servant. Or, we could invoke the servant's inherited `_this()` method, which also implicitly activates the servant and returns an object reference.

## Using a Servant Manager

While the previous example did introduce the POA, it did not demonstrate any of its abilities - the example would have been just as simple using the BOA.

As a more complex example, we want to show a server that generates ``virtual'' object references that point to non-existent objects. We then provide the POA with a servant manager that incarnates the objects on demand.

We continue our series of ``Account'' examples. We provide the implementation for a Bank object with a single ``create'' operation that opens a new account. However, the Account object is not put into existence at that point, we just return a reference that will cause activation of an Account object when it is first accessed. This text will only show some code fragments; find the full code in the `demo/poa/account-2` directory.

The implementation of the Account object does not differ from before. More interesting is the implementation of the Bank's `create` operation:

      Account_ptr
      Bank_impl::create ()
      {
        CORBA::Object_var obj = mypoa->create_reference ("IDL:Account:1.0");
        Account_ptr aref = Account::_narrow (obj);
        assert (!CORBA::is_nil (aref));
        return aref;
      }

The `create_reference()` operation on the POA does not cause an activation to take place. It only creates a new object reference encapsulating information about the supported interface and a unique (system-generated) Object Id. This reference is then returned to the client.

Now, when the client invokes an operation on the returned reference, the POA will first search its Active Object Map, but will find no servant to serve the request. We therefore implement a servant manager, which will be asked to find an appropriate implementation.

There are two types of servant managers: a `Servant Activator` activates a new servant, which will be retained in the POA's Active Object Map to serve further requests on the same object. A `Servant Locator` is used to locate a servant for a single invocation only; the servant will not be retained for future use. The type of servant manager depends on the POA's Servant Retention policy.

In our case, we use a servant activator, which will incarnate and activate a new servant whenever the account is used first. Further operations on the same object reference will use the already active servant. Since the `create_reference()` operation uses a unique Object Id each time it is called, one new servant will be incarnated for each Account - this represents the BOA's Unshared activation mode.

A servant activator provides two operations, `incarnate` and `etherealize`. The former one is called when a new servant needs to be incarnated to serve a previously unknown Object Id. `etherealize` is called when the servant is deactivated (for example in POA shutdown) and allows the servant manager to clean up associated data.

      class AccountManager : public virtual POA_PortableServer::ServantActivator
      { /* declarations */ };

      PortableServer::Servant
      AccountManager::incarnate (/* params */)
      {
        return new Account_impl;
      }

      void
      AccountManager::etherealize (PortableServer::Servant serv,
                                   /* many more params */)
      {
        delete serv;
      }

Our servant activator implements the `POA_PortableServer::ServantActivator` interface. Since servant managers are servants themselves, they must be activated like any other servant (see below).

The `incarnate` operation has nothing to do but to create a new Account servant. `incarnate` receives the current POA and the requested Object Id as parameters, so it would be possible to perform special initialization based on the Object Id that is to be served.

`etherealize` is just as simple, and deletes the servant. In ``real life'', the servant manager would have to make sure that the servant is not in use anywhere else before deleting it. Here, this is guaranteed by our program logic.

The `main()` code is a little more extensive than before. Because the Root POA has the `USE_ACTIVE_OBJECT_MAP_ONLY` policy and does not allow a servant manager, we must create our own POA with the `USE_SERVANT_MANAGER` policy.

      CORBA::ORB_var orb = CORBA::ORB_init (argc, argv, "mico-local-orb");
      CORBA::Object_var poaobj = orb->resolve_initial_references ("RootPOA");
      PortableServer::POA_var poa = PortableServer::POA::_narrow (poaobj);
      PortableServer::POAManager_var mgr = poa->the_POAManager();

      CORBA::PolicyList pl;
      pl.length(1);
      pl[0] = poa->
        create_request_processing_policy (PortableServer::USE_SERVANT_MANAGER);
      PortableServer::POA_var mypoa = poa->create_POA ("MyPOA", mgr, pl);

Note that we use the Root POA's POA Manager when creating the new POA. This means that the POA Manager has now control over both POAs, and changing its state affects both POAs. If we passed `NULL` as the second parameter to `create_POA()`, a new POA Manager would have been created, and we would have to change both POA's states separately.

We can now register the servant manager.

      AccountManager * am = new AccountManager;
      PortableServer::ServantManager_var amref = am->_this ();
      mypoa->set_servant_manager (amref);

After creating an instance of our servant manager, we obtain an object reference using the inherited `_this()` method. This also implicitly activates the servant manager in the Root POA.

      Bank_impl * micocash = new Bank_impl (mypoa);
      PortableServer::ObjectId_var oid = poa->activate_object (micocash);
      mgr->activate ();
      orb->run();

Now the only thing left to do is to activate a Bank object, to change both POAs to the active state, and to enter the ORB's event loop.

## Persistent Objects

Our previous examples used ``transient'' objects which cannot outlive the server process they were created in. If you write a server that activates a servant and export its object reference, and then stop and re-start the server, clients will receive an exception that their object reference has become invalid.

In many cases it is desirable to have persistent objects. A persistent object has an infinite lifetime, not bound by the process that implements the object. You can kill and restart the server process, for example to save resources while it is not needed, or to update the implementation, and the client objects will not notice as long as the server is running whenever an invocation is performed.

An object is persistent if the servant that implements them is activated in a POA that has the `PERSISTENT` lifespan policy.

As an example, we will expand our Bank to create persistent accounts. When the server goes down, we want to write the account balances to a disk file, and when the server is restarted, the balances are read back in. To accomplish this, we use a persistent POA to create our accounts in. Using a servant manager provides us with the necessary hooks to save and restore the state: when etherealizing an account, the balance is written to disk, and when incarnating an account, we check if an appropriately named file with a balance exists.

We also make the Bank itself persistent, but use a different POA to activate the Bank in. Of course, we could use the Accounts' POA for the Bank, too, but then, our servant manager would have to discriminate whether it is etherealizing an Account or a Bank: using a different POA comes more cheaply.

The implementation of the Account object is the same as in the previous examples. The Bank is basically the same, too. One change is that the `create` operation has been extended to activate accounts with a specific Object Id - we will use an Account's Object Id as the name for the balance file on disk.

We also add a `shutdown` operation to the Bank interface, which is supposed to terminate the server process. This is accomplished simply by calling the ORB's shutdown method:

      void
      Bank_impl::shutdown (void)
      {
        orb->shutdown (TRUE);
      }

Invoking `shutdown()` on the ORB first of all causes the destruction of all object adapters. Destruction of the Account's POA next causes all active objects - our accounts - to be etherealized by invoking the servant manager. Consequently, the servant manager is all we need to save and restore our state.

One problem is that the servant manager's `etherealize()` method receives a `PortableServer::Servant` value. However, we need access to the implementation's type, `Account_impl*`, to query the current balance. Since CORBA does not provide narrowing for servant types, we have to find a solution on our own. Here, we use an STL map mapping the one to the other:4.14

      class Account_impl;
      typedef map<PortableServer::Servant,
        Account_impl *,
        less<PortableServer::Servant> > ServantMap;
      ServantMap svmap;

When incarnating an account, we populate this map; when etherealizing the account, we can retrieve the implementation's pointer.

      PortableServer::Servant
      AccountManager::incarnate (/* params */)
      {
        Account_impl * account = new Account_impl;
        CORBA::Long amount = ...  // retrieve balance from disk
        account->deposit (amount);

        svmap[account] = account; // populate map
        return account;
      }

      void
      AccountManager::etherealize (PortableServer::Servant serv,
                                   /* many more params */)
      {
        ServantMap::iterator it = svmap.find (serv);
        Account_impl * impl = (*it).second;
        ... // save balance to disk
        svmap.erase (it);
        delete serv;
      }

Please find the full source code in the `demo/poa/account-3` directory.

One little bit of magic is left to do. Persistent POAs need a key, a unique ``implementation name'' to identify their objects with. This name must be given using the `-POAImplName` command line option:4.15

      ./server -POAImplName Bank

Now we have persistent objects, but still have to start up the server by hand. It would be much more convenient if the server was started automatically. This can be achieved using the MICO Daemon (`micod`) (see section 4.3.2).

For POA-based persistent servers, the implementation repository entry must use the ```poa`'' activation mode, for example

      imr create Bank poa ./server IDL:Bank:1.0

The second parameter to imr, `Bank`, is the same implementation name as above; it must be unique within the implementation repository. If a persistent POA is in contact with the MICO Daemon, object references to a persistent object, when exported from the server process, will not point directly to the server but to the MICO Daemon. Whenever a request is received by `micod`, it checks if your server is running. If it is, the request is simply forwarded, else a new server is started.

Usually, the first instance of your server must be started manually for bootstrapping, so that you have a chance to export object references to your persistent objects. An alternative is to use the MICO Binder: the `IDL:Bank:1.0` in the command line above tells `micod` that `bind()` requests for this repository id can be forwarded to this server - after starting it.

With POA-based persistent objects, you can also take advantage of the ``iioploc:'' addressing scheme that is introduced by the Interoperable Naming Service. Instead of using a stringified object reference, you can use a much simpler, URL-like scheme. The format for an iioploc address is

      iioploc://<host>:<port>/<object-key>

`host` and `port` are as given with the `-ORBIIOPAddr` command-line option, and the object key is composed of the implementation name, the POA name and the Object Id, separated by slashes. So, if you start a server using

      ./server -ORBIIOPAddr inet:thishost:1234 -POAImplName MyService

create a persistent POA with the name ``MyPOA'', and then activate an object using the ``MyObject'' Object Id, you could refer to that object using the IOR

      iioploc://thishost:1234/MyService/MyPOA/MyObject

These ``iioploc'' addresses are understood and translated by the `string_to_object()` method and can therefore be used wherever a stringified object reference can be used.

For added convenience, if the implementation name, the POA name and the Object Id are the same, they are collapsed into a single string. An example for this is the NameService implementation, which uses the ``NameService'' implementation name. The root naming context is then activated in the ``NameService'' POA using the ``NameService'' ObjectId. Consequently, the NameService can be addressed using

      iioploc://<host>:<port>/NameService

Please see the Interoperable Naming Service specification for more details.

## Reference Counting

With the POA, implementations do not inherit from `CORBA::Object`. Consequently, memory management for servants is the user's responsibility. Eventually, a servant must be deleted with C++'s `delete` operator, and a user must know when a servant is safe to be deleted - deleting a servant that is still known to a POA leads to undesired results.

CORBA 2.3 addresses this problem and introduces reference counting for servants. However, to maintain compatibility, this feature is optional and must be explicitly activated by the user. This is done by adding `POA_PortableServer::RefCountServantBase` as a base class of your implementation:

    class HelloWorld_impl :
      virtual public POA_HelloWorld
      virtual public PortableServer::RefCountServantBase
    {
      ...
    }

This activates two new operations for your implementation, `_add_ref()` and `_remove_ref()`. A newly constructed servant has a reference count of 1, and it is deleted automatically once its reference count drops to zero. This way, you can, for example, forget about your servant just after it has been created and activated:

      HelloWorld_impl * hw = new HelloWorld_impl;
      HelloWorld_var ref = hw->_this(); // implicit activation
      hw->_remove_ref ();

During activation, the POA has increased the reference count for the servant, so you can remove your reference immediately afterwards. The servant will be deleted automatically once the object is deactivated or the POA is destroyed. Note, however, that once you introduce reference counting, you must keep track of the references yourself: All POA operations that return a servant (i.e. `id_to_servant()` will increase the servants' reference count. The `PortableServer::ServantBase_var` class is provided for automated reference counting, acting the same as `CORBA::Object_var` does for Objects.

[Previous: BOA] [Next: IDL Compiler]
[Up: Implementation Overview]