2013年8月25日 星期日

在 Jersey 1.17 利用 LogginFilter 攔截 Request 的開始與結束

在使用 Jersey 1.17 作為 JAX-RS 的 RESTful 網頁服務時,如果想要攔截伺服器收到的 Request 和回應的 Response
可以偷偷利用 LoggingFilter 作為攔截的手段。

首先要利用 LogginFilter 之前,需要先寫一個自定義的 Filter,這裡就仿照 [1] 的網友 markthegrea 提供的範例:
package example;

import java.util.Calendar;

import com.sun.jersey.api.container.filter.LoggingFilter;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import com.sun.jersey.spi.container.ContainerResponse;

public class RequestLogger extends LoggingFilter implements ContainerRequestFilter {
  private static ThreadLocal<Long> EXECUTION_TIME = new ThreadLocal<Long>(); 
  
  @Override
  public ContainerRequest filter(ContainerRequest request) {
    EXECUTION_TIME.set(Calendar.getInstance().getTimeInMillis());
    System.out.println("Request is started at " + EXECUTION_TIME.get().longValue());
    return request;
  }

  @Override
  public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
    StringBuilder sb = new StringBuilder();
    sb.append("User:").append((request.getUserPrincipal() == null ? "unknown" : request.getUserPrincipal().getName()));
    sb.append(" - Path:").append(request.getRequestUri().getPath());
    sb.append(" - ").append(Calendar.getInstance().getTimeInMillis() - EXECUTION_TIME.get().longValue());
    System.out.println(sb.toString());
    return response;
  }
}

以上的程式碼有兩個覆寫 filter() 的方法,其實兩個方法分別對應的是不同的事件
上面的 filter(ContainerRequest) 是攔截 Request 收到的時間
而下面的 filter(ContainerRequest, ContainerResponse) 則是攔截 Response 送出的時間。

另外簡單說明一下,第一個攔截 Request 的 filter,做的事情就是在 console 中印出收到 Request 的時間。
而第二個攔截 Response 的 filter 則是依照收到 Request 的時間,去計算出這次 Request 總共花費的時間。

在定義好 LoggingFilter 之後,必須在 web.xml 上宣告 Request 和 Response 的 filter,伺服器在啟動時才知道要去哪裡執行攔截的動作。
<servlet>
  <servlet-name>ServletAdaptor</servlet-name>
  <servlet-class>com.sun.jersey.server.impl.container.servlet.ServletAdaptor</servlet-class>
  <init-param>
      <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
      <param-value>example.RequestLogger</param-value>
  </init-param>
  <init-param>
      <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
      <param-value>example.RequestLogger</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

上面的 XML 只包含關鍵的片段 XML,第 2 行開始是宣告 Jersey 的 Servlet。
其中 4~7 行是宣告 Request 的 filter、8~11 行是宣告 Response 的 filter
因為前面把 Request 和 Response 的 filter 都寫在同一個類別裡,所以這裡的 param-value 都指向同一個 class。

最後執行任意一個 RESTful 服務時,會看到以下的 console 訊息。
Request is started at 1377443204176
User:unknown - Path:/Jersey/web/test/test - 5
其中 Path 那段出現的字串裡,Jersey 是我這次放的專案名稱,web 是我在 web.xml 上定義的 Jersey 的 URL
test/test 則是我測試時呼叫的 RESTful 服務。

參考資料:
1、How does one intercept a request during the Jersey lifecycle?

沒有留言: