1. 概要

このチュートリアルでは、Java8および11のJAX-WSRIを使用してJavaでSOAPクライアントを構築する方法を学習します。

まず、 wsimport ユーティリティを使用してクライアントコードを生成し、次にJUnitを使用してテストします。

始めたばかりの人のために、私たちの JAX-WS の紹介は、この主題に関する素晴らしい背景を提供します。

2. Webサービス

クライアントの構築を開始する前に、サーバーが必要です。 この場合、JAX-WSWebサービスを公開するサーバーが必要です。

このチュートリアルでは、国の名前を指定してデータを取得するWebサービスを使用します。

2.1. 実装の概要

クライアントの構築に重点を置いているため、サービスの実装の詳細については説明しません。

インターフェイスCountryServiceを使用してWebサービスを外部に公開するとします。 簡単にするために、クラスCountryServicePublisherのjavax.xml.ws.EndpointAPIを使用してWebサービスをビルドおよびデプロイします。

CountryServicePublisher をJavaアプリケーションとして実行して、着信要求を受け入れるエンドポイントを公開します。 つまり、これがサーバーになります。

サーバーを起動した後、URL http:// localhost:8888 / ws / country?wsdl を押すと、Webサービス記述ファイルが表示されます。 WSDLは、サービスの提供内容を理解し、クライアントの実装コードを生成するためのガイドとして機能します。

2.2. Webサービス記述言語

WebサービスのWSDLcountryを見てみましょう。

<?xml version="1.0" encoding="UTF-8"?>
<definitions <!-- namespace declarations -->
    targetNamespace="http://server.ws.soap.baeldung.com/" name="CountryServiceImplService">
    <types>
        <xsd:schema>
            <xsd:import namespace="http://server.ws.soap.baeldung.com/" 
              schemaLocation="http://localhost:8888/ws/country?xsd=1"></xsd:import>
        </xsd:schema>
    </types>
    <message name="findByName">
        <part name="arg0" type="xsd:string"></part>
    </message>
    <message name="findByNameResponse">
        <part name="return" type="tns:country"></part>
    </message>
    <portType name="CountryService">
        <operation name="findByName">
            <input wsam:Action="http://server.ws.soap.baeldung.com/CountryService/findByNameRequest" 
              message="tns:findByName"></input>
            <output wsam:Action="http://server.ws.soap.baeldung.com/CountryService/findByNameResponse" 
              message="tns:findByNameResponse"></output>
        </operation>
    </portType>
    <binding name="CountryServiceImplPortBinding" type="tns:CountryService">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"></soap:binding>
        <operation name="findByName">
            <soap:operation soapAction=""></soap:operation>
            <input>
                <soap:body use="literal" namespace="http://server.ws.soap.baeldung.com/"></soap:body>
            </input>
            <output>
                <soap:body use="literal" namespace="http://server.ws.soap.baeldung.com/"></soap:body>
            </output>
        </operation>
    </binding>
    <service name="CountryServiceImplService">
        <port name="CountryServiceImplPort" binding="tns:CountryServiceImplPortBinding">
            <soap:address location="http://localhost:8888/ws/country"></soap:address>
        </port>
    </service>
</definitions>

一言で言えば、これはそれが提供する有用な情報です:

  • string引数を使用してメソッドfindByNameを呼び出すことができます。
  • それに応じて、サービスはのカスタムタイプを返します。
  • タイプは、場所 http:// localhost:8888 / ws / country?xsd =1で生成されたxsdスキーマで定義されます。
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema <!-- namespace declarations -->
    targetNamespace="http://server.ws.soap.baeldung.com/">
    <xs:complexType name="country">
        <xs:sequence>
            <xs:element name="capital" type="xs:string" minOccurs="0"></xs:element>
            <xs:element name="currency" type="tns:currency" minOccurs="0"></xs:element>
            <xs:element name="name" type="xs:string" minOccurs="0"></xs:element>
            <xs:element name="population" type="xs:int"></xs:element>
        </xs:sequence>
    </xs:complexType>
    <xs:simpleType name="currency">
        <xs:restriction base="xs:string">
            <xs:enumeration value="EUR"></xs:enumeration>
            <xs:enumeration value="INR"></xs:enumeration>
            <xs:enumeration value="USD"></xs:enumeration>
        </xs:restriction>
    </xs:simpleType>
</xs:schema>

クライアントを実装するために必要なのはこれだけです。

次のセクションでその方法を見てみましょう。

3. wsimportを使用してクライアントコードを生成する

3.1. JDK8の場合

まず、JDK8を使用してクライアントコードを生成する方法を見てみましょう。

まず、プラグインを pom.xml に追加して、Maven経由でこのツールを使用します。

<plugin> 
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.6</version>
    <executions> 
        <execution> 
            <id>wsimport-from-jdk</id>
            <goals>
                <goal>wsimport</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <wsdlUrls>
            <wsdlUrl>http://localhost:8888/ws/country?wsdl</wsdlUrl> 
        </wsdlUrls>
        <keep>true</keep> 
        <packageName>com.baeldung.soap.ws.client.generated</packageName> 
        <sourceDestDir>src/main/java</sourceDestDir>
    </configuration>
</plugin>

次に、このプラグインを実行してみましょう。

mvn clean jaxws:wsimport

それで全部です! 上記のコマンドは、プラグイン構成で提供したsourceDestDir内の指定されたパッケージcom.baeldung.soap.ws.client.generatedにコードを生成します。

同じことを実現する別の方法は、wsimportユーティリティを使用することです。標準のJDK 8ディストリビューションに付属しており、 JAVA_HOME /binディレクトリにあります。

wsimport を使用してクライアントコードを生成するには、プロジェクトのルートに移動して、次のコマンドを実行します。

JAVA_HOME/bin/wsimport -s src/main/java/ -keep -p com.baeldung.soap.ws.client.generated "http://localhost:8888/ws/country?wsdl"

プラグインまたはコマンドを正常に実行するには、サービスエンドポイントが使用可能である必要があることに注意してください。

3.2. JDK11の場合

JDK 11以降、 wsimportはJDKの一部として削除され、標準ディストリビューションではそのままではなくなりました。

ただし、EclipseFoundationにオープンソース化されていました。

wsimportを使用してJava11以降のクライアントコードを生成するには、 jakarta.xml.ws-api jaxws-rt 、およびjaxws-maven-pluginに加えてjaxws-riの依存関係:

<dependencies>
    <dependency>
        <groupId>jakarta.xml.ws</groupId
        <artifactId>jakarta.xml.ws-api</artifactId
        <version>3.0.0</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.ws</groupId>
        <artifactId>jaxws-rt</artifactId>
        <version>3.0.0</version
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.ws</groupId>
        <artifactId>jaxws-ri</artifactId>
        <version>2.3.1</version
        <type>pom</type>
    </dependency>
</dependencies>
<build>
    <plugins>        
        <plugin>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-maven-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <wsdlUrls>
                    <wsdlUrl>http://localhost:8888/ws/country?wsdl</wsdlUrl>
                </wsdlUrls>
                <keep>true</keep>
                <packageName>com.baeldung.soap.ws.client.generated</packageName>
                <sourceDestDir>src/main/java</sourceDestDir>
            </configuration>
        </plugin>
    </plugins>
</build>

ここで、パッケージ com.baeldung.soap.ws.client.generated でクライアントコードを生成するには、以前と同じMavenコマンドが必要です。

mvn clean jaxws:wsimport

次に、両方のJavaバージョンで同じである生成されたアーティファクトを見てみましょう。

3.3. 生成されたPOJO

前に見たxsdに基づいて、ツールはCountry.javaという名前のファイルを生成します。

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "country", propOrder = { "capital", "currency", "name", "population" })
public class Country {
    protected String capital;
    @XmlSchemaType(name = "string")
    protected Currency currency;
    protected String name;
    protected int population;
    // standard getters and setters
}

ご覧のとおり、生成されたクラスは、XMLとの間でオブジェクトをマーシャリングおよびアンマーシャリングするためにJAXBアノテーションで装飾されています。

また、Currency列挙型を生成します。

@XmlType(name = "currency")
@XmlEnum
public enum Currency {
    EUR, INR, USD;
    public String value() {
        return name();
    }
    public static Currency fromValue(String v) {
        return valueOf(v);
    }
}

3.4. CountryService

2番目に生成されるアーティファクトは、実際のWebサービスへのプロキシとして機能するインターフェイスです。

インターフェイスCountryServiceは、サーバーfindByNameと同じメソッドを宣言します。

@WebService(name = "CountryService", targetNamespace = "http://server.ws.soap.baeldung.com/")
@SOAPBinding(style = SOAPBinding.Style.RPC)
@XmlSeeAlso({ ObjectFactory.class })
public interface CountryService {
    @WebMethod
    @WebResult(partName = "return")
    @Action(input = "http://server.ws.soap.baeldung.com/CountryService/findByNameRequest", 
      output = "http://server.ws.soap.baeldung.com/CountryService/findByNameResponse")
    public Country findByName(@WebParam(name = "arg0", partName = "arg0") String arg0);
}

特に、インターフェースは javax.jws.WebService としてマークされ、SOAPBinding.StyleはサービスのWSDLで定義されているRPCとしてマークされています。

メソッドfindByNameは、 javax.jws.WebMethod であり、予想される入力および出力パラメータータイプであることを宣言するために注釈が付けられています。

3.5. CountryServiceImplService

次に生成されるクラスCountryServiceImplServiceは、javax.xml.ws.Serviceを拡張します。

その注釈WebServiceClientは、それがサービスのクライアントビューであることを示しています。

@WebServiceClient(name = "CountryServiceImplService", 
  targetNamespace = "http://server.ws.soap.baeldung.com/", 
  wsdlLocation = "http://localhost:8888/ws/country?wsdl")
public class CountryServiceImplService extends Service {

    private final static URL COUNTRYSERVICEIMPLSERVICE_WSDL_LOCATION;
    private final static WebServiceException COUNTRYSERVICEIMPLSERVICE_EXCEPTION;
    private final static QName COUNTRYSERVICEIMPLSERVICE_QNAME = 
      new QName("http://server.ws.soap.baeldung.com/", "CountryServiceImplService");

    static {
        URL url = null;
        WebServiceException e = null;
        try {
            url = new URL("http://localhost:8888/ws/country?wsdl");
        } catch (MalformedURLException ex) {
            e = new WebServiceException(ex);
        }
        COUNTRYSERVICEIMPLSERVICE_WSDL_LOCATION = url;
        COUNTRYSERVICEIMPLSERVICE_EXCEPTION = e;
    }

    public CountryServiceImplService() {
        super(__getWsdlLocation(), COUNTRYSERVICEIMPLSERVICE_QNAME);
    }

    // other constructors 

    @WebEndpoint(name = "CountryServiceImplPort")
    public CountryService getCountryServiceImplPort() {
        return super.getPort(new QName("http://server.ws.soap.baeldung.com/", "CountryServiceImplPort"), 
          CountryService.class);
    }

    private static URL __getWsdlLocation() {
        if (COUNTRYSERVICEIMPLSERVICE_EXCEPTION != null) {
            throw COUNTRYSERVICEIMPLSERVICE_EXCEPTION;
        }
        return COUNTRYSERVICEIMPLSERVICE_WSDL_LOCATION;
    }

}

ここで注意すべき重要なメソッドは、getCountryServiceImplPortです。 サービスエンドポイントの修飾名、または QName 、および動的プロキシのサービスエンドポイントインターフェイス名を指定すると、プロキシインスタンスを返します。

Webサービスを呼び出すには、後で説明するように、このプロキシを使用する必要があります。

プロキシを使用すると、サービスをローカルで呼び出しているように見え、リモート呼び出しの複雑さを抽象化できます。

4. クライアントのテスト

次に、生成されたクライアントコードを使用してWebサービスに接続するためのJUnitテストを記述します。

その前に、クライアント側でサービスのプロキシインスタンスを取得する必要があります。

@BeforeClass
public static void setup() {
    CountryServiceImplService service = new CountryServiceImplService();
    CountryService countryService = service.getCountryServiceImplPort();
}

WebServiceFeature の有効化または無効化などのより高度なシナリオでは、CountryServiceImplServiceに対して生成された他のコンストラクターを使用できます。

次に、いくつかのテストを見てみましょう。

@Test
public void givenCountryService_whenCountryIndia_thenCapitalIsNewDelhi() {
    assertEquals("New Delhi", countryService.findByName("India").getCapital());
}

@Test
public void givenCountryService_whenCountryFrance_thenPopulationCorrect() {
    assertEquals(66710000, countryService.findByName("France").getPopulation());
}

@Test
public void givenCountryService_whenCountryUSA_thenCurrencyUSD() {
    assertEquals(Currency.USD, countryService.findByName("USA").getCurrency());
}

ご覧のとおり、リモートサービスのメソッドの呼び出しは、ローカルでメソッドを呼び出すのと同じくらい簡単になりました。 プロキシのfindByNameメソッドは、提供された名前に一致するCountryインスタンスを返しました。 次に、POJOのさまざまなゲッターを使用して、期待値をアサートしました。

5. 結論

この記事では、JAX-WSRIとJava8および11のwsimportユーティリティを使用してJavaでSOAPWebサービスを呼び出す方法を説明しました。

または、Apache CXF、Apache Axis2、Springなどの他のJAX-WS実装を使用して同じことを行うこともできます。

いつものように、ソースコードは JDK8バージョンとJDK11バージョンの両方のGitHubで入手できます。