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.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

One Response to "HTTP Caching using JAX-RS"

  1. dan says:

    line 13 of the last snippet s/b
    if(builder==null){

Leave a Reply


five − = 2



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
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.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close