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?
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();
}
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.
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