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.
Since Bloomreach Experience Manager 12.1, API documentation (/swagger.yaml or /swagger.json) in Swagger format can be generated automatically. This allows developers to use the Swagger UI to read the API specifications and try them out easily.
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:
/restservices: jcr:primaryType: hst:mount hst:alias: restservices hst:ismapped: false hst:namedpipeline: JaxrsRestPlainPipeline hst:types: [rest]
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. Generating API Documentation in Swagger Format
Since Bloomreach Experience Manager 12.1, it is possible to generate API documentation in OpenAPI spec format automatically for your custom plain JAX-RS service components.
To generate API documentation in OpenAPI format, add the following bean definition in a Spring bean assembly XML file located at site/components/src/main/resources/META-INF/hst-assembly/overrides/. e.g, swagger-config.xml:
<?xml version="1.0" encoding="UTF-8"?> <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.xsd"> <bean id="hstOpenApiBeanConfig" class="io.swagger.v3.oas.integration.SwaggerConfiguration"> <property name="resourcePackages" value="org.hippoecm.hst.demo.jaxrs.services" /> <property name="cacheTTL" value="0"/> <property name="openAPI"> <bean class="io.swagger.v3.oas.models.OpenAPI"> <property name="info"> <bean class="io.swagger.v3.oas.models.info.Info"> <property name="title" value="REST API Example" /> <property name="version" value="1.0" /> <property name="description" value="Description of REST Services" /> <property name="termsOfService" value="http://www.example.com/terms-of-services.html" /> <property name="license"> <bean class="io.swagger.v3.oas.models.info.License"> <property name="name" value="Apache License, Version 2.0" /> <property name="url" value="https://www.apache.org/licenses/LICENSE-2.0" /> </bean> </property> <property name="contact"> <bean class="io.swagger.v3.oas.models.info.Contact"> <property name="email" value="[email protected]" /> </bean> </property> </bean> </property> </bean> </property> </bean> </beans>
When you rebuild and restart the project, you will be able to read the auto-generated API documentation in Swagger format by the path (either /openapi.yaml or /openapi.json) after the REST API mount path (e.g, "/restservices"). That is, /restservices/openapi.yaml or /restservices/openapi.json, for instance.
If a Swagger UI web application is installed and configured to consume the auto-generated API documentation, developers can read the API specifications and try them out easily.
If you want to add and test a Swagger UI web application module (e.g, http://localhost:8080/api-docs/) locally in your project, see an example web application submodule ("api-docs") and its configurations (in root pom.xml and cargo.run profile) in the TestSuite project.
7. 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.
-
The API documentation (/openapi.yaml or /openapi.json) in Swagger format can be generated automatically. You can let developers use Swagger UI to understand the API specifications and try them out easily.