提交 df0bf590 编写于 作者: J jbarrez

ACT-221: docced task listeners

上级 400703bd
......@@ -12,10 +12,8 @@
*/
package org.activiti.engine.delegate;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import org.activiti.engine.impl.task.IdentityLinkEntity;
/**
* @author Joram Barrez
......@@ -73,8 +71,14 @@ public interface DelegateTask {
/** Adds the given user as a candidate user to this task. */
void addCandidateUser(String userId);
/** Adds multiple users as candidate user to this task. */
void addCandidateUsers(Collection<String> candidateUsers);
/** Adds the given group as candidate group to this task */
void addCandidateGroup(String groupId);
/** Adds multiple groups as candidate group to this task.
void addCandidateGroups(Collection<String> candidateGroups);
/** Sets the current assignee of this task to the given user */
void setAssignee(String assignee);
......
......@@ -16,9 +16,9 @@ package org.activiti.engine.impl.bpmn;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.impl.el.Expression;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
import org.activiti.engine.impl.runtime.ExecutionEntity;
import org.activiti.engine.impl.task.TaskEntity;
import org.activiti.engine.impl.task.TaskListener;
/**
......
......@@ -12,12 +12,15 @@
*/
package org.activiti.engine.impl.bpmn;
import java.util.Collection;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.impl.el.Expression;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.impl.task.TaskEntity;
import org.activiti.engine.impl.task.TaskListener;
/**
* activity implementation for the user task.
......@@ -59,6 +62,7 @@ public class UserTaskActivity extends TaskActivity {
leave(execution);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void handleAssignments(TaskEntity task, ActivityExecution execution) {
if (taskDefinition.getAssigneeExpression() != null) {
task.setAssignee((String) taskDefinition.getAssigneeExpression().getValue(execution));
......@@ -66,13 +70,27 @@ public class UserTaskActivity extends TaskActivity {
if (!taskDefinition.getCandidateGroupIdExpressions().isEmpty()) {
for (Expression groupIdExpr : taskDefinition.getCandidateGroupIdExpressions()) {
task.addCandidateGroup((String) groupIdExpr.getValue(execution));
Object value = groupIdExpr.getValue(execution);
if (value instanceof String) {
task.addCandidateGroup((String) value);
} else if (value instanceof Collection) {
task.addCandidateGroups((Collection) value);
} else {
throw new ActivitiException("Expression did not resolve to a string or collection of strings");
}
}
}
if (!taskDefinition.getCandidateUserIdExpressions().isEmpty()) {
for (Expression userIdExpr : taskDefinition.getCandidateUserIdExpressions()) {
task.addCandidateUser((String) userIdExpr.getValue(execution));
Object value = userIdExpr.getValue(execution);
if (value instanceof String) {
task.addCandidateUser((String) value);
} else if (value instanceof Collection) {
task.addCandidateUsers((Collection) value);
} else {
throw new ActivitiException("Expression did not resolve to a string or collection of strings");
}
}
}
}
......
......@@ -65,6 +65,7 @@ import org.activiti.engine.impl.jobexecutor.TimerDeclarationImpl;
import org.activiti.engine.impl.jobexecutor.TimerExecuteNestedActivityJobHandler;
import org.activiti.engine.impl.pvm.delegate.ActivityBehavior;
import org.activiti.engine.impl.pvm.delegate.ExecutionListener;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
import org.activiti.engine.impl.pvm.process.ScopeImpl;
......@@ -73,7 +74,6 @@ import org.activiti.engine.impl.repository.DeploymentEntity;
import org.activiti.engine.impl.repository.ProcessDefinitionEntity;
import org.activiti.engine.impl.scripting.ScriptingEngines;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.impl.task.TaskListener;
import org.activiti.engine.impl.util.ReflectUtil;
import org.activiti.engine.impl.util.xml.Element;
import org.activiti.engine.impl.util.xml.Parse;
......@@ -1050,7 +1050,7 @@ public class BpmnParse extends Parse {
addError("Invalid eventName for taskListener: choose 'create' |'assignment'", userTaskElement);
}
} else {
addError("EventName is mandatory on taskListener", userTaskElement);
addError("Event is mandatory on taskListener", userTaskElement);
}
}
}
......
......@@ -17,12 +17,12 @@ import org.activiti.engine.impl.bpmn.UserTaskActivity;
import org.activiti.engine.impl.bpmn.parser.BpmnParseListener;
import org.activiti.engine.impl.cfg.ProcessEngineConfiguration;
import org.activiti.engine.impl.pvm.delegate.ExecutionListener;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ScopeImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.activiti.engine.impl.repository.ProcessDefinitionEntity;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.impl.task.TaskListener;
import org.activiti.engine.impl.util.xml.Element;
import org.activiti.engine.impl.variable.VariableDeclaration;
......
......@@ -15,9 +15,9 @@ package org.activiti.engine.impl.history.handler;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.impl.history.HistoricActivityInstanceEntity;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
import org.activiti.engine.impl.runtime.ExecutionEntity;
import org.activiti.engine.impl.task.TaskEntity;
import org.activiti.engine.impl.task.TaskListener;
/**
......
......@@ -11,7 +11,7 @@
* limitations under the License.
*/
package org.activiti.engine.impl.task;
package org.activiti.engine.impl.pvm.delegate;
import org.activiti.engine.delegate.DelegateTask;
......
......@@ -21,6 +21,7 @@ import java.util.Set;
import org.activiti.engine.impl.el.Expression;
import org.activiti.engine.impl.form.TaskFormHandler;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
/**
* Container for task definition information gathered at parsing time.
......
......@@ -14,6 +14,7 @@ package org.activiti.engine.impl.task;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
......@@ -27,6 +28,7 @@ import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.impl.cfg.RepositorySession;
import org.activiti.engine.impl.db.PersistentObject;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
import org.activiti.engine.impl.repository.ProcessDefinitionEntity;
import org.activiti.engine.impl.runtime.ExecutionEntity;
import org.activiti.engine.impl.util.ClockUtil;
......@@ -197,10 +199,22 @@ public class TaskEntity implements Task, DelegateTask, Serializable, PersistentO
createIdentityLink(userId, null, IdentityLinkType.CANDIDATE);
}
public void addCandidateUsers(Collection<String> candidateUsers) {
for (String candidateUser : candidateUsers) {
addCandidateUser(candidateUser);
}
}
public void addCandidateGroup(String groupId) {
createIdentityLink(null, groupId, IdentityLinkType.CANDIDATE);
}
public void addCandidateGroups(Collection<String> candidateGroups) {
for (String candidateGroup : candidateGroups) {
addCandidateGroup(candidateGroup);
}
}
public List<IdentityLinkEntity> getIdentityLinks() {
if (!isIdentityLinksInitialized) {
taskIdentityLinkEntities = CommandContext
......
......@@ -13,7 +13,7 @@
package org.activiti.examples.bpmn.tasklistener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.impl.task.TaskListener;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
/**
......
......@@ -13,7 +13,7 @@
package org.activiti.examples.bpmn.tasklistener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.impl.task.TaskListener;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
/**
......
......@@ -13,7 +13,7 @@
package org.activiti.examples.bpmn.tasklistener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.impl.task.TaskListener;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
/**
......
......@@ -14,7 +14,7 @@ package org.activiti.examples.bpmn.tasklistener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.impl.el.Expression;
import org.activiti.engine.impl.task.TaskListener;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
/**
......
......@@ -13,7 +13,7 @@
package org.activiti.examples.bpmn.tasklistener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.impl.task.TaskListener;
import org.activiti.engine.impl.pvm.delegate.TaskListener;
/**
......
......@@ -34,7 +34,24 @@ public class CustomTaskAssignmentTest extends PvmTestCase {
runtimeService.startProcessInstanceByKey("assigneeThroughSpringService", CollectionUtil.singletonMap("emp", "fozzie"));
assertEquals(1, taskService.createTaskQuery().taskAssignee("Kermit The Frog").count());
// Remove deployments
cleanup(applicationContext);
}
public void testSetCandidateUsersThroughSpringService() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("org/activiti/spring/test/taskassignment/taskassignment-context.xml");
RuntimeService runtimeService = applicationContext.getBean(RuntimeService.class);
TaskService taskService = applicationContext.getBean(TaskService.class);
runtimeService.startProcessInstanceByKey("candidateUsersThroughSpringService", CollectionUtil.singletonMap("emp", "fozzie"));
assertEquals(1, taskService.createTaskQuery().taskCandidateUser("kermit").count());
assertEquals(1, taskService.createTaskQuery().taskCandidateUser("fozzie").count());
assertEquals(1, taskService.createTaskQuery().taskCandidateUser("gonzo").count());
assertEquals(0, taskService.createTaskQuery().taskCandidateUser("mispiggy").count());
cleanup(applicationContext);
}
private void cleanup(ClassPathXmlApplicationContext applicationContext) {
RepositoryService repositoryService = applicationContext.getBean(RepositoryService.class);
for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
repositoryService.deleteDeploymentCascade(deployment.getId());
......
......@@ -12,6 +12,9 @@
*/
package org.activiti.spring.test.taskassignment;
import java.util.Arrays;
import java.util.List;
/**
* @author Joram Barrez
......@@ -22,5 +25,9 @@ public class FakeLdapService {
// Pretty useless LDAP service ...
return "Kermit The Frog";
}
public List<String> findAllSales() {
return Arrays.asList("kermit", "gonzo", "fozzie");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="definitions"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="Examples">
<process id="candidateUsersThroughSpringService">
<startEvent id="start" />
<sequenceFlow id="flow1" sourceRef="start" targetRef="task" />
<userTask id="task" name="Schedule meeting" activiti:candidateUsers="${fakeLdapService.findAllSales()}"/>
<sequenceFlow id="flow2" sourceRef="task" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>
\ No newline at end of file
......@@ -30,7 +30,7 @@
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="dbSchemaStrategy" value="drop-create" />
<property name="deploymentResources" value="classpath:/org/activiti/spring/test/taskassignment/SetAssigneeThroughSpringService.bpmn20.xml" />
<property name="deploymentResources" value="classpath:/org/activiti/spring/test/taskassignment/*.bpmn20.xml" />
</bean>
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
......
......@@ -1225,9 +1225,11 @@ List&lt;Task&gt; tasks = taskService.createTaskQuery().assignee(&quot;kermit&quo
&lt;formalExpression&gt;accountancy&lt;/formalExpression&gt;</programlisting>
</para>
<section id="bpmnUserTaskUserAssignmentExtension">
</section> <!-- task assignment -->
<section id="bpmnUserTaskUserAssignmentExtension">
<title>Custom extension for simple task assignments</title>
<title>Activiti extensions for task assignment</title>
<para>
It is clear that user and group assignments are quite cumbersome
......@@ -1286,9 +1288,61 @@ List&lt;Task&gt; tasks = taskService.createTaskQuery().assignee(&quot;kermit&quo
</itemizedlist>
</para>
</section> <!-- task assignment extensions -->
<para>
In case the previous approaches are not sufficient, it is possible to delegate to
custom assignment logic using a <link linkend="taskListeners">task listener</link>
on the create event:
<programlisting>
&lt;userTask id=&quot;task1&quot; name=&quot;My task&quot; &gt;
&lt;extensionElements&gt;
&lt;activiti:taskListener event=&quot;create&quot; class=&quot;org.activiti.MyAssignmentHandler&quot; /&gt;
&lt;/extensionElements&gt;
&lt;/userTask&gt;</programlisting>
The <literal>DelegateTask</literal> that is passed to the <literal>TaskListener</literal>
implementation, allows to set the assignee and candidate-users/groups:
<programlisting>
public class MyAssignmentHandler implements TaskListener {
public void notify(DelegateTask delegateTask) {
// Execute custom identity lookups here
// and then for example call following methods:
delegateTask.setAssignee(&quot;kermit&quot;);
delegateTask.addCandidateUser(&quot;fozzie&quot;);
delegateTask.addCandidateGroup(&quot;management&quot;);
...
}
}</programlisting>
</para>
<para>
When using Spring it is possible to use the custom assignment attirbutes as described in the section above,
and delegate to a Spring bean using a <link linkend="taskListeners">task listener</link>
with an <link linkend="springExpressions">expression</link> that listens to task <emphasis>create</emphasis> events.
In the following example, the assignee will be set by calling the <literal>findManagerOfEmployee</literal>
on the <literal>ldapService</literal> Spring bean. The <emphasis>emp</emphasis> parameter
that is passed, is a process variable>.
<programlisting>&lt;userTask id=&quot;task&quot; name=&quot;My Task&quot; activiti:assignee=&quot;${ldapService.findManagerForEmployee(emp)}&quot;/&gt;</programlisting>
This also works similar for candidate users and groups:
<programlisting>&lt;userTask id=&quot;task&quot; name=&quot;My Task&quot; activiti:candidateUsers=&quot;${ldapService.findAllSales()}&quot;/&gt;</programlisting>
Note that this will only work if the return type of the invoked methods is <literal>String</literal>
or <literal>Collection&lt;String&gt;</literal> (for candidate users and groups):
<programlisting>
public class FakeLdapService {
public String findManagerForEmployee(String employee) {
return &quot;Kermit The Frog&quot;;
}
public List&lt;String&gt; findAllSales() {
return Arrays.asList(&quot;kermit&quot;, &quot;gonzo&quot;, &quot;fozzie&quot;);
}
</section> <!-- task assignment -->
}</programlisting>
</para>
</section> <!-- task assignment extensions -->
</section>
......@@ -1687,7 +1741,7 @@ public class ReverseStringsFieldInjected implements JavaDelegation {
<section id="executionListeners">
<title>Execution listeners</title>
<title>Execution listener</title>
<para>Execution listeners allow you to execute external Java code or evaluate an expression when certain events occur
during process exevcution. The events that can be captured are:
<itemizedlist>
......@@ -1822,6 +1876,69 @@ public void testExecutionListenerFieldInjection() {
</para>
</section>
</section>
<section id="taskListeners">
<title>Task listener</title>
<para>
A <emphasis>task listener</emphasis> is used to execute custom Java logic or an expression
upon the occurrence of a certain task-related event.
</para>
<para>
A task listener can only be added in the process definition as a child element of a <link linkend="bpmnUserTask">user task</link>.
Note that this also must happen as a child of the <emphasis>BPMN 2.0 extensionElements</emphasis>
and in the <emphasis>activiti</emphasis> namespace, since a task listener is an Activiti-specific construct.
<programlisting>
&lt;userTask id=&quot;myTask&quot; name=&quot;My Task&quot; &gt;
&lt;extensionElements&gt;
<emphasis role="bold">&lt;activiti:taskListener event=&quot;create&quot; class=&quot;org.activiti.MyTaskCreateListener&quot; /&gt;</emphasis>
&lt;/extensionElements&gt;
&lt;/userTask&gt;</programlisting>
A <emphasis>task listener</emphasis> supports following attributes:
<itemizedlist>
<listitem>
<para>
<emphasis role="bold">event</emphasis> (required): the type of task event on which the task listener will
be invoked. Possible values are <emphasis role="bold">'create'</emphasis> (occurs when the task
has been created an all task properties are set), <emphasis role="bold">'assignment'</emphasis>
(occurs when the task is assigned to somebody) or <emphasis role="bold">'complete'</emphasis>
(occurs when the task is completed and just before the task is deleted from the runtime data).
</para>
</listitem>
<listitem>
<para>
<emphasis role="bold">class</emphasis>: the delegation class that must be called.
This class must implement the <literal>org.activiti.engine.impl.pvm.delegate.TaskListener</literal>
interface.
<programlisting>
public class MyTaskCreateListener implements TaskListener {
public void notify(DelegateTask delegateTask) {
// Custom logic goes here
}
}</programlisting>
It is also possible to use <link linkend="serviceTaskFieldInjection">field injection</link> to pass
process variables or the execution to the delegation class.
Note that an instance of the delegation class is created upon process deployment
(as is the case with any class delegation in Activiti), which means that the
instance is shared between all process instance executions.
</para>
</listitem>
<listitem>
<para>
<emphasis role="bold">expression</emphasis>: (cannot be used together with the <emphasis>class</emphasis> attribute):
specifies an expression that will be executed when the event happens.
<programlisting>&lt;activiti:taskListener event=&quot;create&quot; expression=&quot;${execution.setVariable('myVar', 'Hello from the task listener!')}&quot; /&gt;</programlisting>
</para>
</listitem>
</itemizedlist>
</para>
</section>
<section id="bpmnEmailTask">
......
......@@ -26,7 +26,7 @@
Also, it will be the information from which the reports will be generated.
</para>
<section id="historyConfiguration">
<section id="historyConfig">
<title>History configuration</title>
<para>In the activiti.cfg.xml configuration file, you can configure the level of history archiving
that needs to happen:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册