24 Building Your First Coherence REST Application

This chapter demonstrates basic tasks that are required to build and run Coherence REST applications. Many of the concepts demonstrated in this chapter are detailed in subsequent chapters.

This chapter includes the following sections:

24.1 Overview of the Coherence REST Example

This chapter is organized into a set of steps that are used to configure and run a basic Coherence REST application. The steps demonstrate fundamental concepts, such as: configuring a proxy server responsible for handling HTTP request, configuring a remote cache, and using the Coherence REST API. The example in this chapter uses an embedded HTTP server in order to deploy a standalone application that does not require an application server. For details about deployment options with application servers, such as WebLogic and GlassFish, see Chapter 26, "Deploying Coherence REST."

Coherence for Java must be installed to complete the steps in this chapter. In addition, the following user-defined variables are used in this example:

  • DEV_ROOT - The path to root folder where user is performing all of the listed steps, or in other words all of the following folders are relative to DEV_ROOT.

  • COHERENCE_HOME - The path to folder containing Coherence JARs (coherence.jar and coherence-rest.jar)

24.2 Step 1: Configure the Cluster Side

Coherence REST requires both a cache and a proxy scheme. The proxy scheme must define an HTTP acceptor to handle an incoming HTTP request. The cache and proxy are configured in the cluster-side cache configuration deployment descriptor. For this example, the proxy is configured to accept client HTTP requests on localhost and port 8080. A distributed cache named dist-http-example is defined and is used to store client data in the cluster.

To configure the cluster side:

  1. Create an XML file named example-server-config.xml in the DEV_ROOT\config folder.

  2. Copy the following XML to the file:

    <?xml version="1.0"?>
    <cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
       xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config
       coherence-cache-config.xsd">
       <caching-scheme-mapping>
          <cache-mapping>
             <cache-name>dist-http-example</cache-name>
             <scheme-name>dist-http</scheme-name>
          </cache-mapping>
       </caching-scheme-mapping>
    
       <caching-schemes>
          <distributed-scheme>
             <scheme-name>dist-http</scheme-name>
             <backing-map-scheme>
                <local-scheme/>
             </backing-map-scheme>
             <autostart>true</autostart>
          </distributed-scheme>
    
          <proxy-scheme>
             <service-name>ExtendHttpProxyService</service-name>
             <acceptor-config>
                <http-acceptor>
                   <local-address>
                      <address>localhost</address>
                      <port>8080</port>
                   </local-address>
                </http-acceptor>
             </acceptor-config>
             <autostart>true</autostart>
          </proxy-scheme>
       </caching-schemes>
    </cache-config>
    
  3. Save and close the file.

24.3 Step 2: Create a User Type

Create the Person user type, which is stored in the cache and used to demonstrate basic REST operations.

To create the Person object:

  1. Create a text file in a DEV_ROOT\example folder.

  2. Copy the following Java code to the file:

    package example;
    import java.io.Serializable;
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement(name="person")
    @XmlAccessorType(XmlAccessType.PROPERTY)
    public class Person implements Serializable {
    
        public Person() {}
    
        public Person(String name, int age)
            {
            m_name = name;
            m_age  = age;
            }
    
        public String getName() { return m_name; }
    
        public void setName(String name) { m_name = name; }
    
        public int getAge() { return m_age; }
    
        public void setAge(int age) {  m_age = age; }
    
        protected String m_name;
        protected int    m_age;
    }
    
  3. Save the file as Person.java and close the file.

  4. Compile Person.java:

    javac example\Person.java
    

24.4 Step 3: Configure REST Services

The Coherence REST services require metadata about the cache that it exposes. The metadata includes the cache entry's key and value types as well as key converters and value marshallers. The key and value types are required in order for Coherence to be able to use built-in converters and marshallers (XML and JSON supported).

To configure the REST services:

  1. Create an XML file named coherence-rest-config.xml in DEV_ROOT\config folder.

  2. Copy the following XML to the file:

    <?xml version="1.0"?>
    <rest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns="http://xmlns.oracle.com/coherence/coherence-rest-config"
          xsi:schemaLocation=
             "http://xmlns.oracle.com/coherence/coherence-rest-config
          coherence-rest-config.xsd">
      <resources>
        <resource>
          <cache-name>dist-http-example</cache-name>
          <key-class>java.lang.String</key-class>
          <value-class>example.Person</value-class>
        </resource>
      </resources>
    </rest>
    
  3. Save and close the file

24.5 Step 4: Start the Cache Sever Process

REST services are exposed as part of a cache server process (DefaultCacheServer). The cache server's classpath must be configured to find all the configuration files that were created in the previous steps as well as the Person.class. The classpath must also contain the required dependency libraries (see "Dependencies for Coherence REST"). For the sake of brevity, all of the above files are placed in DEV_ROOT\libs folder.

The DEV_ROOT folder should appear as follows:

\
\config
\config\example-server-config.xml
\config\coherence-rest-config.xml
\example
\example\Person.class
\libs
\libs\jersey-server-1.18.jar
\libs\jersey-servlet-1.18.jar
\libs\jersey-core-1.18.jar
\libs\jersey-json-1.18.jar
\libs\jersey-multipart-1.18.jar
\libs\jersey-grizzly2-1.12.jar
\libs\jackson-core-asl-1.9.2.jar
\libs\jackson-jaxrs-1.9.2.jar
\libs\jackson-mapper-asl-1.9.2.jar
\libs\jackson-xc-1.9.2.jar

The following command line starts a cache server process and explicitly names the cache configuration file created in Step 1 by using the tangosol.coherence.cacheconfig system property. In addition it sets all the needed libraries and configuration files to classpath variable:

java -cp DEV_ROOT\config;DEV_ROOT;DEV_ROOT\libs\jersey-server-1.18.jar;
DEV_ROOT\libs\jersey-servlet-1.18.jar;DEV_ROOT\libs\jersey-core-1.18.jar;
DEV_ROOT\libs\jersey-json-1.18.jar;DEV_ROOT\libs\jersey-multipart-1.18.jar;
DEV_ROOT\libs\jersey-grizzly2-1.12.jar;DEV_ROOT\libs\jackson-core-asl-1.9.2.jar;
DEV_ROOT\libs\jackson-jaxrs-1.9.2.jar;DEV_ROOT\libs\jackson-mapper-asl-1.9.2.jar;
DEV_ROOT\libs\jackson-xc-1.9.2.jar;COHERENCE_HOME\coherence.jar;
COHERENCE_HOME\coherence-rest.jar -Dtangosol.coherence.clusterport=8080 
-Dtangosol.coherence.ttl=0 
-Dtangosol.coherence.cacheconfig=DEV_ROOT\config\example-server-config.xml 
com.tangosol.net.DefaultCacheServer

An example script for UNIX-based system follows:

#!/bin/bash

export CLASSPATH=${DEV_ROOT}/config:${DEV_ROOT}:
${DEVROOT}/lib/jackson-core-asl-1.9.2.jar:${DEVROOT}/lib/jackson-jaxrs-1.9.2.jar:
${DEVROOT}/lib/jackson-mapper-asl-1.9.2.jar:${DEVROOT}/lib/jackson-xc-1.9.2.jar:
${DEV_ROOT}/lib/jersey-core-1.18.jar:${DEV_ROOT}/lib/jersey-json-1.18.jar:
${DEV_ROOT}/lib/jersey-multipart-1.18.jar:${DEV_ROOT}/lib/jersey-server-1.18.jar:
${DEV_ROOT}/lib/jersey-servlet-1.18.jar:${DEV_ROOT}/lib/jersey-grizzly2-1.12.jar:
${COHERENCE_HOME}/lib/coherence.jar:${COHERENCE_HOME}/lib/coherence-rest.jar

java -cp ${CLASSPATH} -Dtangosol.coherence.clusterport=8080 
-Dtangosol.coherence.ttl=0 -Dtangosol.coherence.cacheconfig=
${DEV_ROOT}/config/example-server-config.xml com.tangosol.net.DefaultCacheServer

Check the console output to verify that the proxy service has started. The output message should include the following:

(thread=Proxy:ExtendHttpProxyService:HttpAcceptor, member=1): Started: HttpAcceptor{Name=Proxy:ExtendHttpProxyService:HttpAcceptor, State=(SERVICE_STARTED), HttpServer=com.tangosol.coherence.rest.server.DefaultHttpServer, LocalAddress=localhost, LocalPort=8080, ResourceConfig=com.tangosol.coherence.rest.server.DefaultResourceConfig, RootResource=com.tangosol.coherence.rest.DefaultRootResource}

24.6 Step 5: Access REST Services From a Client

Client applications use Coherence REST services to perform cache operations. There are many application platforms that provide client libraries to build HTTP-based clients. For example, the Jersey project provides Java support for client-side communication with HTTP-based REST Web services. The following sections demonstrate the semantics for PUT, GET, and Post operations that a client would use to access the dist-http-example cache. An example Java client built using Jersey follows and requires the Jersey-client-1.18.jar library. See Chapter 25, "Performing Grid Operations with REST," for complete details on the Coherence REST API.

Put Operations

PUT http://localhost:8080/dist-http-example/1 
Content-Type=application/json 
Request Body: {"name":"chris","age":30}
PUT http://localhost:8080/dist-http-example/2 
Content-Type=application/json
Request Body: {"name":"adam","age":26}

GET Operations

GET http://localhost:8080/dist-http-example/1.json

GET http://localhost:8080/dist-http-example/1.xml

GET http://localhost:8080/dist-http-example?q=name is 'adam'

GET http://localhost:8080/dist-http-example;p=name

GET http://localhost:8080/dist-http-example/count()

GET http://localhost:8080/dist-http-example/double-average(age)

Post Operation

POST http://localhost:8080/dist-http-example/increment(age,1)

Sample Jersey REST Client

package example;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import javax.ws.rs.core.MediaType;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

public class RestExample {
   public static void PUT(String url, MediaType mediaType,String data) {
      process(url,"put",mediaType,data);
   }
   public static void GET(String url, MediaType mediaType) {
      process(url,"get",mediaType,null);
   }
   public static void POST(String url, MediaType mediaType,String data) {
      process(url,"post",mediaType,data);
   }
   public static void DELETE(String url, MediaType mediaType) {
      process(url,"delete",mediaType,null);
   }

   public static void process(String sUrl, String action, MediaType mediaType,
      String data) {
      Client client = Client.create();
      ClientResponse response = null;
      WebResource webResource = client.resource(sUrl);      if (action.equalsIgnoreCase("get")) {
         response = webResource.type(mediaType).get(ClientResponse.class);
      } else if (action.equalsIgnoreCase("post")) {
         response = webResource.type(mediaType).post(ClientResponse.class, data);
      } else if (action.equalsIgnoreCase("put")) {
         response = webResource.type(mediaType).put(ClientResponse.class, data);
      } else if (action.equalsIgnoreCase("delete")) {
         response = webResource.type(mediaType).delete(ClientResponse.class,data);
      }
      int status = response.getStatus();
      System.out.println("status:" + status + "Result: "
         + response.getEntity(String.class));
   }

   public static void main(String[] args) throws URISyntaxException,
      MalformedURLException, IOException {
      PUT("http://localhost:8080/dist-http-example/1",
         MediaType.APPLICATION_JSON_TYPE,"{\"name\":\"chris\",\"age\":32}");
      PUT("http://localhost:8080/dist-http-example/2",
         MediaType.APPLICATION_JSON_TYPE,
         "{\"name\":\"\ufeff\u30b8\u30e7\u30f3A\",\"age\":66}");
      PUT("http://localhost:8080/dist-http-example/3",
         MediaType.APPLICATION_JSON_TYPE,"{\"name\":\"adm\",\"age\":88}");
      POST("http://localhost:8080/dist-http-example/increment(age,1)",
         MediaType.APPLICATION_JSON_TYPE,null);
      GET("http://localhost:8080/dist-http-example/3",
         MediaType.APPLICATION_JSON_TYPE);
      GET("http://localhost:8080/dist-http-example/count()",
         MediaType.APPLICATION_XML_TYPE);
   }
}