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.