Last Updated: February 25, 2016
·
6.575K
· rko

Cache-Control with Jersey 2.0

Inspired by this StackOverflow question, I decided to write a Cache-Control Annotation for Jersey 2.0 (actually 2.2 but that doesn't matter anyways since it's JAX-RS 2.0)

First of, the Annotation which will provide binding the resources to the actual cache header insertion:

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 {

    String value() default "public, must-revalidate";

}

Next, you need the actual implementation of the cache header insertion - this will look for a @CacheControl annotation and add the provided string, if found, to the response header:

import java.io.IOException;
import java.lang.annotation.Annotation;

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 pRequestContext, ContainerResponseContext pResponseContext)
            throws IOException {
        for (Annotation a : pResponseContext.getEntityAnnotations()) {
            if (a.annotationType() == CacheControl.class) {
                String value = ((CacheControl) a).value();
                pResponseContext.getHeaders().putSingle(HttpHeaders.CACHE_CONTROL, value);
                break;
            }
        }
    }

}

Now you can use the @CacheControl header in your resources:

@Path("/api")
public class SomeResource {

    @GET
    @Path("/not-cacheable")
    @CacheControl("no-cache")
    public Respone handleNoneCacheableRequest() {
        return Response.ok().build();
    }
}

Caveats so far:

  • If you declare @CacheControl on your resource class, the CacheControlFilter will not match it and does therefore not add a header - this is because pResponseContext.getEntityAnnotations() will only match Annotations declared directly on the method but not on its class declarations or other ancestors. Unfortunately I did not come up with a solution for this yet.