This article covers a Bloomreach Experience Manager version 11. There's an updated version available that covers our most recent release.

Respond to Workflow Events

Introduction

Goal

Implement additional logic that gets triggered when a certain publication workflow step is performed.

Background

Documents in Hippo CMS move through a publication workflow. An implementation project may require additional processing whenever a certain step in the workflow is performed. For example, a notification email may need to be sent when an author requests publication of a document. This is possible by subscribing to workflow events that are posted on the Hippo Event Bus anytime a workflow step occurs.

This page provides a detailed example showing how to subscribe and respond to workflow events.

See Hippo Event Bus for a general description of the event bus, types of events, and mechanisms for listening for and responding to events.

HippoWorkflowEvent Properties

Workflow events are posted to the event bus as org.onehippo.repository.events.HippoWorkflowEvent objects.

HippoWorkflowEvent object provides some useful information about itself through the following methods:

subjectId The UUID of the content item that is the subject of the event. In the case of a document, the subject is the handle of the document.
subjectPath The repository path at which the content item that is the subject of the event is stored.
interaction

The user interaction that caused the event. A single interaction can cause multiple workflow actions (see action). Each action will cause an event, hence there can be multiple events for the same interaction.

action The workflow action that caused the event. An action is part of an interaction (see interaction).
user The user that initiated the interaction.
documentType The document type of the subject of the interaction.
success Whether the interaction was successful.

For more information see org.onehippo.repository.events.HippoWorkflowEvent in the Hippo Commons API documentation.

Example

The example described here implements an event listener in a repository-managed component, that is packaged with the cms application. It subscribes to workflow events posted to the event bus, and in the case of a publication event, retrieves details about the document that was published and outputs a message "[User] published [document title]".

Note that this example implements a local, non-clustered event listener. It will only receive and respond to workflow operations performed on the same cluster node. In most cases, this is actually desirable because event bus and listener instances will run on each node, so all events are handled locally. It is, however, possible to implement a cluster-wide event listener, as explained in Hippo Event Bus.

Create a Repository-Managed Component

In the cms module of your Hippo project, add a new Java class org.example.PublicationPostProcessingModule which implements the org.onehippo.repository.modules.DaemonModule interface:

cms/src/main/java/org/example/PublicationPostProcessingModule.java

​package org.example;

import org.onehippo.repository.modules.DaemonModule;

public class PublicationPostProcessingModule implements DaemonModule {

    @Override
    public void initialize(final Session session) throws RepositoryException {

    }

    @Override
    public void shutdown() {

    }

}

This is the repository-managed component. You will implement the listener directly in the component class. This is convenient because the repository-managed component has access to the JCR session, which is needed to retrieve information about the published document.

Implement an Event Listener

Implement the basic listener pattern as describe in Hippo Event Bus:

  • Subscribe to workflow events by adding a method handleEvent(final HippoWorkflowEvent event) and annotating it with @Subscribe.
  • In the initialize method, register the listener at the event bus.
  • In the shutdown method, unregister the listener.

cms/src/main/java/org/example/PublicationPostProcessingModule.java

​package org.example;

import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.onehippo.cms7.services.HippoServiceRegistry;
import org.onehippo.cms7.services.eventbus.HippoEventBus;
import org.onehippo.cms7.services.eventbus.Subscribe;
import org.onehippo.repository.events.HippoWorkflowEvent;
import org.onehippo.repository.modules.DaemonModule;

public class PublicationPostProcessingModule implements DaemonModule {

    public static final String PUBLICATION_INTERACTION = "default:handle:publish";

    @Override
    public void initialize(final Session session) throws RepositoryException {
        HippoServiceRegistry.registerService(this, HippoEventBus.class);
    }

    @Override
    public void shutdown() {
        HippoServiceRegistry.unregisterService(this, HippoEventBus.class);
    }

    @Subscribe
    public void handleEvent(final HippoWorkflowEvent event) {
        if (event.success() && PUBLICATION_INTERACTION.equals(event.interaction())) {
            // post-processing to be implemented here
        }
    }

}

Note that the listener only subscribes to workflow events, rather than all events, by specifying a HippoWorkflowEvent object as an argument to the subscribed handleEvent method.

Also, note the check for the relevant interaction (default:handle:publish) causing the event, as well as the check whether the interaction was successful. The latter is necessary because an event will be raised even for unsuccessful workflow operations.

Implement Publication Post-Processing

Implement post-processing for publication events:

  • In initialize, store the JCR session in a member variable for later access.
  • Using the JCR session, retrieve the document handle which is the subject of the publication event.
  • Using the document handle, retrieve the published document variant.
  • Output a message stating the user who performed the publication and the title of the published document variant.

cms/src/main/java/org/example/PublicationPostProcessingModule.java

package org.example;

import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.hippoecm.repository.HippoStdNodeType;
import org.hippoecm.repository.api.HippoNode;
import org.hippoecm.repository.util.JcrUtils;
import org.hippoecm.repository.util.NodeIterable;
import org.onehippo.cms7.services.HippoServiceRegistry;
import org.onehippo.cms7.services.eventbus.HippoEventBus;
import org.onehippo.cms7.services.eventbus.Subscribe;
import org.onehippo.repository.events.HippoWorkflowEvent;
import org.onehippo.repository.modules.DaemonModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PublicationPostProcessingModule implements DaemonModule {

    private static final Logger log = LoggerFactory.getLogger(PublicationPostProcessingModule.class);

    public static final String PUBLICATION_INTERACTION = "default:handle:publish";
    public static final String TITLE_PROPERTY = "myhippoproject:title";

    private Session session;

    @Override
    public void initialize(final Session session) throws RepositoryException {
        this.session = session;
        HippoServiceRegistry.registerService(this, HippoEventBus.class);
    }

    @Override
    public void shutdown() {
        HippoServiceRegistry.unregisterService(this, HippoEventBus.class);
    }

    
    @Subscribe
    public void handleEvent(final HippoWorkflowEvent event) {
        if (event.success() && PUBLICATION_INTERACTION.equals(event.interaction())) {
            postPublish(event);
        }
    }

    private void postPublish(final HippoWorkflowEvent workflowEvent) {
        String title = null;
        try {
            final HippoNode handle = (HippoNode) session.getNodeByIdentifier(workflowEvent.subjectId());
            final Node published = getPublishedVariant(handle);
            if (published != null) {
                title = JcrUtils.getStringProperty(published, TITLE_PROPERTY, handle.getDisplayName());
            } else {
                log.warn("Something's wrong because I can't find the document variant that was just published");
                title = handle.getDisplayName();
            }
        } catch (ItemNotFoundException e) {
            log.warn("Something's wrong because I can't find the handle of the document that was just published");
        } catch (RepositoryException e) {
            log.error("Something's very wrong: unexpected exception while doing simple JCR read operations", e);
        }
        System.out.println(workflowEvent.user() + " published " + title);
    }

    private static Node getPublishedVariant(Node handle) throws RepositoryException {
        for (Node variant : new NodeIterable(handle.getNodes(handle.getName()))) {
            final String state = JcrUtils.getStringProperty(variant, HippoStdNodeType.HIPPOSTD_STATE, null);
            if (HippoStdNodeType.PUBLISHED.equals(state)) {
                return variant;
            }
        }
        return null;
    }


}

Register the Repository Component

Use the Console to register the repository-managed component at /hippo:configuration/hippo:modules:

/hippo:configuration/hippo:modules
  + postpublication [hipposys:module]
    - hipposys:className = org.example.PublicationPostProcessingModule

 

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?