Skip to main content

Http Caching with JAX-RS and Groovy

Posted by lamine_ba on October 24, 2012 at 8:25 AM PDT

Do what you think is interesting, do something that you think is fun and worthwhile, otherwise you won’t do it well anyway.
Brian Kernighan

INTRODUCTION

Ladies and Gentlemen, really we went too far with the server-side. Yes, as much as we have dramatically invented ways that shouldn't be invented or at least in the way they are. But as an argument for our defense, we can only reply to that, it's really true that one can only cope with what he has, while keeping the hope for better days. And once we have transposed such great statement into the context of the web and its current evolution, we can only state that nowadays, it is perfectly legal for one to ask if storing the User's preferences on the server-side is a more efficient and rationale approach than to do it on the client-side using the new HTML5 Web Storage.

HTTP Caching with JAX-RS

OK, today before going too far, I have a great question for a great JSF expert. And maybe one of you will raise his hand? OK, with such great web framework, how one can leverage the HTTP caching mechanism to cache the list of his customers on the client-side? But do we need really to move further on that question since the guy at the corner said that we can use Memcached for that. And after such brilliant answer coupled with such unpleasant irony, wouldn't it better if we could move on to see how one can do it with JAX-RS and with the beautiful Groovy language?

Caching in a nutshell

No feature does come without a bit of theory with it and as patriot Bill Burke has written it in his amazing book, Restful Java with JAX-RS [ O'Reilly ] : "Caching is one of the more important features of the Web. When you visit a website for the first time, your browser stores images and static text in memory and on disk. If you revisit the site within minutes, hours, days, or even months, your browser doesn’t have to reload the data over the network and can instead pick it up locally. This greatly speeds up the rendering of revisited web pages and makes the browsing experience much more fluid. Browser caching not only helps page viewing, it also cuts down on server load. If the browser is obtaining images or text locally, it is not eating up scarce server bandwidth or CPU cycles. Besides browser caching, there are also proxy caches. Proxy caches are pseudo webservers that work as middlemen between browsers and websites. Their sole purpose is to ease the load on master servers by caching static content and serving it to clients directly, bypassing the main servers. Content delivery networks (CDNs) like Akamai have made multimillion-dollar businesses out of this concept. These CDNs provide you with a worldwide network of proxy caches that you can use to publish your website and scale to hundreds of thousand of users. If your web services are RESTful, there’s no reason you can’t leverage the caching semantics of the Web within your applications. If you have followed the HTTP constrained interface religiously, any service URI that can be reached with an HTTP GET is a candidate for caching, as they are, by definition, read-only and idempotent".

RESTful Java with JAX-RS by: Bill Burke
Copyright ©2010 William J. Burke, Jr. All rights reserved. Used with permission

HTTP Caching with JAX-RS and Groovy in Practice

OK, has come now the time for us to put into practice what we have learned from Bill, but before we could do it, may I ask if I have to send an HTTP 1.0 Expires header to the client to tell it to cache the list of the customers until December 25, 2012?

  1. @Path("/customers")
  2. class CustomerService {
  3.  
  4.         @GET
  5.         @Produces([MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML])
  6.         def getCustomers() {
  7.                
  8.                 def date = Calendar.instance.set(2012, 12, 25, 0, 0);
  9.                 def customers= new GenericEntity<Collection<Customer>>(dao.customers){};
  10.                 ok(customers).expires(date).build();
  11.  
  12.         }
  13.  
  14. }

HTTP Response

  1. HTTP/1.1 200 OK
  2. Content-Type: application/xml
  3. Expires: Tue, 25 Dec 2012 00:00 GMT
  4.  
  5. <customers>...</customers>

Or do I have just to forget about this deprecated response header and follow the footsteps of evolution to leverage the HTTP 1.1 specification in which the caching semantics were completely redone to include a much richer feature set that has more explicit controls over browser and CDN/proxy caches? And all of that is done through the amazing Cache-Control header which I reassure you, is just a variable set of comma-delimited directives that define who can cache, how, and for how long.

Directive Description
private The private directive states that no shared intermediary (proxy or CDN) is allowed
to cache the response. This is a great way to make sure that the client, and only the
client, caches the data.
public The public directive is the opposite of private. It indicates that the response may
be cached by any entity within the request/response chain.
no-cache Usually, this directive simply means that the response should not be cached. If it
is cached anyway, the data should not be used to satisfy a request unless it is revalidated
with the server (more on revalidation later).
no-store A browser will store cacheable responses on disk so that they can be used after a
browser restart or computer reboot. You can direct the browser or proxy cache to
not store cached data on disk by using the no-store directive.
no-transform Some intermediary caches have the option to automatically transform their cached
data to save memory or disk space or to simply reduce network traffic. An example
is compressing images. For some applications, you might want to disallow this
using the no-transform directive.
max-age This directive is how long (in seconds) the cache is valid. If both an Expires header
and a max-age directive are set in the same response, the max-age always takes
precedence.
s-maxage The s-maxage directive is the same as the max-age directive, but it specifies the
maximum time a shared, intermediary cache (like a proxy) is allowed to hold the
data. This directive allows you to have different expiration times than the client.

RESTful Java with JAX-RS by: Bill Burke
Copyright ©2010 William J. Burke, Jr. All rights reserved. Used with permission

  1. @Path("/customers")
  2. class CustomerService {
  3.  
  4.         @GET
  5.         @Produces([MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML])
  6.         def getCustomers() {
  7.                
  8.                 def cacheControl = new CacheControl(private:true,noStore:true,maxAge:300)
  9.                 def customers= new GenericEntity<Collection<Customer>>(dao.customers){}
  10.                 ok(customers).cacheControl(cacheControl).build()
  11.  
  12.         }
  13.  
  14. }

HTTP Response

  1. HTTP/1.1 200 OK
  2. Content-Type: application/xml
  3. Cache-Control: private, no-store, max-age=300
  4. <customers>...</customers>

And what is the real value of a caching system if no one is able to perform a revalidation in which the cacher will ask to the server, if the data it is holding is still valid? And how a client can do that if the server does not send to it some extra information about the resource it is caching during the initial response? And is it really true that one can simply achieve that, using an ETag, a Last-Modified header or a complex combination of both?

  1. @Path("/customers")
  2. class CustomerService {
  3.  
  4.         @GET
  5.         @Produces([MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML])
  6.         def getCustomers(@Context Request request) {
  7.                
  8.                 def cacheControl = new CacheControl(private:true,noStore:true,maxAge:300)
  9.                 def tag=new EntityTag(dao.code)
  10.                 def builder = request.evaluatePreconditions(tag)
  11.                 if(builder) return builder.cacheControl(cacheControl).build()
  12.                 def customers= new GenericEntity<Collection<Customer>>(dao.customers){}
  13.                 ok(customers).cacheControl(cacheControl).tag(tag).build()
  14.  
  15.         }
  16.  
  17. }

Initial HTTP Response

  1. HTTP/1.1 200 OK
  2. Content-Type: application/xml
  3. Cache-Control: private, no-store, max-age=300
  4. ETag: "3141271342554322343200"
  5.  
  6. <customers>...</customers>

Subsequent HTTP Request

  1. GET /customers HTTP/1.1
  2. If-None-Match: "3141271342554322343200"

HTML5 Web Storage : A Pratical Usage

Ladies and Gentlemen, right now the client-side is so hot and so is JavaScript which is by the way the only language for which the majority of the new HTML5 features were designed in mind. And to use them all, I hope that you are not waiting for a stupid server-side web framework to bring you, insane, incomplete and unmanageable abstractions so that you can use in the future, what one can already use today in his web application? And as a proof, I'm going now to share with you, how for my web application and for my virtual editor, I was able to save as JSON, the preferences of a user using the new HTML5 Web Storage which is by the way, one of the safest JavaScript APIs to use today because of its widespread support.

HTML5 Local Storage [ WRITE ]

  1. if(window.localStorage) {
  2.                
  3.     window.localStorage.setItem("editor.preferences",JSON.stringify(editor.preferences));
  4.                
  5. }
  6.  

HTML5 Local Storage [ READ ]

  1. if(window.localStorage) {
  2.  
  3.         var preferences=window.localStorage.getItem("editor.preferences");
  4.         if(preferences){
  5.                 editor.preferences=JSON.parse(preferences);
  6.         }
  7.  
  8. }

And to see it in action in the demo below, just open a file with one editor and click on the Config button to display its settings. And my best recommendation is to play around with the Orion Editor, ACE or CodeMirror since they have really some fancier themes.




And in the same time, you can also check out my lovely plugins to see how beautiful is jsbeautify. And to verify the correctness of a JavaScript file, is there a tool more handy than JSLint?





twitter : lamine_ba

 

AttachmentSize
editor-config.PNG13.38 KB
editor-plugins.PNG6.22 KB
ace-configuration.PNG81.92 KB