> For the complete documentation index, see [llms.txt](https://abhishek-gupta.gitbook.io/rest-assured-with-jaxrs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://abhishek-gupta.gitbook.io/rest-assured-with-jaxrs/jax-rs-for-power-users-part-ii.md).

# JAX-RS for Power Users Part II

This chapter is about efficient JAX-RS. We'll explore

* **Content Negotiation**
* **Caching**: how does the JAX-RS framework help leverage the HTTP caching mechanism
* **Conditional access**: criteria based GET and PUT

## HTTP Content Negotiation

Content Negotiation is the feature using which a client is able to specify certain characteristics of the response it is looking for. It is not JAX-RS specific. Rather, it is closely related to HTTP (hence, the web) and the JAX-RS framework supports the same

> **Must know**
>
> * Criteria (characteristics): the client can negotiate on the following properties of the HTTP response - encoding, media type, language
> * Uses standard HTTP headers: `Accept` (media type), `Accept-Language` (language), `Accept-Encoding` (encoding)
> * Preferential selection for Media types: uses additional metadata (**q**) to specify affinity for a particular media type from a list of multiple choices

### Content Negotiation: the obvious way

Let's start off with an example

```
//limited negotiation

@Path("/books")
public class BooksResource {

    @Context 
    HTTPHeaders headers;

    @GET
    @Produces("application/json", "application/xml")
    public Response all(){
      MediaType mType = headers.getAcceptableMediaTypes().get(0);
      Locale lang = headers.getAcceptableLanguages().get(0);
       Books books = getAll();

      return Response.ok(books).type(mType).language(lang).build();
    }
}
```

**What's going on here?**

* the JAX-RS resource offers JSON & XML media types (as per `@Produces`)
* the HTTP request headers are injected and the `getAcceptableMediaTypes` extracts the information from the `Accept` header
* `getAcceptableLanguages` does the same for language
* the extracted language and media type are set in the HTTP response as well

**Drawbacks**

* suitable for single negotiation values
* not fine grained in nature
* cannot handle complex/preferential negotiation&#x20;

### Content Negotiation: the fine grained way

Making use of the `Variant` API in JAX-RS can help. It provides a simple abstractions and can used with some of the advanced use cases where

* the client provides multiple choices e.g. more than one media type
* it specifies their weightage as well i.e. which one does it prefer more

```
//fine grained negotiation

@Path("/books")
public class BooksResource {

    @Context 
    Request req;

    @GET
    @Produces("application/json", "application/xml")
    public Response all(){
      List<Variant> variants = Arrays.asList(
      new Variant(MediaType.APPLICATION_XML_TYPE,"en", "deflate"),
      new Variant(MediaType.APPLICATION_JSON_TYPE,"en", "deflate")
      );

       //heavy lifting done by JAX-RS
       Variant theOne = req.selectVariant(variants);

       MediaType selectedMType = theOne.getMediaType();
       Locale lang = theOne.getLanguage();

      return Response.ok(books)
          .type(selectedMType).language(lang).build();
    }
}
```

**Why is this better ??**

* The above logic can handle complex preferential negotiation requests e.g. Accept: application/xml;q=1.0, application/json;q=0.5. This actually means, I prefer XML but JSON would work (in case you don't have XML data)
* The calculation is done by `selectVariant` method in `Request` API - all you need to do is give it probable list from which to choose from

> **Implicit Content Negotiation based on Media Types**
>
> At its very core, JAX-RS drives media type based content negotiation on the basis of the Accept header sent by the client and the media type specified by @Produces annotation in the JAX-RS methods

## Caching in JAX-RS

Caching is not a new concept. It is the act of storing data temporarily in a location from where it can be accessed faster (e.g. in-memory) as compared to it's original source (e.g. a database)

Before we dive in further, it's very important that we understand the following

* From a JAX-RS perspective, caching does not imply a server side cache
* It just provides *hints* to the client in terms of the durability/validity of the resource data
* It does not define how the client will use this hint. It ensures that it sticks to the HTTP semantics and assumes that the client (e.g. a browser, programmatic API based client etc.) understands the HTTP protocol

JAX-RS has had support for the [Cache-Control header](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) was added in [HTTP 1.1](http://www.w3.org/Protocols/rfc2616/rfc2616.html) since its initial (1.0) version. The [CacheControl ](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/CacheControl.html) class is an equivalent of the `Cache-Control` header in the HTTP world. It provides the ability to configure the header (and its different attributes) via simple setter methods.

### So how to I use the `CacheControl` class?

Just return a [Response ](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Response.html)object around which you can wrap an instance of the `CacheControl` class.

```
//Create a CacheControl instance manually and send it along with the Response

@Path("/testcache")
public class RESTfulResource {
    @GET
    @Produces("text/plain")
    public Response find(){
      CacheControl cc = new CacheControl();
      cc.setMaxAge(20);
      return Response.ok(UUID.randomUUID().toString()).cacheControl(cc).build();
    }
}
```

At runtime

* The caller receives the `Cache-Control` response header and is free to use the information therein in order to decide when to fetch a new version of the resource
* If the `max-age` attribute has a value of 3600 seconds, it means that the client is free to cache the resource representation for the next one minute (and does not need to call upon the server)

> Please note that the JAX-RS framework does not define how the client will actually 'cache' the response

### CDI Producers

We can use CDI to enforce caching semantics in a declarative manner. [CDI ](https://github.com/abhirockzz/rest-assured-with-jaxrs/tree/4d902d1208300d4f51b514c10a4007954c82212b/cdispec.org)Producers can help inject instances of classes which are *not technically beans* (as per the strict definition) or for classes over which you do not have control as far as decorating them with scopes and qualifiers are concerned. The idea is to

* Have a custom annotation (`@CacheControlConfig`) to define default values for Cache-Control header and allow for flexibility in case you want to override it
* Use a CDI Producer to create an instance of the CacheControl class by using the `InjectionPoint` object (injected with pleasure by CDI !) depending upon the annotation parameters
* Just `inject` the `CacheControl` instance in your REST resource class and use it in your methods

```
//A custom annotation to configure Cache Control parameters

@Retention(RUNTIME)
@Target({FIELD, PARAMETER})
public @interface CachControlConfig {

    public boolean isPrivate() default true;
    public boolean noCache() default false;
    public boolean noStore() default false;
    public boolean noTransform() default true;
    public boolean mustRevalidate() default true;
    public boolean proxyRevalidate() default false;
    public int maxAge() default 0;
    public int sMaxAge() default 0;

}
```

```
//A CDI Producer (factory)

public class CacheControlFactory {

    @Produces
    public CacheControl get(InjectionPoint ip) {

        CachControlConfig ccConfig = ip.getAnnotated()
        .getAnnotation(CachControlConfig.class);

        CacheControl cc = null;
        if (ccConfig != null) {
            cc = new CacheControl();
            cc.setMaxAge(ccConfig.maxAge());
            cc.setMustRevalidate(ccConfig.mustRevalidate());
            cc.setNoCache(ccConfig.noCache());
            cc.setNoStore(ccConfig.noStore());
            cc.setNoTransform(ccConfig.noTransform());
            cc.setPrivate(ccConfig.isPrivate());
            cc.setProxyRevalidate(ccConfig.proxyRevalidate());
            cc.setSMaxAge(ccConfig.sMaxAge());
        }

        return cc;
    }
}
```

```
//Good to go!

@Path("/testcache")
public class RESTfulResource {
    @Inject
    @CachControlConfig(maxAge = 20)
    CacheControl cc;

    @GET
    @Produces("text/plain")
    public Response find() {
        return Response.ok(UUID.randomUUID()
        .toString()).cacheControl(cc).build();
    }
}
```

> **Additional thoughts**
>
> * In this case, the scope of the produced CacheControl instance is `@Dependent` i.e. it will live and die with the class which has injected it. In this case, the JAX-RS resource itself is `RequestScoped` (by default) since the JAX-RS container creates a new instance for each client request, hence a new instance of the injected `CacheControl` instance will be created along with each HTTP request
> * You can also introduce CDI qualifiers to further narrow the scopes and account for corner cases
> * You might think that the same can be achieved using a JAX-RS filter. That is correct. But you would need to set the Cache-Control header manually (within a mutable `MultivaluedMap`) and the logic will not be flexible enough to account for different `Cache-Control` configurations for different scenarios

## Efficient JAX-RS: Conditional GETs & PUTs

This section discusses how to leverage features in the JAX-RS API to execute RESTful operations based on conditions/criteria in order to aid with scalability and performance. It covers

* which HTTP headers are involved
* which JAX-RS APIs to use
* details of the entire request-response flow

### Must know HTTP headers

In addition to the Cache-Control HTTP header, we would also encounter few others in this section. If you haven't heard of these before, don't worry, their names are self-explanatory

* Last-Modified
* If-Modified-Since
* If-Unmodified-Since
* ETag
* If-None-Match

> It would be niceif you have a basic understanding of (at least some of) these headers. These are best referred from the official [HTTP specification document](https://tools.ietf.org/html/rfc2616#section-4.2).

![RFC 2616](/files/-M3y22dK7rg9sYeglcrn)

### Before we proceed...

Here is a quickie on the two important JAX-RS APIs which we will be discussing here

* [EntityTag](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/EntityTag.html): JAX-RS equivalent (simple class) of the HTTP `ETag` header
* [Request](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Request.html): the main API which contains utility methods to evaluate the conditions which in turn determine the criteria for access

### Cache Revalidation: that's what it's all about

A simple interaction with a JAX-RS service can be as follows

* Client sends a GET request
  * Server replies back with the requested resource (with a HTTP 200 status)
  * It also sends the `Cache-Control` & `Last-Modified` headers in response

`Cache-Control` defines the **expiration** semantics (along with other fine grained details) for the resource on the basis of which the client would want to

* **revalidate** it's cache i.e. invoke the GET operation for same resource (again)
  * make sure it does so in an efficient/scalable/economic manner i.e. not repeat the same process of exchanging data (resource info) if there are no changes to the information that has been requested

Common sense stuff right ? Let's look at how we can achieve this

#### Leverage the `Last-Modified` and `If-Modified-Since` headers

```
//Improving GET request performance in JAX-RS by using the Last-Modified and If-Modified-Since headers

@Path("books")
@Stateless
public class BooksResource_1{

  @PersistenceContext
  EntityManager em;

  @Context
  Request request;

  @Path("{id}")
  @GET
  @Produces("application/json")
  public Response getById(@PathParam("id") String id){
    //get book info from backend DB
    Book book = em.find(Book.class, id); 
    //get last modified date
    Date lastModified = book.getLastModified(); 

    //let JAX-RS do the math!
    ResponseBuilder evaluationResultBuilder = request.evaluatePreconditions(lastModified); 

    if(evaluationResultBuilder == null){
      //resource was modified, send latest info (and HTTP 200 status)
      evaluationResultBuilder = Response.ok(book); 
    }else{
      System.out.println("Resource not modified - HTTP 304 status");
    }
    CacheControl caching = ...; //decide caching semantics
    //add metadata
    evaluationResultBuilder.cacheControl(caching)
                           .header("Last-Modified",lastModified); 

    return evaluationResultBuilder.build();
  }
}
```

* Server sends the `Cache-Control` & `Last-Modified` headers as a response (for a GET request)
* In an attempt to refresh/revalidate it's cache, the client sends the value of the Last-Modified header in the `If-Modified-Since` header when requesting for the resource in a subsequent request
* [`Request.evaluatePreconditions(Date)`](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Request.html#evaluatePreconditions-java.util.Date-) determines whether or not the value passed in the If-Modified-Since header is the same as the date passed to the method (ideally the modified date would need to extracted from somewhere and passed on this method)- [`Request#evaluatePreconditions(Date)`](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Request.html#evaluatePreconditions-java.util.Date-) determines whether or not the value passed in the `If-Modified-Since` header is the same as the date passed to the method (ideally the modified date would need to extracted from somewhere and passed on this method)

#### `ETag` in action

```
//Improving GET request performance in JAX-RS by using the ETag header

@Path("books")
@Stateless
public class BooksResource_2{

  @PersistenceContext
  EntityManager em;

  @Context
  Request request;

  @Path("{id}")
  @GET
  @Produces("application/json")
  public Response getById(@PathParam("id") String id){
    Book book = em.find(Book.class, id); //get book info from backend DB
    //calculate tag value based on your custom implementation
    String uniqueHashForBook = uniqueHashForBook(book);
    //instantiate the object 
    EntityTag etag = new EntityTag(uniqueHashForBook)  

    //let JAX-RS do the math!
    ResponseBuilder evaluationResultBuilder = request.evaluatePreconditions(etag); 

    if(evaluationResultBuilder == null){
      //resource was modified, send latest info (and HTTP 200 status)
      evaluationResultBuilder = Response.ok(book); 
    }else{
      System.out.println("Resource not modified - HTTP 304 status");
    }
    CacheControl caching = ...; //decide caching semantics
    evaluationResultBuilder.cacheControl(caching)
                           .tag(etag); //add metadata

    return evaluationResultBuilder.build();
  }
}
```

* In addition to the Last-Modified header, the server can also set the `ETag` header value to a string which uniquely identifies the resource and changes when it changes e.g. a hash/digest
* client sends the value of the ETag header in the `If-None-Match` header when requesting for the resource in a subsequent request
* and then its over to the [`Request.evaluatePreconditions(EntityTag)`](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Request.html#evaluatePreconditions-javax.ws.rs.core.EntityTag-)

> With the [`Request.evaluatePreconditions(Date,EntityTag)`](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Request.html#evaluatePreconditions-java.util.Date-javax.ws.rs.core.EntityTag-)the client can use both last modified date as well as the `ETag` values for criteria determination. This would require the client to set the `If-Modified-Since` header

### Making use of the API response...

In both the scenarios

* if the `Request.evaluatePreconditions` method returns null, this means that the pre-conditions were met (the resource was modified since a specific time stamp and/or the entity tag representing the resource does not match the specific `ETag` header) and the *latest version of the resource must be fetched* and sent back to the client
* otherwise, a **HTTP 304 (Not Modified)** response is automatically returned by the method, and it can be returned as is

> - **Choice of ETag**: this needs to be done carefully and depends on the dynamics of your application. *What are the attributes of your resource whose changes are critical for your clients ?* Those are the ones which you should use within your ETag implementation
> - **Not a magic bullet**: based on the precondition evaluation, you can help prevent unnecessary exchange of data b/w client and your REST service layer, but not between your JAX-RS service and the backend repository (e.g. a database). It's important to understand this

### Can I only improve my GETs ...?

No ! the HTTP spec cares abut *PUT* operations as well; and so does the JAX-RS spec

```
//Improving PUTs

@Path("books")
@Stateless
public class BooksResource_3{

  @PersistenceContext
  EntityManager em;

  @Context
  Request request;

  @Path("{id}")
  @PUT
  public Response update(@PathParam("id") String id, Book updatedBook){
    Book book = em.find(Book.class, id); //get book info from backend DB
    Date lastModified = book.getLastModified(); //get last modified date
    //let JAX-RS do the math!
    ResponseBuilder evaluationResultBuilder = request.evaluatePreconditions(lastModified); 

    if(evaluationResultBuilder == null){
      em.merge(updatedBook); //no changes to book data. safe to update book info
      //(ideally) nothing needs to sent back to the client in case of successful 
      evaluationResultBuilder = Response.noContent(); update
    }else{
      System.out.println("Resource was modified after specified time stamp - HTTP 412 status");
    }

    CacheControl caching = ...; //decide caching semantics
    evaluationResultBuilder.cacheControl(caching)
                           .header("Last-Modified",lastModified); //add metadata

    return evaluationResultBuilder.build();
}
```

* Server sends the `Cache-Control` & `Last-Modified` headers as a response (for a GET request)
* In an attempt to send an updated value of the resource, the client sends the value of the Last-Modified header in the `If-Unmodified-Since` header
* `Request.evaluatePreconditions(Date)` method determines whether or not the value passed in the `If-Unmodified-Since` header is the same as the date passed to the method (in your implementation)

#### here is the gist ...

* If the API returns a non null response, this means that the pre-conditions were not met (HTTP 412) i.e. the resource was in fact modified after the time stamp sent in the `If-Unmodified-Since` header, which of course means that the caller has a (potentially) *stale* (outdated) version of the resource
* Otherwise (for a null output from the API), its a hint for the client to go ahead and execute the update operation
* In this scenario, what you end up saving is the cost of update operation executed against your database in case the client's version of the resource is outdated
