HST Spring ComponentManager event publishing - BloomReach Experience - Open Source CMS
06-12-2019

HST Spring ComponentManager event publishing

Bloomreach Experience Manager's delivery framework (HST) provides a generic way to publish and subscribe to application events between modules and components by using the Google Guava EventBus. By using the @Subscribe annotation of Google Guava, you can implement an application event subscriber component. The internal event dispatching in the HST through the event bus is synchronous and all listeners are invoked by the same thread that publishes the event. The event dispatching is also within a single HST webapp only and therefor not suited for cross web application or cluster wide events. For the latter use cases, you should use the Hippo Event Bus.

If you want to subscribe to a specific application event, you have to register your subscriber implementation with the ComponentManager by invoking #registerEventSubscriber(). You are also responsible for unregistering your application event subscribing component by invoking #unregisterEventSubscriber() when your application stops or when event subscription is no longer needed.

HST provides two built-in event objects for HTTP session creation and destruction, which can be subscribed by your component at any time if you want.

Introduction

The HST ComponentManager supports application event publishing, event subscription, and event listener registration.

The relevant methods in the org.hippoecm.hst.core.container.ComponentManager interface are :

package org.hippoecm.hst.core.container;

import java.util.EventObject;

public interface ComponentManager {

    // <SNIP>

    /**
     * Publish the given event to all components which wants to listen to.
     * @param event the event to publish (may be an application-specific or
     * built-in HST event)
     */
    void publishEvent(EventObject event);

    /**
     * Registers event subscriber object to receive events.
     * @param subscriber
     */
    void registerEventSubscriber(Object subscriber);

    /**
     * Unregisters event subscriber object.
     * @param subscriber
     */
    void unregisterEventSubscriber(Object subscriber);

    // <SNIP>

}

As described in the Javadocs above, if one of your application components wants to publish an application event, you should get a ComponentManager instance and invoke the #publishEvent(EventObject) operation with a java.util.EventObject instance. And the subscribing component can subscribe to the specific event by registering a subscriber instance through the #registerEventSubscriber(Object subscriber) operation. You can also unregister the subscriber instance by invoking #unregisterEventSubscriber(Object subscriber).

The application events handling of the HST ComponentManager actually wraps the Google Guava EventBus internally. The HST ComponentManager recognizes any event subscribers with a Google Guava EventBus  @Subscribe annotation . So, the event subscriber must implement methods annotated with the @com.google.common.eventbus.Subscribe annotation. For more detail on how to subscribe to events with Google Guava EventBus, see the "Subscribing For Events" section of the following article:

Also, note that an event object published and subscribed to must be a descandant class of java.util.EventObject. You might want to wrap your own domain event object by java.utill.EventObject to use with the HST ComponentManager.

The Built-in HTTP Session Events and a Subscription Example

There are two built-in event objects which are provided by HST Container: org.hippoecm.hst.container.event.HttpSessionCreatedEvent and org.hippoecm.hst.container.event.HttpSessionDestroyedEvent. The first event object gets published by the HST Container whenever an HTTP session instance is created by the servlet container. The second one gets published by the HST Container whenever an HTTP session instance is destroyed by the servlet container.

Therefore, if you want to subscribe to those HTTP session events, you can create a subscriber component and register the component to the ComponentManager.

Here's an example subscriber component:

Note that the @Subscribe annotation is com.google.common.eventbus.Subscribe and not org.onehippo.cms7.services.eventbus.Subscribe. The latter should be used for the Hippo Event Bus for cross-application and optionally cluster-wide events.
package example.events;

import java.util.List;
import org.hippoecm.hst.container.event.HttpSessionCreatedEvent;
import org.hippoecm.hst.container.event.HttpSessionDestroyedEvent;
import org.hippoecm.hst.core.container.ComponentManager;
import org.hippoecm.hst.core.container.ComponentManagerAware;
import com.google.common.eventbus.Subscribe;

/**
 * This example subscriber simply store the http session ids into an internal
 * list just for demonstration purpose.
 */
public class SessionIdStoringApplicationListener
                           implements ComponentManagerAware {

    private List<String> sessionIdStore;
    private ComponentManager componentManager;

    public SessionIdStoringApplicationListener(List<String> sessionIdStore) {
        if (null == sessionIdStore) {
            throw new IllegalArgumentException("Set non null set.");
        }

        this.sessionIdStore = sessionIdStore;
    }

    @Override
    public void setComponentManager(ComponentManager componentManager) {
        this.componentManager = componentManager;
    }

    public void init() {
        componentManager.registerEventSubscriber(this);
    }

    public void destroy() {
        componentManager.unregisterEventSubscriber(this);
    }

    @Subscribe
    public void onHttpSessionCreatedEvent(HttpSessionCreatedEvent event) {
        sessionIdStore.add(event.getSession().getId());
    }

    @Subscribe
    public void onHttpSessionDestroyedEvent(HttpSessionDestroyedEvent event) {
        sessionIdStore.remove(event.getSession().getId());
    }

}

As a Google Guava EventBus based application event subscriber, it has a @Subscribe annotation for the HttpSessionCreatedEvent event instance on #onHttpSessionCreatedEvent() and another @Subscribe annotation for the HttpSessionDestroyedEvent event instance on #onHttpSessionDestroyedEvent(). So, ComponentManager will invoke one of those method whenever HttpSessionCreated event instance or HttpSessionDestroyed event instance is published through the ComponentManager#publishEvent(EventObject) method from somewhere.

However, please note that you have to register the subscriber component in order to subscribe the events. That is why the above code invokes componentManager.registerEventSubscriber(this) in its init() method.

Also, don't forget that you should unregister the subscriber component in order to clean up the event subscribers in the ComponentManager. See the #destroy() method in the example.

In the example above, the SessionIdStoringApplicationListener component is supposed to be loaded as a spring bean through HST ComponentManager. Here's a spring bean XML definition example for the component:

<?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 class="example.events.SessionIdStoringApplicationListener"
        init-method="init" 
        destroy-method="destroy">
    <constructor-arg name="sessionIdStore">
      <list />
    </constructor-arg>
  </bean>

</beans>

If the above bean is loaded by HST ComponentManager (e.g., by adding the bean definition in a file in classpath:META-INF/hst-assembly/overrides/*.xml, for instance), then the HST ComponentManager will invoke #setComponentManasger(ComponentManager) on any beans which implement org.hippoecm.hst.core.container.ComponentManagerAware.

Also, the Spring ApplicationContext of the ComponentManager will invoke the #init() and #destroy() methods on each lifecycle phase as configured in the init-method and destroy-method attributes of the bean definition, providing an opportunity to register your subscriber component and unregister it.

If you want to register and unregister your subscriber component programmatically, then it is also possible to get the ComponentManager by invoking HstServices.getComponentManager().

Summary

HST provides a generic way to publish and subscribe to application events between modules and components. By using the @Subscribe annotation of Google Guava EventBus, you can implement an application event subscriber component.

If you want to subscribe to a specific application event, you have to register your subscriber implementation to the ComponentManager by invoking #registerEventSubscriber(). You are also responsible for unregistering your application event subscribing component by invoking #unregisterEventSubscriber() when your application stops or when event subscription is no longer needed.

HST provides two built-in event objects for HTTP session creation and destruction, which can be subscribed to by your component at any time if you want as shown above with an example.

Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?