SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

続 Struts 2入門

Struts 2の同期処理を手助けするExecuteAndWaitインターセプタ

続 Struts 2入門(1)

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クラスのフィールドで行うことにします。

リスト6 ExecAndWaitDownloadAction.java
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メソッドを対で記述するだけです。

次のページ
まとめ

この記事は参考になりましたか?

続 Struts 2入門連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 東 浩二(アズマ コウジ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/4912 2010/03/16 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー