Creating REST endpoints in Hippo 

Gary Law is a freelance IT Developer offering Multimedia development and consultation services in interactive design and full stack development. Over his 12 years in full stack web and infrastructure development he has ammassed expertise in UX and graphic design, application and systems development ( front and back end ), agile processes and feature and bug management systems. When hes not spending time on contracts, he been learning and playing with Objective C, the Android SDK and native mobile development.  He is a keen supporter of the open source community and actively applies open source technologies where appropriate.

He shares his thoughts and experiences on alll things technology on his website  http://www.skindd.co.uk/

I occasionally spend time researching tech or frameworks and I have recently spent some time exploring hippo CMS  for possible use in projects in the future. I would like to say that I am very impressed with this Java based CMS and its flexibility. This article however is not to explain why Im impressed but to explain how to expose a RESTful API for resources created in the CMS.

I will presume the following;

  • You have a Hippo project up and running on localhost.
  • You have some knowledge of Java Maven Projects.
  • You know how to rebuild the project.
  • You know the terminology of Hippo in regards to Documents.

Generating the Beans

When using the CMS UI to create document types the Java beans for these document types are not generated automatically and the beans are needed for the Rest API. This is one part that threw me as I did not know this had to be done for the Document Types to appear in the REST API Setup Tool. 
This is where we use the Beanwriter tool in the essentials application. It is presumed you have come across the essentials application on the initial setup of your hippo CMS project but in case not, when the Hippo project is running it can be accessed via the url;

localhost:8080/essentials

On the left hand side you will see the menu in which there is tools item. In here you will find the Beanwriter. When you have the Beanwriter open you should see a list of your Document Types. Check each of the document types you wish to generate Beans for, so each one you want to create a REST Endpoint for and then click 'Generate HST Content Beans'.

At this point we want to shutdown the Hippo CMS and have a look at the source for the project.

We want to look at the Java source in the site project of the hippo source. In here we will find the generated Beans. The package will depend on what you set as the package path during the set up of the project. So if you package path is;

com.mydomain

The beans will be in;

<HippoSource>/site/src/main/java/com/mydomain/beans

You should see a class for each of the document types you selected in the Beanwriter.

The REST setup.

In this next part we will be using the Rest Service Setup tool that can be found in the essentials application. This is not installed by default so if you haven't you should install it now and rebuild the hippo CMS as instructed. This utility can be found in the library in the essentials application.

Although I know this tool does not complete everything to get your API up and running we are going to use it and I will show you what it modifies / creates in the source, and what it does not complete.

Ok so now we go to the Rest Services Setup tool.

In here the first thing we need to do is set the endpoint name in the field where we see ''. I personally use 'api'. So the url in completion would be;

http://localhost:8080/site/api

Below this you will see where you can select the document types for which you can create RESTful Endpoints.

Note: This part was a gotcha for me as I did not realise you have to have beans generated for them to appear here in this list.

Select the Document types you wish to create endpoints for and then click 'Run setup'.

You will notice the tool is telling you that you need to rebuild for this to take affect so shutdown the Hippo CMS.

We are now going to take a look at the source for the hippo project again to see whats been done, and also to see whats missing.

First we are going to look at the hosts file inside the bootstrap project which can be found at;

<HippoSource>/bootstrap/src/main/resources/hst/hosts.xml

This file contains the mount points for the JAXRS (REST) Services and when we look inside this file we should see a declaration as follows;

    ...
     <sv:node sv:name="api">
      <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>api</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:name="hst:types" sv:type="String" sv:multiple="true">
        <sv:value>rest</sv:value>
      </sv:property>
    </sv:node>
...

You will notice

<sv:node sv:name="api">

and

<sv:property sv:name="hst:alias" sv:type="String">
 <sv:value>api</sv:value>
</sv:property>

and that these declare the 'api' string that we entered as the value for the Endpoint name.

We don't need to do anything to this file I just wanted to show you where the mount point declaration was created.

Next we going to go back to look at the 'site' project Java source and have a look at the Rest Endpoints created. For the sakes of this article I suggest that the Document Type we have just created the RESTful Service for is one of 'Product'.

Again the package where the classes are created depend on the package path set on creation of the Hippo project and again we will presume com.mydomain as we did when we looked at the Beans creation. So in this case if it doesn't exist the Rest Service Setup tool would have created the directory

<HippoSource>/site/src/main/java/com/mydomain/rest

and in here we should see the class

ProductResource.java

and in this class we will see

com.mydomain.rest

...

@Produces({MediaType.APPLICATION_JSON,     MediaType.APPLICATION_XML})
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.APPLICATION_FORM_URLENCODED})
@Path("/product/")
public class ProductResource extends BaseRestResource {

@GET
@Path("/")
public Pageable<Product> index(@Context HttpServletRequest request) {
    return findBeans(new DefaultRestContext(this, request), Product.class);
}

@GET
@Path("/page/{page}")
public Pageable<Product> page(@Context HttpServletRequest request, @PathParam("page") int page) {
    return findBeans(new DefaultRestContext(this, request, page, DefaultRestContext.PAGE_SIZE), Product.class);
}

@GET
@Path("/page/{page}/{pageSize}")
public Pageable<Product> pageForSize(@Context HttpServletRequest request, @PathParam("page") int page, @PathParam("pageSize") int pageSize) {
    return findBeans(new DefaultRestContext(this, request, page, pageSize), Product.class);
}

}

Although quite basic this class provides the endpoints for the full list or paginated lists for all the Documents in the CMS that are of the type Product.

Ok, thats great but know I will show you what the Rest Service Setup tool doesn't do that had me stumped for a while. For this we need to open the file;

<HippoSource>/site/src/main/resources/META-INF/hst-assembly/overrides/spring-plain-rest-api.xml

The CXF JaxRS implementation doesn't seem to automatically pickup the resources and so in this file we need to manually add them. In this file you will see;

<bean id="customRestPlainResourceProviders" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
  <list>
    ...
  </list>
</property>

 

Inside the list element we need to add a declaration for each of our Rest services. So in our case we are going to add a declaration for the ProductResource.java that we have just looked at. We do this by adding a element to the list as follows;

<bean id="customRestPlainResourceProviders" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
  <list>
    ...
    <bean class="org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider">
      <constructor-arg>
        <bean class="com.mydomain.rest.ProductResource"/>
      </constructor-arg>
    </bean>
    ...
  </list>
</property>

If you have more services you need to add a declaration for each of the classes in the rest package. We are now ready to restart the Hippo CMS and test the endpoint. If you do not have any documents in your CMS that are of one of the types we have created Beans and services for please do so.Again presuming one of these types is of type Product we can test using curl with the following command. 

curl -H "Content-type:application/json" -H "Accept:application/json"  http://localhost:8080/site/api/product

I hope this helps anyone get over the gotchas that stumped me.