Understanding AEM Workflow

Manu Mathew
5 min readFeb 3, 2023

--

AEM Workflows allow you to automate a series of steps that are performed on (one or more) pages and/or assets. Workflows can interact with assets, user accounts and services.

Tiles for workflow

Once you create a workflow model- this is like a blueprint for the workflow,

you'll notice a number of OOTB workflows as shown below-

Note that, the model always has a start and a end node.

Once you create a workflow, you'll have to create a launcher to automate the trigger for the workflow-

Required properties:

  • Event Type: It contains the type of event to trigger the workflow like created, modified and removed.
  • Node Type: It is used to mention the primary type of your node.
  • Path: Defines the path on which the workflow is activated(payload).
  • Run Modes: It contains the run modes on which the workflow needs to be run- author, publish or both.
  • Workflow Model: To select the workflow model from the list.

Note: Activate the workflow launcher for it to be active, using the activate switch.

Once the workflow is triggered, you will find the workflow instance in http://localhost:4502/libs/cq/workflow/admin/console/content/instances.html and completed workflows in http://localhost:4502/libs/cq/workflow/admin/console/content/archive.html

If your workflow fails it will show the same in http://localhost:4502/libs/cq/workflow/admin/console/content/failures.html

Note: workflow models and launchers(Model designs) are stored under /conf, but workflow running instances(Runtime models) are stored in /var.

To Run workflow manually, you will have to provide payload(path) and run it.

We can provide/configure stages(logical sections) to workflow — via open properties. This can be used in participant chooser, to assign phases to the workflow.

  • Process Step: Used to execute a service or ECMA script at runtime. This is generally used when we want our application to execute a certain logic.
  • Participant Step: Used to assign an ID of the user for the generated work item.
  • Dynamic Participant Step: Used to selects the ID of the user that is assigned the work item via service or ECMA script.
  • Decision Step: This contains the AND and OR operations for the workflow via routing expressions.

Payload: Defines the resource on which the workflow is performed eg: page, asset etc.

We also can call another workflow inside a workflow using container step-

To trigger the workflow via Java code:

Step1: Create a workflow session using the resource resolver-

WorkflowSession workflowSession = resourceResolver.adaptTo(WorkflowSession.class);

Step2: Define WorkflowModel and WorkflowData-

WorkflowModel workflowModel = workflowSession.getModel("/runtime/model/path");

WorkflowData workflowData = workflowSession.newWorkflowData("JCR_PATH",payload);

Step3: Start the workflow-

Map<String,Object> workflowMetaData = new HashMap<>();
workflowSession.startWorkflow(workflowModel,workflowData,workflowMetaData);

Example — via servlet TriggerWorkFlowServlet :


package com.mysite.core.servlets;

import com.adobe.granite.workflow.WorkflowSession;
import com.adobe.granite.workflow.exec.WorkflowData;
import com.adobe.granite.workflow.model.WorkflowModel;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.servlets.annotations.SlingServletPaths;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;


@Component(service = { Servlet.class })
@SlingServletPaths(value={"/mysite/triggerworkflow"})
public class TriggerWorkFlowServlet extends SlingSafeMethodsServlet {

private final Logger LOG = LoggerFactory.getLogger(TriggerWorkFlowServlet.class);

@Override
protected void doGet(final SlingHttpServletRequest req,
final SlingHttpServletResponse resp) throws ServletException, IOException {
final ResourceResolver resourceResolver = req.getResourceResolver();

String returnStatus = "Workflow in progress";
String payload = req.getRequestParameter("payload").getString();
try{
if(StringUtils.isNotBlank(payload)){
WorkflowSession workflowSession = resourceResolver.adaptTo(WorkflowSession.class);

WorkflowModel workflowModel = workflowSession.getModel("/var/workflow/models/my-sample-workflow");
WorkflowData workflowData = workflowSession.newWorkflowData("JCR_PATH",payload);

returnStatus = workflowSession.startWorkflow(workflowModel,workflowData).getState();
}
}catch (Exception e){
LOG.info("Exception Occurred:{}",e.getMessage());
}
resp.setContentType("application/json");
resp.getWriter().write(returnStatus);
}
}

To create a Custom Workflow Process-

  • Create an OSGi service implementing the interface com.adobe.granite.workflow.exec.WorkflowProcess.
  • Set the property process.label — Name of the workflow that will be listed.
  • Implement the execute(WorkItem, WorkflowSession, MetaDataMap) method with the implementation code(code logic).

The execute() method has three parameters -

  1. WorkItem — It contains the WorkflowData. The instances act on and a reference to the WorkflowNode that describes the underlying workflow step.
  2. WorkflowSession — This class provides a workflow session to perform/manage WorkflowModels, Workflow instances and their execution.
  3. MetaDataMap — A value map for generic access to meta data values via arguments in the dialog.
package com.mysite.core.workflows;

import com.adobe.granite.workflow.WorkflowException;
import com.adobe.granite.workflow.WorkflowSession;
import com.adobe.granite.workflow.exec.WorkItem;
import com.adobe.granite.workflow.exec.WorkflowData;
import com.adobe.granite.workflow.exec.WorkflowProcess;
import com.adobe.granite.workflow.metadata.MetaDataMap;
import org.apache.commons.lang3.StringUtils;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.Session;
import java.util.Arrays;


@Component(service = WorkflowProcess.class, property = { "process.label=" + "Process Step Example" })
public class CustomWorkflow implements WorkflowProcess{
private final Logger log = LoggerFactory.getLogger(this.getClass());

@Override
public void execute(WorkItem workItem, WorkflowSession workflowSession, MetaDataMap metaDataMap) throws WorkflowException {
//Get Workflow Data
WorkflowData workflowData = workItem.getWorkflowData();

//Getting payload from Workflow
String payloadType = workflowData.getPayloadType();

try {
// Check type of payload; there are two - JCR_PATH and JCR_UUID
if (StringUtils.equals(payloadType, "JCR_PATH")) {
log.info("Payload type: {}", payloadType);

Session session = workflowSession.adaptTo(Session.class);
// Get the JCR path from the payload
String path = workItem.getWorkflowData().getPayload().toString();
log.info("Payload path: {}", path);

Node node = (Node) session.getItem(path);
node.setProperty("property","value");

//To get the MetaDataMap
MetaDataMap workFlowMetaDataMap = workItem.getWorkflow().getWorkflowData().getMetaDataMap();

// Get workflow process arguments
String[] processArguments = metaDataMap.get("PROCESS_ARGS", "Default").split(",");
log.info("Process args: {}", Arrays.toString(processArguments));
}
} catch (Exception e) {
log.info("Exception Occurred:{}",e.getMessage());
}


}

}

To create a Custom Workflow Step-

Step1: Create a custom workflow process and then create a component and map it as shown below-

To allow the component group add it the respective node as shown below(You may need to overlay it in /apps )—

--

--