Introduction - Improving the Server

In this section we will improve over the simple server described before. We will discuss how to use POA policies to assign our own object ids.

In our previous example we used two fields of the Quoter_Stock_Factory_i to represent the stock quotes. If we wish to create hundreds of stock objects this approach would not scale. We need to use some collection to keep track of the Stock objects, possibly indexed by the stock symbol. One solution is to use an STL map or something similar, but this is clearly wasteful. After all, the ORB is also keeping a collection of objects, indexed by the object ids. If we only could choose the ids ourselves, then our problem would be solved. The good news is that the POA allows this, the bad news is that we must create a child POA for that. Why? Because the Root POA ids are assigned by the ORB, and we don't want to conflict with those. Furthermore, creating a separate POA is easier to manage, as multiple components of the application can get their own POA, that they can treat as a private namespace.

Child POA creation

As before, we gain access to the RootPOA:

    CORBA::Object_var poa_object =
      orb->resolve_initial_references ("RootPOA");
    PortableServer::POA_var poa =
      PortableServer::POA::_narrow (poa_object.in ());

Now we create the policies for the child poa. In this case we want the USER_ID policy so we can assign our own ids. We also want the NO_IMPLICIT_ACTIVATION policy, to have more control over additions to our POA. The POA has more policies that we can control, but we will use the defaults in this example. There are many examples in $TAO_ROOT/examples/POA/ that show how to use other policies in the POA.

The policies are stored in a sequence, so we first create the sequence and initialize its length:

    CORBA::PolicyList policies (2);
    policies.length (2);

now we create the policies:

    policies[0] =
      poa->create_id_assignment_policy (PortableServer::USER_ID);
    policies[1] =
      poa->create_implicit_activation_policy (PortableServer::NO_IMPLICIT_ACTIVATION);

now we can create the child POA;

    PortableServer::POA_var stock_factory_poa =
      poa->create_POA ("Stock_Factory_POA",
                       poa_manager.in (),
                       policies);

Notice that we shared the POA manager with the RootPOA, so we only need to use a single POA manager to control the state of both. The new POA makes a copy of the policies, so we need to destroy them to avoid memory leaks:

    for (CORBA::ULong i = 0; i != policies.length (); ++i) {
      policies[i]->destroy ();
    }

Activating objects in the child POA

Now we must use this POA to activate the stock objects. To keep the example simple, we will assume that we read the list of stocks from the stdin, as in:

    while (!std::cin.eof () && std::cin.peek () != EOF) {
      const int max_symbol_length = 8;
      char symbol[max_symbol_length];
      const int max_full_name_length = 64;
      char full_name[max_full_name_length];
      double price;

      std::cin.getline (symbol, max_symbol_length, '\n');
      std::cin.getline (full_name, max_full_name, '\n');
      std::cin >> price;
      std::cin.ignore (1, '\n');

      // The interesting stuff goes here!
    }

For each trio of symbol, full name, and price, we create a stock implementation object:

      PortableServer::ServantBase_var servant =
        new Quoter_Stock_i (symbol, full_name, price);

The ServantBase_var acts like an auto pointer and will take care of deallocation in case there is an exception. This time we cannot use _this() to activate the servant though, because we want to create our own ids:

      PortableServer::ObjectId_var oid =
        PortableServer::string_to_ObjectId (symbol);

and then activate the object with that id:

      stock_factory_poa->activate_object_with_id (oid.in (),
                                                  servant.in ());

Be careful not to invoke _this() on any of these objects, as that would activate them in the RootPOA, ending with two activations! It is perfectly legal to activate the same servant multiple times in different POAs (and sometimes even in the same POA!), but this is not what we want in this case.

Modifying the Stock Factory

Now we need to implement a different version of the stock factory. We pass a reference to the child POA into the constructor and keep a reference to it:

class Quoter_Stock_Factory_i : public POA_Quoter::Stock_Factory
{
public:
  Quoter_Stock_Factory (PortableServer::POA_ptr stock_factory_poa)
    : stock_factory_poa_ (PortableServer::POA::_duplicate (stock_factory_poa))
  {}

  Quoter::Stock_ptr get_stock (const char *symbol)
    throw (Quoter::Invalid_Stock_Symbol);

private:
  PortableServer::POA_var stock_factory_poa_;
};

Notice that we duplicate the POA, following the usual CORBA memory rules for input arguments. Since the constructor is not a CORBA operation, we could use any rules we wished, but it is less confusing if we stick to the CORBA set.

The implementation of the get_stock operation is more interesting. First we create an object id based on the symbol:

Quoter::Stock_ptr
Quoter_Stock_Factory_i::get_stock (const char *symbol)
    throw (Quoter::Invalid_Stock_Symbol)
{
  PortableServer::ObjectId_var oid =
    PortableServer::string_to_ObjectId (symbol);

next we look up that object id in the POA:

  try {
    CORBA::Object_var tmp =
      this->stock_factory_poa_->id_to_reference (oid.in ());

then narrow the object reference to the right type and return it:

    return Quoter::Stock::_narrow (tmp.in ());
  }

If the symbol was invalid, the POA will not find the right object id and raise an exception:

  catch (PortableServer::POA::ObjectNotActive &) {
    throw Quoter::Invalid_Stock_Symbol ();
  }
}

Memory management for the Stock objects

So far we have not discussed memory management for servants. This is a good opportunity to do it, because the stock objects are completely controlled by the POA. The POA provides reference counting for the Servants. You are not required to use reference counting, but if you do, most of the memory management is extremely simple. So why is reference counting not used all the time? Because some applications do not require it. For example, our previous simple server did not require any complex memory management, as the objects were all created on the stack.

To use the reference counting features in the POA, you must override the _add_ref() and _remove_ref() methods, to increase and decrease the reference count. Once the count reaches 0, you can safely remove the object (but remember to start the count at 1!). Implementing these methods in a thread safe way is a tedious task. To simplify the task, we use a mixin with the PortableServer::RefCountServantBase class, as in:

class Quoter_Stock_i
    :  public virtual POA_Quoter::Stock,
       public virtual PortableServer::RefCountServantBase
{
public:
  Quoter_Stock_i (const char *symbol,
                  const char *full_name,
                  CORBA::Double price);
  // as before
};

TAO's implementation of the RefCountServantBase is even thread safe, so you can use this technique in your multi-threaded servers to dynamically destroy objects. You simply delegate control to the POA. Once you deactivate the object, the POA will only invoke _remove_ref() once all the threads performing calls on an object terminate, so the object is not removed if it is still in use. Just remember to increment the reference count if you need to use it, too.

Exersice

Modify the following files in the simple server as follows:

You can use the same Quoter.idl, Stock_i.cpp and MPC file.

Solution

Compare your solution with the following files:

Does this solution scale when the number of stock symbols is in the thousands or millions? Find out about Servant Locators and Servant Activators in the POA!

Testing

A sample input file is available. You can use the simple client to check the results, as follows:

$ server < stock_list.txt > ior_file
$ client file://ior_file AAAA BBBB MSFT RHAT CCCC
Also test invalid symbols!

More Reading

The Henning and Vinoski CORBA book discusses POA policies in detail. Likewise, the Schmidt and Vinoski columns in C++ Report also include several articles about the POA. Finally, the TAO distribution includes examples that illustrate how to use the POA policies.


Carlos O'Ryan
Last modified: Sun Apr 1 18:11:01 PDT 2001