While CORBA provides a robust, well-defined standard for the development of distributed object computing (DOC) middleware, the machinery needed to deploy a non-trivial application tends to be, at best, tedious and repetitive and, at worst, a source of hardto- discover errors. As an application/system grows in complexity, the need for a reusable CORBA machinery abstraction becomes self-evident. The tried-and-true practice of cutting and pasting and the common setup function paradigm does not provide an adequate solution to the problem. In order to address these needs and overcome the problems of the classical design, this paper presents a policy-driven abstraction mechanism that promotes code reuse while preserving the rich capabilities CORBA provides.
CORBA has developed into a robust, mature distributed objectcomputing standard over the years, but the effort in environment setup of large systems with many servant types is problematic. The cost of recreating the same or slightly differing requirements for multiple servant types or for multiple applications is unacceptable given that the reward for such work is non-existent and the powerof modern development tools renders classical methods [Henning-] inefficient. Large-scale CORBA applications and systems may provide hundreds of disparate servant types with varying requirements on the underlying ORB facilities. To meet the needs of CORBA server/system developers, the following presents a C++ policy-driven library design that eliminates the code redundancy issues in traditional CORBA applications while maintaining the rich set of options provided by the CORBA standard.
Template meta-programming has emerged as a powerful tool with which to construct reusable, extensible libraries. The CORBA Template Library (CTL) relies heavily on said techniques and borrows the policy-driven design approach pioneered by Andrei Alexandrescu in "Modern C++ Design" (MCD) [Alexandrescu]. The generic functionality this provides is ideal for library designers as it allows an extensibility lacking in traditional OO- and procedure-based libraries. Template-based policies provide the necessary paradigm to accomplish the "write once use many" goal, while providing a method for future extension that is both safe and predictable, and allowing us to leverage working code to dramatically cut down on testing, development, and redesign time.
Expertise in the application of CORBA and the design of distributed computing systems is secondary to that of constructing a system that "does something." Work involved in the design and development of the machinery needed to deploy an application using CORBA is wasted effort because the real objective is the functionality exposed via CORBA. For instance, in order to set up a minimal CORBA server environment, an ORB must be initialized, a servant must be instantiated and activated, and an active thread must run the ORB dispatching mechanism. In the simplest case, this is not a burden and does not indicate a need for yet another library/abstraction. A transient servant could simply be activated under the Root POA and theapplication's main thread would run the dispatch loop. If, however, we were to need a system with many servant instances, multiple POAs with differing POA policies and ORB requirements, the task becomes substantially more difficult. If we wish to develop a true peer-to-peer application, where theapplication is both a client and a server, instead of a pure server application, the necessary setup becomes even more cumbersome.
Historically, these concerns have been met with procedural setup and initialization functions, code repetition, or an OO abstraction mechanism, none of which are ideal. Procedural solutions fall short because of lack of easy reconfiguration of servant initializationbehavior. Environmental changes require modification of servant setup code, possibly in multiple locations, for instance, calls to a number of different POA setup procedures and object instantiation and activation functions. A simple change from a transient, systemidentified, stack-allocated servant to a persistent, user-identified, heap-allocated servant is error prone and takes more effort than the alternate policy-driven model presented here. The traditional OO-based design also fails to simplify deployment significantly and can in fact be more difficult to adapt than the procedural solution. Inheritance-based solutions rely on a given base type that limits your design choices or requires a large number of similar base types to achieve all possible combinations, i.e., a reference counted transient servant, a non reference counted version, and persistent version of both, etc.
Addressing these limitations with a reusable and extensible template library provides an abstraction layer that will allow developers to concentrate on the functionality they want to expose, not the machinery needed to expose them. To that end, the following presents some meta-programming techniques to simplify our goal, and a policy driven library of reusable types that simplify DOC middleware development to a point where the only new work needed in most cases is the development of the methods exposed by a given interface. The following pages present the basic techniques employed by the library and a synopsis of the library policies and their usage.
The CORBA Template Library (CTL) presented here attempts to provide an extensible library of policies and composable elements to simplify the deployment of large-scale CORBA applications. The design concentrates on the servant activation process, the mechanism by which the servant accesses the information it needs during the activation process, and the organization of servant instances in a larger service framework. The CTL is a freely available C++ source code library, available under the BSD license.
Currently the library is tied to ACE/TAO but work is underway to make it as ORB agnostic as possible.
Servants represent the concrete implementation of a CORBA Object in a particular programming language. They are local instances of a type that are made known to the CORBA framework in order to service remote requests. A local servant instance is "activated," made known to the CORBA framework, by first instantiating the servant, finding or creating a suitable Portable Object Adapter (POA), registering with that POA, and finally some secondary object-activation activity such as Object exposition through the Naming Service. In order to preserve the rich set of options available to CORBA developers a policybased design was chosen that allows every set of possible activation scenarios to be expressed by way of four policy-type delegates: memory policies, POA- activation/selection policies, object-activation policies, and auxiliary policies. The servant activation (createServant()) and deactivation (destroyServant()) functions simply dispatch the requests to the specified policy delegates.
The memory policy is the first called during activation and the last called during deactivation. Most users will simply use the empty memory policy, which does nothing. But a number of policies are provided to simplify garbage collection of free store allocated servants. The memory policy also provides a convenient base type for alternate allocation strategies, such as a pooled allocator.
The POA activation process is arguably the most cumbersome aspect of CORBA applications, and generates the most redundant code. While it is possible to use a procedural approach that simplifies the most common-use scenarios, with seven basic policies and a number of extension policies (pluggable protocols such as bi-directional GIOP) an exhaustive set of POA activation procedures is not a practical option.
The CTL provides two mechanisms by which a POA can be created: two functions create_poa and create_from_poa, which are explicitly specialized with the policy type desired; and a servant POA policy that will create the POA for a servant during the activation process. The two options allow for easy POA activation whether the POA to Object relationship is one-to-many or one-to-one.
A classic POA activation scenario involves first obtaining and narrowing a reference to the RootPOA obtaining the POAManager, generating the policy list, creating the new POA, and then cleaning up.
CORBA::Object_var obj = orb->resolve_initial_references("RootPOA"); PortableServer::POA_var root = PortableServer::POA::_narrow(obj); PortableServer::POAManager_var poa_manager = root->the_POAManager(); CORBA::PolicyList policies; policies.length(2); policies[0] = root->create_lifespan_policy( PortableServer::PERSISTENT); policies[1] = root->create_id_assignment_policy( PortableServer::USER_ID); PortableServer::POA_var wfa_poa = root->create_POA( "WidgetFactoryAdmin_i", poa_manager,policies); poa_manager->activate(); for(CORBA::ULong i=0;i<policies.length();++i) policies[i]->destroy();
The equivalent when using CTL is to specify a POA activation policy during servant activation:
POAPolicy::FromRoot<D,POAPolicy::PersistentUserID<> > // POA policies which require an id to create the // POA also provide a set_poa_id method.
And an example when using CTL to create a new POA outside of servant activation:
PortableServer::POA_var w_poa = POAPolicy::create_poa< POAPolicy::PersistentUserID<> >(orb,"Widget_i");
The POA policy types provided are composable in a linear hierarchy, as are most of the types in the CTL. So for instance, assuming you wanted a POA that used bi-directional GIOP, hosted transient system id Objects, and didn't allow implicit activation of servants, the appropriate specification would merely be a string of nested template types:
POAPolicy::Bidirectional< POAPolicy::TransientSystemID< POAPolicy::NoImplicit<> > >
POA deactivation, in both the classic and CTL use scenarios, is relatively simple. The CTL POA policy takes care of it automatically if it created the POA. If the POA was not created during the activation process but was instead created as above the user is responsible for destroying the POA when it is no longer needed, for instance:
w_poa->destroy(true,true);
Once a suitable POA has been created a servant instance must be tied to the POA and an Object reference that specifies the servant to the outside world must be generated. A persistent Object reference remains the same across different execution contexts while a transient Object reference is unique each time the servant is activated. CTL provides two simple policy types that activate persistent and transient Objects under the specified POA during the activation process. A number of other policies are also provided such as Multicast servant activation but are not discussed here.
A classic persistent Object activation scenario:
PortableServer::ObjectId_var oid = PortableServer::string_to_ObjectId( "WidgetFactoryAdmin_i"); wfa_poa->activate_object_with_id(oid.in(),&wfa); CORBA::Object_var wfa_obj = wfa_poa->id_to_reference((oid.in())); Example::WidgetFactoryAdmin_var wfa_ref = Example::WidgetFactoryAdmin::_narrow(wfa_obj);
In contrast, when using the CTL, activating a servant with a persistent Object reference is simplified to specifying the object activation policy:
ObjPolicy::UserID<D> // object policies which require an id during // activation expose a set_object_id method.
Object deactivation is done automatically during servant deactivation in the case of CTL servants. A classic CORBA application would need to explicitly deactivate the Object when it is no longer needed:
PortableServer::ObjectId_var oid = wfa_poa->servant_to_id(&wfa); wfa_poa->deactivate_object(oid);
Auxiliary policy processing is the last step during activation and the first during deactivation. It provides a point during the activation/deactivation process after the servant has been associated with a CORBA Object and before, in the case of deactivation, that Object has been torn down. As such it is an ideal point from which to expose the Object to some form of external lookup such as the Naming Service; or to set up messaging policies to be used, such as timeout policies. Two simple exposition policies are presented here: one that binds the newly activated Object to the ORB's IOR table, and one that binds the Object to the root naming-context of the Naming Service.
The IOR table provides a mechanism by which external ORB processes can discover an Object and obtain a reference to the Object. When a call to orb->resolve_initial_references("NameService") is made, if an initial reference was not supplied during ORB initialization a multicast request is sent to external ORB processes that in turn query their IOR table to try to fulfill the request for the name specified("NameService" in this case). A CTL servant can request IOR table binding and unbinding during activation and deactivation respectively, by providing the appropriate Auxiliary policy:
AUXPolicy::BindIORTable<D> // The BindIORTable policy exposes a method // set_iortable_id to specify the ID the Object // reference will be associated with in the table.
The equivalent classic CORBA application would need to:
obj = orb->resolve_initial_references("IORTable"); IORTable::Table_var tbl = IORTable::Table::_narrow(obj); CORBA::String_var str = orb1->object_to_string(wfa_obj); tbl->rebind("WidgetFactoryAdmin_i",str.in());
in order to bind the Object reference to the IOR table. And in order to unbind do the complementary
tbl->unbind("WidgetFactoryAdmin_i");
during deactivation. This of course entails either maintaining the above IORTable::Table_var or reacquiring it when needed. The corresponding CTL use is transparent and automatically done during the servant deactivation process.
The Naming Service provides a directory service for exposing "well known" servants to distributed systems. Much like the IOR Table it provides a mechanism for exposition and lookup. CTL servants that wish to take advantage of the Naming Service simply specify the appropriate auxiliary policy such as:
AUXPolicy::BindRootNamingContext<D> // The BindRootNamingContext policy provides a // set_cosnaming_data method to specify the id and // kind that will be bound to the root naming-context.
to be used during activation and deactivation. A classic CORBA application on the other hand needs to manually register with the Naming Service after the Object has been activated:
CosNaming::Name name; name.length(1); name[0].id = CORBA::string_dup("WidgetFactoryAdmin_i"); name[0].kind = CORBA::string_dup("WidgetFactoryAdmin_i"); obj = orb->resolve_initial_references("NameService"); CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow(obj); nc->rebind(name,wfa_obj);
And correspondingly unbind before deactivation:
nc->unbind(name);
Like the other CTL policy types the Auxiliary policies may be combined into a composite. So if a servant wanted to bind both to the IOR table and the Naming Service a composite policy type:
AUXPolicy<BindIORTable<D, AUXPolicy::BindRootNamingContext<D> >
could be used.
The CTL makes extensive use of the Curiously Recurring Template Pattern and Parameterized Inheritance to deliver a "composable" design. Composable in this context refers to the ability to extend an inheritance hierarchy by nesting template types. The main use for this technique in the CTL is to provide a consistent means by which to retrieve the necessary data needed during servant activation/deactivation. The choice of whether to "host" the data in the servant type or to "delegate" the request to some other type is left up to the user. Hosts store the data as a member of the servant type and provide a common interface used by the policies to retrieve that data. Delegates provide the same interface but act as proxies for the data. The CTL provides a number of Host and Delegate mix-in types in the CTL::MIX::Host and CTL::MIX::Delegate namespaces respectively.
All the types in the MIX namespace use both a D (Derived) and B (Base) template parameter and are therefore "composable" as nested templates. Assuming S is the servant base type and we wished to construct a servant that hosts its own ORB_var (CTL::MIX::Host::ORB) and POA_var (CTL::MIX::Host::POA) we could form the composite type by writing out the template CTL::MIX::Host::ORB<D, CTL::MIX::Host::POA<D, S> > where D, as noted above, represents the most derived type i.e. the type that inherits from the composite. To simplify the above, which gets rather ugly when many types are composed, the CTL::Compose namespace provides a family of templates that will allow the type to be expressed in a simpler manner. For instance, the composeDB type could be use to rewrite the above as CTL::Compose::composeDB<D,S, CTL::MIX::Host::ORB, CTL::MIX::Host::POA>::type, or without the fully qualified names composeDB<D,S,ORB, POA>::type.
For a further discussion of composition see the sidebar Template Techniques Used in the CTL (next page), and the material in the references section. The following sections present some of the more important elements of the CTL::MIX namespace.
Every servant needs a reference to an ORB so that a POA can either be found or created, and an Object can be activated. The ORB mix-in type provides an interface for accessing the ORB_var that the servant type will use. The Host variation maintains a member ORB_var and provides an additional method set_orb, while the Delegate dispatches the request to the servant's "container".
CTL provides a helper type CTL::OrbTask that is simply an ORB_var and thread that runs the ORB reactor loop. The mix-in type CTL::MIX::Host::ORBTask helps simplify many multi-orb applications.
Once a POA has been created or found on behalf of a servant, it needs to be stored for subsequent use during Object activation and deactivation. The mix-in types CTL::MIX::Host::POA and CTL::MIX::Delegate::POA provide an interface that returns a PortableServer::POA_var& that the activation/deactivation policy methods can use to store and gain access to the associated POA. The Host variety also provides a set_poa method.
Once a servant has been activated and an Object reference (IOR) has been generated that data needs to be stored somewhere. The CTL provides two simple holder types, CTL::BasicServant and CTL::TunneledServant. The only difference between the two is that CTL::TunneledServant allows the storage of a second IOR that identifies the servant, for instance an alternate route through a DSI gateway. Both Holder types support a getServantPtr and setServantPtr method. CTL::TunneledServant supports the additional methods getTServantPtr and setTServantPtr.
An optional Holder type that maintains a parameterized-backpointer to some arbitrary type is also provided. CTL::BasicContainer maintains this data and provides a simple interface for modifying and accessing the information. The Delegate types in CTL::MIX::Delegate use this information to redirect requests for activation data to the type specified to CTL::BasicContainer. The interface supported is very simple: getContainer, attachToContainer and detachFromContainer.
CTL::ServantState acts as a common base that servants can use to report activation and deactivation failure. The interface is simple and merely provides a method of setting and querying a bit field that represents different servant states. All CTL servants support the CTL::ServantState interface.
All CTL servants provide a simple interface: createServant() and destroyServant(). These in turn use the policy types provided to activate and deactivate the servant. Though the interface is simple the templated nature of the policy driven design allows for an unlimited set of servant activation/deactivation scenarios. Two base servant types are presented below. They are identical except that the second inherits from an extra "container" template type that represents a container holder type as described in section 3.4. The number of template parameters may seem extravagant but they allow us to formulate a generic servant activation/deactivation mechanism.
template <typename D,typename S, typename SS, typename PP = CTL::POAPolicy::Empty, typename OP = CTL::ObjPolicy::Empty, typename MP = CTL::MemPolicy::Empty, typename AP = CTL::AUXPolicy::Empty, typename B = CTL::EmptyType > struct CompositionServant : CTL::CompositionType<D,B>, S,SS,MP,PP,OP,AP,CTL::ServantState { typedef B BASE; typedef D DERIVED; virtual void createServant() { initializeMemoryPolicy( static_cast<DERIVED*>(this)); initializePOAPolicy( static_cast<DERIVED*>(this)); initializeObjectPolicy( static_cast<DERIVED*>(this)); initializeAUXPolicy( static_cast<DERIVED*>(this)); } virtual void destroyServant() { destroyAUXPolicy(static_cast<DERIVED*>(this)); destroyObjectPolicy( static_cast<DERIVED*>(this)); destroyPOAPolicy(static_cast<DERIVED*>(this)); destroyMemoryPolicy( static_cast<DERIVED*>(this)); } virtual ~CompositionServant() {} };
And the similar servant base type that also provides a template parameter for a container holder type.
template <typename D, typename S, typename SS, typename PP = CTL::POAPolicy::Empty, typename OP = CTL::ObjPolicy::Empty, typename MP = CTL::MemPolicy::Empty, typename AP = CTL::AUXPolicy::Empty, typename C = CTL::EmptyType, typename B = CTL::EmptyType > struct ContainedCompositionServant : CTL::CompositionServant< D,S,SS,MP,PP,OP,AP,B>, C { typedef B BASE; typedef D DERIVED; virtual ~ContainedCompositionServant() {} }; // The D parameter represents the Derived type. // The S parameter represents a Servant holder type // i.e. BasicServant<Example::Widget> // The SS parameter represents the skeleton type // i.e. POA_Example::Widget // PP is the POA policy // OP is the object-activation policy // MP is the memory policy // AP is the Auxiliary policy // B is an arbitrary base type // C, which is used in the second type, is the // Container Holder type.
Note that the resultant types inherit from all the template parameters and have their combined interface.
As shown above the CTL::ContainedCompositionServant type reuses the CTL::CompositionServant type. The CTL::CompositionType is not discussed here, but can be considered as B (the arbitrary base parameter).
To illustrate that the above is not as complicated as it seems from initial inspection, let's consider a simple servant. The servant will have a transient system id POA, and register with the Naming Service when activated.
module Example { interface MyType { string fn(); } };
The types Example::MyType and POA_Example::MyType are generated from the above IDL. Below is a ready to use servant type and server. Note that the fully qualified names are dropped for readability.
struct simple : composeDB< simple, CompositionServant< simple, BasicServant<Example::MyType>, POA_Example::MyType, POAPolicy::FromRoot< POAPolicy::TransientSystemID<> >, ObjPolicy::SystemID<>, MemPolicy::Empty, AUXPolicy::BindRootNamingContext< simple> >, Host::ORB, Host::POA >::type { simple() { set_cosnaming_data("simple", "simple kind"); } char* fn() { return CORBA::string_dup("Hello World"); } }; int main(int argc,char* argv[]) { simple s; s.orb_ = CORBA::ORB_init(argc,argv); s.createServant(); s.orb_->run(); return 0; }
The above is a complete server application, simple::createServant() will construct the POA, activate the servant and register it with the Naming Service using the id "simple" and the kind "simple kind". If a client resolved the Object reference in the Naming Service and invoked the fn() method, the string "Hello World" would be returned.
To illustrate the power and ease of use of the CTL, the following presents an example that uses many of the CTL features. Experienced CORBA developers will note how much simpler the code is than a similar traditional application [Henning-]. An equivalent classic application can be found on the ACCU website. Beyond the exotic-looking base types, the code is almost reduced to that of a standard C++ application. Furthermore, if an error exists in the setup machinery, we have a single point of failure and only a single policy that needs to be debugged.
The example demonstrates a simple Widget server application. The server uses two ORBs, one for public access to the factory's interface and one that provides private access (from the local machine) to the factory's administrative interface. The two different servants that implement these interfaces share common information and are in fact part of a larger Widget Factory "component". Both the servants of the factory component are persistent Objects and each advertises its existence with the Naming Service and IOR table. The publicly accessible interface provides a method for Widget creation while the private admin interface provides a method to destroy all the Widgets created and a method to shut down the Widget Factory.
<example.idl> module Example { interface Widget { void do_something(); }; interface WidgetFactory { Widget create_widget(); }; interface WidgetFactoryAdmin { void destroy_all_widgets(); void shutdown_widget_factory(); };
The above IDL describes the three basic interfaces and types that the following Widget factory will implement. Once it is run through an IDL compiler a skeleton and stub will be generated for Widget, WidgetFactory, and WidgetFactoryAdmin.
The servants presented here make use of a simple helper type CTL::RefCountImpl that extends the functionality of PortableServer::RefCountServantBase by providing methods to get the current reference count and to wait on a specific count.
template <typename D,typename S, typename SS,typename C> struct factory_servant : ContainedCompositionServant< D,BasicServant<S>, SS, MemPolicy::Empty, POAPolicy::FromRoot< D,POAPolicy::PersistantUserID<> >, ObjPolicy::UserID<D>, AUXPolicy::BindRootNamingContext< D,AUXPolicy::BindIORTable<D> >, BasicContainer<C*>, RefCountImpl< D,PortableServer ::RefCountServantBase> > {}; template <typename D,typename S, typename SS,typename C> struct simple_servant : ContainedCompositionServant< D,BasicServant<S>, SS, MemPolicy::Empty, POAPolicy::Empty, ObjPolicy::SystemID<D>, AUXPolicy::Empty, BasicContainer<C*>, RefCountImpl< D,PortableServer ::RefCountServantBase> > {};
These two servant base types will allow us to reuse the groups of policies without restating them for each servant type. The factory_servant base type will be shared between the WidgetFactory_i and WidgetFactoryAdmin_i types. The Widget_i type will use the base type simple_servant.
template <typename C> struct Widget_I : composeDB<Widget_i<C>, simple_servant<Widget_i<C>, Example::Widget, POA_Example::Widget,C>, Delegate::ORB,Delegate::POA>::type { void do_something() throw (CORBA::Exception){} };
Widgets are the product created by our factory for use by remote clients. Once Widget_i has been specialized for the container type it is a ready-to-use servant type. When createServant() is called the corresponding CORBA Object is activated and an Object reference is generated. WidgetFactoryAdmin_i will eventually call deactivateServant() for each of the Widget_i servants created by the factory.
template <typename C> struct WidgetFactory_I : composeDB<WidgetFactory_i<C>, factory_servant<WidgetFactory_i<C>, Example::WidgetFactory, POA_Example::WidgetFactory, C>, Host::ORBTask, Host::POA >::type { WidgetFactory_i() { set_cosnaming_data("WidgetFactory_i", "WidgetFactory_i"); set_object_id("WidgetFactory_i"); set_poa_id("WidgetFactory_i"); set_iortable_id("WidgetFactory_i"); } Example::Widget_ptr create_widget() throw (CORBA::SystemException) { ACE_Guard<ACE_Thread_Mutex> grd(getContainer()->mtx_); Widget_i<C> * p = new Widget_i<C>; //exception unsafe for clarity p->attachToContainer(getContainer()); p->createServant(); getContainer()->widget_data_.push_back(p); return p->getServantPtr(); } };
The WidgetFactory interface is the publicly visible portion of our component; any remote client can request a new Widget. WidgetFactory_i, a servant type that supports the WidgetFactory interface, is a template that generates a servant once it is specialized with a container type. The C (container) parameter will later be provided as WidgetFactoryData. Before the servant is activated, the user will provide a (C*) WidgetFactoryData* by way of attachToContainer, allowing us to gain access to the vector of Widget_i<WidgetFactoryData>* that is shared between the public factory interface's servant and the private factory admin interface's servant that is a member of WidgetFactoryData.
template <typename C> struct WidgetFactoryAdmin_I : composeDB<WidgetFactoryAdmin_i<C>, factory_servant< WidgetFactoryAdmin_i<C>, Example::WidgetFactoryAdmin, POA_Example::WidgetFactoryAdmin,C>, Host::ORBTask, Host::POA >::type { WidgetFactoryAdmin_i() { set_cosnaming_data("WidgetFactoryAdmin_i", "WidgetFactoryAdmin_i"); set_object_id("WidgetFactoryAdmin_i"); set_poa_id("WidgetFactoryAdmin_i"); set_iortable_id("WidgetFactoryAdmin_i"); } void destroy_all_widgets() throw (CORBA::SystemException) { ACE_Guard<ACE_Thread_Mutex> grd(getContainer()->mtx_); for(std::size_t i=0; i<getContainer()->widget_data_.size(); ++i) { //simple loop for clarity getContainer() ->widget_data_[i]->destroyServant(); getContainer() ->widget_data_[i]->_remove_ref(); } getContainer()->widget_data_.clear(); } void shutdown_widget_factory() throw (CORBA::SystemException) { getContainer()->event_.signal(); } };
WidgetFactoryAdmin_i is similar to the above WidgetFactory_i, except for the interface the servant will expose to remote clients. It also requires a template parameter to designate its "container" type, which will later be specified as WidgetFactoryData. Like the above servant type it also hosts its own CTL::OrbTask and POA. Because WidgetFactory_i and WidgetFactoryAdmin_i operate on independent ORBs a request coming in on WidgetFactory_i's ORB can not gain access to the WidgetFactoryAdmin_i. Therefore if the ORB hosting the admin servant is listening on localhost:10000 only local access is granted to the admin interface.
struct WidgetFactoryData { ACE_Event event_; ACE_Thread_Mutex mtx_; PortableServer::POA_var widget_poa_; WidgetFactory_i<WidgetFactoryData> factory_; WidgetFactoryAdmin_i<WidgetFactoryData> factory_admin_; std::vector<Widget_i<WidgetFactoryData>*> widget_data_; // widget delegates to container to // determine orb to use CORBA::ORB_var& orb(Widget_i< WidgetFactoryData>*) {return factory_.orb_;} // widget delegates to container // to determine which POA to use PortableServer::POA_var& poa(Widget_i< WidgetFactoryData>*) {return widget_poa_;} };
WidgetFactoryData is a "container" for all three servant-types. It provides the necessary interface for the Widget_i servant delegates, and specializes the WidgetFactory_i and WidgetFactoryAdmin_i templates with itself as the container type. Because Widgets are publicly available we reuse the public ORB specified by the WidgetFactory_i servant. WidgetFactoryData stores all the information that's shared across our "component".
int main(int argc,char* argv[]) { try { WidgetFactoryData data; data.factory_.orb_.initialize( argc,argv,"public"); data.factory_admin_.orb_.initialize( argc,argv,"local");
Instantiate the "container" and initialize/activate the two independent ORBs
data.factory_.attachToContainer(&data); data.factory_admin_.attachToContainer(&data); data.factory_.createServant(); data.factory_admin_.createServant();
Attach the two factory servant types to the container and create the Objects, which will by virtue of the policies given automatically register themselves with the IOR table and the Naming Service.
data.widget_poa_ = POAPolicy::create_from_poa< POAPolicy::TransientSystemID<> > (data.factory_.orb_, data.factory_.poa_, "widget_poa");
Create the POA that Widget_Is will be activated in.
data.event_.wait();
Wait for the WidgetFactoryAdmin_i servant to signal that the application should shut down. And then proceed to final cleanup.
data.factory_admin_.destroy_all_widgets();
Destroy all Widget_i instances that were created on behalf of clients.
data.widget_poa_->destroy(true,true);
Destroy the POA we manually created above
data.factory_admin_.destroyServant(); data.factory_.destroyServant();
Destroy the two factory servants
data.factory_.orb_->destroy(); data.factory_admin_.orb_->destroy ();
Finally, shutdown the two ORBs and their associated threads.
} catch(const CORBA::Exception&) {return -1;} catch(...) {return -2;} return 0; }
With the aid of a policy-driven CORBA template library, the deployment of the machinery needed to build large-scale distributed object computing facilities has been greatly simplified. Expertise about the CORBA setup mechanisms has been encapsulated in an extensible and easily applied framework of policies, thus allowing rapid development that concentrates on the application's functionality and lowers the barrier to entry for developers. Code reuse is promoted without relying on methods that are, at best, hard to extend (procedural, simple objectoriented inheritance or cut and paste). Therefore, the developer's task is reduced to that of providing the application's core functionality, freed of the burdens imposed by classic CORBA application designs. Easy extensibility ensures that future needs such as Real-Time ORB usage, SSLIOP setup, and Trading Service registration can be added once as policies and deployed with minimal effort.
Overload Journal #57 - Oct 2003 + Programming Topics
Browse in : |
All
> Journals
> Overload
> 57
(12)
All > Topics > Programming (877) Any of these categories - All of these categories |