In our previous example, we extended our simple client using synchronous method invokation to handle asynchronous requests: Asynchronous Method Invokation by using the reply handlers.
In applications which have many number of objects, activating all of them all the time would be unnecessary and also might require too much memory or too many database lookups. For such applications, the POA provides an option for the application to supply servant managers that can dynamically supply servants on a per-request basis.
A servant manager is a call-back object that the application
registers with a POA. When the POA attempts to determine the
servant associated with a particular request, it calls back the
application's servant manager to obtain the servant. To be able
to register a servant manager with the application, the
RequestProcessingPolicyValue, which controls the
matching of requests to servants, is to be to set to
USE_SERVANT_MANAGER.
There are two types of servant managers depending on if the POA
retains the associations of objects to servants in its Active
Object Map or not. This is determined by the value of the
ServantRetentionPolicy policy set when the
POA is created. If the value of this policy is set as RETAIN,
the POA retains the associations and if the policy value is set
to be NON_RETAIN, the POA doesnt retain any associations between
the object and the servant.
For a POA with the RETAIN value, the servant manager must activate the servant associated with the object. This would need the servant manager object to support the ServantActivator interface. In the case of a POA with the NON_RETAIN value set, our servant manager object should be able to locate the servant for the requested object and then invoke it.
In this example, lets use a servant locator to locate the servant associated with our Stock_Factory object when a request is invoked on this object.
Our implementation of the Stock_Factory_Locator_i will help us find the Quoter_Stock_Factory servant.
A servant Locator interface provides two operations: preinvoke and postinvoke. The preinvoke operation is invoked to obtain the a servant to dispatch the request to. The servant returned by the preinvoke is used only for a single request. The postinvoke operation is later invoked to destroy the servant created by the preinvoke operation.
#include "tao/corba.h"
class Quoter_Stock_Factory_Locator_i : public POA_PortableServer::ServantLocator
{
public:
Quoter_Stock_Factory_Locator_i (CORBA::ORB_ptr orb);
// Preinvoke function
virtual PortableServer::Servant preinvoke (const PortableServer::ObjectId &oid,
PortableServer::POA_ptr poa,
const char * operation,
void * & cookie)
throw (CORBA::SystemException, PortableServer::ForwardRequest);
// Postinvoke function
virtual void postinvoke (const PortableServer::ObjectId & oid,
PortableServer::POA_ptr poa,
const char * operation,
void * cookie,
PortableServer::Servant servant)
throw (CORBA::SystemException);
private:
CORBA::ORB_var orb_;
};
In the implementation of the preinvoke operation, we check if
the object ID is valid and then check for the servant we want to
create and create adn return the requested servant.
PortableServer::Servant
Quoter_Stock_Factory_Locator_i::preinvoke (const PortableServer::ObjectId &oid,
PortableServer::POA_ptr poa,
const char * operation,
void * & cookie)
throw (CORBA::SystemException, PortableServer::ForwardRequest)
{
try {
// Get the ObjectID in string format
CORBA::String_var oid_str =
PortableServer::ObjectId_to_string (oid);
// Check if the ObjectId is valid
if (strcmp (oid_str.in (), "Quoter/Stock_Factory") != 0) {
// Create the requested servant.
PortableServer::Servant servant =
new Quoter_Stock_Factory_i ();
cookie = servant;
return servant;
}catch (const CORBA::BAD_PARAM &) {
throw CORBA::OBJECT_NOT_EXIST ();
}
The implementation of the postinvoke operation is
simple. We just destroy the servant which we created by the
preinvoke operation. The Cookie IDL
type which is a parameter in both these operations
helps associate the invokation of preinvoke with
its postinvoke operation.
void
Quoter_Stock_Factory_Locator_i::postinvoke (const PortableServer::ObjectId &oid,
PortableServer::POA_ptr poa,
const char * operation,
void * cookie,
PortableServer::Servant servant)
throw (CORBA::SystemException)
{
// Delete the servant as it is no longer needed.
PortableServer::Servant my_servant = (PortableServer::Servant) cookie;
if (servant == my_servant)
delete servant;
}
Our first steps would be to create a new POA from the RootPOA
with the USE_SERVANT_MANAGER value for the
RequestProcessingPolicy and NON_RETAIN
for the ServantRetentionPolicy.
CORBA::PolicyList policies (3);
policies.length (3);
// Assign the polices
policies [0] =
poa->create_id_assignment_policy (PortableServer::USER_ID);
policies [1] =
poa->create_request_processing_policy
(PortableServer::USE_SERVANT_MANAGER);
policies [2] =
poa->create_servant_retention_policy (PortableServer::NON_RETAIN);
// Create the POA with these policies
PortableServer::POA_var child_poa =
poa->create_POA ("childPOA",
poa_manager.in (),
policies);
// Destroy the policy objects
for (CORBA::ULong i = 0; i != policies.length (); ++i) {
policies[i]->destroy ();
}
The policy values are assigned, the childPOA is
created with these policies and later these policy objects can be
deleted as a copy of these obejcts is made by the
create_POA and we wouldnot need these objects anymore.
Now that we have the POA which can support servant managers, the next step would be to create a servant for the servant locator object, activate it to obtain its reference and set it as the servant manager with the childPOA.
// Create a Stock_Factory_Locator servant
Quoter_Stock_Factory_Locator_i servant_locator_i(orb.in ());
// Need to activate a servant_manager object in the Root POA
PortableServer::ServantLocator_var servant_locator =
servant_locator_i._this ();
// Set the SM with the childPOA
child_poa->set_servant_manager (servant_locator.in ());
Now that we have set the servant manager with the childPOA, the
next step would be to create a reference with the user created
ID in childPOA which uses the Quoter_Stock_Fcatory_Locator_i
create_reference_with_id operation lets us
create the required object without actually creating its servant.
The application supplies the ObjectId which signifies the
identity of the object in the application domain.
// Get the Object Id
PortableServer::ObjectId_var child_oid =
PortableServer::string_to_ObjectId ("childFoo");
//Create the Object without creating the servants
CORBA::Object_var stock_factory =
child_poa->create_reference_with_id (child_oid.in (),
"IDL:Quoter/Stock_Factory:1.0");
After this, as before, lets put this object reference as an IOR string and print it out.
// Put the object reference as an IOR string
CORBA::String_var ior = orb->object_to_string (stock_factory.in ());
// Print it out!
std::cout << ior.in () << std::endl;
Modify the server.cpp in the simple server to use servant managers and locators. Use these files to help complete the implementation. Stock_Factory_locator_i.h Stock_Factory_locator_i.cpp Makefile.
Look at the server.cpp file. It should not be much different from yours.
A client which uses request handlers is provided: client.cpp. As before the following files are provided. Quoter.idl Stock_i.h Stock_i.cpp Stock_Factory_i.h Stock_Factory_i.cpp Handler_i.h and Handler_i.cpp.
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.