Axis ユーザガイド

目次

はじめに

Axisへようこそ。AxisはApache SOAPの第三世代です。この文書は1.0版です。

SOAPとは?

SOAPは、アプリケーション間通信向けのXML-ベースの通信プロトコル、および、エンコーディングフォーマットです。元々は、MicrosoftとUserlandソフトウェアによって考案されたものでした。いくつかの改訂を経て、現在の仕様 SOAP 1.1 は、急速に人気が高まり、利用されるようになりました。W3CのXMLプロトコル作業グループでは、SOAPを真のオープンな標準に変更する作業を行っており、SOAP 1.1の仕様で混乱していた部分をさらに明確にするためにSOAP 1.2のワーキングドラフトを策定しています。

SOAPは、Webサービスと呼ばれる、プラットフォーム非依存、言語非依存の分散アプリケーションの新世代のバックボーンとして幅広く採用されています。

Axisとは?

Axisは、本質的にはSOAPエンジン、つまり、クライアント、サーバ、ゲートウェイなどのSOAPプロセッサを構成するフレームワークです。Axisの最新バージョンは、Javaで作成されていますが、C++によるAxisのクライアントサイドの実装が開発中です。

しかし、Axisは単なるSOAPエンジンというわけではなく、以下のものも含まれています:

Axisは、(IBMによる"SOAP4J"を起源とする)Apache SOAPの第三世代です。2000年後半に、Apache SOAP v2のコミッタは、このエンジンをさらに柔軟に、設定変更可能に、そして、W3Cから提示されるXMLプロトコル仕様とSOAPの両方を扱えるようにするための議論を始めました。

すぐに、すべてを再構築する必要があることが明確になりました。v2のコミッタの多くから非常に類似した設計が提示されました。それは、柔軟、かつ、構成可能な方式で、限定された機能を実装するメッセージ「ハンドラ」の設定変更可能な「チェーン」を全てのベースとするというものです。

数ヶ月続いた議論と、この方針によるコーディングの後、Axisは以下のキーとなる機能を持つようになりました:

Axisで楽しんで下さい。これがオープンソースの成果であることには注意して下さい。そのコードが何らかの新機能や不具合修正となると思ったら、どうか参加して助けて下さい!Axisの開発者コミュニティは、あなたの参加を待っています。

あなたの考えを教えてください!

パッケージについてのご意見は、"axis-user@xml.apache.org" に送って下さい。また、Axisは、Apacheのバグ追跡および機能要求データベースであるbugzillaにも登録されています。

このリリースは何?

このリリースでは以下の機能を含んでいます:

何が不足しているか?

Axisのインストールとこのガイドの使用方法

使用中のJ2EEサーバにWebアプリケーションとしてAxisをインストールする手順については、Axis インストールガイドを参照して下さい。

このガイド内のサンプルを実行する前に、CLASSPATHに以下が含まれているかどうか確認して下さい(もし、CVS checkoutしたものからAxisを構築した場合、これらはaxis-1_0/libではなくxml-axis/java/build/libにあります):

AxisでWebサービスを使用

基本 - ここから始めよう

Apacheの公開AxisサーバにあるechoStringメソッドを呼び出すWebサービスクライアントのサンプルを見てみましょう。
1   import org.apache.axis.client.Call;
2   import org.apache.axis.client.Service;
3   import javax.xml.namespace.QName;
4   
5   public class TestClient {
6      public static void main(String [] args) {
7          try {
8              String endpoint =
9                       "http://nagoya.apache.org:5049/axis/services/echo";
10  
11             Service  service = new Service();
12             Call     call    = (Call) service.createCall();
13  
14             call.setTargetEndpointAddress( new java.net.URL(endpoint) );
15             call.setOperationName(new QName("http://soapinterop.org/", "echoString"));
16  
17             String ret = (String) call.invoke( new Object[] { "Hello!" } );
18  
19             System.out.println("Sent 'Hello!', got '" + ret + "'");
20         } catch (Exception e) {
21             System.err.println(e.toString());
22         }
23     }
24  }
(このファイルは、samples/userguide/example1/TestClient.javaにあります)

ネットワークに接続していれば、このプログラムは以下のように実行できます:

% java samples.userguide.example1.TestClient
Sent 'Hello!', got 'Hello!'
%
さて、何が起こったのでしょうか?11,12行目で、新しいServiceオブジェクトとCallオブジェクトを生成しました。これらは、標準JAX-RPCオブジェクトで、実行するサービスに関するメタデータを格納するために使用されます。14行目では、終端URL、つまり、SOAPメッセージの宛先を設定しています。15行目で、Webサービスの操作(メソッド)名を定義しています。そして、17行目で、実際にパラメータの配列を渡して実行したいサービスを呼び出しています。この場合は、ちょうど1つのStringです。

通信経路上に流れるSOAP要求を見ることで、この引数に何が起こるのかを確認することができます(色をつけた部分を見て、これらが上の呼び出しにおける値と一致していることに注目して下さい):

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SOAP-ENV:Body>
    <ns1:echoString xmlns:ns1="http://soapinterop.org/">
      <arg0 xsi:type="xsd:string">Hello!</arg0>
    </ns1:echoString>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

このString引数は、自動的にXMLにシリアライズされ、サーバは同一のStringを返します。上ではこれをデシリアライズして表示しています。

注意: 実際にクライアントとサーバ間でやりとりされるXMLを見るためには、配布内に含まれるtcpmon、または、SOAPモニタツールを使用することができます。概要は、付録を参照して下さい。

命名パラメータ

上の例では、Axisが自動的にSOAPメッセージ内のXMLにエンコードされた引数に対して"arg0"、"arg1"などと命名していることが分かります(この場合は、"arg0"のみです)。これを変更することは簡単です。以下のように、invoke()を呼び出す前に、パラメータ毎にaddParameterを、戻り値に対してsetReturnTypeを呼び出す必要があります:
  call.addParameter("testParam", 
                    org.apache.axis.Constants.XSD_STRING,
                    javax.xml.rpc.ParameterMode.IN);
  call.setReturnType(org.apache.axis.Constants.XSD_STRING); 
ここでは、呼び出しにおける(唯一の)第1パラメータに、testParamという名前を割り当てています。また、ここで、パラメータの型(org.apache.axis.Constants.XSD_STRING)と、このパラメータが入力用、出力用、もしくは、入出力用かを定義します。上の場合は入力用としています。この状態で、プログラムを実行すると、以下のようなメッセージになります:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SOAP-ENV:Body>
    <ns1:echoString xmlns:ns1="http://soapinterop.org/">
      <testParam xsi:type="xsd:string">Hello!</testParam>
    </ns1:echoString>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
このパラメータが予想通り "testParam" という名前になっていることに注目して下さい。

「型付けされていない」サーバ間の相互運用

上の例では、Objectとして戻されるinvoke()の戻り値の型を適切な「本当の」型にキャストしています。例えば、echoStringメソッドがStringを返すことが分かっているので、client.invoke()からはStringが返されることを想定しています。少し時間をかけて、どのようなことが起こっているかを調査して、潜在的な問題に解決の光を与えてみましょう(もちろん、答えが分かっているので苦悩することはありません :))

以下に、echoStringメソッドでよく見られる典型的な応答を示します:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SOAP-ENV:Body> <ns1:echoStringResponse xmlns:ns1="http://soapinterop.org/"> <result xsi:type="xsd:string">Hello!</result> </ns1:echoStringResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>

色で示した部分に注目して下さい。この属性は、スキーマ型宣言であり、Axisは、これを使用して要素の内容が何かを決定します。この例では、JavaのStringオブジェクトにデシリアライズ可能なものとなります。多くのツールキットは、この種の明示的な型情報をXML内に埋め込み、メッセージを「自己記述」可能なものとしています。一方、ツールキットの中には以下のような応答を返すものもあります:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <SOAP-ENV:Body>
  <ns1:echoStringResponse xmlns:ns1="http://soapinterop.org/">
   <result>Hello, I'm a string!</result>
  </ns1:echoStringResponse>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

メッセージ内に型がありません。この場合、どうすれば<result>要素をどのJavaオブジェクトにデシリアライズすれば良いのかが分かるでしょうか?答えは、メタデータ、つまりデータについてのデータです。この場合は、戻り値として何を想定すれば良いかを通知する、サービスの記述が必要です。以下に、Axisにおけるクライアントサイドでの実現方法を示します:

  call.setReturnType( org.apache.axis.Constants.XSD_STRING );

このメソッドは、返される要素が型付けされていない場合、戻り値が定義済みのSOAP String型として設定されたxsi:type属性を持つかのように動作すべきであることを、Axisクライアントに通知します(この動作のサンプルとして、interop echo-testクライアント、samples/echo/TestClient.javaがあります)。

また同様に、想定される戻り値の型をJavaクラスとして指定できる以下のメソッドがあります:

call.setReturnClass(String.class);

ここまでで、クライアントとしてSOAPサービスにアクセスするための基本を理解できたかと思います。しかし、どのようにすれば独自サービスを公開できるでしょうか?

AxisでWebサービスを公開

以下のような簡単なクラスがあるものとします:
public class Calculator {
  public int add(int i1, int i2)
  {
    return i1 + i2; 
  }
  
  public int subtract(int i1, int i2)
  {
    return i1 - i2;
  }
}
(この非常に小さなクラスは、samples/userguide/example2/Calculator.javaにあります)。

このクラスをSOAP経由で利用可能にするには、どうすれば良いでしょうか?この問いには、2つの答えがあります。まずは、最も簡単な方法から始めましょう。Axisは、このほとんど手間がかからない手法を提供しています!

JWS (Java Web Service) ファイル - 簡易配備

まずは、ステップ1です:上の.javaファイルをwebappディレクトリにコピーし、"Calculator.jws"に名前を変更して下さい。以下のような操作を行います:
% copy Calculator.java <your-webapp-root>/axis/Calculator.jws
次にステップ2...は、1分間待って下さい。これで終りです!これで、以下のURLでこのサービスにアクセスすることができるはずです(Axis Webアプリケーションが8080ポート上にあると仮定しています):

http://localhost:8080/axis/Calculator.jws

Axisは、自動的にこのファイルを検知し、クラスをコンパイルし、正確にSOAP呼び出しをサービスクラスのJava呼び出しに変換します。試してみて下さい。samples/userguide/example2/CalcClient.javaに電卓クライアントがあります。これを次のように使用して下さい:

% java samples.userguide.example2.CalcClient -p8080 add 2 5
Got result : 7
% java samples.userguide.example2.CalcClient -p8080 subtract 10 9
Got result : 1
%
(J2EEサーバが使用しているポートで "-p8080" を置き換えなければならないことに注意して下さい)。

カスタム配備 - WSDDの紹介

JWSファイルは、クラスをWebサービスとする非常に簡単な方法ですが、常に最善の方法というわけではありません。例えば、ソースコードが必要な場合です。ソースの付いていないシステム上の既存のクラスの中身を確認したいと思ったことは何回もあるはずです。また、サービスに対してどのようにアクセスさせるかに関する設定変更可能な項目数が、かなり限定されます。カスタム型マッピングを指定することや、サービスを使用する際にどのハンドラを呼び出すかを制御することはできません。 (今後のための注意:AxisチームとJava SOAPコミュニティの多くは、この種のメタデータを必要に応じてソース内に埋め込むための方法を検討しています。期待して待っていて下さい!)

記述子経由の配備

Axisで用意された柔軟性を十分に使用するためには、AxisのWebサービス配備記述子(WSDD)のフォーマットを理解しなければなりません。配備記述子は、Axisに「配備」したい、つまり、Axisエンジンで有効とさせたい、全てのものが含まれています。最も良く配備するものは、Webサービスです。そのため、ここでは、基本的なサービスの配備記述子を見ることから始めましょう(このファイルは、samples/userguide/example3/deploy.wsddです):

<deployment xmlns="http://xml.apache.org/axis/wsdd/"
            xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
 <service name="MyService" provider="java:RPC">
  <parameter name="className" value="samples.userguide.example3.MyService"/>
  <parameter name="allowedMethods" value="*"/>
 </service>
</deployment>
実際のところ非常に単純です。最も外側の要素は、エンジンに対して、これがWSDD配備であること、および、「java」名前空間を定義することを通知しています。そして、service要素が実際にサービスを定義します。サービスは、対象となるチェーンです(Axis アーキテクチャガイドを参照して下さい)。つまり、サービスは要求フロー、折り返しハンドラ(サービスから見るとプロバイダと呼ばれます)、応答フローを全て、もしくは、一部持つことを意味しています。この場合、プロバイダは、Axisに組込み済みのJava RPCサービスを表す "java:RPC" です。実際にこれを扱うクラスは、org.apache.axis.providers.java.RPCProviderです。 異なる様式のサービスやそのプロバイダについては、後で詳しく説明します。

RPCProviderに対して、正しいクラス(例えば、samples.userguide.example3.MyService)のインスタンス化と呼び出しを行うよう通知しなければなりません。これは、クラス名を設定するserviceの1つのパラメータと、そのクラス内のpublicメソッド全てがSOAP経由で呼び出し可能かどうかをエンジンに通知する別のパラメータを指定した<parameter>タグを含めることで行うことができます(「*」がこれを意味します。呼び出し可能なメソッド名を空白、または、カンマで区切ることでSOAPでアクセス可能なメソッドを制限することもできます)。

高度なWSDD - より多くのオプションの指定

WSDD記述子は、サービスについての他の情報や、後述の"ハンドラ"と呼ばれるAxisの別の部分も含めることができます。

スコープ付きのサービス

Axisがサポートするサービスオブジェクト(実際はメソッドを実装するJavaオブジェクト)のスコープには、3種類あります。デフォルトの"Request"スコープは、サービスにSOAP要求が届く度に新しいオブジェクトを作成します。"Application"スコープは、サービスへの全ての要求内で共有されるシングルトンオブジェクトを作成します。"Session"スコープは、サービスにアクセスするセッションが有効なクライアント毎に新しいオブジェクトを作成します。スコープオプションを指定するには、サービスに以下のような<parameter>を追加します("value"は、request、session、applicationのいずれかです):

<service name="MyService"...>
  <parameter name="scope" value="value"/>
  ...
</service>

AdminClientの使用

このファイルの用意ができたら、記述したサービスを実際に配備するために、Axisサーバに送信する必要があります。AdminClient、または、"org.apache.axis.client.AdminClient"を使用して、これを行います。Tomcat以外のサーバ上にAxisを配備する場合は、-p <port> 引数を指定する必要があるかもしれません。デフォルトポートは、8080です。以下に、AdminClientの典型的な呼び出しを示します:
% java org.apache.axis.client.AdminClient deploy.wsdd
<Admin>Done processing</Admin>
このコマンドによって、SOAP経由でサービスにアクセスできるようになります。Clientクラスを実行して確認して下さい。以下のようになるはずです:
% java samples.userguide.example3.Client -lhttp://localhost:8080/axis/services/MyService "test me!"
You typed : test me!
%
配備が本当に行われたかどうかを自分自身で確認したい場合は、サービスの配備を元に戻し、再度呼び出してください。example3/ディレクトリに、"undeploy.wsdd"があります。これまでdeploy.wsddで行ったように、このファイルを使用することができます。このファイルで、AdminClientを実行して下さい。そして、サービスクライアントを再実行し、何が起こるかを確認して下さい。

AdminClientを使用して、サーバ上に配備された全てのコンポーネントの一覧を入手することができます:

% java org.apache.axis.client.AdminClient list
<big XML document returned here>
ここには、サービス、ハンドラ、転送などが出力されます。この一覧は、サーバの"server-config.wsdd"ファイルの完全なコピーであることに注意して下さい。このファイルについては、少し後でより詳細に説明します。

更なる配備 - ハンドラとチェイン

ここから、Axisエンジンのもっと強力な機能の一部を見ていきましょう。サービスが何回呼び出されたかを追跡したい場合を仮定します。このためだけに、samples/logディレクトリ内にサンプルハンドラを入れています。このようなハンドラクラスを使用するには、まずハンドラ自体を配備する必要があります。そして、サービスの配備時に指定した名前を使用します。ここで、サンプルとしてdeploy.wsddファイルを示します(これは、samples/userguide内の例4です):
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
            xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
  <!-- define the logging handler configuration -->
 <handler name="track" type="java:samples.userguide.example4.LogHandler">
  <parameter name="filename" value="MyService.log"/>
 </handler>
 
 <!-- define the service, using the log handler we just defined -->
 <service name="LogTestService" provider="java:RPC">
  <requestFlow>
   <handler type="track"/>
  </requestFlow>
 
  <parameter name="className" value="samples.userguide.example4.Service"/>
  <parameter name="allowedMethods" value="*"/>
 </service>
</deployment>

最初の節では、samples.userguide.example4.LogHandlerクラスで実装された"track"というハンドラを定義しています。このハンドラにオプションを渡して、どのファイルにメッセージを書き出すかを指定します。

次に、LogTestServiceサービスを定義しています。これは、上で最初の例として示したものと同じRPCサービスです。違いは、<service>内の<requestFlow>要素です。これはサービスが呼び出された時に、そのプロバイダより前に呼び出されるハンドラの集合です。"track"への参照を入れることで、このサービスが呼び出される度にそのメッセージが確実にログに残ります。

リモート管理

デフォルトでは、Axisサーバは、サーバが存在するマシンからの管理要求のみを受け付けるように設定されています。リモートからの操作を有効にしたい場合は、AdminServiceの"enableRemoteAdmin"プロパティをtrueに設定しなければなりません。このためには、webappのWEB-INFディレクトリ配下から"server-config.wsdd"ファイルを見つけて下さい。このファイル内にAdminService用の配備があります。以下のようにオプションを追加して下さい:
<service name="AdminService" provider="java:MSG">
  <parameter name="className" value="org.apache.axis.util.Admin"/>
  <parameter name="allowedMethods" value="*"/>
  <parameter name="enableRemoteAdmin" value="true"/>
</service>

警告:リモート管理を有効にすると、認証されていないパーティに対してマシンへのアクセスを与えることになります。この設定を行う場合は、設定にセキュリティを確実に加えて下さい!

サービス様式 - RPC、Document、Wrapped、Message

Axis1.0のサービスには、4種類の"様式"があります。RPCサービスは、SOAP PRC規約、および、SOAP "section 5" エンコーディングを使用します。Documentサービスは、エンコーディングを全く使用しません(従って、特に通信路上のmultirefオブジェクトのシリアライズや、SOAP様式の配列を解釈できません)。しかし、XML<->Java 間のデータバインディングを行います。Wrappedサービスは、Documentサービスと似ていますが、SOAPボディ全体を1つの巨大な構造体にバインディングするのではなく、SOAPボディ全体から個別のパラメータを"取り出し"ます。Messageサービスは、型マッピングやデータバインディングを行わずに、SOAPエンベロープ内にて任意のXMLの送受信を行います。送信や受信したSOAPエンベロープの生のXMLを扱いたい場合は、Messageサービスを作成して下さい。

RPCサービス

RPCサービスは、Axisにおけるデフォルトです。<service ... provider="java:RPC">、もしくは、<service ... style="RPC">を使用してサービスを配備した場合にこのサービスになります。RPCサービスは、SOAP RPCとエンコーディング規則に従っているので、PRCサービス用のXMLは上の例"echoString"のようなものになります。つまり、1つ1つのRPC 呼び出しは、内側の要素として操作のパラメータにマップされた要素を持つ、操作名と一致する外側の要素としてモデル化されます。Axisは、XMLをサービスを満足させるJavaオブジェクトにデシリアライズし、サービスから返されるJavaオブジェクトをXMLにシリアライズします。RPCサービスは、SOAP section 5 エンコーディング規則を採用していますので、オブジェクトはオブジェクトグラフをエンコードすることが可能な"multi-ref"シリアライゼーション経由でエンコードされます(multi-refシリアライゼーションについての詳細は、SOAP仕様を参照して下さい)。

Document / Wrapped サービス

DocumentサービスとWrappedサービスは、データに対してSOAPエンコーディングを使用しないという点で似ています。単なるプレインな旧XMLスキーマです。しかし両方とも、既にAxisは、Java表現をXMLに"バインド"するので(詳細については、データバインディング参照)、XML構造を直接扱うのではなく、Javaオブジェクトとして扱うことになります。

DocumentサービスとWrappedサービスの違いについては、まず、以下のような商品注文を含む簡単なSOAPメッセージで説明した方が分かり易いでしょう:

<soap:Envelope xmlns="http://xml.apache.org/axis/wsdd/"
            xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
  <soap:Body>
    <myNS:PurchaseOrder xmlns:myNS="http://commerce.com/PO">
      <item>SK001</item>
      <quantity>1</quantity>
      <description>Sushi Knife</description>
    </myNS:PurchaseOrder>
  </soap:Body>
</soap:Envelope>

PurchaseOrder用の適切なスキーマを以下に示します:

<schema targetNamespace="http://commerce.com/PO">
  <complexType name="POType">
    <sequence>
      <element name="item" type="xsd:string"/>
      <element name="quantity" type="xsd:int"/>
      <element name="description" type="xsd:string"/>
    </sequence>
  </complexType>
  <element name="PurchaseOrder" type="POType"/>
</deployment>

Document様式のサービスでは、これは以下のようなメソッドにマップします:

public void method(PurchaseOrder po)

言い替えると、<PurchaseOrder>要素全体が、3つのフィールドを持つ1つのBeanとしてメソッド内で扱われます。一方、Wrapped様式のサービスでは、以下のようなメソッドにマップします:

public void purchaseOrder(String item, int quantity, String description)

"Wrapped"の場合、<PurchaseOrder>要素は"ラッパ"であり(これが名前の由来です)、正しい操作を示すためだけに提供されます。メソッドへの引数は、外側の要素を"取り外し"、パラメータとして存在する内側の各要素を取り出したものになります。

Document様式、Wrapped様式は、WSDDでは以下のように示されます:

<service ... style="document"> Document様式用
<service ... style="wrapped"> Wrapped 様式用

WSDL文書(以下参照)から始める場合は、ほとんどDocumentサービスかWrappedサービスかで悩むことはありません。

Messageサービス

最後に、Axisを後戻りさせて、Javaオブジェクトに変換させずに実際のXMLとしてコーディングしたい場合に使用される、"Message"様式サービスについて説明します。Message様式サービスのメソッドとして、以下の4つの有効なシグネチャがあります:

public Element [] method(Element [] bodies);
public SOAPBodyElement [] method (SOAPBodyElement [] bodies);

public Document method(Document body);
public void method(SOAPEnvelope req, SOAPEnvelope resp);

最初の2つは、DOM Element、もしくは、SOAPBodyElementの配列をメソッドに渡します。この配列は、エンベロープ内の<soap:body>の内側にある各XML要素を配列要素として持ちます。

3番目のシグネチャでは、<soap:body>を表すDOM Documentを渡し、同じものが返るものと想定します。

4番目のシグネチャでは、要求と応答メッセージを表す2つのSOAPEnvelopeオブジェクトを渡します。サービスメソッド内でヘッダを参照したり編集したりする場合に、このシグネチャが使用されます。応答エンベロープに書き出すものは、戻す時に呼び出し元に全て自動的に送信されます。応答エンベロープには、他のハンドラによって追加されたヘッダが既に含まれている可能性があることに注意してください。

Messageの例

Messageサービスのサンプルは、samples/message/MessageService.javaにあります。MessageServiceサービスクラスには、1つのpublicメソッドechoElementsがあります。このメソッドは、上述の3メソッドシグネチャの最初のものに一致します:

public Element[] echoElements(Element [] elems) 

MsgProviderハンドラは、このメソッドを入力メッセージのSOAPボディの子に対応するorg.w3c.dom.Elementオブジェクトの配列を付けて呼び出します。この配列には、よく1つのElement(おそらくは、ある合意されたスキーマに従うXML文書のルート要素)が含まれますが、SOAPボディは複数の子を扱うことができます。このメソッドは、応答メッセージのSOAPボディ内として返されるElement[]の配列を返します。

Messageサービスは、WSDDファイルと共に配備されなければなりません。以下に、MessageServiceクラス用の完全なWSDDを示します:

<deployment name="test" xmlns="http://xml.apache.org/axis/wsdd/"
      xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"
      xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance">
	<service name="MessageService" style="message">
    <parameter name="className" value="samples.message.MessageService"/>
    <parameter name="allowedMethods" value="echoElements"/>
  </service>
</deployment>

"style"属性は、RPC配備の例と異なる点に注意して下さい。"message"様式は、このサービスがorg.apache.axis.providers.java.RPCProviderではなく、org.apache.axis.providers.java.MsgProviderによって扱われることをAxisに通知します。

配備して、samples.message.TestMsgを実行することで、このサービスを試験することができます(この試験用ドライバが何を行うかについてはソースを参照して下さい)。

AxisにおけるXML <-> Javaデータマッピング

Javaの型をSOAP/XMLの型にマップする方法

相互運用性であるinteropは、SOAP実装間で現在進行中の作業です。他のプラットフォームと実装でサービスを稼働させる場合は、この問題を理解する必要があります。この問題に関する外部の記事がいくつかあるので、まず、そちらを参照して下さい。 AxisにおけるJavaの型とWSDL/XSD/SOAP間の基本的なマッピングは、JAX-RPC仕様により決定されます。どのように変換が行われるかについて完全に理解するには、仕様の4章と5章を参照して下さい。以下に顕著な点を示します。

WSDLからJavaへの標準マッピング

xsd:base64Binary byte[]
xsd:boolean boolean
xsd:byte byte
xsd:dateTime java.util.Calendar
xsd:decimal java.math.BigDecimal
xsd:double double
xsd:float float
xsd:hexBinary byte[]
xsd:int int
xsd:integerjava.math.BigInteger
xsd:long long
xsd:QName javax.xml.namespace.QName
xsd:short short
xsd:stringjava.lang.String

WSDLでオブジェクトがnillable可能であると宣言した場合、つまり、呼び出し元はnilという値を返すことが選択できる場合、プリミティブデータ型は、Byte、Double、Booleanなどといったラッパクラスに置き換えられます。

SOAPエンコーディングデータ型

XSDデータ型は SOAP 'section 5' データ型であり、全てnillableです。従って、ラッパクラスへのマップのみとなります。これらの型が存在する理由は、これらが全て"ID"と"HREF"属性をサポートし、multi-refシリアライゼーションをサポートするRPCエンコードされた文脈内で使用されることです。

例外

この部分は、かなり混乱を招く部分です。更に、この節の著者は全てが動作するか、特に相互運用性という観点から見ると確信がありません。この節は完全ではなく、また、不正確である可能性があることを意味しています。JAX-RPCの5.5.5節と14章を参照して下さい。

RemoteExceptionのSOAP Faultへのマップ

サーバのメソッドがjava.rmi.RemoteExceptionをスローした場合、これはSOAP Faultにマップされます。このfaultcodeは、失敗元のクラス名を含みます。受信者は、クラス名に対して失敗のボディをデシリアライズすることが期待されています。

明らかに、受信者は受信した失敗のインスタンスを生成する方法が分からないので、この機構は動作しません。サービスのWSDL記述内に例外クラスに関する情報が含まれていない、または、送信者と受信者が実装を共有していない限り、サブクラスではなくjava.rmi.RemoteExceptionのインスタンスをスローするしか確実な方法はありません。

他の言語の実装がこのような例外を受信した場合、faultCodeとしてクラス名が分かりますが、まだ例外のボディのパースが残っています。そこで何が起きるかを調べるには、実験する必要があります。

例外はwsdl:fault要素として表現される

メソッドが、java.rmi.RemoteExceptionのインスタンスかサブクラスではないExceptionをスローするものとして印がついている場合は、多少異なります。この例外は、SOAP Faultではありません。このメソッドのWSDLでは、wsdl:faultとして記述されます。 JAX-RPC仕様に従い、例外のサブクラスは、マーシャルされるオブジェクトの全てのフィールドにアクセスするアクセッサメソッドを持つ必要があり、かつ、コンストラクタはパラメータとして同じフィールドをとらなければなりません(つまり、同じ名前と型となる引数)。これは、標準JavaBeanの一種の不変バリアントです。そのオブジェクト内のフィールドは、確実にWSDLにマップできるデータ型である必要があります。

例外がこの仕様を満たす場合、メソッドを記述するWSDLは、呼び出し元がプラットフォームに関係なく例外のスタブ実装を生成できるように、この例外についても記述します。

繰り返しますが、相互運用性を確実にするためには多少の実験が必要です。呼び出す言語によっては例外の概念が存在しないもの、または、どのように例外が扱われるかといった規則がJavaのように厳格でないものもあることを覚えておいて下さい。

限定された相互運用性の範囲で、AxisがSOAP経由で何を送信するか

符号無しデータ
純粋なJAX-RPCでは、符号無しデータ型を使用したサービスを使用、生成することはできません。Javaでは、符号無しデータ型が存在しないからです。

Axisでは、符号無し型をサポートします。従って、C++、C#などで記述されたサービスを使用することと、これらの言語向けに設計されたインタフェースを再実装することができます。しかし、Axisを使用しない、もしくは、他の符号無しサポートを行うSOAPクライアントライブラリを使用するJavaクライアントを停止させることになるため、符号無しデータ型のエクスポートは慎重に行って下さい。少なくとも、JAX-RPCのリビジョンがこれを追加されるまでの間です。

Javaコレクション
HashTableなどCollectionクラスの中にはシリアザイザを持つものがあります。しかし、他のSOAP実装との正式な相互運用性はなく、また、複雑なオブジェクトの扱い方はSOAP仕様内にはありません。オブジェクトの集合を送信する最も確実な方法は、配列を使用することです。多くのJava SOAP実装ではハッシュテーブルのマーシャリング、アンマーシャリングを扱うことができますが、特に、.NETでは扱うことができません。

AxisがSOAP経由で何を送信できないか

事前登録されていない任意のオブジェクト
通信路上で任意のJavaオブジェクトを送信することはできません。末端側で理解できることを期待します。RMIでは、Serializableを実装したJavaオブジェクトを送受信することができますが、これは両端でJavaを実行していたからです。Axisは、Axisシリアライザに登録されたオブジェクトのみを送信します。この文書の後の方で、BeanSerializerを使用してアクセッサ、ミューテータといったJavaBeanパターンに従った任意のクラスをシリアライズする方法を示します。オブジェクトを提供するには、このBeanSerializerを使用してクラスを登録するか、Axisに組込み済みのシリアライゼーションサポートがされなけばなりません。
リモート参照
これは、SOAP仕様にもJAX-PRC仕様にもありません。SOAP呼び出しの終端または他の呼び出しのパラメータとして、オブジェクトの参照を返すことや、オブジェクトの参照を使用できることを呼び出し元に期待することはできません。その代わりに、通信路上に渡すことができる数値や文字列をキーとして持つHashMap内にそれらを格納するなどといった、他の参照機構を使用しなければなりません。

Beanのエンコーディング - BeanSerializer

Axisには、get/setアクセッサといった標準JavaBeanパターンに従った任意のJavaクラスを、コーディングすることなくシリアライズ/デシリアライズする機能があります。これを行うために必要なことは、AxisにどのJavaクラスがどのXMLスキーマ型にマップするかを通知することです。Beanのマッピングの設定は以下のように行います:
<beanMapping qname="ns:local" xmlns:ns="someNamespace"
             languageSpecificType="java:my.java.thingy"/>
<beanMapping>タグは、Javaクラス(おそらくBean)をXML QNameにマップします。qname、および、languageSpecificTypeという 2つの重要な属性があることが分かるでしょう。この場合、"my.java.thingy"クラスをXML QName [someNamespace]:[local]にマップしています。

実際にこれがどのように動作するかを見てみましょう。samples/userguide/example5/BeanService.javaを見て下さい。注目すべきキーとなることは、サービスメソッドへの引数がOrderオブジェクトであることです。Orderは、Axisがデフォルトで理解できる基本型ではありませんので、型マッピングなしにこのサービスを実行しようとすると失敗します(自身で試すには、example5ディレクトリにあるbad-deploy.wsddを使用して下さい)。しかし、配備にbeanMappingを付け加えれば、うまく動作します。以下に、この例の(example5ディレクトリからの)実行方法を示します:

% java org.apache.axis.client.AdminClient -llocal:///AdminService deploy.wsdd
<Admin>Done processing</Admin>

% java samples.userguide.example5.Client -llocal://
Hi, Glen Daniels!

You seem to have ordered the following:

1 of item : mp3jukebox
4 of item : 1600mahBattery

If this had been a real order processing system, we'd probably have charged you about now.
%

Beanが十分でない場合 - カスタムシリアライゼーション

JWSによる開発が、全ての要求を十分に満たすほど柔軟でないのと同様に、デフォルトのBeanシリアライゼーションモデルは、全てのケースを扱えるほどの頑健性はありません。非BeanのJavaクラス(特に既存のクラスの場合)をXMLへ/からマップさせる必要がある場合、特別な方法でJavaへのマップを行うカスタムXMLスキーマ型を持つことができます。Axisは、カスタムのシリアライザ/デシリアライザを作成する機能と、それをより簡単に補助するツールをいくつか提供しています。

TBD - この節は将来のバージョンで拡張されます!現時点では、(samples/encoding内の)DataSer/DataDeserクラスを参照します。また、org.apache.axis.encoding.serパッケージ内のBeanSerializer、BeanDeserializer、ArraySerializer、ArrayDeserializerなどのクラスも参照します。

カスタムマッピングの配備 - <typeMapping>タグ

シリアライザとデシリアライザを構築した段階で、Axisにどの型にそれらを適用するかを通知しなければなりません。以下のように、WSDD内でtypeMappingタグを使用してこれを行います:
<typeMapping qname="ns:local" xmlns:ns="someNamespace"
             languageSpecificType="java:my.java.thingy"
             serializer="my.java.Serializer"
             deserializer="my.java.DeserializerFactory"
             encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

これは、以前に見た<beanMapping>タグとかなり似ていますが、3つの特別な属性が存在します。1つ目は、serializerであり、指定したJavaクラス(つまり、my.java.thingy)のオブジェクトをXMLにマーシャルする際に使用されるシリアライザを生成するシリアライザファクトリのJavaクラス名です。2つ目は、deserializerであり、XMLを正確なJavaクラスにアンマーシャルする際に使用されるデシリアライザを生成するデシリアライザファクトリのJavaクラス名です。最後は、SOAPエンコーディングであるencodingStyleです。

(<beanMapping>タグは、本当はserializer="org.apache.axis.encoding.ser.BeanSerializerFactory"deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"を持った<typeMapping>タグの単なる省略表現です。しかし、これが入力の手間を省くことができることは明確でしょう!)

AxisでWSDLを使用

Webサービス記述言語は、IBMとMicrosoftで作成された仕様で、その他の多くの組織でサポートされています。WSDLは、Webサービスの記述を構造化した形で提供します。WSDLによるサービス記述は、マシン読み込み可能な方式で、サービスへのインタフェース、サービスが使用するデータ型、サービスの場所を通知します。WSDLの書式とオプションに関する詳細については、仕様(最初の文内のリンクをたどって下さい)を参照して下さい。

Axisは、以下の3方式でWSDLをサポートしています:

  1. Axisでサービスを配備すると、ユーザは標準的なWebブラウザを使用し、URLの終りに"?WSDL"を付けることでサービスのURLへアクセス可能になります。これにより、サービスを記述する自動的に生成されたWSDL文書を入手できます。
  2. WSDL記述を使用してサービスのJavaプロキシとスケルトンを構築する"WSDL2Java"ツールを提供しています。
  3. JavaクラスからWSDLを構築する"Java2WSDL"ツールを提供しています。

?WSDL: 配備済みのサービスからWSDLを入手

Axisを使用してサービスを利用可能にした場合、サービスに関連付けされた一意なURLが存在します。JWSファイルの場合、このURLは単純で、JWSファイル自体のパスとなります。非JWSサービスの場合、これは通常は"http://<host>/axis/services/<service-name>"というURLになります。

ブラウザからサービスURLにアクセスすると、終端がAxisサービスであることを示すメッセージを確認できます。通常は、SOAPを使用してそこにアクセスすることになります。しかし、そのURLの終りに"?wsdl"を付けると、Axisは自動的に配備されたサービス用のサービス記述を生成し、ブラウザにXMLとして返します(試して下さい!)。こうして得られた記述は、保存することも、以下で説明する代理生成への入力として使用することもできます。オンラインパートナへこのWSDLを生成するURLを渡すことで、パートナはこれを使用して、.NET、SOAP::Lite、その他WSDLをサポートするソフトウェアなどのツールキットでサービスにアクセスすることができます。

既存のJavaクラスからWSDLファイルを生成することもできます(Java2WSDL: JavaからWSDLを構築を参照して下さい)。
 

WSDL2Java: WSDLからスタブ、スケルトン、データ型を構築

クライアントサイドのバインディング

"org.apache.axis.wsdl.WSDL2Java"に、AXIS WSDL -> Javaのツールがあります。以下に基本的な呼び出し方法を示します:

% java org.apache.axis.wsdl.WSDL2Java (WSDL-file-URL)

これにより、クライアントに必要なバインディングのみ生成されます。AXISは、WSDLからJavaクライアントバインディングを生成する際、JAX-RPC仕様に従います:

% cd samples/addr
% java org.apache.axis.wsdl.WSDL2Java AddressBook.wsdl

生成されたファイルは、"AddressFetcher2"ディレクトリに格納されます。ここに格納される理由は、WSDLからのターゲット名前空間とJavaパッケージへの名前空間マップのためです。名前空間についての詳細は、後で説明します。

WSDL句 生成されるJavaクラス
型節における各エントリに対して Javaクラス
この型が入出力/出力パラメータとして使用される場合はホルダ
各portTypeに対して Javaインタフェース
各バインディングに対して スタブクラス
各サービスに対して サービスインタフェース
サービスの実装(ロケータ)

WSDL型から生成されるJavaクラスは、WSDL型に由来した名前を持ちます。このクラスは、典型的にはBeanですが、常にではありません。例えば、以下のWSDLを指定した場合(このWSDLは、WSDL2Javaに関する節全体で使用され、アドレス帳サンプルにあります):

<xsd:complexType name="phone">
  <xsd:all>
    <xsd:element name="areaCode" type="xsd:int"/>
    <xsd:element name="exchange" type="xsd:string"/>
    <xsd:element name="number" type="xsd:string"/>
  </xsd:all>
</xsd:complexType>

WSDL2Javaは、以下を生成します:

public class Phone implements java.io.Serializable {
    public Phone() {...}
    public int getAreaCode() {...}
    public void setAreaCode(int areaCode) {...}
    public java.lang.String getExchange() {...}
    public void setExchange(java.lang.String exchange) {...}
    public java.lang.String getNumber() {...}
    public void setNumber(java.lang.String number) {...}
    public boolean equals(Object obj) {...}
    public int hashCode() {...}
}

XMLからJava型へのマッピング : メタデータ

上のマップングで注目する点は、XML型名が"phone"であり、生成されたJavaクラスが"Phone"である点です。最初の文字の大文字化は、クラスは大文字から始まるというJavaのコーディング規約に合わせるために行われます。この種のことは、多く起こります。XMLの名前/識別子を表現する規則が、Javaよりも制限が緩いためです。例えば、上の"phone"型内の副要素の1つの名前が、"new"の場合、"new"という名前のJavaのフィールドを作成することはできません。これは予約語であるため、生成されたソースコードのコンパイルは失敗してしまいます。

この種のマッピングをサポートするために、また、XML属性のシリアライゼーション/デシリアライゼーションを有効にするために、型メタデータシステムがあります。このシステムによって、Javaデータクラスを、これらの問題を制御する記述子に関連付けることができます。

WSDL2Javaツールが上のPhoneクラスのようなデータBeanを生成する場合、スキーマに属性が含まれていること、もしくは、Javaのフィールド/プロパティ名に直接マップできない名前があることが分かります。このような場合、このクラス用の型記述子を提供するためのスタティックコードを生成します。型記述子は、基本的には1つ1つがJavaのフィールド/プロパティをXML要素、または、属性にマップするフィールド記述子のコレクションです。

このようなメタデータの例を見たい場合、Axisソース内の"test.encoding.AttributeBean"を参照するか、Javaでは不正となる属性や名前を持つXMLから独自のBeanを生成してみて下さい。

ホルダ

この型は、入出力、または、出力パラメータとして使用されるかもしれません。Javaには、入出力パラメータという概念がありません。この振る舞いを実現するために、JAX-RPCではホルダクラスの使用を指定します。ホルダクラスは、その型のインスタンスを持つ、単純なクラスです。例えば、Phoneクラスのホルダは以下のようになります:

package samples.addr.holders;
public final class PhoneHolder implements javax.xml.rpc.holders.Holder {
    public samples.addr.Phone value;

    public PhoneHolder()
    {
    }

    public PhoneHolder(samples.addr.Phone value) {
        this.value = value;
    }
}

ホルダクラスは、その型が入出力パラメータ、または、出力パラメータとして使用される場合にのみ生成されます。ホルダクラスが、元のクラス名に"Holder"という接尾詞を持つこと、および、"holders"という副パッケージを生成することに注意して下さい。

プリミッティブ型用のホルダクラスは、javax.xml.rpc.holdersにあります。

PortType

サービス定義インタフェース(SDI)は、WSDLのportTypeから派生したインタフェースです。これは、サービス上の操作にアクセスする際に使用するインタフェースです。例えば、以下のWSDLの場合:

<message name="empty">
<message name="AddEntryRequest">
  <part name="name" type="xsd:string"/>
  <part name="address" type="typens:address"/>
</message>
<portType name="AddressBook">
  <operation name="addEntry">
    <input message="tns:AddEntryRequest"/>
    <output message="tns:empty"/>
  </operation>
</portType>

WSDL2Javaは、以下を生成します:

public interface AddressBook extends java.rmi.Remote {
    public void addEntry(String name, Address address) throws java.rmi.RemoteException;
}

SDIの名前について、注意点があります。SDIの名前は、通常はportTypeの名前になります。しかし、SDIの生成時、WDSL2Javaは、portType、および、バインディングの両方の情報を必要とします(これはまだ形式化されておらず、WSDLバージョン2の議論対象となっています)。

JAX-RPC (4.3.3節)では、次を宣言しています:"Javaインタフェースの名前は、wsdl:portType要素のname属性よりマップされます。... サービス定義インタフェースへのマッピングがwsdl:bindingを使用するならば、... サービス定義インタフェースの名前は、wsdel:binding要素の名前よりマップされます。"

仕様の名前について、注意点があります。そこには、"PRC"という文字列が含まれています。そのため、この仕様、および、WSDL2Javaは、portTypeから生成されるインタフェースは、RPCインタフェースであるという仮定をしています。バインディングからの情報が他のものを通知している(言い替えると、wsdl:bindingの要素を使用する)のであれば、インタフェースの名前は、このバインディングから派生したものになります。

なぜでしょう?1つのportType - pt - と、2つのバインディング - bRPCとbDoc - を持たせることは可能です。文書/リテラルは、インタフェースの見ためを変更するので、これらのバインディングの両方を1つのインタフェースで使用することができません。そのため、2つのインタフェース - 1つのptという名前と別のbDocという名前 - および、2つのスタブ - (ptを実装する)bRPCStubと(bDocを実装する)bDocStubを用意することになります。

醜いですね。しかし、なぜこれが必要かは分かるでしょう。文書/リテラルは、インタフェースの見ためを変更するため、1つのportTypeを参照する複数のバインディングを持つことが可能なため、複数のインタフェースを、それぞれ一意な名前を持つように作成する必要があります。
 

バインディング

スタブクラスは、SDIを実装します。その名前は、バインディング名に"Stub"という接尾詞を付けたものになります。ここには、AxisサービスとCallオブジェクトを使用してメソッドの呼び出しをSOAP呼び出しに変更するコードが含まれます。これは、リモートサービス用のプロキシ(同一の考え方について別の用語があります)として役割を担います。つまり、ローカルオブジェクトであるかのように呼び出すことができます。言い替えると、終端URL、名前空間、パラメータ配列を扱う必要はありません。これらは、ServiceオブジェクトやCallオブジェクト経由の動的呼び出しに含まれます。スタブは、全ての作業を隠蔽します。

以下のWSDLの一部分があると:

<binding name="AddressBookSOAPBinding" type="tns:AddressBook">
  ...
</binding>

WSDL2Javaは、以下を生成します:

public class AddressBookSOAPBindingStub extends org.apache.axis.client.Stub
                                        implements AddressBook {
    public AddressBookSOAPBindingStub() throws org.apache.axis.AxisFault
    {...}

    public AddressBookSOAPBindingStub(URL endpointURL, 
                                      javax.xml.rpc.Service service)
        throws org.apache.axis.AxisFault
    {...}

    public AddressBookSOAPBindingStub(javax.xml.rpc.Service service) throws org.apache.axis.AxisFault {...}
    public void addEntry(String name, Address address) throws RemoteException {...}
}

サービス

通常、クライアントプログラムは、スタブのインスタンスを直接作成しません。その代わりに、サービスロケータのインスタンスを作成し、スタブを返すgetメソッドを呼び出します。ロケータは、WSDLのservice句から派生されます。WSDL2Javaは、service句から2つのオブジェクトを生成します。例えば、以下のWSDLでは:
<service name="AddressBookService">
  <port name="AddressBook" binding="tns:AddressBookSOAPBinding">
    <soap:address location="http://localhost:8080/axis/services/AddressBook"/>
  </port>
</service>

WSDL2Javaは、以下のサービスインタフェースを生成します:

public interface AddressBookService extends javax.xml.rpc.Service {
    public String getAddressBookAddress();

    public AddressBook getAddressBook() throws javax.xml.rpc.ServiceException;
    public AddressBook getAddressBook(URL portAddress) throws javax.xml.rpc.ServiceException; }

WSDL2Javaは、このインタフェースを実装する以下のようなロケータも生成します:

public class AddressBookServiceLocator extends org.apache.axis.client.Service
                                       implements AddressBookService {
    ...
}

サービスインタフェースは、WSDLのservice要素内で示された各ポート用のgetメソッドを定義します。ロケータは、このサービスインタフェースの実装であり、これらのgetメソッドを実装します。これは、スタブインタフェースを得るためのロケータとしての機能を提供します。サービスクラスは、デフォルトで、WSDLファイル内で記述された終端URLを指し示すスタブを作成します。しかし、PortTypeを要求する場合に、異なるURLを指定することもできます。

以下にスタブクラスの典型的な使用方法を示します:

public class Tester
{
    public static void main(String [] args) throws Exception {
        // Make a service
        AddressBookService service = new AddressBookServiceLocator();
 
        // Now use the service to get a stub which implements the SDI.
        AddressBook port = service.getAddressBook();
 
        // Make the actual call
        Address address = new Address(...);
        port.addEntry("Russell Butek", address);
    }
}

サーバサイドのバインディング

スタブがJavaで記述されたWebサービスのクライアントサイドであることと同様に、スケルトンは、サーバサイドのJavaフレームワークです。スケルトンクラスを作成するには、WSDL2Javaに "--server-side --skeletonDeploy true" オプションを指定して下さい。例えば、上で示したAddressBook.wsdlを以下のように使用します:
% java org.apache.axis.wsdl.WSDL2Java --server-side --skeletonDeploy true AddressBook.wsdl

WSDL2Javaがクライアント用としてこれまで生成したクラスを全て生成していること、そして、数個の新しいファイルも生成していることが分かるでしょう:
 
 
WSDL句 生成されるJavaクラス
各バインディングに対して スケルトンクラス
テンプレートクラスの実装
全てのサービスに対して 1つのdeploy.wsddファイル
1つのundeploy.wsddファイル

"--skeletonDeploy true" オプションを指定しない場合、スケルトンは生成されません。その代わりに、生成されたdeploy.wsddが、直接実装クラスが配備されたことを示します。このような場合、deploy.wsddは、実装クラスの操作とパラメータを記述する特別なメタデータを含みます。 以下に、実装を直接配備させるようにWSDL2Javaを実行させる方法を示します:

% java org.apache.axis.wsdl.WSDL2Java --server-side AddressBook.wsdl

また、以下に生成されるサーバサイドのファイルを示します:
 
 
WSDL句 生成されるJavaクラス
各バインディングに対して テンプレートクラスの実装
全てのサービスに対して 操作についてのメタデータを持つ1つのdeploy.wsddファイル
1つのundeploy.wsddファイル

バインディング

スケルトンの記述(スケルトン配備用)
スケルトンクラスは、Axisエンジンと実際のサービス実装との間に位置するクラスです。その名前は、バインディング名に"Skelton"という接尾詞を付けたものになります。例えば、AddressBookのバインディングでは、WSDL2Javaは以下を生成します:
public class AddressBookSOAPBindingSkeleton implements AddressBook,
                                                       org.apache.axis.wsdl.Skeleton {
    private AddressBook impl;
 
    public AddressBookSOAPBindingSkeleton() {
        this.impl = new AddressBookSOAPBindingImpl();
    }
 
    public AddressBookSOAPBindingSkeleton(AddressBook impl) {
        this.impl = impl;
    }
 
    public void addEntry(java.lang.String name, Address address)
        throws java.rmi.RemoteException
    {
        impl.addEntry(name, address);
    }
}

(実際のスケルトンは、実際はもっと大きくなります。簡単にするために、スケルトンの基本部分のみを示しています。)

このスケルトンは、AddressBookサービスの実装を持ちます。この実装は、スケルトンのコンストラクタに渡されるか、生成された実装のインスタンスが生成されます。AXISエンジンが、スケルトンのaddEntryメソッドを呼び出すと、実際の実装のaddEntryの呼び出しに単純に委譲します。

実装テンプレート記述

WSDL2Javaは、バインディングから実装テンプレートも生成します。

public class AddressBookSOAPBindingImpl implements AddressBook {

    public void addEntry(String name, Address address) throws java.rmi.RemoteException {     } }

このテンプレートは、実際には、試験的な実装として使用されますが、見て分かるように、何も行いません。これは、サービス作成者がこのテンプレートから実装部分を埋めることが意図されています。

WSDL2Javaが、(--server-sideフラグ経由で)実装テンプレートを作成するかを問い合わされる際、未だ存在していない場合に限り生成します。この実装が既に存在する場合、上書きされません。

サービス

このツールは、AdminClientで使用するための各サービスにおける"deploy.wsdd"と"undeploy.wsdd"も作成します。一度、実装クラスのメソッドを埋め、コードをコンパイルし、Axisエンジンで利用可能なクラスを作成すると、これらのファイルは、サービスを配備するために使用することができます。

Java2WSDL: JavaからWSDLを構築

Java2WSDLエミッタとWSDL2Javaエミッタは、新規Webサービスの開発を容易にします。以下の節では、JavaインタフェースからWeb サービスを構築する方法を段階毎に説明します。
 

ステップ1: Javaインタフェースもしくはクラスを提供

Webサービスを記述するJavaインタフェース(もしくはクラス)を作成し、コンパイルして下さい。以下に、部品価格の設定、問い合わせに使用できるWebサービスを記述するインタフェースのサンプルを示します(samples/userguide/example6/WidgetPrice.java):
package samples.userguide.example6;

/**  * Interface describing a web service to set and get Widget prices.  **/ public interface WidgetPrice {     public void setWidgetPrice(String widgetName, String price);     public String getWidgetPrice(String widgetName); }

注意: デバッグ情報を付けてクラスのコンパイルを行った場合、Java2WSDLは、メソッドのパラメータ名を得るためにデバッグ情報を使用します。

ステップ2: Java2WSDL使用してWSDLを作成

上のインタフェースからWSDLファイルを作成するためにJava2WSDLツールを使用して下さい。

以下に、上の節で記載したインタフェースからwsdlファイル(wp.wsdl)を生成する呼び出しの例を示します:

% java org.apache.axis.wsdl.Java2WSDL -o wp.wsdl  -l"http://localhost:8080/axis/services/WidgetPrice" -n  "urn:Example6" -p"samples.userguide.example6" "urn:Example6"  samples.userguide.example6.WidgetPrice

ここで:

出力WSDL文書には、SOAP rpc、WebサービスのエンコーディングをサポートするためのWSDL型、メッセージ、portType、バインディング、サービス記述が含まれます。指定されたインタフェースのメソッドが他のクラスを参照する場合、Java2WSDLツールは、そのクラス、および、入れ子状/継承された型を表現するための適切なXML型を生成します。このツールは、JAX-RPC複合型(Beanクラス)、拡張クラス、列挙クラス、配列、ホルダクラスをサポートします。

Java2WSDLツールには、更に多くのオプションがあります。詳細は、リファレンスガイドで説明されています。

ステップ3: WSDL2Javaを使用してバインディングを作成

Webサービス用の適切なクライアント/サーババインディングを構築するために、作成したWSDLファイルを使用します(WSDL2Javaを参照して下さい):
% java org.apache.axis.wsdl.WSDL2Java -o . -d Session -s -S true  -Nurn:Example6 samples.userguide.example6 wp.wsdl

これは、以下のファイルを生成します:

これで、クライアント/サーバサイドのコードの構築、および、Webサービスの配備に必要なファイルがすべて揃いました!

公開されたAxisインタフェース

Axisに存在する任意のインタフェースとクラスを使用することができますが、その一部分が他に比べてより安定していることに注意して下さい。これは、モジュール性を保守、改良するためにAxisのリファクタリングが継続して必要なためです。

一部のインタフェースは、公開されるものとして設計されていますので、比較的安定しています。Axisのリファクタリングの際、Axisの開発者は、公開されたインタフェースに対する不必要な変更を避け、ユーザへの変更の影響を十分に考慮します。

そのため、公開されたインタフェースのみを使用するように厳守していれば、Axisのリリースを変更する際の痛手が最小化できます。一方、非公開のインタフェースの使用を決めた場合は、リリース間での移行はかなりの練習になるでしょう!インタフェースを公開させたい場合は、axis-userメーリングリストにて議題すべきです。

現時点の公開されたインタフェースの一覧は、以下です:

初心者向けティップス: 問題解決方法の見つけ方

ユーザガイドを一通り読み、最初の.jwsサービスを作成し、全て完璧にうまくいったことでしょう!さあ、これで実際のプロジェクトに取り掛かる時です。ここでユーザガイドでは説明されていないが必要となる何らかの性質に出会うでしょう。それは単純なことです。Axisのどこかにあることが分かっているが、何によって呼び出されるかどのような時に得られるかが分からないということです。この節は、検索を始める場所を示すことを意図しています。

いと口を探す場所

ここには大分類があります。

クラスが知りたい

org.apache.axis.MessageContext

Axis Webサービスにおける、ほとんどの "...は、どこで見ることができますか" という問いへの答えは、"MessageContextの中"です。基本的に、与えられた要求/応答に関してAxisが理解していることは全ては、MessageContext経由で取り出すことができます。Axisは、以下を保存しています:

サービスの内部からは、現在のMessageContextオブジェクトは、スタティックメソッド MessageContext.getCurrentContext()を使用して常に利用可能です。これにより、MessageContextへの明示的な参照を持たないRPCサービス内からであっても、要求および応答メソッドに対して必要な変更を行うことができます。

org.apache.axis.Message

org.apache.axis.Messageオブジェクトは、AxisにおけるSOAPメッセージの表現です。要求および応答メッセージは、上述のMessageContextから取り出すことができます。Messageは以下を持ちます:

org.apache.axis.SOAPEnvelope

これまで示したように、MessageContextから始めることにより、作業をAPIを辿って堀り下げ、1つの要求/応答の交換について利用可能な全ての情報を見つけました。MessageContextは、2つのメッセージがあり、それぞれ、SOAPEnvelopeを含んだSOAPPartを持ちます。SOAPEnvelopeは、順番に、通信路上に送信されたSOAPエンベロープの完全な表現を保持します。ここから、SOAPヘッダとSOAPボディの内容を取り出したり設定したりすることができます。利用可能なプロパティの全リストについては、Javadocを参照して下さい。

付録 : Axis TCPモニタ(tcpmon)の使用

org.apache.axis.utilsパッケージに、"tcpmon"ユーティリティがあります。これは、コマンドラインから以下のように実行します:
% java org.apache.axis.utils.tcpmon [listenPort targetHost targetPort]
引数を全く指定しなかった場合、以下のような GUI が現れます:

このプログラムを使用するためには、入力方向の接続でtcpmonが監視するローカルポート、その接続をフォワードする対象ホスト、"トンネルする"対象ホストのポート番号を選択しなければなりません。そして、"add" をクリックして下さい。新規のトンネルされた接続用のウィンドウ内に現れるその他のタブに注目して下さい。このパネルには以下のようなものが表示されます:

ローカルポートへのSOAP接続が新規に行われる度に、"Request"パネルに要求が表示され、"Response"パネルにサーバからの応答が表示されます。tcpmonは、全ての要求/応答の組み合わせをログし、トップパネル内の項目を選択することで、任意の特定の組み合わせを表示することができます。選択した項目の削除、全ての削除、後で参照するためにファイルに保存することを行うこともできます。

"resend"ボタンは、現在参照している要求を再送信し、新しい応答を記録します。再送信する前に要求ウィンドウ内のXMLを編集できるので、これは特に便利です。そのため、この素晴らしいツールを使用して、SOAPサーバに対して異なるXMLを与えた場合の影響を試験することができます。編集した要求を再送信する前に、Content-length HTTPヘッダを変更する必要があることに注意して下さい。

付録: SOAPモニタの使用

Webサービス開発者は、Webサービスを呼び出す時に使用されるSOAPメッセージから、それらのメッセージの結果までの確認が必要になることがあります。SOAPモニタユーティリティの最終目的は、このような開発者に、特殊な設定やサーバの再起動なしにSOAPメッセージをモニタする手段を提供することです。

このユーティリティでは、ハンドラが作成され、グローバルなハンドラチェインに追加されます。SOAP要求と応答を受け取った時と同様に、SOAPメッセージの情報は、Webブラウザインタフェースを使用して表示させることができるように、SOAPモニタサービスへフォワードされます。

SOAPメッセージの情報は、http://localhost:<port>/axis/SOAPMonitorにアクセスすることで、Webブラウザからアクセスすることができます(ここで、<port>は、アプリケーションサーバが稼働しているポート番号です)。

SOAPメッセージの情報は、SOAPモニタサービスへのソケット接続を開くアプレットを使用することによって、Webブラウザで表示することができます。このアプレットは、Javaプラグイン1.3以上がブラウザにインストールされていることを要求します。正しいプラグインが導入されていない場合は、ブラウザはインストールを促します。

アプレットとの通信用のSOAPモニタサービスで使用されるポートは、設定変更可能です。使用するポートを変更するには、AXIS Webアプリケーション用のweb.xmlファイルを編集して下さい。

用語集

ハンドラ
ある独自の方法によるMessageContextの処理に責任を負う、再利用可能なクラスです。Axisエンジンは、要求がクライアントまたは転送リスナから届いた時、一連のハンドラを呼び出します。
SOAP
シンプル オブジェクト アクセス プロトコル (ご存知のとおり、シンプルとはいえないことも、オブジェクトとまったく関係ないことも行っているという事実とは反しています :))。http://www.w3.org/TR/SOAPで、SOAP 1.1仕様を読むことができます。W3Cでは現在、XMLプロトコルグループの援助の元で、SOAP 1.2についての作業を行っています。
プロバイダ
プロバイダは、所定のSOAP操作のついての実際の"内容"の実行に責任を負う"バックエンド"ハンドラです。通常、これはあるバックエンドサービスオブジェクトのメソッドを呼び出すことを意味します。2つのよく使用されるプロバイダは、RPCProviderとMsgProviderで、どちらもorg.apache.axis.providers.javaパッケージにあります。


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