POJOのActionFormをsessionスコープで

なんでかよくわからんけど、POJOのActionFormをsessionスコープで利用できなかった。



調べてみて、RequestUtils#canReuseActionForm()があやしいことがわかった。
RequestUtils#canReuseActionForm()の処理で、requestかsessionに格納されているActionFormとFormBeanConfig#getType()で取得するクラスがいっしょかを比較してる。
POJOのActionFormを利用している場合、ActionFormはBeanValidatorFormになっててFormBeanConfig#getType()はPOJOになるから常にcanReuseがfalseになってる。



・・・今思ったんだけど、requestスコープでも結局ダメってことみたい。
そういえば、Actionの連鎖をしたときにPOJOのActionFormに設定した値が無効になってるのがあった。
あれもこれが原因だったのか。突然の解決。結局上書きの問題は未解決だから、Action連鎖が危険なことには変わりないか・・・
Struts in Actionを真似て、mutableを追加してfalseのときはset()を無効にするとかの処理が必要かな
とすると独自のBeanValidatorFormが必要になってしまう。


また、話がそれてきている。
「Action連鎖でPOJOActionFormの内容がrequest情報で上書きされるかも」として、本当に発生するかと解決は別途調べてみようっと。



POJOのActionFormをsessionスコープで利用しよーに戻って。
これってStrutsのバグなんかなー。よくわからんけど。


回避するために、自分が思いついた方法は

  1. Strutsの機能のみで回避する
  2. いっそS2ContatierでPOJOのActionFormの有効スコープを管理してしまう。
  3. 両方を混ぜてみる

先に結論を書くと、やっぱり2か3がありだと思う。
struts-config.xmlの記述を減らすこと(かわりにDICONファイルへの記述が増えるから意味がないけど)を目的としていてその副産物みたいな感じでStrutsのバグ(?)を吸収してるっていう雰囲気があるから。


Strutsの機能のみで回避する

これはActionFormの設定情報を管理しているFormBeanConfigクラスを独自に拡張して対応しよーって方法。

結局FormBeanConfig#getType()でBeanValidatorFormだよってかえってきて、FormBeanConfig#formBeanClass()からはPOJOのActionFormを返すという荒業でいいと思う。


独自のFormBeanConfigはこんな感じ

public class POJOFormBeanConfig extends FormBeanConfig {

    public String getType() {
        return BeanValidatorForm.class.getName();
    }

    public String getPOJOType() {
        return this.type;
    }

    protected Class formBeanClass() {

        ClassLoader classLoader = Thread.currentThread()
                .getContextClassLoader();
        if (classLoader == null) {
            classLoader = this.getClass().getClassLoader();
        }
        try {
            return (classLoader.loadClass(getPOJOType()));
        } catch (Exception e) {
            return (null);
        }

    }
    
}

そしてstruts-config.xmlのform-beanでclassName属性を指定すればOK。

    <form-bean
        className="study.xxx.extention.struts.config.POJOFormBeanConfig"
        name="logonForm"
        type="study.xxx.party.dto.Party"
    />
いっそS2ContatierでPOJOのActionFormの有効スコープを管理してしまう。

この方法のポイントは、POJOのActionFormをstruts-config.xmlで定義するんじゃなくてDICONファイルで定義するところ。
これの欠点は、struts-mapping.xmlのaction-mappingのscope属性が無意味になってしまう・・・。S2Containerでの管理だから。
まずは、S2ContatinerからPOJOのActionFormを利用するためにDICONファイルを記述。

party.diconはこんな感じ

<components>
    <component name="logonForm"
               class="study.xxx.party.dto.Party" instance="session"/>
</components>

こっちでも独自のFormBeanConfigを作ってS2ContainerからPOJOのActionFormを作るようにして

public class XxxFormBeanConfig extends FormBeanConfig {

    public ActionForm createActionForm(ActionServlet servlet)
            throws IllegalAccessException, InstantiationException {

        S2Container container = SingletonS2ContainerFactory.getContainer();
        Object obj = container.getComponent(name);

        ActionForm form = new BeanValidatorForm(obj);
        form.setServlet(servlet);
        return form;
    }

}

XxxFormBeanConfigをstruts-config.xmlからじゃなくてparty.diconから取得できるようにするために独自のModuleConfigを作成

public class XxxModuleConfig extends ModuleConfigImpl implements ModuleConfig {

    public XxxModuleConfig(String prefix) {
        super(prefix);
    }

    public FormBeanConfig findFormBeanConfig(String name) {
        FormBeanConfig config = super.findFormBeanConfig(name);
        if (config == null) {
            S2Container container = SingletonS2ContainerFactory.getContainer();
            ComponentDef def = container.getComponentDef(name);
            config = new XxxFormBeanConfig();
            config.setName(name);
            config.setType(def.getComponentName());
            config.freeze();
        }
        return config;
    }

}

XxxModuleConfigを利用するようにするためFactoryも独自にする必要があって

public class XxxModuleConfigFactory extends ModuleConfigFactory {

    public ModuleConfig createModuleConfig(String prefix) {
        return new XxxModuleConfig(prefix);
    }

}

そのFactoryをweb.xmlservletのinit-paramで指定ればOK。

  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.seasar.struts.servlet.S2ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
      <param-name>configFactory</param-name>
      <param-value>study.xxx.extention.struts.config.XxxModuleConfigFactory</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>

これでちゃんとsessionスコープになってることが確認できる。

両方を混ぜてみる

これは、POJOActionFormのスコープ管理はStrutsにやってもらうけど、インスタンスはS2Containerから作成するよっという案。
欠点は、上の案の逆でDICONファイルに記述したinstance属性の意味がなくなる(意味をなくさないといけない)こと。

この場合のDICONファイルは下のように記述してinstance属性をprototypeにしないといけない。

<components>
    <component name="logonForm"
               class="study.xxx.party.dto.Party" instance="prototype"/>
</components>

FormBeanConfigは、前のXxxFormBeanConfigと同じ。


ModuleConfigは、XxxModuleConfigをちょっと修正

public class XxxModuleConfig extends ModuleConfigImpl implements ModuleConfig {

    public XxxModuleConfig(String prefix) {
        super(prefix);
    }

    public FormBeanConfig findFormBeanConfig(String name) {
        FormBeanConfig config = super.findFormBeanConfig(name);
        if (config == null) {
            //S2Container container = SingletonS2ContainerFactory.getContainer();
            //ComponentDef def = container.getComponentDef(name);
            config = new XxxFormBeanConfig();
            config.setName(name);
            //config.setType(def.getComponentName());
            config.setType(BeanValidatorForm.class.getName());
            config.freeze();
        }
        return config;
    }
    
}

factoryは、XxxModuleConfigFactoryと同じで、web.xmlも同じ。

これで確認ができる。