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.
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 (); }
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.
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 (); } }
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.
Modify the following files in the simple server as follows:
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!
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 CCCCAlso test invalid symbols!
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.