28.2. コンシューマーの実装
概要
WSDL コントラクトから開始するときにコンシューマーを実装するには、次のスタブを使用する必要があります。
- サービスクラス
- SEI
これらのスタブを使用して、コンシューマーコードはサービスプロキシーをインスタンス化し、リモートサービスでリクエストを行います。また、コンシューマーのビジネスロジックも実装します。
生成されたサービスクラス
例28.2「生成されたサービスクラスの概要」に、生成されるサービスクラス ServiceName_Service
の典型的な概要を示します。[2]これは、javax.xml.ws.Service
ベースクラスを拡張します。
例28.2 生成されたサービスクラスの概要
@WebServiceClient(name="..." targetNamespace="..." wsdlLocation="...") public class ServiceName extends javax.xml.ws.Service { ... public ServiceName(URL wsdlLocation, QName serviceName) { } public ServiceName() { } // Available only if you specify '-fe cxf' option in wsdl2java public ServiceName(Bus bus) { } @WebEndpoint(name="...") public SEI getPortName() { } . . . }
例28.2「生成されたサービスクラスの概要」 の ServiceName
クラスは、以下のメソッドを定義します。
-
ServiceName(URL wsdlLocation, QName serviceName)
: wsdlLocation から取得される WSDL コントラクトの QName ServiceName サービスを使用するwsdl:service
要素のデータに基づいてサービスオブジェクトを構築します。 -
ServiceName()
: デフォルトのコンストラクター。スタブコードの生成時 (例:wsdl2java
ツールの実行時) に提供されたサービス名と WSDL コントラクトに基づいて、サービスオブジェクトを構築します。このコンストラクターを使用すると、WSDL コントラクトが指定された場所で引き続き使用可能であることが前提となります。 -
ServiceName(Bus bus)
: (CXF 固有) Service の設定に使用される Bus インスタンスを指定できるようにする追加のコンストラクター。これは、複数のバスインスタンスを異なるスレッドに関連付けることができるマルチスレッドアプリケーションのコンテキストで役立ちます。このコンストラクターは、指定したバスがこのサービスで使用されるバスであることを確認する簡単な方法を提供します。wsdl2java
ツールを呼び出すときに-fe cxf
オプションを指定する場合にのみ利用できます。 -
getPortName()
:name
属性が PortName と同じwsdl:port
要素で定義されたエンドポイントのプロキシーを返します。getter メソッドは、ServiceName サービスで定義されるすべてのwsdl:port
要素に対して生成されます。複数のエンドポイント定義が含まれるwsdl:service
要素により、複数のgetPortName()
メソッドを持つサービスクラスが生成されます。
サービスエンドポイントインターフェイス
元の WSDL コントラクトで定義されたすべてのインターフェイスについて、対応する SEI を生成できます。サービスエンドポイントインターフェイスは、wsdl:portType
要素の Java マッピングです。元の wsdl:portType
要素で定義された各操作は、SEI の対応するメソッドにマッピングされます。操作のパラメーターは次のようにマップされます。 .入力パラメーターはメソッド引数にマップされます。
- 最初の出力パラメーターは戻り値にマップされます。
-
複数の出力パラメーターがある場合は、2 番目と後続の出力パラメーターはメソッド引数にマッピングされます (さらに、この引数の値を
Holder
型を使用して渡す必要があります)。
例として、例28.3「グリーターサービスエンドポイントインターフェイス」に、例26.1「HelloWorld WSDL コントラクト」で定義される wsdl:portType
要素から生成される Greeter SEI を示します。簡単にするために、例28.3「グリーターサービスエンドポイントインターフェイス」 標準の JAXB および JAX-WS アノテーションを省略します。
例28.3 グリーターサービスエンドポイントインターフェイス
package org.apache.hello_world_soap_http; ... public interface Greeter { public String sayHi(); public String greetMe(String requestType); public void greetMeOneWay(String requestType); public void pingMe() throws PingMeFault; }
コンシューマー主な機能
例28.4「コンシューマー実装コード」 は、HelloWorld コンシューマーを実装するコードを示しています。コンシューマーは、SOAPService サービスの SoapPort ポートに接続してから、Greeter ポートタイプでサポートされている各操作の呼び出しに進みます。
例28.4 コンシューマー実装コード
package demo.hw.client; import java.io.File; import java.net.URL; import javax.xml.namespace.QName; import org.apache.hello_world_soap_http.Greeter; import org.apache.hello_world_soap_http.PingMeFault; import org.apache.hello_world_soap_http.SOAPService; public final class Client { private static final QName SERVICE_NAME = new QName("http://apache.org/hello_world_soap_http", "SOAPService"); private Client() { } public static void main(String args[]) throws Exception { if (args.length == 0) { System.out.println("please specify wsdl"); System.exit(1); } URL wsdlURL; File wsdlFile = new File(args[0]); if (wsdlFile.exists()) { wsdlURL = wsdlFile.toURL(); } else { wsdlURL = new URL(args[0]); } System.out.println(wsdlURL); SOAPService ss = new SOAPService(wsdlURL,SERVICE_NAME); Greeter port = ss.getSoapPort(); String resp; System.out.println("Invoking sayHi..."); resp = port.sayHi(); System.out.println("Server responded with: " + resp); System.out.println(); System.out.println("Invoking greetMe..."); resp = port.greetMe(System.getProperty("user.name")); System.out.println("Server responded with: " + resp); System.out.println(); System.out.println("Invoking greetMeOneWay..."); port.greetMeOneWay(System.getProperty("user.name")); System.out.println("No response from server as method is OneWay"); System.out.println(); try { System.out.println("Invoking pingMe, expecting exception..."); port.pingMe(); } catch (PingMeFault ex) { System.out.println("Expected exception: PingMeFault has occurred."); System.out.println(ex.toString()); } System.exit(0); } }
例28.4「コンシューマー実装コード」 からの Client.main()
メソッドは、以下のように行われます。
Apache CXF ランタイムクラスがクラスパス上にある場合、ランタイムは暗黙的に初期化されます。Apache CXF を初期化するために特別な関数を呼び出す必要はありません。
コンシューマーは、HelloWorld の WSDL コントラクトの場所を指定する単一の文字列引数を期待しています。WSDL コントラクトの場所は wsdlURL
に保存されます。
WSDL コントラクトの場所とサービス名を必要とするコンストラクターを使用してサービスオブジェクトを作成します。 適切な getPortName()
メソッドを呼び出して、必要なポートのインスタンスを取得します。この場合、SOAPService サービスは Greeter
サービスエンドポイントインターフェイスを実装する SoapPort ポートのみをサポートします。
コンシューマーは、Greeter サービスエンドポイントインターフェイスでサポートされている各メソッドを呼び出します。
pingMe()
メソッドの場合、サンプルコードは PingMeFault フォールト例外をキャッチする方法を示しています。
-fe cxf オプションで生成されたクライアントプロキシー
wsdl2java で -fe cxf
オプションを指定してクライアントプロキシーを生成する場合 (cxf
フロントエンドを選択して)、生成されるクライアントプロキシーコードは Java 7 とより適切に統合されます。この場合は、getServiceNamePort()
メソッドを呼び出すと、SEI のサブインターフェイスである型を返し、以下の追加インターフェイスを実装します。
-
java.lang.AutoCloseable
-
javax.xml.ws.BindingProvider
(JAX-WS 2.0) -
org.apache.cxf.endpoint.Client
これによりクライアントプロキシーの操作がどのように簡素化されるかを確認するには、標準の JAX-WS プロキシーオブジェクトを使用して記述された次の Java コードサンプルを検討してください。
// Programming with standard JAX-WS proxy object // (ServiceNamePortType port = service.getServiceNamePort(); ((BindingProvider)port).getRequestContext() .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address); port.serviceMethod(...); ((Closeable)port).close();
そして、前述のコードを cxf
フロントエンドで生成したコードを使用して記述した以下の等価なコードサンプルと比較してください。
// Programming with proxy generated using '-fe cxf' option // try (ServiceNamePortTypeProxy port = service.getServiceNamePort()) { port.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address); port.serviceMethod(...); }