3.3. JAX-WS Web サービスクライアント
3.3.1. JAX-WS Web サービスの消費とアクセス
Web サービスエンドポイントの作成後、手動で、あるいは JAX-WS アノテーションを使用して、その WSDL にアクセスできます。XML ベースの Web Services の Jakarta EE は Jakarta Enterprise Web Services 1.4 仕様 です。これは、Web サービスと通信する基本的なクライアントアプリケーションを作成するのに使用できます。パブリッシュされた WSDL から Java コードを生成するプロセスは、Web サービスを使用すると呼ばれます。これは以下の場合に発生します。
クライアントアーティファクトの作成
クライアントアーティファクトを作成する前に、WSDL コントラクトを作成する必要があります。以下の WSDL コントラクトは、本セクションの残りの部分に記載の例に使用します。
以下の例は、ProfileMgmtService.wsdl
ファイルにこの WSDL コントラクトがあることを前提としています。
<definitions name='ProfileMgmtService' targetNamespace='http://org.jboss.ws/samples/retail/profile' xmlns='http://schemas.xmlsoap.org/wsdl/' xmlns:ns1='http://org.jboss.ws/samples/retail' xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/' xmlns:tns='http://org.jboss.ws/samples/retail/profile' xmlns:xsd='http://www.w3.org/2001/XMLSchema'> <types> <xs:schema targetNamespace='http://org.jboss.ws/samples/retail' version='1.0' xmlns:xs='http://www.w3.org/2001/XMLSchema'> <xs:complexType name='customer'> <xs:sequence> <xs:element minOccurs='0' name='creditCardDetails' type='xs:string'/> <xs:element minOccurs='0' name='firstName' type='xs:string'/> <xs:element minOccurs='0' name='lastName' type='xs:string'/> </xs:sequence> </xs:complexType> </xs:schema> <xs:schema targetNamespace='http://org.jboss.ws/samples/retail/profile' version='1.0' xmlns:ns1='http://org.jboss.ws/samples/retail' xmlns:tns='http://org.jboss.ws/samples/retail/profile' xmlns:xs='http://www.w3.org/2001/XMLSchema'> <xs:import namespace='http://org.jboss.ws/samples/retail'/> <xs:element name='getCustomerDiscount' nillable='true' type='tns:discountRequest'/> <xs:element name='getCustomerDiscountResponse' nillable='true' type='tns:discountResponse'/> <xs:complexType name='discountRequest'> <xs:sequence> <xs:element minOccurs='0' name='customer' type='ns1:customer'/> </xs:sequence> </xs:complexType> <xs:complexType name='discountResponse'> <xs:sequence> <xs:element minOccurs='0' name='customer' type='ns1:customer'/> <xs:element name='discount' type='xs:double'/> </xs:sequence> </xs:complexType> </xs:schema> </types> <message name='ProfileMgmt_getCustomerDiscount'> <part element='tns:getCustomerDiscount' name='getCustomerDiscount'/> </message> <message name='ProfileMgmt_getCustomerDiscountResponse'> <part element='tns:getCustomerDiscountResponse' name='getCustomerDiscountResponse'/> </message> <portType name='ProfileMgmt'> <operation name='getCustomerDiscount' parameterOrder='getCustomerDiscount'> <input message='tns:ProfileMgmt_getCustomerDiscount'/> <output message='tns:ProfileMgmt_getCustomerDiscountResponse'/> </operation> </portType> <binding name='ProfileMgmtBinding' type='tns:ProfileMgmt'> <soap:binding style='document' transport='http://schemas.xmlsoap.org/soap/http'/> <operation name='getCustomerDiscount'> <soap:operation soapAction=''/> <input> <soap:body use='literal'/> </input> <output> <soap:body use='literal'/> </output> </operation> </binding> <service name='ProfileMgmtService'> <port binding='tns:ProfileMgmtBinding' name='ProfileMgmtPort'> <!-- service address will be rewritten to actual one when WSDL is requested from running server --> <soap:address location='http://SERVER:PORT/jaxws-retail/ProfileMgmtBean'/> </port> </service> </definitions>
JAX-WS アノテーションを使用して Web サービスのエンドポイントを作成すると、WSDL コントラクトは自動的に生成されます。その URL のみが必要になります。この URL を見つけるには、Runtime に移動して、該当するサーバーを選択し、Webservicesを 選択してエンドポイントを選択します。
Wsconsume.sh
ツールまたは wsconsume.bat
ツールは、抽象的なコントラクト (WSDL) を消費し、アノテーション付きの Java クラスと、これを定義するオプションのソースを生成するために使用されます。このファイルは EAP_HOME/bin/
ディレクトリーにあります。
$ ./wsconsume.sh --help WSConsumeTask is a cmd line tool that generates portable JAX-WS artifacts from a WSDL file. usage: org.jboss.ws.tools.cmd.WSConsume [options] <wsdl-url> options: -h, --help Show this help message -b, --binding=<file> One or more JAX-WS or Java XML Binding files -k, --keep Keep/Generate Java source -c --catalog=<file> Oasis XML Catalog file for entity resolution -p --package=<name> The target package for generated source -w --wsdlLocation=<loc> Value to use for @WebService.wsdlLocation -o, --output=<directory> The directory to put generated artifacts -s, --source=<directory> The directory to put Java source -t, --target=<2.0|2.1|2.2> The JAX-WS target -q, --quiet Be somewhat more quiet -v, --verbose Show full exception stack traces -l, --load-consumer Load the consumer and exit (debug utility) -e, --extension Enable SOAP 1.2 binding extension -a, --additionalHeaders Enable processing of implicit SOAP headers -n, --nocompile Do not compile generated sources
以下のコマンドは、出力に一覧表示されている .java
ファイルを ProfileMgmtService.wsdl
ファイルから生成します。ソースは、-p
スイッチで指定されたパッケージのディレクトリー構造を使用します。
[user@host bin]$ wsconsume.sh -k -p org.jboss.test.ws.jaxws.samples.retail.profile ProfileMgmtService.wsdl output/org/jboss/test/ws/jaxws/samples/retail/profile/Customer.java output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountRequest.java output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountResponse.java output/org/jboss/test/ws/jaxws/samples/retail/profile/ObjectFactory.java output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmt.java output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmtService.java output/org/jboss/test/ws/jaxws/samples/retail/profile/package-info.java output/org/jboss/test/ws/jaxws/samples/retail/profile/Customer.class output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountRequest.class output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountResponse.class output/org/jboss/test/ws/jaxws/samples/retail/profile/ObjectFactory.class output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmt.class output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmtService.class output/org/jboss/test/ws/jaxws/samples/retail/profile/package-info.class
.java
ソースファイルとコンパイルされた .class
ファイルはどちらも、コマンドを実行するディレクトリー内の output/
ディレクトリーに生成されます。
表3.3 wsconsume.sh
で作成されるアーティファクトの説明
ファイル | 説明 |
---|---|
| サービスエンドポイントインターフェイス。 |
| カスタムデータタイプ。 |
| カスタムデータタイプ。 |
| JAXB XML レジストリー。 |
| JAXB パッケージのアノテーション。 |
| サービスファクトリー。 |
wsconsume
コマンドは、すべてのカスタムデータタイプ (JAXB アノテーションが付けられたクラス)、サービスエンドポイントインターフェイス、およびサービスファクトリークラスを生成します。これらのアーティファクトは、Web サービスクライアントの実装をビルドするために使用されます。
サービススタブの設定
Web サービスクライアントはサービススタブを使用して、リモート Web サービス呼び出しの詳細を抽象化します。クライアントアプリケーションの場合、Web サービスの呼び出しは他のビジネスコンポーネントの呼び出しと同様になります。この場合、サービスエンドポイントインターフェイスはビジネスインターフェイスとして機能し、サービスファクトリークラスはサービススタブとしての構築に使用されません。
以下の例では、まずは WSDL の場所とサービス名を使用してサービスファクトリーを作成します。次に、 wsconsume
により作成されたサービスエンドポイントインターフェイスを使用してサービススタブを構築します。最後に、スタブは他のビジネスインターフェイスと同様に使用できます。
JBoss EAP 管理コンソールでは、エンドポイントの WSDL URL を確認できます。この URL を見つけるには、Runtime に移動して、該当するサーバーを選択し、Webservicesを 選択してエンドポイントを選択します。
import javax.xml.ws.Service; [...] Service service = Service.create( new URL("http://example.org/service?wsdl"), new QName("MyService") ); ProfileMgmt profileMgmt = service.getPort(ProfileMgmt.class); // Use the service stub in your application
3.3.2. JAX-WS クライアントアプリケーションの開発
クライアントは Java Enterprise Edition 7 コンテナーにデプロイされた JAX-WS エンドポイントと通信し、そこから要求を行います。以下のクラス、メソッド、およびその他の実装の詳細は、JBoss EAP に含まれる Javadocs バンドルの関連セクションを参照してください。
概要
Service
は、WSDL サービスを表す抽象化です。WSDL サービスは、一連の関連ポートです。それぞれには、特定のプロトコルとエンドポイントアドレスに結合したポートタイプが含まれます。
通常、サービスの生成は、残りのコンポーネントのスタブが、既存の WSDL コントラクトから生成されると行われます。WSDL コントラクトは、デプロイされたエンドポイントの WSDL URL 経由で利用できます。または、EAP_HOME/bin/
ディレクトリーの wsprovide
ツールを使用してエンドポイントソースから作成できます。
このタイプの使用は、静的なユースケースと呼ばれます。この場合、コンポーネントのスタブのいずれかとして作成された Service
クラスのインスタンスを作成します。
Service.create
メソッドを使用してサービスを手動で作成することもできます。これは、動的なユースケースと呼ばれます。
使用方法
静的ユースケース
JAX-WS クライアントの静的ユースケースでは、すでに WSDL コントラクトが存在することを前提としています。これは外部ツールで生成されるか、JAX-WS エンドポイントの作成時に適切な JAX-WS アノテーションを使用して生成される可能性があります。
コンポーネントのスタブを生成するには、EAP_HOME/bin
に含まれる wsconsume
ツールを使用します。このツールは、コンテナー URL またはファイルをパラメーターとして取り、ディレクトリーツリーで構造化された複数のファイルを生成します。Service
を表すソースおよびクラスファイルはそれぞれ _Service.java
と _Service.class
です。
生成された実装クラスには、引数のないパブリックコンストラクターと、2 つの引数を持つコンストラクターがあります。これら 2 つの引数はそれぞれ WSDL の場所 (java.net.URL
) とサービス名 (javax.xml.namespace.QName
) を表します。
引数なしのコンストラクターは最もよく使用されます。この場合、WSDL の場所とサービス名は WSDL にあるものです。これらは、生成されたクラスをデコレートする @WebServiceClient
アノテーションから暗黙的に設定されます。
@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); } ... }
サービスからポートを取得する方法と、ポートでの操作の呼び出し方法に関する詳細は、Dynamic Proxy を参照してください。XML ペイロードを直接使用するか、SOAP メッセージ全体の XML 表現を使用する方法は、Dispatch を参照してください。
動的ユースケース
動的の場合、スタブは自動的に生成されません。代わりに、Web サービスクライアントは Service.create
メソッドを使用して Service
インスタンスを作成します。以下のコードはこのプロセスを示しています。
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
インスタンスでのハンドラーには影響を与えません。
Executor
Service
インスタンスは、java.util.concurrent.Executor
を使用して設定できます。Executor
は、アプリケーションによってリクエストされた非同期コールバックを呼び出します。Service
の setExecutor
および getExecutor
メソッドは、サービスに設定された Executor
を変更および取得できます。
動的プロキシー
動的プロキシーは、Service
で提供される getPort
メソッドのいずれかを使用するクライアントプロキシーのインスタンスです。portName
は、サービスが使用する WSDL ポートの名前を指定します。serviceEndpointInterface
は、作成された動的プロキシーインスタンスでサポートされるサービスエンドポイントインターフェイスを指定します。
public <T> T getPort(QName portName, Class<T> serviceEndpointInterface) public <T> T getPort(Class<T> serviceEndpointInterface)
Service Endpoint Interface は通常 wsconsume
ツールを使用して生成されます。これは、WSDL を解析し、そこから Java クラスを作成します。
ポートを返す、タイプ指定されたメソッドも提供されます。これらのメソッドは、SEI を実装する動的プロキシーも返します。以下の例を参照してください。
@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 サービスへの参照を宣言します。これは、JSR 250 で定義された javax.annotation.Resource
アノテーションによって示されるリソースパターンに従います。これらのアノテーションの Jakarta EE と同等の Jakarta EE は、Jakarta Annotations 1.3 仕様 に含まれています。
-
これを使用して、生成される
Service
クラスであるタイプのリファンレスを定義できます。この例では、type および value 要素はそれぞれ、生成されたService
クラスターイプを参照します。さらに、リファンレンスタイプがアノテーションが適用されるフィールドまたはメソッド宣言によって推定される場合、type および value 要素にはデフォルト値のObject.class
が設定されることがあります。これは必須ではありません。タイプを推定できない場合、type 要素がデフォルト以外の値を持つ必要があります。 これは、タイプが SEI のリファンレスを定義するのに使用できます。この場合、リファレンスのタイプが annotated フィールドまたは method 宣言から推測される場合、type 要素はデフォルト値で存在することがあります (ただし、必須ではありません)。ただし、value 要素は常に存在し、
javax.xml.ws.Service
ファイルのサブタイプである、生成されたサービスクラスターイプを参照する必要があります。wsdlLocation
要素が存在する場合は、参照される生成サービスクラスの@WebService
アノテーションで指定された WSDL の場所情報を上書きします。public class EJB3Client implements EJB3Remote { @WebServiceRef public TestEndpointService service4; @WebServiceRef public TestEndpoint port3; }
Dispatch
XML Web サービスは、Java EE コンテナーおよびすべてのクライアントにデプロイされる、エンドポイント間の通信に XML メッセージを使用します。XML メッセージは、Simple Object Access Protocol (SOAP) と呼ばれる XML 言語を使用します。JAX-WS API は、エンドポイントとクライアントのメカニズムを提供し、各エンドポイントが SOAP メッセージを送受信できるようにします。マーシャリングは、Java オブジェクトを SOAP XML メッセージに変換するプロセスです。マーシャリング解除とは、SOAP XML メッセージを Java オブジェクトに変換するプロセスのことです。
変換の結果ではなく、raw SOAP メッセージ自体にアクセスする必要がある場合があります。Dispatch
クラスはこの機能を提供します。Dispatch
は、以下の定数のいずれかによって識別される、2 つの使用モードのいずれかで動作します。
-
javax.xml.ws.Service.Mode.MESSAGE
: このモードは、クライアントアプリケーションがプロトコル固有のメッセージ構造で直接動作するようにします。SOAP プロトコルバインディングを使用すると、クライアントアプリケーションは SOAP メッセージで直接機能します。 -
javax.xml.ws.Service.Mode.PAYLOAD
: このモードでは、クライアントがペイロード自体と動作します。たとえば、SOAP プロトコルバインディングと使用すると、クライアントアプリケーションは SOAP メッセージ全体ではなく SOAP ボディーのコンテンツで動作します。
Dispatch
は低レベルの API で、クライアントはメッセージまたはペイロードを XML として設定する必要があります。これは、個別のプロトコルの標準と、メッセージまたはペイロード構造の詳細な知識とメッセージに忠実に従った状態を意味します。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
インターフェイスを使用して個別の応答コンテキストを利用できます。
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
メソッドは、ビジネスメソッドの実行前に、制御のスレッドを呼び出しアプリケーションに戻します。
@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; } }
タイムアウトの設定
2 種類のプロパティーが HTTP 接続のタイムアウトの動作と、メッセージの受信を待機しているクライアントのタイムアウトを制御します。最初のプロパティーは、javax.xml.ws.client.connectionTimeout
で、次のプロパティーは、javax.xml.ws.client.receiveTimeout
です。それぞれはミリ秒単位で示され、正しい構文を以下に示します。
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"); }