目次 前 次 PDF


サーバー側のマッピング

サーバー側のマッピング
サーバー側のマッピングとは、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インタフェースで操作ごとに実装する必要があります。インタフェースAの実装クラス宣言は、リスト15-4に示した形式を取ります。
リスト15-4 インタフェースAの実装クラスの宣言
// C++
class A_impl : public POA_A
{
public:
CORBA::Short op1();
void op2(CORBA::Long val);
...
};
 
デレゲーション・ベースのインタフェース実装
デレゲーション・ベースのインタフェース実装の手法は、CORBAオブジェクトを実装するときに継承のかわりに使用されます。この手法は、継承のオーバーヘッドが大きすぎるか、継承が使用できないときに使用します。たとえば、グローバル・クラスの継承が必要になっても、継承に侵略的な性質があるため、既存のレガシー・コードを使用してオブジェクトを実装できないことがあります。かわりに、デレゲーションを使用すると、このようなタイプの問題を解決できます。デレゲーションを使用すると、Process-Entityデザイン・パターンにより、オブジェクトをより自然に実装できます。このパターンでは、プロセス・オブジェクトが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を含める必要があります。
注意:
メンバー関数内で、このポインタはクラスの定義に従って実装オブジェクトのデータを参照します。データへのアクセスに加えて、メンバー関数は同じクラスに定義されている別のメンバー関数を暗黙的に呼び出す場合があります。リスト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++のメンバー関数として呼び出されています。
 

Copyright ©1994, 2017,Oracle and/or its affiliates. All rights reserved