<chapter id="Cache_NoCache_CacheControl">
   <title>RESTEasy Caching Features</title>
   <para>
     RESTEasy provides numerous annotations and facilities to support HTTP caching semantics.  Annotations to make setting Cache-Control
     headers easier and both server-side and client-side in-memory caches are available.
   </para>
  <sect1 id="Cache_Annotation">
  <title>@Cache and @NoCache Annotations</title>
   <para>
      RESTEasy provides an extension to &REST-API; that allows you to automatically set Cache-Control headers on a successful
      GET request.  It can only be used on @GET annotated methods.  A successful @GET request is any request
      that returns 200 OK response.
   </para>
   <programlisting>
package org.jboss.resteasy.annotations.cache;

public @interface Cache
{
   int maxAge() default -1;
   int sMaxAge() default -1;
   boolean noStore() default false;
   boolean noTransform() default false;
   boolean mustRevalidate() default false;
   boolean proxyRevalidate() default false;
   boolean isPrivate() default false;
}

public @interface NoCache
{
   String[] fields() default {};
}

   </programlisting>
   <para>
      While @Cache builds a complex Cache-Control header, @NoCache is a simplified notation to indicate no
      caching is wanted; i.e. Cache-Control: nocache.
   </para>
   <para>
      These annotations can be put on the resource class or interface and specifies a default cache value for each
      @GET resource method, or they can be put individually on each @GET resource method.
   </para>
   </sect1>
   <sect1 id="client_cache">
     <title>Client "Browser" Cache</title>
     <para>
        RESTEasy has the ability to set up a client-side, browser-like, cache.  It can be used with the Client Proxy Framework, or with
        ordinary requests.  This cache looks for Cache-Control headers sent back with a server response.  If the Cache-Control headers specify
       that the client is allowed to cache the response, Resteasy caches it within local memory.  The cache obeys max-age requirements 
       and will also automatically do HTTP 1.1 cache revalidation if either or both the Last-Modified and/or ETag headers are sent back with the
       original response.  See the HTTP 1.1 specification for details on how Cache-Control or cache revalidation works.
     </para>
     <para>It is very simple to enable caching.  Here's an example of using the client cache with the Client Proxy Framework</para>
<programlisting>
@Path("/orders")
public interface OrderServiceClient {

   @Path("{id}")
   @GET
   @Produces("application/xml")
   public Order getOrder(@PathParam("id") String id);
}
</programlisting>

     <para>To create a proxy for this interface and enable caching for that proxy requires only a few simple steps
     in which the <classname>BrowserCacheFeature</classname> is registered:</para>
    
<programlisting>
ResteasyWebTarget target = (ResteasyWebTarget) ClientBuilder.newClient().target("http://localhost:8081");
BrowserCacheFeature cacheFeature = new BrowserCacheFeature();
OrderServiceClient orderService = target.register(cacheFeature).proxy(OrderServiceClient.class);
</programlisting>

     <para>
     <classname>BrowserCacheFeature</classname> will create a Resteasy
     <classname>LightweightBrowserCache</classname> by default. It is also possible to configure
     the cache, or install a completely different cache implementation:
     </para>
   
<programlisting>
ResteasyWebTarget target = (ResteasyWebTarget) ClientBuilder.newClient().target("http://localhost:8081");
LightweightBrowserCache cache = new LightweightBrowserCache();
cache.setMaxBytes(20);
BrowserCacheFeature cacheFeature = new BrowserCacheFeature();
cacheFeature.setCache(cache);
OrderServiceClient orderService = target.register(cacheFeature).proxy(OrderServiceClient.class); 
</programlisting>

   <para>If using the standard &REST-API; client framework to make invocations rather than the
   proxy framework, it is just as easy:</para>

<programlisting>
ResteasyWebTarget target = (ResteasyWebTarget) ClientBuilder.newClient().target("http://localhost:8081/orders/{id}");
BrowserCacheFeature cacheFeature = new BrowserCacheFeature();
target.register(cacheFeature);
String rtn = target.resolveTemplate("id", "1").request().get(String.class);
</programlisting>

   <para>The LightweightBrowserCache, by default, has a maximum 2 megabytes of caching space.  This can be changed programmatically by calling
         its setMaxBytes() method.   If the cache gets full, the cache completely wipes itself of all cached data.  This may seem
         a bit draconian, but the cache was written to avoid unnecessary synchronizations in a concurrent environment where the cache is 
         shared between multiple threads.  If a more complex caching solution is desired or a third party cache
         is to be plugged in please contact our resteasy-developers list and discuss it with the community.
   </para>
   </sect1>
   <sect1 id="server_cache">
      <title>Local Server-Side Response Cache</title>
      <para>RESTEasy has a server-side cache that can sit in front of &REST-API; services.
          It automatically caches marshalled responses from HTTP GET &REST-API; invocations if, and only if the
          &REST-API; resource method sets a Cache-Control header.  When a GET comes in, the RESTEasy Server Cache checks
          to see if the URI is stored in the cache.  If found, it returns the already marshalled response without
          invoking the &REST-API; method.  Each cache entry has a max age to whatever is specified in the Cache-Control
          header of the initial request.  The cache also will automatically generate an ETag using an MD5 hash on the
          response body.  This allows the client to do HTTP 1.1 cache revalidation with the IF-NONE-MATCH header.
          The cache is also smart enough to perform revalidation if there is no initial cache hit, but the &REST-API;
          method still returns a body that has the same ETag.
      </para>
       <para>
         The cache is also automatically invalidated for a particular URI that has
           PUT, POST, or DELETE invoked on it.  A reference to the cache can be obtained by injecting an
           org.jboss.resteasy.plugins.cache.ServerCache
           via the @Context annotation
       </para>
<programlisting>

    @Context
    ServerCache cache;

    @GET
    public String get(@Context ServerCache cache) {...}

</programlisting>
      <para>To set up the server-side cache an instance of org.jboss.resteasy.plugins.cache.server.ServerCacheFeature must be registered
          via the Application's getSingletons() or getClasses() methods.  The underlying cache is Infinispan.  By default,
          RESTEasy will create an Infinispan cache for you.  Alternatively, you can create and pass in an instance
          of your cache to the ServerCacheFeature constructor.  Infinispan can also be configured by specifying
          various parameters.  If using Maven, add RESTEasy's cache-core artifact to the project:</para>
<programlisting>
<![CDATA[
<dependency>
   <groupId>org.jboss.resteasy.cache</groupId>
   <artifactId>cache-core</artifactId>
   <version>${version.org.jboss.resteasy.cache}</version>
</dependency>
]]>
</programlisting>
      <para>Next set up the Infinispan configuration in the application's web.xml, it would look like</para>
<programlisting>
<![CDATA[
<web-app>
    <context-param>
        <param-name>server.request.cache.infinispan.config.file</param-name>
        <param-value>infinispan.xml</param-value>
    </context-param>

    <context-param>
        <param-name>server.request.cache.infinispan.cache.name</param-name>
        <param-value>MyCache</param-value>
    </context-param>

</web-app>
]]>
</programlisting>
   <para>
       <code>server.request.cache.infinispan.config.file</code> can either be a classpath or a file path.
       <code>server.request.cache.infinispan.cache.name</code>
       is the name of the cache to reference that is declared in the config file.
   </para>
   
   <para>See <xref linkend="microprofile_config"/> for more information about application configuration. </para>
   
   </sect1>
   <sect1 id="Http_Precondition">
        <title>HTTP preconditions</title>
        <para>
            &REST-API; provides an API for evaluating HTTP preconditions based on <literal>"If-Match"</literal>, <literal>"If-None-Match"</literal>, <literal>"If-Modified-Since"</literal> and <literal>"If-Unmodified-Since"</literal> headers.
        </para>
        <programlisting>
            Response.ResponseBuilder rb = request.evaluatePreconditions(lastModified, etag);
        </programlisting>
        <para>
            By default RESTEasy will return status code 304 (Not modified) or 412 (Precondition failed) if any of conditions fails,
            however it is not compliant with RFC 7232 which states that headers <literal>"If-Match"</literal>, <literal>"If-None-Match"</literal> MUST have higher precedence.
            RFC 7232 compatible mode can be enabled by setting the parameter <literal>resteasy.rfc7232preconditions</literal> to <literal>true</literal>.
            See <xref linkend="microprofile_config"/> for more information about application configuration.
        </para>
   </sect1>
</chapter>
