HowToDoInJava

  • Java 8
  • Regex
  • Concurrency
  • Best Practices
  • Spring Boot
  • JUnit5
  • Interview Questions
  • Dark Mode

RESTEasy Cache control with ETag Example

By Lokesh Gupta | Filed Under: RESTEasy

ETags or entity tags are useful HTTP headers which can help in building a super fast application by minimizing the server load on system. ETag is set to the response to the client so a client can use various control request headers such as If-Match and If-None-Match for conditional requests. javax.ws.rs.core.Response.ResponseBuilder#tag() and javax.ws.rs.core.EntityTag are useful classes to work on ETags.

On server side, a unchanged ETag (match done between ETag attached with HTTP request and ETag calculated for requested resource) simply means, resource is unchanged from last requested time, so simply sending a HTTP 304 header [Not Modified] will be enough so that client can use the local available copy of resource without worrying much.

To demonstrate the example, I have two REST APIs in service class.

a) GET http://localhost:8080/RESTEasyEtagDemo/user-service/users/1

This API will get the user resource which will have a ETag attached to it. This ETag will be used by server to verify if user details have been updated sincl last request or not.

b) PUT http://localhost:8080/RESTEasyEtagDemo/user-service/users/1

This API will make some update on User resource on server, which will force the server to return the new copy of resource again.

Lets build the example step by step.

Step 1) Create a new java web project using maven and add following dependencies.

<repositories>
	<repository>
	  <id>jboss</id>
	  <url>http://repository.jboss.org/maven2</url>
	</repository>
</repositories>
<dependencies>
	<!-- core library -->
	<dependency>
		<groupId>org.jboss.resteasy</groupId>
		<artifactId>resteasy-jaxrs</artifactId>
		<version>2.3.1.GA</version>
	</dependency>
	<!-- JAXB support -->
	<dependency>
		<groupId>org.jboss.resteasy</groupId>
		<artifactId>resteasy-jaxb-provider</artifactId>
		<version>2.3.1.GA</version>
	</dependency>
	<dependency>
		<groupId>net.sf.scannotation</groupId>
		<artifactId>scannotation</artifactId>
		<version>1.0.2</version>
	</dependency>
</dependencies>

Step 2) Make a class to represent User Resource

Make sure to provide some logic to verify the last updated time when this resource was modified. I have added one field lastModied of Date type.

package com.howtodoinjava.demo.rest.model;

import java.io.Serializable;
import java.util.Date;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user")
public class User implements Serializable 
{
	@XmlAttribute(name = "id")
	private int id;
	
	@XmlAttribute(name="uri")
	private String uri;
		
	@XmlElement(name = "firstName")
	private String firstName;
	
	@XmlElement(name = "lastName")
	private String lastName;
	
	@XmlElement(name="last-modified")
	private Date lastModified;
	
	//Setters and Getters

Step 3) DAO layer with methods to access and modify the resource

I have used a static HashMap for example purpose. In real time applications, It will be a data base. I have added only one user in Map for example.

package com.howtodoinjava.demo.rest.data;

import java.util.Date;
import java.util.HashMap;

import com.howtodoinjava.demo.rest.model.User;

public class UserDatabase 
{
	public static HashMap<Integer, User> users = new HashMap<Integer, User>();
	static 
	{
		User user = new User();
		user.setId(1);
		user.setFirstName("demo");
		user.setLastName("user");
		user.setUri("/user-management/users/1");
		user.setLastModified(new Date());
		users.put(1, user);
	}
	
	public static User getUserById(Integer id)
	{
		return users.get(id);
	}
	
	public static void updateUser(Integer id)
	{
		User user = users.get(id);
		user.setLastModified(new Date());
	}
	
	public static Date getLastModifiedById(Integer id)
	{
		return users.get(id).getLastModified();
	}
}

Step 4) Write Restful APIs to access and modify the resource

This is the main step. I have written above described two APIs. GET API return the user resource with ETag attached to it. This ETag is calculated on last modified date of User resource. This will ensure that everytime User is updated, a new ETag will be generated.

package com.howtodoinjava.demo.rest.service;

import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;

import com.howtodoinjava.demo.rest.data.UserDatabase;

@Path("/user-service")
public class UserService 
{
	@GET
	@Path("/users/{id}")
	public Response getUserById(@PathParam("id") int id, @Context Request req) 
	{
		//Create cache control header
		 CacheControl cc = new CacheControl();
		 //Set max age to one day
	     cc.setMaxAge(86400);
	        
		Response.ResponseBuilder rb = null;
		
		//Calculate the ETag on last modified date of user resource  
		EntityTag etag = new EntityTag(UserDatabase.getLastModifiedById(id).hashCode()+"");
		
		//Verify if it matched with etag available in http request
        rb = req.evaluatePreconditions(etag);
        
        //If ETag matches the rb will be non-null; 
        //Use the rb to return the response without any further processing
        if (rb != null) 
        {
            return rb.cacheControl(cc).tag(etag).build();
        }
        
        //If rb is null then either it is first time request; or resource is modified
        //Get the updated representation and return with Etag attached to it
        rb = Response.ok(UserDatabase.getUserById(id)).cacheControl(cc).tag(etag);
		return rb.build();
	}
	
	@PUT
	@Path("/users/{id}")
	public Response updateUserById(@PathParam("id") int id) 
	{
		//Update the User resource
		UserDatabase.updateUser(id);
		return Response.status(200).build();
	}
}
Caution: The granularity of dates used in HTTP headers is not as precise as some dates used in data sources.  
For example, the precision for a date in a database row might be defined to the millisecond. However, the date 
in an HTTP header field is only precise to seconds. When evaluating HTTP preconditions, if you compare a 
java.util.Date object directly to the date in an HTTP header, the difference in precision might produce 
unexpected results. 

To avoid this problem, use the hashcode of date object or use some normalize form.

Test the example code

1) First time request : GET http://localhost:8080/RESTEasyEtagDemo/user-service/users/1

User resource request first time
User resource request first time

2) Subsequent request(s) : GET http://localhost:8080/RESTEasyEtagDemo/user-service/users/1

User resource subsequent  requests
User resource subsequent requests

3) Modify request : PUT http://localhost:8080/RESTEasyEtagDemo/user-service/users/1

User resource Updated using PUT API
User resource Updated using PUT API

4) Updated Resource : GET http://localhost:8080/RESTEasyEtagDemo/user-service/users/1

Update User resource retrieved
Update User resource retrieved

To download the sourcecode of above example, click on given below link.

Sourcecode Download

Happy Learning !!

TwitterFacebookLinkedinRedditPocket

About Lokesh Gupta

A family guy with fun loving nature. Love computers, programming and solving everyday problems. Find me on Facebook and Twitter.

9
Leave a Reply

This comment form is under antispam protection
5 Comment threads
4 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
6 Comment authors
This comment form is under antispam protection
  Subscribe  
newest oldest most voted
Notify of
Harsh

users.get(id).getLastModified(); in UserDatabase class is throwing Null pointer exception , kindly tell me the mistake that am doing there.
Thanks

Vote Up0Vote Down  Reply
3 years ago
Lokesh Gupta

I believe no user exist for passed ‘id’. Try to debug the application.

Vote Up0Vote Down  Reply
3 years ago
Ramanujam

Hi Lokesh ,Thanks for Knowledge sharing. The content published in these site is so good,informative and well organized. Here w.rt cache management , Suppose i am using java rest client application ex: RestTemplate,httpclient..etc as a consumer but not any web application /browser also. So in this point of view ,where cache management will done and it will work. can you explain.

Vote Up0Vote Down  Reply
3 years ago
Lokesh Gupta

https://docs.jboss.org/resteasy/docs/1.2.GA/userguide/html/Cache_NoCache_CacheControl.html#server_cache

Vote Up0Vote Down  Reply
3 years ago
subbareddy

super ….lokesh.it works great man

Vote Up0Vote Down  Reply
5 years ago
Gonzalo Melo

Nice article! I have been looking on how to implement authentication in Resteasy. However I’m getting the following error: .

Do you know whats happening?

Thanks in advance!

Vote Up0Vote Down  Reply
6 years ago
Lokesh Gupta

Link does not exist.

Vote Up0Vote Down  Reply
6 years ago
Commissar

How can we make the second request return a 304 not modified response instead? Do we need to update anything server side or is it just the client who needs to send the correct cache control headers?

Vote Up0Vote Down  Reply
6 years ago
Lokesh Gupta

You have to set it on server while returning the response.

Vote Up0Vote Down  Reply
6 years ago

Search Tutorials

RESTEasy Tutorial

  • JAX-RS – Introduction
  • RESTEasy – JBoss
  • RESTEasy – Tomcat
  • RESTEasy – @Path
  • RESTEasy – HATEOAS
  • RESTEasy – SLF4J
  • RESTEasy – Log4j
  • RESTEasy – Download
  • RESTEasy – Upload (MultipartForm)
  • RESTEasy – Upload (HTTPClient)
  • RESTEasy – Custom Validation
  • RESTEasy – Hibernate Validator
  • RESTEasy – ContainerRequestFilter
  • RESTEasy – PreProcessorInterceptor
  • RESTEasy – JAXB XML
  • RESTEasy – Jettison JSON
  • RESTEasy – Jackson JSON
  • RESTEasy – ExceptionMapper

RESTEasy Client APIs

  • RESTEasy – java.net
  • RESTEasy – JAX-RS Client
  • RESTEasy – Apache HttpClient
  • RESTEasy – JavaScript API
  • RESTEasy – ResteasyClientBuilder

RESTEasy Best Practices

  • RESTEasy – Sharing Context Data
  • RESTEasy – Exception Handling
  • RESTEasy – ETag Cache control
  • RESTEasy – GZIP Compression
  • RESTful vs. SOAP

Popular Tutorials

  • Java 8 Tutorial
  • Core Java Tutorial
  • Collections in Java
  • Java Concurrency
  • Spring Boot Tutorial
  • Spring AOP Tutorial
  • Spring MVC Tutorial
  • Spring Security Tutorial
  • Hibernate Tutorial
  • Jersey Tutorial
  • Maven Tutorial
  • Log4j Tutorial
  • Regex Tutorial

Meta Links

  • Advertise
  • Contact Us
  • Privacy policy
  • About Me

Recommended Reading

  • 10 Life Lessons
  • Secure Hash Algorithms
  • How Web Servers work?
  • How Java I/O Works Internally?
  • Best Way to Learn Java
  • Java Best Practices Guide
  • Microservices Tutorial
  • REST API Tutorial
  • How to Start New Blog

Copyright © 2016 · HowToDoInjava.com · All Rights Reserved. | Sitemap

wpDiscuz