Heinous merge from HEAD. Seems to basically work. Be on guard however.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@4137 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Britt Park
2006-10-18 02:24:36 +00:00
parent 6441f470f5
commit 111296d4dc
156 changed files with 18940 additions and 14167 deletions

View File

@@ -23,6 +23,7 @@ import java.util.Map;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.dom4j.Element;
import org.jbpm.context.def.VariableAccess;
import org.jbpm.context.exe.ContextInstance;
@@ -36,7 +37,7 @@ import org.xml.sax.InputSource;
* A jBPM Action Handler for executing Alfresco Script
*
* The configuration of this action is as follows:
* <script language="javascript">
* <script>
* <expression>
* the script to execute
* </expression>
@@ -72,6 +73,12 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
@SuppressWarnings("unchecked")
public void execute(ExecutionContext executionContext) throws Exception
{
// validate script
if (script == null)
{
throw new WorkflowException("Script has not been provided");
}
// extract action configuration
String expression = null;
List<VariableAccess> variableAccesses = null;
@@ -96,7 +103,12 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
else
{
variableAccesses = jpdlReader.readVariableAccesses(script);
expression = script.element("expression").getTextTrim();
Element expressionElement = script.element("expression");
if (expressionElement == null)
{
throw new WorkflowException("Script expression has not been provided");
}
expression = expressionElement.getTextTrim();
}
// construct script arguments and execute

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.workflow.jbpm;
import java.util.List;
import junit.framework.TestCase;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.db.GraphSession;
import org.jbpm.db.TaskMgmtSession;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.taskmgmt.exe.TaskInstance;
/**
* Unit Test for reproducing constraint violation during JBPM process deletion
*
* http://jira.jboss.com/jira/browse/JBPM-757
*
* @author davidc
*/
public class JBPMDeleteProcessTest extends TestCase {
static JbpmConfiguration jbpmConfiguration = null;
static long processId = -1L;
static String currentTokenPath = null;
static {
jbpmConfiguration = JbpmConfiguration.parseXmlString(
"<jbpm-configuration>" +
" <jbpm-context>" +
" <service name='persistence' " +
" factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />" +
" </jbpm-context>" +
" <string name='resource.hibernate.cfg.xml' " +
" value='jbpmresources/hibernate.cfg.xml' />" +
" <string name='resource.business.calendar' " +
" value='org/jbpm/calendar/jbpm.business.calendar.properties' />" +
" <string name='resource.default.modules' " +
" value='org/jbpm/graph/def/jbpm.default.modules.properties' />" +
" <string name='resource.converter' " +
" value='org/jbpm/db/hibernate/jbpm.converter.properties' />" +
" <string name='resource.action.types' " +
" value='org/jbpm/graph/action/action.types.xml' />" +
" <string name='resource.node.types' " +
" value='org/jbpm/graph/node/node.types.xml' />" +
" <string name='resource.varmapping' " +
" value='org/jbpm/context/exe/jbpm.varmapping.xml' />" +
"</jbpm-configuration>"
);
}
public void setUp() {
jbpmConfiguration.createSchema();
}
public void tearDown() {
jbpmConfiguration.dropSchema();
}
public void testDelete() {
deployProcessDefinition();
startProcess();
step2TaskEnd();
deleteProcess();
}
public void deployProcessDefinition() {
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString
(
"<process-definition name='deletetest'>" +
" <start-state name='start'> " +
" <task name='startTask'> " +
" <controller> " +
" <variable name='var1' access='write'/> " +
" </controller> " +
" </task> " +
" <transition name='' to='step2'/> " +
" </start-state> " +
" <task-node name='step2'> " +
" <task name='step2Task'/> " +
" <transition name='' to='step3'/> " +
" </task-node>" +
" <task-node name='step3'> " +
" <task name='step3Task'/> " +
" <transition name='' to='end'/> " +
" </task-node> " +
" <end-state name='end' />" +
"</process-definition>"
);
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
jbpmContext.deployProcessDefinition(processDefinition);
} finally {
jbpmContext.close();
}
}
public void startProcess() {
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
GraphSession graphSession = jbpmContext.getGraphSession();
ProcessDefinition processDefinition = graphSession.findLatestProcessDefinition("deletetest");
ProcessInstance processInstance = new ProcessInstance(processDefinition);
processId = processInstance.getId();
TaskInstance taskInstance = processInstance.getTaskMgmtInstance().createStartTaskInstance();
taskInstance.setVariableLocally("var1", "var1Value");
taskInstance.end();
Token token = taskInstance.getToken();
currentTokenPath = token.getFullName();
jbpmContext.save(processInstance);
} finally {
jbpmContext.close();
}
}
public void step2TaskEnd() {
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
GraphSession graphSession = jbpmContext.getGraphSession();
ProcessInstance processInstance = graphSession.loadProcessInstance(processId);
Token token = processInstance.findToken(currentTokenPath);
TaskMgmtSession taskSession = jbpmContext.getTaskMgmtSession();
List tasks = taskSession.findTaskInstancesByToken(token.getId());
TaskInstance taskInstance = (TaskInstance)tasks.get(0);
//
// Uncomment the following line to force constraint violation
//
// taskInstance.setVariableLocally("var1", "var1TaskValue");
taskInstance.setVariableLocally("var2", "var2UpdatedValue");
taskInstance.end();
token = taskInstance.getToken();
currentTokenPath = token.getFullName();
jbpmContext.save(processInstance);
} finally {
jbpmContext.close();
}
}
public void deleteProcess()
{
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
GraphSession graphSession = jbpmContext.getGraphSession();
ProcessInstance processInstance = graphSession.loadProcessInstance(processId);
graphSession.deleteProcessInstance(processInstance, true, true, true);
} finally {
jbpmContext.close();
}
}
}

View File

@@ -59,6 +59,7 @@ import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.cmr.workflow.WorkflowTransition;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.hibernate.Query;
@@ -259,8 +260,7 @@ public class JBPMEngine extends BPMEngine
{
// retrieve process definition
GraphSession graphSession = context.getGraphSession();
ProcessDefinition processDefinition = graphSession.loadProcessDefinition(getJbpmId(workflowDefinitionId));
// NOTE: if not found, should throw an exception
ProcessDefinition processDefinition = getProcessDefinition(graphSession, workflowDefinitionId);
// undeploy
// NOTE: jBPM deletes all "in-flight" processes too
@@ -331,7 +331,7 @@ public class JBPMEngine extends BPMEngine
{
GraphSession graphSession = context.getGraphSession();
ProcessDefinition processDef = graphSession.findLatestProcessDefinition(createLocalId(workflowName));
return createWorkflowDefinition(processDef);
return processDef == null ? null : createWorkflowDefinition(processDef);
}
});
}
@@ -341,6 +341,23 @@ public class JBPMEngine extends BPMEngine
}
}
/**
* Gets a jBPM process definition
*
* @param graphSession jBPM graph session
* @param workflowDefinitionId workflow definition id
* @return process definition
*/
private ProcessDefinition getProcessDefinition(GraphSession graphSession, String workflowDefinitionId)
{
ProcessDefinition processDefinition = graphSession.getProcessDefinition(getJbpmId(workflowDefinitionId));
if (processDefinition == null)
{
throw new WorkflowException("Workflow definition '" + workflowDefinitionId + "' does not exist");
}
return processDefinition;
}
//
// Workflow Instance Management...
@@ -366,7 +383,7 @@ public class JBPMEngine extends BPMEngine
// construct a new process
GraphSession graphSession = context.getGraphSession();
ProcessDefinition processDefinition = graphSession.loadProcessDefinition(getJbpmId(workflowDefinitionId));
ProcessDefinition processDefinition = getProcessDefinition(graphSession, workflowDefinitionId);
ProcessInstance processInstance = new ProcessInstance(processDefinition);
// assign initial process context
@@ -398,7 +415,7 @@ public class JBPMEngine extends BPMEngine
throw new WorkflowException("Failed to start workflow " + workflowDefinitionId, e);
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#getActiveWorkflows(java.lang.String)
*/
@@ -432,6 +449,46 @@ public class JBPMEngine extends BPMEngine
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#getWorkflowById(java.lang.String)
*/
public WorkflowInstance getWorkflowById(final String workflowId)
{
try
{
return (WorkflowInstance) jbpmTemplate.execute(new JbpmCallback()
{
public Object doInJbpm(JbpmContext context)
{
// retrieve workflow
GraphSession graphSession = context.getGraphSession();
ProcessInstance processInstance = graphSession.getProcessInstance(getJbpmId(workflowId));
return processInstance == null ? null : createWorkflowInstance(processInstance);
}
});
}
catch(JbpmException e)
{
throw new WorkflowException("Failed to retrieve workflow instance '" + workflowId + "'", e);
}
}
/**
* Gets a jBPM Process Instance
* @param graphSession jBPM graph session
* @param workflowId workflow id
* @return process instance
*/
private ProcessInstance getProcessInstance(GraphSession graphSession, String workflowId)
{
ProcessInstance processInstance = graphSession.getProcessInstance(getJbpmId(workflowId));
if (processInstance == null)
{
throw new WorkflowException("Workflow instance '" + workflowId + "' does not exist");
}
return processInstance;
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#getWorkflowPaths(java.lang.String)
*/
@@ -446,7 +503,7 @@ public class JBPMEngine extends BPMEngine
{
// retrieve process instance
GraphSession graphSession = context.getGraphSession();
ProcessInstance processInstance = graphSession.loadProcessInstance(getJbpmId(workflowId));
ProcessInstance processInstance = getProcessInstance(graphSession, workflowId);
// convert jBPM tokens to workflow posisitons
List<Token> tokens = processInstance.findAllTokens();
@@ -479,16 +536,21 @@ public class JBPMEngine extends BPMEngine
{
return (WorkflowInstance) jbpmTemplate.execute(new JbpmCallback()
{
@SuppressWarnings("unchecked")
public Object doInJbpm(JbpmContext context)
{
// retrieve and cancel process instance
GraphSession graphSession = context.getGraphSession();
ProcessInstance processInstance = graphSession.loadProcessInstance(getJbpmId(workflowId));
processInstance.end();
// save the process instance along with the task instance
context.save(processInstance);
return createWorkflowInstance(processInstance);
ProcessInstance processInstance = getProcessInstance(graphSession, workflowId);
// TODO: Determine if this is the most appropriate way to cancel workflow...
// It might be useful to record point at which it was cancelled etc
WorkflowInstance workflowInstance = createWorkflowInstance(processInstance);
// delete the process instance
graphSession.deleteProcessInstance(processInstance, true, true, true);
workflowInstance.active = false;
workflowInstance.endDate = new Date();
return workflowInstance;
}
});
}
@@ -681,6 +743,22 @@ public class JBPMEngine extends BPMEngine
}
}
/**
* Gets a jBPM Task Instance
* @param taskSession jBPM task session
* @param taskId task id
* @return task instance
*/
private TaskInstance getTaskInstance(TaskMgmtSession taskSession, String taskId)
{
TaskInstance taskInstance = taskSession.getTaskInstance(getJbpmId(taskId));
if (taskInstance == null)
{
throw new WorkflowException("Task instance '" + taskId + "' does not exist");
}
return taskInstance;
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.TaskComponent#updateTask(java.lang.String, java.util.Map, java.util.Map, java.util.Map)
*/
@@ -694,7 +772,7 @@ public class JBPMEngine extends BPMEngine
{
// retrieve task
TaskMgmtSession taskSession = context.getTaskMgmtSession();
TaskInstance taskInstance = taskSession.loadTaskInstance(getJbpmId(taskId));
TaskInstance taskInstance = getTaskInstance(taskSession, taskId);
// create properties to set on task instance
Map<QName, Serializable> newProperties = properties;
@@ -705,7 +783,7 @@ public class JBPMEngine extends BPMEngine
if (add != null || remove != null)
{
Map<QName, Serializable> existingProperties = getTaskProperties(taskInstance);
Map<QName, Serializable> existingProperties = getTaskProperties(taskInstance, false);
if (add != null)
{
@@ -814,8 +892,8 @@ public class JBPMEngine extends BPMEngine
{
// retrieve task
TaskMgmtSession taskSession = context.getTaskMgmtSession();
TaskInstance taskInstance = taskSession.loadTaskInstance(getJbpmId(taskId));
TaskInstance taskInstance = getTaskInstance(taskSession, taskId);
// signal the transition on the task
if (transition == null)
{
@@ -860,8 +938,8 @@ public class JBPMEngine extends BPMEngine
{
// retrieve task
TaskMgmtSession taskSession = context.getTaskMgmtSession();
TaskInstance taskInstance = taskSession.loadTaskInstance(getJbpmId(taskId));
return createWorkflowTask(taskInstance);
TaskInstance taskInstance = taskSession.getTaskInstance(getJbpmId(taskId));
return taskInstance == null ? null : createWorkflowTask(taskInstance);
}
});
}
@@ -992,7 +1070,7 @@ public class JBPMEngine extends BPMEngine
TypeDefinition typeDef = dictionaryService.getType(typeName);
if (typeDef == null)
{
typeDef = dictionaryService.getType(WorkflowModel.TYPE_WORKFLOW_TASK);
typeDef = dictionaryService.getType(task.getStartState() == null ? WorkflowModel.TYPE_WORKFLOW_TASK : WorkflowModel.TYPE_START_TASK);
if (typeDef == null)
{
throw new WorkflowException("Failed to find type definition '" + WorkflowModel.TYPE_WORKFLOW_TASK + "'");
@@ -1072,7 +1150,7 @@ public class JBPMEngine extends BPMEngine
}
// retrieve jBPM token for workflow position
ProcessInstance processInstance = session.loadProcessInstance(getJbpmId(path[0]));
ProcessInstance processInstance = getProcessInstance(session, path[0]);
String tokenId = path[1].replace(WORKFLOW_TOKEN_SEPERATOR, "/");
Token token = processInstance.findToken(tokenId);
if (token == null)
@@ -1090,43 +1168,55 @@ public class JBPMEngine extends BPMEngine
* @param properties properties to set
*/
@SuppressWarnings("unchecked")
protected Map<QName, Serializable> getTaskProperties(TaskInstance instance)
protected Map<QName, Serializable> getTaskProperties(TaskInstance instance, boolean localProperties)
{
// retrieve type definition for task
TypeDefinition taskDef = getAnonymousTaskDefinition(getTaskDefinition(instance.getTask()));
Map<QName, PropertyDefinition> taskProperties = taskDef.getProperties();
Map<QName, AssociationDefinition> taskAssocs = taskDef.getAssociations();
// map arbitrary task variables
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(10);
Map<String, Object> vars = instance.getVariablesLocally();
Map<String, Object> vars = (localProperties ? instance.getVariablesLocally() : instance.getVariables());
for (Entry<String, Object> entry : vars.entrySet())
{
String key = entry.getKey();
Object value = entry.getValue();
QName qname = mapNameToQName(key);
//
// perform data conversions
//
// Convert Nodes to NodeRefs
if (value instanceof JBPMNode)
// add variable, only if part of task definition or locally defined on task
if (taskProperties.containsKey(qname) || taskAssocs.containsKey(qname) || instance.hasVariableLocally(key))
{
value = ((JBPMNode)value).getNodeRef();
Object value = entry.getValue();
//
// perform data conversions
//
// Convert Nodes to NodeRefs
if (value instanceof JBPMNode)
{
value = ((JBPMNode)value).getNodeRef();
}
else if (value instanceof JBPMNodeList)
{
JBPMNodeList nodes = (JBPMNodeList)value;
List<NodeRef> nodeRefs = new ArrayList<NodeRef>(nodes.size());
for (JBPMNode node : nodes)
{
nodeRefs.add(node.getNodeRef());
}
value = (Serializable)nodeRefs;
}
// place task variable in map to return
properties.put(qname, (Serializable)value);
}
else if (value instanceof JBPMNodeList)
{
JBPMNodeList nodes = (JBPMNodeList)value;
List<NodeRef> nodeRefs = new ArrayList<NodeRef>(nodes.size());
for (JBPMNode node : nodes)
{
nodeRefs.add(node.getNodeRef());
}
value = (Serializable)nodeRefs;
}
// place task variable in map to return
QName qname = QName.createQName(key, this.namespaceService);
properties.put(qname, (Serializable)value);
}
// map jBPM task instance fields to properties
properties.put(WorkflowModel.PROP_TASK_ID, instance.getId());
properties.put(WorkflowModel.PROP_DESCRIPTION, instance.getDescription());
properties.put(WorkflowModel.PROP_START_DATE, instance.getStart());
properties.put(WorkflowModel.PROP_DUE_DATE, instance.getDueDate());
properties.put(WorkflowModel.PROP_COMPLETION_DATE, instance.getEnd());
@@ -1207,6 +1297,15 @@ public class JBPMEngine extends BPMEngine
}
// map property to specific jBPM task instance field
if (key.equals(WorkflowModel.PROP_DESCRIPTION))
{
if (value != null && !(value instanceof String))
{
throw new WorkflowException("Task description '" + value + "' is invalid");
}
instance.setDescription((String)value);
continue;
}
if (key.equals(WorkflowModel.PROP_DUE_DATE))
{
if (value != null && !(value instanceof Date))
@@ -1269,6 +1368,39 @@ public class JBPMEngine extends BPMEngine
instance.setPooledActors(pooledActors);
continue;
}
else if (key.equals(WorkflowModel.ASSOC_PACKAGE))
{
// Attach workflow definition & instance id to Workflow Package in Repository
String name = mapQNameToName(key);
JBPMNode existingWorkflowPackage = (JBPMNode)instance.getVariable(name);
// first check if provided workflow package has already been associated with another workflow instance
if (existingWorkflowPackage != null && value != null)
{
NodeRef newPackageNodeRef = ((JBPMNode)value).getNodeRef();
ProcessInstance processInstance = instance.getToken().getProcessInstance();
String packageInstanceId = (String)nodeService.getProperty(newPackageNodeRef, WorkflowModel.PROP_WORKFLOW_INSTANCE_ID);
if (packageInstanceId != null && packageInstanceId.length() > 0 && (processInstance.getId() == getJbpmId(packageInstanceId)))
{
String workflowInstanceId = createGlobalId(new Long(processInstance.getId()).toString());
throw new WorkflowException("Cannot associate workflow package '" + newPackageNodeRef + "' with workflow instance '" + workflowInstanceId + "' as it's already associated with workflow instance '" + packageInstanceId + "'");
}
}
// initialise workflow package
if (existingWorkflowPackage == null && value != null)
{
// initialise workflow package
NodeRef newPackageNodeRef = ((JBPMNode)value).getNodeRef();
ProcessInstance processInstance = instance.getToken().getProcessInstance();
WorkflowInstance workflowInstance = createWorkflowInstance(processInstance);
nodeService.setProperty(newPackageNodeRef, WorkflowModel.PROP_WORKFLOW_DEFINITION_ID, workflowInstance.definition.id);
nodeService.setProperty(newPackageNodeRef, WorkflowModel.PROP_WORKFLOW_DEFINITION_NAME, workflowInstance.definition.name);
nodeService.setProperty(newPackageNodeRef, WorkflowModel.PROP_WORKFLOW_INSTANCE_ID, workflowInstance.id);
}
// NOTE: Fall-through to allow setting of Workflow Package on Task Instance
}
}
// untyped value, perform minimal conversion
@@ -1283,7 +1415,7 @@ public class JBPMEngine extends BPMEngine
// no specific mapping to jBPM task has been established, so place into
// the generic task variable bag
String name = key.toPrefixString(this.namespaceService);
String name = mapQNameToName(key);
instance.setVariableLocally(name, value);
}
}
@@ -1295,7 +1427,7 @@ public class JBPMEngine extends BPMEngine
*/
protected void setDefaultTaskProperties(TaskInstance instance)
{
Map<QName, Serializable> existingValues = null;
Map<QName, Serializable> existingValues = getTaskProperties(instance, true);
Map<QName, Serializable> defaultValues = new HashMap<QName, Serializable>();
// construct an anonymous type that flattens all mandatory aspects
@@ -1308,10 +1440,6 @@ public class JBPMEngine extends BPMEngine
String defaultValue = entry.getValue().getDefaultValue();
if (defaultValue != null)
{
if (existingValues == null)
{
existingValues = getTaskProperties(instance);
}
if (existingValues.get(entry.getKey()) == null)
{
defaultValues.put(entry.getKey(), defaultValue);
@@ -1319,6 +1447,22 @@ public class JBPMEngine extends BPMEngine
}
}
// special case for task description default value
String description = (String)existingValues.get(WorkflowModel.PROP_DESCRIPTION);
if (description == null || description.length() == 0)
{
description = (String)instance.getContextInstance().getVariable(mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION));
if (description != null && description.length() > 0)
{
defaultValues.put(WorkflowModel.PROP_DESCRIPTION, description);
}
else
{
WorkflowTask task = createWorkflowTask(instance);
defaultValues.put(WorkflowModel.PROP_DESCRIPTION, task.title);
}
}
// assign the default values to the task
if (defaultValues.size() > 0)
{
@@ -1327,16 +1471,62 @@ public class JBPMEngine extends BPMEngine
}
/**
* Set Task Outcome based on specified Transition
* Sets default description for the Task
*
* @param instance task instance
* @param transition transition
*/
protected void setTaskOutcome(TaskInstance instance, Transition transition)
public void setDefaultStartTaskDescription(TaskInstance instance)
{
Map<QName, Serializable> outcome = new HashMap<QName, Serializable>();
outcome.put(WorkflowModel.PROP_OUTCOME, transition.getName());
setTaskProperties(instance, outcome);
String description = instance.getTask().getDescription();
if (description == null || description.length() == 0)
{
description = (String)instance.getContextInstance().getVariable(mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION));
if (description != null && description.length() > 0)
{
Map<QName, Serializable> defaultValues = new HashMap<QName, Serializable>();
defaultValues.put(WorkflowModel.PROP_DESCRIPTION, description);
setTaskProperties(instance, defaultValues);
}
}
}
/**
* Initialise Workflow Instance properties
*
* @param startTask start task instance
*/
protected void setDefaultWorkflowProperties(TaskInstance startTask)
{
Map<QName, Serializable> taskProperties = getTaskProperties(startTask, true);
ContextInstance processContext = startTask.getContextInstance();
String workflowDescriptionName = mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION);
if (!processContext.hasVariable(workflowDescriptionName))
{
processContext.setVariable(workflowDescriptionName, taskProperties.get(WorkflowModel.PROP_WORKFLOW_DESCRIPTION));
}
String workflowDueDateName = mapQNameToName(WorkflowModel.PROP_WORKFLOW_DUE_DATE);
if (!processContext.hasVariable(workflowDueDateName))
{
processContext.setVariable(workflowDueDateName, taskProperties.get(WorkflowModel.PROP_WORKFLOW_DUE_DATE));
}
String workflowPriorityName = mapQNameToName(WorkflowModel.PROP_WORKFLOW_PRIORITY);
if (!processContext.hasVariable(workflowPriorityName))
{
processContext.setVariable(workflowPriorityName, taskProperties.get(WorkflowModel.PROP_WORKFLOW_PRIORITY));
}
String workflowPackageName = mapQNameToName(WorkflowModel.ASSOC_PACKAGE);
if (!processContext.hasVariable(workflowPackageName))
{
Serializable packageNodeRef = taskProperties.get(WorkflowModel.ASSOC_PACKAGE);
processContext.setVariable(workflowPackageName, convertNodeRefs(packageNodeRef instanceof List, packageNodeRef));
}
String workflowContextName = mapQNameToName(WorkflowModel.PROP_CONTEXT);
if (!processContext.hasVariable(workflowContextName))
{
Serializable contextRef = taskProperties.get(WorkflowModel.PROP_CONTEXT);
processContext.setVariable(workflowContextName, convertNodeRefs(contextRef instanceof List, contextRef));
}
}
/**
@@ -1403,6 +1593,39 @@ public class JBPMEngine extends BPMEngine
return authority;
}
/**
* Map jBPM variable name to QName
*
* @param name jBPM variable name
* @return qname
*/
private QName mapNameToQName(String name)
{
QName qname = null;
String qnameStr = name.replaceFirst("_", ":");
try
{
qname = QName.createQName(qnameStr, this.namespaceService);
}
catch(NamespaceException e)
{
qname = QName.createQName(name, this.namespaceService);
}
return qname;
}
/**
* Map QName to jBPM variable name
*
* @param name QName
* @return jBPM variable name
*/
private String mapQNameToName(QName name)
{
String nameStr = name.toPrefixString(this.namespaceService);
return nameStr.replace(':', '_');
}
/**
* Get an I18N Label for a workflow item
*
@@ -1516,6 +1739,7 @@ public class JBPMEngine extends BPMEngine
{
WorkflowInstance workflowInstance = new WorkflowInstance();
workflowInstance.id = createGlobalId(new Long(instance.getId()).toString());
workflowInstance.description = (String)instance.getContextInstance().getVariable(mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION));
workflowInstance.definition = createWorkflowDefinition(instance.getProcessDefinition());
workflowInstance.active = !instance.hasEnded();
JBPMNode initiator = (JBPMNode)instance.getContextInstance().getVariable("initiator");
@@ -1523,6 +1747,16 @@ public class JBPMEngine extends BPMEngine
{
workflowInstance.initiator = initiator.getNodeRef();
}
JBPMNode context = (JBPMNode)instance.getContextInstance().getVariable(mapQNameToName(WorkflowModel.PROP_CONTEXT));
if (context != null)
{
workflowInstance.context = context.getNodeRef();
}
JBPMNode workflowPackage = (JBPMNode)instance.getContextInstance().getVariable(mapQNameToName(WorkflowModel.ASSOC_PACKAGE));
if (workflowPackage != null)
{
workflowInstance.workflowPackage = workflowPackage.getNodeRef();
}
workflowInstance.startDate = instance.getStart();
workflowInstance.endDate = instance.getEnd();
return workflowInstance;
@@ -1566,7 +1800,7 @@ public class JBPMEngine extends BPMEngine
workflowTask.path = createWorkflowPath(task.getToken());
workflowTask.state = getWorkflowTaskState(task);
workflowTask.definition = createWorkflowTaskDefinition(task.getTask());
workflowTask.properties = getTaskProperties(task);
workflowTask.properties = getTaskProperties(task, false);
workflowTask.title = getLabel(processName + ".task." + workflowTask.name, TITLE_LABEL, null);
if (workflowTask.title == null)
{

View File

@@ -152,6 +152,21 @@ public class JBPMEngineTest extends BaseSpringTest
}
public void testGetWorkflowInstance()
{
WorkflowDefinition workflowDef = getTestDefinition();
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, null);
assertNotNull(path);
assertTrue(path.id.endsWith("-@"));
assertNotNull(path.node);
assertNotNull(path.instance);
assertEquals(workflowDef.id, path.instance.definition.id);
WorkflowInstance instance = workflowComponent.getWorkflowById(path.instance.id);
assertNotNull(instance);
assertEquals(path.instance.id, instance.id);
}
public void testStartWorkflowParameters()
{
WorkflowDefinition workflowDef = getTestDefinition();
@@ -303,20 +318,27 @@ public class JBPMEngineTest extends BaseSpringTest
public void testCancelWorkflowInstance()
{
WorkflowDefinition workflowDef = getTestDefinition();
WorkflowDefinition workflowDef = getTestDefinition();
workflowComponent.startWorkflow(workflowDef.id, null);
List<WorkflowInstance> instances1 = workflowComponent.getActiveWorkflows(workflowDef.id);
assertNotNull(instances1);
assertEquals(1, instances1.size());
List<WorkflowTask> tasks = taskComponent.getAssignedTasks("admin", WorkflowTaskState.IN_PROGRESS);
assertNotNull(tasks);
assertTrue(tasks.size() > 0);
WorkflowInstance cancelledInstance = workflowComponent.cancelWorkflow(instances1.get(0).id);
assertNotNull(cancelledInstance);
assertFalse(cancelledInstance.active);
List<WorkflowInstance> instances2 = workflowComponent.getActiveWorkflows(workflowDef.id);
assertNotNull(instances2);
assertEquals(0, instances2.size());
List<WorkflowTask> tasks1 = taskComponent.getAssignedTasks("admin", WorkflowTaskState.IN_PROGRESS);
assertNotNull(tasks1);
tasks1 = filterTasksByWorkflowInstance(tasks1, cancelledInstance.id);
assertEquals(0, tasks1.size());
}
public void testSignal()
{
WorkflowDefinition workflowDef = getTestDefinition();
@@ -335,7 +357,6 @@ public class JBPMEngineTest extends BaseSpringTest
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
assertNotNull(path);
assertNotNull(path);
List<WorkflowTask> tasks = workflowComponent.getTasksForWorkflowPath(path.id);
assertNotNull(tasks);
assertEquals(1, tasks.size());
@@ -361,7 +382,6 @@ public class JBPMEngineTest extends BaseSpringTest
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
assertNotNull(path);
assertNotNull(path);
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
assertNotNull(tasks1);
assertEquals(1, tasks1.size());

View File

@@ -0,0 +1,225 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.workflow.jbpm;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListener;
import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.graph.def.ProcessDefinition;
import org.springmodules.workflow.jbpm31.JbpmTemplate;
/**
* JBPM Template that manages JBPM Context at the Alfresco Transaction level
*
* @author davidc
*/
public class JBPMTransactionTemplate extends JbpmTemplate
implements TransactionListener
{
// Logging support
private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow");
/** Id used in equals and hash */
private String id = GUID.generate();
// JBPM Template Keys
private static final String JBPM_CONTEXT_KEY = JBPMTransactionTemplate.class.getName() + ".context";
/** Use local or transaction bound JBPM Context */
private boolean localContext = true;
/*
* Constructor
*/
public JBPMTransactionTemplate()
{
super();
}
/*
* Constructor
*/
public JBPMTransactionTemplate(JbpmConfiguration jbpmConfiguration, ProcessDefinition processDefinition)
{
super(jbpmConfiguration, processDefinition);
}
/*
* Constructor
*/
public JBPMTransactionTemplate(JbpmConfiguration jbpmConfiguration)
{
super(jbpmConfiguration);
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception
{
try
{
JBPMTransactionTemplate.super.afterPropertiesSet();
}
finally
{
localContext = false;
}
}
/* (non-Javadoc)
* @see org.springmodules.workflow.jbpm31.JbpmTemplate#getContext()
*/
@Override
protected JbpmContext getContext()
{
if (localContext)
{
return super.getContext();
}
else
{
JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY);
if (context == null)
{
context = super.getContext();
AlfrescoTransactionSupport.bindResource(JBPM_CONTEXT_KEY, context);
AlfrescoTransactionSupport.bindListener(this);
if (logger.isDebugEnabled())
logger.debug("Attached JBPM Context to transaction " + AlfrescoTransactionSupport.getTransactionId());
}
return context;
}
}
/* (non-Javadoc)
* @see org.springmodules.workflow.jbpm31.JbpmTemplate#releaseContext(org.jbpm.JbpmContext)
*/
@Override
protected void releaseContext(JbpmContext jbpmContext)
{
if (localContext)
{
jbpmContext.close();
}
else
{
// NOTE: Defer release to end of transaction
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListener#flush()
*/
public void flush()
{
}
/* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListener#beforeCommit(boolean)
*/
public void beforeCommit(boolean readOnly)
{
}
/* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListener#beforeCompletion()
*/
public void beforeCompletion()
{
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListener#afterCommit()
*/
public void afterCommit()
{
JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY);
if (context != null)
{
super.releaseContext(context);
if (logger.isDebugEnabled())
logger.debug("Detached (commit) JBPM Context from transaction " + AlfrescoTransactionSupport.getTransactionId());
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListener#afterRollback()
*/
public void afterRollback()
{
JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY);
if (context != null)
{
super.releaseContext(context);
if (logger.isDebugEnabled())
logger.debug("Detached (rollback) JBPM Context from transaction " + AlfrescoTransactionSupport.getTransactionId());
}
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
return this.id.hashCode();
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj instanceof JBPMTransactionTemplate)
{
JBPMTransactionTemplate that = (JBPMTransactionTemplate) obj;
return (this.id.equals(that.id));
}
else
{
return false;
}
}
}

View File

@@ -69,7 +69,7 @@ public class NodeListConverterTest extends BaseSpringTest
taskComponent = registry.getTaskComponent("jbpm");
// deploy latest review and approve process definition
ClassPathResource processDef = new ClassPathResource("org/alfresco/repo/workflow/jbpm/review_and_approve_processdefinition.xml");
ClassPathResource processDef = new ClassPathResource("alfresco/workflow/review_processdefinition.xml");
WorkflowDeployment deployment = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML);
testWorkflowDef = deployment.definition;
assertNotNull(testWorkflowDef);

View File

@@ -69,7 +69,7 @@ public class ReviewAndApproveTest extends BaseSpringTest
taskComponent = registry.getTaskComponent("jbpm");
// deploy latest review and approve process definition
ClassPathResource processDef = new ClassPathResource("org/alfresco/repo/workflow/jbpm/review_and_approve_processdefinition.xml");
ClassPathResource processDef = new ClassPathResource("alfresco/workflow/review_processdefinition.xml");
WorkflowDeployment deployment = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML);
testWorkflowDef = deployment.definition;
assertNotNull(testWorkflowDef);
@@ -97,9 +97,10 @@ public class ReviewAndApproveTest extends BaseSpringTest
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(WorkflowModel.ASSOC_PACKAGE, testNodeRef);
Date reviewDueDate = new Date();
params.put(QName.createQName("http://www.alfresco.org/model/workflow/1.0", "reviewDueDate"), reviewDueDate);
params.put(WorkflowModel.PROP_WORKFLOW_DUE_DATE, reviewDueDate);
NodeRef reviewer = personService.getPerson("admin");
params.put(QName.createQName("http://www.alfresco.org/model/workflow/1.0", "reviewer"), reviewer);
params.put(WorkflowModel.ASSOC_ASSIGNEE, reviewer);
params.put(WorkflowModel.PROP_WORKFLOW_DESCRIPTION, "Test review");
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, params);
assertNotNull(path);
@@ -113,6 +114,8 @@ public class ReviewAndApproveTest extends BaseSpringTest
assertNotNull(endedTask);
assertTrue(endedTask.properties.containsKey(WorkflowModel.PROP_OUTCOME));
assertEquals("", endedTask.properties.get(WorkflowModel.PROP_OUTCOME));
assertEquals("Test review", endedTask.properties.get(WorkflowModel.PROP_DESCRIPTION));
assertEquals("Test review", endedTask.path.instance.description);
List<WorkflowTask> assignedTasks = taskComponent.getAssignedTasks("admin", WorkflowTaskState.IN_PROGRESS);
assertNotNull(assignedTasks);
@@ -134,9 +137,9 @@ public class ReviewAndApproveTest extends BaseSpringTest
params.put(WorkflowModel.ASSOC_PACKAGE, testNodeRef);
params.put(WorkflowModel.PROP_COMPLETED_ITEMS, (Serializable)nodeRefs);
Date reviewDueDate = new Date();
params.put(QName.createQName("http://www.alfresco.org/model/workflow/1.0", "reviewDueDate"), reviewDueDate);
params.put(WorkflowModel.PROP_WORKFLOW_DUE_DATE, reviewDueDate);
NodeRef reviewer = personService.getPerson("admin");
params.put(QName.createQName("http://www.alfresco.org/model/workflow/1.0", "reviewer"), reviewer);
params.put(WorkflowModel.ASSOC_ASSIGNEE, reviewer);
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, params);
assertNotNull(path);

View File

@@ -16,7 +16,13 @@
*/
package org.alfresco.repo.workflow.jbpm;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.alfresco.service.namespace.QName;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.taskmgmt.exe.TaskInstance;
@@ -96,14 +102,30 @@ public class WorkflowTaskInstance extends TaskInstance
@Override
public void end(Transition transition)
{
// NOTE: Set the outcome first, so it's available during the submission of
// Set task properties on completion of task
// NOTE: Set properties first, so they're available during the submission of
// task variables to the process context
Map<QName, Serializable> taskProperties = new HashMap<QName, Serializable>();
Transition outcome = (transition == null) ? token.getNode().getDefaultLeavingTransition() : transition;
if (outcome != null)
{
getJBPMEngine().setTaskOutcome(this, outcome);
taskProperties.put(WorkflowModel.PROP_OUTCOME, outcome.getName());
}
taskProperties.put(WorkflowModel.PROP_STATUS, "Completed");
getJBPMEngine().setTaskProperties(this, taskProperties);
// perform transition
super.end(transition);
if (getTask().getStartState() != null)
{
// if ending a start task, push start task properties to process context, if not
// already done
getJBPMEngine().setDefaultWorkflowProperties(this);
// set task description
getJBPMEngine().setDefaultStartTaskDescription(this);
}
}
}

View File

@@ -1,57 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="test_script">
<swimlane name="initiator"></swimlane>
<swimlane name="initiator"></swimlane>
<start-state name="start">
<task name="submit" swimlane="initiator">
<event type="task-assign">
<start-state name="start">
<task name="submit" swimlane="initiator">
<event type="task-assign">
<script>
System.out.println("taskInstance.create: " + taskInstance.create);
System.out.println("taskInstance.description: " + taskInstance.description);
</script>
</event>
<controller>
<variable name="testNode" access="write,required" />
<variable name="bpm_workflowDescription" access="write" />
</controller>
</task>
<transition name="" to="doit" />
</start-state>
<node name="doit">
<event type="node-enter">
<script>
System.out.println("taskInstance.create: " + taskInstance.create);
<expression>
System.out.println("testNode.created: " + testNode.properties{"cm:created"});
System.out.println("test node " + testNode.name + " contains " + testNode.children.length + " children");
</expression>
<variable name="testNode" access="read" />
</script>
</event>
<controller>
<variable name="testNode" access="write,required" />
</controller>
</task>
<transition name="" to="doit"/>
</start-state>
<node name="doit">
<event type="node-enter">
<script>
<expression>
System.out.println("testNode.created: " + testNode.properties{"cm:created"});
System.out.println("test node " + testNode.name + " contains " + testNode.children.length + " children");
</expression>
<variable name="testNode" access="read"/>
</script>
</event>
<event type="node-enter">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
<!-- following line fails as it attempts to convert properties of children to javascript objects -->
<!-- except the beanshell line above has already pre-created the children without the javascript scope -->
<!-- object -->
<!-- var result = "testNode.created: " + testNode.properties["cm:created"] + ", testNode.children.length: " + testNode.children[0].properties["cm:name"]; -->
<expression>
var result = "testNode.created: " + testNode.properties["cm:created"] + ", testNode.children.length: " + testNode.children.length;
result;
</expression>
<variable name="testNode" access="read"/>
<variable name="alfrescoScriptResult" access="write"/>
</script>
</action>
</event>
<transition name="" to="end"/>
</node>
<end-state name="end">
<event type="node-enter">
<script>
System.out.println("javascript: " + alfrescoScriptResult);
</script>
</event>
</end-state>
</event>
<event type="node-enter">
<action
class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
<!-- following line fails as it attempts to convert properties of children to javascript objects -->
<!-- except the beanshell line above has already pre-created the children without the javascript scope -->
<!-- object -->
<!-- var result = "testNode.created: " + testNode.properties["cm:created"] + ", testNode.children.length: " + testNode.children[0].properties["cm:name"]; -->
<expression>
var result = "testNode.created: " + theTestNode.properties["cm:created"] + ", theTestNode.children.length: " + theTestNode.children.length;
if (logger.isLoggingEnabled())
{
logger.log(result);
}
result;
</expression>
<variable name="testNode" access="read" mapped-name="theTestNode" />
<variable name="alfrescoScriptResult" access="write" />
</script>
</action>
</event>
<transition name="" to="end" />
</node>
<end-state name="end">
<event type="node-enter">
<script>
System.out.println("javascript: " + alfrescoScriptResult);
System.out.println("bpm_workflowDescription: " + bpm_workflowDescription);
</script>
</event>
</end-state>
</process-definition>