Chapter 13. The REST API

13.1. The REST Interface

Red Hat JBoss Data Grid provides a REST interface, allowing for loose coupling between the client and server. Its primary benefit is interoperability with existing HTTP clients, along with providing a connection for php clients. In addition, the need for specific versions of client libraries and bindings is eliminated.

The REST API introduces an overhead, and requires a REST client or custom code to understand and create REST calls. It is recommended to use the Hot Rod client where performance is a concern.

To interact with Red Hat JBoss Data Grid’s REST API only a HTTP client library is required. For Java, this may be the Apache HTTP Commons Client, or the java.net API.

Important

The following examples assume that REST security is disabled on the REST connector. To disable REST security remove the authentication and encryption elements from the connector.

13.2. Ruby Client Code

The following code is an example of interacting with Red Hat JBoss Data Grid REST API using ruby. The provided code does not require any special libraries and standard net/HTTP libraries are sufficient.

Using the REST API with Ruby

require 'net/http'

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

#An example of how to create a new entry

http.post('/rest/MyData/MyKey', 'DATA_HERE', {"Content-Type" => "text/plain"})

#An example of using a GET operation to retrieve the key

puts http.get('/rest/MyData/MyKey').body

#An Example of using a PUT operation to overwrite the key

http.put('/rest/MyData/MyKey', 'MORE DATA', {"Content-Type" => "text/plain"})

#An example of Removing the remote copy of the key

http.delete('/rest/MyData/MyKey')

#An example of creating binary data

http.put('/rest/MyImages/Image.png', File.read('/Users/michaelneale/logo.png'), {"Content-Type" => "image/png"})

13.3. Using JSON with Ruby Example

Prerequisites

To use JavaScript Object Notation (JSON) with ruby to interact with Red Hat JBoss Data Grid’s REST Interface, install the JSON Ruby library (see your platform’s package manager or the Ruby documentation) and declare the requirement using the following code:

require 'json'

Using JSON with Ruby

The following code is an example of how to use JavaScript Object Notation (JSON) in conjunction with Ruby to send specific data, in this case the name and age of an individual, using the PUT function.

data = {:name => "michael", :age => 42 }
http.put('/rest/Users/data/0', data.to_json, {"Content-Type" => "application/json"})

13.4. Python Client Code

The following code is an example of interacting with the Red Hat JBoss Data Grid REST API using Python. The provided code requires only the standard HTTP library.

Using the REST API with Python

import httplib

#How to insert data

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

#How to retrieve data

import httplib
conn = httplib.HTTPConnection("localhost:8080")
conn.request("GET", "/rest/default/0")
response = conn.getresponse()
print response.status
print response.read()

13.5. Java Client Code

The following code is an example of interacting with Red Hat JBoss Data Grid REST API using Java.

Defining Imports

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

Adding a String Value to a Cache

// Using the imports in the previous example
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();
   }

The following code is an example of a method used that reads a value specified in a URL using Java to interact with the Red Hat JBoss Data Grid REST Interface.

Get a String Value from a Cache

    // Continuation of RestExample defined in previous example
		/**
    * Method that gets an 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 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();
   }

Using a Java Main Method

    // Continuation of RestExample defined in previous example
		/**
    * Main method example.
    * @param args
    * @throws IOException
    */
    public static void main(String[] args) throws IOException {
        //Note that the cache name is "cacheX"
        RestExample restExample = new RestExample();
        restExample.putMethod("http://localhost:8080/rest/cacheX/1", "Infinispan REST Test");
        restExample.getMethod("http://localhost:8080/rest/cacheX/1");
    }
}

13.6. Using the REST Interface

13.6.1. REST Interface Operations

In Remote Client-Server mode, Red Hat JBoss Data Grid provides a REST interface that allows clients to:

  • Add data
  • Retrieve data
  • Remove data
  • Query data

13.6.1.1. Data Formats

The REST API exposes caches that store data in a format defined by a configurable media type.

The following XML snippet shows an example configuration that defines the media type for keys and values:

<cache>
   <encoding>
      <key media-type="application/x-java-object; type=java.lang.Integer"/>
      <value media-type="application/xml; charset=UTF-8"/>
   </encoding>
</cache>

For more information, see Configuring Media Types.

13.6.1.2. Headers

Calls to the Red Hat JBoss Data Grid REST API can provide headers that describe:

  • Content written to the cache.
  • Required data format of the content when reading from the cache.

JBoss Data Grid supports the HTTP/1.1 Content-Type and Accept headers applied to values as well as the Key-Content-Type header for keys.

13.6.1.3. Accept Header

The Red Hat JBoss Data Grid REST server complies with the RFC-2616 specification for Accept headers and negotiates the correct media type based on the supported conversions.

For example, a client sends the following header in a call to read data from the cache:

Accept: text/plain;q=0.7, application/json;q=0.8, */*;q=0.6

In this case, JBoss Data Grid gives precedence to JSON format during negotiation because that media type has highest priority (0.8). If the server does not support JSON format, text/plain takes precedence because the media type has the next highest priority (0.7).

In the event that the server does not support either the JSON or text/plain media types, / takes precedence, which indicates any suitable media type based on the cache configuration.

When the negotiation completes, the server continues using the chosen media type for the operation. If any errors occur during the operation, the server does not attempt to use any other media type.

13.6.1.4. Key-Content-Type Header

Most calls to the REST API include the key in the URL. When handling those calls, Red Hat JBoss Data Grid uses the java.lang.String as the content type for keys by default. However, you can use the Key-Content-Type header to specify different content types for keys.

Table 13.1. Key-Content-Type Header Examples

UseAPI CallHeader

Specify a byte[] key as a Base64 string

PUT /my-cache/AQIDBDM=

Key-Content-Type: application/octet-stream

Specify a byte[]` key as a hexadecimal string

GET /my-cache/0x01CA03042F

Key-Content-Type: application/octet-stream; encoding=hex

Specify a double key

POST /my-cache/3.141456

Key-Content-Type: application/x-java-object;type=java.lang.Double

Note

The type parameter for application/x-java-object is restricted to primitive wrapper types and java.lang.String. This parameter is also restricted to bytes, with the result that application/x-java-object;type=Bytes is equivalent to application/octet-stream;encoding=hex.

13.6.2. Adding Data Through the REST API

13.6.2.1. Adding Data to the Cache

Add data to the cache with the following methods:

  • HTTP PUT method
  • HTTP POST method

When you call the REST API with the PUT and POST methods, the body of the request contains the data.

13.6.2.2. PUT /{cacheName}/{cacheKey}

A PUT request from the provided URL form places the payload, from the request body in the targeted cache using the provided key. The targeted cache must exist on the server for this task to successfully complete.

As an example, in the following URL, the value hr is the cache name and payRoll%2F3 is the key. The value %2F indicates that a / character was used in the key.

http://someserver/rest/hr/payRoll%2F3

Any existing data is replaced and Time-To-Live and Last-Modified values are updated, if required.

Note

A cache key that contains the value %2F to represent a / in the key (as in the provided example) can be successfully run if the server is started using the following argument:

-Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true

13.6.2.3. POST /{cacheName}/{cacheKey}

The POST method from the provided URL form places the payload (from the request body) in the targeted cache using the provided key. However, in a POST method, if a value in a cache/key exists, a HTTP CONFLICT status is returned and the content is not updated.

13.6.2.4. Headers for the PUT and POST Methods

Table 13.2. Headers for PUT and POST Methods

HeaderOptional or RequiredDescription

Key-Content-Type

Optional

Specifies the content type for the key in the URL.

Content-Type

Optional

Specifies the media type of the value sent to the REST API.

performAsync

Optional

Specifies a boolean value. If the value is true, it returns immediately and then independently replicates data to the cluster, which is useful when inserting data in bulk or for large clusters.

timeToLiveSeconds

Optional

Specifies the number of seconds before the entry is automatically deleted. Negative values create entries that are never deleted.

maxIdleTimeSeconds

Optional

Specifies the number of seconds that the entry can remain idle before it is deleted. Negative values create entries that are never deleted.

The following combinations can be set for the timeToLiveSeconds and maxIdleTimeSeconds headers:

  • If both the timeToLiveSeconds and maxIdleTimeSeconds headers are assigned the value 0, the cache uses the default timeToLiveSeconds and maxIdleTimeSeconds values configured either using ` XML` or programatically.
  • If only the maxIdleTimeSeconds header value is set to 0, the timeToLiveSeconds value should be passed as the parameter (or the default -1, if the parameter is not present). Additionally, the maxIdleTimeSeconds parameter value defaults to the values configured either using ` XML` or programatically.
  • If only the timeToLiveSeconds header value is set to 0, expiration occurs immediately and the maxIdleTimeSeconds value is set to the value passed as a parameter (or the default -1 if no parameter was supplied).

13.6.3. Retrieving Data Through the REST API

13.6.3.1. Retrieving Data from the Cache

Retrieve data from the cache with the following methods:

  • HTTP GET method
  • HTTP HEAD method

13.6.3.2. GET /{cacheName}/{cacheKey}

The GET method returns the data located in the supplied cacheName, matched to the relevant key, as the body of the response. The Content-Type header provides the type of the data. A browser can directly access the cache.

A unique entity tag (ETag) is returned for each entry along with a Last-Modified header which indicates the state of the data at the requested URL. ETags allow browsers (and other clients) to ask for data only in the case where it has changed (to save on bandwidth). ETag is a part of the HTTP standard and is supported by Red Hat JBoss Data Grid.

The type of content stored is the type returned. As an example, if a String was stored, a String is returned. An object which was stored in a serialized form must be manually deserialized.

Appending the extended parameter to the query returns additional information. For example,

GET /{cacheName}/{cacheKey}?extended

Returns the following custom headers:

  • Cluster-Primary-Owner which identifies the node that is the primary owner of the key.
  • Cluster-Node-Name which specifies the JGroups node name of the server that handled the request.
  • Cluster-Physical-Address which specifies the physical JGroups address of the server that handled the request.

13.6.3.3. HEAD /{cacheName}/{cacheKey}

The HEAD method operates in a manner similar to the GET method, however returns no content (header fields are returned).

Note

The HEAD method also supports the extended parameter to return additional information.

13.6.3.4. GET /{cacheName}

The GET method can return a list of keys that reside in the cache. The list of keys is returned in the body of the response.

The Accept header can format the response as follows:

  • application/xml returns a list of keys in XML format.
  • application/json returns a list of keys in JSON format.
  • text/plain returns a list of keys in plain text with one key per line.

If the cache is distributed then only keys that are owned by the node that handles the request are returned. To return all keys, append the global parameter to the query as follows:

GET /{cacheName}?global

13.6.3.5. Headers for the GET and HEAD Methods

Table 13.3. Headers for GET and HEAD Methods

HeaderOptional or RequiredDescription

Key-Content-Type

Optional

Specifies the content type for the key in the URL. Defaults to application/x-java-object; type=java.lang.String if not specified.

Accept

Optional

Specifies the format in which to return the content for calls with the GET method.

13.6.4. Removing Data Through the REST API

13.6.4.1. Removing Data from the Cache

Remove data from Red Hat JBoss Data Grid with the HTTP DELETE method.

The DELETE method can:

  • Remove a cache entry/value. (DELETE /{cacheName}/{cacheKey})
  • Remove all entries from a cache. (DELETE /{cacheName})

13.6.4.2. DELETE /{cacheName}/{cacheKey}

Used in this context (DELETE /{cacheName}/{cacheKey}), the DELETE method removes the key/value from the cache for the provided key.

13.6.4.3. DELETE /{cacheName}

In this context (DELETE /{cacheName}), the DELETE method removes all entries in the named cache. After a successful DELETE operation, the HTTP status code 200 is returned.

13.6.4.4. Background Delete Operations

Set the value of the performAsync header to true to ensure an immediate return while the removal operation continues in the background.

13.6.5. ETag Based Headers

ETag Based Headers

ETags (Entity Tags) are returned for each REST Interface entry, along with a Last-Modified header that indicates the state of the data at the supplied URL. ETags are used in HTTP operations to request data exclusively in cases where the data has changed to save bandwidth. The following headers support ETags (Entity Tags) based optimistic locking:

Table 13.4. Entity Tag Related Headers

HeaderAlgorithmExampleDescription

If-Match

If-Match = "If-Match" ":" ( "*" | 1#entity-tag )

-

Used in conjunction with a list of associated entity tags to verify that a specified entity (that was previously obtained from a resource) remains current.

If-None-Match

 

-

Used in conjunction with a list of associated entity tags to verify that none of the specified entities (that was previously obtained from a resource) are current. This feature facilitates efficient updates of cached information when required and with minimal transaction overhead.

If-Modified-Since

If-Modified-Since = "If-Modified-Since" ":" HTTP-date

If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT

Compares the requested variant’s last modification time and date with a supplied time and date value. If the requested variant has not been modified since the specified time and date, a 304 (not modified) response is returned without a message-body instead of an entity.

If-Unmodified-Since

If-Unmodified-Since = "If-Unmodified-Since" ":" HTTP-date

If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT

Compares the requested variant’s last modification time and date with a supplied time and date value. If the requested resources has not been modified since the supplied date and time, the specified operation is performed. If the requested resource has been modified since the supplied date and time, the operation is not performed and a 412 (Precondition Failed) response is returned.

13.6.6. Querying Data via the REST Interface

Red Hat JBoss Data Grid lets you query data via the REST interface using Ickle queries in JSON format.

Important

Querying data via the REST interface is a Technology Preview feature in JBoss Data Grid 7.2.

13.6.6.1. JSON to Protostream Conversion

JBoss Data Grid uses protocol buffers to efficiently store data in binary format in the cache while exposing queries and enabling you to read and write content in JSON format.

To store Protobuf encoded entries, the cache must be configured with the application/x-protostream media type. JBoss Data Grid then automatically converts JSON to Protobuf.

If the cache is indexed, you do not need to perform any configuration. By default, an indexed cache stores entries with the application/x-protostream media type.

However, if the cache is not indexed, you must configure keys and values with the application/x-protostream media type, as in the following example:

<cache>
   <encoding>
      <key media-type="application/x-protostream"/>
      <value media-type="application/x-protostream"/>
   </encoding>
</cache>

13.6.6.2. Registering Protobuf Schemas

To register a Protobuf schema, you can use the HTTP POST method to insert the schema in the ___protobuf_metadata cache, as in the following example:

curl -u user:password -X POST --data-binary @./schema.proto http://127.0.0.1:8080/rest/___protobuf_metadata/schema.proto

For more information about Protobuf encoding and registering Protobuf schemas, see Protobuf Encoding.

13.6.6.3. Mapping JSON Documents to Protobuf Messages

The _type field must be included in JSON documents. This field identifies the Protobuf message to which the JSON document corresponds.

For example, the following is a Protobuf message defined as Person:

message Person {
  required string name = 1;
  required int32 age = 2;
}

The corresponding JSON document is as follows:

Person.json

{
  "_type": "Person",
  "name": "user1",
  "age": 32
}

13.6.6.4. Populating the Cache

You can write content to the cache in JSON format as follows:

curl -u user:user -XPOST --data-binary @./Person.json -H "Content-Type: application/json; charset=UTF-8" http://127.0.0.1:8080/rest/{cacheName}/{key}
  • {cacheName} specifies the name of the cache to query.
  • {key} specifies the name of the key that stores the data in the cache.

After you write content to the cache, you can read it in JSON format as follows:

curl -u user:user http://127.0.0.1:8080/rest/{cacheName}/{key}

13.6.6.5. Querying REST Endpoints

Use the HTTP GET method or the POST method to query data via the REST interface.

Query requests with the GET method have the following structure:

{cacheName}?action=search&query={ickle query}
  • {cacheName} specifies the name of the cache to query.
  • {ickle query} specifies the Ickle query to perform.

The following are example queries:

  • Return all data from the entity named Person: http://localhost:8080/rest/mycache?action=search&query=from Person
  • Refine the query with a select clause: http://localhost:8080/rest/mycache?action=search&query=Select name, age from Person
  • Group the results of the query: http://localhost:8080/rest/mycache?action=search&query=from Person group by age

Query requests with the POST method have the following structure:

{cacheName}?action=search

The body of the request specifies the query and any parameters in JSON format.

The following is an example query that returns data from the entity named Entity and filters results using a where clause:

{
 "query":"from Entity where name:\"user1\"",
 "max_results":20,
 "offset":10
}
13.6.6.5.1. Optional Request Parameters

The following optional parameters can apply to query requests:

ParameterDescription

max_results

Limits the results of the query to a maximum number. The default value is 10.

offset

Specifies the index of the first result to return. The default value is 0.

query_mode

Specifies how the server executes the query with the following values:

BROADCAST broadcasts a query to each node in the cluster and then retrieves and combines the results of the query before returning them. This execution mode is suitable for non-shared indexes where each node contains a subset of data in its index.

FETCH executes the query in the node that the query calls. This execution mode is suitable where all of the indexes for data across the cluster are available locally. This is the default value.

13.6.6.5.2. Query Results

Results of Ickle queries are returned in JSON format as in the following example:

{
  "total_results" : 150,
  "hits" : [ {
    "hit" : {
      "name" : "user1",
      "age" : 35
    }
  }, {
    "hit" : {
       "name" : "user2",
       "age" : 42
    }
  }, {
    "hit" : {
       "name" : "user3",
       "age" : 25
    }
  } ]
}
  • total_results is the number of results that the query returned.
  • hits lists all results that match the query.
  • hit contains the fields for each result in the query.

For more information about Ickle queries, see Building a Query using the Ickle Query Language.