ScopeInterceptorの使い方

ScopeInterceptorを使用するとsessionやapplicationスコープでBeanのプロパティを簡単に管理できるようになる。
例えば、更新前に確認画面を表示した回数をカウントし、更新を実行したらカウントを破棄するような動作をさせる場合次のように実装できる。

最初にcountプロパティをアクションに定義する。
tutorial.UserAction.java

package tutorial;

import java.util.Map;

import jp.co.hershe.struts2.util.ParamUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.acegisecurity.annotation.Secured;
import org.apache.struts2.interceptor.ParameterAware;
import org.apache.struts2.interceptor.validation.SkipValidation;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import com.opensymphony.xwork2.interceptor.ParameterNameAware;

/**
 * Struts2 アクションクラス
 * 
 * @author mikuni
 *
 */
public class UserAction extends ActionSupport implements ModelDriven<User>, Preparable, ParameterAware, ParameterNameAware {
	private static final long serialVersionUID = 1870444428158165140L;
	static Log log = LogFactory.getLog(UserAction.class);
	ParamUtil u;
	UserDAO dao;
	User	user = null;
	boolean preread = false;
	int count;
	
	// 事前読み込みを行うかどうかをアクションのパラメータとして渡してもらう
	public void setPreread( String on ) {
		preread = "true".equals(on);
	}
	
	// @Inject
	public UserAction(UserDAO dao) {
		this.dao = dao;
		count = 0;
	}
	public UserAction() {
	}
	// ParameterAware
	public void setParameters(Map p) {
		u = new ParamUtil(p);
	}

	// Prepareable
	public void prepare() throws Exception {
		if (preread) {
			int id = u.getInt("id",0);
			if (id != 0) {
				user = dao.get( id );
			}
		}
		if (user==null) {
			user = new User();
		}
	}
	// ModelDriven
	public User getModel() {
		return user;
	}
	
	@SkipValidation
	public String doNothing() {
		return SUCCESS;
	}
	public String doCheck() throws Exception {
		count++;
		return SUCCESS;
	}
	public String doUpdate() {
		dao.update(user);
		return SUCCESS;
	}
	public String doAdd() {
		dao.insert(user);
		return SUCCESS;
	}

	public boolean acceptableParameterName(String parameterName) {
		return !parameterName.equals("count");
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}
}

※ちょっとTIPS:念のためcountにurlパラメータから user_update_pre.action?count=1000 などと値設定できないようにParameterNameAwareで保護もしておく。

そして、struts.xml で scopeインターセプターを次のように指定する

 <! -- 編集入力画面表示 -->
 <action name="user_edit" class="UserAction" method="doNothing">
   <param name="preread">true</param>
   <interceptor-ref name="transactauth_chk">
      <param name="scope.session">count</param>         <= スコープで管理するパラメータをカンマ区切りで指定する
      <param name="scope.type">start</param>            <= startを指定すると最初の session値からのsetCount呼び出しが無くなる
   </interceptor-ref>
   <result>/edit.jsp</result>
 </action>
 <! -- 確認画面表示 -->
 <action name="user_update_pre" class="UserAction" method="doCheck">
   <param name="preread">true</param>
   <interceptor-ref name="transactauth_chk">
      <param name="scope.session">count</param>         <= doCheckの呼び出し前に session値から setCount でプロパティ値初期化、
                                                           終了後 getCountの値で session更新
   </interceptor-ref>
   <result>/editconf.jsp</result>
   <result name="input">/edit.jsp</result>
 </action>

 <! -- 更新実行 -->
 <action name="user_update" class="UserAction" method="doUpdate">
   <param name="preread">true</param>
   <interceptor-ref name="transactauth_chk">
      <param name="scope.session">count</param>
      <param name="scope.type">end</param>              <= end を指定すると、最後に sessioncount を破棄してくれる
   </interceptor-ref>
   <result type="redirect-action">user_list</result>
   <result name="input">/edit.jsp</result>
 </action>

これだけで、競合更新のロックまでやってくれるsessionスコープのプロパティが扱える。

2008-01-25 競合更新のロックらしきコードは書かれているけど、こちらが考えているような動作では有りません