Tuxedo CORBA プログラミング リファレンス

     前  次    新しいウィンドウで目次を開く     
ここから内容の開始

サーバサイドのマッピング

サーバサイドのマッピングでは、C++ で記述されたオブジェクト実装の移植性の制約を参照します。ここでは、「サーバ」という用語は、メソッド呼び出しで領域間またはマシン間でアドレス指定する状況に実装を制限するという意味ではありません。ここでのマッピングは、Object Management Group (OMG) インタフェース定義言語 (IDL) のインタフェースのあらゆる実装にアドレス指定します。

注意 : この章の情報は、Object Management Group (OMG) の「Common Object Request Broker: Architecture and Specification, Revision 2.4.2」(2001 年 2 月) に基づいています。また、この情報は、OMG の許可を得て転載しています。

 


インタフェースの実装

C++ で実装を定義するには、有効な C++ の名前で C++ クラスを定義します。インタフェースのオペレーションごとに、クラスはオペレーションのマッピングされた名前で非静的なメンバー関数を定義します。マッピングされた名前は、OMG IDL の識別子と同じです。

サーバ アプリケーションのマッピングでは、アプリケーションで提供された実装クラスと、インタフェースに対して生成されたクラスとの間に別の 2 つの関係を指定します。具体的には、継承ベースの関係とデレゲーション ベースの関係の両方のサポートがマッピングで必要になります。標準的なアプリケーションでは、これらの関係の一方または両方を使用できます。Oracle Tuxedo CORBA では、継承ベースとデレゲーション ベースの両方の関係をサポートしています。

 


継承ベースのインタフェース実装

継承ベースのインタフェース実装の手法では、実装クラスは、OMG IDL インタフェース定義に基づいて生成された基本クラスから派生します。生成された基本クラスはスケルトン クラスと呼ばれ、派生クラスは実装クラスと呼ばれます。インタフェースの各オペレーションには、スケルトン クラスで宣言された対応するメンバー関数があります。生成されたスケルトン クラスは、インタフェースの各オペレーションに対応するメンバー関数を備えていますが、プログラマにとってはオペークな部分もあります。メンバー関数のシグネチャは、生成されたクライアント スタブ クラスのシグネチャと同じです。

このインタフェースを継承で実装するには、プログラマはこのスケルトン クラスから派生させて、OMG IDL インタフェースでオペレーションごとに実装する必要があります。ほかの基本インタフェース用のスケルトン クラスと実装クラスから、エラーやあいまいさをなくして複数のインタフェースに移植性のある実装を可能にするには、スケルトンの仮想基本クラスに Tobj_ServantBase クラスを、Tobj_ServantBase クラスの仮想基本クラスに PortableServer::ServantBase を使用する必要があります。実装クラス、スケルトン クラス、Tobj_ServantBase クラス、および PortableServer::ServantBase クラスの継承は、すべてパブリック仮想でなければなりません。

実装クラスまたはサーバントは、単一の生成されたスケルトン クラスからのみ直接派生しなければなりません。複数のスケルトンから直接派生すると、_this() オペレーションの定義が複数あるため、あいまいエラーが発生します。ただし、CORBA オブジェクトには単一の最終派生インタフェースのみがあるので、これは制限ではありません。C++ サーバントが複数のインタフェース型をサポートしている場合、デレゲーション ベースのインタフェース実装の手法に利用できます。コード リスト 15-1 に、インタフェース継承を使用した OMG IDL の例を示します。

コード リスト 15-1 インタフェース継承を使用した OMG IDL
// IDL
interface A
{
short op1() ;
void op2(in long val) ;
};
コード リスト 15-2 インタフェース クラス A
// C++ 
class A : public virtual CORBA::Object
{
public:
virtual CORBA::Short op1 ();
virtual void op2 (CORBA::Long val);
};

サーバサイドではスケルトン クラスが生成されます。このクラスは、インタフェースの各オペレーションに対応するメンバー関数を備えていますが、プログラマにとってはオペークな部分もあります。

ポータブル オブジェクト アダプタ (POA) の場合、スケルトン クラスの名前は、対応するインタフェースの完全にスコープ指定された名前の先頭に文字列「POA_」が付きます。また、このクラスは、サーバントの基本クラス Tobj_ServantBase から直接派生します。次に、Tobj_ServantBase の C++ マッピングを示します。

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

activate_object() および deactivate_object() メンバー関数の詳細については、「Tobj_ServantBase:: activate_object()」および「Tobj_ServantBase::_add_ref()」を参照してください。

コード リスト 15-3 に、上で示したインタフェース A のスケルトン クラスを示します。

コード リスト 15-3 インタフェース A のスケルトン クラス
// C++
class POA_A : public Tobj_ServantBase
{
public:
// ... サーバサイドの ORB 実装
// 固有の記述 ...
        virtual CORBA::Short op1 () = 0; 
virtual void op2 (CORBA::Long val) = 0;
//...
};

グローバル スコープ (Mod::A など) ではなく、モジュール内部でインタフェース A が定義された場合、そのスケルトン クラス名は POA_Mod::A になります。これは、サーバ アプリケーションのスケルトンの宣言および定義と、クライアントから生成された C++ コードとを区別するのに役立ちます。

このインタフェースを継承で実装するには、このスケルトン クラスから派生させて、対応する OMG IDL インタフェースでオペレーションごとに実装する必要があります。コード リスト 15-4 では、インタフェース A の実装クラスの宣言を示します。

コード リスト 15-4 インタフェース A の実装クラスの宣言
// C++
class A_impl : public POA_A
{
public:
CORBA::Short op1();
void op2(CORBA::Long val);
...
};

 


デレゲーション ベースのインタフェース実装

デレゲーション ベースのインタフェース実装は、CORBA オブジェクトを継承とは別の手法で CORBA オブジェクトを実装します。この手法は、継承のオーバーヘッドが大きすぎたり、使用できなかったりする場合に使用します。たとえば、一部のグローバル クラスに継承が必要な場合に既存のレガシー コードを使用すると、継承の侵襲的な性質が原因で、オブジェクトを実装できないことがあります。デレゲーションは、この種の問題を解決するために使用できます。デレゲーションを使用すると、Process-Entity デザイン パターンにより、オブジェクトをより自然に実装できます。このパターンでは、Process オブジェクトが 1 つまたは複数のエンティティ オブジェクトにオペレーションを委譲します。

デレゲーション ベースの手法では、実装はスケルトン クラスから継承しません。代わりに、実装はアプリケーションの必要に応じてコーディングでき、ラッパー オブジェクトがその実装に呼び出しを委譲します。この「ラッパー オブジェクト」は「tie」と呼ばれ、継承手法で使用される同じスケルトン クラスと共に IDL コンパイラによって生成されます。生成された tie クラスは、スケルトンと同様に、関連付けられたインタフェースの各 OMG IDL オペレーションに対応するメソッドを備えていますが、プログラマにとっては非透過的な部分もあります。生成された tie クラスの名前は、生成されたスケルトン クラスと同様に、クラス名の末尾に文字列 _tie が付け加えられます。

tie クラスのインスタンスはサーバントで、tie オブジェクトによって委譲される C++ オブジェクトではありません。このサーバントは、Servant 引数を必要とするオペレーションに引数として渡されます。また、関連付けられたオブジェクトは、_this( ) オペレーションにアクセスしたり、直接データ メンバーにアクセスしたりすることはできません。

型保障 tie クラスは、C++ テンプレートで実装されます。コード リスト 15-5 のコードでは、前に例示した OMG IDL の Derived インタフェースから生成された tie クラスを示します。

コード リスト 15-5 Derived インタフェースから生成された tie クラス
// 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 固有の関数
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 オペレーション *************************************
CORBA::Short op1 ()
{
return _ptr->op1 ();
}


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

// ServantBase オペレーションをオーバーライド
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;

// コピーおよび代入は不可
POA_A_tie (const POA_A_tie<T> &);
void operator=(const POA_A_tie<T> &);
};

このクラス定義は、IDL コンパイラによって生成されたテンプレートです。通常、このテンプレートを使用するには、レガシー クラスに対するポインタを取得してから、tie クラスをそのポインタでインスタンス化します。たとえば、次のように入力します。

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

この例からわかるように、tie クラスにはインタフェースの op1 オペレーションと op2 オペレーションが定義されています。これらのオペレーションは、レガシー クラスには IDL と同じシグネチャのオペレーションがあることを想定しています。この想定どおりの場合、tie クラス ファイルをそのまま使用でき、正確に委譲できます。ただし、レガシー クラスのシグネチャが異なっていたり、単一の関数呼び出しを複数回にわたって行わなければならない場合も多くあります。このような場合は、生成されたコードの op1 と op2 のコードを置き換えてください。通常、各オペレーションのコードでは、tie クラス変数 _ptr を使用してレガシー クラスを呼び出します。このクラス変数には、レガシー クラスに対するポインタが格納されます。たとえば、次の行の場合、

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

次のように変更します。

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

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

このテンプレート クラスのインスタンスは、デレゲーションのタスクを実行します。Derived インタフェースのオペレーションを提供するクラス型でテンプレートがインスタンス化された場合、POA_Derived_tie クラスは、その実装クラスのインスタンスにオペレーションをすべて委譲します。実際の実装オブジェクトに対するリファレンスまたはポインタは、POA_Derived_tie クラスが作成されるときに適切な tie コンストラクタに渡されます。そこで要求が呼び出されると、tie サーバントは実装クラスの対応するメソッドを呼び出して要求を委譲します。

tie クラスのテンプレートを使用すると、アプリケーション開発者は、テンプレートの指定のインスタンス化用にテンプレートのオペレーションの一部またはすべてを特化することができます。これにより、アプリケーションでは関連付けられたオブジェクト型のレガシー クラスを使用できるようになります。この場合、関連付けられたオブジェクトのシグネチャは、tie クラスのシグネチャとは異なります。

 


オペレーションの実装

実装メンバー関数のシグネチャは、OMG IDL オペレーションのマッピングされたシグネチャです。クライアントサイドのマッピングとは異なり、OMG の仕様では、サーバサイドのマッピングの関数ヘッダには適切な例外指定が含まれています。コード リスト 15-6 に、この例を示します。

コード リスト 15-6 例外の指定
// IDL
interface A
{
exception B {};
void f() raises(B);
};
// C++
class MyA : public virtual POA_A
{
public:
void f();
...
};

すべてのオペレーションおよび属性が原因で CORBA システム例外は発生するため、オペレーションに raises 句がない場合でも、すべての例外指定で CORBA::SystemException を含める必要があります。

注意 : C++ コンパイラには違いがあるため、メソッド シグネチャで「送出宣言」を除外することをお勧めします。送出される例外が宣言済みのメソッドで未宣言の例外が送出された場合、システムによってはアプリケーション サーバがクラッシュすることもあります。

メンバー関数内で、「this」ポインタは、クラスで定義された実装オブジェクトのデータを参照します。データにアクセスするだけでなく、メンバー関数は同じクラスで定義されたほかのメンバー関数を暗黙的に呼び出すこともできます。コード リスト 15-7 に、この例を示します。

コード リスト 15-7 ほかのメンバー関数の呼び出し
// 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();
}

この方法でサーバント メンバー関数が呼び出されるときは、CORBA オブジェクトのオペレーションの実装としてではなく、単純に C++ のメンバー関数として呼び出されています。


  ページの先頭       前  次