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.
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.