13.4.2. JAX-WS クライアントアプリケーションの開発
サービス
- 概要
Serviceは WSDL サービスを表す抽象化です。WSDL サービスは関連ポートの集合で、それぞれには特定のプロトコルおよび特定のエンドポイントアドレスにバインドされたポート型が含まれます。通常、サービスは既存の WSDL コントラクトから残りのコンポーネントスタブが生成されるときに生成されます。WSDL コントラクトはデプロイされたエンドポイントの WSDL URL を介して利用できます。もしくはEAP_HOME/bin/ディレクトリーでwsprovide.shコマンドを使用してエンドポイントソースから作成することもできます。このようなタイプの使用法は静的ユースケースと呼ばれます。この場合、コンポーネントスタブの 1 つとして作成されたServiceクラスのインスタンスを作成します。Service.createメソッドを使用して、手動でサービスを作成することも可能です。このような使用法は 動的ユースケースと呼ばれます。- 使用法
- 静的ユースケース
- JAX-WS クライアントの静的ユースケースは WSDL コントラクトがすでに存在することを前提としています。WDSL コントラクトは、外部ツールで生成するか、AX-WS エンドポイントの作成時に正しい JAX-WS アノテーションを使用して生成します。コンポーネントスタブを生成するには、
EAP_HOME/bin/に格納されたwsconsume.shまたはwsconsume.batのスクリプトを使用します。スクリプトは、WSDL URL またはファイルをパラメーターとして取り、ディレクトリーツリー構造の複数のファイルを生成します。Serviceを表すソースおよびクラスのファイルはそれぞれCLASSNAME_Service.javaとCLASSNAME_Service.classと名付けられます。生成された実装クラスには、引数のないパブリックコンストラクターと 2 つの引数を持つパブリックコンストラクターの 2 つがあります。2 つの引数は WSDL の場所 (java.net.URL) とサービス名 (javax.xml.namespace.QName) を表します。引数のないコンストラクターは最も頻繁に使用されます。この場合、WSDL の場所とサービス名は WSDL に記述された設定となります。これらは、生成されたクラスをデコレートする@WebServiceClientアノテーションから暗黙的に設定されます。例13.19 生成されたサービスクラスの例
@WebServiceClient(name="StockQuoteService", targetNamespace="http://example.com/stocks", wsdlLocation="http://example.com/stocks.wsdl") public class StockQuoteService extends javax.xml.ws.Service { public StockQuoteService() { super(new URL("http://example.com/stocks.wsdl"), new QName("http://example.com/stocks", "StockQuoteService")); } public StockQuoteService(String wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } ... } - 動的ユースケース
- 動的なケースでは、スタブは自動的には生成されず、代わりに Web サービスクライアントが
Service.createメソッドを使用してServiceインスタンスを作成します。以下のコードフラグメントは、このプロセスの例を示しています。例13.20 手動によるサービスの作成
URL wsdlLocation = new URL("http://example.org/my.wsdl"); QName serviceName = new QName("http://example.org/sample", "MyService"); Service service = Service.create(wsdlLocation, serviceName);
- ハンドラーリゾルバー
- JAX-WS は、ハンドラー として知られるメッセージ処理モジュール向けの柔軟性の高いプラグインフレームワークを提供します。このようなハンドラーにより、JAX-WS ランタイムシステムの機能が拡張されます。
Serviceインスタンスは、サービス、ポート、またはプロトコルバインディングごとにハンドラーのセットを設定できるgetHandlerResolverメソッドとsetHandlerResolverメソッドのペアを介してHandlerResolverへのアクセスを提供します。ServiceインスタンスがプロキシまたはDispatchインスタンスを作成する際には、現在サービスに登録されているハンドラーリゾルバーによって必要なハンドラーチェーンが作成されます。Serviceインスタンス用に設定されたハンドラーリゾルバーがそれ以降に変更されても、以前に作成されたプロキシやDispatchインスタンスには影響を及ぼしません。 - エグゼキューター
Serviceインスタンスはjava.util.concurrent.Executorを使用して設定できます。Executorはアプリケーションが要求する任意の非同期コールバックを呼び出します。ServiceのsetExecutorメソッドとgetExecutorのメソッドはサービス用に設定されたExecutorを変更および取得できます。
動的プロキシ とは、Service で提供される getPort メソッドの 1 つを使用するクライアントプロキシのインスタンスです。portName は、サービスが使用する WSDL ポートの名前を指定します。serviceEndpointInterface は、作成された動的プロキシインスタンスのサポートするサービスエンドポイントインターフェースを指定します。
例13.21 getPort メソッド
public <T> T getPort(QName portName, Class<T> serviceEndpointInterface)
public <T> T getPort(Class<T> serviceEndpointInterface)
wsconsume.sh コマンドを使用して生成されます。これにより WSDL が解析されて、Java クラスが作成されます。
例13.22 サービスポートの戻り値
@WebServiceClient(name = "TestEndpointService", targetNamespace = "http://org.jboss.ws/wsref",
wsdlLocation = "http://localhost.localdomain:8080/jaxws-samples-webserviceref?wsdl")
public class TestEndpointService extends Service
{
...
public TestEndpointService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
@WebEndpoint(name = "TestEndpointPort")
public TestEndpoint getTestEndpointPort()
{
return (TestEndpoint)super.getPort(TESTENDPOINTPORT, TestEndpoint.class);
}
}
@WebServiceRef
@WebServiceRef アノテーションは Web サービスの参照を宣言します。これは http://www.jcp.org/en/jsr/summary?id=250 で定義されている javax.annotation.Resource アノテーションにより示されるリソースパターンに従います。
@WebServiceRef のユースケース
- このアノテーションを使用して、型が生成された
Serviceクラスである参照を定義できます。この場合、型要素と値要素はそれぞれ生成されたServiceクラス型を参照します。また、アノテーションが適用されるフィールドまたはメソッドの宣言によって参照型を推定できる場合、型要素および値要素にデフォルト値のObject.classを使用できますが、必須ではありません。型が推測できない場合は、少なくとも型要素をデフォルトでない値で示す必要があります。 - このアノテーションを使用して型が SEI の参照を定義できます。この場合、アノテーションが付いたフィールドまたはメソッド宣言から参照型を推定できる場合、型要素にデフォルト値を使用できますが、必須ではありません。ただし、値要素は常に存在する必要があり、
javax.xml.ws.Serviceのサブタイプである生成されたサービスクラス型を参照する必要があります。wsdlLocation要素がある場合は、参照される生成されたサービスクラスの@WebServiceアノテーションで指定された WSDL の場所情報をオーバーライドします。例13.23
@WebServiceRefの例public class EJB3Client implements EJB3Remote { @WebServiceRef public TestEndpointService service4; @WebServiceRef public TestEndpoint port3;
XML Web Services は、Java EE コンテナ内にデプロイされたエンドポイントと任意のクライアントとの間の通信に XML メッセージを使用します。XML メッセージは Simple Object Access Protocol (SOAP) と呼ばれる XML 言語を使用します。JAX-WS API は、エンドポイントとクライアントがそれぞれ SOAP メッセージを送受信し、SOAP メッセージから Java (および Java から SOAP メッセージ) へ変換できるようにするメカニズムを提供します。これは marshalling および unmarshalling と呼ばれます。
Dispatch クラスはこの機能を提供します。Dispatch は、以下の定数の 1 つで特定される 2 つの使用モードの 1 つで動作します。
javax.xml.ws.Service.Mode.MESSAGE- このモードは、クライアントアプリケーションがプロトコル固有のメッセージ構造を使用して直接連動するように指示します。SOAP プロトコルバインディングと併用すると、クライアントアプリケーションは SOAP メッセージと直接連動します。javax.xml.ws.Service.Mode.PAYLOAD- このモードを使用すると、クライアントはペイロード自体と連動します。たとえば、 SOAP プロトコルバインディングと併用した場合、クライアントアプリケーションは SOAP メッセージ全体ではなく、SOAP ボディのコンテンツと連動します。
Dispatch は、メッセージまたはペイロードを XML として構築する必要がある低レベルの API で、各プロトコルの標準やメッセージまたはペイロード構造の詳細知識に準拠します。Dispatch は、あらゆるタイプのメッセージまたはメッセージペイロードの入出力をサポートする、汎用クラスです。
例13.24 Dispatch の使用法
Service service = Service.create(wsdlURL, serviceName);
Dispatch dispatch = service.createDispatch(portName, StreamSource.class, Mode.PAYLOAD);
String payload = "<ns1:ping xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>";
dispatch.invokeOneWay(new StreamSource(new StringReader(payload)));
payload = "<ns1:feedback xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>";
Source retObj = (Source)dispatch.invoke(new StreamSource(new StringReader(payload)));
BindingProvider インターフェースはクライアントが使用可能なプロトコルバインディングを提供するコンポーネントを表します。これはプロキシによって実装され、Dispatch インターフェースによって拡張されます。
BindingProvider インスタンスは非同期オペレーション機能を提供することが可能です。非同期オペレーション呼び出しは、呼び出し時に BindingProvider インスタンスから切り離されます。オペレーション完了時には、応答コンテキストは更新されず、その代わりに Response インターフェースを使用して別の応答コンテキストを利用できるようになります。
例13.25 非同期呼び出しの例
public void testInvokeAsync() throws Exception
{
URL wsdlURL = new URL("http://" + getServerHost() + ":8080/jaxws-samples-asynchronous?wsdl");
QName serviceName = new QName(targetNS, "TestEndpointService");
Service service = Service.create(wsdlURL, serviceName);
TestEndpoint port = service.getPort(TestEndpoint.class);
Response response = port.echoAsync("Async");
// access future
String retStr = (String) response.get();
assertEquals("Async", retStr);
}
@Oneway 呼び出し
@Oneway アノテーションは、所定の Web メソッドが入力メッセージを受け取っても出力メッセージは返さないことを表します。通常、@Oneway メソッドは、ビジネスメソッドが実行される前に、制御のスレッドを呼び出し元アプリケーションに返します。
例13.26 @Oneway 呼び出しの例
@WebService (name="PingEndpoint")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class PingEndpointImpl
{
private static String feedback;
@WebMethod
@Oneway
public void ping()
{
log.info("ping");
feedback = "ok";
}
@WebMethod
public String feedback()
{
log.info("feedback");
return feedback;
}
}
HTTP 接続のタイムアウト動作およびメッセージの受信を待つクライアントのタイムアウトは 2 つの異なるプロパティーによって制御されます。HTTP 接続のタイムアウト動作を制御するプロパティーは javax.xml.ws.client.connectionTimeout で、javax.xml.ws.client.receiveTimeout はメッセージの受信を待つクライアントのタイムアウトを制御します。各プロパティーはミリ秒で指定されます。正しい構文は次のとおりです。
例13.27 JAX-WS タイムアウト設定
public void testConfigureTimeout() throws Exception
{
//Set timeout until a connection is established
((BindingProvider)port).getRequestContext().put("javax.xml.ws.client.connectionTimeout", "6000");
//Set timeout until the response is received
((BindingProvider) port).getRequestContext().put("javax.xml.ws.client.receiveTimeout", "1000");
port.echo("testTimeout");
}