Understanding AEM Workflow
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.
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 -
- WorkItem — It contains the WorkflowData. The instances act on and a reference to the WorkflowNode that describes the underlying workflow step.
- WorkflowSession — This class provides a workflow session to perform/manage WorkflowModels, Workflow instances and their execution.
- 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 )—