RESTful API Support - Content Context Aware JAX-RS Services

1. Introduction

HST-2 supports Content/Context Aware JAX-RS Services. So developers can easily develop JAX-RS components for their own contents and make use of full HST-2 URL mappings and link rewriting 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 Content/Context Aware JAX-RS Services, you need to configure mount(s) in the repository configuration to use the JaxrsContentRestPipeline, as well as enable that pipeline in the overriding Spring configurations.

2. How to enable a Content/Context Aware RESTful Mount

To enable a Content/Context Aware RESTful JAX-RS Services, you should configure a mount for that purpose first.

/restapi:
  jcr:primaryType: hst:mount
  hst:alias: restapi
  hst:namedpipeline: JaxrsRestContentPipeline

In the above example we configured a /restapi mount to enable a JAX-RS RESTful services endpoint. The mount is processed via JaxrsRestContentPipeline, which is provided by default by the HST-2 Container for the Content/Context Aware RESTful services.

Assuming you added this restapi Mount directly below the hst:root>(/) Mount, URLs starting with /restapi/ will be treated as RESTful JAX-RS service URLs by the pipeline.

Also, the alias of this mount is set to "restapi", which can be used in creating links later with <hst:link /> tag by setting " mount" attribute to this alias value.

3. How to develop and configure Content/Context Aware JAX-RS Components for your contents

You can implement and use any JAX-RS component with HST-2, but the root URI path must be the primary node type name directory pattern like the following example:

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


@Path("/demosite:productdocument/")
public class ProductContentResource extends AbstractContentResource {

    @GET
    @Path("/")
    public ProductRepresentation getProductResource(
                           @Context HttpServletRequest servletRequest,
                           @Context HttpServletResponse servletResponse) {
        // do nothing for now...
        return null;
    }

}

The JaxrsRestContentPipeline will dynamically construct a JAX-RS resource base path based on the primary node type of the target content node for the request and invoke the JAX-RS engine to 'match' that path to a registered JAX-RS resource.

Furthermore, the invoked JAX-RS component will be provided with access to the HstRequestContext for this request, and thus can access the target content node for the request as well as the original invocation path (different from the JAX-RS invocation path).

For example, when you navigate to http://localhost:8080/site/restapi/products/opel.html, if the content node mapped by the path, /products/opel, is type of " demosite:productdocument", the ProductContentResource#getProductResource method above will be invoked by the JAX-RS engine.

To register the JAX-RS component above, you should add its bean definition to the sourceList of customRestContentResourceProviders 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 !!! -->
  <!-- The following sourceList is not used for Content/Context Aware
       JAX-RS Services, but used for Plain JAX-RS Services. -->
  <bean id="customRestPlainResourceProviders"
        class="org.springframework.beans.factory.config.ListFactoryBean">
    <property name="sourceList">
      <list>
      </list>
    </property>
  </bean>

  <!-- Your custom JAX-RS REST Resource Providers will be added into the
       following list !!! -->
  <bean id="customRestContentResourceProviders"
        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.ProductContentResource" />
          </constructor-arg>
        </bean>
      </list>
    </property>
  </bean>

</beans>

If needed, you can configure and register more custom JAX-RS components.

4. How to use URLs and Links

You can create URLs for the RESTful services with HST-2 API and tag libraries.

For example, in a JSP page, you can create a link to the RESTful services mount as follows:

<script language="javascript">
// the following uri is created for the RESTful services mount based on
// the current content path.
var uri = '<hst:link path=
               "${hstRequest.requestContext.resolvedSiteMapItem.pathInfo}"
                mount="restapi"/>';
</script>

So, you can use the URI generated by the link tag to invoke your JAX-RS component.

In the example above, you will be able to invoke #getProductResource() method with the URI.

What if you have more methods in your JAX-RS component?

For example, suppose your JAX-RS component code looks like this:

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

@Path("/demosite:productdocument/")
public class ProductContentResource extends AbstractContentResource {

    @GET
    @Path("/")
    public ProductRepresentation getProductResource(
                           @Context HttpServletRequest servletRequest,
                           @Context HttpServletResponse servletResponse) {
        // do nothing for now...
        return null;
    }


    @GET
    @Path("/body/")
    public HippoHtmlRepresentation getHippoHtmlRepresentation(
                           @Context HttpServletRequest servletRequest,
                           @Context HttpServletResponse servletResponse) {
        return super.getHippoHtmlRepresentation(servletRequest, null, null);
    }

}

In the previous example with the link tag, we created a link for the #getProductResource() method.

Now, if you want to create a link for the #getHippoHtmlRepresentation() method which has a sub-path, " /body/", you should add " subPath" attribute in the link tag like the following example:

<script language="javascript">
// the following uri is created for the RESTful services mount based on
// the current content path with "/html/" sub-path.
var uri = '<hst:link path=
              "${hstRequest.requestContext.resolvedSiteMapItem.pathInfo}"
               mount="restapi" subPath="body/" />';
</script>

Therefore, you can use multiple methods in your JAX-RS component and you can create different links for each methods.

By default, the URI created in the example above will look like this:

http://localhost:8080/site/restapi/products/opel.html./body/

Please note that the subPath " body/" is separated by " ./" from the main content path URL.

The HST-2 Link Rewriting Component uses " ./" as separator to find out the content path and the subPath by default. That is, in this example, HST-2 Container will use " /products/open.html" to determine the target content path for the request and its corresponding node primary type and " body/" to build the subPath for this RESTful service URL. For the above example, the JAX-RS engine will then be invoked with the dynamically constructed resource path /demosite:productdocument/body/".

For most cases using the " ./" separator for the subPath should be good enough by default, but if you want to change this separator to something else, you can add and redefine the following property in /WEB-INF/hst-config.properties:

container.request.path.suffix.delimiter = ./

5. Using Matrix Parameters

Content/Context Aware JAX-RS Services can also use matrix parameters by using @MatrixParam annotations.

However, you should be careful when using matrix parameters for invoking RESTful URLs. Content/Context Aware JAX-RS Services support matrix parameters only when the matrix parameters are appended to the subPath.

For example, the following RESTful URLs with matrix parameters are valid:

So, you may have corresponding matrix parameter annotations in your method such as @MatrixParam("p1"), @MatrixParam("p2") or @MatrixParam("p3") in the examples above.

However, the following RESTful URLs are not effective.

The first example puts a matrix parameter ("p1") in the page path, not in the subPath. So, the matrix parameter will not be effective for your JAX-RS service. In other words, you cannot access the matrix parameter, " p1", in your JAX-RS service. In the second example, as mentioned above, the matrix parameters (" p1" and " p2") will not be effective for your JAX-RS service; the last matrix parameter (" p3") only will be effective and available for your JAX-RS service.

Also, 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 subPath is " body/content" and you need to use two matrix parameters, " p1" and " p2", in your JAX-RS service, you should use URLs ending like " ./body/content;p1=1;p2=2".
If you spread the matrix parameters into multiple path segments like " ./body;p1=1/content;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 " /body/content" any more.

6. Built-in JAX-RS Components

The following JAX-RS components are provided as out-of-box components for the system node types in the org.hippoecm.hst.jaxrs.services.content package.

Primary node type

Component class

Description

hippo:document

HippoDocumentContentResource

Default document type content resource provider

hippostd:folder

HippoFolderContentResource

Default folder type content resource provider

hippostd:directory

HippoDirectoryContentResource

Default directory type content resource provider

hippostd:fixeddirectory

HippoFixedDirectoryContentResource

Default fixed directory type content resource provider

hippofacnav:facetnavigation

HippoFacetNavigationContentResource

Default facet navigation content resource provider

hippofacnav:facetsavailablenavigation

HippoFacetsAvailableNavigationContentResource

Default facet available navigation content resource provider

hippofacnav:facetsubnavigation

HippoFacetSubNavigationContentResource

Default facet sub-navigation content resource provider

hippogallery:imageset

ImageSetContentResource

Default image content resource provider

Also, you can have your JAX-RS components extend org.hippoecm.hst.jaxrs.services.content.AbstractContentResource or org.hippoecm.hst.jaxrs.services.AbstractResource classes. Those two abstract classes provide many useful operations to retrieve and manage contents. You can refer to those built-in implementations when you implement your custom JAX-RS components.

7. Summary

HST-2 provides JAX-RS component development framework with Content/Context Awareness.

You can add custom JAX-RS components for your content node types with custom node type name as root path annotation.

Also, you can add many other operations with sub-paths which can be linked with " subPath" attribute in the link tag.

Some system content node types are mapped to built-in JAX-RS components by default and those can be referred to as examples as well as some helper abstract classes for your convenience.

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?