RESTful JAX-RS Component Support
Info: This page assumes that Bloomreach Content is configured in multi-webapp mode. If your project uses single-webapp mode, adjust file paths and configuration settings as needed. See Single vs Multi Webapp Mode for details.
Overview
This page describes how to create custom JAX-RS-based RESTful web services in the delivery tier of Bloomreach Content.
Purpose
Enable custom JAX-RS RESTful web services within the delivery tier.
Service Types
Bloomreach Content supports two types of JAX-RS services:
- Plain JAX-RS services: Expose preconfigured functionality within a site. These services are not directly mapped to content but can access it.
- Context-aware JAX-RS services: Dynamically expose site content based on content type mappings.
Both service types are integrated into the HST and have full access to the HST API.
The Essentials setup application includes a REST Services Setup tool to assist with configuring plain JAX-RS services.
For implementation details, refer to this page and the dedicated guides for plain and context-aware services.
Enabling JAX-RS Services
If you created your project using the Bloomreach Content Maven archetype, JAX-RS support is already enabled. No further steps are required.
To enable JAX-RS support in other projects:
-
Add the following dependency to the
site/componentsmodule'spom.xml:<dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-annotation_1.1_spec</artifactId> <version>1.0.1</version> <!-- Use 'provided' scope if your application container supplies javax.annotation.security --> <scope>compile</scope> </dependency> -
Add a Spring configuration file at
/META-INF/hst-assembly/overrides/custom-jaxrs-resources.xmlin thesite/componentsmodule:<?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-3.0.xsd"> <!-- Import 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" /> <!-- List for custom Plain JAX-RS Resource Providers --> <bean id="customRestPlainResourceProviders" class="org.springframework.beans.factory.config.ListFactoryBean"> <property name="sourceList"> <list> </list> </property> </bean> <!-- List for custom Content/Context-Aware JAX-RS Resource Providers --> <bean id="customRestContentResourceProviders" class="org.springframework.beans.factory.config.ListFactoryBean"> <property name="sourceList"> <list> </list> </property> </bean> </beans>The three imports must be present to enable either the
JaxrsRestPlainPipelineorJaxrsRestContentPipeline.- Add your custom Plain RESTful JAX-RS components to the
sourceListofcustomRestPlainResourceProviders. These are used by theJaxrsRestPlainPipeline. - Add your custom Content/Context-Aware RESTful JAX-RS components to the
sourceListofcustomRestContentResourceProviders. These are used by theJaxrsRestContentPipeline.
Info: Spring bean configuration files in
classpath:/META-INF/hst-assembly/overrides/*.xmlare automatically loaded and merged unless you set theassembly.overridesproperty to an empty string in/WEB-INF/hst-config.properties. - Add your custom Plain RESTful JAX-RS components to the
XML and JSON Payload Handling
The delivery tier uses Apache CXF as the JAX-RS runtime. Apache CXF automatically consumes and produces XML or JSON payloads based on the request, even if the JAX-RS resource beans do not explicitly specify a format.
By default, Apache CXF inspects the "Accept" HTTP request header:
- If the header is "text/xml" or "application/xml", XML is used.
- If the header is "application/json", JSON is used.
Apache CXF also supports a special request parameter, _type. For example:
?_type=xmlor...&_type=xmlforces XML output.?_type=jsonor...&_type=jsonforces JSON output.
Customizing the _type Parameter Name
You can change the default _type parameter name by adding configuration to your Spring assembly XML file:
<!-- Use '_format' as a parameter name instead of the default '_type' for the plain JAX-RS pipeline. Setting 'additionalQueryString' forces all JAX-RS requests to include additional parameters. For example, '_type=json' globally enforces JSON output regardless of the 'Accept' header. --> <bean id="jaxrsRestPlainServiceQueryStringReplacingInterceptor" class="org.hippoecm.hst.jaxrs.cxf.QueryStringReplacingInterceptor"> <property name="paramNameReplaces"> <map> <!-- Replace '_format' with '_type' before JAX-RS processing --> <entry key="_format" value="_type" /> </map> </property> <property name="additionalQueryString"> <value></value> <!-- To force JSON output globally: <value>_type=json</value> --> </property> </bean> <!-- Use '_format' as a parameter name instead of the default '_type' for the Content/Context-Aware JAX-RS pipeline. Setting 'additionalQueryString' forces all JAX-RS requests to include additional parameters. --> <bean id="jaxrsRestContentServiceQueryStringReplacingInterceptor" class="org.hippoecm.hst.jaxrs.cxf.QueryStringReplacingInterceptor"> <property name="paramNameReplaces"> <map> <entry key="_format" value="_type" /> </map> </property> <property name="additionalQueryString"> <value></value> <!-- To force JSON output globally: <value>_type=json</value> --> </property> </bean>
jaxrsRestPlainServiceQueryStringReplacingInterceptorapplies to the Plain JAX-RS Service Pipeline.jaxrsRestContentServiceQueryStringReplacingInterceptorapplies to the Content/Context-Aware JAX-RS Service Pipeline.
Configure the paramNameReplaces property with parameter replacement pairs. In the example above, if a request includes _format, it is replaced with _type before processing. For example, ?_format=json is treated as ?_type=json.
Forcing Default Output Format
To enforce a default output format (JSON or XML) regardless of the "Accept" header, set the additionalQueryString property to _type=json or _type=xml.
By default, additionalQueryString is empty. If you set it to _type=json, the container appends this parameter to every request, overriding the client's "Accept" header. However, if the client explicitly specifies _type, that value takes precedence.
Configuring JAX-RS Extension Providers
JAX-RS supports the javax.ws.rs.ext.Provider interface for extensions such as MessageBodyReader, MessageBodyWriter, ContextResolver, and ExceptionMapper.
For example, you can map a custom Java exception to a specific HTTP error response using an ExceptionMapper:
package org.hippoecm.hst.demo.jaxrs.ext; @Provider public class MyCustomSecurityExceptionMapper implements ExceptionMapper<MyCustomSecurityException> { public Response toResponse(MyCustomSecurityException ex) { return Response.status(Response.Status.FORBIDDEN).build(); } }
Register your provider in a Spring assembly file, for example in classpath:/META-INF/hst-assembly/overrides/custom-jaxrs-resources.xml:
<bean id="customJaxrsRestEntityProviders" class="org.springframework.beans.factory.config.ListFactoryBean"> <property name="sourceList"> <list> <bean class= "org.hippoecm.hst.demo.jaxrs.ext.MyCustomSecurityExceptionMapper" /> </list> </property> </bean>
Your provider is then registered with the JAX-RS runtime (Apache CXF).
Security Annotation Support
HST supports authentication and authorization per site mount or site map item. This allows you to control access at the site or sitemap level.
For finer-grained control, you can secure individual JAX-RS component operations using Java EE security annotations:
javax.annotation.security.DenyAlljavax.annotation.security.PermitAlljavax.annotation.security.RolesAllowed
Example usage:
package org.hippoecm.hst.demo.jaxrs.services; @Path("/demosite:productdocument/") public class ProductContentResource extends AbstractContentResource { @RolesAllowed(value={"everybody"}) @GET @Path("/") public ProductRepresentation getProductResource( @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse) { // Implementation return null; } @RolesAllowed(value={"author", "editor"}) @GET @Path("/body/") public HippoHtmlRepresentation getHippoHtmlRepresentation( @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse) { return super.getHippoHtmlRepresentation(servletRequest, null, null); } }
In this example:
getProductResource()is accessible to users with the "everybody" role.getHippoHtmlRepresentation()is accessible only to users with the "author" or "editor" roles.
@Persistable Annotation Support
HST supports the @Persistable annotation (org.hippoecm.hst.content.annotations.Persistable) for JAX-RS service bean operations. This applies to both context/content-aware and plain JAX-RS service beans.
When you annotate a JAX-RS service operation with @Persistable, all calls to HstRequestContext#getSession() within that method return a JCR session from the writable session pool. This eliminates the need to manually manage separate read and write sessions.
Hint: Ensure the
sitewriteruser has write access before performing RESTful POST operations. See Access rights when you want to use workflow from the HST.
Example:
@Path("/products/") public class ProductPlainResource extends org.hippoecm.hst.jaxrs.services.AbstractResource { @GET @Path("/search/") public List<ProductRepresentation> searchProductResources( @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse, @Context UriInfo uriInfo) { List<ProductRepresentation> products = new ArrayList<ProductRepresentation>(); try { HstRequestContext requestContext = RequestContextProvider.get(); // Uses default session pool (not writable) since not annotated with @Persistable HstQueryManager hstQueryManager = getHstQueryManager(requestContext); HippoBean scope = getMountContentBaseBean(requestContext); HstQuery hstQuery = hstQueryManager.createQuery(scope, ProductBean.class, true); 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); products.add(productRep); } } } catch (Exception e) { throw new WebApplicationException(e, ResponseUtils.buildServerErrorResponse(e)); } return products; } @Persistable @POST public ProductRepresentation createProductResources( @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse, @Context UriInfo uriInfo, ProductRepresentation productRepresentation) { HstRequestContext requestContext = getRequestContext(servletRequest); try { // Uses writable session pool due to @Persistable annotation WorkflowPersistenceManager wpm = (WorkflowPersistenceManager) getPersistenceManager(requestContext); HippoFolderBean contentBaseFolder = getMountContentBaseBean(requestContext); String productFolderPath = contentBaseFolder.getPath() + "/products"; String beanPath = wpm.createAndReturn(productFolderPath, "demosite:productdocument", productRepresentation.getBrand(), true); ProductBean productBean = (ProductBean) wpm.getObject(beanPath); productBean.setBrand(productRepresentation.getBrand()); productBean.setColor(productRepresentation.getColor()); productBean.setType(productRepresentation.getType()); productBean.setPrice(productRepresentation.getPrice()); wpm.update(productBean); wpm.save(); productBean = (ProductBean) wpm.getObject(productBean.getPath()); } catch (ObjectBeanManagerException e) { throw new WebApplicationException(e, ResponseUtils.buildServerErrorResponse(e)); } catch (RepositoryException e) { throw new WebApplicationException(e, ResponseUtils.buildServerErrorResponse(e)); } return productRepresentation; } }
In this example, the createProductResources() method is annotated with @Persistable. All calls to HstRequestContext#getSession() within this method use a writable JCR session. Use @Persistable for operations that need to persist data to the repository.
The ProductBean class used here must implement the CodeBinderInterface to map bean properties to node properties.
@ParametersInfo Annotation Support
Starting with brXM 12.1, HST supports the @ParametersInfo annotation in JAX-RS service components for both plain and context/content-aware services.
To use this feature:
- Annotate the JAX-RS service class with
@ParametersInfo. - Add a
@Context ParametersInfoProviderargument to each method that requires access to parameters.
Example:
@Path("/products/") @ParametersInfo(type=ProductPlainResourceInfo.class) public class ProductPlainResource extends org.hippoecm.hst.jaxrs.services.AbstractResource { @GET @Path("/search/") public List<ProductRepresentation> searchProductResources( @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse, @Context UriInfo uriInfo, @Context ParametersInfoProvider paramsInfoProvider) { // Retrieve the parameters info instance from the ParametersInfoProvider context argument final ProductPlainResourceInfo paramsInfo = paramsInfoProvider.getParametersInfo(); // Access parameters final String scopePath = paramsInfo.getScopePath(); final String tags = paramsInfo.getTags(); final String sortFields = paramsInfo.getSortFields(); final String sortOrder = paramsInfo.getSortOrder(); // Implement query logic based on parameters // ... } }
Retrieve the parameters info type instance using the @Context ParametersInfoProvider argument.
In plain JAX-RS service components, parameters are read from the resolved HST Mount.
For more details on each feature, refer to the linked documentation pages.