A Tornado Tour of Customizing Workflows in Adobe Experience Manager

Axis41podcast_TornadoTour_AEMYou’re Not in Kansas
Probably as a reflection of the businesses who use them, enterprise software packages always seem to bundle their own, custom workflow engines. And since you’re reading this, you probably already know that Adobe Experience Manager is no different, as it contains one with some reasonable default workflows in the out-of-the-box configuration. However, if your implementation needs to customize these workflows, it can be daunting to know how to start.

I was recently tasked with creating such a customization. According to the business requirements, we needed a custom workflow step that would gather comments from each of the steps in the process and send them to the next assigned participant. Upon completion of the workflow, it needed to write various pieces of data to a custom log file. As it turns out, this wasn’t as complicated as I’d feared, and once I’d wrapped my head around some of the documentation from Adobe, most of what I needed was available with very little custom work.

Following the Yellow Brick Road
Before we map out the customization process I followed, I think it’s worth taking a quick look at how Adobe Experience Manager does workflows.AEM-workflow-flowchart

The first piece is called a Model. I like to think of the Model as a flowchart, just to have the general idea in my brain. In the workflow console (located at /libs/cq/workflow/content/console.html in AEM 5.6.1) there’s a tab for Models—there are around 30 in an out-of-the-box install. Upon opening one up, the page loads a nice editor with boxes and lines. The first box (at the top) is titled “Flow Start” and the last one is called “Flow End.” Each of the boxes is a workflow component that can be configured and customized. When we get to the customization part below, these boxes will be represented as WorkItem objects in Java.

The second big piece of Adobe Experience Manager workflows are Launchers. The Models don’t do much good if there’s no way to trigger them. Launchers can be set up in the Workflow console to start a process when a node is added, changed, or removed. One can also set up Workflows to fire from the sidekick in the authoring environment. Whichever way the workflow triggers, once it starts it’ll just follow the process outlined in the Model.

The third term to keep in mind is Instance. This is just like an instance of an object in Java code. The progress of a workflow Instance can be tracked in the Workflow console. Once an Instance has completed it will move to the Archive tab. That’s it; let’s talk about how to build custom steps using Java.

Pay No Attention to the Man Behind the Curtain
To start with, I created a new Java class that implements the WorkflowProcess interface, which requires an execute method; this method takes three parameters: a WorkItem, a WorkflowSession, and a MetaDataMap.

Very loosely, the WorkItem is the current step of the workflow Model. It contains a WorkflowData object as well as meta information about the current step, such as its title and description in the Model and the time it started. I needed the title of the current step because I had slightly different events happening based on whether the title contained “Approve” or “Reject”. I also log the time started to a custom log file.

The WorkflowData object basically just gives me access to the workflow payload.

The WorkflowSession is a really meaty object. The most obvious use I had for this object was adapting it to a JCR session class. I had a lot of experience using JCR sessions for node lookups in other custom Java classes, and knew I would need it for a few things in the custom step I wrote. As I look up each of the comments in the workflow, I get a username to go along with it. I used the JCR session to look up the user’s full name to print out next to the comment rather than relying on the email recipient knowing who is who based on username.

Additionally, some information in my custom workflow is collected on the payload jcr:content node through a custom dialog step (more on that below). I use the JCR session to look up that information.

Apart from getting a JCR session, the WorkflowSession gave me access to all the HistoryItems in the workflow. Each HistoryItem has the comment and userID from that step in the process. That was mostly what I needed HistoryItems for, but that class also contains a method for moving back and forth through the process history as well access to the WorkItem for the given step.

The MetaDataMap contains, unsurprisingly, metadata on the workflow. More importantly to me, it contains access to the PROCESS_ARGS key. That is a value I set in the Model when I drag a Process Step onto the dropzone:AEM-Workflow-metadatamap

In the field corresponding to “Arguments” I placed the name of a CQ usergroup, and in my custom workflow, I use that value to determine who should receive the email I send out. It is really dead simple to do so:

String processArg = "";
if (args.containsKey("PROCESS_ARGS")){
processArg = args.get("PROCESS_ARGS", "");


If I Only Had a Brain
As part of the custom Model my team was working on, we needed to collect some data from the initiator before advancing to the next step. We used a dialog step in our Model to do this. It’s very simple to do so. Just as you can create custom dialogs for components, you create a dialog node tree in your CRX repository somewhere (we chose to put it in the /etc tree, but there’s no reason it couldn’t live in /apps.)

A custom dialog field can store values in two spots: the workflow metadata or the payload jcr:content node. To make it easier to read the collected data after the workflow had completed, I used the jcr:content node. Because we stored the data on the payload, I also built a method to clear that data once the Model hits its final step.

Logging Logger
Our final piece was to provide support for capturing this data in a custom log file so it could be reported out on. This was possibly the easiest step of all as I simply created a new config file (we put it in our config.author folder: /apps/projectname/config.author/org.apache.sling.commons.log.LogManager.factory.config-fbe25a8e-354a-4708-915a-7ba132289721.xml). You can see examples of other loggers in the Felix console configuration manager; just search for logging logger. This is what our config file looks like:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"

org.apache.sling.commons.log.pattern="\{5\}"     org.apache.sling.commons.log.names="[com.aempodcast.exampleworkflow]"

There’s No Place Like Home
While there are a lot of moving parts to the Adobe Experience Manager Workflow engine, the design of the system as a whole has been pretty well thought out for those who need to customize it. There are a lot of nice features out of the box, but I always feel a lot more comfortable once I’m in the driver’s seat. Thankfully AEM has done a good job of providing the pieces in Java for developers to provide pretty much any custom behavior they can think of. Feel free to reach out to us with any specific questions about workflows within AEM at our email: info@aempodcast.com.