About Anirudh Bhatnagar

Anirudh is a Java programmer with extensive experience in building Java/J2EE applications. He has always been fascinated by the new technologies and emerging trends in software development. He has been involved in propagating these changes and new technologies in his projects. He is an avid blogger and agile enthusiast who believes in writing clean and well tested code.

HTTP Caching using JAX-RS

In the last blog we discussed different types of caches and their use cases.
In this post we will explore how we can leverage caching using HTTP response headers and the support provided by JAX-RS.

Expires Header

In HTTP 1.0, a simple response header called Expires would tell the browser how long it can cache an object or page. It would be a date in future after which the cache would not be valid. So, if we made an API call to retrieve data :
 
 

GET /users/1

The response header would be:

HTTP/1.1 200 OK
Content-Type: application/xml
Expires: Tue, 25 Aug 2013 16:00 GMT
-----
<user id="1">...</users>

This means the XML data is valid until 25th Aug 2013, 16:00 hours GMT.

JAX-RS supports this header in javax.ws.rs.core.Response object.

@Path("{id}")
	@GET
	@Produces(MediaType.APPLICATION_XML)
	public Response getUserXML(@PathParam("id") Long id){
		User user = userDB.get(id);
		ResponseBuilder builder = Response.ok(user,MediaType.APPLICATION_XML);
		//Putting expires header for HTTP broswer caching.
		Calendar cal = Calendar.getInstance();
		cal.set(2013,7,25,16,0);
		builder.expires(cal.getTime());
		return builder.build();
	}

But to support CDNs, proxy caches and revalidations there was a need for more enhanced headers with richer set of features, having more explicit controls. Hence in HTTP 1.1 few new headers were introduced and Expires was depricated. Lets explore them.

Cache-Control

Cache-Control has a variable set of comma-delimited directives that define who,how and for how long it can be cached. Lets explore few of them:

  • -private/public : these are accessibility directives, private means a browser can cache the object but the proxies or CDNs can not and public makes it cachable by all.
  • -no-cache,no-store,max-age are few others where name tells the story.

JAX-RS provides javax.ws.rs.core.CacheControl class to represent this header.

@Path("{id}")
	@GET
	@Produces(MediaType.APPLICATION_XML)
	public Response getUserXMLwithCacheControl(@PathParam("id") Long id){
		User user = userDB.get(id);
		CacheControl cc = new CacheControl();
		cc.setMaxAge(300);
		cc.setNoStore(true);
		cc.setPrivate(true);
		ResponseBuilder builder = Response.ok(user,MediaType.APPLICATION_XML);
		builder.cacheControl(cc);
		return builder.build();
	}

Revalidation and Conditional GETs : After the cache has expired the cacher can revalidate the cache sending a request to the server to check if the cache is stale or holds good. This is done with the help of a header called as “Last-Modified“.

HTTP/1.1 200 OK
....
Cache-Control: max-age=1000
Last-Modified: Mon, 19 aug 2013 16:00 IST

To revalidate one must send a GET request with the header “If-modified-since“.This is called a conditional GET, in case the data is modified a response code 200 (OK) with current value of resource will be sent. And if the data is not modified a response code of “304″ is sent which would mean the cache is still valid, at this point the “Last-Modified” tag can be updated.

Etag

Etag is another HTTP header which can be used to revalidate caches, It is usually an MD5 hash value. A hash generated from resource is sent by server in the response as Etag value, so that while validating, client can send its Etag value to server to check if the value residing at the server matches.(As the hash is generated from resource, change in resource would generate a different hash)

For this conditional GET, a request with header “If-none-Match” is sent to validate.

GET /users/23 HTTP/1.1
If-None-Match: "23432423423454654667444"

Also, we can have strong and weak Etag values depending on different usecases.

JAX-RS provides us with javax.ws.rs.core.EntityTag for the same.

public class EntityTag {
.....
.....

To help with conditional GETs, JAX-RS also provided one injectable helper class Request, which has methods like…

....
ResponseBuilder evalutatePostConditions(EntityTag eTag);
ResponseBuilder evaluatePreConditions(Date isLastModified);
.....

The etag or LastModified values sent in the request Header are compared. Lets see an example…

@Path("{id}")
	@GET
	@Produces(MediaType.APPLICATION_XML)
	public Response getUserWithEtagSupport(@PathParam("id") Long id,
			@Context Request request){
		User user = userDB.get(id);
		//generating Etag out of hashCode of user
		EntityTag tag = new EntityTag(Integer.toString(user.hashCode()));
		CacheControl cc = new CacheControl();
		cc.setMaxAge(1000);

		ResponseBuilder builder = request.evaluatePreconditions(tag);
		if(builder!=null){
			//means the preconditions have been met and the cache is valid
			//we just need to reset the cachecontrol max age (optional)
			builder.cacheControl(cc);
			return builder.build();
		}

		//preconditions are not met and the cache is invalid
		//need to send new value with reponse code 200 (OK)
		builder = Response.ok(user,MediaType.APPLICATION_XML);
		//reset cache control and eTag (mandatory)
		builder.cacheControl(cc);
		builder.tag(tag);
		return builder.build();

	}

If the conditions have been met it returns a null which means that the latest tag and the tag provided in request header match, and there is no need to send new data with reponse OK. “304″ response meaning not-modified is send.

If the tags don’t match up, a new RequestBuilder object is returned in which we set the new etag and the current version of data (user in this case.)

This is how using JAX-RS we can leverage HTTP caching to its full potential effectively.
 

Reference: HTTP Caching using JAX-RS from our JCG partner Anirudh Bhatnagar at the anirudh bhatnagar blog.
Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


five × 8 =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books