WebServices - Axis

Axis 開発者ガイド

バージョン 1.2
フィードバック: axis-dev@ws.apache.org

目次

はじめに

このガイドは Axis のコード開発に関連するトピックのコレクションです。

一般的なガイドライン

開発環境

Axis の開発には以下のパッケージが必要です。

  • ant [英語] - Java に基づくビルドツール。注意: バージョン 1.5 以上が必要です。
  • junit [英語] - テストパッケージ
  • xerces [英語] - XML プロセッサ
  • Java 1.3.1 JDK (あるいはそれ以降) をインストールしてください。

Axis jar ファイルは xml-axis/java/build/lib にビルドされています。以下に私がコードを開発する時に使用している CLASSPATH の例を示します。

G:\xerces\xerces-1_4_2\xerces.jar
G:\junit3.7\junit.jar
G:\xml-axis\java\build\lib\commons-discovery.jar
G:\xml-axis\java\build\lib\commons-logging.jar
G:\xml-axis\java\build\lib\wsdl4j.jar
G:\xml-axis\java\build\lib\axis.jar
G:\xml-axis\java\build\lib\log4j-1.2.8.jar
G:\xml-axis\java\build\classes

もし proxy サーバ経由でインターネットにアクセスしているのであれば、Axis テストが同じ動作を行うために環境変数を設定する必要があります。例えば ANT_OPTS を以下のように設定します。

-Dhttp.proxyHost=proxy.somewhere.com
-Dhttp.proxyPort=80
-Dhttp.nonProxyHosts="localhost"

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

Axis アーキテクチャガイドはプラグイン可能なコンポーネントの要件について説明しています。

発見

Axis 特有のコンポーネントは以下の形式で作成する必要があります。

org.apache.axis.components.<コンポーネント型>.<ファクトリクラス名>

例えば、org.apache.axis.components.logger.LogFactory はロガーコンポーネント/サービスのためのファクトリ、あるいは発見機構です。

The org.apache.axis.components.image パッケージは、ファクトリと、Axis によって利用される様々な画像ツールの補助クラス、の両方の実例を示しています。これは、外部ツールを利用したプラグイン可能なコンポーネントの見本で、Axis の最小限の要件を満たすために、Axis を包んでいる、制限されたインターフェースのみを提供する '薄い' ラッパの背後に隔離されています。これにより今後の設計者や実装者は、これらのツールに対する Axis 特有の要件の明確な理解を得ることができます。

ロギング/トレーシング

Axis のロギングとトレーシングは Jakarta Commons [英語] プロジェクトの Logging コンポーネント、つまり Jakarta Commons Logging (JCL) SPI に基づいています。JCL は、Log4J [英語]、Avalon LogKit [英語]、JDK 1.4 を含むその他のロギングツール用に、薄いラッパと共に Log インターフェースを提供しています。このインターフェースは Log4J と LogKit に密接にマッピングします。

ロガー SPI の利用

Java クラスから JCL SPI を利用するには、以下の重要な文を入れてください。

import org.apache.commons.logging.Log;
import org.apache.axis.components.logger.LogFactory;

それぞれのクラス定義で、以下のように log 属性を宣言し、初期化してください。

public class CLASS {
  private static Log log =
    LogFactory.getLog(CLASS.class);
    ...

優先順位に対応するメソッドを呼ぶことにより、メッセージは log のような ロガーにログ出力されます。Log インターフェースは、ログ/トレースメッセージをログに書き込むのに利用する以下のメソッドを定義しています。

log.fatal(Object message);
log.fatal(Object message, Throwable t);
log.error(Object message);
log.error(Object message, Throwable t);
log.warn(Object message);
log.warn(Object message, Throwable t);
log.info(Object message);
log.info(Object message, Throwable t);
log.debug(Object message);
log.debug(Object message, Throwable t);
log.trace(Object message);
log.trace(Object message, Throwable t);

これらのメソッドのセマンティックスは最終的には Log インターフェースの実装によって定義されていますが、メッセージの重要度は上記リストに示されたような順序にすることが求められています。

ロギングメソッドに加え、以下のメソッドも提供されています。

log.isFatalEnabled();
log.isErrorEnabled();
log.isWarnEnabled();
log.isInfoEnabled();
log.isDebugEnabled();
log.isTraceEnabled();

これらは一般的に、ロギングのサポートがある時のみ実行する必要のあるコードと、一般的な場合 (ロギングが無効時) に望ましくない実行時オーバヘッドを導入するコードとを守るために利用されます。

ガイドライン
メッセージプロパティ

ログメッセージが内容と重要度の点で適切であるか確かめることは重要です。以下のガイドラインが提案されています。

  • fatal - Axis サーバをすぐに停止させてしまう原因となる深刻なエラー。これらはすぐにコンソールに表示され、また国際化されるべきです。
  • error - その他の実行時エラーと予期せぬ状態です。これらはすぐにコンソールに表示され、また国際化されるべきです。
  • warn - 推奨されない API の利用、API の下手な利用、ほとんどエラー、必ずしも "間違い" ではないけれど望ましくない、あるいは予期しないその他の実行時状況です。これらはすぐにコンソールに表示され、また国際化されるべきです。
  • info - 興味深い実行時イベント (起動/停止) です。これらはすぐにコンソールに表示されるべきなので、控えめで最小限にするべきです。これらは国際化されるべきです。
  • debug - システムを通した流れの詳細情報です。これらはログのみに書かれるべきです。これらは国際化される必要はありませんが、されても特に問題はありません。
  • trace - より詳細な情報です。これらはログのみに書かれるべきです。これらは国際化される必要はありませんが、されても特に問題はありません。
ロガーの設定

Jakarta Commons Logging (JCL) SPI は設定により異なるロギングツールキットを利用することができます。JCL が利用するロガーを設定する方法は Axis システム統合ガイドをご覧下さい。

JCL の動作設定は、最終的には利用しているロギングツールキットに依存します。JCL SPI (と、それ故に Axis) は (CLASSPATH の中で) 利用可能であれば、デフォルトで Log4J [英語] を利用します。

Log4J

Log4J [英語] は Axis の推奨する/デフォルトのロガーなので、開発者の開発のためにここでいくつかの詳細を紹介します。

システムプロパティとプロパティファイルの両方、あるいは片方を利用して Log4J を設定します。

  • log4j.configuration=log4j.properties

    このシステムプロパティを利用して Log4J 設定ファイルの名前を指定します。指定しない場合、デフォルトの設定ファイルは log4j.properties になります。log4j.properties ファイルは axis.jar で提供されています。

    このプロパティファイルは、同じ名前のファイルを CLASSPATH 中の axis.jar よりも前に置くことで上書きすることができるかもしれません。しかし正確な動作は実行時に利用されるクラスローダに依存するので、このテクニックはお勧めしません。

    プロパティファイルを上書きする安全な方法は、axis.jar の中にあるプロパティファイルを置き換えることです。しかしこれは、特にデバッグ時に設定を調整して不必要なログエントリーをフィルタリングしたい時に、それほど使い勝手は良くありません。より使い勝手のいい別の方法として、プロパティファイルを指定するのに絶対ファイルパスを利用する方法があります。これはWebアプリケーションのプロパティファイルとそれらのクラスローダさえも無視します。Linux での例を挙げると、システムプロパティを以下のように指定することができます。

    log4j.configuration=file:/home/fred/log4j.props

  • log4j.debug

    log4j がどこから設定を取ってくるのかを指定する良い方法は、このシステムプロパティを設定し、標準出力のメッセージを見ることです。

  • log4j.rootCategory=priority [, appender]*

    デフォルト (ルート) の ロガー優先度を設定します。

  • log4j.logger.logger.name=priority

    log4j.logger.logger.name=priority ここで指定したロガーと、階層的にそのロガーより下位の全てのロガーの優先度を設定します。logger.name は、ロガーインスタンスを作成するのに利用される LogFactory.getLog(logger.name) のパラメータに対応します。優先度は以下の通りです。DEBUGINFOWARNERRORFATAL

    Log4J は階層的な名前を理解し、パッケージや高レベル修飾子によって制御することができます。log4j.logger.org.apache.axis.encoding=DEBUGorg.apache.axis.encodingorg.apache.axis.encoding.ser の両方の中にある全てのクラスのデバッグメッセージを有効にします。同様に、log4j.logger.org.apache.axis=DEBUG を設定することによりその他の Jakarta プロジェクト以外の全ての Axis クラスのデバッグメッセージを有効にします。

    設定を組み合わせることで、興味のあるログイベントを取り出し、それ以外を省略することができます。例えば、以下の組み合わせは、

    log4j.logger.org.apache.axis=DEBUG 
    log4j.logger.org.apache.axis.encoding=INFO
    log4j.logger.org.apache.axis.utils=INFO
    log4j.logger.org.apache.axis.message=INFO

    1回のリクエストで生成されるログエントリの数を管理可能な数に減らします。

  • log4j.appender.appender.Threshold=priority

    Log4J appender はコンソール、ファイル、ソケットなどの異なる出力デバイスに対応します。もしアペンダの threshold がメッセージ優先度以下であれば、メッセージはそのアペンダによって出力されます。これにより異なるレベルの詳細を、異なるログ出力先に出力することができます。

    例えば、DEBUG (とそれ以上の) レベルの情報をログファイルで捕獲し、その一方でコンソール出力を INFO (とそれ以上) に制限する、といったことができます。

Axis サーブレットクエリー文字列プラグイン

org.apache.axis.transport.http.AxisServlet クラスから派生する全てのサーブレットは、多数の標準クエリー文字列 (?list?method?wsdl) をサポートします。これらの標準クエリー文字列はWebサービスから情報を提供したり、Webサービスの操作を実行したりします (例えば、?method はWebサービス上のメソッドを呼び出すのに利用され、?wsdl はWebサービスの WSDL ドキュメントを取得するのに利用されます)。Axis サーブレットはこれら3つのクエリー文字列だけに制限しているわけではなく、開発者は org.apache.axis.transport.http.QSHandler インターフェースを実装することで自分用の "プラグイン" を生成することができます。このインターフェースには実装しなければならない1つのメソッドがあり、このメソッドは以下のシグネチャを持ちます。

public void invoke (MessageContext msgContext) throws AxisFault;

org.apache.axis.MessageContext インスタンスは、その getProperty メソッドでアクセス可能な多数の有用なオブジェクト (Axis エンジンインスタンスや HTTP サーブレットオブジェクト等) を開発者に提供しています。以下の定数は、Axis サーブレットがクエリー文字列プラグインを呼び出すことによって提供される様々なオブジェクトを取得するのに利用することができます。

  • org.apache.axis.transport.http.HTTPConstants.PLUGIN_NAME

    クエリー文字列プラグインの名前を持っている String です。例えば、もしクエリー文字列 ?wsdl が与えられると、そのプラグインの名前は wsdl になります。

  • org.apache.axis.transport.http.HTTPConstants.PLUGIN_SERVICE_NAME

    クエリー文字列プラグインを呼び出した Axis サーブレットの名前を持っている String です。

  • org.apache.axis.transport.http.HTTPConstants.PLUGIN_IS_DEVELOPMENT

    もしこの Axis のバージョンが開発モードであるならば true、そうでなければ false を持つ Boolean です。

  • org.apache.axis.transport.http.HTTPConstants.PLUGIN_ENABLE_LIST

    もし Axis サーバ設定のリストアップが許されていたら true、そうでなければ false を持つ Boolean です。

  • org.apache.axis.transport.http.HTTPConstants.PLUGIN_ENGINE

    Axis サーバのエンジンを持っている org.apache.axis.server.AxisServer オブジェクトです。

  • org.apache.axis.transport.http.HTTPConstants.MC_HTTP_SERVLETREQUEST

    クエリー文字列プラグインを呼び出した Axis サーブレットからの javax.servlet.http.HttpServletRequest オブジェクトです。

  • org.apache.axis.transport.http.HTTPConstants.MC_HTTP_SERVLETRESPONSE

    クエリー文字列プラグインを呼び出した Axis サーブレットからの javax.servlet.http.HttpServletResponse オブジェクトです。

  • org.apache.axis.transport.http.HTTPConstants.PLUGIN_WRITER

    クエリー文字列プラグインを呼び出した Axis サーブレットからの java.io.PrintWriter オブジェクトです。

  • org.apache.axis.transport.http.HTTPConstants.PLUGIN_LOG

    クエリー文字列プラグインを呼び出した Axis サーブレットからの org.apache.commons.logging.Log オブジェクトで、ログメッセージに利用されます。

  • org.apache.axis.transport.http.HTTPConstants.PLUGIN_EXCEPTION_LOG

    クエリー文字列プラグインを呼び出した Axis サーブレットからの org.apache.commons.logging.Log オブジェクトで、ログ例外に利用されます

出力の同じ基本情報とメソッドを開発者が利用できるという点において、クエリー文字列プラグイン開発は普通のサーブレット開発に大変似ています。以下は、システム時計の値を単に表示するクエリー文字列プラグインの例です (簡潔にするために、import 文は省略しています)。

public class QSClockHandler implements QSHandler {
  public void invoke (MessageContext msgContext) throws AxisFault {
    PrintWriter out = (PrintWriter) msgContext.getProperty (HTTPConstants.PLUGIN_WRITER);
    HttpServletResponse response = (HttpServletResponse)
        msgContext.getProperty (HTTPConstants.MC_HTTP_SERVLETRESPONSE);

    response.setContentType ("text/html");

    out.println ("<HTML><BODY><H1>" + System.currentTimeMillis()
        + "</H1></BODY></HTML>");
  }
}

クエリー文字列プラグインクラスを作成したら、Axis サーバを、それを呼び出すクエリー文字列を認識するように設定する必要があります。Axis サーバ設定ファイルの HTTP トランスポート節の設定方法に関する情報は Axis リファレンスガイド内のデプロイ (WSDD) リファレンス節をご覧下さい。

設定プロパティ

Axis は内部設定の主要ポイントとしてシステムプロパティを利用することをやめる過程にいます。System.getProperty() を呼ぶことを避け、替わりに AxisProperties.getProperty を呼んでください。AxisProperties.getPropertySystem.getProperty を呼び、(最終的には) その他の設定情報源に問い合わせを行います。

このアクセスの中央ポイントを利用することにより、グローバル設定システムを、単一の JVM 上で複数の Axis エンジンをよりサポートするように再設計することができます。

例外処理

Axis 例外処理に関するガイドラインは例外処理のベストプラクティスに基づいています。これらのガイドラインには Axis 特有の詳細がありますが、基本的には全てのプロジェクトに適用できます。これらのガイドラインは2つの理由でここに含まれました。1つめは、Apache/Jakarta ガイドラインのどこにも書かれていなかった (あるいは見つからなかった) からです。2つめは、これらのガイドに順守することはエンタープライズ用のミドルウェアにとって重大であると考えられるからです。

これらのガイドラインは基本的にプログラミング言語から独立しています。これらは経験に基づいていますが、何年も前に無邪気 (?) な人を開眼させたという適切な功績は Scott Meyers による More Effective C++ に与えられるべきです。

最後に、これらはガイドラインです。これらのガイドラインには必ず例外があります。その場合、その例外が "コードの中にコメントがある" という形式でログが取られている状態で (これらのガイドラインごとに) 質問してください。

  • 主要規則: どのように扱うか知っている例外のみキャッチする

    コードが例外をキャッチする場合、プログラムのその時点でそれをどのように扱うかコードが知っている必要があります。この規則の例外は、納得させる理由と共にドキュメント化されていなければなりません。コードレビューアは、彼らのハゲワシのくちばしをのせ、突き続けるでしょう...

    この規則にはいくつかの必然的な結果があります。

    • 内部コード内特有の例外の扱い

      内部コードとはプログラム内の深いコードです。そのようなコードは、例外が解決でき、通常フローがコードに回復できる時かつその時に限り、特定の例外や、例外の部類 (例外階層内の親) をキャッチするべきです。この種の動作は非対話型コードと対話型ツール間で極めて異なることに注意してください。

    • 一番外側の制御フロー内の全ての例外をキャッチする

      最終的には、全ての例外は1つのレベルかもう1つのレベルで扱われなければなりません。コマンドラインツールでは、これは main メソッドかプログラムを意味します。ミドルウェアコンポーネントでは、コンポーネントのエントリーポイントを意味します。Axis では、AxisServlet か、それと同等のものを意味します。

      内部的に解決できる特定の例外をキャッチした後、一番外側のコードは内部的に生成された例外が全てキャッチされ、処理されたことを確かめなければなりません。一般的にはそこまでできないので、最低限コードが例外のログを取るようにするべきです。ロギングに加え、Axis サーバはそのような例外全てを AxisFault に包み込み、それをクライアントコードに返します。

      これは主要規則と正反対のように思えますが、事実私達は、Axis はこの種の例外についてどうすればいいか知っている、ということを主張しています。つまり潔く終了することです。

  • 例外のキャッチとロギング

    例外がコンポーネント境界 (クライアント/サーバ、あるいはシステム/ビジネスロジック) をまたがる際、例外は、スローするコンポーネントによってキャッチされてログ出力されなければなりません。それが終われば下記のように再スローする、あるいはラップすることができます。

    疑わしい時は例外のログを取る

    • キャッチとスロー

      例外がキャッチされ再スローされる (未解決) 場合の例外のロギングは、コードを書く人とレビューアの判断次第です。コメントのログを取るのであれば、例外のログも取るべきです。

      疑わしい時は、例外と、その例外の完全なコンテキストを識別するのに役立つ関連するローカル情報のログを取りましょう。

      例外が未解決エラー、あるいは解決不可能なエラーであることがわかっているのであれば、その例外を error (log.error()) としてログを取り、そうでなければ 情報 レベル (log.info()) としてログを取ります。

    • キャッチとラップ

      もし例外 e がキャッチされ、新しい例外 w によってラップされたら、w をスローする前に例外 e のログを取りましょう。

      例外が未解決エラー、あるいは解決不可能なエラーであることがわかっているのであれば、その例外を error (log.error()) としてログを取り、そうでなければ 情報 レベル (log.info()) としてログを取ります。

    • キャッチと解決

      例外 e がキャッチされ解決される場合の例外のロギングは、コードを書く人とレビューアの判断次第です。もし何かしらのコメントのログを取るのであれば、その例外のログも取る (log.info()) べきです。バランスを取る必要がある課題は、パフォーマンスと問題解決可能性です。

      多くの場合、例外を無視するのが適切であるかもしれない、ということに注意してください。

  • コンポーネント境界の留意

    このガイドラインには複数の側面があります。一方では、ビジネスロジックはシステムロジックから分離していなければならないことを意味しています。他方では、特にサーバが外部団体に公開されている時に、クライアントへのサーバの実装の詳細の公開/可視性を制限すべきであることを意味しています。これは良くデザインされたサーバインターフェースを暗に意味しています。

    • システムロジックをビジネスロジックから分離

      Axis ランタイムによって生成された例外は可能であれば Axis ランタイム内で処理すべきです。最悪の場合、Axis ランタイムが例外の詳細なログを取り、一般的にはビジネスロジックに説明的な例外を投げます。

      ビジネスロジック (これはサーバと Axis ハンドラを含みます) に投げられた例外はクライアントコードに届けられなければなりません。

    • システムコードをユーザコードから保護する

      Axis ランタイムを制御されていないユーザビジネスロジックから保護します。Axis においてこれは、動的に設定可能な ハンドラプロバイダ、その他のユーザが制御可能なフックポイントは catch(Exception ...) によって保護されなければならないことを意味しています。ユーザコードによって生成された例外と、システムコードによってキャッチされた例外は

      • そのログを取り、
      • クライアントプログラムに届けなければなりません。
    • サーバの視認性をクライアントから分離

      特定の例外はサーバ側でログを取るべきで、より一般的な例外はクライアントにスローするべきです。これによりサーバの性質 (例えばハンドラ、プロバイダ等) の手掛かりを、クライアントコードにさらけ出すことを防ぎます。留意すべき Axis コンポーネント境界は以下の通りです。

      • クライアントコード <--> AxisClient
      • AxisClient <--> AxisServlet (AxisServer/AxisEngine)
      • AxisServer/AxisEngine <--> Webサービス
  • コンストラクタ内での例外のスロー

    コンストラクタに例外をスローする前に、そのオブジェクトが所有するリソースが全て後片付けされたか確かめてください。これは、リソースを保持しているオブジェクトに対して、コンストラクタ内で呼ばれたメソッドによってスローされた例外全てをキャッチし、後片付けし、例外を再スローすることを要求します。

コンパイルと実行

xml-axis/java/build.xml ファイルは、アプリケーションをビルドしてテストを実行するために ant が利用する主要な 'make' ファイルです。build.xml は ant のビルドの targetを定義しています。より詳細な情報については build.xml ファイルをお読みください。ここにいくつかの有用な target を挙げます。

  • compile -> ソースをコンパイルして xml-axis/java/build/lib/axis.jar を生成します。
  • javadocs -> xml-axis/java/build/javadocs に javadoc を生成します。
  • functional-tests -> コンパイルして関数テストを実行します。
  • all-tests -> コンパイルして全てのテストを実行します。

ソースコードをコンパイルするには次のように実行します。

cd xml-axis/java
ant compile

Tテストを実行するには次のように実行します。

cd xml-axis/java
ant functional-tests

注意: これらのテストはポート8080でサーバを起動します。もしこのポートが、あなたの (Tomcatのような) Webアプリケーションサーバが利用するポートと衝突する場合、どちらかのポートを変えるか、テストを実行するときにあたなのWebアプリケーションサーバを停止する必要があります。

新しいコードをチェックする前に、ant functional-tests と ant all-tests を実行してください。

国際化

ソースコードに変更を加えて、テキスト (エラーメッセージやデバッグ情報) を生成するようにした場合、そのテキストが確実に正しく変換されるために以下のガイドラインに従う必要があります。

開発者ガイドライン

  1. あなたのテキスト文字列は resource.properties ファイル (xml-axis/java/src/org/apache/axis/i18n/resource.properties) にプロパティとして追加されるべきです。いくつかのユーティリティアプリケーション (例: tcpmon) は独自のリソースプロパティファイル (tcpmon.properties) を持っています。
  2. resource.properties ファイルには変換と利用に関する説明が含まれています。メッセージリソースファイルのエントリーは <key>=<message> という形式です。ここにメッセージの例を示します。

    sample00=My name is {0}, and my title is {1}.

    1. sample00 は、コードがこのメッセージにアクセスするために利用するキーです。
    2. = の後のテキストはメッセージテキストです。
    3. {} 文法は挿入場所を定義しています。
  3. コードは static メソッド org.apache.axis.i18n.Messages.getMessage を利用してテキストを取得し、挿入を加える必要があります。ここに利用例を示します。

    Messages.getMessage("sample00", "Rich Scheuerle", "Software Developer");

  4. プロパティファイル内の全てのキーは <文字列><2桁の接尾辞> という文法を利用する必要があります。
    1. プロパティファイル内のメッセージテキストは決して変更しないで下さい。そのメッセージはコード内の複数の場所で利用されているかもしれません。追加の変換は新しいキーにのみ行います。
    2. コードの変更が、メッセージの変更を必要とする場合、インクリメントされた2桁接尾辞の新しいエントリーを作成します。
    3. 変換を簡単にするため、新しいエントリーは全てファイルの一番下に置くべきです。
    4. ときどき古いデータのプロパティファイルを削除したくなりますが、これはメジャーリリースの時のみ行われるべきです。

以下の文について考えてください。

if (operationName == null)
  throw new AxisFault( "No operation name specified" );

org/apache/axis/i18n/resource.properties にエントリーを追加します。

noOperation=No operation name specified.

そしてそれを読み込むようにコードを変更します。

if (operationName == null)
  throw new AxisFault(Messages.getMessage("noOperation"));

インターフェース

Axis はプロパティファイルとメッセージ文字列にアクセスするために標準 Java 国際化クラス java.util.ResourceBundle を利用し、変数を利用して文字列の書式を整えるために java.text.MessageFormat を利用します。Axis は ResourceBundle クラスと MessageFormat クラスの両方を管理する単一のクラス org.apache.axis.i18n.Messages を提供しています。Messages のメソッドは以下の通りです。

public static java.util.ResourceBundle getResourceBundle();

public static String getMessage(String key) throws java.util.MissingResourceException;

public static String getMessage(String key, String var) throws java.util.MissingResourceException;

public static String getMessage(String key, String var1, String var2) throws java.util.MissingResourceException;

public static String getMessage(String key, String[] vars) throws java.util.MissingResourceException;

Axis プログラマは Messages.getResourceBundle() 呼び出し経由で直接リソースバンドルを扱うことができますが、2つの理由から、代わりに getMessage() メソッドを利用すべきです。

  1. これはショートカットです。
    Messages.getMessage("myMsg00");
    を呼ぶよりも
    Messages.getResourceBundle().getString("myMsg00"); を呼ぶ方がきれいです。
  2. getMessage メソッドにより変数付きのメッセージが有効になります。
getMessage メソッド

変数のないメッセージであれば

myMsg00=This is a string.

単に次のように呼びます。

Messages.getMessage("myMsg00");

変数付きのメッセージであれば、X が0から始まる変数の名前を表す、文法 "{X}" を利用します。例えば、

myMsg00=My {0} is {1}.

であれば、次のように呼び、

Messages.getMessage("myMsg00","name", "Russell");

その結果の文字列は "My name is Russell." になります。

getMessage の String 配列バージョンも呼ぶことができます。

Messages.getMessage("myMsg00", new String[] {"name", "Russell"});

本当に必要なのは getMessage の String 配列バージョンだけですが、大部分のメッセージは0個か1個か2個の変数を持つので、String 配列バージョンの複雑さを避けるための利便性として、そのほかの getMessage メソッドが提供されています。

もしリソースが見つからなければ getMessage メソッドは MissingResourceException をスローすることに注意してください。また、引数より多くの {X} エントリーがあれば ParseException をスローします。これらの例外は RuntimeException の例外なので、呼び出し側は明示的にそれらをキャッチする必要はありません。

リソースバンドルプロパティファイルは org/apache/axis/i18n/resource.properties です。

メッセージファイルの拡張

一般的に、Axis 内では全てのメッセージは org.apache.axis.i18n.resource.properties にあります。Axis への統合や、Axis のサードパーティ拡張のために、このファイルを修正することなくメッセージを拡張する機構があります。詳細については統合ガイドをご覧下さい。

テストケースの追加

テスト構造とサンプル構造もご覧下さい。

編集者注釈: テストの追加の合理化と簡素化のさらなる努力が必要です。また、テストが増えるに従って、テストの分類を考慮に入れる必要もあります。

Axis に変更を加えたのであれば、その変更を利用するテストを追加してください。理由は以下の通りです。

  • そのテストはあなたの新しいコードが動作することを検証します。
  • そのテストはあなたの変更を、将来のコード変更からもたらされるバグから守ります。
  • そのテストは Axis のその機能のユーザへの例になります。
  • そのテストは新しい開発の出発点として利用することができます。

いくつかの一般的な原則を挙げます。

  • テストは自己説明的であるべきです。
  • テストは大量の出力を生成するべきではありません。
  • テストは既存の junit フレームワーク内にフックするべきです。
  • 各テストや関連するテストの各グループは xml-axis/java/test ディレクトリ内に自分のディレクトリを持つべきです。

テストをビルドする1つの方法は、既存のテストを "カットアンドペースト" し、あなたのニーズを満たすようにテストを修正することです。このアプローチは異なる種類のテストが増えるにつれてより複雑になってきます。

参考になる "wsdl のない" 良いテストは test/saaj です。

WSDL テストの作成

私が sequence テストを作成するために利用したステップを紹介します。このテストは wsdl ファイルからコードを生成し、シーケンスの検証テストを実行します。

  1. xml-axis/java/test/wsdl/sequence ディレクトリを作成します。
  2. Webサービスを定義する SequenceTest.wsdl ファイルを作成します。
  3. Java ファイルを作成するために Wsdl2java 出力器を実行します。

    java org.apache.axis.wsdl.Wsdl2java -t -s SequenceTest.wsdl

    1. -t オプションにより出力器はテストハーネスにフックする *TestCase.java ファイルを生成します。このファイルは変更を一切加えることなく利用できます。*TestCase.java ファイルをあなたの wsdl ファイルと同じディレクトリにコピーしてください。(理想的には、変更された Java ファイルのみがあなたのディレクトリにある必要があります。ですからこのファイルは必要ありませんが、テストケースを出力するために <wsdl2java ...> 節を修正する (下で説明しています) ことを確認してください。)
    2. -s オプションにより出力器は *SOAPBindingImpl.java を生成します。この Java ファイルにはサービスの空のメソッドが含まれています。それらに自分のロジックを埋め込んでください。*SOAPBindingImpl.java ファイルをあなたの wsdl ファイルと同じディレクトリにコピーしてください。(この Java ファイル内で変更がまったく必要なければ、保存する必要はありません。しかし <wsdl2java ...> 節がスケルトンを生成するか確かめる必要があります)。
    3. 修正を必要としない全ての Java ファイルを削除します。ですからあなたのディレクトリには3つのファイル (wsdl ファイル、*TestCase.java、*SOAPBindingImpl.java) があるはずです。私のシーケンステストには、私が必要としたいくつかの追加的なロジックのため、そのほかのファイルもあります。
  4. test/wsdl/sequence/build.xml ファイルはこのテストのビルドを制御します。"compile" ターゲットを探してください。Wsdl2java コードを実行する節を追加します。test/wsdl/roundtrip/build.xml ファイル (これは多くの wsdl2java 呼び出しと java2wsdl 呼び出しを行っています) から流用することをお勧めします。ここにシーケンステストの1つを示します。
    <!-- Sequence Test -->
    <wsdl2java url="${axis.home}/test/wsdl/sequence/SequenceTest.wsdl"
        output="${axis.home}/build/work"
        deployscope="session"
        skeleton="yes"
        messagecontext="no"
        noimports="no"
        verbose="no"
        testcase="no">
    <mapping namespace="urn:SequenceTest2" package="test.wsdl.sequence"/>
    </wsdl2java>
  5. 新しい build.xml ファイル内の run ターゲットを有効にします。execute-Component ターゲットと (もうすぐ紹介する) execute-Simple-Test ターゲットから選ぶ必要があります。これらは単一のコンポーネントとして実行したときに、どのようにしてテストが呼び出されるかを制御します。execute-Component はテストを実行する前に tcp-server と http-server をセットアップし、同様に、必要になるかもしれないデプロイとサービスを処理します。execute-Simple-test は単に生のテストクラスファイルを呼び出します。
  6. これで終わりです。ant functional-tests を実行して検証してください。あなたのテストをチェックインしてください。

テスト構造

テストとサンプルの再設計ドキュメントはここにあります。 [英語]

Axis 1.0, RC1 以降、"コンポーネント化された" テスト構造に移行しました。1つの高レベルの大きな再帰関数を持つ代わりに、test/** ツリーと samples/** ツリーの葉レベルに小さくて単純な "コンポーネント" である build.xml ファイルがあります。

これらの "コンポーネント" ファイルは共通のレイアウトを持っています。それらの主要な target は以下の通りです。

  • clean - ビルド先をリセットします。
  • compile - javac、wsdl2java、java2wsdl 指示です。
  • run - テストを "実行" します

"サンプル" であるテスト xml ファイルは test/templateTest にあります。

ソースコードチェックを追加する

Axis ビルドは、メッセージを発行する際に国際化された文字列を利用しているかといった、特定の規約に従っているか確かめるために、ソースディレクトリ (java/src) 内のファイルに対して一定の自動化されたチェックを実行します。

規約を正規表現マッチに変形できるのであれば、java/test/utils/TestSrcContent.java を更新することでビルド時にそれを強制することができます。

必要なことは、static な FileNameContentPattern 配列にパターンを追加するだけです。各パターンは3つのパラメータを持っています。

  1. チェックされるファイル名にマッチするパラメータ。
  2. 選択したファイル内で検索されるパラメータ。
  3. そのパターンが許されているかどうかを示す boolean (一般的には false は許されていないことを示しています)。

正規表現表記の適度な要約は Jakarta ORO javadocs [英語] で提供されています。

JUnit と Axis

Webサービスを呼び出す Axis クライアントの JUnit テストを実行しようとすると、必ず以下の例外を得ることになるでしょう。

java.lang.ExceptionInInitializerError 
at org.apache.axis.client.Service.<init>(Service.java:108) 
... 

Caused by: org.apache.commons.logging.LogConfigurationException: ... 
org.apache.commons.logging.impl.Jdk14Logger does not implement Log 
at org.apache.commons.logging.impl.LogFactoryImpl.newInstance
(LogFactoryImpl.java:555) 
...

実際には、Jdk14Logger は Log を実装しています。これは JUnit のクラスローディングの問題です。JUnit のグラフィカルな TestRunner には、ユーザが "Run" ボタンを押すたびに修正されたクラスを動的にリロードする機能があります。これによりユーザは修正のたびに TestRunner を再起動する必要がなくなります。このために JUnit は自身のクラスローダ junit.runner.TestCaseClassLoader を利用します。JUnit 3.8.1 現在、TestCaseClassLoader とシステムクラスローダ間で、どのクラスに対してどちらのローダが実行されたか、あるいはどちらのローダでロードすべきかという混乱が生じます。

この問題を回避するのに2つの方法があります。

  • 確実で簡単な調整です。junit.swingui.TestRunner を -noloading 引数付きで実行することにより、動的クラス再ロードの機能をオフにします。
  • 細かくて手の込んだ調整です。動的クラス再ロードを行いたい場合にのみ必要になります。特定のパッケージとそのサブパッケージを無視し、それらをシステムクラスローダに先延ばしすることを TestCaseClassLoader に対して指示します。これは junit.jar 内にある junit/runner/excluded.properties ファイルを利用することで行えます。このファイルの内容は以下の通りです。
    #
    # The list of excluded package paths for the TestCaseClassLoader
    #
    excluded.0=sun.*
    excluded.1=com.sun.*
    excluded.2=org.omg.*
    excluded.3=javax.*
    excluded.4=sunw.*
    excluded.5=java.*
    excluded.6=org.w3c.dom.*
    excluded.7=org.xml.sax.*
    excluded.8=net.jini.*

ディレクトリパスを保持しながらこのファイルを他の場所、例えば deployDir にコピーします。ですからコピーされたプロパティファイルのパスは deployDir/junit/runner/excluded.properties になります。このファイルの最後に余分なエントリを追加します。

excluded.9=org.apache.*

あなたのクラスパスを修正して、deployDir が junit.jar の前に来るようにします。これによりデフォルトではなく修正した excluded.properties が利用されます。(クラスパスに excluded.properties 自身へのパスを追加しないで下さい。)

この修正により commons-logging 例外を防ぐことができます。しかし、他のクラスローディング問題が出てくるかもしれません。例えば以下のようなものです。

Dec 10, 2002 7:16:16 PM org.apache.axis.encoding.ser.BeanPropertyTarget set 
SEVERE: Could not convert [Lfoo.bar.Child; to bean field 'childrenAsArray', 
type [Lfoo.bar.Child; 
Dec 10, 2002 7:16:16 PM org.apache.axis.client.Call invoke 
SEVERE: Exception: 
java.lang.IllegalArgumentException: argument type mismatch 
at org.apache.axis.encoding.ser.BeanPropertyTarget.set
(BeanPropertyTarget.java:182) 
at org.apache.axis.encoding.DeserializerImpl.valueComplete
(DeserializerImpl.java:284) 
...

この場合、あなたには選択肢はなく、動的クラス再ロードをあきらめて -noloading 引数を利用します。

Axis Webサービスの JUnit テストに関してもう1つ注意があります。Webサービスとして公開するコンポーネントをローカルで JUnit テストを実行したとします。一連のテストを初期化するために "Run" ボタンを押します。各テスト間で、全てのデータ構造は再初期化されます。あなたのテストは長い緑色のバーを生成します。これはいいでしょう。

次に、Axis Webアプリケーションと共にあなたのWebサービスが実行されているアプリケーションサーバに接続する Axis クライアントに対して JUnit テストを実行するとします。各テスト間で、JUnit はあなたのクライアントを自動的に再初期化します。

サーバ側のデータ構造が問題となります。各テストの終わりでサーバのデータをチェックしていて (するべきです)、一度に2つ以上のテストを実行すると、2つめ以降のテストが失敗します。なぜなら、現在のテストのみに基づいた新鮮なデータではなく、以前のテストに基づいた累積したデータを Axis サーバ上で生成しているからです。

これは、各テストにおいてあなたのWebサービスを手動で最初期化しなくてはいけないことを意味しています。これを達成する1つの方法は、あなたのWebサービスインターフェースに最初期化操作を加えることです。そして各テストの最初で、クライアントがその操作を呼ぶようにします。

ファンクションテストを監視するために tcpmon を利用する

functional-tests (あるいは all-tests) の実行時にメッセージを監視する簡単な方法を紹介します。

8080ポートをリッスンし、異なるポートにフォワードする tcpmon を起動します。

java org.apache.axis.utils.tcpmon 8080 localhost 8011

SimpleAxisServer にポートをフォワードし、失敗が発生しても functional-tests を続けるように指示してテストを実行します。

ant functional-tests -Dtest.functional.SimpleAxisPort=8011 -Dtest.functional.fail=no

すべてのテストの SOAP メッセージは tcpmon ウィンドウに現れるはずです。

tcpmonAxis ユーザガイドで詳細に説明されています。

ファンクションテストを監視するために SOAP Monitor を利用する

(Tomcat のような) Webアプリケーションサーバを利用してWebアプリケーションとして実行しているコードをデバッグする際に、SOAP リクエストメッセージと SOAP レスポンスメッセージを見るために SOAP Monitor ユーティリティを利用することもできます。

ウェブブラウザウィンドウ内で SOAP Monitor アプレットをロードして SOAP Monitor ユーティリティを立ち上げてください。

http://localhost:<port>/axis/SOAPMonitor

テストを実行するに従って、SOAP メッセージが SOAP Monitor ウィンドウに現れます。

SOAP MonitorAxis ユーザガイドで詳細に説明されています。

単一のファンクションテストを実行する

あるウィンドウでサーバを起動します。

java org.apache.axis.transport.http.SimpleAxisServer -p 8080

別のウィンドウでまずテストを行うサービスをデプロイします。

java org.apache.axis.client.AdminClient deploy.wsdd

次にテストを指定して JUnit ユーザインターフェースを立ち上げます。例えば、マルチスレッドのテストケースを実行するには以下のようにします。

java junit.swingui.TestRunner -noloading test.wsdl.multithread.MultithreadTestCase

デバッギング

デバッグ出力を行う

この節では Axis のデフォルトのロガーである Log4J の説明をします。Log4J の追加的な情報はロギング/トレーシング節をご覧下さい。

  • Log4J プロパティの上書き

    log4j.properties ファイルは妥当なデフォルトの設定で axis.jar にパッケージ化されています。これ以降の項目では設定を変更していきます。多くのオプションが開発者に公開されていて、そのほとんどは axis.jar から log4j.properties を取り出して適切に修正しなくてはいけません。

    • コマンドラインやスクリプトファイルから Java プログラムをビルドし、実行しているのであれば、JVM オプション -Dlog4j.configuration=yourConfigFile を含めます。
    • CLASSPATH中であなたの log4j.propertiesaxis.jar よりも前に来るように CLASSPATH を設定します。
    • ant を利用してプログラムをビルドし、実行している (これは Axis のビルドとそのテストの実行を含んでいます) のであれば、環境変数 ANT_OPTS-Dlog4j.configuration=yourConfigFile に設定します。
    • Axis をビルドしているのであれば、src/log4j.properties を直接変更することができます。その変更をコミットしないように注意してください。
  • 全てのデバッグ出力を行う
    • log4j.rootCategory 優先度DEBUG に設定します。
    • アペンダの優先度閾値を DEBUG に設定します (Axis 内の log4j.properties ファイルは2つのアペンダを定義しています。CONSOLELOGFILE です)。
  • 選択可能な DEBUG 出力
    • log4j.rootCategory 優先度INFO 以上に設定します。
    • 対象とするロガーの log4j.logger.logger.name 優先度DEBUG に設定する。
    • アペンダの優先度閾値を DEBUG に設定します (Axis 内の log4j.properties ファイルは2つのアペンダを定義しています。CONSOLELOGFILE です)。
    • これでもまだ必要以上の情報が出力されているのであれば、ログ出力から必要な情報を取り出すためにその他のツールを利用する必要があります。ログメッセージから、適切なキーワードを用いて grep などのツールを利用して検索します。

一時出力の出力

Axis は多くのオープンソースのWebアプリケーションやその他のWebアプリケーションでの利用を目標としているので、善良な市民である必要があります。System.out.printlnSystem.err.println を利用して出力を書き出すことは避けるべきです。

開発者はシステムをデバッグしたり分析したりする際に System.out.println を利用する傾向があります。もしこれを行うのであれば、System.out.printlnSystem.err.println の回避を強制する util/TestSrcContent テストを無効にする必要があります。また、そのコードをチェックインして戻す前にあなたの文を削除する必要が出てきます。

別の方法として、デバッグ文 log.debug("適度に簡潔で意味のあるメッセージ") を導入することを強くお勧めします。デバッグメッセージが問題の理解に今役立つのであれば、将来あなたと仲間にとってもまた役立つかもしれません。

JAX-RPC 互換性テストの実行

仕様と同様に、JAX-RPC には JAX-RPC エキスパートグループのメンバ (とその他の人達?) が手に入れることのできる Technology Compatibility Kit (TCK) があります。

このキットは zip ファイルで提供されているので、自分の好きなディレクトリに unzip する必要があります。インストールの説明は docs ディレクトリ内にある JAX-RPC リリースノートドキュメントにあります。ウェブブラウザを利用して doc ディレクトリ内の index.html ファイルを開くと、キットで提供されている全てのドキュメントのリストを見ることができます。

このキットには、互換性テストを実行するのに利用される JavaTest テストハーネスが含まれていることに注意してください。

これらのテストの実行に関する情報がさらに必要であれば、ここに追加してください。