【Java】【JAX-WS】 Webサービス / Metro [10] ~ WSDLファイルをクライアントのローカルに置く ~

はじめに

Webサービス / Metro [9] ~ タイムアウトを設定する / クライアントサイド
https://blogs.yahoo.co.jp/dk521123/36559479.html
で、クライアントサイドのタイムアウトを設定した。

 しかし、上記の関連記事のタイムアウト値はあくまで、
下記「クライアント-Webサービス間のシーケンス図」で言うと
「[3] Call Web Service APIWebサービスAPI呼び出し)」と
「[4] Result from APIWebサービスAPIからの結果・レスポンス)」
に対してのタイムアウト値で、
「[1] Request WSDL file(WSDLファイルの取得リクエスト)」と
「[2] Response WSDL file(WSDLファイルの取得レスポンス)」
に対してのタイムアウト値ではなかった。

 色々試したが、タイムアウト値を設定できなかったが、
一つの解決案として、クライアント側に予めWSDLファイルを置いておくという
方法があったので、その方法について、記述する。
(ローカルにあれば、ネットに繋ぐ必要もないので、タイムアウト値が不要になる)

クライアント-Webサービス間のシーケンス図

 Client                            Web Service
   |                                   |
   +--- [1] Request WSDL file -------->|
   |                                   |
   |<-- [2] Response WSDL file --------+
   |                                   |
   |                                   |
   +--- [3] Call Web Service API ----->|
   |                                   |
   |<-- [4] Result from API -----------+
   |                                   |
   |                                   |

サンプル

Webサービス

* サービスを立ち上げておく
 * 以下の関連記事を参照のこと。
Webサービス / Metro [1] ~入門編 / サーバサイドの構築 ~
http://blogs.yahoo.co.jp/dk521123/36139336.html

クライアント側

WSDLファイル】SampleWebService.wsdl
[[https://localhost:8443/SampleWebService/services/SampleWebService.ws?wsdl]]
をブラウザなどでアクセスし、ファイル「SampleWebService.wsdl」として保存
<!--
 Published by JAX-WS RI (http://jax-ws.java.net). RI's version is Metro/2.3.1 (UNKNOWN_BRANCH-false; 2015-01-15T16:53:43+0100) JAXWS-RI/2.2.10 JAXWS-API/2.2.11 JAXB-RI/2.2.10-b140802.1033 JAXB-API/2.2.12-b140109.1041 svn-revision#unknown. 
-->
<!--
 Generated by JAX-WS RI (http://jax-ws.java.net). RI's version is Metro/2.3.1 (UNKNOWN_BRANCH-false; 2015-01-15T16:53:43+0100) JAXWS-RI/2.2.10 JAXWS-API/2.2.11 JAXB-RI/2.2.10-b140802.1033 JAXB-API/2.2.12-b140109.1041 svn-revision#unknown. 
-->
<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://webservice.sample.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://webservice.sample.com/" name="SampleWebServiceService">
<types>
<xsd:schema>
<!-- ★「schemaLocation」属性の値を必要であれば書き直す★ -->
<xsd:import namespace="http://webservice.sample.com/"
 schemaLocation="">https://localhost:8443/SampleWebService/services/SampleWebService.ws?xsd=1"/>
</xsd:schema>
</types>
<message name="sayYourAge">
<part name="parameters" element="tns:sayYourAge"/>
</message>
<message name="sayYourAgeResponse">
<part name="parameters" element="tns:sayYourAgeResponse"/>
</message>
<portType name="SampleWebService">
<operation name="sayYourAge">
<input wsam:Action="http://webservice.sample.com/SampleWebService/sayYourAgeRequest" message="tns:sayYourAge"/>
<output wsam:Action="http://webservice.sample.com/SampleWebService/sayYourAgeResponse" message="tns:sayYourAgeResponse"/>
</operation>
</portType>
<binding name="SampleWebServicePortBinding" type="tns:SampleWebService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="sayYourAge">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="SampleWebServiceService">
<port name="SampleWebServicePort" binding="tns:SampleWebServicePortBinding">
<!-- ★「location」属性の値を必要であれば書き直す★ -->
<soap:address location="">https://localhost:8443/SampleWebService/services/SampleWebService.ws"/>
</port>
</service>
</definitions>
Javaコード】Main.java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.ws.BindingProvider;

public class Main {

  public static void main(String[] args) {
    System.out.println("Start!");
    try {
      SSLContext sslContext = SSLContext.getInstance("TLS");
      char[] keyPass = "changeit".toCharArray();

      try (FileInputStream fileInputStream = new FileInputStream("./etc/client.keystore")) {
        KeyStore keyStore = creatKeyStore(fileInputStream, keyPass);
        KeyManager[] keyManagers = creatKeyManagers(keyStore, keyPass);
        TrustManager[] trustManager = creatTrustManagers(keyStore);
        SecureRandom secureRandom = SecureRandom.getInstanceStrong();
        System.out.println("Algorithm : " + secureRandom.getAlgorithm());
        sslContext.init(keyManagers, trustManager, secureRandom);

        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
        HostnameVerifier hostnameVerifier = creatHostnameVerifier();
        HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
      }

      // ★ここ(ポイントは「file:///【ローカルのWSDLファイルパス】」)★
      URL url = new URL("file:///C:/workspace/SampleCode/conf/SampleWebService.wsdl");
      SampleWebServiceService service = new SampleWebServiceService(url);
      SampleWebService proxy = service.getSampleWebServicePort();

      Map<String, Object> context = ((BindingProvider) proxy).getRequestContext();
      // コネクションを確立するまでの時間[ミリ秒]
      context.put("com.sun.xml.internal.ws.request.timeout", 2 * 1000);
      // レスポンスを受け取るまでの時間[ミリ秒]
      context.put("com.sun.xml.internal.ws.connect.timeout", 2 * 1000);
      
      long start = System.currentTimeMillis();
      String result = proxy.sayYourAge("Ken", 11L);
      long end = System.currentTimeMillis();
      System.out.println((end - start) + "ms");

      System.out.println("Result : " + result);
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    System.out.println("Done");
  }

  private static KeyStore creatKeyStore(InputStream inputStream, char[] trustpass)
      throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException {
    KeyStore keyStore = KeyStore.getInstance("JKS");
    keyStore.load(inputStream, trustpass);

    return keyStore;
  }

  private static KeyManager[] creatKeyManagers(KeyStore keyStore, char[] keyPass)
      throws GeneralSecurityException, IOException {
    String algorithm = KeyManagerFactory.getDefaultAlgorithm();
    KeyManagerFactory factory = KeyManagerFactory.getInstance(algorithm);
    factory.init(keyStore, keyPass);
    return factory.getKeyManagers();
  }

  private static TrustManager[] creatTrustManagers(KeyStore trustStore)
      throws NoSuchAlgorithmException, KeyStoreException {
    String algorithm = KeyManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory factory = TrustManagerFactory.getInstance(algorithm);
    factory.init(trustStore);
    return factory.getTrustManagers();
  }

  private static HostnameVerifier creatHostnameVerifier() {
    return new HostnameVerifier() {
      public boolean verify(String hostname, SSLSession session) {
        System.out.println("verify " + hostname + " " + session.getPeerHost());
        return true;
      }
    };
  }
}

実行結果

Start!
Algorithm : Windows-PRNG
1047ms
Result : [0] Ken's age is 11.
Done

トラブルシューティング

例外「java.net.MalformedURLException: unknown protocol: c」発生する

 * 以下の関連記事を参照
https://blogs.yahoo.co.jp/dk521123/36872203.html

関連記事

Webサービス / Metro [1] ~入門編 / サーバサイドの構築 ~

http://blogs.yahoo.co.jp/dk521123/36139336.html

Webサービス / Metro [2] ~入門編 / クライアントサイドの構築 ~

http://blogs.yahoo.co.jp/dk521123/36140561.html

Webサービス / Metro [8] ~ SSL通信を行う (2) / クライアントサイド ~

http://blogs.yahoo.co.jp/dk521123/36528991.html

Webサービス / Metro [9] ~ タイムアウトを設定する / クライアントサイド ~

https://blogs.yahoo.co.jp/dk521123/36559479.html

例外「java.net.MalformedURLException: unknown protocol: c」発生時の対応

https://blogs.yahoo.co.jp/dk521123/36872203.html