BEA Logo BEA BEA Tuxedo Release [Release Number]

  BEA Home  |  Events  |  Solutions  |  Partners  |  Products  |  Services  |  Download  |  Developer Center  |  WebSUPPORT

 

   BEA Tuxedo Doc Home   |   CORBA Programming Reference   |   Previous Topic   |   Next Topic   |   Contents   |  

Server-side Mapping

 

Server-side mapping refers to the portability constraints for an object implementation written in C++. The term server is not meant to restrict implementations to situations in which method invocations cross-address space or machine boundaries. This mapping addresses any implementation of an Object Management Group (OMG) Interface Definition Language (IDL) interface.

Note: The information in this chapter is based on the Common Object Request Broker: Architecture and Specification, Revision 2.4.2, February 2001, published by the Object Management Group (OMG). Used with permission of the OMG.

 


Implementing Interfaces

To define an implementation in C++, you define a C++ class with any valid C++ name. For each operation in the interface, the class defines a nonstatic member function with the mapped name of the operation (the mapped name is the same as the OMG IDL identifier).

The server application mapping specifies two alternative relationships between the implementation class supplied by the application and the generated class or classes for the interface. Specifically, the mapping requires support for both inheritance-based relationships and delegation-based relationships. Conforming applications may use either or both of these alternatives. BEA Tuxedo CORBA supports both inheritance-based and delegation-based relationships.

 


Inheritance-based Interface Implementation

In the inheritance-based interface implementation approach, the implementation classes are derived from a generated base class based on the OMG IDL interface definition. The generated base classes are known as skeleton classes, and the derived classes are known as implementation classes. Each operation of the interface has a corresponding virtual member function declared in the skeleton class. The generated skeleton class is partially opaque to the programmer, though it will contain a member function corresponding to each operation in the interface. The signature of the member function is identical to that of the generated client stub class.

To implement this interface using inheritance, a programmer must derive from this skeleton class and implement each of the operations in the OMG IDL interface. To allow portable implementations to multiple inheritances from both skeleton classes and implementation classes for other base interfaces without error or ambiguity, the Tobj_ServantBase class must be a virtual base class of the skeleton, and the PortableServer::ServantBase class must be a virtual base class of the Tobj_ServantBase class. The inheritance among the implementation class, the skeleton class, the Tobj_ServantBase class, and the PortableServer::ServantBase class must all be public virtual.

The implementation class or servant must only derive directly from a single generated skeleton class. Direct derivation from multiple skeleton classes could result in ambiguous errors due to multiple definitions of the _this() operation. This should not be a limitation, however, since CORBA objects have only a single most-derived interface. C++ servants that are intended to support multiple interface types can utilize the delegation-based interface implementation approach. See Listing 15-1 for an example of OMG IDL that uses interface inheritance.

Listing 15-1 OMG IDL That Uses Interface Inheritance

// IDL
interface A
{
short op1() ;
void op2(in long val) ;
};

Listing 15-2 Interface Class A

// C++ 
class A : public virtual CORBA::Object
{
public:
virtual CORBA::Short op1 ();
virtual void op2 (CORBA::Long val);
};

On the server side, a skeleton class is generated. This class is partially opaque to the programmer, though it does contain a member function corresponding to each operation in the interface.

For the Portable Object Adapter (POA), the name of the skeleton class is formed by prepending the string "POA_" to the fully scoped name of the corresponding interface, and the class is directly derived from the servant base class Tobj_ServantBase. The C++ mapping for Tobj_ServantBase is as follows:

// C++
class Tobj_ServantBase
{
public:
virtual void activate_object(const char* stroid);
virtual void deactivate_object (
const char* stroid,
TobjS::DeactivateReasonValue reason
);
}

The activate_object() and deactivate_object() member functions are described in detail in the sections Tobj_ServantBase:: activate_object() and Tobj_ServantBase::_add_ref().

The skeleton class for interface A shown above would appear as shown in Listing 15-3.

Listing 15-3 Skeleton Class for Interface A

// C++
class POA_A : public Tobj_ServantBase
{
public:
// ... server-side ORB-implementation-specific
// goes here...
        virtual CORBA::Short op1 () = 0; 
virtual void op2 (CORBA::Long val) = 0;
//...
};

If interface A were defined within a module rather than at global scope (for example, Mod::A), the name of its skeleton class would be POA_Mod::A. This helps to separate server application skeleton declarations and definitions from C++ code generated for the client.

To implement this interface using inheritance, you must derive from this skeleton class and implement each of the operations in the corresponding OMG IDL interface. An implementation class declaration for interface A would take the form shown in Listing 15-4.

Listing 15-4 Interface A Implementation Class Declaration

// C++
class A_impl : public POA_A
{
public:
CORBA::Short op1();
void op2(CORBA::Long val);
...
};

 


Delegation-based Interface Implementation

The delegation-based interface implementation approach is an alternative to using inheritance when implementing CORBA objects. This approach is used when the overhead of inheritance is too high or cannot be used. For example, due to the invasive nature of inheritance, implementing objects using existing legacy code might be impossible if inheritance for some global class were required. Instead, delegation can be used to solve these types of problems. Delegation is a more natural fit doing object implementations when the Process-Entity design pattern is used. In this pattern, the Process object would delegate operations onto one or more entity objects.

In the delegation-based approach, the implementation does not inherit from a skeleton class. Instead, the implementation can be coded as required for the application, and a wrapper object will delegate upcalls to that implementation. This "wrapper object," called a tie, is generated by the IDL compiler, along with the same skeleton class used for the inheritance approach. The generated tie class is partially opaque to the programmer, though, like the skeleton, it provides a method corresponding to each OMG IDL operation for the associated interface. The name of the generated tie class is the same as the generated skeleton class with the addition that the string _tie is appended to the end of the class name.

An instance of the tie class is the servant, not the C++ object being delegated to by the tie object, that is passed as the argument to the operations that require a Servant argument. It should also be noted that the tied object has no access to the _this( ) operation, nor should it access data members directly.

A type-safe tie class is implemented using C++ templates. The code shown in Listing 15-5 illustrates a tie class generated from the Derived interface in the previous OMG IDL example.

Listing 15-5 Tie Class Generated from the Derived Interface

// C++
template <class T>
class POA_A_tie : public POA_A {
public:
POA_A_tie(T& t)
: _ptr(&t), _poa(PortableServer::POA::_nil()), _rel(0) {}
POA_A_tie(T& t, PortableServer::POA_ptr poa)
: _ptr(&t), _poa(PortableServer::POA::_duplicate(poa)), _rel(0) {}
POA_A_tie(T* tp, CORBA::Boolean release = 1)
: _ptr(tp), _poa(PortableServer::POA::_nil()), _rel(release) {}
POA_A_tie(T* tp, PortableServer::POA_ptr poa, CORBA::Boolean release = 1)
: _ptr(tp), _poa(PortableServer::POA::_duplicate(poa)), _rel(release) {}
~POA_A_tie()
{ CORBA::release(_poa);
if (_rel) delete _ptr;
}

// tie-specific functions
T* _tied_object () {return _ptr;}
void _tied_object(T& obj)
{ if (_rel) delete _ptr;
_ptr = &obj;
_rel = 0;
}
void _tied_object(T* obj, CORBA::Boolean release = 1)
{ if (_rel) delete _ptr;
_ptr = obj;
_rel = release;
}

CORBA::Boolean _is_owner() { return _rel; }
void _is_owner (CORBA::Boolean b) { _rel = b; }

// IDL operations*************************************
CORBA::Short op1 ()
{
return _ptr->op1 ();
}


void op2 (CORBA::Long val)
{
_ptr->op2 (val);
}
// ***************************************************

// override ServantBase operations
PortableServer::POA_ptr _default_POA()
{
if (!CORBA::is_nil(_poa))
{
return _poa;
}
else {
#ifdef WIN32
return ServantBase::_default_POA();
#else
return PortableServer::ServantBase::_default_POA();
#endif
}
}

private:
T* _ptr;
PortableServer::POA_ptr _poa;
CORBA::Boolean _rel;

// copy and assignment not allowed
POA_A_tie (const POA_A_tie<T> &);
void operator=(const POA_A_tie<T> &);
};

This class definition is a template generated by the IDL compiler. You typically use it by first getting a pointer to the legacy class and then instantiating the tie class with that pointer. For example:

Old::Legacy * legacy = new Old::Legacy( oid);
POA_A_tie<Old::Legacy> * A_servant_ptr =
new POA_A_tie<Old::Legacy>( legacy );

As you can see, the tie class contains definitions for the op1 and op2 operations of the interface that assume that the legacy class has operations with the same signatures as those given in the IDL. If this is the case, you can use the tie class file as is, letting it delegate exactly. It is more likely, however, that the legacy class will not have identical signatures or you may have to do more than a single function call. In that case, it is your job to replace the code for op1 and op2 in this generated code. The code for each operation typically makes invocations on the legacy class using the tie class variable _ptr, which contains the pointer to the legacy class. For example, you might change the following lines:

    CORBA::Short op1 () {return _ptr->op1 (); }
void op2 (CORBA::Long val) {_ptr->op2 (val); }

to the following:

CORBA::Short op1 ()
{
return _ptr->op37 ();
}

void op2 (CORBA::Long val)
{
CORBA::Long temp;
temp = val + 15;
_ptr->lookup(val, temp, 43);
}

An instance of this template class performs the task of delegation. When the template is instantiated with a class type that provides the operation of the Derived interface, then the POA_Derived_tie class will delegate all operations to an instance of that implementation class. A reference or pointer to the actual implementation object is passed to the appropriate tie constructor when an instance of the POA_Derived_tie class is created. When a request is invoked on it, the tie servant will just delegate the request by calling the corresponding method on the implementation class.

The use of templates for tie classes allows the application developer to provide specializations for some or all of the template's operations for a given instantiation of the template. This allows the application to use legacy classes for tied object types, where the operation signatures of the tied object will differ from that of the tie class.

 


Implementing Operations

The signature of an implementation member function is the mapped signature of the OMG IDL operation. Unlike the client-side mapping, the OMG specifies that the function header for the server-side mapping include the appropriate exception specification. An example of this is shown in Listing 15-6.

Listing 15-6 Exception Specification

// IDL
interface A
{
exception B {};
void f() raises(B);
};
// C++
class MyA : public virtual POA_A
{
public:
void f();
...
};

Since all operations and attributes may raise CORBA system exceptions, CORBA::SystemException must appear in all exception specifications, even when an operation has no raises clause.

Note: Because of the differences in C++ compilers, it is best to leave out the "throw declaration" in the method signature. Some systems cause the application server to crash if an undeclared exception is thrown in a method that has declared the exceptions it will throw.

Within a member function, the "this" pointer refers to the implementation object's data as defined by the class. In addition to accessing the data, a member function may implicitly call another member function defined by the same class. An example of this is shown in Listing 15-7.

Listing 15-7 Calling Another Member Function

// IDL
interface A
{
void f();
void g();
};

// C++
class MyA : public virtual POA_A
{
public:
void f();
void g();
private:
long x_;
};

void
MyA::f();
{
x_ = 3;
g();
}

When a servant member function is invoked in this manner, it is being called simply as a C++ member function, not as the implementation of an operation on a CORBA object.

 

back to top previous page