Extending Workflow Functionality

You are reading the AEM 5.6 version of Extending Workflow Functionality.
This documentation is also available for the following versions: CQ 5.5 

Developing Custom Steps

Creating a custom workflow step involves the following activities:

  • Develop the workflow step component.
  • Develop the step implementation as an OSGi service or an ECMA script. 

Workflow Step Components

The workflow step component defines the appearance and behavior of the step when creating workflow models:

  • The category and step name in Sidekick.
  • The appearance of the step in workflow models.
  • The edit dialog for configuring component properties.
  • The service or script that is executed at runtime.

As with all components, workflow step components inherit from the component that is specifed for the sling:resourceSuperType property. The following diagram shows the heirarchy of cq:component nodes that form the basis of all workflow step components. The diagram also includes the Process Step, Participant Step, and Dynamic Paraticipant Step components, which are the most basic starting points for developing custom step components. 

file

The cq/workflow/components/model/step component is the nearest common ancestor of the Process Step, Participant Step, and Dynamic Participant Step, which all inherit the following items:

  • The step.jsp script that renders the title and description of the step component when it is added to a model. The default title and description are "Double click to enter title" and "Double click to enter description", respectively.

    file
  • The details.jsp script that renders the text that appears at the bottom of the step in the model editor. There is no detail text by default.

  • A dialog with the following tabs:

    • Common, for editing the title and description.
    • Advanced, for editing email notification properties.
    file
    file

    When the tabs of the edit dialog of a step component do not match this default appearance, the step component has defined scripts, node properties, or dialog tabs that override the inherited tabs.

Creating Custom Workflow Step Components

To create a custom workflow step component, copy an existing step component to your application folder and modify it as required. Step components are described in the Workflow Step Reference.

You can also create a  cq:Component node in your application folder that specifies a convenient step component as the sling:resourceSuperType property. To create the most basic step component, use one of the following components as the sling:resourceSuperType:

  • Process Step: /libs/cq/workflow/components/model/process
  • Participant Step: /libs/cq/workflow/components/model/participant
  • Dynamic Participant Step: /libs/cq/workflow/components/model/dynamic_participant
Use the following procedure to create a component based on one of these base step components. 
  1. Create the following node in your components folder:

    • Name: A meaningful name for your step component.
    • Type: cq:Component
  2. To specify the component on which you are basing your component, add the following property to the cq:Component node:

    • Name: sling:resourceSuperType
    • Type: String
    • Value: One of the following paths that resolves to a base component:
      • cq/workflow/components/model/process
      • cq/workflow/components/model/participant
      • cq/workflow/components/model/dynamic_participant
  3. Below the cq:component node, add the following node:

    • Name: cq:editConfig
    • Type: cq:EditConfig

    For more information about the cq:editConfig node, see Configuring the Edit Behaviour of a Component.

  4. To inherit the implementation of the component super type, add the following property to the cq:editConfig node:

    • Name: cq:inherit
    • Type: Boolean
    • Value: True

Configuring the Appearance of Step Components

Certain properties of the cq:Component node affect how the component appears in the following locations:

  • In Sidekick.
  • In the Edit dialog for the parsys component (available in Design mode).
  1. Select the step component node and add the String properties of the following names:

    • componentGroup: The group that the component belongs to.
    • jcr:title: The name of the component. When this property is omitted, the node name appears by default.
    • jcr:description: The description that appears for the component.
  2. To enable the compnent to be added to a worklfow model, add the following property to the cq:Component node:

    • Name: allowedParents
    • Type: String[] (in CRXDE Lite, specify String as the type and select the Multi option)
    • Value: */parsys

Specifying the Default Title and Description for Step Instances

In the workflow model editor, the graphical representation of a step includes the title and description that are stored with the step instance. The title and description are retrieved from the Title and Description fields on the Common tab of the Edit dialog. However, when an instance does not have values for Title or Description, the following default text appears instead:

  • Double-click to enter title.
  • Double-click to enter description.
file

Use the following procedure to specify default values for the Title and Description fields on the Common tab.

Note: The field values appear on the step instance when both of the following requirements are satisfied:

  • The edit dialog of the step stores the title and description in the ./jcr:title and ./jcr:description locations, respectively. This requirement is satisfied when the edit dialog uses the Common tab that the /libs/cq/flow/components/step/step component implements.
  • The step component or an ancestor of the component does not override the step.jsp script that the /libs/cq/flow/components/step/step component implements.
  1. Below the cq:component node, add the following node:

    • Name: cq:editConfig
    • Type: cq:EditConfig

    For more information about the cq:editConfig node, see Configuring the Edit Behaviour of a Component.

  2. Below the cq:EditConfig node, add the following node:

    • Name: cq:formParameters
    • Type: nt:unstructured
  3. Add String properties of the following names to the cq:FormParameters node:

    • jcr:title: The value fills the Title field of the Common tab.
    • jcr:description: The value fills the Description field of the Common tab.

Adding Detailed Information to Steps in the Workflow Model

Add detailed information about a step that appears on the step instance when it is added to a workflow model. To see an example of this configuration, use CRXDE Lite to explore the /libs/cq/workflow/components/model/process component.

Note: The following procedure requires that the step component or an ancestor of the component does not override the step.jsp script that the /libs/cq/flow/components/step/step component implements.

  1. Add the following node below the cq:component node:

    • Name: details.jsp
    • Type: nt:file
  2. Edit the details.jsp file so that it generates the detailed information as HTML code.  

Overriding the Edit Dialog

Define a dialog for your component to enable wofklow model developers to configure step properties other than those that are provided in the default dialog tabs.

When you define a dialog, the dialog contains no tabs or widgets because it overrides the dialog that the component super type defines. To include the tabs that the super type defines, add a reference to the tab nodes. 

Tip: Typically, components define tabs outside of the node structure that defines the dialog. The dialog references the tab nodes to include them. The step components that are installed below the /libs/cq/workflow/components/workflow folder provide several examples.

The following procedure creates the node structure that defines a component dialog, and adds a tab either explicitly or by reference.

  1. Add the following node below your cq:component node:

    • Name: dialog
    • Type: cq:Dialog
  2. Add String properties of the following names to the dialog node:

    • title: The value is the title of the dialog, such as Step Properties.
    • xtype: The value is dialog.
  3. Below the dialog node, add the following child node:

    • Name: items
    • Type: cq:WidgetCollection
  4. Below the items node, add the following child node:

    • Name: tabs
    • Type: cq:TabPanel
  5. Below the tabs node, add the following child node:

    • Name: items
    • Type: cq:WidgetCollection
  6. Below the items node, add cq:Widget child nodes that define the tabs of the dialog. You can either add the cq:Widget nodes explicitly, or refer to nodes that are located elsewhere in the repository:

    • Add expliciitly: Add a type of cq:Widget that includes fields (for example DialogFieldSet), or that serves as a container for other widgets (for example cq:Panel).
    • Reference existing: Add a cq:Widget node that has the xtype property of cqinclude. The value of the path property returns the json representation of widget nodes that are located elsewhere. For example, the dialog of the No Opertation component (/libs/cq/workflow/components/workflow/noopprocess) uses the /libs/cq/workflow/components/model/process/process_head.infinity.json path to refer to the tabs that the Process Step component defines.

Saving Property Values in Workflow Metadata

The name property of cq:widget items specifies the JCR node that stores the widget's value. When widgets in the dialog of workflow step compnents store values below the ./metaData node, the value is added to the workflow MetaDataMap. 

For example, a text field in a dialog is a cq:widget node that has the following properites:

Name Type Value
xtype String textarea
name String ./metaData/subject
fieldLabel String Email Subject

The value that is specified in this text field is added to the workflow instance's MetaDataMap object, and is associated with the subject key. 

Note: When the key is PROCESS_ARGS, the value is readily available in ECMA script implementations via the args variable. In this case, the value of the name property is ./metaData/PROCESS_ARGS.

For information about accessing the property value at runtime, see Accessing Dialog Property Values at Runtime.

Overriding the Step Implementation

Each base step component enable workflow model developers to configure the following key features  at design time:

  • Process Step: The service or ECMA script to execute at runtime. 
  • Participant Step: The ID of the user that is assigned the generated work item. 
  • Dynamic Participant Step: The service or ECMA script that selects the ID of the user that is assigned the work item.

To focus the component for use in a specific workflow scenario, configure the key feature in the design and remove the ability for model developers to change it.

  1. Below the cq:component node, add the following node:

    • Name: cq:editConfig
    • Type: cq:EditConfig

    For more information about the cq:editConfig node, see Configuring the Edit Behaviour of a Component.

  2. Below the cq:EditConfig node, add the following node:

    • Name: cq:formParameters
    • Type: nt:unstructured
  3. Add a String property to the cq:FormParameters node. The component super type determines the name of the property:

    • Process Step: PROCESS
    • Participant Step: PARTICIPANT
    • Dynamic Participant Step: DYNAMIC_PARTICIPANT
  4. Specify the value of the property:

    • PROCESS: The path to the ECMA script or the PID of the service that implements the step behavior.
    • PARTICIPANT: The ID of the user who is assigned the work item.
    • DYNAMIC_PARTICIPANT: The path to the ECMA script or the PID of the service that selects the user to assign the work item.
  5. To remove the ability of model developers to change your property values, override the dialog of the component super type. Define a dialog for the component as described in Overriding the Edit Dialog.

Adding Forms and Dialogs to Participant Steps

Customize your Participant step component to provide features that are found in the  Form Participant Step and Dialog Participant Step components:

  • Present a form to the user when they open the generated work item. 
  • Present a custom dialog to the user when they complete the generated work item.
Peform the following procedure on the component that you created in Creating Custom Workflow Step Components.
  1. Below the cq:component node, add the following node:

    • Name: cq:editConfig
    • Type: cq:EditConfig

    For more information about the cq:editConfig node, see Configuring the Edit Behaviour of a Component.

  2. Below the cq:EditConfig node, add the following node:

    • Name: cq:formParameters
    • Type: nt:unstructured
  3. To present a form when the user opens the work item, add the following property to the cq:FormParameters node:

    • Name: FORM_PATH
    • Type: String
    • Value: The path that resolves to the form
  4. To present a custom dialog when the user completes the work item, add the following property to the cq:FormParameters node

    • Name: DIALOG_PATH
    • Type: String
    • Value: The path that resolves to the dialog

Developing Process Step Implementations

When process steps are started during the process of a workflow, the steps send a request to an OSGi service or execute an ECMA script. Develop the service or ECMA script that performs the actions that your workflow requires.

For information about associating your Process Step component with the service or script, see Process Step or Overriding the Step Implementation.

Implementing a Process Step with a Java Class

To define a process step as an OSGI service component (Java bundle):

  1. Create the bundle and deploy it into the OSGI container. Refer to the documentation about creating a bundle with CRXDE LiteCRXDE or Eclipse.
    Note 1: the OSGI component needs to implement the WorkflowProcess interface with its execute() method. See the example code below.
    Note 2: The package name needs to be added to the <Private-Package> section of the maven-bundle-plugin configuration.
  2. Add the SCR property "process.label"  and set the value as you please. This will be the name which your process step is listed as when using the generic Process Step component. See the example below.
  3. In the CQ Workflow console, add the process step to the workflow using the generic Process Step component.
  4. In the edit dialog, go to the Process tab and select your process implementation. 
  5. If you use arguments in your code, set the Process Arguments . For example: false.
  6. Save the changes.

The java methods, respectively the classes that implement the executable Java method are registered as OSGI services, enabling you to add methods at anytime during runtime.

The following OSGI component adds the property approved to the page content node when the payload is a page:

package com.adobe.example.workflow.impl.process;

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.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;

import org.osgi.framework.Constants;

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

/**
 * Sample workflow process that sets an <code>approve</code> property to the payload based on the process argument value.
 */
@Component
@Service
public class MyProcess implements WorkflowProcess {

	@Property(value = "An example workflow process implementation.")
	static final String DESCRIPTION = Constants.SERVICE_DESCRIPTION; 
	@Property(value = "Adobe")
	static final String VENDOR = Constants.SERVICE_VENDOR;
	@Property(value = "My Sample Workflow Process")
	static final String LABEL="process.label";


	private static final String TYPE_JCR_PATH = "JCR_PATH";

	public void execute(WorkItem item, WorkflowSession session, MetaDataMap args) throws WorkflowException {
		WorkflowData workflowData = item.getWorkflowData();
		if (workflowData.getPayloadType().equals(TYPE_JCR_PATH)) {
			String path = workflowData.getPayload().toString() + "/jcr:content";
			try {
				Session jcrSession = session.adaptTo(Session.class); 
				Node node = (Node) jcrSession.getItem(path);
				if (node != null) {
					node.setProperty("approved", readArgument(args));
					jcrSession.save();
				}
			} catch (RepositoryException e) {
				throw new WorkflowException(e.getMessage(), e);
			}
		}
	}

	private boolean readArgument(MetaDataMap args) {
		String argument = args.get("PROCESS_ARGS", "false");
		return argument.equalsIgnoreCase("true");
	}
}

        

Note

If the process fails three times in a row, an item is placed in the Inbox of the workflow administrator to inform him.

Implementing a Process Step with an ECMA Script

ECMA scripts enable script developers to implement process steps. The scripts are located in the JCR repository and executed from there.

The following table lists the variables that are immediately available to process scripts, providing access to objects of the workflow Java API.

Java Class Script variable name
com.adobe.granite.workflow.exec.WorkItem graniteWorkItem
com.adobe.granite.workflow.WorkflowSession graniteWorkflowSession
String[] (contains process arguments) args
com.adobe.granite.workflow.metadata.MetaDataMap metaData
org.apache.sling.scripting.core.impl.InternalScriptHelper sling

The following example script demonstrates how to access the JCR node that represents the workflow payload. The graniteWorkflowSession variable is adapted to a JCR session variable, which is used to obtain the node from the payload path.

var workflowData = graniteWorkItem.getWorkflowData();
if (workflowData.getPayloadType() == "JCR_PATH") { 
    var path = workflowData.getPayload().toString(); 
    var jcrsession = graniteWorkflowSession.adaptTo(Packages.javax.jcr.Session);
    var node = jcrsession.getNode(path);
    if (node.hasProperty("approved")){
    	node.setProperty("approved", args[0] == "true" ? true : false);
    	node.save();
	}
}
        

The following script checks if the payload is an image (png file), creates a black and white image from it, and saves it as a sibling node.

var workflowData = graniteWorkItem.getWorkflowData();
if (workflowData.getPayloadType() == "JCR_PATH") { 
    var path = workflowData.getPayload().toString(); 
    var jcrsession = graniteWorkflowSession.adaptTo(Packages.javax.jcr.Session);
    var node = jcrsession.getRootNode().getNode(path.substring(1));
     if (node.isNodeType("nt:file") && node.getProperty("jcr:content/jcr:mimeType").getString().indexOf("image/") == 0) { 
        var is = node.getProperty("jcr:content/jcr:data").getStream();
        var layer = new Packages.com.day.image.Layer(is);
        layer.grayscale();
                var parent = node.getParent();
                var gn = parent.addNode("grey" + node.getName(), "nt:file"); 
        var content = gn.addNode("jcr:content", "nt:resource");
                content.setProperty("jcr:mimeType","image/png");
                var cal = Packages.java.util.Calendar.getInstance();
                content.setProperty("jcr:lastModified",cal);
                var f = Packages.java.io.File.createTempFile("test",".png");
        var tout = new Packages.java.io.FileOutputStream(f);
        layer.write("image/png", 1.0, tout);
        var fis = new Packages.java.io.FileInputStream(f);
                content.setProperty("jcr:data", fis);
                parent.save();
        tout.close();
        fis.close();
        is.close();
        f.deleteOnExit();
    }
}
        
  1. Create the script (for example with CRXDE Lite) and save it in the repository below the /etc/workflow/scripts folder.

  2. To specify a title that identifies the script in the Process Step edit dialog, add the following properties to the jcr:content node of your script:

    Name Type Value
    jcr:mixinTypes Name[] mix:title
    jcr:title String The name to appear in the edit dialog.
  3. Edit the Process Step instance and specify that it uses the script.

Developing Participant Choosers for Dynamic Participant Step Components

When Dynamic Participant Step components are started during the process of a workflow, the steps send a request to an OSGi service or execute an ECMA script that select the participant to assign the generated work item. Develop the service or ECMA script that selects the participant according to the requirements of your workflow.

For information about associating your Dynamic Participant Step component with the service or script, see Dynamic Participant Step or Overriding the Step Implementation.

Developing a Participant Chooser Using a Java class

To define a participant step as an OSGI service component (Java class):

  1. The OSGI component needs to implement the ParticipantStepChooser interface with its getParticipant() method. See the example code below. 
    Create the bundle and deploy it into the OSGI container. 
  2. Add the SCR property "chooser.label" and set the value as you please. This will be the name as which your participant chooser is listed, using the Dynamic Participant Step component. See the example below.
  3. In the CQ Workflow console, add the dynamic participant step to the workflow using the generic Dynamic Participant Step component.
  4. In the edit dialog select the Participant Chooser tab and select your chooser implementation.
  5. If  you use arguments in your code set the Process Arguments. For this example: /content/geometrixx/en.
  6. Save the changes.
package com.adobe.example.workflow.impl.process;

import com.adobe.granite.workflow.WorkflowException;
import com.adobe.granite.workflow.WorkflowSession;
import com.adobe.granite.workflow.exec.ParticipantStepChooser;
import com.adobe.granite.workflow.exec.WorkItem;
import com.adobe.granite.workflow.exec.WorkflowData;
import com.adobe.granite.workflow.metadata.MetaDataMap;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;

import org.osgi.framework.Constants;
 
/**
 * Sample dynamic participant step that determines the participant based on a path given as argument.
 */
@Component
@Service

public class MyDynamicParticipant implements ParticipantStepChooser {
	
	@Property(value = "An example implementation of a dynamic participant chooser.")
	static final String DESCRIPTION = Constants.SERVICE_DESCRIPTION; 
    @Property(value = "Adobe")
    static final String VENDOR = Constants.SERVICE_VENDOR;
    @Property(value = "Dynamic Participant Chooser Process")
    static final String LABEL=ParticipantStepChooser.SERVICE_PROPERTY_LABEL;
 
    private static final String TYPE_JCR_PATH = "JCR_PATH";
 
    public String getParticipant(WorkItem workItem, WorkflowSession workflowSession, MetaDataMap args) throws WorkflowException {
        WorkflowData workflowData = workItem.getWorkflowData();
        if (workflowData.getPayloadType().equals(TYPE_JCR_PATH)) {
            String path = workflowData.getPayload().toString();
            String pathFromArgument = args.get("PROCESS_ARGS", String.class);
            if (pathFromArgument != null && path.startsWith(pathFromArgument)) {
                return "admin";
            }
        }
        return "administrators";
    }
}
        

Developing a Participant Chooser Using an ECMA Script

Create an ECMA script that selects the user that is assigned the work item that the Participant Step generates. The script must include a function named getParticipant that requires no argumemts, and returns a String that contains the ID of a user or group.

Scripts are located in the JCR repository and executed from there.

The following table lists the variables that provide immediate access to workflow Java objects in your scripts.

Java Class Script variable name
com.adobe.granite.workflow.exec.WorkItem graniteWorkItem
com.adobe.granite.workflow.WorkflowSession graniteWorkflowSession
String[] (contains process arguments) args
com.adobe.granite.workflow.metadata.MetaDataMap metaData
org.apache.sling.scripting.core.impl.InternalScriptHelper sling
function getParticipant() {
    var workflowData = graniteWorkItem.getWorkflowData();
    if (workflowData.getPayloadType() == "JCR_PATH") { 
        var path = workflowData.getPayload().toString(); 
        if (path.indexOf("/content/geometrixx/en") == 0) {
            return "admin";
        } else {
            return "administrators";
        }
    }
}
        
  1. Create the script (for example with CRXDE Lite) and save it in the repository below the /etc/workflow/scripts folder.

  2. To specify a title that identifies the script in the Process Step edit dialog, add the following properties to the jcr:content node of your script:

    Name Type Value
    jcr:mixinTypes Name[] mix:title
    jcr:title String The name to appear in the edit dialog.
  3. Edit the Dynamic Participant Step instance and specify that it uses the script.

Accessing Dialog Property Values at Runtime

The MetaDataMap object of workflow instances is useful for storing and retrieving data throughout the lifetime of the workflow. For workflow step components implementations, the MetaDataMap is especially useful for retrieving component property values at runtime.

For information about configuring the component dialog to store properties as workflow metadata, see Saving Property Values in Workflow Metadata.

The workflow MetaDataMap is available to Java and ECMA script process implementations:

  • In Java implementations of the WorkflowProcess interface, the args parameter is the MetaDataMap object for the workflow. 
  • In  ECMA script implementations, the value is available using the args and metadata variables. 

 

Example: Retrieving the Arguments of the Process Step Component

The Edit dialog of the Process Step component includes the Arguments property. The value of the Arguments property is stored in the worklow metadata, and is associated with the PROCESS_ARGS key.

In the following diagram, The value of the Arguments property is argument1, argument2:

file

Java

The following Java code is the execute method for a WorkflowProcess implementation. The method logs the value in the args MetaDataMap that is associated with the PROCESS_ARGS key.

 

public void execute(WorkItem item, WorkflowSession session, MetaDataMap args) throws WorkflowException {
    	if (args.containsKey("PROCESS_ARGS")){
    		log.info("workflow metadata for key PROCESS_ARGS and value {}",args.get("PROCESS_ARGS","string").toString());
    	}    	
    }
        

When a process step that uses this Java implementation executes, the log contains the following entry:

16.05.2013 12:07:39.566 *INFO* [JobHandler: /etc/workflow/instances/2013-05-16/model_855140139900189:/content/geometrixx/de] com.adobe.example.workflow.impl.process.LogArguments workflow metadata for key PROCESS_ARGS and value argument1, argument2

ECMA Script

The following ECMA script is used as the process for the Process Step. It logs the number of arguments and the argument values:

log.info("Number of process arguments: " + args.length);
for (var i=0;i<args.length;i++){
   log.info(args[i]);
}
        

When the step executes, the log contains the following messages, as expected:

16.05.2013 10:44:22.783 *INFO* [JobHandler: /etc/workflow/instances/2013-05-16/model_850143353702817:/content/geometrixx/de] etc.workflow.scripts.tests$ecma Process arguments: 2 to report

16.05.2013 10:44:22.783 *INFO* [JobHandler: /etc/workflow/instances/2013-05-16/model_850143353702817:/content/geometrixx/de] etc.workflow.scripts.tests$ecma argument1

16.05.2013 10:44:22.783 *INFO* [JobHandler: /etc/workflow/instances/2013-05-16/model_850143353702817:/content/geometrixx/de] etc.workflow.scripts.tests$ecma  argument2

Note

This section describes how to work with arguments for process steps. The information also applies to dynamic participant choosers.

 

For another example of storing component properties in workflow metadata, see Example: Create a Logger Workflow Step. This example features a dailog that associates metadata value with a key other than PROCESS_ARGS.

Scripts and Process Arguments

Within a script for a Process Step component, arguments are available through the args object.

When creating a custom step component, the object metaData is available in a script. This object is limited to single string argument. 

Example: Create a Logger Workflow Step

The following procedures create an example workflow step that sends workflow-reated information to the CQ log. Generally, the quickest way to create a custom step component is to create a copy of the No Op step component. The No Op step component includes the nodes that define the edit dialog, process arguments, and parameters for using the process with forms. After copying the component, change several title and description properties that appear in the UI, and specify the class that implements the step behavior.

Creating the step component

Create a custom step component that enables model creators to set two process arguments. Copy the No Op component to your application and modify node properties.

  1. Use CRXDE Lite to create a folder named mycompany below the /content/apps folder. Continue to create the folders of the following path:

    /content/apps/mycompany/components/workflow

  2. Copy the noopprocess at /libs/cq/workflow/components/workflow/noopprocess to the /apps/mycompany/components/workflow folder. Change the name of the node to loggerprocess.

  3. Change following properties of the /apps/mycompany/components/workflow/loggerprocess node:

    • jcr:title: Logger
    • jcr:description: Writes workflow information to the log file.

    These properties identify the component step in Sidekick.

  4. Select the loggerprocess/cq:editConfig/cq:formParameters node and change the following property values:

    • jcr:title: Logger Process
    • jcr:description: Logs workflow values to the log.
    • PROCESS: com.adobe.example.workflow.impl.process.LoggerProcess

    These properties identify the workflow component and implementation when using it with a form.

  5. Select the loggerprocess/dialog node and change the value of the title property to Logger Process - Step Properties.

    The dialog node defines the edit dialog of the component.

  6. Select the loggerprocess/dialog/items/tabs/items/processargs node and change the value of the path property to /apps/mycompany/components/workflow/loggerprocess/processargs.infinity.json.

    The widgets that are defined below the loggerprocess/processargs node appear in the dialog.

  7. To add argument widgets to the component, add two nodes of type cq:Widget below the /apps/mycompany/components/workflow/loggerprocess/processargs/items/arguments/items node. Use the following node names:

    • singlearg
    • multiarg
  8. Add the following properites to the singlearg node:

    Name Type Value
    xtype String textfield
    name String ./metaData/argSingle
    fieldLabel String Single Argument
  9. Add the following properites to the multiarg node:

    Name Type Value
    xtype String multifield
    name String ./metaData/argMulti
    fieldLabel String Multi Arg

The Logger Process component is added to the Sidekick as follows:

file

The Edit diolog of the Logger Process step component contains the widgets that the singlearg and multiarg nodes define:

 

file

Creating the step implementation

Create the com.adobe.example.workflow.impl.process.LoggerProcess implementation. This is the class that you specified for the PROCESS property of the loggerprocess/cq:editConfig/cq:formParameters node. 

Tip: Create a Maven project using the multimodule-content-package-archetype archetype. To install the content package, use the command mvn clean install -P autoInstallPackage.

See here for details about creating a Java based process step.

package com.adobe.example.workflow.impl.process;

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.WorkflowProcess;
import com.adobe.granite.workflow.metadata.MetaDataMap;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;

import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import java.util.Arrays;
 
/**
 * Sample workflow process that logs arguments into the logfile.
 */
@Component
@Service

public class LoggerProcess implements WorkflowProcess {
 
	@Property(value = "Logger process implementation.")
	static final String DESCRIPTION = Constants.SERVICE_DESCRIPTION; 
    @Property(value = "Adobe")
    static final String VENDOR = Constants.SERVICE_VENDOR;
    @Property(value = "Logger Process")
    static final String LABEL="process.label";
	
	
   private static final Logger log = LoggerFactory.getLogger(LoggerProcess.class);
 
    public void execute(WorkItem item, WorkflowSession session, MetaDataMap args) throws WorkflowException {
        String singleValue = args.get("argSingle", "not set");
        String[] multiValue = args.get("argMulti", new String[]{"not set"});
 
        log.info("-- Workflow Paramter - Single Value: {}", singleValue);
        log.info("-- Workflow Paramter - Multi Value: {}", Arrays.toString(multiValue));
    }
}
        

LoggerProcess Maven Dependencies

When you create a Maven project using the multimodule-content-package-archetype archetype, to support the LoggerProcess class you need to add the following dependencies to the pom.xml files.

<dependency>
	<groupId>com.adobe.granite</groupId>
	<artifactId>com.adobe.granite.workflow.api</artifactId>
	<version>1.0.0</version>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>org.apache.sling</groupId>
	<artifactId>org.apache.sling.jcr.jcr-wrapper</artifactId>
	<version>2.0.0</version>
	<scope>provided</scope>
</dependency>
        

Also, ensure the dependency for the slf4j-api artifact uses version 1.6.4.


Interacting with Workflows Programmatically

Access workflow objects using the Java, ECMA, and REST APIs.

Workflow Java API

The workflow Java API consists of the com.adobe.granite.workflow package and several sub-packages. The most significant member of the API is the com.adobe.granite.workflow.WorkflowSession class. The WorkflowSession class provides access to both design-time and runtime workflow objects:

  • workflow models
  • work items
  • workflow instances
  • workflow data
  • inbox items

The class also provides several methods for intervening in workflow lifecycles.

The following table provides links to the reference documentation of several key Java objects to use when interacting programmatically with workflows. The examples that follow demonstrate how to obtain and use the class objects in code.

Features Objects
Accessing a workflow
WorkflowSession
Executing and querying a workflow instance
Workflow
WorkItem
WorkflowData
Managing a workflow model
WorkflowModel
WorkflowNode
WorkflowTransition

Obtaining Workflow Objects in ECMA Scripts

As described in Locating the Script, Experience Manager (via Apache Sling) provides an ECMA script engine that executes server-side ECMA scripts. The org.apache.sling.scripting.core.ScriptHelper class is immediately available to your scripts as the sling variable.

The ScriptHelper class provides access to the SlingHttpServletRequest that you can use to eventually obtain the WorkflowSession object:

var wfsession = sling.getRequest().getResource().getResourceResolver().adaptTo(Packages.com.adobe.granite.workflow.WorkflowSession);
        

Using the Workflow REST API

The curl command line tool enables you to use the  Workflow REST API to access workflow objects and manage instance lifecycles. The examples that follow demonstrate the use of the REST API via the curl command line tool.

Obtaining a WorkflowSession Object

The com.adobe.granite.workflow.WorkflowSession class is adaptable from a javax.jcr.Session object or a org.apache.sling.api.resource.ResourceResolver object.

Java

In a JSP script (or Java code for a servlet class), use the HTTP request object to obtain a SlingHttpServletRequest object, which provides access to a ResourceResolver object. Adapt the ResourceResolver object to WorkflowSession.

<%
%><%@include file="/libs/foundation/global.jsp"%><%
%><%@page session="false" 
    import="com.adobe.granite.workflow.WorkflowSession,
		org.apache.sling.api.SlingHttpServletRequest"%><%

SlingHttpServletRequest slingReq = (SlingHttpServletRequest)request;
WorkflowSession wfSession = slingReq.getResourceResolver().adaptTo(WorkflowSession.class);
%>
        

ECMA Script

Use the sling variable to obtain the SlingHttpServletRequest object that you use to obtain a ResourceResolver object. Adapt the ResourceResolver object to the WorkflowSession object.

var wfsession = sling.getRequest().getResource().getResourceResolver().adaptTo(Packages.com.adobe.granite.workflow.WorkflowSession);
        

Creating, Reading or Deleting Workflow Models

The following examples show how to access workflow models:

  • The Java and ECMA script code uses the WorkfowSession.createNewModel method. 
  • The curl command accesses the model directly using its URL. 

The workflow model editor requires that models use a specific node structure below /etc/workflow/models. The parent node of the model must be a cq:Page that has a jcr:content node with the following property values:

  • sling:resourceType: cq/workflow/components/pages/model
  • cq:template: /libs/cq/workflow/templates/model 

When you create a model, you must first create this cq:Page node and use its jcr:content node as the parent of the model node.

The id argument that some methods require for identifying the model is the absolute path of the model node in the repository:

/etc/workflow/models/model_name/jcr:content/model

The following examples create a model with the ID /etc/workflow/models/mymodel/jcr:content/model, and then delete the model. Deleting the model sets the deleted property of the model's metaData child node to true. Deleting does not remove the model node.

Java

<%@include file="/libs/foundation/global.jsp"%><%
%><%@page session="false" import="com.adobe.granite.workflow.WorkflowSession,
    		           com.adobe.granite.workflow.model.WorkflowModel,
		           org.apache.sling.api.SlingHttpServletRequest"%><%

SlingHttpServletRequest slingReq = (SlingHttpServletRequest)request;
WorkflowSession wfSession = slingReq.getResourceResolver().adaptTo(WorkflowSession.class);
/* Create the parent page */
String modelRepo = new String("/etc/workflow/models");
String modelTemplate = new String ("/libs/cq/workflow/templates/model");
String modelName = new String("mymodel");
Page modelParent = pageManager.create(modelRepo, modelName, modelTemplate, "My workflow model");

/* create the model */
String modelId = new String(modelParent.getPath()+"/jcr:content/model")
WorkflowModel model = wfSession.createNewModel("Made using Java",modelId);

/* delete the model */
wfSession.deleteModel(modelId);
%>
        

ECMA Script

var resolver = sling.getRequest().getResource().getResourceResolver();
var wfSession = resolver.adaptTo(Packages.com.adobe.granite.workflow.WorkflowSession);
var pageManager = resolver.adaptTo(Packages.com.day.cq.wcm.api.PageManager);

//create the parent page node
var workflowPage = pageManager.create("/etc/workflow/models", "mymodel", "/libs/cq/workflow/templates/model", "Created via ECMA Script");
var modelId = workflowPage.getPath()+ "/jcr:content/model";
//create the model
var model = wfSession.createNewModel("My Model", modelId);
//delete the model
var model = wfSession.deleteModel(modelId);
        

REST

# creating the model called "name"
curl -u admin:admin -d "title=name" http://localhost:4502/etc/workflow/models

# deleting the model by its id
curl -u admin:admin -X DELETE http://localhost:4502/etc/workflow/models/{id}

# getting the model by its id
curl -u admin:admin http://localhost:4502/etc/workflow/models/{id}
        

Managing Workflow Instances

Java

// starting a workflow
WorkflowModel model = wfSession.getModel(workflowId);
WorkflowData wfData = wfSession.newWorkflowData("JCR_PATH", repoPath);
wfSession.startWorkflow(model, wfData);

// querying and managing a workflow
Workflow[] workflows workflows = wfSession.getAllWorkflows();
Workflow workflow= wfSession.getWorkflow(id);
wfSession.suspendWorkflow(workflow);
wfSession.resumeWorkflow(workflow);
wfSession.terminateWorkflow(workflow);
        

ECMA Script

// starting a workflow
var model = wfSession.getModel(workflowId);
var wfData = wfSession.newWorkflowData("JCR_PATH", repoPath);
wfSession.startWorkflow(model, wfData);

// querying and managing a workflow
var workflows = wfSession.getWorkflows(“RUNNING“);
var workflow= wfSession.getWorkflow(id);
wfSession.suspendWorkflow(workflow);
wfSession.resumeWorkflow(workflow);
wfSession.terminateWorkflow(workflow);
        

REST

# starting a workflow
curl -d "model={id}&payloadType={type}&payload={payload}" http://localhost:4502/etc/workflow/instances
# for example:
curl -u admin:admin -d "model=/etc/workflow/models/name&payloadType=JCR_PATH&payload=/content/geometrixx/en/services" http://localhost:4502/etc/workflow/instances

# listing the instances
curl -u admin:admin http://localhost:4502/etc/workflow/instances/

# suspending a workflow
curl -d "state=SUSPENDED" http://localhost:4502/etc/workflow/instances/{id}
# for example:
curl -u admin:admin -d "state=SUSPENDED" http://localhost:4502/etc/workflow/instances/2009-12-09/name_1_1260365650960705000

# resuming a workflow
curl -d "state=RUNNING" http://localhost:4502/etc/workflow/instances/{id}
# for example:
curl -u admin:admin -d "state=RUNNING" http://localhost:4502/etc/workflow/instances/2009-12-09/name_1_1260365650960705000

# terminating a workflow
curl -d "state=ABORTED" http://localhost:4502/etc/workflow/instances/{id}
# for example:
curl -u admin:admin -d "state=ABORTED" http://localhost:4502/etc/workflow/instances/2009-12-09/name_1_1260365650960705000
        

Managing Work Items

Java

// querying work items
WorkItem[] workItems = wfSession.getActiveWorkItems();
WorkItem workItem = wfSession.getWorkItem(id);

// getting routes
List<Route> routes = wfSession.getRoutes(workItem);

// delegating
List<Authorizable> delegatees = wfSession.getDelegatees(workItem);
wfSession.delegateWorkItem(workItem, delegatees.get(0));

// completing or advancing to the next step
wfSession.complete(workItem, routes.get(0));
        

ECMA Script

// querying work items
var workItems = wfSession.getActiveWorkItems();
var workItem = wfSession.getWorkItem(id);

// getting routes
var routes = wfSession.getRoutes(workItem);

// delegating
var delegatees = wfSession.getDelegatees(workItem);
wfSession.delegateWorkItem(workItem, delegatees.get(0));

// completing or advancing to the next step
wfSession.complete(workItem, routes.get(0));
        

REST

# listing the work items
curl -u admin:admin http://localhost:4502/bin/workflow/inbox

# delegating
curl -d "item={item}&delegatee={delegatee}" http://localhost:4502/bin/workflow/inbox
# For example: curl -u admin:admin -d "item=/etc/workflow/instances/2010-01-11/test1_1263223169820641000/workItems/node1_etc_workflow_instances_2010-01-11_test1_1263223169820641000&delegatee=author" http://localhost:4502/bin/workflow/inbox

# completing or advancing to the next step
curl -d "item={item}&route={route}" http://localhost:4502/bin/workflow/inbox
# For example:
curl -d "item=/etc/workflow/instances/2010-01-11/test1_1263223169820641000/workItems/node1_etc_workflow_instances_2010-01-11_test1_1263223169820641000&route=233123169" http://localhost:4502/bin/workflow/inbox
        

Listening for Workflow Events

Use the OSGi event framework to listen for events that the com.adobe.granite.workflow.event.WorkflowEvent class defines. This class also provides several useful methods for obtaining information about the subject of the event. For example, the getWorkItem method returns the WorkItem object for the workitem that is involved in the event.

The following example code defines a service that listens to workflow events and performs tasks according to the type of event.

package com.adobe.example.workflow.listeners;

import org.apache.sling.event.jobs.JobProcessor;
import org.apache.sling.event.jobs.JobUtil;

import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;

import com.adobe.granite.workflow.event.WorkflowEvent;
import com.adobe.granite.workflow.exec.WorkItem;

/**
 * The <code>WorkflowEventCatcher</code> class listens to workflow events. 
 */
@Component(metatype=false, immediate=true)
@Service(value=org.osgi.service.event.EventHandler.class)
public class WorkflowEventCatcher implements EventHandler, JobProcessor {

	@Property(value=com.adobe.granite.workflow.event.WorkflowEvent.EVENT_TOPIC)
	static final String EVENT_TOPICS = "event.topics";

	private static final Logger logger = LoggerFactory.getLogger(WorkflowEventCatcher.class);

	public void handleEvent(Event event) {
		JobUtil.processJob(event, this);
	}

	public boolean process(Event event) {
		logger.info("Received event of topic: " + event.getTopic());
		String topic = event.getTopic();

		try {
			if (topic.equals(WorkflowEvent.EVENT_TOPIC)) {
				WorkflowEvent wfevent = (WorkflowEvent)event;
				String eventType = wfevent.getEventType();
				String instanceId = wfevent.getWorkflowInstanceId();

				if (instanceId != null) {
					//workflow instance events
					if (eventType.equals(WorkflowEvent.WORKFLOW_STARTED_EVENT) ||
							eventType.equals(WorkflowEvent.WORKFLOW_RESUMED_EVENT) ||
							eventType.equals(WorkflowEvent.WORKFLOW_SUSPENDED_EVENT)) {
						// your code comes here...
					} else if (
							eventType.equals(WorkflowEvent.WORKFLOW_ABORTED_EVENT) ||
							eventType.equals(WorkflowEvent.WORKFLOW_COMPLETED_EVENT)) {
						// your code comes here...
					}
					// workflow node event
					if (eventType.equals(WorkflowEvent.NODE_TRANSITION_EVENT)) {
						WorkItem currentItem = (WorkItem) event.getProperty(WorkflowEvent.WORK_ITEM);
						// your code comes here...
					}
				}
			}
		} catch(Exception e){
			logger.debug(e.getMessage());
			e.printStackTrace();
		}
		return true;
	}
}
        

Your comments are welcome!
Did you notice a way we could improve the documentation on this page? Is something unclear or insufficiently explained? Please leave your comments below and we will make the appropriate changes. Comments that have been addressed, by improving the documentation accordingly, will then be removed.

COMMENTS

  • By Ove Lindström - 3:06 PM on Apr 23, 2013   Reply
    The JavaDoc @scr annotation has been deprecated for quite some time now. Time to update the documentation examples soon???
  • By siva - 6:48 PM on May 01, 2013   Reply
    Request for Adobe team to update the document, Example: "com.day.cq.workflow.exec.ParticipantStepChooser" API has been replaced with "com.adobe.granite.workflow.exec.ParticipantStepChooser"

    • By Scott Brodersen - 3:22 PM on May 02, 2013   Reply
      Hi siva,

      This work is scheduled. I will add your feedback to the issue and move it up in priority.

      thanks!
      scott
      • By Scott Brodersen - 3:01 PM on May 17, 2013   Reply
        Siva, I have updated all the code samples to use the granite API.

        thanks
        scott

      ADD A COMMENT

       

      In order to post a comment, you need to sign-in.

      Note: Customers with DayCare user accounts need to create a new account for use on day.com.

      ***