Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cache response with Jersey?

Tags:

java

rest

jersey

I am trying to develop restful API using Jersey.I have GET API's for a particular get operation my GET is taking same time from the same client. Is it possible to cache response? Any pointers is appreciated.

Thanks

like image 475
chiru Avatar asked Feb 26 '13 11:02

chiru


People also ask

Can API response be cached?

Caching in REST APIs POST requests are not cacheable by default but can be made cacheable if either an Expires header or a Cache-Control header with a directive, to explicitly allows caching, is added to the response. Responses to PUT and DELETE requests are not cacheable at all.

How does REST API handle cache?

Cache-Control Header Indicates that resource is cacheable by any component. Indicates that resource is cacheable only by the client and the server, no intermediary can cache the resource. Indicates that a resource is not cacheable. Indicates the caching is valid up to max-age in seconds.

Should I cache REST API?

Caching REST APIs offers so many benefits: It improves response times: When a client repeatedly initiates an API without caching instructions, the API's response time is the same whether or not the data changes or not.


2 Answers

Summary of solutions:

  1. Request as method parameter

    Interface:

     @Path("myentity")
     public interface MyEntityResource
    
         @GET
         @Produces(MediaType.APPLICATION_JSON)
         public Response getMyEntity(@Context final Request request);
     }
    

    Implementation:

     public class MyEntityResourceImpl implements MyEntityResource
    
         @Override
         public Response getMyEntity(final Request request) {
    
             final MyEntity myEntity = ... // load entity
             final String eTagValue = ... // calclutate value of ETag
    
             final EntityTag eTag = new EntityTag(eTagValue);
    
             ResponseBuilder responseBuilder = request.evaluatePreconditions(eTag);
    
             if (responseBuilder == null) {
                 return Response.ok(user).tag(eTag).build();
             }
    
             return responseBuilder.build();
         }
     }
    

    Disadvantages:

    • implementation detail Requestis exposed

    • return type Reponse is generic

    • missing grammar of return type in WADL

    • client proxy with unnecessary parameter Request

  2. Request as instance variable

    Interface:

     @Path("myentity")
     public interface MyEntityResource
    
         @GET
         @Produces(MediaType.APPLICATION_JSON)
         public Response getMyEntity();
     }
    

    Implementation:

     public class MyEntityResourceImpl implements MyEntityResource
    
         @Context
         private Request request
    
         @Override
         public Response getMyEntity() {
    
             final MyEntity myEntity = ... // load entity
             final String eTagValue = ... // calclutate value of ETag
    
             final EntityTag eTag = new EntityTag(eTagValue);
    
             ResponseBuilder responseBuilder = request.evaluatePreconditions(eTag);
    
             if (responseBuilder == null) {
                 return Response.ok(user).tag(eTag).build();
             }
    
             return responseBuilder.build();
         }
     }
    

    Disadvantages:

    • return type Reponse is generic

    • missing grammar of return type in WADL

    • dependency injection with @Context is complicated, see https://stackoverflow.com/questions/33240443

  3. ShallowEtagHeaderFilter as web filter

    web.xml:

     <filter>
         <filter-name>etagFilter</filter-name>
         <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class>
     </filter>
     <filter-mapping>
         <filter-name>etagFilter</filter-name>
         <url-pattern>/api/*</url-pattern>
     </filter-mapping>
    

    Interface:

     @Path("myentity")
     public interface MyEntityResource
    
         @GET
         @Produces(MediaType.APPLICATION_JSON)
         public MyEntity getMyEntity();
     }
    

    Implementation:

     public class MyEntityResourceImpl implements MyEntityResource
    
         @Override
         public MyEntity getMyEntity() {
    
             final MyEntity myEntity = ... // load entity
             return myEntity;
         }
     }
    

    Disadvantages:

    • bad server performance, see JavaDoc

    • works only on uncommitted response

    • no support of weak ETag

  4. Custom WriterInterceptor as JAX-RS Interceptor

    Interceptor:

     public class CustomInterceptor implements WriterInterceptor {
    
         @Context
         private Request request;
    
         @Override
         public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
    
             OutputStream old = context.getOutputStream();
    
             ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    
             try {
    
                 context.setOutputStream(buffer);
                 context.proceed();
    
                 byte[] entity = buffer.toByteArray();
    
                 String etag = ... // calclutate value of ETag
                 context.getHeaders().putSingle(HttpHeaders.ETAG, etag);
    
                 ResponseBuilder responseBuilder = request.evaluatePreconditions(eTag);
    
                 if (responseBuilder == null) {
                      throw new WebApplicationException(responseBuilder.status(Response.Status.NOT_MODIFIED).header(HttpHeaders.ETAG, etag).build());
                 }
    
                 old.write(entity);
    
             } finally {
                 context.setOutputStream(old);
             }
         }
     }
    

    See also: ServerCacheInterceptor (Resteasy)

    Interface:

     @Path("myentity")
     public interface MyEntityResource
    
         @GET
         @Produces(MediaType.APPLICATION_JSON)
         public MyEntity getMyEntity();
     }
    

    Implementation:

     public class MyEntityResourceImpl implements MyEntityResource
    
         @Override
         public MyEntity getMyEntity() {
    
             final MyEntity myEntity = ... // load entity
             return myEntity;
         }
     }
    

    Disadvantages:

    • no predefined interceptor for Jersey available

    • bad server performance

    • no support of weak ETag

    • ugly workaround with WebApplicationException

like image 155
dur Avatar answered Sep 18 '22 12:09

dur


Recently I've been solving a similar (if not the same) problem. As a side result of it the following library emerged: https://github.com/AndreyLebedenko/dropwizard-caching-filter

The way it can be used is like below:

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/cached")
@ResponseCachedByFilter(10000)
public Object getCached() {
    return dao.get();
} 

Hope it helps.

like image 20
Andrey Lebedenko Avatar answered Sep 21 '22 12:09

Andrey Lebedenko