S2Axis 開発記 プロトタイプその1 最初の一歩
準備その4までで,S2Axis として何を作ればいいかめどが立ったので,とりあえず動くものを作ってみようということで,やってみました.
まずは簡単な方から,S2Provider
.こんな感じになりました.
package org.seasar.axis.server; import org.apache.axis.MessageContext; import org.apache.axis.providers.java.RPCProvider; import org.seasar.framework.container.ComponentDef; public class S2Provider extends RPCProvider { public static final String OPTION_COMPONENT_DEF = "componentDef"; protected Object makeNewServiceObject(final MessageContext msgContext, final String className) { final ComponentDef componentDef = (ComponentDef) msgContext .getService().getOption(OPTION_COMPONENT_DEF); return componentDef.getComponent(); } }
なはは,これだけですよ,これだけ.
最初は S2Handler
で設定したコンポーネント名を使って,S2Container
からコンポーネントを取ってこようかと思ったのですが,向こうで ComponentDef
まで取得するので,それを使うことにしました.
そのキーが OPTION_COMPONENT_DEF
.
次に S2Handler
.JWSHandler
をコピーして,今すぐ必要になりそうなところ以外は削りまくって,最低限のコードからスタートして,こんな感じになりました.
package org.seasar.axis.server; import java.util.HashMap; import java.util.Map; import org.apache.axis.AxisFault; import org.apache.axis.MessageContext; import org.apache.axis.components.logger.LogFactory; import org.apache.axis.handlers.BasicHandler; import org.apache.axis.handlers.soap.SOAPService; import org.apache.axis.transport.http.HTTPConstants; import org.apache.axis.utils.Messages; import org.apache.commons.logging.Log; import org.seasar.framework.container.ComponentDef; import org.seasar.framework.container.S2Container; import org.seasar.framework.container.factory.SingletonS2ContainerFactory; public class S2Handler extends BasicHandler { private static final Log logger = LogFactory.getLog(S2Handler.class .getName()); private final Map soapServices = new HashMap(); public void invoke(final MessageContext msgContext) throws AxisFault { try { setupService(msgContext); } catch (final Exception e) { logger.error(Messages.getMessage("exception00"), e); throw AxisFault.makeFault(e); } } protected void setupService(final MessageContext msgContext) throws Exception { final String componentName = getComponentName(msgContext); if (componentName == null) { return; } final S2Container container = SingletonS2ContainerFactory .getContainer(); if (!container.hasComponentDef(componentName)) { return; } msgContext.setService(getService(msgContext, container, componentName)); } protected String getComponentName(final MessageContext msgContext) { final String pathInfo = (String) msgContext .getProperty(HTTPConstants.MC_HTTP_SERVLETPATHINFO); if (pathInfo == null || pathInfo.length() == 0) { return null; } return pathInfo.substring(1); } protected SOAPService getService(final MessageContext msgContext, final S2Container container, final String componentName) throws AxisFault { final ComponentDef componentDef = container .getComponentDef(componentName); SOAPService soapService = (SOAPService) soapServices.get(componentDef); if (soapService == null) { soapService = createService(msgContext, componentDef); soapServices.put(componentDef, soapService); } soapService.setEngine(msgContext.getAxisEngine()); soapService.init(); // ?? return soapService; } protected SOAPService createService(final MessageContext msgContext, final ComponentDef componentDef) throws AxisFault { final SOAPService soapService = new SOAPService(new S2Provider()); final String componentClassName = componentDef.getComponentClass() .getName(); soapService.setName(componentClassName); soapService.setOption(S2Provider.OPTION_CLASSNAME, componentClassName); soapService.setOption(S2Provider.OPTION_COMPONENT_DEF, componentDef); soapService.getInitializedServiceDesc(msgContext); return soapService; } }
// ??
というコメントは JWSHandler
そのままです.(^^;
最初,リクエストされた URL をどう取得すればいいのかよく分からなかったのですが,URLMapper
というハンドラあったのでそれを参考にしました.
この 2 つのハンドラを Axis に組み込むには,普通ならデプロイ用の WSDD を書いて AdminClient というコマンドラインから起動できるクラスを使って Axis サーバに登録するようなのですが,面倒なので Axis の WSDD に直接書いてしまいました.
Axis のソースディレクトリの中にある org/apache/axis/server/server-config.wsdd
がそのファイルです.これを Axis を動かしている WEB アプリケーション (Axis のサンプルなら axis
) の WEB-INF
にコピーします.
その中を見ると /deployment/globalConfiguration/requestFlow
の下に JWSHandler
の定義が 2 つあるので,その後に次の内容を記述します.
<handler type="java:org.seasar.axis.server.S2Handler"/>
あと,S2 を使うので web.xml
には S2ContainerServlet
の記述を追加します.
それから,サービスとして呼び出されるクラスを用意しましょう.
package foo; public class Hello { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String say() { return message; } }
実につまらないクラスですが,サンプルなんてそんなものなのです.
後は app.dicon
だけです.
<?xml version="1.0" encoding="Shift_JIS"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN" "http://www.seasar.org/dtd/components.dtd" > <components> <component name="world" class="foo.Hello"> <property name="message">"Hello World!"</property> </component> <component name="yuri" class="foo.Hello"> <property name="message">"Hello EbiYuri!"</property> </component> </components>
前述の Hello
を二つ定義しています.
説明が雑ですが,これで準備は整いました.
本来なら Web サービスのクライアントもちゃんと作らないといけないのでしょうが,今回は手抜きしてブラウザから実行します.
http://localhost:8080/axis/services/world?method=say
この URL にアクセスすると...
<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <soapenv:Body> <sayResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" > <sayReturn xsi:type="xsd:string">Hello World!</sayReturn> </sayResponse> </soapenv:Body> </soapenv:Envelope>
見事にメッセージが返ってきました!! \(^o^)/
続いて次の URL .
http://localhost:8080/axis/services/yuri?method=say
こいつにアクセスすると...
<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <soapenv:Body> <sayResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" > <sayReturn xsi:type="xsd:string">Hello EbiYuri!</sayReturn> </sayResponse> </soapenv:Body> </soapenv:Envelope>
大・成・功.
ということで,ベースは出来たかな.後は必要ならセッションを使ったステートフルなサービスの対応とかかな? でも,これは Axis 固有の機能なので .NET との連携では使えないらしいですが.
そうすると他には... なんだろ? 何か要望があればコメントください.
ところで,S2 の名前空間ってコンテナからコンポーネント取ってくるときには使えないもの?
.dicon
ファイルの中から異なる名前空間のコンポーネントを取ってくるときと同じように,コンテナからコンポーネントを取るときにも名前空間接頭辞をつけてみたのですが,空振りしたみたい.
最後に...
準備の段階でハンドラの単体テストはどうすればいいのか調べておくべきでした.テストファーストしていないばかりか現時点では単体テストなしです...
心より恥じる.
でも,これはあくまでもプロトタイプなので... あうあう.