This article covers a Bloomreach Experience Manager version 11. There's an updated version available that covers our most recent release.

RESTful API Support - Plain JAX-RS Services

1. Introduction

HST-2 supports Plain JAX-RS Components integration. So developers can easily develop JAX-RS components for their own RESTful APIs and make use of full HST-2 content retrieval/management features in their JAX-RS components.

Hippo provides several mechanisms to expose REST APIs, read this overview to see which best fits your use case.

In order to use plain JAX-RS Services, you need to configure mount(s) in the repository configuration to use the JaxrsPlainRestPipeline, as well as enable that pipeline in the overriding Spring configurations.

2. How to enable a Plain RESTful Mount

To enable a Plain RESTful JAX-RS Services endpoint, you should configure a mount for that purpose first.

For example, you can configure a " restservices" mount like the following:

<sv:node sv:name="restservices">
  <sv:property sv:name="jcr:primaryType" sv:type="Name">
    <sv:value>hst:mount</sv:value>
  </sv:property>
  <sv:property sv:name="hst:alias" sv:type="String">
    <sv:value>restservices</sv:value>
  </sv:property>
  <sv:property sv:name="hst:ismapped" sv:type="Boolean">
    <sv:value>false</sv:value>
  </sv:property>
  <sv:property sv:name="hst:namedpipeline" sv:type="String">
    <sv:value>JaxrsRestPlainPipeline</sv:value>
  </sv:property>
  <sv:property sv:multiple="true" sv:name="hst:types" sv:type="String">
    <sv:value>rest</sv:value>
  </sv:property>
</sv:node>

In the above example we configured a /restservices mount to enable Plain JAX-RS RESTful services endpoint. The mount is processed via the JaxrsRestPlainPipeline which is provided by default by the HST-2 Container for the Plain RESTful services.

3. How to develop and configure Plain JAX-RS Components

Plain JAX-RS Components are just standard based JAX-RS Components. So, you can follow the best practices for building JAX-RS Components.

First, your JAX-RS resource component should be mapped by a path. In the example below, it is mapped to the " /products/" path.

package org.hippoecm.hst.demo.jaxrs.services;

@Path("/products/")
public class ProductPlainResource 
          extends org.hippoecm.hst.jaxrs.services.AbstractResource {

    @GET
    @Path("/{productType}/")
    public List<ProductRepresentation> getProductResources(
            @Context HttpServletRequest servletRequest,
            @Context HttpServletResponse servletResponse,
            @Context UriInfo uriInfo,
            @PathParam("productType") String productType) {

        // do nothing for now...
        return null;

    }

}   

The JaxrsRestPlainPipeline will select a target JAX-RS services endpoint through the configured mount path.

For example, when you navigate to http://localhost:8080/site/restservices/products/bike/, HST-2 Container will resolve the request to the " restservices" mount endpoint and its JaxrsRestPlainPipeline will delegate the discovery and invocation of the ProductPlainResource component to the JAX-RS engine by the remainder of the path, " /products/bike/", which will resolve " bike" as a path parameter named ' productType'. The JAX-RS engine then will invoke #getProductResources() method because the path is best matched with that method.

In order to register the JAX-RS resource component above, you must add the bean definition to the sourceList of customRestPlainResourceProviders like in the following example:

<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-3.0.xsd">

  <!-- The following three imports will include pipeline configurations for
       both JaxrsRestPlainPipeline and JaxrsRestContentPipeline !!! -->
  <import resource="classpath:/org/hippoecm/hst/site/optional/jaxrs/
                    SpringComponentManager-rest-jackson.xml" />
  <import resource="classpath:/org/hippoecm/hst/site/optional/jaxrs/
                    SpringComponentManager-rest-plain-pipeline.xml" />
  <import resource="classpath:/org/hippoecm/hst/site/optional/jaxrs/
                    SpringComponentManager-rest-content-pipeline.xml" />

  <!-- Your custom JAX-RS REST Plain Resource Providers will be added into
       the following list !!! -->
  <bean id="customRestPlainResourceProviders"
        class="org.springframework.beans.factory.config.ListFactoryBean">
    <property name="sourceList">
      <list>
        <!-- Wrap your JAX-RS component by SingletonResourceProvider. -->
        <bean class=
             "org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider">
          <constructor-arg>
            <bean class=
             "org.hippoecm.hst.demo.jaxrs.services.ProductPlainResource" />
          </constructor-arg>
        </bean>
      </list>
    </property>
  </bean>


  <!-- Your custom JAX-RS REST Resource Providers will be added into the
       following list !!! -->
  <!-- The following sourceList is not used for Plain JAX-RS Services,
       but used for Content/Context Aware JAX-RS Services. -->
  <bean id="customRestContentResourceProviders"
        class="org.springframework.beans.factory.config.ListFactoryBean">
    <property name="sourceList">
      <list>
      </list>
    </property>
  </bean>

</beans>

You can configure and register more custom JAX-RS components when necessary.

4. Detailed Example

The above example JAX-RS component can be further implemented like in the following example:

package org.hippoecm.hst.demo.jaxrs.services;
@Path("/products/")
public class ProductPlainResource 
       extends org.hippoecm.hst.jaxrs.services.AbstractResource {

    @GET
    @Path("/{productType}/")
    public List<ProductRepresentation> getProductResources(
            @Context HttpServletRequest servletRequest,
            @Context HttpServletResponse servletResponse,
            @Context UriInfo uriInfo,
            @PathParam("productType") String productType) {

        List<ProductRepresentation> products =
            new ArrayList<ProductRepresentation>();

        try {
            HstRequestContext requestContext = RequestContextProvider.get();
            HstQueryManager hstQueryManager =
                getHstQueryManager(requestContext.getSession(),
                                requestContext);

            String mountContentPath =
                requestContext.getResolvedMount().getMount()
                                                    .getContentPath();
            Node mountContentNode =
                requestContext.getSession().getRootNode()
                  .getNode(PathUtils.normalizePath(mountContentPath));

            HstQuery hstQuery =
                hstQueryManager.createQuery(mountContentNode,
                                            ProductBean.class);
            Filter filter = hstQuery.createFilter();
            filter.addEqualTo("demosite:product", productType);
            hstQuery.setFilter(filter);
            hstQuery.addOrderByDescending("demosite:price");
            hstQuery.setLimit(10);

            HstQueryResult result = hstQuery.execute();
            HippoBeanIterator iterator = result.getHippoBeans();

            while (iterator.hasNext()) {
                ProductBean productBean =
                    (ProductBean) iterator.nextHippoBean();

                if (productBean != null) {
                    ProductRepresentation productRep =
                        new ProductRepresentation().represent(productBean);
                    productRep.addLink(getNodeLink(requestContext,
                                       productBean));
                    productRep.addLink(getSiteLink(requestContext,
                                                   productBean));
                    products.add(productRep);
                }
            }

        } catch (Exception e) {
            throw new WebApplicationException(e);
        }

        return products;
    }
}

In a client-side HTML page or script you now can invoke the resource service URL. Here's a simple HTML link example invoking the resource service:

<p>  Product :
  <a href=
        '<hst:link path="/restservices/products/${document.product}/" />'
     target='_blank'
     title=
        'Click the left link to see all products of the same product
         type in XML generated by a Plain JAX-RS Service.'>
     ${document.product}</a>
</p>

Unlike Content/Context Aware JAX-RS Services, when using Plain JAX-RS Services, you will need to know how the URL should be like. In the example above, it used <hst:link/> tag with predefined URL paths.

5. Using Matrix Parameters

Plain JAX-RS Services can use matrix parameters by using @MatrixParam annotations.

However, please note that the current JAX-RS runtime library, Apache CXF, adopted by HST-2, does not support matrix parameters in multi path segments.

For example, if your RESTful URL is " http://localhost:8080/site/restservices/a/b" and you need to use two matrix parameters, " p1" and " p2", in your JAX-RS service, you should use URLs ending like " http://localhost:8080/site/restservices/a/b;p1=1;p2=2". If you spread the matrix parameters into multiple path segments like " http://localhost:8080/site/restservices/a;p1=1/b;p2=2", then Apache CXF runtime will fail to match your JAX-RS service. In other words, Apache CXF will fail to find your JAX-RS service or operation with " /a/b" any more.

6. Summary

  • HST-2 supports normal standard based JAX-RS Components.

  • You can take advantage of HST Content Beans to retrieve/manage JCR contents in your Plain JAX-RS Components.

  • Unlike with Content/Context Aware JAX-RS Components, you will need to know the URL patterns on each resource service in order to access them.

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?