Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced CORBA Programming wit C++ - M. Henning, S. Vinoski.pdf
Скачиваний:
65
Добавлен:
24.05.2014
Размер:
5 Mб
Скачать

IT-SC book: Advanced CORBA® Programming with C++

oneway is implemented as a standard, round-trip DCE RPC because DCE does not support oneway semantics.

The client invoking ProcessTerminator::shutdown cannot know what other clients might be using the server. If another client is in the middle of a multirequest transaction, for example, the client might not appreciate your shutting the server down before it finishes.

Despite these problems, you might find this technique useful for servers that you want to control using a single network management application.

Application Shutdown Versus ORB Shutdown

The three approaches described in the previous sections for shutting down an application all suffer from the same problem: they assume that "application" and "ORB" are synonymous. In other words, they fail to account for the fact that a single application can contain multiple ORB instances (created by calling ORB_init multiple times with different arguments). In a multi-ORB application, just because you initiate shutdown for one ORB does not mean that you cause the whole application to shut down.

For a multi-ORB application, you must use a non-blocking event-handling loop that calls

ORB::work_pending and ORB::perform_work on each ORB. This technique allows each ORB to use the main thread to perform work as required. Rather than have signal handlers or application-specific shutdown methods invoke ORB::shutdown, they could instead set a flag to mark the fact that the application should shut itself down. This flag can then be checked from within the event loop. If the event loop notices that the flag is set, it can exit and allow clean-up code to initiate shutdown for each ORB instance.

11.12 POA Activation

Like servants, POAs can be created on demand. This technique can be useful for applications that have POAs whose objects are rarely invoked. POA activation occurs when a request arrives for an object in a descendant POA that has not yet been created or when the application searches a hierarchy of POAs using the POA::find_POA operation for a named POA that has not yet been created. The application registers an AdapterActivator with each POA that must activate its descendant POAs.

module PortableServer { interface AdapterActivator {

boolean unknown_adapter(in POA parent, in string name);

}; // ...

};

451

IT-SC book: Advanced CORBA® Programming with C++

Adapter activators are normal CORBA objects, so they are incarnated via servants. A C++ servant for an adapter activator derives from the

POA_PortableServer::AdapterActivator skeleton.

#include <poaS.hh>

class ExampleAdapterActivator :

public virtual POA_PortableServer::AdapterActivator

{

public: ExampleAdapterActivator() {}

virtual ~ExampleAdapterActivator() {}

virtual CORBA::Boolean unknown_adapter( PortableServer::POA_ptr parent, const char * name

) throw(CORBA::SystemException);

private:

// copy not supported

ExampleAdapterActivator(const ExampleAdapterActivator &); void operator=(const ExampleAdapterActivator &);

};

The only interesting member function of this servant class is the unknown_adapter function. It takes a reference to the POA that will be the parent of the POA being activated, along with the name of the new POA.

CORBA::Boolean

ExampleAdapterActivator:: unknown_adapter(

PortableServer::POA_ptr parent, const char * name

) throw(CORBA::SystemException)

{

CORBA::Boolean return_val = 0;

if (strcmp(name, "child") == 0) {

// Create a PERSISTENT LifespanPolicy object. PortableServer::LifespanPolicy_var lifespan =

parent->create_lifespan_policy( PortableServer::PERSISTENT

);

//Create PolicyList. CORBA::PolicyList policies; policies.length(1); policies[0] =

PortableServer::LifespanPolicy::_duplicate(lifespan);

//Use the parent's POAManager. PortableServer::POAManager_var poa_mgr =

parent->the_POAManager();

//Create the child POA.

452

IT-SC book: Advanced CORBA® Programming with C++

try {

PortableServer::POA_var child = parent->create_POA("child", poa_mgr, policies);

return_val = 1;

}

catch(const PortableServer::POA::AdapterAlreadyExists &) { // Do nothing, return_val already set to 0.

}

catch(const PortableServer::POA::InvalidPolicy &) { abort(); // design error

}

// Destroy our LifespanPolicy object. lifespan->destroy();

}

return return_val;

}

Adapter activators have only the name of the child POA to be created, as well as the name of the parent POA and its ancestors, by which to decide whether to create the POA. The reference to the parent POA can be used to request its name via the POA::the_name read-only attribute, which returns a string containing the parent's name. References to ancestors of the parent POA can be obtained using the POA::the_parent read-only attribute.

Our example code checks that the name of the child POA to be activated is "child" and, if it is, proceeds to create the POA. We first create a POA policy list consisting of the PERSISTENT life span policy so that we can create the child as a persistent POA. We then obtain a reference to the POAManager object of the parent POA to have the child share it. Finally, we invoke create_POA on the parent POA. We perform the creation within a try block to catch the nonsystem exceptions that create_POA can raise because unknown_adapter is not allowed to raise any user-defined exceptions.

This example also raises an interesting issue related to request flow control. If we were to create our child POA without using an adapter activator and if the POA contained objects that had been previously created, we could find that those objects were being invoked before our application was finished initializing the new POA. The problem originates in the fact that we are using the parent POA's POAManager for our child POA as well. If that POAManager is in the active state when we pass it to create_POA, it will let requests flow into the child POA immediately upon its creation. If we wanted to install a servant manager or default servant on the new child POA, we might be out of luck.

Using an adapter activator prevents this problem because while the adapter activator is running, all requests for objects in the POA being activated are queued. As with the queues managed within a POAManager implementation, the size of this queue is implementation-dependent. Another way to prevent this problem is to explicitly transition the POAManager into the holding state before passing it to create_POA and then to

453

IT-SC book: Advanced CORBA® Programming with C++

change it back to active afterward. This approach can be somewhat tedious, however, and it can cause unexpected problems if you forget either POAManager state transition. You set an adapter activator on a POA using the POA::the_activator attribute.

//Create our AdapterActivator object. ExampleAdapterActivator adapter_activator_servant; PortableServer::AdapterActivator_var adapter_activator =

adapter_activator_servant._this();

//Make it the AdapterActivator of our Root POA.

root_poa->the_activator(adapter_activator);

Our example creates the AdapterActivator object as a transient object using implicit object creation and activation via the servant's _this member function. Because AdapterActivator objects must be local to the process in which they activate POAs, creating them as transient objects imposes no practical limitations on their use. A single AdapterActivator can be registered with multiple POAs simultaneously.

As with all software, the requirements for server applications tend to change over time. An application that starts out using one or two POAs might end up needing ten, twenty, or even more, depending on how many different types of CORBA objects the application supports and on how it uses POA features such as servant managers and default servants. We therefore recommend that at a minimum, whether or not you initially use adapter activators, you write all your POA creation code so that it is easy to invoke from an adapter activator. Better yet, you should always use adapter activators to create POAs even if you employ find_POA invocations to explicitly cause the necessary POAs to be created. This technique helps avoid the POAManager race conditions described earlier.

The following example shows how the POA hierarchy shown in Figure 11.6 can be created using a different implementation of our ExampleAdapterActivator.

CORBA::Boolean

ExampleAdapterActivator:: unknown_adapter(

PortableServer::POA_ptr parent, const char * name

) throw(CORBA::SystemException)

{

CORBA::Boolean install_adapter_activator = 0; CORBA::PolicyList policies;

// Obtain our own object reference. PortableServer::AdapterActivator_var me = _this();

if (strcmp(name, "A") == 0) {

//Create policies for POA A (not shown).

}else if (strcmp(name, "B") == 0) {

//Create policies for POA B (not shown). install_adapter_activator = 1;

454

IT-SC book: Advanced CORBA® Programming with C++

}else if (strcmp(name, "C") == 0) {

//Create policies for POA C (not shown).

}else if (strcmp(name, "D") == 0) {

//Create policies for POA D (not shown). install_adapter_activator = 1;

}else if (strcmp(name, "E") == 0) {

//Create policies for POA E (not shown).

}else {

//Unknown POA.

return 0;

}

//Use the parent's POAManager for all POAs. PortableServer::POAManager_var poa_mgr =

parent->the_POAManager();

//Create the child POA.

try {

PortableServer::POA_var child = parent->create_POA(name, poa_mgr, policies);

if (install_adapter_activator) child->the_activator(me);

}catch(const PortableServer::POA::AdapterAlreadyExists &) { return 0;

}catch(const PortableServer::POA::InvalidPolicy &) { abort(); // design error

}

return 1;

}

We first compare the name of the POA being activated against all known POA names to set up the correct policies for that POA. If the POA name is unknown, we do not activate a POA and instead return 0. Assuming that a valid name was passed to us, we retrieve the POAManager from the parent POA and pass it, along with the name of the new POA and its policies, to create_POA. As before, we catch the non-system exceptions that create_POA can throw because unknown_adapter is not allowed to raise them. Because they have child POAs, POA "B" and POA "D" each require an adapter activator as well, so we install our ExampleAdapterActivator for them if the install_adapter_activator flag is set to true.

Our server main can use find_POA to explicitly force this adapter activator to run. It must ensure that it invokes find_POA in the right order to ensure that the POA hierarchy gets set up as desired.

int

main(int argc, char * argv[])

{

// Initialize the ORB.

CORBA::ORB_var orb = CORBA::ORB_init(argc, argv); // Obtain a reference to the Root POA.

455

IT-SC book: Advanced CORBA® Programming with C++

CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");

PortableServer::POA_var root_poa = PortableServer::POA::_narrow(obj);

//Install our AdapterActivator. ExampleAdapterActivator aa_servant; PortableServer::AdapterActivator_var aa =

aa_servant._this(); root_poa->the_activator(aa);

//Create POA A.

PortableServer::POA_var poa_a = root_poa->find_POA("A", 1);

// Create POA B.

PortableServer::POA_var poa_b = root_poa->find_POA("B", 1);

// Create POA C.

PortableServer::POA_var poa_c = root_poa->find_POA("C", 1);

// Create POA D.

PortableServer::POA_var poa_d = poa_b->find_POA("D", 1);

// Create POA E.

PortableServer::POA_var poa_e = poa_d->find_POA("E", 1);

//Activate our POAManager. PortableServer::POAManager_var mgr =

root_poa->the_POAManager(); mgr->activate();

//Let the ORB listen for requests. orb->run();

return 0;

}

We initialize the ORB and obtain a reference to the Root POA as usual. We then create a servant for the AdapterActivator and implicitly create a transient CORBA object from it. After the AdapterActivator is registered with the Root POA, we invoke find_POA for POAs "A", "B", "C", "D", and "E" to force them into existence. The second argument to find_POA is a Boolean that tells it to attempt to activate the POA if it is not found. We then activate the POAManager of the Root POA, which, because of the work of the ExampleAdapterActivator servant, is also shared by POAs "A", "B", "C", "D", and "E". Finally, we let our ORB run so that it will allow requests into our server.

Although we did not activate our POAManager until after our POAs had been created, we could have done it beforehand just the same, and the presence of the AdapterActivator would have ensured that any requests for any POA being created were queued until the POA was properly initialized.

456