April 10, 2020
Estimated Post Reading Time ~

Handle Workflow Events in AEM

We all know workflows play a very important role in the day to day activities of AEM. It is not unheard of that you as a user want to trigger some service/component/scheduler on some of the events in a workflow.

To achieve this, it is necessary that we listen to the workflow events in our code and perform our business logic as that event is triggered. In this post, we will see how we can achieve this using an EventHandler.

Since AEM has a rich set of APIs, we are fortunate that it has also an API through which we can listen to workflow-related events.

Steps:

Below are the steps through which we can listen to workflow events - 

Step #1

Create an Event Handler (WorkflowEventsHandler, in our case) which implements the org.osgi.service.event.EventHandler interface and implement its handleEvent(Event event) method.

Step #2

Since any EventHandler needs some topic to listen to, in our case, we will provide the topic as com.day.cq.workflow.event.WorkflowEvent.EVENT_TOPIC. WorkflowEvent provides an extension of Event that is used for propagating workflow-related information as OSGI events.

Step #3

We will implement the handleEvent(Event event) method and get the event topic. We will check if the received topic is of type WorkflowEvent and if it is true then we will proceed further.

Step #4

Now, we will get the event type from the Event object. The event type gives us information about what kind of event our EventHandler has received. Since we can get multiple types of events in a workflow such as WORKFLOW_STARTED, WORKFLOW_COMPLETED, WORKFLOW_RESUMED, etc. Based on our requirements, we can implement our business logic.

Code

The complete code for this EventHandler is below -

package org.redquark.demo.core.listeners;

import java.util.HashMap;
import java.util.Map;

import javax.jcr.Session;

import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.day.cq.workflow.WorkflowException;
import com.day.cq.workflow.WorkflowService;
import com.day.cq.workflow.WorkflowSession;
import com.day.cq.workflow.event.WorkflowEvent;
import com.day.cq.workflow.exec.Workflow;
import com.day.cq.workflow.exec.WorkflowData;

/**
 * @author Anirudh Sharma
 *
 */
@Component(service = EventHandler.class, immediate = true, property = {
  Constants.SERVICE_DESCRIPTION
    + "= This event handler listens to the events that occur in the life cycle of a Workflow",
  EventConstants.EVENT_TOPIC + "=" + WorkflowEvent.EVENT_TOPIC })
public class WorkflowEventsHandler implements EventHandler {

 private final Logger LOG = LoggerFactory.getLogger(this.getClass());

 @Reference
 private ResourceResolverFactory resourceResolverFactory;

 @Reference
 private WorkflowService workflowService;

 /**
  * This method returns the workflow session
  * 
  * @return {@link WorkflowSession}
  */
 private WorkflowSession getWorkflowSession() {
  try {
   // Creating and getting the service user map
   Map<String, Object> serviceUserMap = new HashMap<>();
   serviceUserMap.put(ResourceResolverFactory.SUBSERVICE, "eventingService");
   // Get the reference of ResourceResolver from the ResourceResolverFactory and
   // the Service User map
   ResourceResolver resourceResolver = resourceResolverFactory.getServiceResourceResolver(serviceUserMap);
   // Adapting ResourceResolver to the Session object
   Session session = resourceResolver.adaptTo(Session.class);
   WorkflowSession workflowSession = workflowService.getWorkflowSession(session);
   return workflowSession;
  } catch (Exception e) {
   LOG.error(e.getMessage(), e);
  }
  return null;
 }

 @Override
 public void handleEvent(Event event) {
  LOG.info("Received event of topic: {}", event.getTopic());
  WorkflowSession workflowSession = null;
  try {
   // Get the workflow session
   workflowSession = getWorkflowSession();
   // Get the received topic
   String topic = event.getTopic();
   // Check if the received topic is of type Workflow event
   if (topic.equals(WorkflowEvent.EVENT_TOPIC)) {
    // Get the type of event
    Object eventType = event.getProperty(WorkflowEvent.EVENT_TYPE);
    // Check for each types of events in a workflow
    if (eventType.equals(WorkflowEvent.WORKFLOW_STARTED_EVENT)) {
     LOG.info("Workflow has started");
     String instanceId = (String) event.getProperty(WorkflowEvent.WORKFLOW_INSTANCE_ID);
     try {
      // Get the reference of the Workflow object
      Workflow workflow = workflowSession.getWorkflow(instanceId);
      // Get the workflow data
      WorkflowData workflowData = workflow.getWorkflowData();
      LOG.info("Data in workflow: {}", workflowData.getPayload().toString());
     } catch (WorkflowException e) {
      LOG.error(e.getMessage(), e);
     }
    } else if (eventType.equals(WorkflowEvent.WORKFLOW_COMPLETED_EVENT)) {
     LOG.info("Workflow has completed");
    } else if (eventType.equals(WorkflowEvent.WORKFLOW_RESUMED_EVENT)) {
     LOG.info("Workflow is resumed");
    } else if (eventType.equals(WorkflowEvent.WORKFLOW_ABORTED_EVENT)) {
     LOG.info("Workflow is aborted");
    } else if (eventType.equals(WorkflowEvent.WORKFLOW_SUSPENDED_EVENT)) {
     LOG.info("Workflow is suspended");
    } else if (eventType.equals(WorkflowEvent.WORKITEM_DELEGATION_EVENT)) {
     LOG.info("Workflow is delegated");
    } else {
     LOG.warn("Something is wrong with the workflow");
    }
   }
  } catch (Exception e) {
   LOG.error(e.getMessage(), e);
  } finally {
   if (workflowSession != null) {
    workflowSession.getSession().logout();
   }
  }
 }

}

Deploy this code on AEM and start testing.

Testing

For testing, the following steps can be taken - 

Step #1

Choose a workflow and run it.

Step #2

You should be able to see logs getting printed as - 

2020-03-01 13:25:19.407 INFO [org.redquark.demo.core.listeners.WorkflowEventsHandler] Received event of topic: com/day/cq/workflow/event
2020-03-01 13:25:19.409 INFO [org.redquark.demo.core.listeners.WorkflowEventsHandler] Workflow has started
2020-03-01 13:25:19.412 INFO [org.redquark.demo.core.listeners.WorkflowEventsHandler] Data in workflow: /content/dam/we-retail/en/activities/biking/mountain-biking.jpg

Conclusion

In this post, we saw how can we create an EventHandler and perform our business logic when our required event triggers.

You can find the code to this in my GitHub repository.


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.