Repository JAX-RS Service
Info: Bloomreach Content offers multiple ways to expose REST APIs. Review this overview to determine which approach fits your requirements.
Overview
Purpose
The Repository JAX-RS Service enables you to define custom REST endpoints for the content repository.
Context
Bloomreach Content includes a RepositoryJaxrsService that allows dynamic registration and removal of JAX-RS REST Application endpoints. These endpoints are accessible through the RepositoryJaxrsServlet, which is included by default in projects generated with the Maven archetype. Several built-in REST endpoints use this mechanism, and you can add your own endpoints in implementation projects.
Default Configuration
By default, the hippo-repository-jaxrs dependency is included as a runtime-scoped dependency in the CMS/platform web application.
The RepositoryJaxrsServlet is configured in the CMS application's web.xml file under the */ws/*<endpoint address> path:
<servlet> <servlet-name>RepositoryJaxrsServlet</servlet-name> <servlet-class>org.onehippo.repository.jaxrs.RepositoryJaxrsServlet</servlet-class> <load-on-startup>6</load-on-startup> </servlet> ... <servlet-mapping> <servlet-name>RepositoryJaxrsServlet</servlet-name> <url-pattern>/ws/*</url-pattern> </servlet-mapping>
All REST endpoints registered with the RepositoryJaxrsService are secured by default using basic authentication. Users must provide a valid repository (CMS) username and password to access these endpoints.
Creating a Custom Repository REST Endpoint
Maven Module Configuration
To implement a custom repository REST endpoint, create a separate Maven module and add the following dependency with scope provided to its pom.xml:
<dependency> <groupId>org.onehippo.cms7</groupId> <artifactId>hippo-repository-jaxrs</artifactId> <scope>provided</scope> </dependency>
Define and Register a Repository REST Endpoint
The hippo-repository-jaxrs library provides the RepositoryJaxrsEndpoint builder class. Use this class to define and register a JAX-RS Application instance, or one or more JAX-RS resources, as an endpoint with the RepositoryJaxrsService at a specific endpoint address. A common use case is exposing a REST API from a repository managed daemon module.
The following example demonstrates a simple daemon module that registers a "Hello World" REST endpoint:
package com.example.hello; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import org.onehippo.repository.jaxrs.RepositoryJaxrsEndpoint; import org.onehippo.repository.jaxrs.RepositoryJaxrsService; import org.onehippo.repository.modules.DaemonModule; public class HelloModule implements DaemonModule { @Override public void initialize(final Session session) throws RepositoryException { RepositoryJaxrsService.addEndpoint( new RepositoryJaxrsEndpoint("/hello").singleton(new HelloResource())); } @Override public void shutdown() { RepositoryJaxrsService.removeEndpoint("/hello"); } public static class HelloResource { @Path("/") @GET public String sayHello(@QueryParam("name") String name) { return "Hello " + name +"!"; } } }
When this daemon module is deployed with the CMS, it is started by the repository and registers its JAX-RS HelloResource endpoint at /hello. You can access this endpoint at http://localhost:8080/cms/ws/hello?name=world (authentication required). The response will be: Hello world!
Building a RepositoryJaxrsEndpoint
Create a RepositoryJaxrsEndpoint builder instance with the desired root address, for example "/hello". The RepositoryJaxrsService does not allow multiple endpoints at the same address, so ensure that your endpoint address is unique. The address must start with /; the builder will add this prefix automatically if needed.
You can configure the builder with either a JAX-RS Application instance:
RepositoryJaxrsEndpoint endpoint = new RepositoryJaxrsEndpoint("/").app(myJaxrsApplication);
Or with one or more JAX-RS resource, provider, or feature root classes and/or singleton instances:
RepositoryJaxrsEndpoint endpoint = new RepositoryJaxrsEndpoint("/") .rootClass(MyRootClass.class) .rootClass(OtherRootClass.class) .singleton(mySingletonResource) .singleton(otherSingletonResource);
Building an Apache CXF-Specific CXFRepositoryJaxrsEndpoint
The RepositoryJaxrsService uses Apache CXF as its JAX-RS engine. Apache CXF supports extensions such as CXF Interceptors. Use CXFRepositoryJaxrsEndpoint, which extends RepositoryJaxrsEndpoint, to configure CXF-specific interceptors:
RepositoryJaxrsEndpoint endpoint = new CXFRepositoryJaxrsEndpoint("/") .inInterceptor(myInInterceptor) .outInterceptor(myOutInterceptor) .singleton(mySingletonResource);
Customizing or Overriding REST Endpoint Authentication
By default, each REST endpoint is configured for basic authentication against the repository. Authentication (and authorization, if required) can be customized per endpoint when using the CXFRepositoryJaxrsEndpoint builder. Authentication is handled by a custom CXF JAXRSInvoker, which provides pre- and post-processing for request invocations.
The default authentication is implemented by AuthenticatingRepositoryJaxrsInvoker, which enforces repository login before request handling. To use a custom invoker, configure it on the builder:
RepositoryJaxrsEndpoint endpoint = new CXFRepositoryJaxrsEndpoint("/") .invoker(new AuthenticatingRepositoryJaxrsInvoker()) // default .singleton(mySingletonResource);
If you configure a null invoker, the default invoker is used. To disable repository authentication, configure the default CXF JAXRSInvoker:
RepositoryJaxrsEndpoint endpoint = new CXFRepositoryJaxrsEndpoint("/") .invoker(new org.apache.cxf.jaxrs.JAXRSInvoker()) // default CXF JAXRSInvoker .singleton(mySingletonResource);
Configuring REST Endpoint Authorization
In addition to authentication, you can require repository-based authorization for a REST endpoint. The RepositoryJaxrsEndpoint builder provides the authorized(String authorizationNodePath, String authorizationPermission) method. This method checks that the authenticated user has the specified permission on the given repository node path.
The constant RepositoryJaxrsService.HIPPO_REST_PERMISSION is a predefined permission for this purpose:
public static final String HIPPO_REST_PERMISSION = "hippo:rest";
For example, when exposing REST endpoints from a repository daemon module, you can use the daemon module's configuration path for authorization:
package org.example.hello; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import org.onehippo.repository.jaxrs.RepositoryJaxrsEndpoint; import org.onehippo.repository.jaxrs.RepositoryJaxrsService; import org.onehippo.repository.modules.AbstractReconfigurableDaemonModule; import static org.onehippo.repository.jaxrs.RepositoryJaxrsService.HIPPO_REST_PERMISSION; public class HelloModule extends AbstractReconfigurableDaemonModule { private String modulePath; @Override protected void doConfigure(final Node moduleConfig) throws RepositoryException { modulePath = moduleConfig.getParent().getPath(); } @Override public void initialize(final Session session) throws RepositoryException { RepositoryJaxrsService.addEndpoint( new RepositoryJaxrsEndpoint("/hello") .singleton(new HelloResource()) .authorized(modulePath, HIPPO_REST_PERMISSION); } ...
For details on setting up a security domain that grants the HIPPO_REST_PERMISSION on the daemon module path or another path, see Repository Authorization and Permissions. The hippo-repository-jaxrs module provides a predefined restuser role with the HIPPO_REST_PERMISSION, which you can use when configuring security domains for REST endpoint authorization. Authorization is enforced by AuthorizingRepositoryJaxrsInvoker, which extends AuthenticatingRepositoryJaxrsInvoker. If you override the endpoint's JAXRSInvoker with a custom implementation, you are responsible for handling both authentication and authorization.
Repository authorization model notes:
- If you check for a permission on a path that does not exist for any user, the check always succeeds. Always use a path that is guaranteed to exist. For repository daemon modules, use the module's configuration root.
- You can configure a security domain so that users have the
HIPPO_REST_PERMISSIONprivilege but do not have read access to a specific path.