どちらの場合でも、Axisのフレームワークの仕事は単純で、作成されたMessageContextを設定されたハンドラの集合に渡すことです。 これにより各ハンドラは、設計されたMessageContextに対する処理を行う機会が与えられます。
AxisEngineの最初の仕事は、名前によるトランスポートの検索です。 トランスポートとは、要求チェーンと応答チェーンの両方もしくは片方から構成されるオブジェクトです。 チェーンとは、順番に呼び出される一連のハンドラ群からなるハンドラです。 チェーンについては後で詳細に説明します。 トランスポートの要求チェーンがあれば、MessageContextをinvoke()メソッドに渡す形でそれが呼び出されます。 これにより、その要求チェーンの設定で指定された全てのハンドラが呼び出されることになります。
トランスポート要求ハンドラの後、エンジンは、設定されていれば、グローバル要求チェーンに移り、そこで指定されたハンドラを呼び出します。
ここまでの処理の中のどこかで、うまく行けば一部のハンドラが、MessageContextのserviceHandlerフィールドを設定しています(これは通常、HTTPトランスポートの"URLMapper"ハンドラによって行われます。このハンドラは、"http://localhost/axis/services/AdminService"といったURLを"AdminService"サービスにマッピングします)。 このフィールドによって、バックエンドオブジェクトのRPC呼び出しを作成するなどといった、サービス固有の機能を実行するために呼び出すハンドラを決定します。 Axisにおけるサービスは通常は"SOAPService"クラス(org.apache.axis.handlers.soap.SOAPService)のインスタンスです。 このインスタンスは、(ここまででトランスポートとグローバルレベルで示したことと同じように)要求チェーンと応答チェーンを持つことができ、また、簡単にいうとそのサービスの実際のバックエンドロジックを実装する責任をもつハンドラである、プロバイダを持たなければなりません。
RPC書式の要求では、プロバイダはorg.apache.axis.providers.java.RPCProviderクラスとなります。 これは、単なる別ハンドラであり、呼び出されると、配備時に指定された"className"パラメータによって決定されるクラスのバックエンドJavaオブジェクトの呼び出しを試行します。 これは、SOAP RPC規約を使用して呼び出すメソッドを決定し、入力されたXML符号化引数の型が決定したメソッドが必要とするパラメータの型に一致するかどうかを確認します。
サービス要求チェーンの後、グローバル要求チェーンがあれば呼び出され、その後トランスポートに移ります。 トランスポートセンダは、対象となるSOAPサーバとの間のメッセージを扱うために必要な全てのプロトコル固有の操作を実際に行い、メッセージを送信するために呼び出されます。 (もしあれば)応答は、MessageContextのresponseMessageフィールドに格納され、そのMessageContextは応答チェーンを通して伝播します - 最初にトランスポート、次にグローバル、最後にサービス。
次の図は、サブシステムの階層を示しています。 下位の階層は上位の階層と独立しています。 'スタックされた'箱は互いに独立していることを表していますが、互いに排他的、入れ換え可能である必要はありません。 例えば、HTTP、SMTP、JMSトランスポートは互いに独立していますが、同時に使用することができます。
実際のところ、Axisのソースコードは、上図が意図するようなサブシステムに明確に分割されていません。 サブシステムの中には複数のパッケージにまたがっているものもあり、パッケージの中には複数のサブシステムと重複するものもあります。 コードの構造を改良し、Axisサブシステムの概念により正確に適合させる計画は、Axisバージョン1.0が完成した後に検討します。
Webサービスは、各要求メッセージに対して応答メッセージを送信する必要はありませんが、多くは送信します。 しかし、応答メッセージが無くても、例えばタイマを停止する、リソースを片付けるなどのため、応答ハンドラはメッセージの経路内で有用です。
チェーンは、合成ハンドラ、つまり、ハンドラの集合をまとめ、かつ、下のUML図が示すようにハンドラのインタフェースを実装したものです。
チェーンはまた、要求が処理されるまでハンドラのシーケンスに沿って流れるという点で、Chain of Responsibilityデザインパターンと似ています。 Axisのチェーンでは、要求は連続するハンドラに渡って段階的に処理されますが、Chain of Responsibilityと同様の、柔軟性と新規機能の追加が容易であるという利点があります。
メッセージの処理に戻りましょう。 メッセージは適切なチェーンに渡されることで処理されます。 メッセージ文脈はメッセージと関連する環境をハンドラの流れを通して渡すために使用されます。 Axisのチェーンは、オフラインで一度に1つのハンドラが加えられることで構成されるというモデルです。 そして、これらがオンラインになると、メッセージ文脈はそのチェーンを流れるようになります。 複数のメッセージ文脈が1つのチェーンを同時に流れることもできます。 オンラインになるとチェーンにハンドラが追加されることは決してありません。 ハンドラの追加や削除が必要となる場合、チェーンは'複製'されなければならず、変更は複製に対して行われます。 そして、複製がオンラインになります。古いチェーンは使用されなくなった段階で回収されます。 古いチェーンを使用していたメッセージ文脈は、終了するまで古いチェーンを使用し続けます。 これは、チェーンにハンドラの追加や削除を行う場合に、メッセージ文脈を処理している最中かどうかを考慮する必要がないことを意味しています。 重要な単純化です。
配備レジストリには、ハンドラとチェーンのファクトリがあります。 ハンドラとチェーンは、'アクセス毎'、'要求毎'、'シングルトン'というスコープを持つように定義することができます。 しかし、今の所、レジストリは、非シングルトンなスコープを持つオブジェクトは要求があった時に生成し、シングルトンなスコープのオブジェクトでは一度生成し、それをその後の生成要求の時に使用するために保持するということによる区別しかしません。
サービスは、"プロバイダ"と呼ばれる折り返しハンドラを持つ特殊な対象となるチェーンです。
"FaultableHandlers"と"WSDD Fault Flows"がどのように適用されるかについて説明が必要です。
EngineConfigurationインタフェースは、メッセージフローサブシステムに属します。つまり、メッセージフローサブシステムは管理用サブシステムとは独立していることを意味します。
メッセージフローサブシステムのEngineConfigurationインタフェースは、管理用サブシステムによって実装されます。FileProviderにより、エンジンを配備記述子を内容とするファイルから静的に設定することができます。この内容は、WSDDDeploymentクラスによって解釈されます。反対に、SimpleProviderにより、エンジンを動的に設定することができます。
WSDD文法の構造は、実行時人工物ファクトリのクラス階層を反映したものです。下図は、クラスとそれらが生成する実行時人工物の型を示しています(点線の矢印は"インスタンス化"を意味します)。
SOAPメッセージのXML要素の中には、URIとローカル名によって表される名前空間や、SOAPによって標準が定義された符号化書式を定義するものがあります。
ヘッダ項目は、以下の省略可能なSOAP属性でタグ付けできます:
したがって、SOAPメッセージは以下のようになります:
走査済みの要素や特別なデシリアライザを持たない要素は記録されます。 言い替えると、SAXイベントはキュー内に保持され、後で任意のSAX ContextHandlerに'再生'することができます。
解析、もしくは、ユーザによるマニュアルでの作成のいずれかによってSOAPEnvelopeが構築されると、SerializationContextを使用する出力が行われる場合があります(符号化サブシステムを参照)。MessageElementは、全て自身の内容を書き出すoutput()メソッドを持ちます。
SAXハンドラは、以下のようなクラス階層を形成します:
また、下図のようにスタックされます:
まず、SAXハンドラのスタックは、SOAPエンベロープの解析がまだ始まっていないことを表すEnvelopeHandlerのインスタンスだけを含みます。 EnvelopeHandlerは、SOAPエンベロープの解析を行うSAXハンドラであるEnvelopeBuilderの参照を持つように生成されます。
解析中、DCIはSAXパーサからのイベントを受け付け、ハンドラスタックの頂点にあるSAXハンドラとSAXイベントレコーダの両方もしくは片方に通知します。
開始要素で、DCIはハンドラスタックの頂点のSAXハンドラのonStartChildを呼び出します。このメソッドは、子要素の解析に使用されるSAXハンドラを返します。DCIは、そのハンドラをSAXハンドラスタックにプッシュし、startElementを呼び出します。特にstartElementは、通常新しい適切なクラスのMessageElementを生成し、DCIのpushNewElementを呼び出します。後者の動作により、解析ツリーの親子関係が作成されます。
終了要素で、DCIはハンドラスタックの上からSAXハンドラをポップし、endElementを呼び出します。そして、新たにハンドラスタックの最上位にあるSAXハンドラのonEndChildを駆動します。最後に、デシリアライズされたMessageElementを、現在のMessageElementの親として設定します。
SOAPによって定義されない要素は、SOAPHandlerをSAXイベントハンドラとし、そして、MessageElementを解析ツリーのノードとして使用して扱われます。
特別のシリアライザとデシリアライザは、DOMまたはSAXなどの特定のXML処理機構をサポートするよう作成されています。そのため、パラメータとして指定されたXML処理機構用のシリアライザとデシリアライザを生成するためのシリアライザファクトリとデシリアライザファクトリが導入されました。
上のクラス図から明らかなように、符号化と復号化が必要なJavaの型とXMLデータ型の組合せはそれぞれ(実際は、XML解析機構毎に1つの)、特定のシリアライザとデシリアライザを要求します。したがって、QNameによって識別される、Javaの型とXMLデータ型の組合せからシリアライザファクトリとデシリアライザファクトリへのマッピングを保持しなければなりません。
このようなマッピングは、型マッピングと呼ばれます。
型マッピングクラスの階層を以下に示します。
デフォルトの型マッピングにより、様々なシリアライザファクトリとデシリアライザファクトリがどのようにインスタンス化されることに注目してください。
ここまでで明確にしていなかったことがあります。
あるメッセージに対してどの型マッピングを使用すればいいかをどのように判断するのでしょうか?
これは、メッセージ内で規定された符号化によって決定されます。
型マッピングレジストリは、符号化名(URI)から型マッピングへのマッピングを保持します。
XMLデータ型であるQNameは符号化により定義されることに注意して下さい。
ここまでをまとめると、Javaオブジェクトやプリミティブデータ値をXMLデータ型に符号化する、もしくは、後者から前者に復号化するためには以下の点を知らなければなりません:
このツールの内部には次の3層があります:
Serviceインスタンスと関連するAxisClientインスタンスは、Callオブジェクトの前に作成されます。
その後、Service.createCallファクトリメソッドによってCallオブジェクトが作成されます。
適切なTransportがまだCallインスタンスと関連付いていない場合、Call.setOperationはTransportインスタンスを作成します。
そして、Call.invokeはMessageContextと関連する要求Messageを作成し、AxisClientの呼び出しを開始し、結果として起こるMessageContextを処理します。
下の相互作用図にこの流れの中で重要なメソッド呼び出しを示します。
ほとんどのプラグイン可能なコンポーネント基盤(jaxp/xerces、commons-loggingなど)は、検出機能を提供していますが、今後も改良されるであろうと予想されます。例えば、先端技術は再検討され標準化されています。検出機構も同様に変更されます。
したがって、コンポーネント検出はAXISにおける制御のある一箇所、通常はAXIS固有のファクトリメソッド、にまとめる必要があります。 利用可能であれば、これらのファクトリメソッドは現時点の標準に従わなければなりません。 技術の発展、標準化に従って、ファクトリメソッドも適切な検出機構を使用して最新の状態を維持できなければなりません。
ここで何が進んでいるかを考える必要があります。
ハンドラの並びを考え、それからその並びの配置について境界を引いてみて下さい。メッセージ文脈への影響という点で、この並びのセマンティクスが持つべき影響は何でしょうか?
次の図はクライアント側のハンドラの並びが、サーバ側のハンドラの並びを呼び出していることを示しています。
この結合された並びのセマンティクスは、トランスポート関連のハンドラを省略することにより作られる並びとどのように比較されるを考慮する必要があります。
本文書は、日本Apache XMLプロジェクト(本田)により翻訳されました。
翻訳に対するコメントは、jaxmldev@xml.gr.jpに送って下さい。