13.4.2. JAX-WS クライアントアプリケーションの開発

本トピックでは JAX-WS Web Service クライアントについて概説します。クライアントは JAX-WS エンドポイントと通信し、そこから作業を要求します。 JAX-WS エンドポイントは Java Enterprise Edition 6 コンテナにデプロイされています。以下で説明するクラス、メソッド、およびその他の実装に関する詳しい情報は、「JAX-WS の共通 API リファレンス」 ならびに JBoss Enterprise Application Platform 6 に同梱されている Javadocs の該当箇所を参照してください。

サービス

概要
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.javaCLASSNAME_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 はアプリケーションが要求する任意の非同期コールバックを呼び出します。ServicesetExecutor メソッドと 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 クラスが作成されます。
ポートを返す、型指定されたメソッドも提供されます。このようなメソッドは、SEI を実装する動的プロキシも返します。以下の例を参照してください。

例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;
    
Dispatch

XML Web Services は、Java EE コンテナ内にデプロイされたエンドポイントと任意のクライアントとの間における通信に XML メッセージを使用します。XML メッセージでは Simple Object Access Protocol (SOAP) と呼ばれる XML 言語を採用しています。JAX-WS API は、エンドポイントとクライアントがそれぞれ SOAP メッセージを送受信し、SOAP メッセージから Java への変換およびその逆の変換を行うことを可能にするメカニズムを提供します。これは marshalling および unmarshalling と呼ばれています。

場合によっては、変換の結果ではなく、raw SOAP メッセージ自体にアクセスする必要があります。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");
}

このページには機械翻訳が使用されている場合があります (詳細はこちら)。