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のバグなんかなー。よくわからんけど。
回避するために、自分が思いついた方法は
先に結論を書くと、やっぱり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.xmlでservletの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も同じ。
これで確認ができる。