Menu Close

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 インスタンスを指定できるようにする追加のコンストラクター。これは、複数の Bus インスタンスを異なるスレッドに関連付けることのできる、マルチスレッドアプリケーションのコンテキストで役に立ちます。このコンストラクターは、指定した Bus がこのサービスで使用される Bus となるようにする簡単な方法を提供します。wsdl2java ツールを呼び出す時に -fe cxf オプションを指定している場合はのみ利用できます。
  • getPortName(): name 属性が PortName と同じ wsdl:port 要素で定義されたエンドポイントのプロキシーを返します。getter メソッドは、ServiceName サービスで定義されるすべての wsdl:port 要素に対して生成されます。複数のエンドポイント定義が含まれる wsdl:service 要素により、複数の getPortName() メソッドを持つサービスクラスが生成されます。

サービスエンドポイントインターフェース

元の WSDL コントラクトで定義されたすべてのインターフェースに対して、対応する SEI を生成できます。サービスエンドポイントインターフェースは、wsdl:portType 要素の Java マッピングです。元の wsdl:portType 要素で定義された各操作は、SEI の対応するメソッドにマッピングされます。操作のパラメーターは以下のようにマッピングされます。 .入力パラメーターは、メソッド引数にマッピングされます。

  1. 最初の出力パラメーターは戻り値にマッピングされます。
  2. 複数の出力パラメーターがある場合は、2 番目と後続の出力パラメーターはメソッド引数にマッピングされます (さらに、この引数の値を Holder 型を使用して渡す必要があります)。

例として、例28.3「Greeter サービスエンドポイントインターフェース」に、例26.1「HelloWorld WSDL コントラクト」で定義される wsdl:portType 要素から生成される Greeter SEI を示します。単純化するために、例28.3「Greeter サービスエンドポイントインターフェース」では、標準の JAXB および JAX-WS アノテーションを省略しています。

例28.3 Greeter サービスエンドポイントインターフェース

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(...);
}


[2] wsdl:service 要素の name 属性が Service で終わる場合、_Service は使用されません。