SOAP Webサービスクライアントを作ろう(Apache Axis 編)
Webサービスを用意する(ASP.NET)
今回は Java のライブラリである Apache Axis を利用してクライアントを作成するため、
せっかくなので繋ぎ先は Java 以外で。
Visual Studio 2010 で[新しいプロジェクト] -> [ASP.NET Web アプリケーション]を作成し、
ソリューション エクスプローラーで適当なディレクトリを作成し右クリック -> [追加] -> [新しい項目] -> [Web サービス]を選択。
文字列と数値を受け取って文字列を返すWebサービスを作成。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Services; namespace WebServices.WS { [WebService(Namespace = "http://www.zaneli.com/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] public class ZaneliWS : System.Web.Services.WebService { [WebMethod(Description="テスト用Webサービスオペレーション")] public string GetMessage(String text, int num) { return "text=" + text + ", num=" + num; } } }
自動生成される WSDL (http://localhost:49175/WS/ZaneliWS.asmx?WSDL)は以下の通り。
<?xml version="1.0" encoding="utf-8"?> <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://www.zaneli.com/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://www.zaneli.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> <wsdl:types> <s:schema elementFormDefault="qualified" targetNamespace="http://www.zaneli.com/"> <s:element name="GetMessage"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="text" type="s:string" /> <s:element minOccurs="1" maxOccurs="1" name="num" type="s:int" /> </s:sequence> </s:complexType> </s:element> <s:element name="GetMessageResponse"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="GetMessageResult" type="s:string" /> </s:sequence> </s:complexType> </s:element> </s:schema> </wsdl:types> <wsdl:message name="GetMessageSoapIn"> <wsdl:part name="parameters" element="tns:GetMessage" /> </wsdl:message> <wsdl:message name="GetMessageSoapOut"> <wsdl:part name="parameters" element="tns:GetMessageResponse" /> </wsdl:message> <wsdl:portType name="ZaneliWSSoap"> <wsdl:operation name="GetMessage"> <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">テスト用Webサービスオペレーション</wsdl:documentation> <wsdl:input message="tns:GetMessageSoapIn" /> <wsdl:output message="tns:GetMessageSoapOut" /> </wsdl:operation> </wsdl:portType> <wsdl:binding name="ZaneliWSSoap" type="tns:ZaneliWSSoap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="GetMessage"> <soap:operation soapAction="http://www.zaneli.com/GetMessage" style="document" /> <wsdl:input> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:binding name="ZaneliWSSoap12" type="tns:ZaneliWSSoap"> <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="GetMessage"> <soap12:operation soapAction="http://www.zaneli.com/GetMessage" style="document" /> <wsdl:input> <soap12:body use="literal" /> </wsdl:input> <wsdl:output> <soap12:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="ZaneliWS"> <wsdl:port name="ZaneliWSSoap" binding="tns:ZaneliWSSoap"> <soap:address location="http://localhost:49175/WS/ZaneliWS.asmx" /> </wsdl:port> <wsdl:port name="ZaneliWSSoap12" binding="tns:ZaneliWSSoap12"> <soap12:address location="http://localhost:49175/WS/ZaneliWS.asmx" /> </wsdl:port> </wsdl:service> </wsdl:definitions>
ライブラリのダウンロード
Apache Axis サイトから axis-bin-1_4 をダウンロードする。
2006 年時点で開発は停止し、Axis2 への移行が行われているため今後新規に Axis を選ぶことはあまり無いだろう。
(SOAP Web サービス自体が…というのは置いておいて。)
スタブを使用して Web サービス実行
WSDL2Java コマンドで WSDL からスタブを生成する。
java org.apache.axis.wsdl.WSDL2Java http://localhost:49175/WS/ZaneliWS.asmx?WSDL
以下の 5 クラスが自動生成される。
- com.zaneli.www.ZaneliWS
- com.zaneli.www.ZaneliWSLocator
- com.zaneli.www.ZaneliWSSoap
- com.zaneli.www.ZaneliWSSoap12Stub
- com.zaneli.www.ZaneliWSSoapStub
以下のように実行。
import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import com.zaneli.www.ZaneliWS; import com.zaneli.www.ZaneliWSLocator; import com.zaneli.www.ZaneliWSSoap; public class AxisExecutor1 { public String execute(String text, int num) throws ServiceException, RemoteException { ZaneliWS service = new ZaneliWSLocator(); ZaneliWSSoap port = service.getZaneliWSSoap(); return port.getMessage(text, num); } public static void main(String[] args) throws ServiceException, RemoteException { System.out.println(new AxisExecutor1().execute("Axisで実行", 111)); } }
「text=Axisで実行, num=111」が出力されることを確認。
スタブを使用せず Web サービス実行
WSDL の URL, サービス名, ポート名, オペレーション名を直接指定する場合は以下のようにする。
import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import javax.xml.namespace.QName; import javax.xml.rpc.Call; import javax.xml.rpc.ServiceException; import org.apache.axis.client.Service; public class AxisExecutor2 { 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 QName SERVICE_NAME = new QName(NAMESPACE, "ZaneliWS"); private static final QName PORT_NAME = new QName(NAMESPACE, "ZaneliWSSoap"); private static final String OPERATION_NAME = "GetMessage"; public String execute(String text, int num) throws MalformedURLException, ServiceException, RemoteException { Service service = new Service(new URL(WSDL_URI), SERVICE_NAME); Call call = service.createCall(PORT_NAME, OPERATION_NAME); return (String) call.invoke(new Object[]{text, num}); } public static void main(String[] args) throws ServiceException, RemoteException, MalformedURLException { System.out.println(new AxisExecutor2().execute("Axisで動的実行", 222)); } }
「text=Axisで動的実行, num=222」が出力されることを確認。
エンジンを入れ替える
Axis では HTTP1.0 で SOAP リクエストを送信しているが、エンジンを入れ替えることで HTTP1.1 での送信が可能になる。
CommonsHTTPSender を使用する場合、commons-httpclient と commons-codec をパスに通す必要がある。
import java.rmi.RemoteException; import java.util.Hashtable; import javax.xml.rpc.ServiceException; import org.apache.axis.AxisEngine; import org.apache.axis.SimpleTargetedChain; import org.apache.axis.client.AxisClient; import org.apache.axis.configuration.SimpleProvider; import org.apache.axis.transport.http.CommonsHTTPSender; import org.apache.axis.transport.http.HTTPConstants; import org.apache.axis.transport.http.HTTPTransport; import com.zaneli.www.ZaneliWS; import com.zaneli.www.ZaneliWSLocator; import com.zaneli.www.ZaneliWSSoap; public class AxisExecutor1 { public String execute(String text, int num) throws ServiceException, RemoteException { ZaneliWS service = new ZaneliWSLocator(); ((org.apache.axis.client.Service) service).setEngine(getEngine()); ZaneliWSSoap port = service.getZaneliWSSoap(); return port.getMessage(text, num); } private AxisEngine getEngine() { CommonsHTTPSender sender = new CommonsHTTPSender(); SimpleProvider clientConfig = new SimpleProvider(); clientConfig.deployTransport( HTTPTransport.DEFAULT_TRANSPORT_NAME, new SimpleTargetedChain(sender)); AxisClient engine = new AxisClient(clientConfig); engine.setOption(HTTPConstants.REQUEST_HEADERS, getChunkedOffHeader()); return engine; } private Hashtable<String, Boolean> getChunkedOffHeader() { Hashtable<String, Boolean> httpHeaders = new Hashtable<String, Boolean>(); httpHeaders.put(HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED, false); return httpHeaders; } public static void main(String[] args) throws ServiceException, RemoteException { System.out.println(new AxisExecutor1().execute("Axisで実行", 111)); } }
import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import java.util.Hashtable; import javax.xml.namespace.QName; import javax.xml.rpc.Call; import javax.xml.rpc.ServiceException; import org.apache.axis.AxisEngine; import org.apache.axis.SimpleTargetedChain; import org.apache.axis.client.AxisClient; import org.apache.axis.client.Service; import org.apache.axis.configuration.SimpleProvider; import org.apache.axis.transport.http.CommonsHTTPSender; import org.apache.axis.transport.http.HTTPConstants; import org.apache.axis.transport.http.HTTPTransport; public class AxisExecutor2 { 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 QName SERVICE_NAME = new QName(NAMESPACE, "ZaneliWS"); private static final QName PORT_NAME = new QName(NAMESPACE, "ZaneliWSSoap"); private static final String OPERATION_NAME = "GetMessage"; public String execute(String text, int num) throws MalformedURLException, ServiceException, RemoteException { Service service = new Service(new URL(WSDL_URI), SERVICE_NAME); service.setEngine(getEngine()); Call call = service.createCall(PORT_NAME, OPERATION_NAME); call.setProperty(HTTPConstants.REQUEST_HEADERS, getChunkedOffHeader()); return (String) call.invoke(new Object[]{text, num}); } private AxisEngine getEngine() { SimpleProvider clientConfig = new SimpleProvider(); clientConfig.deployTransport( HTTPTransport.DEFAULT_TRANSPORT_NAME, new SimpleTargetedChain(new CommonsHTTPSender())); return new AxisClient(clientConfig); } private Hashtable<String, Boolean> getChunkedOffHeader() { Hashtable<String, Boolean> httpHeaders = new Hashtable<String, Boolean>(); httpHeaders.put(HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED, false); return httpHeaders; } public static void main(String[] args) throws ServiceException, RemoteException, MalformedURLException { System.out.println(new AxisExecutor2().execute("Axisで動的実行", 222)); } }
getEngine() で CommonsHTTPSender を設定するようにしている。
getChunkedOffHeader() で何をやっているのかは次回 Axis2 編で触れる予定。
WS-Securityに対応する
Apache WSS4Jを使用し、エンジン設定の際に
private AxisEngine getEngine() { CommonsHTTPSender sender = new CommonsHTTPSender(); SimpleProvider clientConfig = new SimpleProvider(); clientConfig.deployTransport( HTTPTransport.DEFAULT_TRANSPORT_NAME, new SimpleTargetedChain(sender)); AxisClient engine = new AxisClient(clientConfig); engine.setOption(HTTPConstants.REQUEST_HEADERS, getChunkedOffHeader()); clientConfig.setGlobalRequest(getSecuritySender()); return engine; }
と指定すれば設定可能。
getSecuritySender() の詳細は今後気が向けばまとめよう。