Transcript

Advanced Workflow: Deeper Dive!

Nick Smith!Senior Software Engineer, Services Team, Alfresco!

Agenda!

Service Tasks •  Java Delegate Class!•  Java Delegate Bean!•  Arbitrary Expressions!

Listeners •  Execution Listeners!•  Task Listeners!

Scripting •  Scope Variables!•  Execution Variables!•  Examples!

Timers

Questions

Service Tasks!

Service Tasks!

•  Service Tasks allow Java code to be executed as part of a workflow!

•  Allows easy unit testing and code re-use!

•  Three ways to implement:!•  JavaDelegate Class!

•  JavaDelegate Bean:!

•  Arbitrary Expression!

Service Tasks: Java Delegate Class!

•  The supplied class must implement JavaDelegate interface!

•  Fields can be set on the class!

•  Use the ʻactiviti:classʼ attribute to specify the delegate class

<serviceTask id=“getMimetypet" name=“Get Mimetype” activiti:class="org.alfresco.examples.MimetypeGetter“ > <extensionElements> <activiti:field name=“document"> <activiti:expression>${dcwkflw_document}</activiti:expression> </activiti:field> </extensionElements> </serviceTask>

Service Tasks: Java Delegate Bean!

•  The supplied bean must implement JavaDelegate interface!

•  The same bean instance is used for all executions!

•  The bean must be defined in Spring and registered with the activitiBeanRegistry!

•  Use ʻactiviti:delegateExpressionʼ attribute to specify the delegate bean in the process definition:!

<serviceTask id=“getMimetype" name=“Get Mimetype“ activiti:delegateExpression="${mimetypeGetter}" />

Service Tasks: Java Delegate Bean!

•  Recommended to extend BaseJavaDelegate class!

•  Recommend extending baseJavaDelegate bean!

•  If the bean class extends BaseJavaDelegate and the Spring bean definition extends baseJavaDelegate, then the bean will

automatically be registered with the activitiBeanRegistry and will have access to the serviceRegistry!

<bean id=“mimetypeGetter" parent="baseJavaDelegate" class="org.alfresco.example.MimetypeGetter" />

Service Tasks: Java Delegate Bean!

public class MimetypeGetter extends BaseJavaDelegate { @Override public void execute(DelegateExecution execution) throws Exception { ScriptNode document = (ActivitiScriptNode) execution.getVariable("dcwkflw_document"); NodeRef nodeRef = document.getNodeRef();

ServiceRegistry serviceRegistry = getServiceRegistry(); FileFolderService fileService = serviceRegistry.getFileFolderService(); FileInfo file = fileService.getFileInfo(nodeRef); String mimetype = file.getContentData().getMimetype();

execution.setVariable("dcwkflw_mimetype“, mimetype); } }

Service Tasks: Arbitrary Expression!

•  Execute any arbitrary expression!

•  The expression may reference any bean defined in Spring and

registered with the activitiBeanRegistry!

•  A process variable can be specified for the return value using the

activiti:result attribute!

<serviceTask id=”getMimetype” name="Get Mimetype" activiti:resultVariable="dcwkflw_mimetype" activiti:expression=“${mimetypeGetter.getMimetype(dcwkflow_document)}” />!

Listeners!

Listeners!

•  Used to react to certain events during workflow execution!

•  Two types: TaskLisener and ExecutionListener!

•  Three ways to configure, as with ServiceTasks:!•  Listener Class:!<activiti:executionListener class="org.alfresco.example.MyExecutionListener" event=“start” />!

•  Listener Bean:!<activiti:taskListener delegateExpression=“${myTaskListener}" event=“create" />!

•  Arbitrary Expression <activiti:executionListener expression=“myPojo.myMethod(myVar)" event="start" />

Listeners: Execution Listener Events!•  Event: Execution (Workflow) starts or ends!<extensionElements> <activiti:executionListener class=“org.alfresco.example.ExecutionEventLogger" event=“start” /> </extensionElements>!

•  Event: Activiti (Workflow Node) starts or ends!<userTask id=“theTask” name=“The Task” > <extensionElements> <activiti:executionListener delegateExpression=“${executionEventLogger}" event=“end” /> </extensionElements> </userTask>!

•  Event: Sequence Flow (Transition) is taken!<sequenceFlow id=“theFlow” sourceRef=“theTask” targetRef=“theEnd” > <extensionElements> <activiti:executionListener expression=“${logger.info(execution.eventName)}" /> </ sequenceFlow > </userTask>

Listeners: Execution Listener Implementation!

Execution Listener class or delegate bean must implement ExecutionListener interface:!

public class ExecutionEventLogger implements ExecutionListener { private static final Log LOGGER = LogFactory.getLog(ExecutionEventLogger.class);

@Override public void notify(DelegateExecution execution) throws Exception { String eventName = execution.getEventName(); LOGGER.info("Received event: " + eventName); }

}!

Listeners: Task Listener Events!•  All Task Listeners defined inside task elements:!<userTask id=“theTask” name=“The Task” > <extensionElements> <activiti:taskListener ... [Listener Details] ... /> </extensionElements> </userTask>!

•  Event: assignment is called when a task is assigned to a user,

usually called before create:!<activiti:taskListener event=“assignment” class=“org.alfresco.example.TaskEventLogger” />

•  Event: create is called when the task is created, after assignment:!<activiti:taskListener event=“create” delegateExpression=“${taskEventLogger}” />

•  Event: completed is called when the task is completed:!<activiti:taskListener event=“completed” expression=“${logger.info(task.eventName)}” />

Listeners: Task Listener Implementation!

Task Listener class or delegate bean must implement TaskListener interface:!

public class ExecutionEventLogger implements TaskListener { private static final Log LOGGER = LogFactory.getLog(ExecutionEventLogger.class);

@Override public void notify(DelegateTask task) { String eventName = task.getEventName(); LOGGER.info("Received event: " + eventName); }

}

Scripting!

Scripting!

•  Scripting Lagnuage:!•  JavaScript!

•  Activiti Implementations:!•  AlfrescoScriptDelegate!

•  ScriptExecutionListener !

•  ScriptTaskListener!

Scripting: Scope Variables!

•  person ScriptNode, the current user!

•  userhome ScriptNode, the home space of the current user!

•  execution DelegateExecution, the current execution.!

•  task DelegateTask, the current task (ScriptTaskListener only)!

•  cancelled boolean, was the execution cancelled (ScriptExecutionListener only)!

•  deleted boolean, was the execution deleted

(ScriptExecutionListener)!

Scripting: Execution Variables!

•  All execution variables added to scope!

•  Variable names translated from “prefix:suffix” to “prefix_suffix”!

•  Associations and noderef properties converted to ScriptNodes!

•  Use execution.setVariable(name, value) to modify variables!

•  Check if a variable exists using:!

if (typeof <variable name> != 'undefined')

Scripting: Examples!

•  Copy a task variable to a process variable:

execution.setVariable('dcwkflw_reviewOutcome', task.getVariable('dcwkflw_reviewOutcome'));

•  Set a task property from a pocess variable if it exists:

if (typeof bpm_workflowDueDate != 'undefined') task.dueDate = bpm_workflowDueDate;

•  Apply an aspect ʻdcwkflw:publishedʼ to a document:

var presentation = bpm_package.children[0]; // Get the presentation presentation.addAspect('dcwkflw:published'); // Apply published aspect

Timers!

Timers!

•  Timers are used to delay an event until a specified time/duration!

•  Timers can be attached to three types of event:!•  startEvent: Starts the workflow!

•  intermediateCatchEvent: Between nodes/events!

•  boundaryEvent: Associated with a node (e.g. a userTask)!

•  Three ways to set trigger time:!•  timeDate: Triggers at specified date/time!

•  timeDuration: Triggers after specified delay duration!

•  timeCycle: Triggers repeatedly with specified delay/interval!

•  All dates/times/durations/intervals use ISO8601 format!

Timers: Start Event Date Example!

•  Create a workflow which sends a Christmas Greeting

<!-- Start workflow at 12:05 on Christmas Day --> <startEvent id="start" >

<timerEventDefinition> <timeDate>2011-12-25T12:05:00</timeDate> </timerEventDefinition> </startEvent>

<sequenceFlow id='flow1' sourceRef='start' targetRef='sendGreeting' />

Timers: Intermediate Event Delay Example!

•  Delay after some service task performs some asynchronous event

to wait for the job to complete:!

<sequenceFlow id='flow1' sourceRef='asyncJob' targetRef='waitForJobToFinish' />

<!-- Wait 1 hour 30 mins for the job to finish --> <intermediateEvent id="waitForJobToFinish" > <timerEventDefinition> <timeDuration>PT1H30M</timeDate> </timerEventDefinition> </intermediateEvent>

<sequenceFlow id='flow2' sourceRef='waitForJobToFinish' targetRef='nextTask' />

Timers: Repeating Boundary Event Example!

•  Send a reminder email if a task isnʼt completed after 1 week.

Repeat the email every day for 3 days:! <userTask id="theTask" >

<!-- Wait 1 week, then repeat every 2 days a further 2 times --> <boundaryEvent id="repeatingNotification" cancelActivity="false" attachedToRef="theTask" /> <timerEventDefinition> <timeCycle>R3/P1W/P1D</timeDate> </timerEventDefinition> </boundaryEvent>

<sequenceFlow id='flow1' sourceRef='repeatingNotification' targetRef='sendEmail' />

<serviceTask id="sendEmail" activiti:delegateExpression="${sendEmailDelegate}" />

Questions ?!