13.4.2. JAX-WS クライアントアプリケーションの開発
サービス
- 概要
Service
は WSDL サービスを表す抽象化です。WSDL サービスは関連ポートの集合で、それぞれには特定のプロトコルおよび特定のエンドポイントアドレスにバインドされたポート型が含まれます。通常サービスは、既存の WSDL コントラクトから残りのコンポーネントスタブが生成される時に生成されます。WSDL コントラクトはデプロイされたエンドポイントの WSDL URL を介して利用することができます。もしくはEAP_HOME/bin/
ディレクトリでwsprovide.sh
コマンドを使用してエンドポイントソースから作成することもできます。このようなタイプの使用法は 静的 ユースケースと呼ばれています。この場合、コンポーネントスタブの一つとして作成されたService
クラスのインスタンスを作成します。サービスはService.create
メソッドを使用して手動で作成することも可能です。このような使用法は 動的 ユースケースと呼ばれています。- 使用法
- 静的ユースケース
- JAX-WS クライアントの 静的 ユースケースは WSDL コントラクトが既にあることを前提としています。これは、外部ツールで生成したり、 JAX-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
メソッドの一つを使用するクライアントプロキシのインスタンスです。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 への変換およびその逆の変換を行うことを可能にするメカニズムを提供します。これは marshalling
および unmarshalling
と呼ばれています。
Dispatch
クラスはこの機能を提供します。Dispatch
は 2 つの使用モードで動作し、次にあげる定数のいずれか一方により特定されます。
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 つの異なるプロパティによって制御されます。第 1 のプロパティは javax.xml.ws.client.connectionTimeout
、第 2 のプロパティは 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"); }