ファイヤープロジェクト
アクションからアクションにフォワードした際のrequestスコープのActionFormについて
2004-05-23T15:30+09:00   matsu
場合によってはアクションからアクションにフォワードしたい場合がある.で,この際にrequestスコープのActionFormがどうなるかというのを,はっきりさせてみた.なお,ここではStruts-1.1で実験した.Struts-1.0.2とは動きが割と異なるようなので注意.
struts-config.xmlのaction-mappingsが以下だったとする.
  <action-mappings>
    <action    path="/Action1"
               type="matsu.struts.sample.Action1"
	       scope="request"
	       name="Type1Form">
      <forward name="success"        path="/Out.do" />
    </action>
    <action    path="/Action2"
               type="matsu.struts.sample.Action2"
	       scope="request"
	       name="Type2Form">
      <forward name="success"              path="/Out.do" />
    </action>
    <action    path="/Action3"
               type="matsu.struts.sample.Action3"
	       scope="request"
	       name="Type2Form">
      <forward name="success"              path="/Out.do" />
    </action>
    <action    path="/Out"
               type="matsu.struts.sample.OutAction" 
	       scope="request"
	       name="Type1Form">
      <forward name="success"              path="/out.jsp" />
    </action>
</action-mappings>
4つのアクションがあり,Action1,2,3からOutへフォワードする.いずれのアクションもrequestスコープのActionFormである.これをもとにWebアプリケーションを作成して,以下を確認する.
requestスコープの範囲
JSPへフォワードする際と同様,HTTPセッションと同じ範囲と考えてよいと思うが,念のため.
ActionFormの生成
上と被るような気がするが,フォワード元とフォワード先が同じクラスだった場合に,ActionFormは同じインスタンスが使用されるのか?それともAction毎にnewされるのか?
ActionFormの初期化
HTTPリクエストの値をActionFormに詰め込む作業の確認.
異なるActionFormを持つActionへのフォワード
フォワード前のActionのActionFormインスタンスはフォワード後どうなる?
ActionFormの引渡し
フォワード前のActionでフォワード後のActionをnewして設定などしてフォワード後のActionに渡すことができるのか?
確認のためにサンプルを作成してみた.これ.ビルド方法はだいたい以下.
  1. 展開
  2. ant init
  3. libディレクトリの下にstrutsのjarファイルを置く.動作確認時は以下を置いた.
    commons-beanutils.jar    commons-lang.jar         struts-legacy.jar
    commons-collections.jar  commons-logging.jar      struts.jar
    commons-digester.jar     commons-validator.jar    
    commons-fileupload.jar   jakarta-oro.jar        
    
  4. src/WEB-INFの下にstrutsのタグリブを置く.動作確認時は以下を置いた.
    struts-form.tld    struts-template.tld
    struts-html.tld    struts-tiles.tld
    struts-bean.tld    struts-logic.tld
    struts.tld         struts-nested.tld 
    
  5. ant warを実行すると,warファイルができる.
結論から言うと,servletと同様,HTTPセッションの範囲と同じである.すなわちHTTPリクエストを受けてからレスポンスを返すまでであり,HttpServletRequestの生存期間と一致する...というのは,ActionFormのインスタンスはrequestスコープならHttpServletRequest,それ以外ならHttpSessionにsetAttributeされる.以下はStrutsのソースから.
        ActionForm instance = null;
        HttpSession session = null;
        if ("request".equals(mapping.getScope())) {
            instance = (ActionForm) request.getAttribute(attribute);
        } else {
            session = request.getSession();
            instance = (ActionForm) session.getAttribute(attribute);
        }
結論から言うと,フォワード元とフォワード先が同じクラスだった場合に,ActionFormは同じインスタンスが使用される.ただし,各Actionのexecuteを実行する前に,毎回ActionFormを初期化する.サンプルでこれを確認できる.まずAction1.
そしてOut.
さらにType1Form.
Action1,2,3へのPOSTメソッドを投げるためのページindex.jsp.
最後に最終的なフォワード先out.jsp.
index.jspのからmessageの値を"ACION1"と設定して投げてみた.以下catalina.out.
Type1Form created : 3
Type1Form 3 setMessage "ACTION1"
===== Action1 execute START =====
GET Type1Form message : "ACTION1"
Type1Form 3 setMessage "Action1 checked. ACTION1"
Type1Form 3 setMessage2 "Not over write"
from request : matsu.struts.sample.Type1Form@1c4a5ec
parameter form : matsu.struts.sample.Type1Form@1c4a5ec
===== Action1 execute END =====
Type1Form 3 setMessage "ACTION1"
===== OutAction execute START =====
GET type1Form message : "ACTION1"
Type1Form 3 setMessage "OutAction checked. ACTION1"
parameter form : matsu.struts.sample.Type1Form@1c4a5ec
Session Attribute Names : org.apache.struts.action.LOCALE
Request Attribute Names  : org.apache.struts.action.MESSAGE
                  Values : org.apache.struts.util.PropertyMessageResources@178aae1
Request Attribute Names  : org.apache.struts.action.mapping.instance
                  Values : ActionConfig[path=/Out,name=Type1Form,scope=request,type=matsu.struts.sample.OutAction
Request Attribute Names  : Type1Form
                  Values : matsu.struts.sample.Type1Form@1c4a5ec
Request Attribute Names  : org.apache.struts.action.MODULE
                  Values : org.apache.struts.config.impl.ModuleConfigImpl@11abd68
===== OutAction execute END =====
"matsu.struts.sample.Type1Form@1c4a5ec"というのはオブジェクトを一意に示す文字列であるが,Action1とOutのexecuteの引数formでこれが一致し,さらにAction1とOutにおいてrequestからキー"Type1Form"でgetAttributeしたものも一致する.さらにType1Formのコンストラスタで出力する"Type1Form created : n"という文字列も1HTTP要求で一回しか出て来ない.ソース(主にorg.apache.struts.util.RequestUtils.java)の雰囲気からするとActionServletのActionForm作成判断動作は以下である(DynaFormBeanっぽい処理はここでは無視).
  1. HttpServletRequest(requestスコープでなければHttpSession)にstruts-config.xmlのactionタグのname属性をキーにgetAttribute.
  2. あればそのクラスをチェックしstruts-config.xmlと矛盾しなければ,それを使用.
  3. なければnew.
ブラウザでは以下が出た.
messageの値を見ると,Action1におけるmessageに対する処理が入っていない.もう一度catalina.outを見ると,
Type1Form 3 setMessage "ACTION1"
===== Action1 execute START =====
...
===== Action1 execute END =====
Type1Form 3 setMessage "ACTION1"
===== OutAction execute START =====
となっており,各アクションのexecute呼び出しの前にType1FormのsetMessageメソッドが呼ばれていることが分かる.ここで,先のサンプルでのmessage2の値に注目してみる.これはAction1で設定し,out.jspでも出力された.HTTP要求のパラメータはmessageの値だけでmessage2の値はなかったので,Action1での設定が上書きされることなくOutまで届き,そしてout.jspでも出力された.
サンプルではAction2はActionFormにType2Formを持ち,Type1Formを持つアクションOutにフォワードする.Action2.javaは以下である.
Action2のActionFormであるType2Formは以下である.
index.jspを使用してAction2に要求してみた.以下はcatalina.out.
Type2Form created : 2
Type2Form 2 setMessage "ACTION2"
===== Action2 execute START =====
GET type2Form message : "ACTION2"
Type2Form 2 setMessage "Action2 checked. ACTION2"
from request : matsu.struts.sample.Type2Form@3eec1a
parameter form : matsu.struts.sample.Type2Form@3eec1a
===== Action2 execute END =====
Type1Form created : 4
Type1Form 4 setMessage "ACTION2"
===== OutAction execute START =====
GET type1Form message : "ACTION2"
Type1Form 4 setMessage "OutAction checked. ACTION2"
parameter form : matsu.struts.sample.Type1Form@fedfb6
Session Attribute Names : org.apache.struts.action.LOCALE
Request Attribute Names  : org.apache.struts.action.MESSAGE
                  Values : org.apache.struts.util.PropertyMessageResources@178aae1
Request Attribute Names  : org.apache.struts.action.mapping.instance
                  Values : ActionConfig[path=/Out,name=Type1Form,scope=request,type=matsu.struts.sample.OutAction
Request Attribute Names  : Type2Form
                  Values : matsu.struts.sample.Type2Form@3eec1a
Request Attribute Names  : Type1Form
                  Values : matsu.struts.sample.Type1Form@fedfb6
Request Attribute Names  : org.apache.struts.action.MODULE
                  Values : org.apache.struts.config.impl.ModuleConfigImpl@11abd68
===== OutAction execute END =====
そしてブラウザ出力.
意外にも(?)Type2Formに格納された値がType1Formにも設定されている.catalina.outをもう一度みてみると,要求のmessageの値がAction2のexecute前にType2Formに設定され,
Type2Form created : 2
Type2Form 2 setMessage "ACTION2"
===== Action2 execute START =====
さらにOutのexecute前にType1Formに設定されている.
Type1Form created : 4
Type1Form 4 setMessage "ACTION2"
===== OutAction execute START =====
すなわち,HTTP要求のパラメータは全て,各Actionのexecute前にActionFormに設定される.一つ目のActionのActinFormにアクセサがあっても,二つ目のActionのActionFromになければ,HTTP要求のパラメータに含めることができない(やるとこける).
前節のcatalina.outの出力で,フォワード前のActionFormがフォワード後のActionのexecute時にも参照できることが確認できる.
Request Attribute Names  : Type2Form
                  Values : matsu.struts.sample.Type2Form@3eec1a
Request Attribute Names  : Type1Form
                  Values : matsu.struts.sample.Type1Form@fedfb6
ということで,フォワード前Actionのexecuteでフォワード後ActionのActionFormを作成し,突っ込むことはできそうである.すなわちキーをstruts-config.xmlのactionタグのname属性の値として,HttpSevletRequestにsetAttributeする.サンプルではAction3でこれを行っている.
index.jspからAcion3.doに要求してみた.以下はcatalina.out.
Type2Form created : 2
Type2Form 2 setMessage "ACTION3"
===== Action3 execute START =====
Type1Form created : 2
Type1Form 2 setMessage "Action3 checked."
Type1Form 2 setMessage2 "Action3 checked."
from request : matsu.struts.sample.Type1Form@300429
===== Action3 execute END =====
Type1Form 2 setMessage "ACTION3"
===== OutAction execute START =====
GET type1Form message : "ACTION3"
Type1Form 2 setMessage "OutAction checked. ACTION3"
parameter form : matsu.struts.sample.Type1Form@300429
Session Attribute Names : org.apache.struts.action.LOCALE
Request Attribute Names  : org.apache.struts.action.MESSAGE
                  Values : org.apache.struts.util.PropertyMessageResources@1e2481b
Request Attribute Names  : org.apache.struts.action.mapping.instance
                  Values : ActionConfig[path=/Out,name=Type1Form,scope=request,type=matsu.struts.sample.OutAction
Request Attribute Names  : Type2Form
                  Values : matsu.struts.sample.Type2Form@ebf3f0
Request Attribute Names  : Type1Form
                  Values : matsu.struts.sample.Type1Form@300429
Request Attribute Names  : org.apache.struts.action.MODULE
                  Values : org.apache.struts.config.impl.ModuleConfigImpl@dc4c81
===== OutAction execute END =====
そしてブラウザ出力.
Action3はType2FormをActionFormとし,execute内で
	Type1Form type1Form = new Type1Form();
	type1Form.setMessage("Action3 checked.");
	type1Form.setMessage2("Action3 checked.");
	request.setAttribute("Type1Form", type1Form);
等として,フォワード後のOutのためのActionFormをnewしてrequestに格納している.これは例によって,Outのexecute前に値を設定されているが,
Type1Form 2 setMessage "ACTION3"
===== OutAction execute START =====
それに注意すれば,ActionFormは無事フォワード後のActionに渡っている.
from request : matsu.struts.sample.Type1Form@300429
===== Action3 execute END =====
...
Request Attribute Names  : Type1Form
                  Values : matsu.struts.sample.Type1Form@300429
以上をまとめると,ActionからActionへフォワードする際のrequestスコープのActinFormについて,以下が言える.
    requestスコープのActionFormはHTTP要求受信時に作成され,HTTPServletRequestにて保持される.
    • キーはactionタグの属性nameで指定した値である.
    • フォワード前後で異なるActionFormにした場合,フォワード後のActionFormでは両方のActionFormが参照できる.
    • 明示的に消すという操作はないらしく(HttpServletRequestが消える時にActionFormも消える),フォワード前にフォワード後のActionFormをsetAttributeすることができる.
    requestスコープのActionFormは各アクションのexecute前に毎回HTTP要求のパラメータで初期化される.
    • フォワード前のexecuteでHTTP要求のパラメータにあったActionFormのフィールドを上書きしても,フォワード後のexecuteまでに再びHTTP要求のパラメータで上書きされてしまう.
    • HTTP要求のパラメータにないものは,フォワード前後のActionで保持される(execute前に上書きされない).
    • 上はフォワード前後のActionFormのクラスの違いに関係なく行われるので注意が必要(例えばフォワード前のActionFormにはアクセサがあるが,フォワード後のActionFormにアクセサがないパラメータがHTTP要求にあるとこける).
matsu(C)
Since 2002
Mail to matsu