Seasar DI Container with AOP

概要

S2Strutsの機能について説明します。

ActionFormとActionの定義

ActionFormとActionは、Strutsのクラスを継承して定義できますが、S2Strutsではそれらを継承せずにPOJOとして定義することもできます。

Actionには、適切なsetterメソッドによりSeasar2のコンポーネントをDIすることができます。 ただし、DIを行う前提として、ActionをコンポーネントとしてS2コンテナに登録しておく必要があります。 登録の方法については、Actionコンポーネントの登録を参照してください。

ActionFormのインスタンスはStrutsで管理されるため、ActionFormにはSeasar2のコンポーネントはDIされません。 またActionFormをS2コンテナに登録する必要はありません(原則的には登録しないでください)。

POJO ActionForm

POJOのActionFormは、Strutsのorg.apache.struts.action.ActionFormを継承しないだけで、役割は同じです。HTMLのフォームに対応するプロパティを持つように定義してください。

public class AddForm {
    private String arg1;
    private String arg2;
    private String result;
    public String getArg1(){ return arg1; }
    public void setArg1(String arg1){ this.arg1 = arg1; }  
    public String getArg1(){ return arg1; }
    public void setArg2(String arg2){ this.arg2 = arg2; }
    public String getArg2(){ return arg2; }
    public void setResult(String result){ this.result = result; }
}

resetメソッド

次のシグネチャのメソッドが定義されている場合、ActionFormに値が設定される前に呼び出されます。 SessionスコープのActionFormを使う場合で、プロパティのリセットが必要な場合に使用してください。

public void reset();

POJO Action

POJOのActionは、Strutsのorg.apache.struts.action.Actionを継承しないだけで、役割は同じです。

public class AddAction {
    private AddForm addForm;
    public void setAddForm(AddForm addForm) {
        this.addForm = addForm;
    }
    public String calculate() {
        int result = Integer.valueOf(addForm.getArg1())
                + Integer.valueOf(addForm.getArg2());
        addForm.setResult(String.valueOf(result));
        return "success";
    }
}

アクションメソッドの定義

Actionにはリクエストに対応するメソッドを定義します。 メソッドのシグネチャは次のようなものをサポートしています(xxxは任意の名称です)。 Forwardが必要な場合、ActionForwardの名前を返す必要があります
  • Forwardしない場合
    public void xxx();
    
  • インデックスつきのボタンやリンクから呼び出されForwardしない場合
    public void xxx(int index);
    
  • Forwardする場合
    public String xxx();
    
  • インデックスつきのボタンやリンクから呼び出されForwardする場合
    public String xxx(int index);
    

アクションメソッドの呼び出し

アクションメソッドの呼び出しは、S2Strutsのタグライブラリを利用して呼び出すことができます。

<s2struts:submit action="@{add_addAction.calculate}">
    calculate
</s2struts:submit>

自動バインディング

POJO Actionにsetterメソッドがある場合、アクションのメソッドが実行される前に、プロパティ名をキーとしてHttpServletRequestHttpSessionから取得した値がプロパティにインポートされます。 値の取得を試みる優先順位は次の通りです。

  1. HttpServletRequest#getAttribute(プロパティ名)
  2. HttpServletRequest#getParameter(プロパティ名)
  3. HttpSession#getAttribute(プロパティ名)

また、POJO Actionにgetterメソッドがある場合、アクションのメソッドが実行された後で、プロパティの値がプロパティ名をキーとしてHttpServletRequestHttpSessionにエキスポートされます。

  1. デフォルトで、HttpServletRequest#setAttribute(プロパティ名,プロパティ値)が実行されます
  2. アノテーションで指定された場合に限り、HttpSession#setAttribute(プロパティ名,プロパティ値)が実行されます
@ExportToSession()
public String getFoo() {
    return foo;
}

ActionFormのインポートとエクスポートもこの自動バインディングの仕組みを用いて行われます。

Actionのマッピング定義の登録

Actionのマッピング定義に登録する方法は大きく分けて2つあります。 struts-config.xmlに記述する方法と、記述しない方法です。 記述しない場合は、無設定Strutsという機能を使います。

struts-config.xmlに記述する方法

通常の方法

通常通り、Strutsでaction-mapping要素内にaction要素を記述する方法です。
<action
    path="/add"
    type="org.seasar.struts.examples.add.AddAction"
    name="calcForm"
    scope="request"
    validate="false"
    input="/pages/addInput.html"/>
   <forward name="success" path="/pages/result.html" />
</action>

action要素のpath属性とActionコンポーネント名を一致させる方法

action要素のtype属性にActionProxyを指定する方法

<action
    path="/add"
    type="org.seasar.struts.action.ProxyAction"
    name="calcForm"
    scope="request"
    validate="false"
    input="/pages/addInput.html"/>
   <forward name="success" path="/pages/result.html" />
</action>
S2Strutsで管理するコンポーネントはaction要素のpath属性に指定した名前で登録しておく必要があります。

action要素のtype属性に何も指定しない方法

<action
    path="/add"
    name="calcForm"
    scope="request"
    validate="false"
   <forward name="success" path="/pages/result.html" />
</action>
S2Strutsで管理するコンポーネントはaction要素のpath属性に指定した名前で登録しておく必要があります。 この方法を利用する場合、action要素のtype属性、forward属性、include属性には何も指定してはいけません。

無設定Strutsを利用する方法

設定をアノテーションで記述します。無設定Strutsについては無設定Sturtsリファレンスを参照してください。
@StrutsActionForm
public class AddForm {
    ...
}
@StrutsAction(input = AddAction.ADD)
public class AddAction {
...
}

Actionコンポーネントの登録

diconファイルにひとつずつ記述する方法

Actionクラスの数だけ設定を記述します。インスタンス属性はrequestにします。
<component class="org.seasar.struts.examples.add.AddAction" instance="request"/>

自動登録を使用する方法

設定に従い、コンポーネントを一括で登録します。インスタンス属性はrequestにします。
<component
  class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister">
  <property name="instanceDef">
    @org.seasar.framework.container.deployer.InstanceDefFactory@REQUEST
  </property>
  <property name="autoNaming">
    <component class="org.seasar.framework.container.autoregister.DefaultAutoNaming"/>
  </property>
  <initMethod name="addClassPattern">
    <arg>"examples.action"</arg>
    <arg>".*Action"</arg>
  </initMethod>
</component>
この設定は、app.diconもしくはapp.diconにインクルードされるdiconファイルで行ってください。 詳しくはコンポーネントの自動登録を参照してください。

SMART deployを使用する方法

命名規約によりActionクラスのパッケージ名とクラス名を決定し、自動で登録されるようにします。 SMART deployについては、S2コンテナのドキュメントHOT deployを参照してください。 HOT deployはSMART deployの1つのモードです。

メッセージの管理

org.seasar.struts.pojo.MessageManagerクラスを利用すると任意のコンポーネントからエラーメッセージの表示を管理する事が可能になります。

MessageManager.addGlobalError("examplemessage");
MessageManager.addGlobalError("examplemessage", "foo");
MessageManager.addGlobalError("examplemessage", "bar", "baz", "qux");
MessageManager.addError("property", "examplemessage");
MessageManager.addError("property", "examplemessage", "foo");
MessageManager.addError("property", "examplemessage", "bar", "baz", "qux");
MessageManager.saveErrors();

これは、StrutsのActionクラスで実装する以下のコードとほぼ同意です。

ActionMessages errors = new ActionMessages();
errors.add(ActionErrors.GLOBAL_MESSAGE,new ActionMessage("examplemessage"));
errors.add(ActionErrors.GLOBAL_MESSAGE,new ActionMessage("examplemessage", "foo"));
errors.add(ActionErrors.GLOBAL_MESSAGE,new ActionMessage("examplemessage", "bar", "baz", "qux"));
errors.add("property",new ActionMessage("examplemessage"));
errors.add("property",new ActionMessage("examplemessage", "foo"));
errors.add("property",new ActionMessage("examplemessage", "bar", "baz", "qux"));
if(!errors.isEmpty()) {
    saveErrors(request,errors);
}

"ほぼ"同意と記述した理由は、上記で利用するリソースのキーexamplemessageの中に、{4}などが含まれていた場合にそれを削除する機能がMessageManagerには備わっている為です。

また、MessageManagerは、メッセージを管理するためのaddGlobalMessage()、addMessage()メソッドや、saveMessages()メソッドも備えています。 引数などはエラーメッセージを扱う場合と同じです。

HOT deploy

HOT deployの説明についてはSMART deployを参照してください。 HOT deployを利用したサンプルとしてはS2StrutsExample V1.3.xやS2StrutsBlank V1.3.xを使用してください。 これらの利用法についてはS2Strutsのセットアップを参照してください。

HOT deployの前提

HOT deployを利用するにはServlet2.4が必要です。 S2Strutsでは、次のものをHOT deployの対象にできます。

  • SMART deployの規約に従ったSeasar2のコンポーネント
  • struts-config.xml
  • validation.xml
  • struts-config.xmlに指定するメッセージ用のプロパティファイル

HOT deployの設定

HOT deployを利用するには次の設定を確認してください。

  1. 必要なdiconファイル
    次のdiconファイルがクラスパス上に存在している必要があります。
    • s2container.dicon
    • creator.dicon
    • customizer.dicon
    • convention.dicon
      convention.diconには、ルートパッケージ名を指定する必要があります。
      <components>
          <component class="org.seasar.framework.convention.impl.NamingConventionImpl">
              <initMethod name="addRootPackageName">
                  <arg>"xxx.yyy.examples"</arg>
              </initMethod>
          </component>
      </components>
      
  2. env.txt
    • 条件インクルードを利用するためにenv.txtをクラスパスに追加します。 env.txtには「ct」と記述します。 運用環境ではブランクもしくは「product」と記述しHOT deployは使用しないようにしてください。
      ct
      
  3. app.dicon
    • 条件インクルードによりs2struts-hotdeploy.diconがインクルードされるようにします。
        <include condition="#ENV != 'ct'" path="s2struts.dicon"/>
        <include condition="#ENV == 'ct'" path="s2struts-hotdeploy.dicon"/>
      
  4. struts-config.xml
    • S2RequestProcessorS2TilesRequestProcessorを利用している場合は、 controllerにcatalogを指定し、プロパティでACTION_CONTEXT_CLASSにS2Strutsのクラスを指定します。 S2RequestProcessorS2TilesRequestProcessorはHOT deployに完全対応していないため、Commons Chainを利用するこの設定が必要です。
        <controller catalog="s2struts">
          <set-property 
            key="ACTION_CONTEXT_CLASS" 
            value="org.seasar.struts.processor.contexts.S2ServletActionContext"/>
        </controller>
    • plug-inにorg.seasar.struts.hotdeploy.plugin.HotdeployPlugInを追加します。
        <plug-in 
          className="org.seasar.struts.hotdeploy.plugin.HotdeployPlugIn"/>
      
  5. web.xml
    • HotdeployFilterを追加します。dispatcherの指定を正確に行ってください。
        <filter>
          <filter-name>hotdeployfilter</filter-name>
          <filter-class>
            org.seasar.framework.container.hotdeploy.HotdeployFilter
          </filter-class>
        </filter>
      ...
        <filter-mapping>
          <filter-name>hotdeployfilter</filter-name>
          <url-pattern>/*</url-pattern>
          <dispatcher>REQUEST</dispatcher>
          <dispatcher>FORWARD</dispatcher>
          <dispatcher>INCLUDE</dispatcher>
        </filter-mapping>
      
    • S2StrutsHotdeployFilterを追加します。
        <filter>
          <filter-name>s2strutshotdeployfilter</filter-name>
          <filter-class>
            org.seasar.struts.hotdeploy.filter.S2StrutsHotdeployFilter
          </filter-class>
        </filter>
      ...
        <filter-mapping>
          <filter-name>s2strutshotdeployfilter</filter-name>
          <url-pattern>/*</url-pattern>
        </filter-mapping>