適用されるアプリケーション
まずはシンプルな具体例を挙げていきます。
1つのテーブルのデータをCSVでダウンロードする機能をWebアプリケーションで実現したいのですが、そのデータ行数は、抽出条件によっては数千行~数万行になることがあります。このまま要望を実装してもどうしても長い待ち時間が発生してしまい、最悪ブラウザがタイムアウトをすることが考えられるため、なんらかの同期処理が欲しくなります。
従来であれば、時間がかかる処理のみを切り出して、処理の開始~終了までを通知するスレッド管理の処理やメッセージの通知処理を使って、かつブラウザとの同期方法を実装しなければならないところですが、ここでExecuteAndWaitインターセプタを使うだけで簡単に実装することができます。
シンプルなActionクラスの例
ここではさらにシンプルに次の処理をActionで実装し、仮想的に長時間の処理がかかるActionを用意します。
- リクエストを受けると、15秒間待機し完了画面を出力する。
- 待機画面は1秒ごとに更新する。
これに対するActionクラスの実装は非常に単純で、リスト1のようになります。
public class ExecAndWaitSampleAction {
public String execute() throws Exception {
Thread.sleep(15000);
return "success";
}
}
次に、このActionに対して設定をしていきます。
Actionクラスに対する設定
Actionに対する設定はstruts.xmlまたはActionクラスに対してアノテーションで定義することができます。
Actionクラス終了時はsuccessを返すよう設定していますので、さらにインターセプタ用に1つの定義を追加します。
- 処理中(wait) - Actionが処理中であった場合に返すレスポンス
また、waitを省略した場合にはデフォルトのリフレッシュページが表示されます。
(1)struts.xmlの場合
<struts>
<package name="xnw" namespace="/xnw" extends="struts-default">
<interceptors>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="execAndWait">
<param name="delay">500</param>
<param name="delaySleepInterval">1000</param>
</interceptor-ref>
</interceptors>
<action class="sample.action.ExecAndWaitSampleAction">
<result name="wait">wait.jsp</result>
<result name="success">success.jsp</result>
</action>
</package>
</struts>
(2)アノテーションを利用する場合
先ほどのActionクラスに対して、リスト3のように@をつけてアノテーションを追加します。アノテーションを利用する場合は、先程のstruts.xmlは不要になります。
package seren.lumi2.action;
import org.apache.log4j.Logger;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.InterceptorRefs;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import com.opensymphony.xwork2.ActionSupport;
/**
* ExecuteAndWaitインターセプタ利用アクションクラス。
*
* @author koji.azuma(A-pZ)
*/
// アクションが属するパッケージ名。パッケージ名=パス。
@Namespace(value = "/xnw")
// 継承パッケージ。デフォルトのStruts 2の内容を継承。
@ParentPackage(value = "struts-default")
// インターセプタ定義
@InterceptorRefs( {
// Struts 2デフォルト提供のインターセプタの組み合わせの後にExecuteAndWaitインターセプタを追加。
@InterceptorRef("defaultStack"),
@InterceptorRef(value = "execAndWait", params = { "delay", "500",
"delaySleepInterval", "1000" }),
// threadPriority :スレッドの優先順位。
// delay :リクエストされてから初回レスポンスするまでの待ち時間。
// delaySleepInterval:アクションが起動しているかをチェックする間隔。delay定義時のみ指定。
})
@Results({
@Result(name = "wait", location = "wait.jsp"),
@Result(name = "success", location = "success.jsp"),
})
public class ExecAndWaitSampleAction extends ActionSupport {
private static final long serialVersionUID = -8764919761726224220L;
public static final Logger log = Logger
.getLogger(ExecAndWaitSampleAction.class);
/**
* ExecuteAndWaitインターセプタを利用したサンプルアクション。
* Conventionプラグインを利用すると、アノテーションで定義された名前がリクエスト情報になる。
* Actionアノテーションで定義されたメソッドは、@Namespace/@Action.actionでリクエストされる。 このメソッドの場合、
* /xnw/sample.actionとなる
*
* ExecuteAndwaitインターセプタで管理されるActionは、処理が終わるまで自動的にreturnの値がハンドリングされる。
*/
@Override
@Action("sample")
public String execute() throws Exception {
log.debug(" >> 処理開始 ...");
Thread.sleep(15000); // 待機
log.debug(" << 処理終了");
return "success";
}
}
ExecuteAndWaitインターセプタにて定義できるパラメータは次の3つです。いずれも省略可能です。
| 属性名 | デフォルト値 | 概要 |
| threadPriority | Thread.NORM_PRIORITY | 使われるスレッドの優先順位。 |
| delay | 0(ミリ秒) | リクエストされてから初回レスポンスするまでの待ち時間。実際にはActionは即時に起動しています。レスポンスするのに必要な情報を出す準備に時間がかかる場合は設定するといいでしょう。 |
| delaySleepInterval | 100(ミリ秒) | アクションが既に起動しているかをチェックする間隔。delay定義時のみ指定します。 |
これでActionクラスの準備は完了です。
