Develop a CXF Client

Previous - Next

In the previous step you generated JAXB model classes for the remote service. This page describes how to create a simple CXF client.

Add Maven Dependencies

Add the following property to the properties section of the root pom.xml of your project:

<ehcache.version>2.6.0</ehcache.version>

Add the following dependencies to the  dependencyManagement section of the root pom.xml of your project:

      <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-rs-client</artifactId>
        <version>${cxf.version}</version>
      </dependency>

      <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache-core</artifactId>
        <version>${ehcache.version}</version>
      </dependency>   

(Note that ${cxf.version} is already defined in the hippo-cms7-project top level pom outside your project)

Add the following dependencies to site/pom.xml:

    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-rs-client</artifactId>
    </dependency>

    <dependency>
      <groupId>net.sf.ehcache</groupId>
      <artifactId>ehcache-core</artifactId>
    </dependency>    

Create the Client Class

Create a class GoGreenClient in the the site module of your project.

Make sure the client has getters and setters for configuration purposes.

Use org.apache.cxf.jaxrs.client.WebClient#create to create a web client. The WebClient object is chainable by several functions such as: accept type header, path, query, return type, post actions etc.

site/src/main/java/org/example/rest/client:

package org.example.rest.client;

import javax.ws.rs.core.MediaType;

import org.apache.cxf.jaxrs.client.WebClient;
import org.example.jaxb.Products;

public class GoGreenClient {

    private String baseAddress = "https://www.demo.onehippo.com/";
    private String path = "restapi/topproducts";

    public Products getTopTenProducts() {
        return WebClient.create(baseAddress).path(path).accept(MediaType.APPLICATION_XML).query("sortby", "hippogogreen:rating").query("_type", "xml").get(Products.class);
    }

    public String getBaseAddress() {
        return baseAddress;
    }

    public void setBaseAddress(String baseAddress) {
        this.baseAddress = baseAddress;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
}

Also, add the following basic CXF configuration:

site/src/main/resources/cxf.xml

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sec="http://cxf.apache.org/configuration/security"
  xmlns:http="http://cxf.apache.org/transports/http/configuration"
  xsi:schemaLocation=" http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <http:conduit name="*.http-conduit">
    <http:tlsClientParameters useHttpsURLConnectionDefaultHostnameVerifier="true" />
  </http:conduit>
</beans>

Invoke the Client from an HST Component

For the GoGreenClient to be accessed and invoked from an HST Component, we must add it to the Spring configuration.

In site/src/main/resources/META-INF/hst-assembly/overrides, add a file called gogreen-client.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="org.example.rest.client.GoGreenClient" class="org.example.rest.client.GoGreenClient">
  </bean>

</beans>

Now, in an HST Component, you can access the GoGreenClient by calling:

GoGreenClient goGreenClient = HstServices.getComponentManager().getComponent(GoGreenClient.class);
Products products = goGreenClient.getTopTenProducts();

The products object returned from the client can be stored in a request attribute and rendered on the site.

Set Time-Outs

This is crucial for HST components that use remote service calls. In general, it’s a good practice to set a time-out on such a remote connection call. When this timeout is not set and the connection cannot be established, the site will ‘hang’ for the end-user and this can cause an unresponsive website.
The following method can manipulate the receive and connection time-out of a remote service call:

    private void setTimeouts(final WebClient client, final long connectionTimeout, final long receiveTimeout) {
        HTTPConduit conduit = WebClient.getConfig(client).getHttpConduit();
        if (receiveTimeout != 0) {
            conduit.getClient().setReceiveTimeout(receiveTimeout);
        }
        if (connectionTimeout != 0) {
            conduit.getClient().setConnectionTimeout(connectionTimeout);
        }
    }

Add attributes connectionTimeout and receiveTimeout to the GoGreenClient class:

    private long connectionTimeout = 5000L;
    private long receiveTimeout = 5000L;

And their corresponding getters and setters:

    public long getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(long connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    public long getReceiveTimeout() {
        return receiveTimeout;
    }

    public void setReceiveTimeout(long receiveTimeout) {
        this.receiveTimeout = receiveTimeout;
    }

Modify the getTopTenProducts method:

    public Products getTopTenProducts() {
        WebClient client = WebClient.create(baseAddress).path(path).accept(MediaType.APPLICATION_XML)
                                    .query("_type", "xml");
        setTimeouts(client, connectionTimeout, receiveTimeout);
        return client.get(Products.class);
    }

Add Response Caching

It is important for performance reasons to cache a remote service call response so that the remote service doesn’t experience an unnecessary load for each call.

For caching Hippo recommends using the built-in EhCache.

In the GoGreenClient class, declare static strings for the name of the cache and the cache key you are going to use:

    private static final String GO_GREEN_CACHE_NAME = "GoGreenCache";
    private static final String KEY_TOP_TEN_PRODUCTS = "topTenProducts";

Add a CacheManager attribute:

    // create a singleton CacheManager using defaults
    private CacheManager manager = CacheManager.create();

Add an init method:

    public void init() {
        log.info("(Re-)Initializing GoGreenClient");
        // (re)create cache
        manager.removeCache(GO_GREEN_CACHE_NAME);
        Cache testCache = new Cache(new CacheConfiguration(GO_GREEN_CACHE_NAME, 100));
        manager.addCache(testCache);
    }

Modify the getTopTenProducts method so it uses the cache:

    public Products getTopTenProducts() {
        final Cache testCache = manager.getCache(GO_GREEN_CACHE_NAME);
        // try to retrieve element from cache
        final Element element = testCache.get(KEY_TOP_TEN_PRODUCTS);
        if (element != null) {
            return (Products) element.getObjectValue();
        } else {
            // if cache element does not exist retrieve object and place in cache
            final WebClient client = WebClient.create(baseAddress).path(path).accept(MediaType.APPLICATION_XML)
                    .query("_type", "xml")
                    .query("sortby", "hippogogreen:rating").query("max", 10);
            setTimeouts(client, this.connectionTimeout, this.receiveTimeout);
            final Products products = client.get(Products.class);
            testCache.put(new Element(KEY_TOP_TEN_PRODUCTS, products));
            return products;
        }
    }

Finally, modify the GoGreenClient Spring configuration to call init:

<bean id="org.example.rest.client.GoGreenClient" class="org.example.rest.client.GoGreenClient" 
init-method="init">
</bean>

Working with JSON

Serializing to JSON can be quite troublesome because there isn’t always a one-on-one mapping between XML and JSON.

If you would call the same REST service from the www.demo.onehippo.com and the JSON response:

https://www.demo.onehippo.com/restapi/topproducts?_type=json

You will see that there aren’t any wrapping elements. For JAXB it is necessary to have wrapping elements.

For example to support the following snippet from above URL:

[
    {
        name: "organic-cotton-reusable-lunch-bag",
        localizedName: "Organic Cotton Reusable Lunch Bag",
        ...
    },
    {
]

It needs to be converted to the following snippet for Jackson to serialize this as it would serialize the XML variant:

{" products": [
    {
        "product": {
            "name": "organic-cotton-reusable-lunch-bag",
            "localizedName": "Organic Cotton Reusable Lunch Bag",
            ...
        }
    },
    {
]

It’s highly recommended to address the REST services on wrapping elements instead of fixing this with your own Provider.

If by any chance you need to create your own provider you’ll have to create the WebClient with a list of providers at creation: org.apache.cxf.jaxrs.client.WebClient#create.

There is an option to manipulate the org.apache.cxf.jaxrs.provider.json.JSONProvider to drop/ignore with e.g. org.apache.cxf.jaxrs.provider.AbstractJAXBProvider#setOutDropElements and other options.
There are some examples of manipulating the standard jackson provider in the CXF project in org.apache.cxf.jaxrs.provider.json.JSONProviderTest.

Previous - Next

Full Source Code

For easy reference, the complete source of the GoGreenClient is provided below.

GoGreenClient.java

package org.example.rest.client;

import javax.ws.rs.core.MediaType;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;

import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.transport.http.HTTPConduit;
import org.example.jaxb.Products;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GoGreenClient {

    private static final Logger log = LoggerFactory.getLogger(GoGreenClient.class);

    private static final String GO_GREEN_CACHE_NAME = "GoGreenCache";
    private static final String KEY_TOP_TEN_PRODUCTS = "topTenProducts";

    private String baseAddress = "https://www.demo.onehippo.com/";
    private String path = "restapi/topproducts";
    private long connectionTimeout = 5000L;
    private long receiveTimeout = 5000L;

    // create a singleton CacheManager using defaults
    private CacheManager manager = CacheManager.create();

    public void init() {
        log.info("(Re-)Initializing GoGreenClient");
        // (re)create cache
        manager.removeCache(GO_GREEN_CACHE_NAME);
        Cache testCache = new Cache(new CacheConfiguration(GO_GREEN_CACHE_NAME, 100));
        manager.addCache(testCache);
    }

    public Products getTopTenProducts() {
        final Cache testCache = manager.getCache(GO_GREEN_CACHE_NAME);
        // try to retrieve element from cache
        final Element element = testCache.get(KEY_TOP_TEN_PRODUCTS);
        if (element != null) {
            return (Products) element.getObjectValue();
        } else {
            // if cache element does not exist retrieve object and place in cache
            final WebClient client = WebClient.create(baseAddress).path(path).accept(MediaType.APPLICATION_XML)
                    .query("_type", "xml")
                    .query("sortby", "hippogogreen:rating").query("max", 10);
            setTimeouts(client, this.connectionTimeout, this.receiveTimeout);
            final Products products = client.get(Products.class);
            testCache.put(new Element(KEY_TOP_TEN_PRODUCTS, products));
            return products;
        }
    }

    public String getBaseAddress() {
        return baseAddress;
    }

    public void setBaseAddress(String baseAddress) {
        this.baseAddress = baseAddress;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public long getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(long connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    public long getReceiveTimeout() {
        return receiveTimeout;
    }

    public void setReceiveTimeout(long receiveTimeout) {
        this.receiveTimeout = receiveTimeout;
    }

    private void setTimeouts(final WebClient client, final long connectionTimeout, final long receiveTimeout) {
        HTTPConduit conduit = WebClient.getConfig(client).getHttpConduit();
        if (receiveTimeout != 0) {
            conduit.getClient().setReceiveTimeout(receiveTimeout);
        }
        if (connectionTimeout != 0) {
            conduit.getClient().setConnectionTimeout(connectionTimeout);
        }
    }
}

Previous - Next

Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?