ExecuteAndWaitインターセプタを使ったActionクラスの応用例
ブラウザとサーバ側の同期処理が簡単にできるので、応用先としては、
- データベースから大量のデータを一括ダウンロードする処理を、プログレスバーで進捗状況を表示し、終わったらダウンロードダイアログを出す
- 画面の上部にタスク進捗状況を表示させて並行作業をできるようにする
など、Webアプリケーションでは少し敬遠されがちな内容も簡単に実装できるようになりますし、複数のコンテンツを同時に扱うイマドキのWebアプリケーションを実装する際には必須でしょう。リクエストされた処理の進捗状況を簡単に実装できるようになりますので、もっと素晴らしい仕組みができるかも知れません。
次のサンプルでは、データベースとのやり取りは省略して、Actionクラスでテキストファイルを生成し、その状況を監視できるようにします。画面では単純にカウントを表示するようにして進捗状況を表示するようにします。
ExecuteAndWaitインターセプタ+ダウンロード出力をするActionクラス
まずはActionクラスです。リスト3のActionクラスに手を加え、テキストの生成とダウンロードの処理を加えたものです。さらにActionクラスの処理の進み具合を監視するようにカウンタを設置します。
Struts 2ではダウンロードの処理用に定められたResultタイプとフィールドがあらかじめ定義されており、その定義に沿って値を埋めていくことでダウンロード処理を簡単に実装できるようになっています。
ダウンロード用のActionクラスの設定として、Resultタイプはstreamを使います。streamはファイルダウンロードやPDFを生成する場合など、バイナリで出力する時に指定します。
その他、ダウンロード用のActionクラスに設定するフィールド名は次のようになります。
| フィールド名 | クラス | デフォルト値 | 概要 |
| contentType | String | text/plain | ダウンロードする際のコンテンツタイプ。 |
| contentLength | Long | (なし) | ブラウザに通知されるダウンロードファイルの長さ。 |
| contentDisposition | String | inline(ただし必ず定義することが推奨されます) | ダウンロードファイル名の定義。 |
| inputName | String | inputStream | ダウンロード用に使うInputStreamのフィールド名。 |
| bufferSize | String | 1024 | ダウンロード利用時のバッファサイズ。 |
| inputStream | InputStream | (なし) | ダウンロード用InputStream本体。出力する内容をInputStreamに変換して代入する。 |
ダウンロード用の設定はActionクラスのフィールドや設定ファイル(struts.xml)やアノテーションで定義も可能で、Actionごとに定義することが可能です。
今回はActionクラスにてResultをアノテーションで設定し、ダウンロードの細かい設定はActionクラスのフィールドで行うことにします。
package seren.lumi2.action;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
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;
/**
* @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", type="stream"), // 終了後、ダウンロードへ。
})
public class ExecAndWaitDownloadAction {
public static final Logger log = Logger.getLogger(ExecAndWaitDownloadAction.class);
// download.actionで起動できるように設定
@Action(value="download")
public String execute() throws Exception {
// ダウンロードするファイルの内容のテキスト
StringBuffer sb = new StringBuffer("");
// StringBufferに、カウントアップした文字列を追加する。
for ( int i=0;i<10;i++ ) {
log.debug("count:" +i);
sb.append(i).append("\r\n"); // カウント数値+改行
setCount(i); // 現在のカウントを設定。
Thread.sleep(1000); // 1秒待機。
}
// 以下にダウンロード処理に必要なパラメータを設定する。
// ダウンロード処理に関してはデフォルトで定められたフィールドを定義し、それらに値をバインドしておくだけでよい。
// 入力ストリームを定義。この内容がダウンロードさせる内容になる。必ずInputStreamクラス(またはそのサブクラス)にする。
setInputStream(new ByteArrayInputStream(sb.toString().getBytes()));
// ダウンロード時のファイル名を定義する。※正確にはHTTPレスポンスのContentDispositionを設定。
setContentDisposition("attachment;filename=\"counter.txt\"");
// ブラウザに通知するダウンロードファイルの長さを定義。
setContentLength( new Long(sb.length()) );
log.debug("download start.");
// ダウンロード処理を実行。
return "success";
}
/** JSPで出力する現在のカウント */
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
/** ダウンロード用:コンテンツタイプ。 */
private String contentType;
/** ダウンロード用:ダウンロードコンテンツのバイト長。 */
private Long contentLength;
/** ダウンロード用:レスポンスヘッダに記述するcontentDispositionの内容。 */
private String contentDisposition;
/** ダウンロード用:InputStreamをバインドするフィールド名。 */
private String inputName;
/** ダウンロード用:レスポンス出力のバッファ領域。サーバのスペックにあわせてサイズ調整をすること。 */
private String bufferSize;
/** ダウンロード用:ダウンロード用のバイトストリーム */
private InputStream inputStream;
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public Long getContentLength() {
return contentLength;
}
public void setContentLength(Long contentLength) {
this.contentLength = contentLength;
}
public String getContentDisposition() {
return contentDisposition;
}
public void setContentDisposition(String contentDisposition) {
this.contentDisposition = contentDisposition;
}
public String getInputName() {
return inputName;
}
public void setInputName(String inputName) {
this.inputName = inputName;
}
public String getBufferSize() {
return bufferSize;
}
public void setBufferSize(String bufferSize) {
this.bufferSize = bufferSize;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
}
Actionクラスでは1秒おきにStringBufferにカウンタを出力していき、10秒後にそのテキストがダウンロードされる仕組みになります。ダウンロード用の設定は、privateのフィールドで宣言して、さらにpublicのset/getメソッドを対で記述するだけです。
