# 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://abhishek-gupta.gitbook.io/rest-assured-with-jaxrs/jax-rs-for-power-users-part-ii.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
