Repository-Managed Components
Overview
Bloomreach Content supports repository-managed components known as daemon modules. These are singleton components whose lifecycle is managed by the repository. Daemon modules can either expose services to other parts of the system through the Hippo Service Registry or provide self-contained functionality. For example, the Repository Scheduler exposes a service, while the event log cleanup module operates independently.
Implementing a Daemon Module
To create a repository-managed component, implement the DaemonModule interface. This interface defines two lifecycle methods that the repository calls during startup and shutdown:
/** * Called when the component is started. * * @param session a {@link Session} available for the module's lifetime * @throws RepositoryException */ public void initialize(Session session) throws RepositoryException; /** * Called by the repository before shutdown. */ public void shutdown();
If your module requires configuration, implement the ConfigurableDaemonModule interface. This interface provides a method to receive configuration from a JCR node:
/** * Allows a {@link DaemonModule} to configure itself. * Called on startup if a module config node exists, before {@link #initialize}. * * @param moduleConfig the configuration node for this module * @throws javax.jcr.RepositoryException */ void configure(Node moduleConfig) throws RepositoryException;
Configuring a Daemon Module
To register a daemon module, add a hipposys:module node under /hippo:configuration/hippo:modules. Set the hipposys:className property to the fully qualified class name of your implementation. You can add a child node named hippo:moduleconfig to store configuration properties for your module. The repository passes this node to your module during the configuration phase.
/hippo:configuration/hippo:modules: /example-module: jcr:primaryType: hipposys:module hipposys:className: org.example.modules.ExampleModule /hippo:moduleconfig: jcr:primaryType: hipposys:moduleconfig exampleConfigurationOption: exampleConfigurationOptionValue
You can choose any node type for the configuration node. The repository provides this node to your module if it implements ConfigurableDaemonModule.
Registering Services
Some daemon modules expose services to other parts of the system. To do this, use the HippoServiceRegistry to register and unregister your service. The following example demonstrates a module that registers a service:
package org.example.modules; import javax.jcr.RepositoryException; import javax.jcr.Session; import org.onehippo.cms7.services.HippoServiceRegistry; /** * Implements and exposes an example service over the service registry. */ @ProvidesService(types = ExampleService.class) public class ExampleModule implements DaemonModule { private ExampleService service; @Override public void initialize(final Session session) throws RepositoryException { HippoServiceRegistry.register(service = new ExampleService() { @Override public void execute() { doExecute(); } }, ExampleService.class); } private void doExecute() { // implementation } @Override public void shutdown() { HippoServiceRegistry.unregister(service, ExampleService.class); } } interface ExampleService { void doExecute(); }
Managing Dependencies Between Modules
The @ProvidesService annotation indicates which services a module provides. A module can provide multiple services by listing them in the annotation. To declare dependencies on services from other modules, use the @RequiresService annotation. The system uses these annotations to resolve dependencies and control the startup and shutdown order of modules.
The @RequiresService annotation includes a types attribute for the required service classes. It also has an optional optional attribute, which is a boolean array matching the length of types. If a required service is not available and optional is set to false, the module will not load.
Handling Reconfiguration
You can change a module's configuration at runtime. The module determines how to handle configuration changes. One approach is to read configuration properties each time they are needed, avoiding caching. Alternatively, extend AbstractReconfigurableDaemonModule to receive notifications when the configuration changes. The doConfigure method is called both at startup and when the configuration updates, so handle both cases in your implementation.
package org.onehippo.repository.modules; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; import org.onehippo.cms7.services.HippoServiceRegistry; /** * Implements and exposes an example service over the service registry. */ @ProvidesService(types = ExampleService.class) public class ExampleReconfigurableModule extends AbstractReconfigurableDaemonModule { private final Object configurationLock = new Object(); private ExampleService service; private String exampleConfigurationOption; @Override protected void doConfigure(final Node moduleConfig) throws RepositoryException { synchronized (configurationLock) { exampleConfigurationOption = moduleConfig.getProperty("exampleConfigurationOption").getString(); } } @Override protected void doInitialize(final Session session) throws RepositoryException { HippoServiceRegistry.registerService(service = new ExampleService() { @Override public void execute() { doExecute(); } }, ExampleService.class); } private void doExecute() { synchronized (configurationLock) { // do it } } @Override protected void doShutdown() { HippoServiceRegistry.unregisterService(service, ExampleService.class); } } interface ExampleService { void doExecute(); }
When implementing reconfiguration, ensure that any services registered by your module remain functional after configuration changes. Dependent modules may hold references to your service instance, so the instance must remain valid. The example above synchronizes access to the service and its configuration to maintain consistency during reconfiguration.
Daemon Modules for CMS Webapp Only
If your daemon module registers a service that depends on classes available only in the CMS web application (for example, when deploying CMS and site separately), configure the module to run only when the CMS webapp is present. Add the following property to your module registration:
hipposys:cmsonly: true
For example, if ExampleModule depends on CMS-specific code, register it as follows:
/hippo:configuration/hippo:modules: /example-module: jcr:primaryType: hipposys:module hipposys:className: org.example.modules.ExampleModule hipposys:cmsonly: true /hippo:moduleconfig: jcr:primaryType: hipposys:moduleconfig exampleConfigurationOption: exampleConfigurationOptionValue
This configuration ensures that the module runs only when the CMS web application is available.