S2Axis 開発記 仕様その1 コンポーネント名

先に書いたプロトタイプでは,リクエスURI の PATH_INFO から先頭の '/' を取り除いたものをコンポーネントの名前としています.コンポーネント名が '/' から始まるのはどうかなぁ? って思ったからなんですが,S2Struts なんかだとそのまま '/' 残してるんですね.
S2Axis も合わせた方がいいですかね?

「山田優を語る会」

石原さとみ眞鍋かをりに続く語る会第三弾! 08/29 (日) に開催決定!!
08/07 に見逃したチューボーですよ!」をまこたんが録画してくれていたので,それを眺めながら山田優について語りましょう.ちょい可愛!
まこたん,来週 (08/25) の「ぶっちゃけ! 99」も録画お願い.ちょい辛!


01:45 追記
キーワード見たらすごく話題になっているようでびっくり.24時間テレビに出てたんだぁ.また見逃した... (;_;)


02:25 追記
優ちゃんがレベッカを歌っているところを見ました!! さすが歌って踊れるモデル,かっけー!!!!
っていうかモデルの前から歌って踊れるタレントだったわけですが.

本棚

本棚.org ってのが流行っているみたいですが,皆さんどうやって登録しているのでしょう?
Amazon とかで購入した分だけでもまとめて登録できるのでしょうか? さっき Amazon の「マイストア」を見たら,「持っている商品」が 551 でした.(^^;
全部ではないにしてもほとんどが書籍なわけで,普通に書店で購入した本もあるし,とても手作業で入力する気にはなれません.
ちなみに orkut のプロフィールに使っている写真は部屋の壁一面を覆っている本棚です♪
幅 50cm,高さ 200cm のものが 6 本.机やベッドとお揃いの真っ白なもので,格好いいのです.
しかし,それでも入りきらない本が部屋の床を埋めつつあります.職場にも大量の本を置いているし... 困ったものだ.

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


次に S2HandlerJWSHandler をコピーして,今すぐ必要になりそうなところ以外は削りまくって,最低限のコードからスタートして,こんな感じになりました.

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 ファイルの中から異なる名前空間コンポーネントを取ってくるときと同じように,コンテナからコンポーネントを取るときにも名前空間接頭辞をつけてみたのですが,空振りしたみたい.


最後に...
準備の段階でハンドラの単体テストはどうすればいいのか調べておくべきでした.テストファーストしていないばかりか現時点では単体テストなしです...
心より恥じる.
でも,これはあくまでもプロトタイプなので... あうあう.