首页 > 代码库 > [搬运]CORBA中BOA和POA的含义

[搬运]CORBA中BOA和POA的含义

先来BOA,搬自:http://www.cl.cam.ac.uk/research/dtg/attarchive/omniORB/doc/2.8/omniORB2/node6.html

The Basic Object Adaptor (BOA)

 

This chapter describes the BOA implementation in omniORB2. The CORBA specification defines the Basic Object Adaptor as the entity that mediates between object implementations and the ORB. Unfortunately, the BOA specification is incomplete and does not address the multi-threading issues appropriately. The end result is that different ORB vendors implement different extensions to their BOAs. Worse, the implementation of the operations defined in the specification are different in different ORBs. Recently, a new Object Adaptor specification (the Portable Object Adaptor- POA) has been adopted and will replace the BOA as the standard Object Adaptor in CORBA. The new specification recognises the compatibility problems of BOA and recommends that all BOAs should be considered propriety extensions. OmniORB2 will support POA in future releases. Until then, you have to use the BOA to attach object implementations to the ORB.

The rest of this chapter describes the interface of the BOA in detail. It is important to recognise that the interface described below is omniORB2 specific and hence the code using this interface is unlikely to be portable to other ORBs.

Unless it is stated otherwise, the term ``object‘‘ will be used below to refer to object implementations. This should not be confused with ``object references‘‘ which are handles held by clients.

 

BOA Initialisation

It takes two steps to put the BOA into service. The BOA has to be initialised using BOA_init and activated using impl_is_ready.

BOA_init is a member of the CORBA::ORB class. Its signature is:

 

BOA_ptr BOA_init(int & argc,                 char ** argv,                 const char * boa_identifier);

Typically, it is used in the startup code as follows:

 

CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2");   // line 1CORBA::BOA_ptr boa = orb->BOA_init(argc,argv,"omniORB2_BOA"); // line 2

The argv parameters may contain BOA options. These options will be removed from the argv list when BOA_init returns. Other parameters in argv will remain. The supported options are:

 

-BOAiiop_port <port number (0-65535)>
Use the port number to receive IIOP requests. This option can be specified multiple times in the command line and the BOA would be initialised to listen on all of the ports.

 

-BOAid <id (string)>
If this option is used the id must be ``omniORB2_BOA‘‘.

 

-BOAiiop_name_port <hostname[:port number]>
Similar to -BOAiiop_port, this options tells the BOA the hostname and optionally the port number to use.

 

If the third argument of BOA_init is non-nil, it must be the string constant ``omniORB2_BOA‘‘. If the argument is nil, -BOAid must be present in argv.

If there is any problem in the initialisation process, a CORBA::INITIALIZE exception would be raised.

To register an object with the BOA, the method _obj_is_ready should be called with the return value of BOA_init as the argument.

BOA_init is thread-safe. It can be called multiple times and the same BOA_ptr will be returned. However, only the argv in the first call will be scanned, the argument is ignored in subsequent calls.

BOA_init returns a pseudo object of type CORBA::BOA_ptr. Similar to CORBA::Object_ptr, the pointer can be managed using CORBA::BOA_varBOA::_duplicate and CORBA::release. The pointer can be tested using CORBA::is_nil which returns true if the pointer is equivalent to the return value of BOA::_nil.

After BOA_init is called, objects can be registered. However, incoming IIOP requests would not be despatched until impl_is_ready is called.

 

class BOA {public:   impl_is_ready(CORBA::ImplementationDef_ptr p = 0,                 CORBA::Boolean NonBlocking = 0);};

One of the common pitfall in using the BOA is to forget to call impl_is_ready. Until this call returns, there is no thread listening on the port from which IIOP requests are received. The remote client may hang because of this.

When impl_is_ready is called with no argument. The calling thread would be blocked indefinitely in the function until impl_shutdown (see below) is called. The thread that is calling impl_is_ready is not used by the BOA to perform its internal functions. The BOA has its own set of threads to process incoming requests and general housekeeping. Therefore, it is not necessary to have a thread blocked in the call if it can be put into use elsewhere. For example, the main thread may callimpl_is_ready once in non-blocking mode (see below) and then enter the event loop to handle the GUI frontend.

If non-blocking behaviour is needed, the NonBlocking argument should be set to 1. For instance, if you creates a callback object, you might call impl_is_ready in non-blocking mode to tell the BOA to start receiving IIOP requests before sending the callback object to the remote object. The first argument ImplementationDef_ptr is ignored by the BOA. Just set the argument to nil.

impl_is_ready is thread safe and can be called multiple times. Multiple threads can be blocked in impl_is_ready.

 

Object Registration

Once the BOA is initialised, objects can be registered. The purpose of object registration is to let the BOA know of the existence of the object and to dispatch requests for the object as upcalls into the object.

To register an object, the _obj_is_ready function should be called. _obj_is_ready is a member function of the implementation skeleton class. The function should be called only once for each object. The call should be made only after the object is fully initialised.

The member function obj_is_ready of the BOA may also be used to register an object. However, this function has been superseded by _obj_is_ready and should not be used in new application code.

 

Object Disposal

Once an object is registered, it is under the management of the BOA. To remove the object from the BOA and to delete it (when it is safe to do so), the _disposefunction should be called. _dispose is a member function of the implementation skeleton class. The function should be called only once for each object.

Notice the asymmetry in object instantiation and destruction. To instantiate an object, the application code has to call the new operator. To remove the object, the application should never call the delete operator on the object directly.

At the time the _dispose call is made, there may be other threads invoking on the object, the BOA ensures that all these calls are completed before removing the object from its internal tables and calling the delete operator.

Internally, the BOA keeps a reference count on each object. Initially, the reference count is 0. After a call to _obj_is_ready, the reference count is 1. The BOA increases the reference count by 1 before an upcall into the object is made. The count is decreased by 1 when the upcall returns. _dispose decreases the reference count by 1, if the reference count is 0, the delete operator is called. If the count is non-zero, the object is marked as disposed. The object will be deleted when the reference count eventually goes to zero.

The reference count is also increased by 1 for each object reference held in the same address space. Hence, the delete operator will not be called when there are outstanding object references in the same address space. To ensure that an object is deleted, all its object references in the same address space should be released using CORBA::release.

Unlike colocated object references, references held by clients in other address spaces would not prevent the deletion of objects. If these clients invoke on the object after it is disposed, the system exception INV_OBJREF would be raised. The difference in semantics is an undesirable side-effect of the current BOA implementation. In future, colocated references will have the same semantics as remote references, i.e. their presence will not delay the deletion of the objects.

Instead of _dispose, it may be useful to have a method to deactivate the object but not deleting it. This feature is not supported in the current BOA implementation.

 

BOA Shutdown

The BOA can be withdrawn from service using member functions impl_shutdown and destroy.

 

class BOA {public:   void impl_shutdown();   void destroy();};

impl_shutdown and destroy are the inverse of impl_is_ready and BOA_init respectively.

impl_shutdown deactivates the BOA. When the call returns, all the internal threads and network connections will be shutdown. Any thread blocking in impl_is_ready would be unblocked. After the call, no request from other address spaces will be processed. In other words, the BOA will be in the same state as it was in before impl_is_readywas called. For example, a remote client may hang if it tries to connect to the server after impl_shutdown was called because no thread is listening on the IIOP port.

impl_shutdown does not wait for incoming requests to complete before it closes the network connections. The remote clients will see the network connections shutdown and the replies may not reach them even if the upcalls have been completed. Therefore, if the application is to define an operation in an IDL interface to shutdown the BOA, the operation should be defined as an oneway operation.

impl_shutdown is thread-safe and can be called multiple times. The call is silently ignored if the BOA has already been shutdown. After impl_shutdown is called, the BOA can be reactivated by another call to impl_is_ready.

It should be noted that impl_shutdown does not affect outgoing network connections. That is, clients in the same address space will still be able to make calls to objects in other address spaces.

While remote requests are not delivered after impl_shutdown is called, the current implementation does not stop colocated clients from calling the objects. In future, colocated clients will exhibit the same behaviour as remote clients.

destroy permanently removed the BOA. This function will call impl_shutdown implicitly if it has not been called. When this call returns, the IIOP port(s) held by the BOA will be freed. Remote clients will see their requests refused by the operating system when they try to open a connection to the IIOP port(s).

After destroy is called, the BOA should not be used. If there is any objects still registered with the BOA, the objects should not be invoked afterwards. The objects are not disposed. Invoking on the objects after destroy would result in undefined behaviour. Initialisation of another BOA using BOA_init is not supported. The behaviour of BOA_init after this call is undefined.

 

Unsupported functions

The following member functions are not implemented. Calling these functions do not have any effect.

 

  • Object_ptr create(...)
  • ReferenceData* get_id(Object_ptr)
  • Principal_ptr get_principal(Object_ptr,Environment_ptr)
  • void change_implementation(Object_ptr, ImplementationDef_ptr)
  • void deactivate_impl(ImplementationDef_ptr)
  • void deactivate_obj(Object_ptr)

Loading Objects On Demand

 

Since 2.5.0, there is limited support for loading objects on demand. An application can register a handler for loading objects dynamically. The handler should have the signature omniORB::loader::mapKeyToObject_t:

 

  namespace omniORB {    ...    class loader {    public:      typedef CORBA::Object_ptr (*mapKeyToObject_t) (const objectKey& key);      static void set(mapKeyToObject_t NewKeyToObject);    };  };

When the ORB cannot locate the target object in this address space, it calls the handler with the object key of the target. The handler is expected to instantiate the object, either in this address space or in another address space, and returns the object reference to the newly instantiated object. The ORB will then reply with a LOCATION_FORWARD message to instruct the client to retry using the object reference returned by the handler. When the handler returns, the ORB assumes ownership of the returned value. It will call CORBA::release() on the returned value when it has finished with it.

The handler may be called concurrently by multi-threads. Hence it must be thread-safe.

If the handler cannot load the target object, it should return CORBA::Object::_nil(). The object will be treated as non-existing.

The application registers the handler with the ORB at runtime using omniORB::loader::set(). This function is not thread-safe. Calling this function again will replace the old handler with the new one.

 

再来POA,搬自:http://www.dre.vanderbilt.edu/~schmidt/DOC_ROOT/TAO/docs/poa_migration.html

Migrating CORBA Applications from BOA to POA

Starting with the CORBA 2.2, the Basic Object Adapter (BOA) has been deprecated in favor of the Portable Object Adapter (POA). This document explains the changes required to migrate CORBA applications based on the BOA to use TAO‘s POA implementation, which is the only Object Adapter supported by TAO. For more information on the benefits of the POA please see the Object Interconnection columns written by Doug Schmidt and Steve Vinoski.

Contents

  • Client-side Changes
  • Server-side Changes
  • Reference counting Servants

Client-side Changes

    • Very little has changed. Thus, many applications require no changes.

 

Server-side Changes

    • POA_init is replaced with resolve_initial_references("RootPOA") followed by a _narrow operation.

 

    • The implementation no longer inherits from the client-side stub. Instead, they inherit from PortableServer::ServantBase. The implications of this are (a) if you want a object reference for that, you must use the _this method.

 

    • Object ID‘s are assigned by the POA unless you activate the servant with a specific ID. IDL_Cubit has examples on how to do this.

 

    • Unlike the BOA, the POA explicitly addresses the temporal nature of servants and declares that a POA can service either transient or persistent servants (not both). The root POA‘s (mandated, unchangeable) policy is "transient". The implications of this are that in order for a client to be able to manufacture an object reference on its own and use that to access an object, the servant for that object must be registered with a POA whose policy is "persistent". Thus, you must create a child POA with that policy and register the servant with that POA. NOTE: when the POA declares something as "persistent", it is only stating that the key is valid between different runs of the server; it makes no claims that state or anything else is persistent.

 

      • Servants are not automatically activated, hence you must register them by calling some of the activate_object* methods on a POA or calling _this on the servant; with the latest you have no control on the ObjectId (which sometimes is good), and the POA must support the right policies (the RootPOA does).

 

      • Servant constructors use to take a const char* parameter to set they object id, this is not needed now, in fact in many cases they use to pass this argument to the skeleton class: this will fail now.

 

      This list is not intended to be exhaustive, but should give you a good starting point. If you find things along the way that change your applications and I didn‘t note them, please send them to me. Perhaps we can work together on the ultimate migration document.

 

Reference counting Servants

The new POA/servant reference counting rules of the CORBA 2.3 spec are somewhat tricky. Here are two main reasons why:

 

    • If a servant is deleted without deactivating from the POA, the application will crash because the POA will try to access the still registered (but now non-existent) servant when the POA is destroyed.

      The solution to this is to make sure that the servant is deleted after the POA is deleted or make sure that the servant is deactivated from the POA before the servant is deleted.

 

  • You cannot delete a servant which is the target of the current upcall/request. A good example of this is the typical destroy() method, usually written like this:
    class TAO_Export TAO_Thread_Policy : public POA_PortableServer::ThreadPolicy{  void destroy (CORBA_Environment &ACE_TRY_ENV);};voidTAO_Thread_Policy::destroy (CORBA::Environment &ACE_TRY_ENV){  PortableServer::POA_var poa = this->_default_POA (ACE_TRY_ENV);  ACE_CHECK;  PortableServer::ObjectId_var id = poa->servant_to_id (this,                                                        ACE_TRY_ENV);  ACE_CHECK;  poa->deactivate_object (id.in (),                          ACE_TRY_ENV);  ACE_CHECK;  // Commit suicide: must have been dynamically allocated.  delete this;}
    The correct implementation is:
    class TAO_Export TAO_Thread_Policy : public virtual PortableServer::RefCountServantBase,                                     public virtual POA_PortableServer::ThreadPolicy{  void destroy (CORBA_Environment &ACE_TRY_ENV);};voidTAO_Thread_Policy::destroy (CORBA::Environment &ACE_TRY_ENV){  //  // Remove self from POA.  Because of reference counting, the POA  // will automatically delete the servant when all pending requests  // on this servant are complete.  //  PortableServer::POA_var poa = this->_default_POA (ACE_TRY_ENV);  ACE_CHECK;  PortableServer::ObjectId_var id = poa->servant_to_id (this,                                                        ACE_TRY_ENV);  ACE_CHECK;  poa->deactivate_object (id.in (),                          ACE_TRY_ENV);  ACE_CHECK;}
    One additional step required is to make the POA responsible for the servant after it has been registered with the POA:
      // Register with the POA.  PortableServer::ThreadPolicy_var result = new_thread_policy->_this (ACE_TRY_ENV);  // Give ownership of this servant to the POA.  new_thread_policy->_remove_ref (ACE_TRY_ENV);
    If you use the above approach of multiple inheritance, you must add the following to your header file:
    // This is to remove "inherits via dominance" warnings from MSVC.// MSVC is being a little too paranoid.#if defined (_MSC_VER)# pragma warning (disable : 4250)#endif /* _MSC_VER */
    To see the above example in detail, checkout TAO/examples/POA/Reference_Counted_Servant and/or Root_POA.cpp and Root_POA.h.

[搬运]CORBA中BOA和POA的含义