連載
» 2011年06月10日 00時00分 公開

Tomcat 7の新機能で何ができるようになるのか?(2):Tomcat 7も対応したServlet 3.0の変更点 後編 (1/3)

[藤野圭一,NTT OSSセンタ]

 前回の「Tomcat 7も対応したServlet 3.0の6つの主な変更点」では、「Tomcat 7」が実装したServlet 3.0の新機能のうち「Ease of Development(EoD、開発容易性)」「Pluggability and Extendibility(モジュール化と拡張性)」について紹介しました。今回はAsynchronous processing(非同期処理)、セキュリティ、Session Tracking、マルチパート対応を紹介します。

アノテーションによる非同期処理

 Servlet 3.0から非同期処理が使えるようになりました。Servlet 2.5までは、Servlet上でスレッドを生成・起動し、リクエストやレスポンスなどのコンテナ管理オブジェクトにアクセスすることを推奨していませんでした。「Servlet仕様2.5 2.3.3.3 Thread Safety」でも、以下のように記載されています。

Implementations of the request and response objects are not guaranteed to be thread safe. This means that they should only be used within the scope of the request handling thread.

References to the request and response objects should not be given to objects executing in other threads as the resulting behavior may be nondeterministic. If the thread created by the application uses the container-managed objects, such as the request or response object, those objects must be accessed only within the servlet’s service life cycle and such thread itself should have a life cycle within the life cycle of the servlet’s service method because accessing those objects after the service method ends may cause undeterministic problems. Be aware that the request and response objects are not thread safe. If those objects were accessed in the multiple threads, the access should be synchronized or be done through the wrapper to add the thread safety, for instance, synchronizing the call of the methods to access the request attribute, or using a local output stream for the response object within a thread.

 Servlet 3.0では、上記のような非推奨とされている動作に対して、非同期処理を利用することでスレッドの生成・起動をサポートするようになりました。

非同期処理の流れ

 非同期処理では、DBコネクションプールからの接続待ちや、JMSキューからのメッセージ待ちなど時間を要する処理に対して、スレッドがコンテナに戻り、他のタスクを実行できるように、リクエストを非同期に処理できる機能を提供します。

図1 従来の同期モデル 図1 従来の同期モデル

 従来の同期モデルでは、リクエスト処理スレッドは全ての処理が完了してからコンテナに戻ることになるので、時間のかかる処理が含まれる場合は、その処理でブロッキングになってしまい、効率が悪いです。

図2 非同期(AsyncContext#start(Runnable))モデル 図2 非同期(AsyncContext#start(Runnable))モデル
図3 非同期 (AsyncContext#dispatch (...))モデル 図3 非同期 (AsyncContext#dispatch (...))モデル

 Servlet 3.0で提供される非同期処理では、時間のかかる処理を非同期で実行することになるので、ブロッキングが発生しません。

非同期処理を行うには

 非同期処理は、「web.xml」のタグにasync-supported属性を設定するかアノテーション「@WebServlet」「@WebFilterでasyncSupported」を指定するかの2通りの方法で簡単に利用可能です。

アノテーションで非同期処理を行う基本サンプル

 今回はアノテーションを利用して簡単なサンプルを用意しました。

AsyncServlet

@WebServlet(name = "async", value = "/async", asyncSupported = true)
public class AsyncServletTest extends HttpServlet {
 
    private static final long serialVersionUID = 1L;
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        AsyncContext asyncC = req.startAsync();
        asyncC.addListener(new AsyncServiceListener());
        asyncC.start(new AsyncService(asyncC));
        // do something.
    }
 
    private static class AsyncService implements Runnable {
        AsyncContext ctx;
 
        public AsyncService(AsyncContext ctx) {
            this.ctx = ctx;
        }
 
        public void run() {
            try {
                // do something
                PrintWriter writer = ctx.getResponse().getWriter();
                writer.write("Asynchronous processing complete");
                writer.close();
                ctx.complete();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
 
    private static class AsyncServiceListener implements AsyncListener {
 
        @Override
        public void onComplete(AsyncEvent event) throws IOException {
         // do something.
        }
 
        @Override
        public void onError(AsyncEvent event) throws IOException {
         // do something.
        }
 
        @Override
        public void onStartAsync(AsyncEvent event) throws IOException {
         // do something.
        }
 
        @Override
        public void onTimeout(AsyncEvent event) throws IOException {
         // do something.
        }
    }
}

 「@WebServle」のアノテーションで「asyncSupported=true」とすることで非同期処理を有効にできます。

非同期処理利用時の注意点

 リクエスト処理のFilterチェーンに含まれる全てのFilterやServletが非同期を有効にしていないと非同期実行できません。例えば、asyncのServletの前にFilterが存在した場合、そのFilterも「asyncSupported=true」に設定する必要があります。

非同期処理サンプルの詳細

 以下でリクエストとレスポンスを保持した非同期動作のための実行コンテキストであるAsyncContextを取得します。

AsyncContext asyncCtxt = req.startAsync();

 非同期処理の完了通知が必要な場合は、以下のように非同期実行の動作完了をリスンするAsyncListenerを追加します。


asyncC.addListener(new AsyncServiceListener());

 今回はリスナを追加しただけで、特に処理は記述していません。

 以下のように、取得したAsyncContextを指定してAsyncServiceというRunnableオブジェクトに起動を依頼します。


asyncCtxt.start(new AsyncService(asyncCtxt));

 すると、このRunnableオブジェクトがコンテナからスレッドを取得し非同期で動作します。

AsyncContextを利用して「urlA」のパスにリクエストをディスパッチするサンプル

 もう1つサンプルを用意しました。AsyncContextを利用して「urlA」のパスにリクエストをディスパッチします。

AsyncServletTest2

@WebServlet(name = "async2", value = "/async2", asyncSupported = true)
public class AsyncServletTest2 extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
AsyncContext asyncC = req.startAsync();
asyncC.dispatch("/urlA");
// do something.
}
}

 次ページでは、さらにServlet 3.0のセキュリティ対応を紹介します。

       1|2|3 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。