![]() |
![]() |
|
|
サーバ側のマッピング
サーバ側のマッピングでは、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 つの関係を指定します。具体的には、継承ベースの関係とデレゲーション・ベースの関係の両方のサポートがマッピングで必要になります。標準的なアプリケーションでは、これらの関係の一方または両方を使用できます。BEA 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++ のメンバ関数として呼び出されています。
![]() |
![]() |
|
Copyright © 2001, BEA Systems, Inc. All rights reserved.
|