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

     前  次    新規ウィンドウで目次を開く  新規ウィンドウで索引を開く  PDFとして表示 - 新規ウィンドウ  Adobe Readerを取得 - 新規ウィンドウ
コンテンツはここから始まります

サーバー側のマッピング

サーバー側のマッピングでは、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:
// ... server-side ORB-implementation-specific
// goes here...
        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-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> &);
};

このクラス定義は、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++のメンバー関数として呼び出されています。


  先頭に戻る       前  次