LoginSignup
6
5

More than 5 years have passed since last update.

アノテーションでCache-Control HTTPヘッダを制御する

Posted at

概要

JAX-RSリソースに @CacheControl(ほげほげ) のようなアノテーションを付与することで、レスポンスフィルタでCacheControl HTTPヘッダを出力します。

使い方

SampleResource.java
public class SampleResource {
    @GET
    @CacheControl(value=CacheResponseDirective.MustRevalidate, maxAge=0)
    public SomeEntity getEntity() {
    }
}

上記リソースにアクセスすると、このようなHTTPヘッダが出力されます。

Cache-Control: must-revalidate,max-age=0

環境

  • Java 1.8 u31
  • Glassfish 4.1

コード

Cache-Controlヘッダのenum

Cache-Controlヘッダで使えるものの一覧です。

CacheResponseDirective.java
public enum CacheResponseDirective {

    Public("public"),
    Private("private"),
    NoCache("no-cache"),
    NoStore("no-store"),
    NoTransform("no-transform"),
    MustRevalidate("must-revalidate"),
    ProxyRevalidate("proxy-revalidate");

    private final String represent;

    private CacheResponseDirective(String represent) {
        this.represent = represent;
    }

    @Override
    public String toString() {
        return represent;
    }
}

アノテーション

JAX-RSリソースに付けるアノテーションです。

@NameBindingを付けたので、このアノテーションが付与されたリソースにのみフィルタが適用されます。

CacheControl.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.NameBinding;

@NameBinding
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheControl {

    CacheResponseDirective[] value() default CacheResponseDirective.Public;

    /**
     * max-ageをを指定。単位は秒。
     * <p>
     * 値が0以上の時出力します。
     * @return 
     */
    int maxAge() default -1;

    /**
     * s-maxageを指定。単位は秒。
     * <p>
     * 値が0以上の時出力します。
     * @return 
     */
    int sMaxage() default -1;
}

レスポンスフィルタ

実際にCache-Controlヘッダを出力するフィルタです。

CacheControlFilter.java
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Optional;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.ext.Provider;

@Provider
@CacheControl
public class CacheControlFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
        final Annotation[] annotations = response.getEntityAnnotations();
        final Optional<Annotation> optional = Arrays.stream(annotations)
                .filter(x -> x.annotationType() == CacheControl.class)
                .findFirst();

        if (optional.isPresent()) {
            final CacheControl annotation = (CacheControl) optional.get();
            final List<String> directives = Arrays.stream(annotation.value())
                    .map(x -> x.toString())
                    .collect(Collectors.toList());

            // max-ageやs-maxageは0以上の値が指定されたときに出力します。
            if (annotation.maxAge() >= 0) {
                directives.add(String.format("max-age=%d", annotation.maxAge()));
            }

            if (annotation.sMaxage() >= 0) {
                directives.add(String.format("s-maxage=%d", annotation.sMaxage()));
            }

            final String token = String.join(",", directives);

            response.getHeaders().putSingle(HttpHeaders.CACHE_CONTROL, token);
        }
    }
}

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5