VisiBroker for C++ Developer’s Guide : Using object activators

Using object activators
This section describes how to use the VisiBroker object activators.
In this release, as well as the VisiBroker 4.1 release and later, the Portable Object Adaptor (POA) supports the features that were provided by the BOA in the VisiBroker 3.x and 4.0 releases. For backward compatibility reasons, you may still use the object activators as described in this section with your code. For more information on how to use the BOA activators with this release, see “Using the BOA with VisiBroker”.
Deferring object activation
You can defer activation of multiple object implementations using service activation with a single Activator when a server needs to provide implementations for a large number of objects.
Activator interface
You can derive your own interface from the Activator class. This allows you to implement the pure virtual activate and deactivate methods that the VisiBroker ORB will use for the AccountImpl object. You can then delay the instantiation of the AccountImpl object until the BOA receives a request for that object. It also allows you to provide clean-up processing when the BOA deactivates the object.
The following code sample shows the Activator class.
class Activator {
public:
virtual CORBA::Object_ptr activate(
extension::ImplementationDef impl)=0;
virtual void deactivate(
Object_ptr, extension::ImplementationDef_ptr impl)=0;
};
The following code sample shows you how to create an Activator for the AccountImpl interface.
class extension {
. . .
class AccountImplActivator : public extension::Activator {
public:
virtual CORBA::Object_ptr activate(
CORBA::ImplementationDef_ptr impl);
virtual void deactivate(CORBA::Object_ptr,
CORBA::ImplementationDef_ptr impl);
};
CORBA::Object_ptr AccountImplActivator::activate(
CORBA::ImplementationDef_ptr impl) {
// When the BOA needs to activate us, instantiate the AccountImpl object.
extension::ActivationImplDef* actImplDef =
extension::ActivationImplDef::_downcast(impl);
CORBA::Object_var obj = new AccountImpl(actImplDef->object_name());
return CORBA::_duplicate(obj);
}
void AccountImplActivator::deactivate(CORBA::Object_ptr obj,
CORBA::ImplementationDef_ptr impl) {
// When the BOA deactivates us, release the Account object.
obj->_release;
}
}
Using the service activation approach
Service activation can be used when a server needs to provide implementations for a large number of objects (commonly thousands of objects, possibly millions) but only a small number of implementations need to be active at any specific time. The server can supply a single Activator which is notified whenever any of these subsidiary objects are needed. The server can also deactivate these objects when they are not in use.
For example, you might use service activation for a server that loads object implementations whose states are stored in a database. The Activator is responsible for loading all objects of a given type or logical distinction. When VisiBroker ORB requests are made on the references to these objects, the Activator is notified and creates a new implementation whose state is loaded from the database. When the Activator determines that the object should no longer be in memory and, if the object had been modified, it writes the object's state to the database and releases the implementation.
Figure 39
Deferring object activation using service activators
Assuming the objects that will make up the service have already been created, the following steps are required to implement a server that uses service activation:
1
2
3
Implement the Activator which creates the object implementations on demand. In the implementation, you derive an Activator interface from extension::Activator, overriding the activate and deactivate methods.
4
Example of deferred object activation for a service
The following sections describe the odb example for service activation which is located in the following VisiBroker directory:
<install_dir>/examples/vbe/boa/odb
This directory contains the following files:
When make or nmake (on Windows) is invoked in the odb subdirectory, builds the following client and server programs:
Server.exe, Creator.exe, and Client.exe.
The odb example shows how an arbitrary number of objects can be created by a single service. The service alone is registered with the BOA, instead of each individual object, with the reference data for each object stored as part of the IOR. This facilitates object-oriented database (OODB) integration, since you can store object keys as part of an object reference. When a client calls for an object that has not yet been created, the BOA calls a user-defined Activator. The application can then load the appropriate object from persistent storage.
In this example, an Activator is created that is responsible for activating and deactivating objects for the service named “DBService.” References to objects created by this Activator contain enough information for the VisiBroker ORB to relocate the Activator for the DBService service, and for the Activator to recreate these objects on demand.
The DBService service is responsible for objects that implement the DBObject interface. An interface (contained in odb.idl) is provided to enable manual creation of these objects.
odb.idl interface
The odb.idl interface enables manual creation of objects that implement the DBObject odb interface.
interface DBObject {
string get_name();
};
typedef sequence<DBObject> DBObjectSequence;
interface DB {
DBObject create_object(in string name);
};
The DBObject interface represents an object created by the DB interface, and can be treated as a service object.
DBObjectSequence is a sequence of DBObjects. The server uses this sequence to keep track of currently active objects.
The DB interface creates one or more DBObjects using the create_object operation. The objects created by the DB interface can be grouped together as a service.
Implementing a service-activated object
The idl2cpp compiler generates two kinds of constructors for the skeleton class _sk_DBObject from boa/odb/odb.idl. The first constructor is for use by manually-instantiated objects; the second constructor enables an object to become part of a service. As shown below, the implementation of DBObject constructs its base _sk_DBObject method using the service constructor, rather than the object_name constructor typically used for manually-instantiated objects. By invoking this type of constructor, the DBObject constructs itself as a part of a service called DBService.
class DBObjectImpl: public _sk_DBObject {
private:
CORBA::String_var _name;
public:
DBObjectImpl(const char *nm, const CORBA::ReferenceData& data)
: _sk_DBObject("DBService", data), _name(nm) {}
. . .
};
The base constructor requires a service name as well as an opaque CORBA::ReferenceData value. The Activator uses these parameters to uniquely identify this object when it must be activated due to client requests. The reference data used to distinguish among multiple instances in this example consists of the range of numbers from 0 to 99.
Implementing a service activator
Normally, an object is activated when a server instantiates the classes implementing the object, and then calls BOA::obj_is_ready followed by BOA::impl_is_ready. To defer activation of objects, it is necessary to gain control of the activate method that the BOA invokes during object activation. You obtain this control by deriving a new class from extension::Activator and overriding the activate method, using the overridden activate method to instantiate classes specific to the object.
In the odb example, the DBActivator class derives from extension::Activator, and overrides the activate and deactivate methods. The DBObject is constructed in the activate method.
The following code sample is an example of overriding activate and deactivate.
class DBActivator: public extension::Activator {
virtual CORBA::Object_ptr activate(CORBA::ImplementationDef_ptr impl);
virtual void deactivate(CORBA::Object_ptr,
CORBA::ImplementationDef_ptr impl );
public:
DBActivator(CORBA::BOA_ptr boa) : _boa(boa) {}
private:
CORBA::BOA_ptr _boa;
};
When the BOA receives a client request for an object under the responsibility of the Activator, the BOA invokes the activate method on the Activator. When calling this method, the BOA uniquely identifies the activated object implementation by passing the Activator an ImplementationDef parameter, from which the implementation can obtain the CORBA::ReferenceData, the requested object's unique identifier.
As shown in the following code sample, the DBActivator class creates an object based on its CORBA::ReferenceData parameter.
CORBA::Object_ptr DBActivator::activate(CORBA::ImplementationDef_ptr impl) {
extension::ActivationImplDef* actImplDef =
extension::ActivationImplDef::_downcast(impl);
CORBA::ReferenceData_var id(actImplDef->id());
cout << "Activate called for object=[" << (char*) id->data()
<< "]" << endl;
DBObjectImpl *obj = new DBObjectImpl((char *)id->data(), id);
_impls.length(_impls.length() + 1);
_impls[_impls.length()-1] = DBObject::_duplicate(obj);
_boa->obj_is_ready(obj);
return obj;
}
Instantiating the service activator
The DBActivator service activator is responsible for all objects that belong to the DBService service. All requests for objects of the DBService service are directed through the DBActivator service activator. All objects activated by this service activator have references that inform the VisiBroker ORB that they belong to the DBService service.
As shown in the following code sample, the DBActivator service activator is created and registered with the BOA using the BOA::impl_is_ready call in the main server program.
int main(int argc, char **argv) {
CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);
CORBA::BOA_ptr boa = orb->BOA_init(argc, argv);
MyDB db("Database Manager");
boa->obj_is_ready(&db);
DBObjectImplReaper reaper;
reaper.start();
cout << "Server is ready to receive requests" << endl;
boa->impl_is_ready("DBService", new DBActivator(boa));
return(0);
}
Note
The call to BOA::impl_is ready is a variation on the usual call to BOA::impl_is_ready. It takes two arguments:
Using a service activator to activate an object
Whenever an object is constructed, BOA::obj_is_ready must be explicitly invoked in DBActivator::activate. There are two calls to BOA::obj_is_ready in the server program. One call occurs when the server creates a service object and returns an IOR to the creator program.
DBObject_ptr create_object(const char *name) {
char ref_data[100];
memset(ref_data,'\0',100);
sprintf(ref_data, "%s", name);
CORBA::ReferenceData id(100, 100, (CORBA::Octet *)ref_data);
DBObjectImpl *obj = new DBObjectImpl(name, id);
_boa()->obj_is_ready(obj);
_impls.length(_impls.length() + 1);
_impls[_impls.length()-1] = DBObject::_duplicate(obj);
return obj;
}
The second occurrence of BOA::obj_is_ready is in DBActivator::activate, and this needs to be explicitly called.
Deactivating service-activated object implementations
The main use for service activation is to provide the illusion that a large number of objects are active within a server, but to have only a small number of these objects actually active at any given moment. To support this model, the server must be able to temporarily remove objects from use. The multithreaded DBActivator example program contains a reaper thread that deactivates all DBObjectImpls every 30 seconds. The DBActivator simply releases the object reference when the deactivate method is invoked. If a new client request arrives for a deactivated object, the VisiBroker ORB informs the Activator that the object should be reactivated.
// static sequence of currently active Implementations
static VISMutex _implMtx;
static DBObjectSequence _impls;
// updated DBActivator to store activated implementations
// in the global sequence.
class DBActivator: public extension::Activator {
virtual CORBA::Object_ptr activate(CORBA::ImplementationDef_ptr impl) {
extension::ActivationImplDef* actImplDef =
extension::ActivationImplDef::_downcast(impl);
CORBA::ReferenceData_var id(actImplDef->id());
DBObjectImpl *obj = new DBObjectImpl((char *)id->data(), id);
VISMutex_var lock(_implMtx);
_impls.length(_impls.length() + 1);
_impls[_impls.length()-1] = DBObject::_duplicate(obj);
return obj;
}
virtual void deactivate(CORBA::Object_ptr,
CORBA::ImplementationDef_ptr impl) {
obj->_release();
}
};
// Multi-threaded Reaper for destroying all activated
// objects every 30 seconds.
class DBObjectImplReaper : public VISThread {
public:
// Reaper methods
virtual void start() {
run();
}
virtual CORBA::Boolean startTimer() {
vsleep(30);
return 1;

virtual void begin() {
while (startTimer()) {
doOneReaping();
}
}
protected:
virtual void doOneReaping() {
VISMutex_var lock(_implMtx);
for (CORBA::ULong i=0; i < _impls.length(); i++) {
// assigning nil into each element will release
// the reference stored in the _var
DBObject_var obj = DBObject::_duplicate(_impls[i-1];
_impls[i] = DBObject::_nil();
CORBA::BOA_var boa = obj->_boa();
boa->deactivate_obj(obj);
}
_impls.length(0);
}
};