intra-mart Accel Platform Webサービス スクリプト開発プログラミングガイド 第2版 2014-04-01

付録

トラブルシューティング

Java スタブ・クラスのコンパイルに失敗する場合

SOAPClient オブジェクトを利用する際に以下の例外が発生する場合は、自動生成された Java スタブ・クラスのコンパイルに失敗しています。
[INFO] j.c.i.s.j.i.SOAPClientObject - Compile Stub(Java): /foo/bar/XXXXXStub.java
[ERROR] j.c.i.s.j.i.SOAPClientObject - null
java.lang.IllegalArgumentException
  at java.lang.ProcessImpl.<init>(ProcessImpl.java)
  at java.lang.ProcessImpl.start(ProcessImpl.java)
  at java.lang.ProcessBuilder.start(ProcessBuilder.java)
  at java.lang.Runtime.exec(Runtime.java)
  at jp.co.intra_mart.system.javascript.imapi.SOAPClientObject.compileStub(SOAPClientObject.java)
この現象が発生する場合は、以下の手順に従って、環境変数「AXIS2_HOME」を設定してください。
  1. e Builder で環境構築時に利用したプロジェクトの「juggling.im」を開き、以下のモジュールが含まれていることを確認します。
    • 「ベースモジュール」タブ内の「ライブラリ」-「サードパーティ製ライブラリ」-「Apache Axis2」
  2. war を展開してできたコンテキストパスと同名のフォルダを開き、以下のファイルが存在することを確認します。
    • %CONTEXT_PATH%/WEB-INF/lib/axis2-xxx-x.x.x.jar (x は任意)
  3. intra-mart Accel Platform を実行する環境の環境変数「AXIS2_HOME」に、war を展開してできた %CONTEXT_PATH%/WEB-INF をパスとして設定します。

https で提供されている WSDL を利用する場合

以下のような、WSDL が https で提供されている場合の SOAPClient を利用するためには、接続先のサーバ証明書の取得・登録が必要です。
  • https://<HOST>/<CONTEXT_PATH>/services/SampleWebService?wsdl
以下の手順に従って、サーバ証明書の取得・登録を行います。
  1. 接続先のサーバ証明書を取得します。
    サーバ証明書の取得方法はいくつかありますが、ここでは Windows 環境の Internet Explorer 9 を利用して証明書を取得する方法を示します。
    1. Internet Explorer 9 を開き、WSDL の URL を入力してアクセスします。
    2. Alt キーを押下してメニューバーを開き、「ツール」-「インターネット オプション」を選択します。
    3. 「コンテンツ」タブを開き、「証明書」をクリックします。
    4. 取得したい証明書を選択して「エクスポート」をクリックします。
    5. ウィザードを進めてサーバ証明書ファイルを保存します。
  2. JDK に含まれる keytool を利用して、サーバ証明書をキーストアに追加します。
    例: サーバ証明書ファイルが C:\temp\server.crt に保存されており、別名「sample_alias」でキーストアエントリに追加する場合
    keytool -import -alias sample_alias -file C:\temp\server.crt
    

    コラム

    上記コマンドを実行すると、ユーザのホームディレクトリの「.keystore」ファイルに、キーストアが作成されます。
    keytool の詳細は、以下「JDK ドキュメントの keytool - 鍵と証明書の管理ツール」を参照してください。

  3. アプリケーションサーバの JavaVM のシステムプロパティに「javax.net.ssl.trustStore」を追加します。
    例: Resin のインストール先が C:\resin で、ユーザ名が user_name の場合

    C:\resin\conf\resin.properties

    jvm_args : -Djavax.net.ssl.trustStore="C:\Users\user_name\.keystore"
    

    コラム

    すでに jvm_args が存在する場合は、末尾に半角空白で1文字空けて追記してください。
また、WSDL の URL が https で始まっていたとしても、WSDL に記述されているエンドポイントが https でない場合は、SOAPClient API を利用する際に、明示的にエンドポイントを指定してください。
// ホスト名、コンテキストパスは適宜置き換えてください。
var wsdlURL     = 'https://localhost/imart/services/SampleWebService?wsdl';
var serviceName = null;
var portName    = null;
var endPoint    = 'https://localhost/imart/services/SampleWebService'; // ← 明示的に指定します。

var soapClient = new SOAPClient(wsdlURL, serviceName, portName, endPoint);

「指定した要求に失敗しました」が発生する場合

本現象が発生した場合に考えられる原因は、以下の通りです。
  • 指定した Web サービスを実行する権限がない可能性があります。
解決方法の詳細は「Webサービス 認証・認可 仕様書」-「認証・認可のSOAPフォルト・コード」の「wsse:RequestFailed」を参照してください。

「指定した RequestSecurityToken を理解できません」が発生する場合

本現象が発生した場合に考えられる原因は、以下の通りです。
  • 認証タイプに対応する認証モジュールが存在しない可能性があります。
解決方法の詳細は「Webサービス 認証・認可 仕様書」-「認証・認可のSOAPフォルト・コード」の「wsse:BadRequest」を参照してください。

「要求が無効か、形式が間違っています」が発生する場合

本現象が発生した場合に考えられる原因は、以下の通りです。
  • SOAP ボディにユーザ情報が存在していない可能性があります。
  • ユーザ情報が格納されている要素名が「wsUserInfo」ではない可能性があります。
  • Web サービスとして公開する Java クラス(JavaScript ラッパークラス)のコンパイル方法が誤っている可能性があります。
WSDL の URL をブラウザで開き、Web サービスの関数定義内の引数名を確認してください。

■正

<xs:element name="add">
   <xs:complexType>
      <xs:sequence>
         <xs:element name="wsUserInfo" type="ax22:WSUserInfo" nillable="true" minOccurs="0"/>
         <xs:element name="member" type="ax24:Member" nillable="true" minOccurs="0"/>
      </xs:sequence>
   </xs:complexType>
</xs:element>

■誤

<xs:element name="add">
   <xs:complexType>
      <xs:sequence>
         <xs:element name="param0" type="ax22:WSUserInfo" nillable="true" minOccurs="0"/>
         <xs:element name="param1" type="ax24:Member" nillable="true" minOccurs="0"/>
      </xs:sequence>
   </xs:complexType>
</xs:element>
上記のように、引数名が「param0」「param1」のようになっている場合は、Java クラスのコンパイル方法が誤っています。
解決方法の詳細は「Webサービス 認証・認可 仕様書」-「認証・認可のSOAPフォルト・コード」の「wsse:InvalidRequest」を参照してください。

Storage 上の WSDL ファイルを利用する場合

Storage 上に保存されている WSDL ファイルを利用する場合は、その WSDL ファイルを指し示している PublicStorage オブジェクトを、SOAPClient のコンストラクタの第1引数に指定してください。
SOAPClient オブジェクトの詳細は、「API リストの SOAPClient オブジェクト 」を参照してください。
なお、WSDL ファイルの解析時に必要なファイルの拡張子や、必要なファイルが格納されているディレクトリ名を設定することができます。
SOAPClient の設定についての詳細は、「設定ファイルリファレンスの SOAPClient オブジェクトの設定 」を参照してください。

複数の Web サービスが定義されている WSDL を利用する場合

WSDL 内に複数の Web サービスが定義されている場合は、Web サービス名を明示的に指定して SOAPClient オブジェクトを利用する必要があります。
具体的には、SOAPClient オブジェクトのコンストラクタ第2引数に実行したい Web サービス名を指定します。

JavaScript 形式から Java 形式へのオブジェクト変換に失敗する場合

本現象が発生した場合に考えられる原因は、以下の通りです。
  • JavaScript 形式 → Java 形式へのオブジェクト変換規則に違反している可能性があります。
例えば、下記のような例外が発生した場合は、「JavaScript の配列」を「配列として定義されていない JavaBean のプロパティ」に変換しようとした際に発生します。
IllegalConversionException: Cannot convert 'JavaScript NativeArray' into 'Java class <クラス名>'
また、下記のような例外が発生した場合は、「JavaScript の “aaa” という文字列」を「String 以外の型(Number など)で宣言されている JavaBean のプロパティ」に変換しようとした際に発生します。
NumberFormatException: For input string: "<文字列>"

サンプルコード

バイナリファイルを送受信するサンプル

Web サービスとして公開する JavaScript ラッパークラスの関数の引数、および、返却値の型に「byte[]」を指定することで、バイナリファイルを送受信することができます。
引き渡されたバイト配列は自動的に Base64 にエンコードされ SOAP メッセージとして送受信されます。
バイナリファイルの送受信を行うサンプルのファイルは以下の通りです。
(ファイルの中身は後述します。)
  • Webサービス・プロバイダ

    • src/main/jssp/src/sample/web_service/provider/public_storage_access.js
    • src/main/java/sample/web_service/provider/PublicStorageAccessService.java
    • src/main/webapp/WEB-INF/services/sample_public_storage/META-INF/services.xml
  • Webサービス・クライアント

    • src/main/jssp/src/sample/web_service/client/public_storage_access.html
    • src/main/jssp/src/sample/web_service/client/public_storage_access.js
    • src/main/conf/routing-jssp-config/sample_public_storage.xml
サンプルをプロジェクトに配置後、ユーザモジュールを作成し、warを作成し、デプロイを行います。
デプロイについての詳細は Webサービス・プロバイダ の「資材をデプロイする」、および、Webサービス・クライアント の「資材をデプロイする」を参照してください。
デプロイ後、以下の手順に従って、サンプル画面を表示します。
  1. 以下の URL にアクセスします。
    http://<HOST>:<PORT>/<CONTEXT_PATH>/sample/web_service/client/public_storage_access
  2. 以下のような画面が表示されることを確認します。

    ../../_images/appendix_1.png
このサンプルプログラムは、Webサービス・クライアント 側と Webサービス・プロバイダ 側の PublicStorage 内にあるファイルの内容を送受信します。
  • バイナリファイルを受信する

    ファイルパスを入力して「A -> B コピー」をクリックすると、Web サービスを経由して Webサービス・プロバイダ 側の PublicStorage からファイルの中身を取得し、Webサービス・クライアント 側の PublicStorage に保存します。
    「A -> B コピー」をクリックした場合の処理の流れは、以下の通りです。
../../_images/appendix_3.png
  • バイナリファイルを送信する

    ファイルパスを入力して「B -> A コピー」をクリックすると、Webサービス・クライアント 側の PublicStorage からファイルを読み取り、Web サービスを経由して Webサービス・プロバイダ 側の PublicStorage にファイルを保存します。
    「B -> A コピー」をクリックした場合の処理の流れは、以下の通りです。
../../_images/appendix_2.png

コラム

スクリプト開発モデルの場合、バイナリデータを送受信する際は String 型を使用します。

注意

Web サービスとして公開する関数の引数に JavaBean が指定されている場合、その JavaBean 内のバイト配列(byte[])型のプロパティは、正常にデータが送受信されません。
これは Apache Axis2 の現行仕様による制限です。
バイナリファイルを送受信する場合は、JavaBean のプロパティではなく、Web サービスとして公開する関数の引数としてバイト配列(byte[])を指定してください。
サンプルファイルの中身は、以下の通りです。
  • src/main/jssp/src/sample/web_service/provider/public_storage_access.js

    /**
     * ファイルの読み込み
     */
    function loadFile(path) {
        var storage = new PublicStorage(path);
        return storage.load();
    }
    
    /**
     * ファイルの書き込み
     */
    function saveFile(path, data) {
        var storage = new PublicStorage(path);
        return storage.save(data);
    }
    
  • src/main/java/sample/web_service/provider/PublicStorageAccessService.java

    package sample.web_service.provider;
    
    import jp.co.intra_mart.foundation.web_service.auth.WSUserInfo;
    import jp.co.intra_mart.jssp.util.JavaScriptUtility;
    
    import org.apache.axis2.AxisFault;
    
    public class PublicStorageAccessService {
        public byte[] loadFile(final WSUserInfo wsUserInfo, final String path) throws AxisFault {
            try {
                final String pagePath = "sample/web_service/provider/public_storage_access";
                final String functionName = "loadFile";
                final byte[] bytes = (byte[]) JavaScriptUtility.executeFunction(pagePath, functionName, byte[].class, path);
                return bytes;
            } catch (final Exception ex) {
                throw AxisFault.makeFault(ex);
            }
        }
    
        public boolean saveFile(final WSUserInfo wsUserInfo, final String path, final byte[] data) throws AxisFault {
            try {
                final String pagePath = "sample/web_service/provider/public_storage_access";
                final String functionName = "saveFile";
                JavaScriptUtility.executeVoidFunction(pagePath, functionName, path, data);
                return true;
            } catch (final Exception ex) {
                throw AxisFault.makeFault(ex);
            }
        }
    }
    
  • src/main/webapp/WEB-INF/services/sample_public_storage/META-INF/services.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <serviceGroup>
        <service name="SamplePublicStorageAccessService">
            <parameter name="ServiceClass">sample.web_service.provider.PublicStorageAccessService</parameter>
            <module ref="im_ws_auth"/>
            <messageReceivers>
                <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only" class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" />
                <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"  class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
            </messageReceivers>
    
            <parameter name="authz-uri">service://intra-mart.jp/public-resources/welcome-to-intramart</parameter>
    
            <operation name="loadFile">
                <parameter name="authz-uri">service://intra-mart.jp/public-resources/welcome-to-intramart</parameter>
            </operation>
    
            <operation name="saveFile">
                <parameter name="authz-uri">service://intra-mart.jp/public-resources/welcome-to-intramart</parameter>
            </operation>
    
        </service>
    </serviceGroup>
    
  • src/main/jssp/src/sample/web_service/client/public_storage_access.html

    <imart type="head">
    <script type="text/javascript">
    (function($) {
      $(function() {
        /**
         * ファイルの読み込み
         */
        $('#loadFile').click(function() {
          var userCd = $('#userCd').val();
          var password = $('#password').val();
          var pathA = $('#pathA').val();
          var pathB = $('#pathB').val();
    
          try {
            jQuery.ajax({
              async:false, cache:false, dataType:'text', type:'POST',
              url:'sample/web_service/client/public_storage_access/loadFile', data:{
                'userCd':userCd,
                'password':password,
                'pathA':pathA,
                'pathB':pathB
              },
              success:function(result) {
                imuiAlert(result == 'true' ? 'OK' : 'NG');
              },
              error:function() {
                imuiAlert('NG');
              }
            });
          } catch (ex) {
            imuiAlert('NG');
          }
        });
    
        /**
         * ファイルの書き込み
         */
        $('#saveFile').click(function() {
          var userCd = $('#userCd').val();
          var password = $('#password').val();
          var pathA = $('#pathA').val();
          var pathB = $('#pathB').val();
    
          try {
            jQuery.ajax({
              async:false, cache:false, dataType:'text', type:'POST',
              url:'sample/web_service/client/public_storage_access/saveFile', data:{
                'userCd':userCd,
                'password':password,
                'pathA':pathA,
                'pathB':pathB
              },
              success:function(result) {
                imuiAlert(result == 'true' ? 'OK' : 'NG');
              },
              error:function() {
                imuiAlert('NG');
              }
            });
          } catch (ex) {
            imuiAlert('NG');
          }
        });
      });
    })(jQuery);
    </script>
    </imart>
    
    <div class="imui-form-container-wide">
      <div class="imui-chapter-title">
        <h2>Web サービスにアクセスするユーザ情報</h2>
      </div>
      <table class="imui-form">
        <tbody>
          <tr>
            <th class="wd-20"><label class="imui-required">ユーザコード</label></th>
            <td><imart type="imuiTextbox" id="userCd" value="aoyagi" autofocus /></td>
          </tr>
          <tr>
            <th class="wd-20"><label class="imui-required">パスワード</label></th>
            <td><imart type="imuiTextbox" id="password" value="aoyagi" /></td>
          </tr>
        </tbody>
      </table>
      <div class="imui-chapter-title">
        <h2>PublicStorage のファイル</h2>
      </div>
      <table class="imui-form">
        <tbody>
          <tr>
            <th class="wd-20"><label class="imui-required">ファイルパスA (プロバイダ側)</label></th>
            <td><imart type="imuiTextbox" id="pathA" style="width: 400px;" /></td>
          </tr>
          <tr>
            <th class="wd-20"><label class="imui-required">ファイルパスB (クライアント側)</label></th>
            <td><imart type="imuiTextbox" id="pathB" style="width: 400px;" /></td>
          </tr>
        </tbody>
      </table>
      <div class="imui-operation-parts">
        <imart type="imuiButton" id="loadFile" value="A -> B にコピー" class="imui-large-button" />
        <imart type="imuiButton" id="saveFile" value="B -> A にコピー" class="imui-large-button" />
      </div>
    </div>
    
  • src/main/jssp/src/sample/web_service/client/public_storage_access.js

    // ホスト名、ポート番号、コンテキストパスは適宜置き換えてください。
    var wsdlURL = 'http://localhost:8080/imart/services/SamplePublicStorageAccessService?wsdl';
    
    /**
     * 初期化
     */
    function init() {
    }
    
    /**
     * 認証情報の取得
     */
    function getWSUserInfo(userCd, password) {
        return {
            'userID':userCd,
            'password':WSAuthDigestGenerator4WSSE.getDigest(userCd, password),
            'authType':WSAuthDigestGenerator4WSSE.getAuthType(),
            'loginGroupID':'default'
        };
    }
    
    /**
     * ファイルの読み込み
     */
    function loadFile(request) {
        try {
            var userCd = request.userCd;
            var password = request.password;
            var pathA = request.pathA;
            var pathB = request.pathB;
    
            var soapClient = new SOAPClient(wsdlURL);
            var wsUserInfo = getWSUserInfo(userCd, password);
            var result = soapClient.loadFile(wsUserInfo, pathA);
            Debug.console('読み取りに成功しました。', result);
    
            var storage = new PublicStorage(pathB);
            storage.save(result);
            Debug.console('書き込みに成功しました。');
    
            outputText('true');
        } catch (ex) {
            Debug.console('エラーが発生しました。', ex);
            throw ex;
        }
    }
    
    /**
     * ファイルの書き込み
     */
    function saveFile(request) {
        try {
            var userCd = request.userCd;
            var password = request.password;
            var pathA = request.pathA;
            var pathB = request.pathB;
    
            var storage = new PublicStorage(pathB);
            var data = storage.load();
            Debug.console('読み取りに成功しました。', data);
    
            var soapClient = new SOAPClient(wsdlURL);
            var wsUserInfo = getWSUserInfo(userCd, password);
            var result = soapClient.saveFile(wsUserInfo, pathA, data);
            Debug.console('書き込みに成功しました。');
            outputText('true');
        } catch (ex) {
            Debug.console('エラーが発生しました。', ex);
            throw ex;
        }
    }
    
    function outputText(text) {
        var response = Web.getHTTPResponse();
        response.setContentType('text/plain; charset=utf-8');
        response.sendMessageBodyString(text);
    }
    
  • src/main/conf/routing-jssp-config/sample_public_storage.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <routing-jssp-config xmlns="http://www.intra-mart.jp/router/routing-jssp-config"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.intra-mart.jp/router/routing-jssp-config routing-jssp-config.xsd">
    
    	<authz-default mapper="welcome-all" />
    
    	<file-mapping path="/sample/web_service/client/public_storage_access"
    		page="sample/web_service/client/public_storage_access">
    	</file-mapping>
    
    	<file-mapping path="/sample/web_service/client/public_storage_access/loadFile"
    		page="sample/web_service/client/public_storage_access" action="loadFile">
    	</file-mapping>
    
    	<file-mapping path="/sample/web_service/client/public_storage_access/saveFile"
    		page="sample/web_service/client/public_storage_access" action="saveFile">
    	</file-mapping>
    
    </routing-jssp-config>