SOAP Webサービスクライアントを作ろう(Apache Axis2 編)
ライブラリのダウンロード
Apache Axis2 サイトから axis2-1.6.1-bin をダウンロードする。
スタブを使用して Web サービス実行
ダウンロードしたライブラリには bin/wsdl2java.bat, bin/wsdl2java.sh が同梱されているため、java コマンドを直接叩く必要がない。
uri のみ指定するとデータバインディングクラスを全てサブクラスに持つ1クラスが巨大な Stub が作成されるので、
u オプションをつけてバインディングクラスを別に生成したほうがいい。
接続先には前回SOAP Webサービスクライアントを作ろう(Apache Axis 編)で作成したものを使用する。
wsdl2java.bat -uri http://localhost:49175/WS/ZaneliWS.asmx?WSDL -u
以下の 6 クラスが自動生成される。
- com.zaneli.www.ExtensionMapper
- com.zaneli.www.GetMessage
- com.zaneli.www.GetMessageResponse
- com.zaneli.www.ZaneliWS
- com.zaneli.www.ZaneliWSCallbackHandler
- com.zaneli.www.ZaneliWSStub
実行は以下の通り。の、はずが…
import java.rmi.RemoteException; import com.zaneli.www.GetMessage; import com.zaneli.www.GetMessageResponse; import com.zaneli.www.ZaneliWSStub; public class Axis2Executor1 { public String execute(String text, int num) throws RemoteException { ZaneliWSStub webService = new ZaneliWSStub(); GetMessage message = new GetMessage(); message.setText(text); message.setNum(num); GetMessageResponse response = webService.getMessage(message); return response.getGetMessageResult(); } public static void main(String[] args) throws Exception { System.out.println(new Axis2Executor1().execute("Axis2で実行", 111)); } }
実行してみるとエラーが…。
Exception in thread "main" org.apache.axis2.AxisFault: The input stream for an incoming message is null.
調べてみたところ、どうもヘッダにTransfer-Encoding: chunkedを指定していることが原因らしい。
(参考
Transfer-Encoding: chunked について : Apache Axis2
傭兵のメモ帳(blog版) | axis2でTransfer-Encoding: chunked を付けない方法
axis2について二点 - Java Solution - @IT
)
Axis2は HTTP1.1 を使用していて、デフォルトでこれをつけている。
今回は用意した ASP.NET Webサービス側に問題がありそうなのだけど、とりあえず動作確認のために
Transfer-Encoding: chunked をつけないように変更。
(前回のAxisでも CommonsHTTPSender を使用すると実行に失敗するので、ヘッダに
HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED, false を指定する必要があった。)
import java.rmi.RemoteException; import org.apache.axis2.transport.http.HTTPConstants; import com.zaneli.www.GetMessage; import com.zaneli.www.GetMessageResponse; import com.zaneli.www.ZaneliWSStub; public class Axis2Executor1 { public String execute(String text, int num) throws RemoteException { ZaneliWSStub webService = new ZaneliWSStub(); webService._getServiceClient().getOptions().setProperty( HTTPConstants.CHUNKED, Boolean.FALSE.toString()); GetMessage message = new GetMessage(); message.setText(text); message.setNum(num); GetMessageResponse response = webService.getMessage(message); return response.getGetMessageResult(); } public static void main(String[] args) throws Exception { System.out.println(new Axis2Executor1().execute("Axis2で実行", 111)); } }
「text=Axis2で実行, num=111」が出力されることを確認。
スタブを使用せず Web サービス実行
何だか Axis に比べて煩雑な気が…。
import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.OMNamespace; import org.apache.axiom.om.OMNode; import org.apache.axis2.AxisFault; import org.apache.axis2.addressing.EndpointReference; import org.apache.axis2.client.Options; import org.apache.axis2.client.ServiceClient; import org.apache.axis2.transport.http.HTTPConstants; public class Axis2Executor2 { private static final String WSDL_URI = "http://localhost:49175/WS/ZaneliWS.asmx?WSDL"; private static final String NAMESPACE = "http://www.zaneli.com/"; private static final String OPERATION_NAME = "GetMessage"; private static final String REQUEST_PARAM_TEXT = "text"; private static final String REQUEST_PARAM_NUM = "num"; private static final String RESPONSE_BODY_NAME = OPERATION_NAME + "Result"; public String execute(String text, int num) throws AxisFault { OMElement payload = createRequestElement(text, num); ServiceClient client = new ServiceClient(); client.setOptions(createOptions()); OMElement response = client.sendReceive(payload); for (OMNode node = response.getFirstOMChild() ; node != null ; node = node.getNextOMSibling()) { if (node instanceof OMElement) { if (RESPONSE_BODY_NAME.equals(((OMElement) node).getLocalName())) { return ((OMElement) node).getText(); } } } throw new IllegalStateException("レスポンスが無いんです!(><;"); } private static OMElement createRequestElement(String text, int num) { OMFactory factory = OMAbstractFactory.getOMFactory(); OMNamespace omNs = factory.createOMNamespace(NAMESPACE, OPERATION_NAME); OMElement method = factory.createOMElement(OPERATION_NAME, omNs); OMElement textElement = factory.createOMElement(REQUEST_PARAM_TEXT, omNs); textElement.setText(text); OMElement numElement = factory.createOMElement(REQUEST_PARAM_NUM, omNs); numElement.setText(Integer.toString(num)); method.addChild(textElement); method.addChild(numElement); return method; } private static Options createOptions() { EndpointReference targetEPR = new EndpointReference(WSDL_URI); Options options = new Options(); options.setTo(targetEPR); options.setAction(NAMESPACE + OPERATION_NAME); options.setProperty(HTTPConstants.CHUNKED, Boolean.FALSE.toString()); return options; } public static void main(String[] args) throws Exception { System.out.println(new Axis2Executor2().execute("Axis2で動的実行", 222)); } }
「text=Axis2で動的実行, num=222」が出力されることを確認。
非同期で実行する
自動生成される CallbackHandler を使用する。
import org.apache.axis2.transport.http.HTTPConstants; import com.zaneli.www.GetMessage; import com.zaneli.www.GetMessageResponse; import com.zaneli.www.ZaneliWSCallbackHandler; import com.zaneli.www.ZaneliWSStub; public class Axis2AscyncExecutor1 { public String execute(String text, int num) throws Exception { ZaneliWSStub webService = new ZaneliWSStub(); webService._getServiceClient().getOptions().setProperty( HTTPConstants.CHUNKED, Boolean.FALSE.toString()); GetMessage message = new GetMessage(); message.setText(text); message.setNum(num); ResponseReceiveHandler callbackHandler = new ResponseReceiveHandler(); webService.startgetMessage(message, callbackHandler); synchronized (callbackHandler) { while (!callbackHandler.isReceived()) { try { callbackHandler.wait(); } catch (Exception ignore) { } } if (callbackHandler.getException() != null) { throw callbackHandler.getException(); } return callbackHandler.getResponse().getGetMessageResult(); } } private static class ResponseReceiveHandler extends ZaneliWSCallbackHandler { private boolean received = false; private GetMessageResponse response; private Exception exception; @Override public synchronized void receiveResultgetMessage(GetMessageResponse response) { this.response = response; received = true; notify(); } @Override public synchronized void receiveErrorgetMessage(Exception exception) { this.exception = exception; received = true; notify(); } public synchronized boolean isReceived() { return received; } public synchronized GetMessageResponse getResponse() { return response; } public synchronized Exception getException() { return exception; } } public static void main(String[] args) throws Exception { System.out.println(new Axis2AscyncExecutor1().execute("Axis2で非同期実行", 333)); } }
「text=Axis2で非同期実行, num=333」が出力されることを確認。
ちなみにデフォルトでは非同期実行用、同期実行用のスタブが全て生成されるが、
wsdl2java にオプションをつけることでどちらかのみを生成するようにできる。
どちらかしか使用せず、スタブがたくさん生成されるのが嫌な場合にはこれをつける。
-a Generate async style code only (Default: off). -s Generate sync style code only (Default: off). Takes precedence over -a.
WS-Securityに対応する
Apache Rampartを使用する。
詳しい実装方法などはAxis2でクラウド用のWebサービスクライアントを作成するを参考に。