![]() ![]() ![]() ![]() ![]() ![]() ![]() |
サーバー側のマッピングでは、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の例を示します。
// IDL
interface A
{
short op1() ;
void op2(in long val) ;
};
// 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のスケルトン・クラスを示します。
// 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の実装クラスの宣言を示します。
// 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クラスを示します。
// 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に、この例を示します。
// 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に、この例を示します。
// 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++のメンバー関数として呼び出されています。
![]() ![]() ![]() |