目次


Java Web サービス

第 2 回 Axis2 の徹底調査: AXIOM

Apache Axis2 Web サービスの基礎となる AXIOM 文書オブジェクト・モデル

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: Java Web サービス

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:Java Web サービス

このシリーズの続きに乞うご期待。

新しい文書モデルが必要な理由とは

Apache Axis2 1.1 がリリースされ、長い間 Apache シリーズの Web サービス・フレームワークを使ってきたファンに心躍るような新しい機能を提供しています。Axis2 自体については今後の記事で説明することにして、今回は Axis2 の中核となっている AXIOM (Axis Object Model) という XML 文書モデルを詳しく取り上げます。AXIOM は Axis2 を支える主要な技術革新の 1 つで、Axis2 が初代 Axis より遥かに優れたパフォーマンスを提供する可能性があるのは、AXIMO の存在が 1 つの理由になっています。この記事では、AXIOM の仕組みと AXIOM を基に構築された Axis2 のさまざまな構成部分を紹介し、最後に AXIOM のパフォーマンスを他の Java™ 文書オブジェクト・モデルと比較してみます。

文書モデルは XML の処理方法として一般に使用されており、初期の W3C DOM 仕様、JDOM、dom4j、XOM といった実装をはじめとして、さまざまな種類のものが Java 開発用に用意されています。各モデルには、パフォーマンス、柔軟性、あるいは厳密な XML 標準準拠といった、他に勝る点がいくつかあるため、それぞれのモデルに熱烈な支持者がいます。それなのに何故、Axis2 に新しいモデルが必要なのでしょう。その答えは、SOAP メッセージの構造にあります。とくに鍵となるのは、基本 SOAP フレームワークに拡張機能を追加する方法です。

SOAP 流を理解する

SOAP 自体は、XML アプリケーションのペイロードをラップする単なるシン・ラッパーでしかありません。リスト 1 に一例を記載します。この例では、SOAP が実際に定義しているのは、soapenv という接頭辞を持つ要素だけです。文書の大部分は、soapenv:Body 要素のコンテンツを構成するアプリケーション・データとなっています。

リスト 1. SOAP の例
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header/>
  <soapenv:Body>
    <matchQuakes xmlns="http://seismic.sosnoski.com/types">
      <min-date>2001-01-06T11:10:43.446Z</min-date>
      <max-date>2001-10-24T19:49:13.812Z</max-date>
      <min-long>-150.94307</min-long>
      <max-long>-22.594208</max-long>
      <min-lat>-11.44651</min-lat>
      <max-lat>55.089058</max-lat>
    </matchQuakes>
  </soapenv:Body>
</soapenv:Envelope>

基本的な SOAP ラッパーは単純ながらも、ヘッダーと呼ばれるオプション・コンポーネントを使用することにより、無限に拡張することができます。ヘッダーはあらゆる種類のメタデータを追加する場所となり、アプリケーションには見えないアプリケーション・データを携えることになります (ヘッダーにアプリケーション・データを組み込むこともできますが、アプリケーション・データに本体ではなくヘッダーを使用しなければならない理由を納得させる事例はありません)。SOAP 上に拡張機能を構築すると (一連の WS-* ファミリーなど) 、アプリケーションに影響することなく、それぞれ固有の目的でヘッダーを使用できます。これによって拡張機能をアドオンとして動作させることが可能になり、アプリケーションに必要な特定の拡張機能をコードに組み込まなくても、デプロイ時に単純に選択できるようになります。

リスト 2 は、リスト 1 の SOAP の例と同じアプリケーション・データですが、WS-Addressing 情報が組み込まれています。元の SOAP メッセージは HTTP トランスポートでしか使用できませんが (HTTP はクライアントに応答を即時送信できるよう、双方向の接続を行うため)、リスト 2 のバージョンは HTTP 以外のプロトコルでも動作します。その理由は、応答メタデータが SOAP 要求メッセージに直接組み込まれているからです。メタデータが要求のターゲットと応答のターゲット両方の情報を提供するため、リスト 2 には メッセージの処理に伴う保管と転送のステップも簡単に含められます。

リスト 2. WS-Addressing を使用した SOAP の例
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:wsa="http://www.w3.org/2005/08/addressing">
  <soapenv:Header>
    <wsa:To>http://localhost:8800/axis2/services/SeisAxis2XBean</wsa:To>
    <wsa:ReplyTo>
      <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
    </wsa:ReplyTo>
    <wsa:MessageID>urn:uuid:97AE2B17231A8584D811537402403691</wsa:MessageID>
  </soapenv:Header>
  <soapenv:Body>
    <matchQuakes xmlns="http://seismic.sosnoski.com/types">
      <min-date>2000-03-28T13:13:08.953Z</min-date>
      <max-date>2001-03-11T02:26:54.283Z</max-date>
      <min-long>-81.532234</min-long>
      <max-long>65.25895</max-long>
      <min-lat>-14.234512</min-lat>
      <max-lat>57.174187</max-lat>
    </matchQuakes>
  </soapenv:Body>
</soapenv:Envelope>

文書モデルのジレンマ

SOAP ヘッダーの核心は、任意のメタデータをメッセージに追加できるようにすることです。そのため、拡張によって追加されるあらゆるものを SOAP フレームワークが許容できなければなりません。一般的に、任意の XML を操作する上で最も簡単な方法となるのは各種形式の文書モデルを使用することです。XML の形式を一切仮定することなく、その XML を忠実に表現することこそが文書モデルの核心だからです。

その一方、操作対象の XML がアプリケーション間でのデータ交換に使用される場合、文書モデルはあまり効率的な方法ではありません。アプリケーション・データには通常、事前定義された構造があり、ほとんどの開発者は、アプリケーション・データを未処理の XML として処理するよりはデータ・オブジェクトの形式で操作することを選びます。データ・オブジェクトと XML 間での変換ジョブは、データ・バインディングと呼ばれる領域に含まれます。開発者にとって、データ・バインディングは文書モデルの操作よりも便利であるだけでなく、パフォーマンスとメモリー使用率の両面でもはるかに効率的です。

そのような理由から、ほとんどのアプリケーションではデータ・バインディングを使用して SOAP メッセージのアプリケーション・ペイロードを操作しますが、ヘッダーに含まれるメタデータを操作するには文書モデルのほうが適しています。理想的な方法は、この 2 つの手法を SOAP フレームワーク内に組み合わせることですが、一般的な文書モデルではこの方法を実現することはできません。一般的な文書モデルは文書全体、あるいは少なくとも文書のサブツリー全体を操作することを目的としているため、SOAP に最も適した文書の選択部分のみの操作に対応するようには構成されていないのです。

AXIOM ツリーのプル

Axis と Axis2 の間には、AXIOM 以外にも変更があります。初代 Axis では標準プッシュ・スタイル (SAX) のパーサーで XML を処理していましたが、Axis2 ではプル・スタイル (StAX) のパーサーを使用します。プッシュ手法では、解析動作を管理するのはパーサーです。つまり、パーサーに解析対象の文書とハンドラー参照を渡すと、パーサーは入力文書を処理し、ハンドラーを使ってコードにコールバックします。ハンドラー・コードではコールバックによって渡された情報を利用できますが、解析を操作することはできません (例外をスローする場合は別です)。一方、プル手法でのパーサーは事実上イテレーターであり、オンデマンドで文書のコンポーネントにアクセスします。

プッシュ手法とプル手法にはそれぞれの用途がありますが、論理的に分離されたコンポーネント (SOAP など) が含まれる XML の場合には、プル・スタイルのほうがはるかに有利です。プル・パーサーでは、文書の一部を処理するコードが必要なだけの解析を行い、文書の次の処理にパーサーを引き渡すことができます。

AXIOM は StAX のプル・パーサー・インターフェースを中心に構築されています。AXIOM は、オンデマンドで AXIMOが拡張する仮想文書モデルを提供し、クライアント・アプリケーションに要求されたツリー構造の文書モデル表現だけを構築します。この仮想文書モデルは XML 文書の要素レベルで機能します。パーサーが要素の開始タグをレポートすると要素の表現が作成されますが、その要素の初期の形は、基本的にパーサーへの参照を保持する単なるシェルでしかありません。アプリケーションが要素に含まれるコンテンツの詳細を取得する必要がある場合、アプリケーションは単に、そのインターフェースのメソッド (org.apache.axiom.om.OMContainer.getChildren() メソッドなど) を呼び出して情報を要求するだけです。

すると、要素がメソッド呼び出しに応答して、パーサーから子コンテンツを作成します。

パーサーは文書の順 (XML 文書テキストで項目が表示される順と同じ) にデータを配信するため、AXIOM が行うオンデマンド構築には何らかの賢い処理方法が必要になります。例えば、未完了 (構築中) 状態に複数の要素があることは普通ですが、これらの要素はすべて直系の継承として並んでいなければなりません。最上部にルート要素がある一般的な XML のツリー・ダイアグラムに関して言うと、未完了の要素は常にツリーの右下に並びます。アプリケーションがさらにデータを要求するにつれ、最初に完了した要素を下にして、ツリーが右の方向へ広がっていきます。

AXIOM を操作する

すべての XML 文書モデルは、API に関しては多くの点で共通しています (同じ基礎データを操作するため、当然のことです)。ただし、それぞれのモデルに、そのモデルを他とは差別化する特徴があります。初期の W3C DOM (Document Object Model) は、言語間およびプラットフォーム間の互換性を目的に設計されました。そのため、基礎とするのはインターフェースで、それぞれ独自のバージョンにあった Java 固有のコレクションを使用することは避けています。一方、JDOM はインターフェースではなく具体的なクラスを使用し、API に 標準 Java コレクションのクラスを組み込んでいるため、多くの Java 開発者はこの方が DOM より使いやすいと評価しています。dom4j は 非常に柔軟性の高い API として DOM のようなインターフェースを Java コレクションのクラスと組み合わせています。これにより機能は強化されていますが、複雑さが増すという犠牲が伴っています。

AXIOM には、これらの文書モデルと多くの共通点があります。同時に、オンデマンド構築プロセスに関連した大きな違いや、Web サービスでの使用をサポートするために特殊化された機能もあります。

AXIOM の動作

AXIOM の API は、全体的な動作としては DOM に最も近いかもしれませんが、独自の特徴が備わっています。例えば、アクセス・メソッドはリスト形式ではなく、(org.apache.axiom.om.OMContainer.getChildren() および関連メソッドによって返される) java.util.Iterator インスタンスを使用したコンポーネントへのアクセスを中心に設計されています。ナビゲーションでは、コンポーネントのリストに索引を付ける代わりに org.apache.axiom.om.OMNode.getNextOMSibling() メソッドと org.apache.axiom.om.OMNode.getPreviousOMSibling() メソッドを使用して、文書ツリーのレベルごとにノードを順次移動します (この点に関しては、DOM と似ています)。つまり、すべての子要素を最初に処理しなくても、開始要素の最初の子に移動できるというわけです。このような AXIOM のアクセス・メソッドとナビゲーション・メソッドの構造化は、オンデマンドのツリー構築方法とぴったり適合します。

DOM と dom4j と同じく、AXIOM もツリー表現へのアクセスとその操作に使用する API をインターフェースで定義しています。AXIOM 配布には、これらのインターフェースを何通りかに特殊化した実装が含まれています。その 1 つ (org.apache.axiom.om.impl.dom パッケージ内) は二重の用途を持ち、AXIOM と DOM 両方のインターフェースを同じ実装クラスでサポートします。データを DOM のビューで操作することを想定した Web サービスのアドオンは多数あるため、この実装は役に立つはずです。それよりも一般的な用途としては、org.apache.axiom.om.impl.llom パッケージがオブジェクトのリンク・リスト (パッケージ名の「ll」の部分) に基づく実装を提供しています。さらに、基本 org.apache.axiom.om インターフェースの拡張、そして SOAP メッセージ用にカスタマイズされた org.apache.axiom.soap パッケージに含まれる実装の拡張も用意されています。

動作中の AXIOM API を簡単に説明するため、ここからはAXIOM と他の文書モデルのパフォーマンスの比較テストで使用したコードのサンプルをいくつか検討していきます。リスト 3 に記載する最初のサンプルは、入力文書の AXIOM 表現を構築するために使用したコードに基づきます。DOM と dom4j の場合と同様、AXIOM で作業するには、まずモデルのコンポーネント・オブジェクトを作成するファクトリーを取得しなければなりません。リスト 3 のコードは、org.apache.axiom.org.OMFactory インターフェースの org.apache.axiom.om.impl.llom.factory.OMLinkedListImplFactory 実装を使用して、AXIOM インターフェースの基本リンク・リスト実装を選択しています。ファクトリー・インターフェースには、各種のソースから文書を直接構築するためのメソッド、XML 文書表現の個別コンポーネントを作成するためのメソッドが組み込まれています。また、このコードには入力ストリームから文書を構築するメソッドも使用されています。コードでは指定していませんが、build() メソッドが返すオブジェクトは、実際には org.apache.axiom.om.OMDocument のインスタンスです。

リスト 3. 文書の AXIOM 解析
import org.apache.axiom.om.*;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axiom.om.impl.llom.factory.OMLinkedListImplFactory;
    ...
    private XMLInputFactory m_parserFactory = XMLInputFactory.newInstance();
    private OMFactory m_factory = new OMLinkedListImplFactory();
    ...
    protected Object build(InputStream in) {
        Object doc = null;
        try {
            XMLStreamReader reader = m_parserFactory.createXMLStreamReader(in);
            StAXOMBuilder builder = new StAXOMBuilder(m_axiomFactory, reader);
            doc = builder.getDocument();
        } catch (Exception ex) {
            ex.printStackTrace(System.out);
            System.exit(0);
        }
        return doc;
    }

リスト 3 では入力ストリームの解析により、org.apache.axiom.om.impl.builder.StAXOMBuilder クラスを使って文書表現を構築しています。これによって作成されるのは StAX パーサーのインスタンス、そしてリターンする前の基本文書構造だけで、パーサーは後で必要な場合に残りの文書表現を作成できるように文書のルート要素内に配置されたままになります。AXIOM の構築には、必ずしも StAX パーサーを使用する必要はありません。事実、org.apache.axiom.om.impl.builder.SAXOMBuilder は SAX プッシュ・パーサーに基づくビルダーの部分的実装です。ただし、他の方法で AXIOM を構築すると、オンデマンド構築のメリットは得られないことになります。

リスト 4 に、文書表現に含まれる要素を「ウォーク」スルーし、要約情報 (要素の数、属性値テキストの数と合計長、テキスト・コンテンツの数と合計長) を集計するためのコードを示します。上の方にある walkElement() メソッドが 1 つの要素を処理し (メソッド自体の再帰呼び出しを行って子要素を処理)、下の方にある walk() メソッドが要約対象の文書を要約データ構造とともに取得します。

リスト 4. AXIOM のナビゲート
    /**
     * Walk subtree for element. This recursively walks through the document
     * nodes under an element, accumulating summary information.
     *
     * @param element element to be walked
     * @param summary document summary information
     */
    protected void walkElement(OMElement element, DocumentSummary summary) {

        // include attribute values in summary
        for (Iterator iter = element.getAllAttributes(); iter.hasNext();) {
            OMAttribute attr = (OMAttribute)iter.next();
            summary.addAttribute(attr.getAttributeValue().length());
        }

        // loop through children
        for (Iterator iter = element.getChildren(); iter.hasNext();) {

            // handle child by type
            OMNode child = (OMNode)iter.next();
            int type = child.getType();
            if (type == OMNode.TEXT_NODE) {
                summary.addContent(((OMText)child).getText().length());
            } else if (type == OMNode.ELEMENT_NODE) {
                summary.addElements(1);
                walkElement((OMElement)child, summary);
            }

        }
    }

    /**
     * Walk and summarize document. This method walks through the nodes
     * of the document, accumulating summary information.
     *
     * @param doc document representation to be walked
     * @param summary output document summary information
     */
    protected void walk(Object doc, DocumentSummary summary) {
        summary.addElements(1);
        walkElement(((OMDocument)doc).getOMDocumentElement(), summary);
    }

リスト 5 は、文書表現を出力ストリームに書き込むためのコードです。AXIOM は OMNode インターフェースの一部として多数の出力メソッドを定義しています。これらには、さまざまな出力先 (出力ストリーム、標準文字書き出しプログラム、または StAX ストリーム書き出しプログラム)、フォーマット設定情報の有無、そして文書表現が書き込まれた後の処理機能 (完全表現がまだ構築されていない場合に組み立てる) の有無などの違いがあります。OMElement インターフェースは、文書情報へアクセスするさらにもう 1 つの方法を定義しています。その方法は、要素から StAX パーサーを取得するというものです。パーサーを使って表現から XML を抽出するという機能は顕著な対称性をもたらし、AXIOM 表現がオンデマンドで構築されるときに有効に働きます (表現を構築するために使用しているパーサーを直接リターンすることができるため)。

リスト 5. AXIOM からの文書の作成
    /**
     * Output a document as XML text.
     *
     * @param doc document representation to be output
     * @param out XML document output stream
     */
    protected void output(Object doc, OutputStream out) {
        try {
            ((OMDocument)doc).serializeAndConsume(out);
        } catch (Exception ex) {
            ex.printStackTrace(System.err);
            System.exit(0);
        }
    }

AXIOM には、既存の文書コンポーネントを変更するための基本メソッド (要素のコンテンツをテキスト値に設定する OMElement.setText() など) が用意されています。ゼロから始める場合は、コンポーネントの新しいインスタンスを直接作成することになります。AXIOM API はインターフェースに基づいているため、ファクトリーを使用してコンポーネントの実際の実装を作成します。

AXIOM API の詳細は、「参考文献」セクションの AXIOM チュートリアルと JavaDocs のリンクにアクセスしてください。

AXIOM での MTOM

AXIOM でとりわけ興味深いフィーチャーの 1 つとして挙げられるのは、SOAP 添付の最新バージョンで使用されている W3C XOP 標準と MTOM 標準のサポートが組み込まれていることです。この 2 つの標準は連携し、XML バイナリー最適化パッケージ (XOP: XML-binary Optimized Packaging) で XML 文書が論理的に任意のバイナリー・データの塊を組み込めるようにし、MTOM (SOAP Message Transmission Optimization Mechanism) で XOP 手法を SOAP メッセージに適用します。XOP と MTOM は待望の相互運用可能な添付サポートを実現し、この分野での現在の問題に終止符を打つことになるため、新世代の Web サービス・フレームワークには不可欠のフィーチャーです。

XOP は base64 エンコード方式の文字データ・コンテンツを処理します。base64 エンコード方式は、元のデータを 6 ビットごとに 1 文字の ASCII 文字で表現することにより、任意のデータ値を印刷可能な ASCII 文字に変換します。バイナリー・データを XML に埋め込むことは通常は不可能なので (XML は未処理のバイトではなく文字を処理するため、文字コードでさえも XML で許可されないものは多数あります)、バイナリー・データを XML メッセージに埋め込むには base64 エンコード方式が役立ちます。

XOP は実際の base64 テキストを XOP 名前空間の特殊な「Include」要素に置き換えます。Include 要素は、個別エンティティー (XML 文書の外部にあって、XML 文書に組み込まれる実際のデータ) を識別する URI を指定します。通常、この URI が XML 文書と同時に伝送される個別ブロックを識別することになります (これは必要条件ではありませんが、文書を中継して交換する場合や文書を保管する場合に備えた利点となります)。base64 テキストを未処理データへの参照に置き換えることの利点は、文書サイズがある程度小さくなること (一般的な文字エンコード方式より最大 25 パーセント小さくなります)、そして base64 データのエンコードとデコードのオーバーヘッドがなくなるため処理速度が上がることです、

MTOM は XOP に基づき、まず SOAP メッセージで可能な XOP 使用方法の抽象モデルを定義し、次にそのモデルを MIME Multipart/Related パッケージ用に特殊化して、最後に HTTP トランスポートにモデルを適用します。このメカニズム全体が、広く使用されている HTTP トランスポートで XOP を SOAP メッセージに適用する標準方法となります。

AXIOM は org.apache.AXIOM.om.OMText インターフェースとその実装によって XOP/MTOM をサポートします。OMText は、バイナリー・データによって返されたテキスト項目をサポートするメソッド (Java Web サービス・フレームワークでの添付サポートに広く用いられている Java Activation API に含まれる javax.activation.DataHandler 形式)、そしてその項目が XOP で処理できるかどうかを示す「最適化」フラグを定義しています。org.apache.AXIOM.om.impl.llom.OMTextImpl 実装は、MTOM 準拠のコンテンツ ID を追加します。この ID はクラスのインスタンスが作成されたときに設定することも、自動的に生成することも可能です。

リスト 6 は、XOP/MTOM を使用した AXIOM でのメッセージ作成方法の一例です。このコードは、Java のシリアライゼーションによって結果のデータ構造をバイトの配列に変換し、その配列を添付として返すパフォーマンス・テストの例から抜粋しました。

リスト 6. XOP/MTOM メッセージの作成
    public OMElement matchQuakes(OMElement req) {
        Query query = new Query();
        Iterator iter = req.getChildElements();
        try {
            ...
            // retrieve the matching quakes
            Response response = QuakeBase.getInstance().handleQuery(query);
            
            // serialize response to byte array
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(response);
            byte[]byts = bos.toByteArray();
            
            // generate response structure with reference to data
            ByteArrayDataSource ds = new ByteArrayDataSource(byts);
            OMFactory fac = OMAbstractFactory.getOMFactory();
            OMNamespace ns =
                fac.createOMNamespace("http://seismic.sosnoski.com/types", "qk");
            OMElement resp = fac.createOMElement("response", ns);
            OMText data = fac.createOMText(new DataHandler(ds), true);
            resp.addChild(data);
            return resp;
            
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

リスト 6 のコードは XOP/MTOM で送信可能な応答を生成しますが、Axis2 の現行バージョンでは、XOP/MTOM サポートはデフォルトで無効に設定されています。サポートを有効にするには、Axis2 の axis2.xml ファイルまたはお使いのサービスの services.xml ファイルのどちらかに <parameter name="enableMTOM">true</parameter> パラメーターを含めます。この例の完全なコードは、今後のパフォーマンス比較の一環として説明しますが、とりあえずは XOP/MTOM の使用例で締めくくることにします。

リスト 7 に、XOP/MTOM が有効な場合と無効な場合にリスト 6 のサービスで生成される応答メッセージの構造を示します (最初の例では、MIME ヘッダーと実際のバイナリー添付がなく、2 番目の例では大部分のデータが削除されています)。

リスト 7. XOP/MTOMを使用した場合と使用しない場合の応答メッセージ
<?xml version='1.0' encoding='UTF-8'?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header />
    <soapenv:Body>
      <qk:response xmlns:qk="http://seismic.sosnoski.com/types"
          xmlns:tns="http://ws.apache.org/axis2">
        <xop:Include href="cid:1.urn:uuid:966CA4565647BEBA3D115028348657315@apache.org"
            xmlns:xop="http://www.w3.org/2004/08/xop/include" />
      </qk:response>
    </soapenv:Body>
  </soapenv:Envelope>
[actual binary data follows as separate MIME part with referenced content id]

<?xml version='1.0' encoding='UTF-8'?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header />
    <soapenv:Body>
      <qk:response xmlns:qk="http://seismic.sosnoski.com/types"
          xmlns:tns="http://ws.apache.org/axis2">rO0ABXNyACdjb20uc29zb...</qk:response>
    </soapenv:Body>
  </soapenv:Envelope>

データ・バインディング・フック

Web サービスに取り組んでいる開発者のほとんどは、データを XML 文書 (あるいは AXIOM などの文書モデル) ではなく、Java オブジェクト形式で扱わなければなりません。Axis や JAX-RPC といった前世代のフレームワークでは独自のデータ・バインディング形式を実装して XML と Java オブジェクトの変換を行っていましたが、ソリューションとしては極めて制約がありました。一般的に、Web サービス・フレームワークでのデータ・バインディング実装は特殊化されたデータ・バインディング・フレームワークと相容れないので、XML の処理をもっと制御できるようにしたいというユーザーは、非効率的な変換コードで 2 つのフレームワークを結合しなければならなかったのです。このような問題があったことから、Axis2 は当初から広範なデータ・バインディング・フレームワークを使って「プラグイン」データ・バインディングをサポートするように設計されました。

データ・バインディング・サポートでは、Axis2 に含まれる WSDL2Java ツールへのカスタマイズ拡張を使用します。このツールは WSDL サービス記述に基づき、クライアント側にはスタブ、サーバー側にはメッセージ・レシーバーという形で Axis2 リンケージ・コードを生成します。クライアント側のスタブはサービスの呼び出しを行うプロキシーとして機能し、サービス操作を実装するメソッド呼び出しを定義します。一方、サーバー側のメッセージ・レシーバーはクライアントのプロキシーとして機能し、実際のユーザー定義サービス・メソッドを呼び出します。WSDL2Java コマンド・ラインでデータ・バインディングが要求されると、ツールは指定されたデータ・バインディング・フレームワークの拡張を呼び出し、OMElements と Java オブジェクト間の変換を行うコードをスタブまたはメッセージ・レシーバーで生成します。スタブの場合、Java オブジェクト (またはプリミティブ値) がメソッド呼び出しで渡され、変換された XML がサーバーに要求として送信されます。返された XML は Java オブジェクトに再び変換されてからメソッド呼び出しの結果として返されます。サーバー側のメッセージ・レシーバーは、この一連の変換を逆の順序で行います。

インバウンド側またはアンマーシャル側 (受信した XML を Java オブジェクトへ変換する側) での処理は簡単です。Java オブジェクトに変換される XML 文書のペイロードは OMElement 形式で使用できるため、データ・バインディング・フレームワークは要素のデータを処理するだけで済みます。OMElement は、現行のほとんどのデータ・バインディング・フレームワークで入力としてそのまま使用できる StAX javax.xml.stream.XMLStreamReaderの形式で要素データへのアクセスを提供します。

アウトバウンド側またはマーシャル側 (Java オブジェクトを送信される XML に変換する側) での処理は多少複雑です。AXIOM の核心は、絶対的に必要でない限り、XML データの完全表現を構築しなくても済むようにすることです。マーシャルする際にこの原則をサポートするには、データ・バインディング・フレームが必要なときにだけ呼び出されるようにしなければなりません。AXIOM はこれに対処するため、データ・バインディング変換のラッパーとして org.apache.AXIOM.om.OMDataSource を使用します。OMDataSource はラップされたコンテンツを AXIOM がサポートするいずれかのメソッドで (java.io.OutputStream、java.io.Writer、または StAX javax.xml.stream.XMLStreamWriter に) 書き出すためのメソッド、そしてラップされたコンテンツのパーサー・インスタンスを返す別のメソッドを定義しています。OMElement 実装では、OMDataSource のインスタンスを使ってオンデマンドでデータを提供することが可能で、OMFactory インターフェースがこの種の要素を作成するメソッドを提供します。

パフォーマンス

AXIOM を話題にした記事の仕上げとして、ここからはパフォーマンスについて簡単に説明します。この記事を書いている時点での AXIOM は 1.1 リリースです。つまり、記事で説明したインターフェースは安定しているはずです。一方パフォーマンスは、実際の実装コードが変更されるにつれ、変化せざるを得ません。AXIOM のパフォーマンスは他の文書モデルと比べて大差ないと思いますが、いくつかの細かい点では違いが出てくる可能性があります。

AXIOM を他の文書モデルと比較するため、文書モデルの以前の記事 (「参考文献」セクションを参照) で使用したコードを更新しました。更新内容は、AXIOM と別の新しい文書モデル (XOM) を追加したこと、以前の SAX 標準ではなく StAX パーサー標準を使用するようにコードを変換したこと、そしてJava 5 で導入された改善済みの System.nanoTime() タイミング・メソッドに切り替えたことです。パフォーマンス・テスト・コードは、テストで使用する文書をメモリーに読み込んでから、その文書で一連の操作を実行します。この一連の操作とは、まず、パーサーからそれぞれに結果の文書オブジェクトが保持された文書表現のコピーを複数作成します。次に、各文書オブジェクトを「ウォークスルー」します (つまり、コードですべての属性、値、テキスト・コンテンスを含む文書表現全体をスキャンします)。そして最後に、各文書オブジェクトを出力ストリームに書き込んで完了です。それぞれの操作にかかった時間を記録し、テストの実行終了時に平均値を出します。

AXIOM の (とくに、Axis2 での用途での) 焦点は SOAP メッセージの処理に置かれているため、パフォーマンスのチェックには 3 種類の SOAP メッセージ・テスト・ケースを使いました。最初のテスト・ケースは Web サービス・パフォーマンス・テストからのサンプル応答で、特定の期間と緯度/経度の範囲で発生した地震に関する情報を提供します (「quakes」、18 KB)。この文書には、ネストを使用したり、属性を頻繁に使用した繰り返し要素が多数含まれています。2 番目のテスト・ケースは Microsoft WCF 相互運用性テストからのサイズの大きなサンプルです。このサンプルでは、単一の構造が少しずつ値を変えて繰り返されています (「persons」、202 KB)。この文書にはテキスト・コンテンツを持つ子要素がありますが、属性は使用されていません。3 番目のテスト・ケースは、以前の相互運用性テストから引用したサイズの小さい 30 の SOAP メッセージ文書です (合計サイズ 19 KB)。フォーマット設定用の空白文字はテスト文書からすべて取り除き、文書が実動サービスで交換される XML を表すようにしています (実動サービスでは通常、メッセージ・サイズを縮小するためにフォーマット設定が無効にされます)。

以下のグラフは、各文書を 50 回渡すのにかかる平均時間を示しています。テスト環境は 1600 MHz の ML-30 AMD Turion プロセッサーと 1.5 GB の RAM を搭載した Compaq ノートブック・システムで、Mandriva 2006 Linux で Sun の 1.5.0_07-b03 JVM を実行しています。テストしたのは、AXIOM 1.0、dom4j 1.6.1、Xerces2 2.7.0、そして Nux 1.6 配布の XOM です。dom4j、JDOM、および Xerces2 には StAX パーサーのカスタム・ビルダーを使用し、XOM には Nux StAX パーサー・ビルダーを使用しました。また、すべてのテストで Woodstox StAX パーサー 2.9.3 を使用しています。

図 1 に示すのは、テスト・シーケンスの最初の 2 つのステップ、パーサーからの文書の構築と文書表現のウォークスルーによるコンテンツのチェックに必要な平均時間の合計です。最初のステップだけを切り離して調べると (時間は記載していません)、少なくとも最初の 2 つの文書に関しては AXIOM のパフォーマンスが他の文書モデルより優れています。ただしこれは、AXION が期待通りに機能し、必要になるまでは完全な文書を構築していないことを示すに過ぎません。公正な比較を行うには、実際にメモリー内で完全な表現を構築するのにかかる時間を使用する必要があります。そのため、このグラフでは最初の 2 つのステップの時間を合計しています。

図 1. 文書構築および拡張の所要時間 (ミリ秒単位)
図 1. 文書構築および拡張の所要時間 (ミリ秒単位)
図 1. 文書構築および拡張の所要時間 (ミリ秒単位)

図 1 を見るとわかるように、AXIOM の全体的な処理速度は、Xerces2 を別として、テストした文書モデルのどれよりも劣っています。

サイズの小さい文書の集合 (「soaps」) となると、文書モデルのいくつかにパフォーマンス上の問題が現れました。これは Xerces2 で特に顕著でしたが、AXIOM にもかなりのオーバーヘッドが発生しています。このグラフが示す問題のなかで、おそらくこれが最も厄介な問題でしょう。複数の小さなメッセージがあるのは大抵の Web サービスでは一般的なことなので、AXIOM にはそのようなメッセージを効率的に処理する能力が求められます。AXIOM はツリーのオンデマンド拡張を中心に設計されたことを考えると、大きいサイズの 2 つの文書にかかる時間はそれほど重大な問題ではありません。これらの文書は少なくとも、他の文書モデルに似通っているからです。

図 2. 文書書き込みの所要時間 (ミリ秒単位)
図 2. 文書書き込みの所要時間 (ミリ秒単位)
図 2. 文書書き込みの所要時間 (ミリ秒単位)

図 2 に、それぞれのモデルを使って文書を出力ストリームに書き込むのにかかる平均時間を示します。ここでは Xerces2 がかなりの差で他を引き離している一方 (ただし、構築ステップでのパフォーマンスを埋め合わせるほどではありません。2 つのグラフでは目盛りが異なります)、AXIOM のパフォーマンスは最下位となっています。ここでも、AXIOM はサイズの小さい文書ではとりわけ処理能力が落ちるようです。

図 3. 文書のメモリー・サイズ (KB 単位)
図 3. 文書のメモリー・サイズ (KB 単位)
図 3. 文書のメモリー・サイズ (KB 単位)

最後に、各フレームワークが文書を表現するために使用したメモリーを図 3 に示します。またもや dom4j が最高の成績で、AXIOM は大幅な差で最下位の成績となっています。AXIOM のメモリー使用率での成績不振は、その理由の 1 つとして、構築された文書がパーサーを参照するため、パーサーのインスタンスが文書の使用中ずっと保持されることにあります。AXIOM がサイズの小さい文書で特に成績が悪いのも、これで一部説明がつきます。ただし、AXIOM が文書のコンポーネントとして使用するオブジェクトは、他の文書モデルでそれに対応するものよりかなり大きいため、この差が、AXIOM がサイズの大きい 2 つのテスト文書でもスペースをかなり消費する理由となっているはずです (パーサーと他のデータ構造の一定サイズのオーバーヘッドは、合計のメモリー使用率に比例して小さくなります)。

最初の 2 つのグラフの時間を合計すると、全体的なパフォーマンスが最も優れているのは dom4j で、最も劣っているのは Xerces2 です (AXIOM は後者よりほんのわずかの差で勝っています)。メモリー使用率でも同じく dom4j が最も優秀ですが、このコンテストで最悪の成績を出しているのは疑いようもなく AXIOM です。AXIOM には不利な状況となってきました。

完全なツリー表現が常に構築されるとしたら AXIOM に勝ち目はありませんが、ここで思い出してもらいたいのは、AXIOM の核心は、完全な表現は必要ないことが多いという点にあります。図 4 に、AXIOM で最初の文書構築にかかった時間を、他の文書モデルが文書表現を構築する時間と比較しています。この場合、AXIOM は断然他の文書モデルより高速です (サイズが大きい 2 つの文書に関しては、時間を記録できないほど高速です)。メモリーにも同じような比較結果が当てはまります。最終結果は、文書モデルの一部だけを操作する場合には (文書の順序から言うと、「最初」の部分)、AXIOM が優れたパフォーマンスをもたらすということです。

図 4. 最初の文書構築
図 4. 最初の文書構築
図 4. 最初の文書構築

Axis2 への移行

この記事では、Axis2 の中核にある AXIOM 文書オブジェクト・モデルの詳細を説明しました。AXIOM には、完全表現のオンデマンド構築という手法をはじめ、いくつかの興味深い技術革新を具現化しています。完全な拡張表現が必要な場合、AXIOMは他の文書モデルほど優れているわけではありません。とりわけサイズが小さい文書となるとパフォーマンスは振るいませんが、AXIOM が Web サービスの処理にもたらす柔軟性はこうした多くの懸念を補います。

データ・バインディング・フレームワークとの XML の受け渡し方法をはじめ、Axis2 が AXIOM を使って SOAP メッセージ表現を処理する方法が理解できたはずです。次回の記事では、3 つのフレームワークを使ったコード・サンプルを用いて、各種データ・バインディング・フレームに対する Axis2 のサポートの仕組みをユーザーの視点から説明します。


ダウンロード可能なリソース


関連トピック

  • Java 文書オブジェクト・モデルのより詳細な比較については、Dennis Sosnoski による以前の記事「Document models, Part 1: Performance」および「Java document model usage」を参照してください。
  • developerWorks の Web services 標準ライブラリーにアクセスして、広範な WS-* 技術に関する情報を入手してください。
  • Eran Chinthaka による「Web services and Axis2 architecture」は、Axis2 の概要を説明した優れた記事です。
  • 次期開発プロジェクトの構築に、developerWorks から直接ダウンロードできるIBM の試用版ソフトウェアをご利用ください。
  • dom4j は、処理速度とメモリー効率、そして拡張性に優れた文書オブジェクト・モデルです。
  • JDOM 文書オブジェクト・モデルはその使いやすさで有名です。
  • XOM 文書オブジェクト・モデルは、XML でユーザーが犯しがちな誤ちを防ぐとともに、優れたパフォーマンスとメモリー効率を提供します。
  • Nux は高スループットの XML メッセージング・ミドルウェアを対象にしたオープン・ソース・プロジェクトで、選り抜きの XML 処理用コンポーネントを 1 つのツール・キットに統合することを目的としています。
  • Xerces2 は、W3C 文書オブジェクト・モデル標準の Apache 実装です。
  • Apache Axis2 の最新情報とダウンロードを入手してください。
  • WoodStox は、StAX プル・パーサー標準のオープン・ソース実装です。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and web services, Java technology
ArticleID=247521
ArticleTitle=Java Web サービス: 第 2 回 Axis2 の徹底調査: AXIOM
publish-date=11302006