HST Addon Module Support
What is an HST Addon Module?
An HST Addon Module is a child Spring component beans manager which can be packaged, deployed, and loaded from a separate classpath resource such as a JAR file. An HST Addon Module has its own component bean manager, with a separate namespace, which manages any sharable component beans for your applications.
For example, you can package all the shareable component beans into a JAR file and deploy the module package JAR file into an HST site application. Then, you can let your HST components access any sharable component beans defined in your addon module through HST ComponentManager API.
Suppose you implemented and packaged a basic matrix operation API component (bean name: matrixOperator, for instance) in the linear-algebra-1.01.00.jar file and the namespace of your addon module is org.example.myproject.addonmodules.linearalgebra. Then, you can get the MatrixOperator bean instance by invoking:
org.hippoecm.hst.core.container.ComponentManager#getComponent("matrixOperator", "org.example.myproject.addonmodules.linearalgebra")
The first argument, matrixOperator, is the bean name in the internal Spring ApplicationContext, and the second one is the context namespace of the bean.
HST ComponentManager loads all the defined addon module packages from all the classpath resources and initializes separate module instances separated by each namespace. So, for example, your HST components can access the sharable component beans of an addon module instance easily in HST site applications.
Each addon module instance actually contains a Spring ApplicationContext instance to manage component beans. So, when you want to access a component bean by invoking org.hippoecm.hst.core.container.ComponentManager#getComponent(...), the HST ComponentManager actually looks up the target module instance by the context namespace(s) first and looks up the component bean from the addon module instance by the component bean name.
How to Define an HST Module?
To add an HST Addon Module, do the following:
- Create a JAR module project and define the module descriptor (classpath:META-INF/hst-assembly/addon/module.xml).
- Add Spring beans definition XML file(s).
- Add the packaged JAR dependency into your SITE application.
In a JAR module project, define the addon module descriptor file in classpath:META-INF/hst-assembly/addon/module.xml like the following example:
<?xml version="1.0" encoding="UTF-8"?> <module xmlns="http://www.onehippo.org/schema/hst/hst-addon-module_1_0.xsd"> <name>org.example.myproject.addonmodules.linearalgebra</name> <config-locations> <config-location> classpath*:META-INF/hst-assembly/addon/org/example/myproject/addonmodules/linearalgebra/*.xml </config-location> </config-locations> </module>
The addon module definition is straightforward. The root element is module of the specified namespace, and the root element can contain a name element which is the namespace of all the component beans shared by this addon module, and config-locations which can have config-location element(s). Each config-location element can specify the Spring assembly XML file pattern to load all the sharable component beans. You can find the XSD file in the hst-core-x.y.z.jar file. Please see hst-core-x.y.z.jar!META-INF/schema/hst-addon-module_1_0.xsd for more detail.
So, in the example above, the addon module is defined with the name, org.example.myproject.addonmodules.linearalgebra, and all the beans of this module will be loaded from the XML resources pattern:
classpath*:META-INF/hst-assembly/addon/org/example/myproject/addonmodules/linearalgebra/*.xml
For example, if your package jar file is linear-algebra-1.01.00.jar and the jar file contains an XML file, linear-algebra-1.01.00.jar!META-INF/hst-assembly/addon/org/example/myproject/addonmodules/linearalgebra/base.xml, then all the beans defined in the Spring beans assembly XML file will be loaded by the addon module instance automatically when the HST Container starts up. If the bean definition assembly XML file contains the following bean definition, for instance, then you will be able to get the bean by invoking:
org.hippoecm.hst.core.container.ComponentManager#getComponent("matrixOperator", "org.example.myproject.addonmodules.linearalgebra")
The first argument, matrixOperator, is the bean name in the internal Spring ApplicationContext, and the second one is the context namespace of the bean.
<bean id="matrixOperator" class="org.example.myproject.addonmodules.linearalgebra.MatrixOperatorImpl"> </bean>
Finally, by adding a dependency on your module jar in your site application, you can have your HST components be able to access the beans shared by your module.
For example, you can add a dependency in your Maven site application project like the following example:
<dependency> <groupId>org.example.myproject.addonmodules</groupId> <artifactId>linear-algebra</artifactId> <version>1.01.00</version> </dependency>
If you have an addon module dependency like the above example and the addon module has a proper module descriptor (classpath:META-INF/hst-assembly/addon/module.xml), then HST Container will load your addon module automatically when it starts up.
HST Module Having Descendant Modules
In a module descriptor, you can also have child module definitions under a module.
For example, the following module definition (name="org.example.analytics") contains two child module definitions (name="reports" and name="statistics").
<module xmlns="http://www.onehippo.org/schema/hst/hst-addon-module_1_0.xsd"> <name>org.example.analytics</name> <config-locations> <config-location>classpath*:META-INF/hst-assembly/addon/org/example/analytics/*.xml</config-location> </config-locations> <modules> <module> <name>reports</name> <config-locations> <config-location>classpath*:META-INF/hst-assembly/addon/org/example/analytics/reports/*.xml</config-location> </config-locations> </module> <module> <name>statistics</name> <config-locations> <config-location>classpath*:META-INF/hst-assembly/addon/org/example/analytics/statistics/*.xml</config-location> </config-locations> </module> </modules> </module>
With the example above, you will get a module instance (name="org.example.analytics") associated with a Spring ApplicationContext instance. The module instance will have two child module instances, each of which is associated with a separate child Spring ApplicationContext instance.
A component bean defined in a child module can be accessed via ComponentManager as well by providing module context names properly. For example, if you have a bean named analyticsStatisticsGreeting in the statistics child module (classpath*:META-INF/hst-assembly/addon/org/example/analytics/statistics/base.xml), then you can get the bean as follows:
componentManager.getComponent("analyticsStatisticsGreeting", "org.example.analytics", "statistics");
Spring Beans Definitions in Modules Hierarchy
The Spring ApplicationContext of the default HST ComponentManager is actually the top ancestor ApplicationContext of all the module instances.
Also, if an addon module instance contains any descendant addon modules (child modules, child modules of a child module, etc.), then any beans defined in the descendant module instances can get the references of the beans defined in the ancestor module instance or in the HST ComponentManager.
However, a bean defined in the ancestor module cannot get references to the beans defined in its descendant modules.
In the same way, beans defined in the HST Container (e.g., beans in classpath: META-INF/hst-assembly/overrides/*.xml) cannot refer to any beans defined in an addon module.
Configure an Explicit Parent Module
Assume that the Spring bean definitions org.example.myproject.addonmodules.linearalgebra for the addon module above depend on Spring beans configured in another HST Addon Module. By default, the Spring beans in another Spring Addon Module are not accessible, but only the Spring beans of the default HST ComponentManager (the top ancestor ApplicationContext of all the module instances). Spring beans in a descendant of the above described HST Module Having Descendant Modules can access Spring beans in its parent, but this is an explicit parent dependency within the same <module> configuration. This is about the use case that some downstream Maven project needs to access a Spring bean that is configured in an upstream project. Accessing Spring Beans of another HST Addon Module can be done by specifying explicitly the parent HST Addon Module. This can be done for example as follows:
<?xml version="1.0" encoding="UTF-8"?> <module xmlns="http://www.onehippo.org/schema/hst/hst-addon-module_1_0.xsd"> <name>org.example.myproject.addonmodules.linearalgebra</name> <parent>org.example.addonmodules</parent> <config-locations> <config-location> classpath*:META-INF/hst-assembly/addon/org/example/myproject/addonmodules/linearalgebra/*.xml </config-location> </config-locations> </module>
Now, all Spring beans from the HST Addon Module with name org.example.addonmodules are accessible in the linearalgebra addon. Note that since every module has as root application context the default HST ComponentManager, also all Spring beans from the top application context are still available.
Addon Module Examples in TestSuite
TestSuite [1] contains some examples with HST Addon Modules.
"linear-algebra" module [2] is a JAR module sub-project which contains a module descriptor [3], Spring beans assembly XML file [4], and component beans [5].
The component bean defined in the Spring beans assembly XML file implements a shared interface, MatrixOperator [6], and the shared interface of the component bean is accessed from an HST Component [7] as follows:
public class Algebra extends BaseHstComponent { public static final String RANDOM_NUMBERS_MODULE_NAME = "org.hippoecm.hst.demo.addonmodules.randomnumbers"; public static final String LINEAR_ALGEBRA_MODULE_NAME = "org.hippoecm.hst.demo.addonmodules.linearalgebra"; @Override public void doBeforeRender(final HstRequest request, final HstResponse response) throws HstComponentException { RandomGenerator randomGenerator = HstServices.getComponentManager().getComponent("randomGenerator", RANDOM_NUMBERS_MODULE_NAME); MatrixOperator matrixOperator = HstServices.getComponentManager().getComponent("matrixOperator", LINEAR_ALGEBRA_MODULE_NAME); double [][] matrixData = new double[2][2]; for (int i = 0; i < 2; i++) { double [] randomNums = randomGenerator.generate(2); for (int j = 0; j < 2; j++) { matrixData[i][j] = randomNums[j]; } } double [][] inverseMatrixData = matrixOperator.inverse(matrixData); request.setAttribute("matrix", ArrayUtils.toString(matrixData)); request.setAttribute("inverse", ArrayUtils.toString(inverseMatrixData)); double [][] multiplied = matrixOperator.multiply(matrixData, inverseMatrixData); request.setAttribute("multiplied", ArrayUtils.toString(multiplied)); } }
As you may see above, the bean implementing MatrixOperator is accessed through ComponentManager#getComponent(String, String ... contextNames) method, and the Algebra HST Component generates matrix data and does some basic matrix operations by the component bean.
This example is quite simple, but it is probably good enough for understanding what/how you can do with HST Addon Modules for you HST site applications.
References
- [1] https://github.com/bloomreach/brxm/tree/brxm-14.7.3/testsuite
- [2] https://github.com/bloomreach/brxm/tree/brxm-14.7.3/testsuite/linear-algebra
- [3] https://github.com/bloomreach/brxm/blob/brxm-14.7.3/testsuite/linear-algebra/src/main/resources/META-INF/hst-assembly/addon/module.xml
- [4] https://github.com/bloomreach/brxm/blob/brxm-14.7.3/testsuite/linear-algebra/src/main/resources/META-INF/hst-assembly/addon/org/hippoecm/hst/demo/addonmodules/linearalgebra/base.xml
- [5] https://github.com/bloomreach/brxm/blob/brxm-14.7.3/testsuite/linear-algebra/src/main/java/org/hippoecm/hst/demo/addonmodules/linearalgebra/MatrixOperatorImpl.java
- [6] https://github.com/bloomreach/brxm/blob/brxm-14.7.3/testsuite/api/src/main/java/org/hippoecm/hst/demo/addonmodules/api/MatrixOperator.java
- [7] https://github.com/bloomreach/brxm/blob/brxm-14.7.3/testsuite/components/src/main/java/org/hippoecm/hst/demo/components/Algebra.java