===  Infinispan REST Server

The Infinispan Server distribution contains a server module that implements link:$$http://en.wikipedia.org/wiki/Representational_State_Transfer$$[RESTful] HTTP access to the Infinispan data grid, built on JAX_RS.
Please refer to Infinispan Server's link:$$../infinispan_server_guide/infinispan_server_guide.html$$[documentation] for instructions on how to configure and run a REST server.

==== REST API
HTTP PUT and POST methods are used to place data in the cache, with URLs to address the cache name and key(s) - the data being the body of the request (the data can be anything you like). It is important that a Content-Type header is set. Other headers are used to control the cache settings and behaviour (detailed in that link). 

===== Putting data in
====== `PUT /{cacheName}/{cacheKey}`
A PUT request of the above URL form will place the payload (body) in the given cache, with the given key (the named cache must exist on the server). For example `http://someserver/hr/payRoll/3` (in which case `hr` is the cache name, and `payRoll/3` is the key). Any existing data will be replaced, and Time-To-Live and Last-Modified values etc will updated (if applicable). 

====== `POST /{cacheName}/{cacheKey}`
Exactly the same as PUT, only if a value in a cache/key already exists, it will return a Http CONFLICT status (and the content will not be updated). 

====== Headers

*  Content-Type : MANDATORY (use link:$$http://www.iana.org/assignments/media-types/$$[media/mime-types] for example: "application/json").  If you set the Content-Type to `application/x-java-serialized-object`, then it will be stored as a Java object 

*  performAsync : OPTIONAL true/false (if true, this will return immediately, and then replicate data to the cluster on its own. Can help with bulk data inserts/large clusters.) 

*  timeToLiveSeconds : OPTIONAL number (the number of seconds before this entry will automatically be deleted). If no parameter is sent, Infinispan assumes -1 as default value, which means that the entry will not expire. Passing any negative value will have the same effect.

*  maxIdleTimeSeconds : OPTIONAL number (the number of seconds after last usage of this entry when it will automatically be deleted). If no  parameter is sent, Infinispan assumes -1 as default value, which means that the entry will not expire as a result of idle time. Passing any negative value will have the same effect.

.Passing 0 as parameter for timeToLiveSeconds and/or maxIdleTimeSeconds
*  If both `timeToLiveSeconds` and `maxIdleTimeSeconds` are 0, the cache will use the default `lifespan` and `maxIdle` values configured in XML/programmatically 
*  If _only_ `maxIdleTimeSeconds` is 0, it uses the `timeToLiveSeconds` value passed as parameter (or -1 if not present), and default `maxIdle` configured in XML/programmatically 
*  If _only_ `timeToLiveSeconds` is 0, it uses default `lifespan` configured in XML/programmatically, and `maxIdle` is set to whatever came as parameter (or -1 if not present)

===== Getting data back out
HTTP GET and HEAD are used to retrieve data from entries. 

====== `GET /{cacheName}/{cacheKey}`
This will return the data found in the given cacheName, under the given key - as the body of the response. A Content-Type header will be supplied which matches what the data was inserted as (other then if it is a Java object, see below). Browsers can use the cache directly of course (eg as a CDN). An link:$$http://en.wikipedia.org/wiki/HTTP_ETag$$[ETag] will be returned unique for each entry, as will the Last-Modified and Expires headers field indicating the state of the data at the given URL. ETags allow browsers (and other clients) to ask for data only in the case where it has changed (to save on bandwidth) - this is standard HTTP and is honoured by Infinispan. 

Since Infinispan 5.3 it is possible to obtain additional information by appending the "extended" parameter on the query string, as follows:

 GET /cacheName/cacheKey?extended

This will return the following custom headers:


* Cluster-Primary-Owner: the node name of the primary owner for this key
* Cluster-Node-Name: the JGroups node name of the server that has handled the request
* Cluster-Physical-Address: the physical JGroups address of the server that has handled the request.

====== `HEAD /{cacheName}/{cacheKey}`
The same as GET, only no content is returned (only the header fields). You will receive the same content that you stored. E.g., if you stored a String, this is what you get back. If you stored some XML or JSON, this is what you will receive. If you stored a binary (base 64 encoded) blob, perhaps a serialized; Java; object - you will need to; deserialize this yourself.

Similarly to the GET method, the HEAD method also supports returning extended information via headers. See above.

===== Listing keys
====== `GET /{cacheName}`

This will return a list of keys present in the given cacheName as the body of the response. The format of the response can be controlled via the Accept header as follows:

* application/xml - the list of keys will be returned in XML format.
* application/json - the list of keys will be return in JSON format.
* text/html - the list of keys will be returned in HTML format.
* text/plain - the list of keys will be returned in plain text format, one key per line

If the cache identified by cacheName is distributed, only the keys owned by the node handling the request will be returned. To return all keys, append the "global" parameter to the query, as follows:

 GET /cacheName?global

===== Removing data
Data can be removed at the cache key/element level, or via a whole cache name using the HTTP delete method.

====== `DELETE /{cacheName}/{cacheKey}`

Removes the given key name from the cache.

====== `DELETE /{cacheName}`
Removes ALL the entries in the given cache name (i.e., everything from that path down). If the operation is successful, it returns 200 code.

.Make it quicker!
TIP: Set the header performAsync to true to return immediately and let the removal happen in the background.

==== Client side code
Part of the point of a RESTful service is that you don't need to have tightly coupled client libraries/bindings. All you need is a HTTP client library. For Java, Apache HTTP Commons Client works just fine (and is used in the integration tests), or you can use java.net API.

===== Ruby example

[source,ruby]
----
# Shows how to interact with Infinispan REST api from ruby.
# No special libraries, just standard net/http
#
# Author: Michael Neale
#
require 'net/http'

http = Net::HTTP.new('localhost', 8080)

#Create new entry
http.post('/infinispan/rest/MyData/MyKey', 'DATA HERE', {"Content-Type" => "text/plain"})

#get it back
puts http.get('/infinispan/rest/MyData/MyKey').body

#use PUT to overwrite
http.put('/infinispan/rest/MyData/MyKey', 'MORE DATA', {"Content-Type" => "text/plain"})

#and remove...
http.delete('/infinispan/rest/MyData/MyKey')

#Create binary data like this... just the same...
http.put('/infinispan/rest/MyImages/Image.png', File.read('/Users/michaelneale/logo.png'), {"Content-Type" => "image/png"})


#and if you want to do json...
require 'rubygems'
require 'json'

#now for fun, lets do some JSON !
data = {:name => "michael", :age => 42 }
http.put('/infinispan/rest/Users/data/0', data.to_json, {"Content-Type" => "application/json"})

----

===== Python example

[source,python]
----

# Sample python code using the standard http lib only
#

import httplib


#putting data in
conn = httplib.HTTPConnection("localhost:8080")
data = "SOME DATA HERE \!" #could be string, or a file...
conn.request("POST", "/infinispan/rest/Bucket/0", data, {"Content-Type": "text/plain"})
response = conn.getresponse()
print response.status

#getting data out
import httplib
conn = httplib.HTTPConnection("localhost:8080")
conn.request("GET", "/infinispan/rest/Bucket/0")
response = conn.getresponse()
print response.status
print response.read()

----

===== Java example


[source,java]
----

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Rest example accessing Infinispan Cache.
 * @author Samuel Tauil (samuel@redhat.com)
 *
 */
public class RestExample {

   /**
    * Method that puts a String value in cache.
    * @param urlServerAddress
    * @param value
    * @throws IOException
    */
   public void putMethod(String urlServerAddress, String value) throws IOException {
      System.out.println("----------------------------------------");
      System.out.println("Executing PUT");
      System.out.println("----------------------------------------");
      URL address = new URL(urlServerAddress);
      System.out.println("executing request " + urlServerAddress);
      HttpURLConnection connection = (HttpURLConnection) address.openConnection();
      System.out.println("Executing put method of value: " + value);
      connection.setRequestMethod("PUT");
      connection.setRequestProperty("Content-Type", "text/plain");
      connection.setDoOutput(true);

      OutputStreamWriter outputStreamWriter = new OutputStreamWriter(connection.getOutputStream());
      outputStreamWriter.write(value);
         
      connection.connect();
      outputStreamWriter.flush();
       
      System.out.println("----------------------------------------");
      System.out.println(connection.getResponseCode() + " " + connection.getResponseMessage());
      System.out.println("----------------------------------------");
         
      connection.disconnect();
   }

   /**
    * Method that gets a value by a key in url as param value.
    * @param urlServerAddress
    * @return String value
    * @throws IOException
    */
   public String getMethod(String urlServerAddress) throws IOException {
      String line = new String();
      StringBuilder stringBuilder = new StringBuilder();

      System.out.println("----------------------------------------");
      System.out.println("Executing GET");
      System.out.println("----------------------------------------");

      URL address = new URL(urlServerAddress);
      System.out.println("executing request " + urlServerAddress);

      HttpURLConnection connection = (HttpURLConnection) address.openConnection();
      connection.setRequestMethod("GET");
      connection.setRequestProperty("Content-Type", "text/plain");
      connection.setDoOutput(true);

      BufferedReader&nbsp; bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

      connection.connect();

      while ((line = bufferedReader.readLine()) \!= null) {
         stringBuilder.append(line + '\n');
      }

      System.out.println("Executing get method of value: " + stringBuilder.toString());

      System.out.println("----------------------------------------");
      System.out.println(connection.getResponseCode() + " " + connection.getResponseMessage());
      System.out.println("----------------------------------------");

      connection.disconnect();

      return stringBuilder.toString();
   }

   /**
    * Main method example.
    * @param args
    * @throws IOException
    */
   public static void main(String\[\] args) throws IOException {
      //Attention to the cache name "cacheX" it was configured in xml file with tag <*-cache name="cacheX">
      RestExample restExample = new RestExample();
      restExample.putMethod("http://localhost:8080/infinispan/rest/cacheX/1", "Infinispan REST Test");
      restExample.getMethod("http://localhost:8080/infinispan/rest/cacheX/1");         
   }
}

----

