Axisアーキテクチャガイド

工事中 ....
バージョン1.0
フィードバック: axis-dev@xml.apache.org

目次

はじめに
アーキテクチャ概要
サブシステム
メッセージフローサブシステム
    ハンドラとチェーン
    メッセージ文脈
    エンジン
管理サブシステム
メッセージモデルサブシステム
    SOAPメッセージモデル
    メッセージ要素
    デシリアライゼーション
符号化サブシステム
WSDLツールサブシステム
    WSDL2Java
相互作用図
    クライアント側の処理
プラグイン可能なコンポーネントの検出
既知の問題

はじめに

このガイドでは、Axisのアーキテクチャと設計の原理を紹介します。

アーキテクチャ概要

Axisは後述の協調動作する複数のサブシステムから構成されています。 この節では、Axisの中核部分がどのように動作するのかについての概要を説明します。

Axisのハンドラとメッセージの経路

簡単にいうと、Axisはメッセージを処理するものです。 Axisの中央処理ロジックが起動すると、一連のハンドラが順番に呼び出されます。 この順番は、配備時の設定とエンジンがクライアントかサーバかのどちらかであるかという2つの要因によって決定されます。 各ハンドラの呼び出し時に渡されるオブジェクトはMessageContextです。 MessageContextは、次の重要な部品からなる構造体です: 1) "要求"メッセージ、2) "応答"メッセージ、3)プロパティ群。 これらの詳細は少し後で説明します。 Axisの呼び出しには、2つの方法があります:
  1. サーバとして。トランスポートリスナがMessageContextを生成し、Axisの処理フレームワークを呼び出します。
  2. クライアントとして。(通常Axisのクライアントプログラミングモデルで補助された)アプリケーションコードがMessageContextを生成し、Axisの処理フレームワークを呼び出します。

どちらの場合でも、Axisのフレームワークの仕事は単純で、作成されたMessageContextを設定されたハンドラの集合に渡すことです。 これにより各ハンドラは、設計されたMessageContextに対する処理を行う機会が与えられます。

サーバにおけるメッセージの経路

サーバ側におけるメッセージの経路を以下の図に示します。 小さな円筒がハンドラを、内部に円筒を含む少し大きめの円筒がチェーン(簡単にいうとハンドラの順序付き集合)を表します。

メッセージはトランスポートリスナに(何らかのプロトコル固有の方法で)到達します。 ここでは、このリスナがHTTPサーブレットであると仮定します。 リスナの役割は、プロトコル固有のデータをMessageオブジェクト(org.apache.axis.Message)に詰め、そのMessageをMessageContextに格納することです。 またリスナによって、このMessageContextにさまざまなプロパティが設定されます。 この例では、"http.SOAPAction"プロパティが、SOAPAction HTTPヘッダの値に設定されます。 また、このトランスポートリスナはMessageContextのtransportName Stringオブジェクトを、この例では"http"に、設定します。 MessageContextの準備ができた段階で、リスナはそれをAxisEngineに渡します。

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は応答チェーンを通して伝播します - 最初にトランスポート、次にグローバル、最後にサービス。

サブシステム

Axisは、責任範囲の明確な分離とAxisのモジュール化を目的に、協調動作する複数のサブシステムを形成します。 適切に階層化されたサブシステムによって、システム全体を使用することなく(もしくはシステム全体のコードをハックすることなく)システムの一部を使用することができます。

次の図は、サブシステムの階層を示しています。 下位の階層は上位の階層と独立しています。 'スタックされた'箱は互いに独立していることを表していますが、互いに排他的、入れ換え可能である必要はありません。 例えば、HTTP、SMTP、JMSトランスポートは互いに独立していますが、同時に使用することができます。

実際のところ、Axisのソースコードは、上図が意図するようなサブシステムに明確に分割されていません。 サブシステムの中には複数のパッケージにまたがっているものもあり、パッケージの中には複数のサブシステムと重複するものもあります。 コードの構造を改良し、Axisサブシステムの概念により正確に適合させる計画は、Axisバージョン1.0が完成した後に検討します。

メッセージフローサブシステム

ハンドラとチェーン

ハンドラは、メッセージを処理するためにシーケンスに沿って呼び出されます。 シーケンス中のどこかで、あるハンドラが要求の送信と応答の受信を、もしくは、要求の処理と応答の生成を行います。 このようなハンドラは、シーケンスにおけるピボットポイントと呼ばれます。 上述のように、ハンドラはトランスポート固有、サービス固有、または、グローバルのいずれかです。 これら3種類のハンドラは、チェーンとしてまとめられます。 そのため、ハンドラのシーケンス全体は、トランスポート、グローバル、サービスという3つのチェーンから形成されます。 次の図は、ハンドラの2つのシーケンスを示しています: 左がクライアント側、右がサーバ側。

Webサービスは、各要求メッセージに対して応答メッセージを送信する必要はありませんが、多くは送信します。 しかし、応答メッセージが無くても、例えばタイマを停止する、リソースを片付けるなどのため、応答ハンドラはメッセージの経路内で有用です。

チェーンは、合成ハンドラ、つまり、ハンドラの集合をまとめ、かつ、下のUML図が示すようにハンドラのインタフェースを実装したものです。

チェーンはまた、要求が処理されるまでハンドラのシーケンスに沿って流れるという点で、Chain of Responsibilityデザインパターンと似ています。 Axisのチェーンでは、要求は連続するハンドラに渡って段階的に処理されますが、Chain of Responsibilityと同様の、柔軟性と新規機能の追加が容易であるという利点があります。

メッセージの処理に戻りましょう。 メッセージは適切なチェーンに渡されることで処理されます。 メッセージ文脈はメッセージと関連する環境をハンドラの流れを通して渡すために使用されます。 Axisのチェーンは、オフラインで一度に1つのハンドラが加えられることで構成されるというモデルです。 そして、これらがオンラインになると、メッセージ文脈はそのチェーンを流れるようになります。 複数のメッセージ文脈が1つのチェーンを同時に流れることもできます。 オンラインになるとチェーンにハンドラが追加されることは決してありません。 ハンドラの追加や削除が必要となる場合、チェーンは'複製'されなければならず、変更は複製に対して行われます。 そして、複製がオンラインになります。古いチェーンは使用されなくなった段階で回収されます。 古いチェーンを使用していたメッセージ文脈は、終了するまで古いチェーンを使用し続けます。 これは、チェーンにハンドラの追加や削除を行う場合に、メッセージ文脈を処理している最中かどうかを考慮する必要がないことを意味しています。 重要な単純化です。

配備レジストリには、ハンドラとチェーンのファクトリがあります。 ハンドラとチェーンは、'アクセス毎'、'要求毎'、'シングルトン'というスコープを持つように定義することができます。 しかし、今の所、レジストリは、非シングルトンなスコープを持つオブジェクトは要求があった時に生成し、シングルトンなスコープのオブジェクトでは一度生成し、それをその後の生成要求の時に使用するために保持するということによる区別しかしません。

対象となるチェーン

対象となるチェーンは、要求ハンドラ、折り返しハンドラ、応答ハンドラの全て、もしくは、いずれかを持つ特殊なチェーンです。次のクラス図は、対象となるチェーンがチェーンとどのように関係しているかを示します。対象となるチェーンは、ハンドラの集合であるChainインタフェースを継承することによってハンドラをまとめたものであることに注意して下さい。

サービスは、"プロバイダ"と呼ばれる折り返しハンドラを持つ特殊な対象となるチェーンです。

フォルト処理

ここで、障害が発生した際に何が起きるのかについて考えてみましょう。 障害が起きたハンドラより以前のハンドラが、逆順にonFault(以前は'undo'という間違った名前でした)のために起動されます。 この後方走査のスコープは興味深いものです:これまでに呼び出されたすべてのハンドラがその現在のメッセージ文脈に対して起動されます。

"FaultableHandlers"と"WSDD Fault Flows"がどのように適用されるかについて説明が必要です。

メッセージ文脈

現在のMessageContextの構造を以下に示します。 メッセージ文脈はそれぞれ要求メッセージと応答メッセージの両方または片方に関連付けされている可能性があります。 各メッセージは、どちらもPartインタフェースを実装するSOAPPartとAttachmentsオブジェクトを持ちます。

メッセージ文脈の型は、Axisアーキテクチャとの関連を十分に考慮する必要があります。メッセージ文脈は、Handlerインタフェース上に現れるので、SOAPのために拘束させたり、偏よらせたりしてはなりません。現在の実装では、setServiceHandlerメソッドが指定されたハンドラをSOAPServiceに狭めるように、SOAPよりに少し偏よらされます。

エンジン

Axisには、2つのコンクリートサブクラスを持つAxisEngine抽象クラスがあります: AxisClientはクライアント側のチェーンを駆動し、AxisServerはサーバ側のチェーンを駆動します。これらのクラスの関係は下図のように、かなり単純なものです:

エンジンの設定

EngineConfigurationインタフェースは、Handlerファクトリとエンジンのインスタンスのグローバルオプションの設定手段です。EngineConfigurationのコンクリート実装のインスタンスは、生成された時にエンジンに渡さなければならず、また、エンジンはEngineConfigurationの内容が変更された場合に通知されなければなりません。エンジンはEngineConfigurationへの参照を保持し、Handlerファクトリとグローバルオプションを取り出すためにそれを使用します。

EngineConfigurationインタフェースは、メッセージフローサブシステムに属します。つまり、メッセージフローサブシステムは管理用サブシステムとは独立していることを意味します。

管理用サブシステム

管理用サブシステムはAxisエンジンの設定手段を提供します。エンジンが必要とする設定情報は、ChainやSOAPServiceといった実行時人工物のファクトリの集合と、エンジンのグローバルな設定オプション群です。

メッセージフローサブシステムのEngineConfigurationインタフェースは、管理用サブシステムによって実装されます。FileProviderにより、エンジンを配備記述子を内容とするファイルから静的に設定することができます。この内容は、WSDDDeploymentクラスによって解釈されます。反対に、SimpleProviderにより、エンジンを動的に設定することができます。

WSDDベースの管理

WSDDは、静的にAxisエンジンを設定するために使用する配備記述子用のXML文法です。各ハンドラは、そのハンドラ用ファクトリのコンクリートクラス名、ハンドラ用のオプション群、ハンドラインスタンス共有のスコープを決定するライフサイクルスコープ値といった設定を必要とします。

WSDD文法の構造は、実行時人工物ファクトリのクラス階層を反映したものです。下図は、クラスとそれらが生成する実行時人工物の型を示しています(点線の矢印は"インスタンス化"を意味します)。

メッセージモデルサブシステム

SOAPメッセージモデル

SOAPメッセージのXML構文は、かなり単純です。SOAPメッセージは、以下を含むエンベロープから構成されます: SOAPで定義されたボディ項目は、エラーを通知するSOAPフォルトのみです。

SOAPメッセージのXML要素の中には、URIとローカル名によって表される名前空間や、SOAPによって標準が定義された符号化書式を定義するものがあります。

ヘッダ項目は、以下の省略可能なSOAP属性でタグ付けできます:

したがって、SOAPメッセージは以下のようになります:

メッセージ要素

SOAPメッセージを表すクラスは、名前空間と符号化を管理するMessageElementクラスを基にしたクラス構成を形成します。 SOAPHeaderElementクラスはactorとmustUnderstandの処理を行います。
デシリアライゼーション中に、下図のような親子関係を持った、上のクラスのインスタンスから構成される解析ツリーが作成されます。

デシリアライゼーション

主にXMLの解析、つまり、デシリアライゼーションを担当するクラスは、DeserializationContextインタフェースのほとんどのメソッドを継承するDeserializationContextImpl ('DCI')です。 DCIは、解析ツリーの作成を管理し、SAXハンドラのスタック、デシリアライズ処理中のMessageElementへの参照、名前空間マッピングのスタック、IDから要素へのマッピング、デシリアライゼーションにおける型マッピングのセット(符号化サブシステムを参照)、および、SAXイベントレコーダを保持します。

走査済みの要素や特別なデシリアライザを持たない要素は記録されます。 言い替えると、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を解析ツリーのノードとして使用して扱われます。

符号化サブシステム

符号化はボトムアップで考えた方が最も容易に理解しやすいでしょう。基本的な要求は、プログラミング言語データ型の値とそのXML表現との間の変換を行うことです。Axisでは、これはJavaオブジェクトとプリミティブをXMLに符号化(もしくは'シリアライゼーション')することと、XMLをJavaオブジェクトとプリミティブに復号化(もしくは'デシリアライゼーション')することを意味します。 これらの手順を実装する基本クラスは、シリアライザデシリアライザです。

特別のシリアライザとデシリアライザは、DOMまたはSAXなどの特定のXML処理機構をサポートするよう作成されています。そのため、パラメータとして指定されたXML処理機構用のシリアライザとデシリアライザを生成するためのシリアライザファクトリデシリアライザファクトリが導入されました。

上のクラス図から明らかなように、符号化と復号化が必要なJavaの型とXMLデータ型の組合せはそれぞれ(実際は、XML解析機構毎に1つの)、特定のシリアライザとデシリアライザを要求します。したがって、QNameによって識別される、Javaの型とXMLデータ型の組合せからシリアライザファクトリとデシリアライザファクトリへのマッピングを保持しなければなりません。 このようなマッピングは、型マッピングと呼ばれます。 型マッピングクラスの階層を以下に示します。 デフォルトの型マッピングにより、様々なシリアライザファクトリとデシリアライザファクトリがどのようにインスタンス化されることに注目してください。

ここまでで明確にしていなかったことがあります。 あるメッセージに対してどの型マッピングを使用すればいいかをどのように判断するのでしょうか? これは、メッセージ内で規定された符号化によって決定されます。 型マッピングレジストリは、符号化名(URI)から型マッピングへのマッピングを保持します。 XMLデータ型であるQNameは符号化により定義されることに注意して下さい。

ここまでをまとめると、Javaオブジェクトやプリミティブデータ値をXMLデータ型に符号化する、もしくは、後者から前者に復号化するためには以下の点を知らなければなりません:

WSDLツールサブシステム

WSDLツールサブシステムには、WSDL2JavaとJava2WSDLがあります。Axis実行環境は、これらのツールとは独立しています。これらはユーザの利便性のためだけにあります。

WSDL2Java

このツールはWSDLで作成されたWebサービスの記述子をとり、そのWebサービスにアクセスするために使用されるJavaコードを生成します。

このツールの内部には次の3層があります:

Java2WSDL

作成中です。

相互作用図

クライアント側の処理

下図に示すようにクライアント側におけるAxisの処理は、AxisClientエンジンを呼び出す前に関連するService、MessageContext、要求Messageを持つCallオブジェクトを構成します。


Serviceインスタンスと関連するAxisClientインスタンスは、Callオブジェクトの前に作成されます。 その後、Service.createCallファクトリメソッドによってCallオブジェクトが作成されます。 適切なTransportがまだCallインスタンスと関連付いていない場合、Call.setOperationはTransportインスタンスを作成します。 そして、Call.invokeはMessageContextと関連する要求Messageを作成し、AxisClientの呼び出しを開始し、結果として起こるMessageContextを処理します。 下の相互作用図にこの流れの中で重要なメソッド呼び出しを示します。

プラグイン可能なコンポーネントの検出

ほとんどのプラグイン可能なコンポーネント基盤(jaxp/xerces、commons-loggingなど)は、検出機能を提供していますが、今後も改良されるであろうと予想されます。例えば、先端技術は再検討され標準化されています。検出機構も同様に変更されます。

したがって、コンポーネント検出はAXISにおける制御のある箇所、通常はAXIS固有のファクトリメソッド、にまとめる必要があります。 利用可能であれば、これらのファクトリメソッドは現時点の標準に従わなければなりません。 技術の発展、標準化に従って、ファクトリメソッドも適切な検出機構を使用して最新の状態を維持できなければなりません。

既知の問題点

  1. Axisサブシステム間の関係について文書化しなければならず、一部のサブシステム間の責任範囲の弱点を何とか整理しなければなりません。 例えば、基本的なMessageContext型と関連するクラスでは、若干SOAPとHTTPに偏よっています。
  2. "符号化"サブシステムにはどのようなクラスがあるのでしょうか? 符号化とメッセージモデルサブシステムは、"メッセージフロー"に依存する他のサブシステムとは独立しているのでしょうか?
  3. (おそらく前述の問題と関連しますが)SOAP固有機能とHTTP固有の機能を考慮したAxisサブシステム間で、どのように上図内のクラスを配置すべきでしょうか。
  4. 現時点でAxisエンジンは、トランスポート、グローバル、サービスというハンドラの3階層を理解しています。しかし、構造的には、多少おかしくなっています。Webサービスが、常にそして今後も3階層であることを保証する法律でもあるのでしょうか? 要求、ピボット、応答ハンドラについてより原始的な概念を持った対象となるチェーンを使用する方がより自然でしょう。 その場合、折り返しハンドラ自身がグローバルな要求ハンドラ、応答ハンドラと(前述のように自身が対象となるチェーンとなる)サービスの折り返しハンドラを持つ対象となるチェーンであるようにAxisエンジンを実装することになります。 このようなAxisエンジンの構造を下図に示します。

  5. WSDDService.faultFlowsは、空のVectorをインスタンス化しますが、フォルトフローをそれに追加する方法がありません。 これは使用されないコードでしょうか、それとも何か無くなったのでしょうか。
  6. 障害が折り返しハンドラの後に発生した場合、後方走査は折り返しハンドラの前に呼び出されたハンドラに通知すべきでしょうか? 現在の実装では、このようなハンドラにも通知します。 しかし、下流方向におけるシステムでの障害発生時の処理とは一貫性がなく、この場合は折り返しハンドラによってメッセージ文脈内に格納されます。 これらの障害は任意の応答ハンドラを経由して渡されますが、ローカルエンジンではonFaultの駆動は起こりません。

  7.  

     
     
      ここで何が進んでいるかを考える必要があります。 ハンドラの並びを考え、それからその並びの配置について境界を引いてみて下さい。メッセージ文脈への影響という点で、この並びのセマンティクスが持つべき影響は何でしょうか? 次の図はクライアント側のハンドラの並びが、サーバ側のハンドラの並びを呼び出していることを示しています。 この結合された並びのセマンティクスは、トランスポート関連のハンドラを省略することにより作られる並びとどのように比較されるを考慮する必要があります。



本文書は、日本Apache XMLプロジェクト(本田)により翻訳されました。
翻訳に対するコメントは、jaxmldev@xml.gr.jpに送って下さい。