How to use CXF, JAX-RS and HTTP Caching

2019-03-08 08:19发布

The CXF documentation mentions caching as Advanced HTTP:

CXF JAXRS provides support for a number of advanced HTTP features by handling If-Match, If-Modified-Since and ETags headers. JAXRS Request context object can be used to check the preconditions. Vary, CacheControl, Cookies and Set-Cookies are also supported.

I'm really interested in using (or at least exploring) these features. However, while "provides support" sounds really interesting, it isn't particularly helpful in implementing such features. Any help or pointers on how to use If-Modified-Since, CacheControl or ETags?

3条回答
Explosion°爆炸
2楼-- · 2019-03-08 08:59

CXF didn't implements dynamic filtering as explained here : http://www.jalg.net/2012/09/declarative-cache-control-with-jax-rs-2-0

And if you use to return directly your own objects and not CXF Response, it's hard to add a cache control header.

I find an elegant way by using a custom annotation and creating a CXF Interceptor that read this annotation and add the header.

So first, create a CacheControl annotation

@Target(ElementType.METHOD )
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheControl {
    String value() default "no-cache";
}

Then, add this annotation to your CXF operation method (interface or implementation it works on both if you use an interface)

@CacheControl("max-age=600")
public Person getPerson(String name) {
    return personService.getPerson(name);
}

Then create a CacheControl interceptor that will handle the annotation and add the header to your response.

public class CacheInterceptor extends AbstractOutDatabindingInterceptor{
    public CacheInterceptor() {
        super(Phase.MARSHAL);
    }

    @Override
    public void handleMessage(Message outMessage) throws Fault {
        //search for a CacheControl annotation on the operation
        OperationResourceInfo resourceInfo = outMessage.getExchange().get(OperationResourceInfo.class);
        CacheControl cacheControl = null;
        for (Annotation annot : resourceInfo.getOutAnnotations()) {
            if(annot instanceof CacheControl) {
                cacheControl = (CacheControl) annot;
                break;
            }
        }

        //fast path for no cache control
        if(cacheControl == null) {
            return;
        }

        //search for existing headers or create new ones
        Map<String, List<String>> headers = (Map<String, List<String>>) outMessage.get(Message.PROTOCOL_HEADERS);
        if (headers == null) {
            headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
            outMessage.put(Message.PROTOCOL_HEADERS, headers);
        }

        //add Cache-Control header
        headers.put("Cache-Control", Collections.singletonList(cacheControl.value()));
    }
}

Finally configure CXF to use your interceptor, you can find all the needed information here : http://cxf.apache.org/docs/interceptors.html

Hope it will help.

Loïc

查看更多
We Are One
3楼-- · 2019-03-08 09:02

With the forthcoming JAX-RS 2.0 it will be possible to apply Cache-Control declaratively, as explained in http://jalg.net/2012/09/declarative-cache-control-with-jax-rs-2-0/

You can already test this at least with Jersey. Not sure about CXF and RESTEasy though.

查看更多
孤傲高冷的网名
4楼-- · 2019-03-08 09:10

Actually, the answer isn't specific to CXF - it's pure JAX-RS:

// IPersonService.java
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;

@GET
@Path("/person/{id}")
Response getPerson(@PathParam("id") String id, @Context Request request);


// PersonServiceImpl.java
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

public Response getPerson(String name, Request request) {
  Person person = _dao.getPerson(name);

  if (person == null) {
    return Response.noContent().build();
  }

  EntityTag eTag = new EntityTag(person.getUUID() + "-" + person.getVersion());

  CacheControl cc = new CacheControl();
  cc.setMaxAge(600);

  ResponseBuilder builder = request.evaluatePreconditions(person.getUpdated(), eTag);

  if (builder == null) {
    builder = Response.ok(person);
  }

  return builder.cacheControl(cc).lastModified(person.getUpdated()).build();
}
查看更多
登录 后发表回答