Java Web サービス: 第 3 回 Axis2 でのデータ・バインディング

Axis2 がサポートする、XML データへのアクセスを容易にする多様なデータ・バインディング・フレームワーク

Apache Axis2 Web サービス・フレームワークは、当初から複数の XML データ・バインディング手法をサポートするように設計されています。現行のリリースで完全にサポートしているのは、XMLBeans および JiBX データ・バインディング、そして Axis2 専用に開発されたカスタム ADB (Axis Data Binding) の手法です。この記事では、Axis2 でこれらの異なるデータ・バインディングを使用する方法を紹介するとともに、アプリケーションに応じてデータ・バインディングを使い分ける理由を説明します。

Dennis Sosnoski, Consultant, Sosnoski Software Solutions, Inc.

Photo of Dennis SosnoskiDennis Sosnoski はシアトル地域にある Java 技術のコンサルティング会社、Sosnoski Software Solutions, Inc. の創立者で、主席コンサルタントでもあり、また XML や Web サービスに関するトレーニングやコンサルティングの専門家でもあります。彼のプロとしてのソフトウェア開発経験は 30年以上に渡り、ここ数年はサーバー側の XML 技術や Java 技術に注力しています。Dennis は、全米各地で行われる会議で頻繁に講演を行っています。また、Java クラスワーキング技術を基に構築された、オープンソースの JiBX XML Data Binding フレームワークの中心開発者でもあります。



2007年 7月 26日

XML メッセージ交換は Web サービスの中核とは言うものの、ほとんどの Web サービス・アプリケーションは XML に関心を払っていません。Web サービス・アプリケーションが必要とするのは、そのアプリケーション固有のビジネス・データを交換することです。この場合、XML はビジネス・データを表すために用いる単なる形式として Web サービス・インターフェースをサポートするに過ぎません。このような用途に XML は有効に機能します。XML はプラットフォームに依存することなく各種のツールで処理可能な表現になるからです。しかし、アプリケーションがその内部でデータを使用するには、結局は XML と独自の内部データ構造との間での変換をしなければなりません。

データ・バインディングとは、この XML とアプリケーション・データ構造間での変換を処理する手法を表す言葉です。アプリケーション用にカスタム・データ・バインディング・コードを作成することも可能ですが、大抵の開発者にとっては、広範なアプリケーションに適用できる一般的な方法で変換を処理するデータ・バインディング・フレームワークを操作するほうが便利です。Apache Axis2 Web サービス・フレームワークでは、当初から各種のデータ・バインディング・フレームワークと連動するように設計されていることがその主たる利点の 1 つとなっています。それぞれのニーズに最適なデータ・バインディング手法を選んで XML とデータ構造との変換を処理できると同時に、Axis2 フレームワーク (およびその拡張機能) を使って実際の Web サービスの作業を処理することが可能です。

この記事では、Axis2 がサポートする 3 つのデータ・バインディングそれぞれを使って実装した同じ Web サービスのサンプル・コードを例に、Axis2 によるデータ・バインディングの柔軟性を使用する方法を説明します。この 3 つのなかから、自分に適したデータ・バインディングを見極めてください。

Axis2 へのリンク

この連載の前回の記事では、Axis2 が XML メッセージを処理するために使用する AXIOM 文書モデルについて説明しました。AXIOM が他の文書モデルと異なる点は、モデルを一度にすべて構築するのではなく、モデルのオンデマンド構築をサポートするところです。データ・バインディング・フレームワークを使って XML とアプリケーション・データ構造間での変換を行う場合、データがバインドされた XML は一般的に AXIOM 文書モデルの仮想の部分でしかありません。何らかの理由でそのモデルが必要にならない限り (WS-Securityを使用する際の暗号化や署名など)、完全な文書モデルに拡張されることはありません。

アプリケーションが直接 AXIOM を操作しなくても済むように、Axis2 では WSDL (Web Services Description Language) サービス記述からリンケージ・コードを生成できるようにサポートしています。生成されたリンケージ・コードが、選択されたデータ・バインディング・フレームワークを使ってデータ構造と XML 間の変換の詳細を処理し、アプリケーションがデータ構造に直接アクセスできるようにします。これとは逆に既存のコードから WSDL を生成する場合も、Axis2 では制限付きでサポートしています。

Axis2 はサービス・クライアントとサービス・プロバイダーの両方に対してリンケージ・コードを生成します。クライアントのリンケージ・コードは常に Axis2 の org.apache.axis2.client.Stub クラスを継承したスタブ・クラスの形となります。一方、プロバイダー (サーバー) のリンケージ・コードはサービス固有の実装スケルトンという形となり、org.apache.axis2.engine.MessageReceiver インターフェースを実装するメッセージ・レシーバー・クラスを伴います。リンケージ・コードの生成を処理するのは、クライアントの場合もサーバーの場合も WSDL2Java ツールです。ここからは、実際のリンケージ・コードを見てから WSDL2Java ツールの詳しい使用方法を探り、最後に既存のコードから作業を始める方法を簡単に紹介してこのセクションを締めくくりたいと思います。

クライアント・リンケージ・コード

クライアント側のスタブ・コードは、アプリケーション・コードがサービス操作を起動するためのアクセス・メソッドを定義します。まず始めに、スタブ・クラスのインスタンスを作成します。インスタンスを作成するのに通常使用するのは、デフォルトのコンストラクター (サービス・エンドポイントが常に、スタブの生成で使った WSDL に定義されたサービス・エンドポイントと同じである場合)、または異なるエンドポイント参照をストリングとして使用するコンストラクターです。スタブのインスタンスを作成した後、オプションで基本 org.apache.axis2.client.Stub クラスが定義するメソッドを使用して各種の機能を構成することもできます。この作業が終わったら、実際に操作を起動するサービス固有のアクセス・メソッドを呼び出せるようになります。

リスト 1 は、サービス・エンドポイントを (WSDL で定義されたものから) クライアント・システム (localhost) のデフォルト Tcpmon ポート8800 に変更してスタブを作成する例です。Tcpmon は Web サービスのメッセージ交換の監視によく使われるツールなので、このツールをクライアント・コードのオプションとして持っていると役立つことがよくあります。スタブ・インスタンスを作成した後、デフォルトのタイムアウト値を変更し (20 秒の標準タイムアウトはすぐに超過してしまう可能性があるので、デフォルト値を変更するとプロバイダー・コードをデバッグするときにも役立ちます)、サービス・メソッドを呼び出します。

リスト 1. クライアント・スタブの使用例
LibraryStub stub = new LibraryStub("http://localhost:8800/axis2/services/library");
stub.getServiceClient().getOptions().setTimeoutInMilliseconds(10000000);
Types[] types = stub.getTypes();

リスト 1 のコードが示しているのは同期サービス・メソッド呼び出しで、この場合、クライアントのスレッドはサービス呼び出し内部でブロックし、呼び出しが完了して結果が利用できるようになるまでリターンしません。Axis2 では非同期呼び出しも、コールバック・インターフェースを使ってサポートします。リスト 2 はリスト 1 のコードを変更したもので、今度はごく普通の非同期呼び出しを使用しています (ごく普通としたのは、アプリケーション・コードが何か有用なことを行うのではなく、ただ単に操作の完了を待機するだけからです)。

リスト 2. クライアント・スタブの非同期バージョン
LibraryStub stub = new LibraryStub("http://localhost:8800/axis2/services/library");
TypesCallback cb = new TypesCallback();
stub.startgetTypes(cb);
Type[] types;
synchronized (cb) {
 while (!cb.m_done) {
  try {
   cb.wait();
  } catch (Exception e) {}
 }
 types = cb.m_result;
 if (types == null) {
  throw cb.m_exception;
 }
}
...
private static class TypesCallback extends LibraryCallbackHandler
{
 private boolean m_done;
 private Types[] m_result;
 private Exception m_exception;
            
 public synchronized void receiveResultgetTypes(Type[] resp) {
   m_result = resp;
   m_done = true;
   notify();
 }
            
 public synchronized void receiveErrorgetTypes(Exception e) {
   m_exception = e;
   m_done = true;
   notify();
 }
}

リスト 2 で使用している HTTP 接続では、応答はクライアントにすぐに返されるのが通常ですが、非同期呼び出しが特に役立つのは、JMS (Java™ Message Service) や SMTP (Simple Mail Transfer Protocol) などといった、要求と応答を分離するトランスポートで動作している場合です。なぜなら、このようなトランスポートでは要求を送信してから応答を受信するまでかなり時間がかかる可能性があるからです。もちろん HTTP でアクセスするサービスにも、大幅な処理遅延が関わってくることがあります。そのような遅延が発生する HTTP サービスでは、WS-Addressing を使用して応答を分離させることができますが、これらの応答を処理するのに非同期呼び出しが役立つことになります。

スタブ・クラス、そしてコールバック・ハンドラー・クラス (非同期サポートで生成する場合) の他、クライアント・コードに対してはインターフェースも生成されます。このインターフェースが定義するのは、WSDL portType で定義された操作と一致するサービス・メソッドです。スタブはこのインターフェースを実装し、内部で使用する特殊化されたいくつかのメソッドを追加します。リスト 1リスト 2 のようにスタブを直接操作することもできますが、代わりに、このインターフェースを使用してサービス定義に含まれるメソッドのみを表示させることもできます。どちらの場合にしても、サービス・メソッドのいずれかを呼び出すと、スタブは選択されたデータ・バインディング・フレームワークを使用して、要求データ・オブジェクトを XML に変換し、そして返された XML から応答データ・オブジェクトに変換します。

クライアント上で XML を直接操作する場合、生成されたクライアント・スタブ・クラスを使用する必要は一切ありません。代わりに、org.apache.axis2.client.ServiceClient クラスを使用できるからです。このクラスを使用するには、サービスと操作を構成してから、ServiceClient.createClient() メソッドを呼び出して操作に対応する org.apache.axis2.client.OperationClient を作成しなければなりません。便宜上、WSDL2Java ツール (この記事で後ほど説明) には XML を直接操作しているとしてもスタブ・クラスを生成するというオプションが用意されています。この場合に生成されるスタブは、データ・バインディングのサンプルとかなり似たものになりますが、データ・オブジェクトではなく AXIOM 要素を渡すという点が異なります。

サーバー・リンケージ・コード

Axis2 のサーバー側のリンケージ・コードは、Axis2 サービス構成の一部として定義されたメッセージ・レシーバーです。このメッセージ・レシーバーは、org.apache.axis2.engine.MessageReceiver インターフェースを実装しなければなりません。このインターフェースが定義するのは、単一の void receive(org.apache.axis2.context.MessageContext) メソッドです。Axis2 フレームワークは要求メッセージを受け取るとこのメソッドを呼び出し、このメソッドによって要求のすべての処理を行わせます (該当する場合は、応答の生成も含まれます)。

XML を (AXIOM 要素という形で) 直接操作している場合、サーバー側のリンケージ・コードには標準 org.apache.axis2.receivers.RawXML*MessageReceiver クラスのいずれかを使用することができます (* は、サービスが使用するメッセージ交換のタイプ)。このクラスを使用しないのであれば、Axis2 AXIOM ベースのインターフェースとデータ・オブジェクトを使用するサービス・コードとの間で適応する生成済みのメッセージ・レシーバー・クラスを使用することになります。このサービス・コードはスケルトン実装という形で生成され、例外をスローするだけのサービス・メソッドを持ちます。サーバー側の連携を完成させるには、このスケルトンに独自のコードを追加する必要があります。

リスト 3 は、サーバー側のスケルトンの一例です (読みやすいようにフォーマットを変えています)。この例では、getBook() メソッドは生成された時点のままで、getTypes() メソッドは実際の実装クラスに委譲することによって実装されています。

リスト 3. サーバー・スケルトンの例
public class LibrarySkeleton
{
    private final LibraryServer m_server;
    
    public LibrarySkeleton() {
        m_server = new LibraryServer();
    }
    
    /**
     * Auto generated method signature
     *
     * @param isbn
     * @return book value
     */
    public com.sosnoski.ws.library.Book getBook(java.lang.String isbn) {
        //Todo fill this with the necessary business logic
        throw new java.lang.UnsupportedOperationException("Please implement " +
            this.getClass().getName() + "#getBook");
    }

    /**
     * Get the types of books included in library.
     *
     * @return types
     */
    public com.sosnoski.ws.library.Type[] getTypes() {
        return m_server.getTypes();
    }
}

このクラスに直接コードを追加する上でのマイナス面は、サーバー・インターフェースが変更された場合にクラスを生成しなおして変更をマージしなければならないという点です。このような事態を避けるためには、生成済みのスケルトンを継承する別個の実装クラスを使用して、生成されたコードを変更せずにスケルトンをオーバーライドできるようにするという方法が考えられます。そのために必要となるのは、生成された services.xml サービス記述の変更です。変更するのは簡単で、単にスケルトン・クラスの名前を実装クラスの名前に置き換えればいいだけのことです。この記事でこれから記載するデータ・バインディングのサンプルではすべて、この別個の実装クラスの手法を使用します。この置換を自動化する方法については、「ダウンロード」セクションから入手できるこれらのサンプルに対応した Ant build.xml ファイルを参照してください。


Axis2 のツール

Axis2 には、フレームワークを使用する開発者を支援するためにさまざまなツールが用意されています。そのうちとりわけ重要なのは、Java リンケージ・コード (前のセクションで説明) を WSDL サービス定義から生成し、WSDL サービス定義を既存の Java コードから生成できるようにするためのツールです。

WSDL からコードを生成する

Axis2 が用意している WSDL2Java ツールは、WSDL サービス記述からコードを生成する際に使用します。このツールは、org.apache.axis2.wsdl.WSDL2Java クラスを Java アプリケーションとして実行して直接使用することも、Ant タスク、Maven プラグイン、または Eclipse あるいは IDEA プラグインを介して使用することもできます。これほどまでに選択肢が多いということは、代替策に関しては機能とバグの修正という点で基本 Java アプリケーションに後れを取りがちだということでもあります。そのため、通常は Java アプリケーションを直接実行する方法のほうが問題ありません (この記事で説明する方法)。

WSDL2Java が提供する豊富なコマンド・ラインのオプションは、次第にその数を増やしています。オプションの詳細は Axis2 の資料に記載されているので、ここではとりわけ重要なオプションだけを抜粋することにします。

  • -o path - 出力クラスとファイルのターゲット・ディレクトリーを設定します (デフォルトでは作業ディレクトリーに設定)。
  • -p package-name - 生成されるクラスのターゲット・パッケージを設定します (デフォルトでは WSDL 名前空間から生成)。
  • -d name - データ・バインディング・フレームワークを設定します (adb は ADB、xmlbeans は XMLBeans、jibx は JiBX、none はデータ・バインディングなしを指定します。デフォルトは adb です)。
  • -uw - document/literal でラップされたメッセージをアンラップします。サポート対象のフレームワーク (現在は ADB および JiBX のみ) 専用です 。
  • -s - 同期クライアント・インターフェースのみを生成します。
  • -ss - サーバー側のコードを生成します。
  • -sd - サーバー側のデプロイメント・ファイルを生成します。
  • -uri path - サービスを生成する対象となる WSDL へのパスを設定します。

この他、特定のデータ・バインディング・フレームワークに固有の WSDL2Java オプションもあります。これらのオプションのいくつかは、この後に記載するデータ・バインディングのサンプルで使用しています。

コードから WSDL を生成する

既存のサービス・コードから WSDL サービス定義を生成するために使用するツールとして、Axis2 では Java2WSDL ツールも用意しています。ただし、このツールには多くの制約があります。例えば、Java コレクション・クラスを操作できないこと、そして Java クラスから生成した XML を構成する段階で柔軟性がないことです。これらの制約は、一部には Web サービスの開発方法の変更により、この分野に関心が持たれていないことが原因となっています。

一般に、Web サービスと SOA の分野における専門家の多くは、既存のコードからの Web サービス開発を好ましいものと考えていません。XML は実装に依存しないことが Web サービスの原則そのものであることから、コードから開発を開始すれば、XML メッセージ構造を特定の実装に結びつけやすくなると考えているからです。明らかにこの考えには実効性がありますが、反対の意見もあります。その 1 つが、WSDL サービス定義と XML スキーマ定義を何もないところから作成する上での難しさです。WSDL とスキーマはどちらも複雑な標準で、これらの定義に取り組むためのツールを有効に使うには、この 2 つの標準を十分に理解していなければなりません。標準の基礎知識がない開発者が使用すると、完成した WSDL やスキーマが、コードから生成したものよりも乱雑になってしまいがちです。また、あくまでも実用本位であることも問題として挙げられています。開発者は、機能を実装するために使っていた既存のコードを今度は Web サービスとして公開しなければなりませんが、彼らは大幅な変更を加えずに既存のコードを使用できるようにしたいと思うものです。以上のことから、コードから WSDL を生成するべきかどうかは、しばらくは問題として残りそうです。

Java2WSDL よりも強力な手段としては、私が開発した Jibx2Wsdl ツールを試してみることもできます (詳細は「参考文献」セクションを参照)。Jibx2Wsdl は、指定された 1 つ以上のサービス・クラスから、WSDL、スキーマ、そして JiBX のバインディング定義のすべてを一式として生成します。このツールは Java 5 列挙型と汎用コレクションをサポートすると同時に以前の Java 仮想マシン (JVM) との互換性を維持し、生成された WSDL 定義とスキーマ定義のドキュメンテーションとして、Java ソース・ファイルから自動的に Javadoc をエクスポートします。Jibx2Wsdl には Java クラスからサービスおよび XML 表現を派生させる方法を制御する包括的なカスタマイズ・メカニズムもあるため、Java 5 以前のコレクションだとしても型付きデータと一緒に使用できます。Jibx2Wsdl の特定の目的は、JiBX バインディング・フレームワーク (これも私が作成しました) を使用して既存のクラスを Web サービスとしてデプロイしやすくすることですが、生成される WSDL およびスキーマはデータ・バインディングにとらわれません。そのため、別の Java データ・バインディング・フレームワークだけでなく、他のプラットフォームでも使用できます。すべてを生成した後、JiBX バインディングを破棄して残りをとっておけばいいだけの話です。

Java 5 以降を使用している場合に代わりの手段となるのは、JAXB (Java Architecture for XML) 2.0 と JAX-WS (Java API for XML Web Services) の注釈を使用してデータ・オブジェクトとサービス・クラスを Web サービスとして公開することです。これらの注釈では Jibx2Wsdl ほどのカスタマイズはできませんが、構成情報を直接ソース・コードに組み込むことができるため、開発者によっては魅力的に映るはずです。Axis2 の 1.2 リリースでは JAXB 2.0 と JAX-WS を試験的にサポートしており、今後のリリースで改善されることになるでしょう。Jibx2Wsdl の今後のバージョンでも JAXB 2.0 と JAX-WS の注釈をカスタマイズとして使用できるようになる可能性があります (連載の今後の記事では、JAXB 2.0 および JAX-WS を詳しく掘り下げてから、コードから WSDL を生成する話題についてもう一度取り上げる予定です)。


データ・バインディングの比較

Axis2 (1.2 リリースの時点) で完全にサポートしているデータ・バインディング方法は 3 つですが、この数はさらに増える予定です。この記事では、完全にサポートされる 3 つのデータ・バインディング・フレームを使ったサンプル・コードを比較し、Axis2 で使用した場合の各フレームワークの利点と欠点を説明します。

リスト 4 のサンプル・コード (「ダウンロード」セクションのサンプル・ダウンロードにも含まれています) は、ライブラリー・サービスを目的としたもので、本のコレクションをサブジェクトのタイプ別に整理します。このライブラリーには、以下をはじめとする操作が定義されています。

  • getBook
  • getTypes
  • addBook
  • getBooksByType

リスト 4 に、このサービスの WSDL を抜粋します。このリストに示されているのは、getBook 操作が関係する部分のみです。

リスト 4. ライブラリー・サービスの WSDL
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://ws.sosnoski.com/library/wsdl"
    xmlns:wns="http://ws.sosnoski.com/library/wsdl"
    xmlns:tns="http://ws.sosnoski.com/library/types"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
  <wsdl:types>
  
    <schema elementFormDefault="qualified"
        targetNamespace="http://ws.sosnoski.com/library/wsdl"
        xmlns="http://www.w3.org/2001/XMLSchema">
      
      <import namespace="http://ws.sosnoski.com/library/types"/>
        
      <element name="getBook">
        <complexType>
          <sequence>
            <element name="isbn" type="string"/>
          </sequence>
        </complexType>
      </element>
      
      <element name="getBookResponse">
        <complexType>
          <sequence>
            <element name="getBookReturn" minOccurs="0" type="tns:BookInformation"/>
          </sequence>
        </complexType>
      </element>
      ...
      
    </schema>
    
    <schema elementFormDefault="qualified"
        targetNamespace="http://ws.sosnoski.com/library/types"
        xmlns="http://www.w3.org/2001/XMLSchema">
      
      <complexType name="BookInformation">
        <sequence>
          <element name="author" minOccurs="0" maxOccurs="unbounded" type="string"/>
          <element name="title" type="string"/>
        </sequence>
        <attribute name="type" use="required" type="string"/>
        <attribute name="isbn" use="required" type="string"/>
      </complexType>
      ...
      
    </schema>

  </wsdl:types>

  <wsdl:message name="getBookRequest">
    <wsdl:part element="wns:getBook" name="parameters"/>
  </wsdl:message>

  <wsdl:message name="getBookResponse">
    <wsdl:part element="wns:getBookResponse" name="parameters"/>
  </wsdl:message>
  ...
  
  <wsdl:portType name="Library">

    <wsdl:operation name="getBook">
      <wsdl:input message="wns:getBookRequest" name="getBookRequest"/>
      <wsdl:output message="wns:getBookResponse" name="getBookResponse"/>
    </wsdl:operation>
    ...

  </wsdl:portType>

  <wsdl:binding name="LibrarySoapBinding" type="wns:Library">

    <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>

    <wsdl:operation name="getBook">
    
      <wsdlsoap:operation soapAction="urn:getBook"/>
      
      <wsdl:input name="getBookRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      
      <wsdl:output name="getBookResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
      
    </wsdl:operation>
    ...

  </wsdl:binding>
  ...

</wsdl:definitions>

実際のサービス実装コードは、ライブラリー・インスタンスにハードコーディングされた本のリストを入力するという単純なものです。クライアント・コードは、以下のクエリーを記載順に実行します。

  1. getBook
  2. getTypes
  3. addBook を 2 回。2 回目に、重複した本の ID を追加しようとしたことに対する SOAP Fault を返します。
  4. getBooksByType

それぞれのサンプルでは、該当するデータ・バインディングに適切なデータ・オブジェクトを使用しているため、実装の詳細はサンプルによって異なります。特に明記していない限り、記載するすべてのコードは、Axis2 の 1.1.1 と 1.2 両方のリリースで共通しています。Axis2 1.3 リリース (この記事が掲載される段階ではまだ進行中) では、サービス障害に対応して生成される例外クラスのネーミングが変更されることから、コードに多少の変更を加える必要があります。コードはどちらのバージョンでもダウンロードできます (「ダウンロード」セクションを参照)。

用意されたダウンロード (「ダウンロード」セクションを参照) には、すべてのサンプル用のクライアント・コードとサーバー・コードの両方が Ant ビルド・ファイルと併せて含まれていますが、この記事で取り上げるのはクライアント・コードのみです。それでは、3 種類のデータ・バインディング・フレームワークのクライアント・コードを検討して、それぞれの手法が持つ利点と欠点を調べることにしましょう。

Axis2 Data Binding

ADB は、Axis2 のデータ・バインディング拡張です。他のデータ・バインディング・フレームワークとは異なり、ADB のコードは Axis2 Web サービスにしか使えません。この点が ADB の際立った制約となっていますが、同時に利点ももたらします。ADB は Axis2 に統合されていることから、コードを Axis2 の要件に合わせて最適化できるのです。その一例として、ADB は Axis2 の中核である AXIOM (AXis Object Model) 文書モデルをベースとします (この連載の前回の記事で説明)。ADB にはまた、現在他のデータ・バインディング・フレームワークには用意されていない拡張機能もあります (自動添付処理など)。WSDL2Java は、XML スキーマ・コンポーネントに対応したデータ・モデル・クラスの生成を含め、ADB コードの生成を完全にサポートします。

ADB のスキーマ・サポートにはいくつかの制約事項があります。現行の Axis2 1.2 リリースでは、例えば maxOccurs="unbounded" が指定されたコンポジターや attributeFormDefault="qualified" が指定されたスキーマ定義、そしてこれと同様のバリエーションなどの、スキーマ機能を使用することができません。ただし、Axis2 1.2 リリースでの ADB スキーマ・サポートは Axis2 1.1 リリースよりも格段に改善されており、Axis2 フレームワークの今後のリリースですべての主要なスキーマ機能がサポートされるように改善されることが見込まれます。

ADB コードを生成する際の基本形としてはダイレクト・モデルを使用します。ダイレクト・モデルでは、個別のクラスが各操作で使用される入力メッセージと出力メッセージに対応します。リスト 5 に、この ADB コード生成の基本形を使った場合の、サンプル・アプリケーションのクライアント・コードで最も注目に値する部分を記載します。このリストに抜粋したクライアント・コードが示しているのは、ADB によって生成されるクラスとの相互作用です。これらのクラスには、getBook() メソッド呼び出しのパラメーターとして使用される GetBookDocument および GetBookDocument.GetBook クラス、この呼び出しのリターンとして使用される GetBookResponseDocument および BookInformation クラスがあります。

リスト 5. ADB のクライアント・コード
// create the client stub
AdbLibraryStub stub = new AdbLibraryStub(target);
        
// retrieve a book directly
String isbn = "0061020052";
GetBook gb = new GetBook();
gb.setIsbn(isbn);
GetBookResponse gbr = stub.getBook(gb);
BookInformation book = gbr.getGetBookReturn();
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
        
// retrieve the list of types defined
GetTypesResponse gtr = stub.getTypes(new GetTypes());
TypeInformation[] types = gtr.getGetTypesReturn();
System.out.println("Retrieved " + types.length + " types:");
for (int i = 0; i < types.length; i++) {
  System.out.println(" '" + types[i].getName() + "' with " +
    types[i].getCount() + " books");
}
        
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
  AddBook ab = new AddBook();
  ab.setType("scifi");
  ab.setAuthor(new String[] { "Cook, Glen" });
  ab.setIsbn(isbn);
  ab.setTitle(title);
  stub.addBook(ab);
  System.out.println("Added '" + title + '\'');
  ab.setTitle("This Should Not Work");
  stub.addBook(ab);
  System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFaultException e) {
  System.out.println("Failed adding '" + title +
    "' with ISBN '" + isbn + "' - matches existing title '" +
    e.getFaultMessage().getBook().getTitle() + '\'');
}
        
// create a callback instance
CallbackHandler cb = new CallbackHandler();
        
// retrieve all books of a type asynchronously
GetBooksByType gbbt = new GetBooksByType();
gbbt.setType("scifi");
stub.startgetBooksByType(gbbt, cb);
long start = System.currentTimeMillis();
synchronized (cb) {
  while (!cb.m_done) {
    try {
      cb.wait(100);
    } catch (Exception e) {}
  }
}
System.out.println("Asynchronous operation took " +
  (System.currentTimeMillis()-start) + " millis");
if (cb.m_response != null) {
  BookInformation[] books = cb.m_response.getGetBooksByTypeReturn();
  ...

リスト 5 では、ADB に固有の -u WSDL2Java オプションでコードを生成しています。このオプションを指定すると、メッセージおよびデータ・モデル・クラスごとに別個の Java ソース・ファイルが生成されます。このオプションを使用しない場合、ADB コードの生成ではこのすべてのクラスを、生成されたスタブの静的内部クラスとして生成します。個々のクラスを操作するほうが遥かに簡単なので、ADB を使用する場合は -u オプションを指定することをお勧めします。

このような直接的な形のコード生成では、各操作の入力と出力に多数の個別のクラスが生成されることになります (-u オプションは同じクラスを異なる方法で編成するだけなので、このオプションを使用するかどうかには関係ありません)。生成されるこれらのメッセージ・クラスには大抵、有効なデータはほとんど含まれませんが (GetTypes クラスの場合にはまったく含まれていません)、生成されるメソッド・シグニチャーに必要です。幸い、多くの共通サービスに適用される別の形のコード生成があります。

ADB アンラップ

Web サービスは、既存のプログラミング API をベースにメソッド呼び出しという形で開発されることがよくあります。この場合、既存の API を Web サービスに組み込むと便利ですが、これは簡単に行えます。サービス (細かいことにこだわる人のために厳密に言っておくと、portType) に定義された操作は、基本的にインターフェース定義のメソッド呼び出しに相当するからです。大きな違いと言えば、サービスでは入力と出力を呼び出しパラメーターと戻り値としてではなく、XML メッセージとして定義するという点のみです。そのため、既存の API を Web サービス定義に組み込むには、呼び出しパラメーターと戻り値を XML メッセージ構造として表現するための規則があればいいだけの話となります。

嬉しいことに、Microsoft® ではこの分野での規則を早い段階で確立しているため、私たち自身で解決する必要はありません。ラップされた document/literal (文書/リテラル) と呼ばれるこの規則は、.NET がメソッド呼び出しを Web サービス操作として公開するときに使うデフォルト表現です。このラップ手法では本質的に、各入力メッセージは一連の子要素だけで構成される XML 要素で、各出力メッセージは単一の子要素を持つ XML 要素となります。Microsoft による実装についての技術的な詳細は他にもありますが、.NET との完全な相互運用性に関するもの以外は重要ではありません。これらの詳細はさておき、サンプル・ライブラリー (抜粋はリスト 4 を参照) で使用するメッセージは、この手法に従っています。

WSDL2Java は、このように ADB コードの生成でラップされた document/literal サービスのアンラップをサポートします。適切な WSDL サービス定義でアンラップを使用すると、生成されるクライアント・スタブは 一層単純かつ直接的になります (サーバー・スケルトンにしても同じです)。リスト 6 は、リスト 5 に相当するクライアント・アプリケーションのコードですが、-uw パラメーターを WSDL2Java に渡して、アンラップされたインターフェースを生成しています。リスト 5 で複雑さを加えていたメッセージ・クラスは、リスト 6 では (GetTypes クラスは例外として) ほとんど取り除かれ、サービス・メソッドが受け取るパラメーターも返している結果も、メッセージ・クラスに組み込まれずに直接やり取りされます。裏では ADB が依然としてメッセージ・クラスを生成し、生成されたコード内で使用していますが、通常、コードはこれらのクラスを無視してデータを直接操作することができます。

リスト 6. ADB のアンラップされたクライアント・コード
// create the client stub
AdbUnwrapLibraryStub stub = new AdbUnwrapLibraryStub(target);
        
// retrieve a book directly
String isbn = "0061020052";
BookInformation book = stub.getBook(isbn);
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
        
// retrieve the list of types defined
TypeInformation[] types = stub.getTypes(new GetTypes());
System.out.println("Retrieved " + types.length + " types:");
for (int i = 0; i < types.length; i++) {
  System.out.println(" '" + types[i].getName() + "' with " +
    types[i].getCount() + " books");
}
        
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
  stub.addBook("scifi", isbn, new String[] { "Cook, Glen" }, title);
  System.out.println("Added '" + title + '\'');
  title = "This Should Not Work";
  stub.addBook("xml", isbn, new String[] { "Nobody, Ima" }, title);
  System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFaultException e) {
  System.out.println("Failed adding '" + title +
    "' with ISBN '" + isbn + "' - matches existing title '" +
    e.getFaultMessage().getBook().getTitle() + '\'');
}
        
// create a callback instance
BooksByTypeCallback cb = new BooksByTypeCallback();
        
// retrieve all books of a type asynchronously
stub.startgetBooksByType("scifi", cb);
long start = System.currentTimeMillis();
synchronized (cb) {
  while (!cb.m_done) {
    try {
      cb.wait(100L);
    } catch (Exception e) {}
  }
}
System.out.println("Asynchronous operation took " +
  (System.currentTimeMillis()-start) + " millis");
if (cb.m_books != null) {
  BookInformation[] books = cb.m_books;
  ...

Axis2 の 1.3 への更新

この記事が掲載される時点で、Axis2 の 1.3 リリースは完成間近となります。このセクションで説明した ADB アンラップの問題はすでに解決されているので、Axis2 1.3 のコードに取り掛かれるのであれば、以前のバージョンの Axis2 に比べ、一段と快適に ADB を使用できるはずです。1.3 専用のサンプル・コードは、「ダウンロード」セクションに記載されています。

ADB アンラップのサポートで主要な問題となっているのは、これがまだ完全に安定していないことです。リスト 6 のコードは Axis2 1.2 で使用するには適していて、Axis2 1.1.1 の ADB アンラップに使用されていたコードより大幅に改善されています。しかし、WSDL2Java ツールでは、他のサンプルで使用した WSDL 文書をこのサンプル用に再構成し、データ・クラスのインライン・スキーマを個別のスキーマ文書に移動させなければなりませんでした。さらに重大なことには、リスト 6 のコードは完全にはr動作しません。非同期操作を使用しているコードの最後の部分は、生成された ADB クライアント・スタブ・コードでのエラーにより、実行時にクラス・キャスト例外で停止します。

Axis2 1.2 の後継がリリースされるまでには、ADB アンラップの問題はほとんど解決されていることでしょう。その一方、アンラップをサポートする Axis2 のデータ・バインディング・フレームワークは ADB だけではありません。JiBX もアンラップをサポートします。そして JiBX の場合のサポートは Axis2 1.1.1 の時点から安定しています。JiBX のクライアント・コードについては、もう 1 つの主要な Axis2 のデータ・バインディング手法を検討した後で記載します。

XMLBeans

XMLBeans は データ・バインディング層を組み込んだ一般的な XML 処理フレームワークです。このフレームワークは BEA Systems プロジェクトに源を発し、その後 Apache Foundation に寄贈されました。Axis2 が最初にサポートしたデータ・バインディング形式である XMLBeans は、今でも Axis2 では人気があり、特に複雑なスキーマ定義にはよく使われています。

リスト 7 は XMLBeans の場合のサンプル・アプリケーションのクライアント・コードで注目に値する部分です。 基本的な (アンラップされていない) ADB コードと同じく、各操作の入力と出力には個々のクラスがありますが、XMLBeans が ADB とは異なる点は、入力または出力クラスが含まれる文書に対してクラスが追加されていることです (例えば、GetBook の他に GetBookDocument が追加されています)。

このように、ADB に代わって XMLBeans を使用すると、最終的にはオブジェクトを作成する層が追加されることになります。Axis2 にはXMLBeans に対するアンラップ・サポートがないため、このオブジェクト作成層の追加を回避する手立てはありません。その結果、XMLBeans が生成したクラスは他のデータ・バインディング・フレームワークで生成したクラスよりも幾分使いにくくなります。

リスト 7. XMLBeans のクライアント・コード
// create the client stub
XmlbeansLibraryStub stub = new XmlbeansLibraryStub(target);
        
// retrieve a book directly
String isbn = "0061020052";
GetBookDocument gbd = GetBookDocument.Factory.newInstance();
GetBookDocument.GetBook gb = gbd.addNewGetBook();
gb.setIsbn(isbn);
gbd.setGetBook(gb);
GetBookResponseDocument gbrd = stub.getBook(gbd);
BookInformation book = gbrd.getGetBookResponse().getGetBookReturn();
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
        
// retrieve the list of types defined
GetTypesDocument gtd = GetTypesDocument.Factory.newInstance();
gtd.addNewGetTypes();
GetTypesResponseDocument gtrd = stub.getTypes(gtd);
TypeInformation[] types =
  gtrd.getGetTypesResponse().getGetTypesReturnArray();
System.out.println("Retrieved " + types.length + " types:");
for (int i = 0; i < types.length; i++) {
  System.out.println(" '" + types[i].getName() + "' with " +
    types[i].getCount() + " books");
}
        
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
  AddBookDocument abd = AddBookDocument.Factory.newInstance();
  AddBookDocument.AddBook ab = abd.addNewAddBook();
  ab.setAuthorArray(new String[] { "Cook, Glen" });
  ab.setIsbn(isbn);
  ab.setTitle(title);
  ab.setType("scifi");
  stub.addBook(abd);
  System.out.println("Added '" + title + '\'');
  title = "This Should Not Work";
  ab.setTitle(title);
  stub.addBook(abd);
  System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFaultException e) {
  System.out.println("Failed adding '" + title +
    "' with ISBN '" + isbn + "' - matches existing title '" +
    e.getFaultMessage().getAddDuplicate().getBook().getTitle() +
    '\'');
}
        
// create a callback instance
BooksByTypeCallback cb = new BooksByTypeCallback();
        
// retrieve all books of a type asynchronously
GetBooksByTypeDocument gbtd =
  GetBooksByTypeDocument.Factory.newInstance();
gbtd.addNewGetBooksByType().setType("scifi");
stub.startgetBooksByType(gbtd, cb);
long start = System.currentTimeMillis();
synchronized (cb) {
  while (!cb.m_done) {
    try {
      cb.wait(100L);
    } catch (Exception e) {}
  }
}
System.out.println("Asynchronous operation took " +
  (System.currentTimeMillis()-start) + " millis");
if (cb.m_response != null) {
  BookInformation[] books =
    cb.m_response.getGetBooksByTypeResponse().getGetBooksByTypeReturnArray();
  ...

Axis2 の 1.3 への更新

この記事が掲載される時点で、Axis2 の 1.3 リリースは完成間近となります。XMLBeans の障害処理に関する問題は、Axis2 1.3 で修正されました。1.3 専用のサンプル・コードは、「ダウンロード」セクションに記載されています。

リスト 7 のクライアント・コード、そして対応するサーバー・コードは Axis2 1.1.1 では適切に実行されますが、リリース 1.2 では、XMLBeans に対する障害処理のコード生成に問題があるため、重複した本の ID を追加したときに期待される例外が発生しません。この問題は、次の Axis2 リリースでは修正されるはずです。

XMLBeans は XML Schema を 100% サポートすると主張していますが、この主張が正確かどうかは解釈の仕方によります。XMLBeans がほとんどすべてのスキーマ構成に対して、スキーマと一致する文書の読み取りと書き込みに使える一連の Java クラスを生成するという点では、主張は合っています。けれどもこの記事で取り上げるその他のデータ・バインディングとは異なり、XMLBeans はデフォルトではスキーマを強制する動作ををほとんど何も行いません。例えば、リスト 7 のコードで追加対象の本のタイトルを設定する行をコメント・アウトしたとしても、XMLBeans は必須の <title> 要素が欠けた無効な文書の読み取り/書き込みを行います。リスト 8 に、このようなコード変更と、追加要求のためにサーバーに送信された XML、そして本の取得時にサーバーから返された XML を示します。本の取得の応答には、XML文書には <title> 要素が含まれていますが、スキーマでは許可されていない無効な xsi:nil="true" 属性が使用されています。

リスト 8. XMLBeans のクライアント・コードと無効な文書
      AddBookDocument abd = AddBookDocument.Factory.newInstance();
      AddBookDocument.AddBook ab = abd.addNewAddBook();
      ab.addAuthor("Cook, Glen");
      ab.setIsbn(isbn);
      ab.setType("scifi");
//            ab.setTitle(title);
      System.out.println("Validate returned " + abd.validate());
      stub.addBook(abd);
      ...

  <addBook xmlns="http://ws.sosnoski.com/library/wsdl">
   <type>scifi</type>
   <isbn>0445203498</isbn>
   <author>Cook, Glen</author>
  </addBook>
    
  <getBooksByTypeResponse xmlns="http://ws.sosnoski.com/library/wsdl">
   ...
   <getBooksByTypeReturn isbn="0445203498" type="scifi">
    <author xmlns="http://ws.sosnoski.com/library/types">Cook, Glen</author>
    <title xmlns="http://ws.sosnoski.com/library/types" xmlns:xsi="http://www.w3.org/2001
      /XMLSchema-instance" xsi:nil="true"/>
   </getBooksByTypeReturn>
  </getBooksByTypeResponse>

これは、必須の値を設定しないという単純な例です。これより複雑なスキーマでは、XMLBeans 生成の API にさらに多くの落とし穴が隠される可能性があります。XMLBeans ユーザー E メール・リストでの最近の論議では、正しい出力を生成するために、値を 2 つの異なるリストに交互に追加しなければならない場合について取り上げていました。このように、XMLBeans では開発者がスキーマについてと、生成されたコードをスキーマに関連付ける方法についての両方を理解し、アプリケーション・コードによって有効な XML 文書が構成されることを確実にしなければなりません。このようなスキーマの詳細を隠すことがデータ・バインディング・フレームワークの大きな利点なのですが、XMLBeans はこの点では期待に応えていないことは確かです。

XMLBeans での無効な XML 文書の処理または生成の問題は、生成されたクラスに含まれる validate() メソッドを呼び出すことで回避できます。XMLBeans を操作しているのであれば、テストおよび開発中に少なくともこのチェックをすべての文書に対して行う必要があります。ただし、検証はパフォーマンスにかなりの影響を与えるため (連載の次回の記事で説明しますが、すべての文書で validate() を呼び出さないとしても、XMLBeans の動作は元から遅いです)、大抵のアプリケーションは実動デプロイメントでの検証のオーバーヘッドを避けるべきです。また、検証の結果情報も限られています。検証失敗の原因を切り分けるには、エラーが発生した文書で通常のスキーマ検証を実行しなければなりません。

JiBX

JiBX (これも私が開発しました) は、スキーマからのコード生成ではなく、既存の Java クラスの操作に主に焦点を絞ったデータ・バインディング・フレームワークです。JiBX では、Java オブジェクトと XML の間での変換方法を定義するバンディング定義を作成してからそのバインディングをコンパイルしますが、このコンパイルの際に使うツールによって、変換を実装するメソッドが (バイトコードとして) 追加され、データ・クラス・ファイルが拡張されます。JiBX ランタイム・フレームワークは追加されたこれらのメソッドを使用して、データと XML 間の変換を行います。

JiBX の手法には独特の利点と欠点があります。プラス面としては、Web サービス・インターフェースを既存のサービス・コードに追加する場合、JiBX では既存のクラスを直接操作することができます。この直接操作にとりわけ重宝するのが Jibx2Wsdl ツールで、このツールは、既存のコードを Axis2 サービスとして簡単にデプロイするために必要なすべてのものを生成してくれます。また、同じクラスに対して複数のバインディングを定義し、異なる XML バージョンの文書を単一のデータ・モデルを使って同時に操作することができます。バインディングを修正すれば、データ・クラスがリファクタリングされる際に同じ XML 表現を維持することも通常は可能です。

リスト 9 は、JiBX のクライアント・コードで注目に値する部分です。このコードはメッセージ要素と一致するクラスを使用しています。コードの内容はリスト 5 に示した ADB のクライアント・コードと同様なので、詳細を繰り返すことはしませんが、リスト 5 との唯一の違いとして、ユーザーがデータ・クラスとメッセージ・クラスの両方を制御できるため、(AddBookRequest の場合と同じく) 便利なコンストラクターや他のサポート・メソッドを JiBX で使用しているクラスに簡単に追加できるという点に注目してください。

リスト 9. JiBX のクライアント・コード
// create the server instance
JibxLibraryStub stub = new JibxLibraryStub(target);
        
// retrieve a book directly
String isbn = "0061020052";
GetBookResponse bresp = stub.getBook(new GetBookRequest(isbn));
Book book = bresp.getBook();
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
isbn = "9999999999";
bresp = stub.getBook(new GetBookRequest(isbn));
book = bresp.getBook();
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
        
// retrieve the list of types defined
GetTypesResponse tresp = stub.getTypes(new GetTypesRequest());
Type[] types = tresp.getTypes();
System.out.println("Retrieved " + types.length + " types:");
for (int i = 0; i < types.length; i++) {
  System.out.println(" '" + types[i].getName() + "' with " +
    types[i].getCount() + " books");
}
        
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
  AddBookRequest abr = new AddBookRequest("scifi", isbn, title,
    new String[] { "Cook, Glen" });
  stub.addBook(abr);
  System.out.println("Added '" + title + '\'');
  title = "This Should Not Work";
  abr = new AddBookRequest("scifi", isbn, title,
    new String[] { "Nobody, Ima" });
  System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFaultException e) {
  System.out.println("Failed adding '" + title +
  "' with ISBN '" + isbn + "' - matches existing title '" +
  e.getFaultMessage().getBook().getTitle() + '\'');
}
        
// create a callback instance
BooksByTypeCallback cb = new BooksByTypeCallback();
        
// retrieve all books of a type asynchronously
stub.startgetBooksByType(new GetBooksByTypeRequest("scifi"), cb);
long start = System.currentTimeMillis();
synchronized (cb) {
  while (!cb.m_done) {
    try {
      cb.wait(100);
    } catch (Exception e) {}
  }
}
System.out.println("Asynchronous operation took " +
  (System.currentTimeMillis()-start) + " millis");
if (cb.m_response != null) {
  Book[] books = cb.m_response.getBooks();

リスト 10 に、上記に相当する JiBX アンラップ・コードを示します。ADB アンラップ・コードの場合と同じく、そのままのコードよりも、アンラップした形のサービス呼び出しのほうが、理解するにも操作するにも遥かに簡単です。JiBX バージョンと ADB バージョンとの大きな違いは 1 つだけで、それは渡される値がない場合、JiBX ではオブジェクトを作成する必要がないという点です。一方 ADB では getTypes() 呼び出しにオブジェクトを作成する必要がありました。また、JiBX では Axis2 の 1.1.1 リリースから完全にアンラップをサポートしているため、ADB よりも JiBX のアンラップ・サポートのほうが安定しています。

リスト 10. JiBX のアンラップされたクライアント・コード
// create the server instance
JibxUnwrapLibraryStub stub = new JibxUnwrapLibraryStub(target);
        
// retrieve a book directly
String isbn = "0061020052";
Book book = stub.getBook(isbn);
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
        
// retrieve the list of types defined
Type[] types = stub.getTypes();
System.out.println("Retrieved " + types.length + " types:");
for (int i = 0; i < types.length; i++) {
  System.out.println(" '" + types[i].getName() + "' with " +
    types[i].getCount() + " books");
}
        
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
  stub.addBook("scifi", isbn, new String[] { "Cook, Glen" }, title);
  System.out.println("Added '" + title + '\'');
  title = "This Should Not Work";
  stub.addBook("xml", isbn, new String[] { "Nobody, Ima" }, title);
  System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFaultException e) {
  System.out.println("Failed adding '" + title +
    "' with ISBN '" + isbn + "' - matches existing title '" +
    e.getFaultMessage().getBook().getTitle() + '\'');
}
        
// create a callback instance
BooksByTypeCallback cb = new BooksByTypeCallback();
        
// retrieve all books of a type asynchronously
stub.startgetBooksByType("scifi", cb);
long start = System.currentTimeMillis();
synchronized (cb) {
  while (!cb.m_done) {
    try {
      cb.wait(100L);
    } catch (Exception e) {}
  }
}
System.out.println("Asynchronous operation took " +
  (System.currentTimeMillis()-start) + " millis");
if (cb.m_books != null) {
  Book[] books = cb.m_books;

JiBX アンラップ・サポートは、使用するクラスに関しても ADB とは異なります。ADB アンラップでは、舞台裏で引き続きすべてのメッセージ要素に対してクラスが生成および使用されます。JiBX ではリスト 9 に示すように、直接操作を使用する場合にはメッセージ要素に対応したクラスを定義する必要がありますが、アンラップを処理する場合には、値として渡されたクラスのみを定義し、バインディング定義に組み込めばいいというわけです。いずれの場合も、JiBX バインディング定義は Axis2 WSDL2Java ツールを実行する前に作成し、-Ebindingfile コマンド・ライン・パラメーターを使用して渡す必要があります。

JiBX バインディング手法の最大の欠点はおそらく (少なくとも Web サービスの観点から見た場合)、現在の JiBX では XML スキーマ定義からの作業に対するサポートが弱いところです。このスキーマからの作業に対する不十分なサポート (Xsd2Jibx ツールの形) でさえも、Axis2 WSDL2Java コード生成には統合されません。これはつまり、Java データ・クラスとバインディング定義を作成させてからでないと、WSDL2Java を実行して Axis2 リンケージ・コードを生成できないということです。JiBX が必要とするバイトコードの拡張ステップも環境によっては問題となります。一般的には、このステップはアプリケーションのビルド時に行わなければならないため、クラス内にソース・コードがまったくないという結果になってしまうからです。

JiBX データ・バインディングには、このセクションの冒頭で説明したように独特の利点がありますが、Axis2 の使用に関しても、JiBX は他のフレームワークに勝る利点があります。それは、JiBX がバグ修正リリースでサポートされているという点です。このバグ修正リリースを Axis2 に追加すれば、リリース後に見つかった問題を修正できます (詳細は「参考文献」セクションの「製品や技術を入手するために」を参照)。他の 2 つのフレームワークでは、バグ修正を入手するには夜通し Axis2 をビルドするしかありませんが、それによって別の問題が引き起こされることも珍しくありません。将来、JiBX はスキーマからの確実なコードおよびバインディング生成をサポートすると期待されています。このサポートが実現すれば、JiBX が Axis2 に代わる万能の優れたデータ・バインディング手法になるはずです。それまでは、既存の Java コードから作業する場合には、JiBX を使うことで最大のメリットがもたらされ、Jibx2Wsdl が優れたサポートを提供することになります。


まとめ

Axis2 は現在、以下の 3 つのデータ・バインディング・フレームワークを完全にサポートしています。

  • ADB は Axis2 専用に設計されているため、Axis2 環境でしか使用できません。Axis2 は 1.3 リリースの時点でスキーマからのコード生成に対して優れた (そして拡張中の) サポートを提供しています。また、便利なアンラップ・サービス・メソッドと自動添付処理もサポートするため、既存の WSDL サービス定義から作業する場合には最適です。
  • XMLBeans は、生成された Java コードでのスキーマ構造のモデル化に対し、最も完全に近いサポートがされます。ただし、特定のスキーマには極めて複雑な Java モデルを作成し、またインターフェースを単純化するためのアンラップ・サービス・メソッドはサポートしません。デフォルトでは、入力 XML 文書または出力 XML 文書に基本スキーマ構造でさえも強制しないため、無効な XML 文書を誤って作成したり、あるいは無効な XML 文書を受け取って処理してしまいがちです。
  • JiBX は、既存の Java クラスの操作をサポートする唯一の手段です。さらに、アンラップ・サービス・メソッドに対する優れたサポートも提供します。ただし、Axis2 に統合された JiBX サポートは、スキーマからのコード生成に対応せず、JiBX ツールが提供しているスキーマ・サポートによる個別のコード生成に対してでさえも、スキーマのサポートは限定されています。

このように、すべてのニーズに単独で対応できるデータ・バインディング・フレームワークはないため、Axis2 がこれらの選択肢を用意しているのは素晴らしいことです。将来的には、Axis2 は JAXB 2.0 を完全にサポートするようになるでしょう。JAXB 2.0については、この連載の一環として、今後の JAX-WS 2.0関連の記事で取り上げる予定です。それぞれのフレームワークが持つ利点と欠点を知ることが、自分のニーズにあった最適なフレームワークを選択するのに役立ち、そして稼動してから問題が発覚する前に考えられる問題領域に注意を向けられるようになります。次回の記事では Axis2 のデータ・バインディング・フレームワークをパフォーマンスという別の視点から比較する予定です。


ダウンロード

内容ファイル名サイズ
Sample code (Axis2 1.2 and earlier)code.zip82KB
Sample code (Axis2 1.3)code1_3.zip87KB

参考文献

学ぶために

製品や技術を入手するために

  • Axis2 に関する詳細情報を入手するとともに、Axis2 をダウンロードして自分自身で試してみてください。ダウンロードには ADB と XMLBeans データ・バインディングの完全サポートが含まれています。
  • 既存のコードを Axis2 やその他の XML アプリケーション用に適応できる JiBX データ・バインディング・フレームワークをダウンロードしてください。
  • IBM ソフトウェアの試用版を使用して、次の開発プロジェクトを革新してください。ダウンロード、あるいは DVD で入手できます。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and web services, Java technology, Open source
ArticleID=253245
ArticleTitle=Java Web サービス: 第 3 回 Axis2 でのデータ・バインディング
publish-date=07262007