Filterの優先度

【前提条件】

[環境]

【概要】

今回はFilterの優先度についてまとめてみました。

Filterの実行順序はアノテーションを使用することにより、
実行順序をしているすることができます。

【Priorityについて】

優先度を指定するためにはjavax.annotation.Priorityアノテーションを指定します。
PriorityアノテーションvalueにはInteger型を指定します。

javax.ws.rs.PrioritiesというEnumクラスがあり、
Priority用パラメータ用に用意されています。

優先度の高い順に
AUTHENTICATION、AUTHORIZATION、HEADER_DECORATOR、ENTITY_CODER、USERとなります。
InterceptorのデフォルトはUSERになります。

【ソース】

それぞれの優先度のFilterを用意します。
HEADER_DECORATORについては2つ用意します。

import java.io.IOException;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationPriorityFilter implements ContainerRequestFilter, ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        System.out.println("Authentication filter start.");
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        System.out.println("Authentication filter end.");
    }
}
// importはAuthenticationPriorityFilterと同じなので省略
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationPriorityFilter implements ContainerRequestFilter, ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        System.out.println("Authorization filter start.");
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        System.out.println("Authorization filter end.");
    }
}
// importはAuthenticationPriorityFilterと同じなので省略
@Provider
@Priority(Priorities.ENTITY_CODER)
public class EntityCoderPriorityFilter implements ContainerRequestFilter, ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        System.out.println("EntityCoder filter start.");
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        System.out.println("EntityCoder filter end.");
    }
}
// importはAuthenticationPriorityFilterと同じなので省略
@Provider
@Priority(Priorities.HEADER_DECORATOR)
public class HeaderDecoratorPriorityFilter1 implements ContainerRequestFilter, ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        System.out.println("HeaderDecorator1 filter start.");
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        System.out.println("HeaderDecorator1 filter end.");
    }
}
// importはAuthenticationPriorityFilterと同じなので省略
@Provider
@Priority(Priorities.HEADER_DECORATOR)
public class HeaderDecoratorPriorityFilter2 implements ContainerRequestFilter, ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        System.out.println("HeaderDecorator2 filter start.");
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        System.out.println("HeaderDecorator2 filter end.");
    }
}
// importはAuthenticationPriorityFilterと同じなので省略
@Provider
@Priority(Priorities.USER)
public class UserPriorityFilte implements ContainerRequestFilter, ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        System.out.println("User filter start.");
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        System.out.println("User filter end.");
    }
}

【実行結果】

適当なリソースクラスを作成し、アクセスすると↓のような結果が出力されます。

Authentication filter start.
Authorization filter start.
HeaderDecorator2 filter start.
HeaderDecorator1 filter start.
EntityCoder filter start.
User filter start.
User filter end.
EntityCoder filter end.
HeaderDecorator2 filter end.
HeaderDecorator1 filter end.
Authorization filter end.
Authentication filter end.

結果を見ると
優先度高のRequestFilter⇒優先度低のRequesetFileter⇒優先度低のResponseFilter⇒優先度高のResponseFilter
の順序で実行されているのがわかります。

今回、優先度がHEADER_DECORATORのFilterを2つ作成しました。
同一優先度の場合、
先発のRequestFilter⇒後発のRequestFilter⇒先発のResponseFilter⇒後発のReqeustFilter
の順序で実行されています。

また、同一優先度のFilterが複数ある場合、同一優先度内の順序は実装依存となります。
GlassFishを起動しなおして、実行すると上記の結果の1と2が逆転していたことから、
GlassFish4ではインジェクション時に決まるようです。

【Priorityのvalue

↑で書いたソースではPrioritiesクラスを使用しましたが、
PriorityのvalueにはInteger型の値であれなんでも良いので、
1とか2とかを指定しても動きます。

その場合、数値の小さいほうが優先度が高くなります。

【個人的感想】

PriorityについてはPrioritiesクラスの定数の意味と
一致する内容であれば、使用したほう良いのかなと思います。

一致しないFilterが一つでもある場合は
独自にEnumクラスを作成して、使用したほうがよさそうです。

同一優先度の定義はせず、異なる優先度で定義したほうが安全な気がします。