Merged HEAD-QA to HEAD (4.2) (including moving test classes into separate folders)

51903 to 54309 


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@54310 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Samuel Langlois
2013-08-20 17:17:31 +00:00
parent 0a36e2af67
commit ab4ca7177f
1576 changed files with 36419 additions and 8603 deletions

View File

@@ -1,428 +0,0 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authority.AuthorityDAO;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.repo.workflow.activiti.variable.ScriptNodeVariableType;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowDeployment;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Nick Smith
* @author Frederik Heremans
* @since 3.4.e
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:test-database-context.xml",
"classpath:activiti/test-activiti-component-context.xml",
"classpath:alfresco/activiti-context.xml"})
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
@Transactional
public class AbstractActivitiComponentTest
{
protected static final String TEST_GROUP = "GROUP_testGroup";
protected static final String TEST_USER = "testUser";
protected static final String TEST_TASK_DEF = "activiti/testTransaction.bpmn20.xml";
protected static final String TEST_TASK_KEY = "testTask";
protected static final String TEST_ADHOC_DEF = "activiti/testAdhoc.bpmn20.xml";
protected static final String TEST_SIGNALLING_DEF = "activiti/testSignalling.bpmn20.xml";
protected static final String TEST_REVIEW_DEF = "activiti/testReview.bpmn20.xml";
protected static final String TEST_ADHOC_KEY = "testAdhoc";
protected static final String TEST_JOB_KEY = "testAdhoc";
protected static final String TEST_JOB_DEF = "activiti/testJob.bpmn20.xml";
protected static final String TEST_DIAGRAM_DEF = "activiti/testDiagram.bpmn20.xml";
protected static final String XML = MimetypeMap.MIMETYPE_XML;
@Autowired
protected ProcessEngine processEngine;
@Resource(name="activitiWorkflowEngine")
protected ActivitiWorkflowEngine workflowEngine;
@Resource(name="activitiRuntimeService")
protected RuntimeService runtime;
@Resource(name="activitiRepositoryService")
protected RepositoryService repo;
@Resource(name="activitiTaskService")
protected TaskService taskService;
@Resource(name="activitiHistoryService")
protected HistoryService historyService;
@Resource(name="activitiManagementService")
protected ManagementService managementService;
@Resource
protected MessageService messageService;
@Resource(name="NamespaceService")
protected NamespaceService namespaceService;
@Resource(name="DictionaryService")
protected DictionaryService dictionaryService;
@Resource(name="NodeService")
protected NodeService nodeService;
@Resource(name="searchService")
protected SearchService unprotectedSearchService;
@Resource
protected PermissionService permissionService;
@Resource(name="PersonService")
protected PersonService personService;
@Resource
protected AuthorityDAO authorityDAO;
@Resource
protected ServiceRegistry serviceRegistry;
protected static final NodeRef rootNode = new NodeRef("workspace://root/");
protected static final NodeRef companyHomeNode = new NodeRef("workspace://companyHome/");
protected static final NodeRef adminPersonNode = new NodeRef("workspace://admin/");
protected static final NodeRef adminHomeNode = new NodeRef("workspace://admin-home/");
protected static final NodeRef testUserNode = new NodeRef("workspace://testUser/");
protected static final NodeRef testGroupNode = new NodeRef("workspace://testGroup/");
protected static final NodeRef testWorkflowPackage = new NodeRef("workspace://testPackage/");
protected static final NodeRef testWorkflowContext = new NodeRef("workspace://testContext/");
protected WorkflowDefinition deployTestTaskDefinition()
{
return deployDefinition(TEST_TASK_DEF);
}
protected WorkflowDefinition deployTestAdhocDefinition()
{
return deployDefinition(TEST_ADHOC_DEF);
}
protected WorkflowDefinition deployTestSignallingDefinition()
{
return deployDefinition(TEST_SIGNALLING_DEF);
}
protected WorkflowDefinition deployTestJobDefinition()
{
return deployDefinition(TEST_JOB_DEF);
}
protected WorkflowDefinition deployTestDiagramDefinition()
{
return deployDefinition(TEST_DIAGRAM_DEF);
}
protected WorkflowDefinition deployDefinition(String resource)
{
InputStream input = getInputStream(resource);
WorkflowDeployment deployment = workflowEngine.deployDefinition(input, XML);
WorkflowDefinition definition = deployment.getDefinition();
return definition;
}
protected InputStream getInputStream(String resource)
{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input= classLoader.getResourceAsStream(resource);
return input;
}
@Before
public void setUp() throws Exception
{
mockNamespaceService();
mockDictionaryService();
mockNodeService();
mockSearchService();
mockPermissionService();
mockPersonService();
mockAuthorityDAO();
mockServiceRegistry();
Repository repoHelper = mock(Repository.class);
when(repoHelper.getCompanyHome()).thenReturn(companyHomeNode);
workflowEngine.setRepositoryHelper(repoHelper);
// Also add custom type
// TODO: Should come from configuration
ScriptNodeVariableType variableType = new ScriptNodeVariableType();
variableType.setServiceRegistry(serviceRegistry);
// ((ProcessEngineImpl)processEngine).getProcessEngineConfiguration().getVariableTypes().addType(variableType, 1);
// Use util to set current user to admin
AuthenticationUtil.setFullyAuthenticatedUser("admin");
workflowEngine.afterPropertiesSet();
}
/**
*
*/
private void mockServiceRegistry()
{
// Set all services on the mocked Serviceregistry, injected by spring
when(serviceRegistry.getNodeService()).thenReturn(nodeService);
when(serviceRegistry.getDictionaryService()).thenReturn(dictionaryService);
when(serviceRegistry.getPermissionService()).thenReturn(permissionService);
}
private void mockAuthorityDAO()
{
when(authorityDAO.authorityExists(TEST_USER)).thenReturn(true);
when(authorityDAO.authorityExists(TEST_GROUP)).thenReturn(true);
// We will return dummy node refs to authorities testUser and testGroup
when(authorityDAO.getAuthorityNodeRefOrNull(TEST_USER)).thenReturn(testUserNode);
when(authorityDAO.getAuthorityNodeRefOrNull(TEST_GROUP)).thenReturn(testGroupNode);
}
private void mockPersonService()
{
// Checking if admin exists
when(personService.personExists("admin")).thenReturn(true);
// Return reference to Admin person
when(personService.getPerson("admin")).thenReturn(adminPersonNode);
// Check if test-user exists
when(personService.personExists(TEST_USER)).thenReturn(true);
when(personService.getPerson(TEST_USER)).thenReturn(testUserNode);
}
private void mockPermissionService()
{
// Allow permission on all nodes
when(permissionService.hasPermission((NodeRef) any(), anyString())).thenReturn(AccessStatus.ALLOWED);
}
private void mockNodeService()
{
// Return root store
when(nodeService.getRootNode(new StoreRef("workspace://SpacesStore"))).thenReturn(rootNode);
// Return company home and it's type
when(nodeService.exists(companyHomeNode)).thenReturn(true);
when(nodeService.getType((NodeRef) any())).thenReturn(QName.createQName("cm:folder"));
// Return admin's home property
when(nodeService.getProperty(adminPersonNode, ContentModel.PROP_HOMEFOLDER)).thenReturn(adminHomeNode);
// Return testUser and testGroup types
when(nodeService.getType(testUserNode)).thenReturn(ContentModel.TYPE_PERSON);
when(nodeService.getType(testGroupNode)).thenReturn(ContentModel.TYPE_AUTHORITY);
}
private void mockSearchService()
{
// When searching for company home, return single node
when(unprotectedSearchService.selectNodes(rootNode, "spaces.company_home.childname", null, namespaceService, false)).thenReturn(Arrays.asList(companyHomeNode));
}
/**
* @return
*/
private void mockDictionaryService()
{
Mockito.reset(dictionaryService);
when(dictionaryService.getType((QName)any())).thenAnswer(new Answer<TypeDefinition>()
{
@Override
public TypeDefinition answer(InvocationOnMock invocation) throws Throwable
{
QName name = (QName) invocation.getArguments()[0];
TypeDefinition type = mock(TypeDefinition.class);
when(type.getName()).thenReturn(name);
return type;
}
});
when(dictionaryService.getAnonymousType((QName)any())).thenAnswer(new Answer<TypeDefinition>()
{
@Override
public TypeDefinition answer(InvocationOnMock invocation) throws Throwable
{
QName name = (QName) invocation.getArguments()[0];
TypeDefinition type = mock(TypeDefinition.class);
when(type.getName()).thenReturn(name);
// Add a default value
Map<QName, PropertyDefinition> props = new HashMap<QName, PropertyDefinition>();
QName qname = QName.createQName("http://test", "myProp");
DataTypeDefinition qNameDef = mock(DataTypeDefinition.class);
when(qNameDef.getName()).thenReturn(DataTypeDefinition.QNAME);
when(qNameDef.getJavaClassName()).thenReturn(QName.class.getName());
// Create dummy property type
DataTypeDefinition def = mock(DataTypeDefinition.class);
when(def.getName()).thenReturn(DataTypeDefinition.TEXT);
when(def.getJavaClassName()).thenReturn(String.class.getName());
// Create dummy property definition
PropertyDefinition prop = mock(PropertyDefinition.class);
when(prop.getName()).thenReturn(qname);
when(prop.getDefaultValue()).thenReturn("Default value");
when(prop.getDataType()).thenReturn(def);
// Also add description
PropertyDefinition description = mock(PropertyDefinition.class);
when(description.getName()).thenReturn(WorkflowModel.PROP_DESCRIPTION);
when(description.getDataType()).thenReturn(def);
// Add outcome property name
PropertyDefinition outcomePropertyName = mock(PropertyDefinition.class);
when(outcomePropertyName.getName()).thenReturn(WorkflowModel.PROP_OUTCOME_PROPERTY_NAME);
when(outcomePropertyName.getDataType()).thenReturn(qNameDef);
when(outcomePropertyName.getDefaultValue()).thenReturn("{http://test}testOutcome");
// Add outcome property
PropertyDefinition outcomeProperty = mock(PropertyDefinition.class);
when(outcomeProperty.getName()).thenReturn(QName.createQName("http://test", "testOutcome"));
when(outcomeProperty.getDataType()).thenReturn(def);
props.put(qname, prop);
props.put(WorkflowModel.PROP_DESCRIPTION, description);
props.put(WorkflowModel.PROP_OUTCOME_PROPERTY_NAME, outcomePropertyName);
props.put(QName.createQName("http://test", "testOutcome"), outcomeProperty);
when(type.getProperties()).thenReturn(props);
return type;
}
});
// Mock type inheritance for person nodes
when(dictionaryService.isSubClass(ContentModel.TYPE_PERSON, ContentModel.TYPE_PERSON)).thenReturn(true);
}
private void mockNamespaceService()
{
namespaceService.registerNamespace(NamespaceService.BPM_MODEL_PREFIX, NamespaceService.BPM_MODEL_1_0_URI);
namespaceService.registerNamespace(NamespaceService.DEFAULT_PREFIX, NamespaceService.DEFAULT_URI);
namespaceService.registerNamespace(NamespaceService.WORKFLOW_MODEL_PREFIX, NamespaceService.WORKFLOW_MODEL_1_0_URI);
namespaceService.registerNamespace("test", "http://test");
}
@After
public void tearDown()
{
List<ProcessDefinition> defs = repo.createProcessDefinitionQuery()
.processDefinitionKey(TEST_TASK_KEY)
.list();
HashSet<String> deployments = new HashSet<String>(defs.size());
for (ProcessDefinition def : defs)
{
deployments.add(def.getDeploymentId());
}
for (String deployment : deployments)
{
List<ProcessDefinition> definitions = repo.createProcessDefinitionQuery()
.deploymentId(deployment)
.list();
for (ProcessDefinition def : definitions)
{
killInstances(def);
}
repo.deleteDeployment(deployment);
}
}
public String mapQNameToName(QName name)
{
// NOTE: Map names using old conversion scheme (i.e. : -> _) as well as
// new scheme (i.e. } -> _)
// NOTE: Use new scheme
String nameStr = name.toPrefixString(this.namespaceService);
if (nameStr.indexOf('_') != -1 && nameStr.indexOf('_') < nameStr.indexOf(':'))
{
return nameStr.replace(':', '}');
}
return nameStr.replace(':', '_');
}
private void killInstances(ProcessDefinition def)
{
List<ProcessInstance> instances = runtime.createProcessInstanceQuery()
.processDefinitionId(def.getId())
.list();
for (ProcessInstance instance : instances)
{
runtime.deleteProcessInstance(instance.getId(), "For test");
}
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.workflow.AbstractMultitenantWorkflowTest;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowDeployment;
import org.alfresco.service.namespace.QName;
/**
* @author Nick Smith
* @since 4.0
*
*/
public class ActivitiMultitenantWorkflowTest extends AbstractMultitenantWorkflowTest
{
private static final String CALLACTIVITY_SUBPROCESS_LOCATION = "activiti/test-callactivity-subprocess.bpmn20.xml";
private static final String CALLACTIVITY_MAINPROCESS_LOCATION = "activiti/test-callactivity-main.bpmn20.xml";
@Override
protected String getEngine()
{
return ActivitiConstants.ENGINE_ID;
}
@Override
protected String getTestDefinitionPath()
{
return "activiti/testTransaction.bpmn20.xml";
}
@Override
protected String getTestDefinitionKey()
{
return "activiti$testTask";
}
@Override
protected String getAdhocDefinitionKey()
{
return "activiti$activitiAdhoc";
}
/**
* ALF-15939: Call-activity should be multi-tenant aware.
*/
public void testSubProcessCallActivity() throws Exception
{
// Run as User1 so tenant domain 1
AuthenticationUtil.setFullyAuthenticatedUser(user1);
// Deploy called sub-process on tenant domain 1
InputStream input = getInputStream(CALLACTIVITY_SUBPROCESS_LOCATION);
WorkflowDeployment deployment = workflowService.deployDefinition(getEngine(), input, XML);
// Deploy called main-process on tenant domain 1
input = getInputStream(CALLACTIVITY_MAINPROCESS_LOCATION);
deployment = workflowService.deployDefinition(getEngine(), input, XML);
WorkflowDefinition mainProcessDefinition = deployment.getDefinition();
// Start a process, which immediately tries to call the sub-process before returning control to thread
try {
workflowService.startWorkflow(mainProcessDefinition.getId(), new HashMap<QName, Serializable>());
} catch(Exception e) {
e.printStackTrace();
fail("No exception was expected while running process, but got: " + e.toString());
}
}
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import java.io.IOException;
import junit.framework.TestCase;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.springframework.core.io.ClassPathResource;
/**
* @author Nick Smith
* @since 3.4.e
*/
public class ActivitiSmokeTest extends TestCase
{
public void testDeploy() throws Exception
{
ProcessEngine engine = buildProcessEngine();
RepositoryService repoService = engine.getRepositoryService();
Deployment deployment = deployDefinition(repoService);
assertNotNull(deployment);
RuntimeService runtimeService = engine.getRuntimeService();
try
{
ProcessInstance instance = runtimeService.startProcessInstanceByKey("testTask");
assertNotNull(instance);
String instanceId = instance.getId();
ProcessInstance instanceInDb = findProcessInstance(runtimeService, instanceId);
assertNotNull(instanceInDb);
runtimeService.deleteProcessInstance(instanceId, "");
}
finally
{
// List<Deployment> deployments = repoService.createDeploymentQuery().list();
// for (Deployment deployment2 : deployments)
// {
// repoService.deleteDeployment(deployment2.getId());
// }
repoService.deleteDeployment(deployment.getId());
}
}
private Deployment deployDefinition(RepositoryService repoService) throws IOException
{
ClassPathResource resource = new ClassPathResource("org/alfresco/repo/workflow/activiti/testTransaction.bpmn20.xml");
Deployment deployment = repoService.createDeployment()
.addInputStream(resource.getFilename(), resource.getInputStream())
.deploy();
return deployment;
}
private ProcessEngine buildProcessEngine()
{
String properties = "org/alfresco/repo/workflow/activiti/activiti.cfg.xml";
ProcessEngine engine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(properties).buildProcessEngine();
return engine;
}
private ProcessInstance findProcessInstance(RuntimeService runtimeService, String instanceId)
{
ProcessInstance instanceInDb = runtimeService.createProcessInstanceQuery()
.processInstanceId(instanceId)
.singleResult();
return instanceInDb;
}
}

View File

@@ -1,260 +0,0 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import java.io.InputStream;
import junit.framework.TestCase;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Nick Smith
* @since 3.4.e
*/
public class ActivitiSpringTest extends TestCase
{
private static final QName PROP_CHECK_VALUE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "check_value");
private static final String PROC_DEF_KEY = "testTask";
private static final String ACTIVITI_CONTEXT = "classpath:alfresco/activiti-context.xml";
private static final QName ASPECT = ContentModel.ASPECT_ATTACHABLE;
private RuntimeService runtime;
private RepositoryService repo;
private Deployment deployment;
private AuthenticationComponent authenticationComponent;
private NodeService nodeService;
private RetryingTransactionHelper txnHelper;
private NodeRef workingNodeRef;
public void testSmoke() throws Exception
{
assertNotNull(runtime);
ProcessInstance instance = runtime.startProcessInstanceByKey(PROC_DEF_KEY);
assertNotNull(instance);
String instanceId = instance.getId();
ProcessInstance instanceInDb = findProcessInstance(instanceId);
assertNotNull(instanceInDb);
runtime.deleteProcessInstance(instance.getId(), "");
assertNotNull(instance);
}
/**
* Start a process and then trigger a rollback by throwing an exception in Alfresco NodeService.
* Check that the process instance was rolled back.
*/
public void testRollbackFromAlfresco()
{
RetryingTransactionCallback<String> callback = new RetryingTransactionCallback<String>()
{
public String execute() throws Throwable
{
ProcessInstance instance = runtime.startProcessInstanceByKey(PROC_DEF_KEY);
String id = instance.getId();
try
{
blowUp();
}
catch (InvalidNodeRefException e)
{
// Expected, but absorbed
}
return id;
}
};
String id = txnHelper.doInTransaction(callback);
ProcessInstance instance = findProcessInstance(id);
if (instance != null)
{
runtime.deleteProcessInstance(id, "For test");
fail("The process instance creation should have been rolled back!");
}
}
/**
* Start a process and then trigger a rollback by throwing an exception in Alfresco NodeService.
* Check that the process instance was rolled back.
*/
public void testRollbackFromActiviti()
{
RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
nodeService.addAspect(workingNodeRef, ASPECT, null);
assertTrue("The node should have the aspect!", nodeService.hasAspect(workingNodeRef, ASPECT));
try
{
runtime.signal("Fake Id");
fail("Should throw an Exception here!");
}
catch (ActivitiException e)
{
// Expected, but absorbed
}
return null;
}
};
txnHelper.doInTransaction(callback);
assertFalse("The node should not have the aspect!", nodeService.hasAspect(workingNodeRef, ASPECT));
}
/**
* Checks nesting of two transactions with <code>requiresNew == true</code>
*/
public void testNestedWithoutPropogation()
{
RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
ProcessInstance instance = runtime.startProcessInstanceByKey(PROC_DEF_KEY);
final String id = instance.getId();
ProcessInstance instanceInDb = findProcessInstance(id);
assertNotNull("Can't read process instance in same transaction!", instanceInDb);
RetryingTransactionCallback<Void> callbackInner = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
ProcessInstance instanceInDb2 = findProcessInstance(id);
assertNull("Should not be able to read process instance in inner transaction!", instanceInDb2);
return null;
}
};
try
{
txnHelper.doInTransaction(callbackInner, false, true);
return null;
}
finally
{
runtime.deleteProcessInstance(id, "FOr test");
}
}
};
txnHelper.doInTransaction(callback);
}
private Long blowUp()
{
NodeRef invalidNodeRef = new NodeRef(workingNodeRef.getStoreRef(), "BOGUS");
nodeService.setProperty(invalidNodeRef, PROP_CHECK_VALUE, null);
fail("Expected to generate an InvalidNodeRefException");
return null;
}
private ProcessInstance findProcessInstance(String instanceId)
{
return runtime.createProcessInstanceQuery()
.processInstanceId(instanceId)
.singleResult();
}
/**
* {@inheritDoc}
*/
@Override
protected void setUp() throws Exception
{
ConfigurableApplicationContext appContext = loadContext();
this.repo = (RepositoryService) appContext.getBean("activitiRepositoryService");
this.runtime = (RuntimeService) appContext.getBean("activitiRuntimeService");
ServiceRegistry serviceRegistry = (ServiceRegistry) appContext.getBean(ServiceRegistry.SERVICE_REGISTRY);
authenticationComponent = (AuthenticationComponent) appContext.getBean("authenticationComponent");
TransactionService transactionService = serviceRegistry.getTransactionService();
nodeService = serviceRegistry.getNodeService();
txnHelper = transactionService.getRetryingTransactionHelper();
// authenticate
authenticationComponent.setSystemUserAsCurrentUser();
StoreRef storeRef = nodeService.createStore(
StoreRef.PROTOCOL_WORKSPACE,
"test-" + getName() + "-" + System.currentTimeMillis());
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
// Create a node to work on
workingNodeRef = nodeService.createNode(
rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, getName()),
ContentModel.TYPE_CMOBJECT).getChildRef();
String resource = "org/alfresco/repo/workflow/activiti/testTransaction.bpmn20.xml";
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input= classLoader.getResourceAsStream(resource);
this.deployment = repo.createDeployment()
.addInputStream(resource, input)
.deploy();
}
private String[] getConfigLocations()
{
String[] defaultLocations = ApplicationContextHelper.CONFIG_LOCATIONS;
Object[] locations = ArrayUtils.add(defaultLocations, ACTIVITI_CONTEXT);
return (String[]) locations;
}
private ConfigurableApplicationContext loadContext() throws Exception
{
String[] locations = getConfigLocations();
return new ClassPathXmlApplicationContext(locations);
}
/**
* {@inheritDoc}
*/
@Override
protected void tearDown() throws Exception
{
try
{
repo.deleteDeployment(deployment.getId());
authenticationComponent.clearCurrentSecurityContext();
}
catch (Exception e)
{
// Do Nothing }
}
}
}

View File

@@ -1,244 +0,0 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import java.io.InputStream;
import junit.framework.TestCase;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
/**
* @author Nick Smith
* @since 3.4.e
*/
public class ActivitiSpringTransactionTest extends TestCase
{
private static final QName PROP_CHECK_VALUE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "check_value");
private static final String PROC_DEF_KEY = "testTask";
private static final QName ASPECT = ContentModel.ASPECT_ATTACHABLE;
private RuntimeService runtime;
private RepositoryService repo;
private Deployment deployment;
private AuthenticationComponent authenticationComponent;
private NodeService nodeService;
private RetryingTransactionHelper txnHelper;
private NodeRef workingNodeRef;
public void testSmoke() throws Exception
{
assertNotNull(runtime);
ProcessInstance instance = runtime.startProcessInstanceByKey(PROC_DEF_KEY);
assertNotNull(instance);
String instanceId = instance.getId();
ProcessInstance instanceInDb = findProcessInstance(instanceId);
assertNotNull(instanceInDb);
runtime.deleteProcessInstance(instance.getId(), "");
assertNotNull(instance);
}
/**
* Start a process and then trigger a rollback by throwing an exception in Alfresco NodeService.
* Check that the process instance was rolled back.
*/
public void testRollbackFromAlfresco()
{
RetryingTransactionCallback<String> callback = new RetryingTransactionCallback<String>()
{
public String execute() throws Throwable
{
ProcessInstance instance = runtime.startProcessInstanceByKey(PROC_DEF_KEY);
String id = instance.getId();
try
{
blowUp();
}
catch (InvalidNodeRefException e)
{
// Expected, but absorbed
}
return id;
}
};
String id = txnHelper.doInTransaction(callback);
ProcessInstance instance = findProcessInstance(id);
if (instance != null)
{
runtime.deleteProcessInstance(id, "For test");
fail("The process instance creation should have been rolled back!");
}
}
/**
* Start a process and then trigger a rollback by throwing an exception in Alfresco NodeService.
* Check that the process instance was rolled back.
*/
public void testRollbackFromActiviti()
{
RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
nodeService.addAspect(workingNodeRef, ASPECT, null);
assertTrue("The node should have the aspect!", nodeService.hasAspect(workingNodeRef, ASPECT));
try
{
runtime.signal("Fake Id");
fail("Should throw an Exception here!");
}
catch (ActivitiException e)
{
// Expected, but absorbed
}
return null;
}
};
txnHelper.doInTransaction(callback);
assertFalse("The node should not have the aspect!", nodeService.hasAspect(workingNodeRef, ASPECT));
}
/**
* Checks nesting of two transactions with <code>requiresNew == true</code>
*/
public void testNestedWithoutPropogation()
{
RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
ProcessInstance instance = runtime.startProcessInstanceByKey(PROC_DEF_KEY);
final String id = instance.getId();
ProcessInstance instanceInDb = findProcessInstance(id);
assertNotNull("Can't read process instance in same transaction!", instanceInDb);
RetryingTransactionCallback<Void> callbackInner = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
ProcessInstance instanceInDb2 = findProcessInstance(id);
assertNull("Should not be able to read process instance in inner transaction!", instanceInDb2);
return null;
}
};
try
{
txnHelper.doInTransaction(callbackInner, false, true);
return null;
}
finally
{
runtime.deleteProcessInstance(id, "FOr test");
}
}
};
txnHelper.doInTransaction(callback);
}
private Long blowUp()
{
NodeRef invalidNodeRef = new NodeRef(workingNodeRef.getStoreRef(), "BOGUS");
nodeService.setProperty(invalidNodeRef, PROP_CHECK_VALUE, null);
fail("Expected to generate an InvalidNodeRefException");
return null;
}
private ProcessInstance findProcessInstance(String instanceId)
{
return runtime.createProcessInstanceQuery()
.processInstanceId(instanceId)
.singleResult();
}
/**
* {@inheritDoc}
*/
@Override
protected void setUp() throws Exception
{
ApplicationContext appContext = ApplicationContextHelper.getApplicationContext();
this.repo = (RepositoryService) appContext.getBean("activitiRepositoryService");
this.runtime = (RuntimeService) appContext.getBean("activitiRuntimeService");
ServiceRegistry serviceRegistry = (ServiceRegistry) appContext.getBean(ServiceRegistry.SERVICE_REGISTRY);
authenticationComponent = (AuthenticationComponent) appContext.getBean("authenticationComponent");
TransactionService transactionService = serviceRegistry.getTransactionService();
nodeService = serviceRegistry.getNodeService();
txnHelper = transactionService.getRetryingTransactionHelper();
// authenticate
authenticationComponent.setSystemUserAsCurrentUser();
StoreRef storeRef = nodeService.createStore(
StoreRef.PROTOCOL_WORKSPACE,
"test-" + getName() + "-" + System.currentTimeMillis());
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
// Create a node to work on
workingNodeRef = nodeService.createNode(
rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, getName()),
ContentModel.TYPE_CMOBJECT).getChildRef();
String resource = "activiti/testTransaction.bpmn20.xml";
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input= classLoader.getResourceAsStream(resource);
this.deployment = repo.createDeployment()
.addInputStream(resource, input)
.deploy();
}
/**
* {@inheritDoc}
*/
@Override
protected void tearDown() throws Exception
{
try
{
repo.deleteDeployment(deployment.getId(), true);
authenticationComponent.clearCurrentSecurityContext();
}
catch (Exception e)
{
// Do Nothing }
}
}
}

View File

@@ -1,944 +0,0 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.engine.history.HistoricDetail;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableUpdate;
import org.activiti.engine.task.Task;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.workflow.BPMEngineRegistry;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.namespace.QName;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* @author Nick Smith
* @author Frederik Heremans
* @since 3.4.e
*/
public class ActivitiTaskComponentTest extends AbstractActivitiComponentTest
{
private WorkflowDefinition workflowDef;
@Test
public void testGetStartTask()
{
try
{
workflowEngine.getStartTask("Foo");
fail("Should blow up if Id is wrong format!");
}
catch(WorkflowException e)
{
// Do Nothing
}
WorkflowTask result = workflowEngine.getStartTask(ActivitiConstants.ENGINE_ID + "$Foo");
assertNull("Should not find any result for fake (but valid) Id.", result);
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
String comment = "Start task description";
params.put(WorkflowModel.PROP_COMMENT, comment);
params.put(WorkflowModel.PROP_PRIORITY, 1 );
Date dueDate = new Date();
params.put(WorkflowModel.PROP_DUE_DATE, dueDate );
WorkflowPath path = workflowEngine.startWorkflow(workflowDef.getId(), params);
String instanceId = path.getInstance().getId();
WorkflowTask task = workflowEngine.getStartTask(instanceId);
assertNotNull("Task shoudl exist!", task);
String localId = ActivitiConstants.START_TASK_PREFIX+BPMEngineRegistry.getLocalId(instanceId);
String taskId = BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, localId);
assertEquals("Start Task Id is wrong", taskId, task.getId());
assertEquals("The start task path is wrong!", path.getId(), task.getPath().getId());
TypeDefinition definition = task.getDefinition().getMetadata();
assertNotNull(definition);
String name = definition.getName().toPrefixString(namespaceService).replace(':', '_');
assertEquals("bpm_foo", name);
assertEquals(name, task.getName());
assertEquals(name, task.getTitle());
assertEquals(name, task.getDescription());
assertEquals(WorkflowTaskState.IN_PROGRESS, task.getState());
assertEquals(name, task.getDescription());
// Check start task properties populated.
Map<QName, Serializable> properties = task.getProperties();
assertEquals(comment, properties.get(WorkflowModel.PROP_COMMENT));
assertEquals(1, properties.get(WorkflowModel.PROP_PRIORITY));
assertEquals(dueDate, properties.get(WorkflowModel.PROP_DUE_DATE));
// Check start task after task is completed.
task = workflowEngine.endTask(task.getId(), null);
assertEquals("Start Task Id is wrong", taskId, task.getId());
assertEquals("The start task path is wrong!", path.getId(), task.getPath().getId());
definition = task.getDefinition().getMetadata();
assertNotNull(definition);
name = definition.getName().toPrefixString(namespaceService).replace(':', '_');
assertEquals("bpm_foo", name);
assertEquals(name, task.getName());
assertEquals(name, task.getTitle());
assertEquals(name, task.getDescription());
assertEquals(WorkflowTaskState.COMPLETED, task.getState());
assertEquals(name, task.getDescription());
// Check start task properties populated.
properties = task.getProperties();
assertEquals(comment, properties.get(WorkflowModel.PROP_COMMENT));
assertEquals(1, properties.get(WorkflowModel.PROP_PRIORITY));
assertEquals(dueDate, properties.get(WorkflowModel.PROP_DUE_DATE));
// Check start task for historic process.
workflowEngine.cancelWorkflow(instanceId);
task = workflowEngine.getStartTask(instanceId);
assertNull(task);
// assertEquals("Start Task Id is wrong", taskId, task.getId());
//
// assertEquals("The start task path is wrong!", path.getId(), task.getPath().getId());
// definition = task.getDefinition().getMetadata();
// assertNotNull(definition);
// name = definition.getName().toPrefixString(namespaceService).replace(':', '_');
// assertEquals("bpm_foo", name);
//
// assertEquals(name, task.getName());
// assertEquals(name, task.getTitle());
// assertEquals(name, task.getDescription());
// assertEquals(WorkflowTaskState.COMPLETED, task.getState());
// assertEquals(name, task.getDescription());
//
// // Check start task properties populated.
// properties = task.getProperties();
// assertEquals(comment, properties.get(WorkflowModel.PROP_COMMENT));
// assertEquals(1, properties.get(WorkflowModel.PROP_PRIORITY));
// assertEquals(dueDate, properties.get(WorkflowModel.PROP_DUE_DATE));
}
@Test
public void testGetTaskById() throws Exception
{
try
{
workflowEngine.getTaskById("Foo");
fail("Should blow up if Id is wrong format!");
}
catch(WorkflowException e)
{
// Do Nothing
}
WorkflowTask result = workflowEngine.getTaskById(ActivitiConstants.ENGINE_ID + "$Foo");
assertNull("Should not find any result for fake (but valid) Id.", result);
WorkflowPath path = workflowEngine.startWorkflow(workflowDef.getId(), new HashMap<QName, Serializable>());
Task task = taskService.createTaskQuery()
.executionId(BPMEngineRegistry.getLocalId(path.getId()))
.singleResult();
assertNotNull("Task shoudl exist!", task);
String taskId = BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, task.getId());
WorkflowTask wfTask = workflowEngine.getTaskById(taskId);
assertNotNull(wfTask);
}
@Test
public void testGetStartTaskById() throws Exception
{
WorkflowTask result = workflowEngine.getTaskById(ActivitiConstants.ENGINE_ID + "$Foo");
assertNull("Should not find any result for fake (but valid) Id.", result);
WorkflowPath path = workflowEngine.startWorkflow(workflowDef.getId(), new HashMap<QName, Serializable>());
Task task = taskService.createTaskQuery()
.executionId(BPMEngineRegistry.getLocalId(path.getId()))
.singleResult();
// A start task should be available for the process instance
String startTaskId = ActivitiConstants.START_TASK_PREFIX + task.getProcessInstanceId();
String taskId = createGlobalId(startTaskId);
WorkflowTask wfTask = workflowEngine.getTaskById(taskId);
assertNotNull(wfTask);
assertEquals(createGlobalId(task.getProcessInstanceId()), wfTask.getPath().getId());
}
@SuppressWarnings("unchecked")
@Test
public void testGetFinishedTaskById() throws Exception
{
WorkflowPath path = workflowEngine.startWorkflow(workflowDef.getId(), new HashMap<QName, Serializable>());
// Finish the start-task
WorkflowTask startTask = workflowEngine.getStartTask(path.getInstance().getId());
workflowEngine.endTask(startTask.getId(), null);
// Set some task properties on the first task, different types
List<WorkflowTask> tasks = workflowEngine.getTasksForWorkflowPath(path.getId());
String finishedTaskId = tasks.get(0).getId();
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(WorkflowModel.PROP_DESCRIPTION, "Task description");
props.put(WorkflowModel.PROP_PRIORITY, 1234);
props.put(QName.createQName("myprop1"), "Property value");
props.put(QName.createQName("myprop2"), Boolean.TRUE);
props.put(QName.createQName("myprop3"), 12345);
props.put(QName.createQName("myprop4"), 45678L);
workflowEngine.updateTask(finishedTaskId, props, null, null);
// Finish the first task, this task will be used in this test
workflowEngine.endTask(finishedTaskId, null);
// Get the finished task
WorkflowTask finishedTask = workflowEngine.getTaskById(finishedTaskId);
assertNotNull(finishedTask);
Assert.assertEquals("Task description", finishedTask.getDescription());
Assert.assertEquals(finishedTaskId, finishedTask.getId());
Assert.assertEquals("bpm_foo_task", finishedTask.getName());
Assert.assertEquals("Task", finishedTask.getTitle());
Assert.assertEquals(WorkflowTaskState.COMPLETED, finishedTask.getState());
// Check if typeDefinition (formKey) is preserved on finished tasks
Assert.assertEquals("task name", finishedTask.getDefinition().getId(), "bpm_foo_task");
// Check workflowpath
Assert.assertEquals(path.getId(), finishedTask.getPath().getId());
Assert.assertEquals(path.getInstance().getId(), finishedTask.getPath().getInstance().getId());
// Check variables
Assert.assertEquals("Property value", finishedTask.getProperties().get(QName.createQName("myprop1")));
Assert.assertEquals(Boolean.TRUE, finishedTask.getProperties().get(QName.createQName("myprop2")));
Assert.assertEquals(12345, finishedTask.getProperties().get(QName.createQName("myprop3")));
Assert.assertEquals(45678L, finishedTask.getProperties().get(QName.createQName("myprop4")));
// Check pooled actors, should be one user and one group
List<NodeRef> pooledActors = (List<NodeRef>) finishedTask.getProperties().get(WorkflowModel.ASSOC_POOLED_ACTORS);
Assert.assertNotNull(pooledActors);
Assert.assertEquals(2, pooledActors.size());
Assert.assertTrue(pooledActors.contains(testGroupNode));
Assert.assertTrue(pooledActors.contains(testUserNode));
}
@Test
public void testEndTask() throws Exception
{
WorkflowPath path = workflowEngine.startWorkflow(workflowDef.getId(), new HashMap<QName, Serializable>());
Task task = taskService.createTaskQuery()
.executionId(BPMEngineRegistry.getLocalId(path.getId()))
.singleResult();
assertNotNull("Task should exist!", task);
String globalTaskId = createGlobalId(task.getId());
// Set a custom property on the task, this will be flushed
// to process-instance once task is completed
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(QName.createQName("http://test", "myVar"), "test123");
workflowEngine.updateTask(globalTaskId, props, null, null);
// Now end the task
workflowEngine.endTask(globalTaskId, null);
// Check if process-instance now contains the variable of the finished task
List<HistoricDetail> updates = historyService.createHistoricDetailQuery()
.variableUpdates()
.processInstanceId(task.getProcessInstanceId())
.list();
boolean found = false;
for(HistoricDetail detail : updates)
{
HistoricVariableUpdate update = (HistoricVariableUpdate) detail;
if (update.getVariableName().equals("test_myVar"))
{
Assert.assertEquals("test123", update.getValue());
found = true;
}
}
Assert.assertTrue("Task variables are not flushed to process-instance", found);
// Check task assignee, should be currently logged in user
HistoricTaskInstance hti = historyService.createHistoricTaskInstanceQuery().taskId(task.getId()).singleResult();
assertNotNull(hti);
assertEquals("admin", hti.getAssignee());
}
@Test
public void testGetAssignedTasks() throws Exception
{
performGetAssignedTasks(false);
}
@Test
public void testGetAssignedTasksLazyInitialization() throws Exception
{
// Lazy tasks should behave in exactly the same way, transparent for the user
performGetAssignedTasks(true);
}
protected void performGetAssignedTasks(boolean lazyInitialization) throws Exception
{
HashMap<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(WorkflowModel.PROP_WORKFLOW_DUE_DATE, new Date());
WorkflowPath path = workflowEngine.startWorkflow(workflowDef.getId(), props);
// Get start task
WorkflowTask startTask = workflowEngine.getStartTask(path.getInstance().getId());
assertNotNull(startTask);
// Finish the start task
workflowEngine.endTask(startTask.getId(), null);
// No task should be assigned yet
Assert.assertEquals(0, workflowEngine.getAssignedTasks("testUser", WorkflowTaskState.IN_PROGRESS, lazyInitialization).size());
// Get pooled task and assign
List<WorkflowTask> tasks = workflowEngine.getTasksForWorkflowPath(path.getId());
assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
workflowEngine.updateTask(tasks.get(0).getId(), Collections.singletonMap(ContentModel.PROP_OWNER, (Serializable)"testUser"), null, null);
// A task should be assigned to the testUser
tasks = workflowEngine.getAssignedTasks("testUser", WorkflowTaskState.IN_PROGRESS, lazyInitialization);
assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
// Check task
WorkflowTask task = tasks.get(0);
assertEquals("Task", task.getDescription());
assertEquals("Task", task.getTitle());
assertEquals(WorkflowTaskState.IN_PROGRESS, task.getState());
assertEquals("testUser", task.getProperties().get(ContentModel.PROP_OWNER));
assertEquals(props.get(WorkflowModel.PROP_DUE_DATE), task.getProperties().get(WorkflowModel.PROP_DUE_DATE));
assertNotNull(task.getProperties().get(WorkflowModel.PROP_START_DATE));
// Complete the task as "testUser"
AuthenticationUtil.setFullyAuthenticatedUser("testUser");
workflowEngine.endTask(task.getId(), ActivitiConstants.DEFAULT_TRANSITION_NAME);
// NO active task should be assigned to the testUser
tasks = workflowEngine.getAssignedTasks("testUser", WorkflowTaskState.IN_PROGRESS, lazyInitialization);
assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// One completed task should be assigned to the testUser
tasks = workflowEngine.getAssignedTasks("testUser", WorkflowTaskState.COMPLETED, lazyInitialization);
assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
// Check completed task
task = tasks.get(0);
assertEquals("Task", task.getDescription());
assertEquals("Task", task.getTitle());
assertEquals(WorkflowTaskState.COMPLETED, task.getState());
assertEquals("testUser", task.getProperties().get(ContentModel.PROP_OWNER));
assertEquals(props.get(WorkflowModel.PROP_DUE_DATE), task.getProperties().get(WorkflowModel.PROP_DUE_DATE));
assertNotNull(task.getProperties().get(WorkflowModel.PROP_START_DATE));
}
@Test
public void testGetPooledTasks() throws Exception
{
performGetPooledTasksTest(false);
}
@Test
public void testGetPooledTasksLazyInitialization() throws Exception
{
// Lazy tasks should behave in exactly the same way, transparent for the user
performGetPooledTasksTest(true);
}
@SuppressWarnings("unchecked")
protected void performGetPooledTasksTest(boolean lazy)
{
// The first task in the TestTaskDefinition has candidate group 'testGroup'
// and candidate-user 'testUser'
WorkflowPath path = workflowEngine.startWorkflow(workflowDef.getId(), new HashMap<QName, Serializable>());
// Get start task
WorkflowTask startTask = workflowEngine.getStartTask(path.getInstance().getId());
assertNotNull(startTask);
// Finish the start task
workflowEngine.endTask(startTask.getId(), null);
List<WorkflowTask> tasks = workflowEngine.getTasksForWorkflowPath(path.getId());
assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
// Check if the ASSOC_POOLED_ACTORS is set on the task, to be sure
// pooled actors are used on task
WorkflowTask theTask = tasks.get(0);
Serializable pooledActors = theTask.getProperties().get(WorkflowModel.ASSOC_POOLED_ACTORS);
assertNotNull(pooledActors);
// Group and user should be present
List<NodeRef> pooledActorNodes = (List<NodeRef>) pooledActors;
Assert.assertEquals(2, pooledActorNodes.size());
Assert.assertTrue(pooledActorNodes.contains(testUserNode));
Assert.assertTrue(pooledActorNodes.contains(testGroupNode));
// The task should be found when pooled tasks are requested
List<WorkflowTask> pooledUserTasks = workflowEngine.getPooledTasks(Arrays.asList(TEST_USER), lazy);
assertNotNull(pooledUserTasks);
Assert.assertEquals(1, pooledUserTasks.size());
Assert.assertEquals(theTask.getId(), pooledUserTasks.get(0).getId());
// The task should be found when pooled taskes are requested
List<WorkflowTask> pooledGroupTasks = workflowEngine.getPooledTasks(Arrays.asList(TEST_GROUP), lazy);
assertNotNull(pooledGroupTasks);
Assert.assertEquals(1, pooledGroupTasks.size());
Assert.assertEquals(theTask.getId(), pooledGroupTasks.get(0).getId());
// Only a single task should be found when task is both pooled for testUser and testGroup
List<WorkflowTask> pooledTasks = workflowEngine.getPooledTasks(Arrays.asList(TEST_USER, TEST_GROUP), lazy);
assertNotNull(pooledTasks);
Assert.assertEquals(1, pooledTasks.size());
Assert.assertEquals(theTask.getId(), pooledTasks.get(0).getId());
// No tasks should be found
List<WorkflowTask> unexistingPooledTasks = workflowEngine.getPooledTasks(Arrays.asList("unexisting"), lazy);
assertNotNull(unexistingPooledTasks);
Assert.assertEquals(0, unexistingPooledTasks.size());
// If one authority matches, task should be returned
pooledGroupTasks = workflowEngine.getPooledTasks(Arrays.asList("unexistinggroup",TEST_GROUP), lazy);
assertNotNull(pooledGroupTasks);
Assert.assertEquals(1, pooledGroupTasks.size());
Assert.assertEquals(theTask.getId(), pooledGroupTasks.get(0).getId());
}
@Test
public void testQueryTasksInProgress() throws Exception
{
// Testing all query functionality for WorkflowTaskState.IN_PROGRESS
WorkflowPath path = workflowEngine.startWorkflow(workflowDef.getId(), new HashMap<QName, Serializable>());
Task task = taskService.createTaskQuery()
.executionId(BPMEngineRegistry.getLocalId(path.getId()))
.singleResult();
assertNotNull("Task should exist!", task);
String globalTaskId = createGlobalId(task.getId());
// Test query by taskId
WorkflowTaskQuery taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
taskQuery.setTaskId(globalTaskId);
List<WorkflowTask> tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
Assert.assertEquals(globalTaskId, tasks.get(0).getId());
// Test query by nonexistent taskId
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
taskQuery.setTaskId(createGlobalId("nonexistentTask"));
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// Test query by process ID
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
taskQuery.setProcessId(createGlobalId(task.getProcessInstanceId()));
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
Assert.assertEquals(globalTaskId, tasks.get(0).getId());
// Test query by nonexistent processId
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
taskQuery.setProcessId(createGlobalId("nonexistentProcess"));
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// Test query by actor ID
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
taskQuery.setActorId(TEST_USER);
tasks = workflowEngine.queryTasks(taskQuery);
// No tasks should be assigned to testUser
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// Assign the task
taskService.setAssignee(task.getId(), TEST_USER);
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
taskQuery.setActorId(TEST_USER);
tasks = workflowEngine.queryTasks(taskQuery);
// Task is assigned to testUser
Assert.assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
Assert.assertEquals(globalTaskId, tasks.get(0).getId());
// Test by nonexistent actor ID
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
taskQuery.setActorId("nonexistentUser");
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// Test query by process-name
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
taskQuery.setWorkflowDefinitionName("testTask");
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
taskQuery.setWorkflowDefinitionName("unexistingTaskName");
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// Test query by task-name
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
taskQuery.setTaskName(QName.createQName("bpm_foo_task"));
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
taskQuery.setTaskName(QName.createQName("unexisting_task_name"));
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// Test querying task variables, using all possible (and allowed) types of variables
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("longVar", 928374L);
variables.put("shortVar", (short) 123);
variables.put("integerVar", 1234);
variables.put("stringVar", "stringValue");
variables.put("booleanVar", true);
Date date = Calendar.getInstance().getTime();
variables.put("dateVar", date);
variables.put("nullVar", null);
ActivitiScriptNode scriptNode = new ActivitiScriptNode(testGroupNode, serviceRegistry);
variables.put("scriptNodeVar", scriptNode);
taskService.setVariablesLocal(task.getId(), variables);
// Query long variable
checkTaskVariableTaskPresent(WorkflowTaskState.IN_PROGRESS, QName.createQName("longVar"), 928374L, globalTaskId);
checkTaskVariableNoMatch(WorkflowTaskState.IN_PROGRESS, QName.createQName("longVar"), 444444L);
// Query short variable
checkTaskVariableTaskPresent(WorkflowTaskState.IN_PROGRESS, QName.createQName("shortVar"), (short) 123, globalTaskId);
checkTaskVariableNoMatch(WorkflowTaskState.IN_PROGRESS, QName.createQName("shortVar"), (short) 456);
// Query integer variable
checkTaskVariableTaskPresent(WorkflowTaskState.IN_PROGRESS, QName.createQName("integerVar"), 1234, globalTaskId);
checkTaskVariableNoMatch(WorkflowTaskState.IN_PROGRESS, QName.createQName("integerVar"), 5678);
// Query string variable
checkTaskVariableTaskPresent(WorkflowTaskState.IN_PROGRESS, QName.createQName("stringVar"), "stringValue", globalTaskId);
checkTaskVariableNoMatch(WorkflowTaskState.IN_PROGRESS, QName.createQName("stringVar"), "noMatchString");
// Query string variable
checkTaskVariableTaskPresent(WorkflowTaskState.IN_PROGRESS, QName.createQName("booleanVar"), true, globalTaskId);
checkTaskVariableNoMatch(WorkflowTaskState.IN_PROGRESS, QName.createQName("booleanVar"), false);
// Query date variable
checkTaskVariableTaskPresent(WorkflowTaskState.IN_PROGRESS, QName.createQName("dateVar"), date, globalTaskId);
Calendar otherDate = Calendar.getInstance();
otherDate.add(Calendar.YEAR, 1);
checkTaskVariableNoMatch(WorkflowTaskState.IN_PROGRESS, QName.createQName("dateVar"), otherDate.getTime());
// Query null variable
checkTaskVariableTaskPresent(WorkflowTaskState.IN_PROGRESS, QName.createQName("nullVar"), null, globalTaskId);
checkTaskVariableNoMatch(WorkflowTaskState.IN_PROGRESS, QName.createQName("nullVar"), "notNull");
// Query script-node variable
checkTaskVariableTaskPresent(WorkflowTaskState.IN_PROGRESS, QName.createQName("scriptNodeVar"), scriptNode, globalTaskId);
ActivitiScriptNode otherNode = new ActivitiScriptNode(testUserNode, serviceRegistry);
checkTaskVariableNoMatch(WorkflowTaskState.IN_PROGRESS, QName.createQName("scriptNodeVar"), otherNode);
// Query task based on process variable
runtime.setVariable(task.getExecutionId(), "processVar", "testing");
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
Map<QName, Object> props = new HashMap<QName, Object>();
props.put(QName.createQName("processVar"), "testing");
taskQuery.setProcessCustomProps(props);
tasks = workflowEngine.queryTasks(taskQuery);
assertNotNull(tasks);
assertEquals(1, tasks.size());
props.put(QName.createQName("processVar"), "notmatching");
tasks = workflowEngine.queryTasks(taskQuery);
assertNotNull(tasks);
assertEquals(0, tasks.size());
}
@Test
public void testQueryTasksCompleted() throws Exception
{
// Testing all query functionality for WorkflowTaskState.COMPLETED
WorkflowPath path = workflowEngine.startWorkflow(workflowDef.getId(), new HashMap<QName, Serializable>());
Task task = taskService.createTaskQuery()
.executionId(BPMEngineRegistry.getLocalId(path.getId()))
.singleResult();
taskService.setVariableLocal(task.getId(), "taskVar", "theValue");
assertNotNull("Task should exist!", task);
String globalTaskId = createGlobalId(task.getId());
// Set the actor
taskService.setAssignee(task.getId(), TEST_USER);
// Set process prop
runtime.setVariable(task.getExecutionId(), "processVar", "testing");
// End the task as TEST_USER
AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER);
workflowEngine.endTask(globalTaskId, null);
AuthenticationUtil.setFullyAuthenticatedUser("admin");
// Test query by taskId
WorkflowTaskQuery taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
taskQuery.setActive(Boolean.FALSE); // Set to false, since workflow this task is in, has finished
taskQuery.setTaskId(globalTaskId);
List<WorkflowTask> tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
Assert.assertEquals(globalTaskId, tasks.get(0).getId());
// Test query by nonexistent task ID
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
taskQuery.setTaskId(createGlobalId("nonexistantTask"));
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// Test query by process ID, this should also return the start-task
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
taskQuery.setProcessId(createGlobalId(task.getProcessInstanceId()));
taskQuery.setActive(Boolean.FALSE);
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(2, tasks.size());
boolean taskFound = false;
boolean startTaskFound = false;
for (WorkflowTask wfTask : tasks)
{
if (wfTask.getId().equals(globalTaskId))
{
taskFound = true;
}
if (wfTask.getId().contains(ActivitiConstants.START_TASK_PREFIX))
{
startTaskFound = true;
}
}
Assert.assertTrue("Task should have been returned", taskFound);
Assert.assertTrue("Start-task should have been returned", startTaskFound);
// Test query by nonexistent process ID
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
taskQuery.setProcessId(createGlobalId("nonexistantProcess"));
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// Test query by actor
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
taskQuery.setActorId(TEST_USER);
taskQuery.setActive(Boolean.FALSE);
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
// Test by nonexistent actor
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
taskQuery.setActorId("unexistingUser");
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// Test query by process-name
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
taskQuery.setWorkflowDefinitionName("testTask");
taskQuery.setActive(Boolean.FALSE);
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
taskQuery.setWorkflowDefinitionName("unexistingTaskName");
tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
// Query task based on task variable
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
Map<QName, Object> props = new HashMap<QName, Object>();
props.put(QName.createQName("taskVar"), "theValue");
taskQuery.setActive(false);
taskQuery.setTaskCustomProps(props);
tasks = workflowEngine.queryTasks(taskQuery);
assertNotNull(tasks);
assertEquals(1, tasks.size());
props.put(QName.createQName("processVar"), "notmatching");
tasks = workflowEngine.queryTasks(taskQuery);
assertNotNull(tasks);
assertEquals(0, tasks.size());
// Query task based on process variable
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
props = new HashMap<QName, Object>();
props.put(QName.createQName("processVar"), "testing");
taskQuery.setActive(false);
taskQuery.setProcessCustomProps(props);
tasks = workflowEngine.queryTasks(taskQuery);
assertNotNull(tasks);
assertEquals(1, tasks.size());
props.put(QName.createQName("processVar"), "notmatching");
tasks = workflowEngine.queryTasks(taskQuery);
assertNotNull(tasks);
assertEquals(0, tasks.size());
}
@Test
public void testQueryUsingNodeRef() {
NodeRef nodeRef = new NodeRef("workspace:///someRef");
QName nodeRefPropQname = QName.createQName("testProp");
HashMap<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(nodeRefPropQname, nodeRef);
// Start the workflow-path
workflowEngine.startWorkflow(workflowDef.getId(), props);
// Test querying with a value of type NodeRef
WorkflowTaskQuery taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
HashMap<QName, Object> queryParams = new HashMap<QName, Object>();
queryParams.put(nodeRefPropQname, nodeRef);
taskQuery.setProcessCustomProps(queryParams);
List<WorkflowTask> tasks = workflowEngine.queryTasks(taskQuery);
assertNotNull(tasks);
assertEquals(1, tasks.size());
}
@SuppressWarnings("unchecked")
@Test
public void testUpdateTask() {
NodeRef nodeRef = new NodeRef("workspace:///someRef");
NodeRef anotherRef = new NodeRef("workspace:///anotherRef");
QName propQname = QName.createQName("testProp");
QName nodeRefPropQname = QName.createQName("testAssoc");
QName singleNodeRefPropQname = QName.createQName("testAssocSingleValue");
HashMap<QName, Serializable> props = new HashMap<QName, Serializable>();
// Start the workflow-path
WorkflowPath path = workflowEngine.startWorkflow(workflowDef.getId(), props);
WorkflowTask startTask = workflowEngine.getStartTask(path.getInstance().getId());
// End the start-task
workflowEngine.endTask(startTask.getId(), null);
// Get the task to update
WorkflowTaskQuery taskQuery = createWorkflowTaskQuery(WorkflowTaskState.IN_PROGRESS);
taskQuery.setProcessId(path.getInstance().getId());
List<WorkflowTask> tasks = workflowEngine.queryTasks(taskQuery);
WorkflowTask task = tasks.get(0);
// Test altering plain properties
props = new HashMap<QName, Serializable>();
props.put(propQname, "54321");
props.put(singleNodeRefPropQname, nodeRef);
workflowEngine.updateTask(task.getId(), props, null, null);
tasks = workflowEngine.queryTasks(taskQuery);
task = tasks.get(0);
assertEquals("54321", task.getProperties().get(propQname));
// Test adding association
HashMap<QName, List<NodeRef>> toAdd = new HashMap<QName, List<NodeRef>>();
toAdd.put(nodeRefPropQname, Arrays.asList(anotherRef, nodeRef));
workflowEngine.updateTask(task.getId(), null, toAdd, null);
tasks = workflowEngine.queryTasks(taskQuery);
task = tasks.get(0);
assertEquals(2, ((List<NodeRef>)task.getProperties().get(nodeRefPropQname)).size());
// Test removing association
HashMap<QName, List<NodeRef>> toRemove = new HashMap<QName, List<NodeRef>>();
toRemove.put(nodeRefPropQname, Arrays.asList(nodeRef));
workflowEngine.updateTask(task.getId(), null, null, toRemove);
tasks = workflowEngine.queryTasks(taskQuery);
task = tasks.get(0);
assertEquals(1, ((List<NodeRef>)task.getProperties().get(nodeRefPropQname)).size());
assertEquals(anotherRef, ((List<NodeRef>)task.getProperties().get(nodeRefPropQname)).get(0));
// Test changing single-valued association
toAdd = new HashMap<QName, List<NodeRef>>();
toRemove = new HashMap<QName, List<NodeRef>>();
toRemove.put(singleNodeRefPropQname, Arrays.asList(nodeRef));
toAdd.put(singleNodeRefPropQname, Arrays.asList(anotherRef));
workflowEngine.updateTask(task.getId(), null, toAdd, toRemove);
tasks = workflowEngine.queryTasks(taskQuery);
task = tasks.get(0);
assertEquals(anotherRef, task.getProperties().get(singleNodeRefPropQname));
// Test clearing single-valued association
toRemove = new HashMap<QName, List<NodeRef>>();
toRemove.put(singleNodeRefPropQname, Arrays.asList(anotherRef));
workflowEngine.updateTask(task.getId(), null, null, toRemove);
tasks = workflowEngine.queryTasks(taskQuery);
task = tasks.get(0);
// Association value should be empty now
assertNull(task.getProperties().get(singleNodeRefPropQname));
}
private void checkTaskVariableTaskPresent(WorkflowTaskState state,
QName varName, Object varValue, String expectedTask)
{
WorkflowTaskQuery taskQuery = createWorkflowTaskQuery(state);
Map<QName, Object> customProperties = new HashMap<QName, Object>();
customProperties.put(varName, varValue);
taskQuery.setTaskCustomProps(customProperties);
assertTaskPresent(taskQuery, expectedTask);
}
private void checkTaskVariableNoMatch(WorkflowTaskState state,
QName varName, Object varValue)
{
WorkflowTaskQuery taskQuery = createWorkflowTaskQuery(state);
Map<QName, Object> customProperties = new HashMap<QName, Object>();
customProperties.put(varName, varValue);
taskQuery.setTaskCustomProps(customProperties);
assertNoTaskPresent(taskQuery);
}
private WorkflowTaskQuery createWorkflowTaskQuery(WorkflowTaskState state)
{
WorkflowTaskQuery taskQuery = new WorkflowTaskQuery();
taskQuery.setTaskState(state);
return taskQuery;
}
private void assertTaskPresent(WorkflowTaskQuery taskQuery,
String taskId)
{
List<WorkflowTask> tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(1, tasks.size());
Assert.assertEquals(taskId, tasks.get(0).getId());
}
private void assertNoTaskPresent(WorkflowTaskQuery taskQuery)
{
List<WorkflowTask> tasks = workflowEngine.queryTasks(taskQuery);
Assert.assertNotNull(tasks);
Assert.assertEquals(0, tasks.size());
}
private String createGlobalId(String id)
{
return BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, id);
}
@Override
@Before
public void setUp() throws Exception
{
super.setUp();
this.workflowDef = deployTestTaskDefinition();
}
}

View File

@@ -1,299 +0,0 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.impl.persistence.entity.TimerEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Job;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.person.TestPersonManager;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.workflow.BPMEngineRegistry;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowDeployment;
import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
/**
* Test to verify timer execution autentication and transaction behaviour.
*
* @author Frederik Heremans
* @since 3.4.e
*/
public class ActivitiTimerExecutionTest extends BaseSpringTest
{
private static final String USER1 = "User1" + GUID.generate();
private RetryingTransactionHelper transactionHelper;
private WorkflowService workflowService;
private AuthenticationComponent authenticationComponent;
private NodeService nodeService;
private ProcessEngine activitiProcessEngine;
private TestPersonManager personManager;
@SuppressWarnings("deprecation")
public void testTimerExecutionAuthentication() throws Exception
{
this.setComplete();
this.endTransaction();
try
{
WorkflowInstance taskAssigneeWorkflowInstance = transactionHelper
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<WorkflowInstance>()
{
public WorkflowInstance execute() throws Throwable
{
// Create test person
personManager.createPerson(USER1);
WorkflowDefinition definition = deployDefinition("activiti/testTimerTransaction.bpmn20.xml");
// Start the test timer transaction process, with 'error' = false, expecting a timer job
// to be executed without an error, with task timer is assigned to assigned to USER1
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(QName.createQName("error"), Boolean.FALSE);
params.put(QName.createQName("theTaskAssignee"), USER1);
WorkflowPath path = workflowService.startWorkflow(definition.getId(), params);
// End start-task
workflowService.endTask(workflowService.getStartTask(path.getInstance().getId()).getId(), null);
return path.getInstance();
}
});
// No timers should be available after a while they should have been executed, otherwise test fails
waitForTimersToBeExecuted(taskAssigneeWorkflowInstance.getId());
// Test assigned task
WorkflowPath path = workflowService.getWorkflowPaths(taskAssigneeWorkflowInstance.getId()).get(0);
// Check if job executed without exception, process should be waiting in "waitTask"
List<WorkflowTask> tasks = workflowService.getTasksForWorkflowPath(path.getId());
assertNotNull(tasks);
assertEquals(1, tasks.size());
assertEquals("waitTask", tasks.get(0).getDefinition().getNode().getName());
// Check if timer was executed as task assignee, was set while executing timer
Map<QName, Serializable> pathProps = workflowService.getPathProperties(path.getId());
assertEquals(USER1, pathProps.get(QName.createQName("timerExecutedAs")));
}
finally
{
cleanUp();
}
}
@SuppressWarnings("deprecation")
public void testTimerExecutionTransactionRollback() throws Exception
{
this.setComplete();
this.endTransaction();
try
{
WorkflowInstance workflowInstance = transactionHelper
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<WorkflowInstance>()
{
public WorkflowInstance execute() throws Throwable
{
// Create test person
personManager.createPerson(USER1);
WorkflowDefinition definition = deployDefinition("activiti/testTimerTransaction.bpmn20.xml");
// Start the test timer transaction process, with 'error' = false, expecting a timer job
// to be executed without an error, with task timer is assigned to assigned to USER1
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(QName.createQName("error"), Boolean.TRUE);
params.put(QName.createQName("theTaskAssignee"), USER1);
WorkflowPath path = workflowService.startWorkflow(definition.getId(), params);
// End start-task
workflowService.endTask(workflowService.getStartTask(path.getInstance().getId()).getId(), null);
return path.getInstance();
}
});
String processInstanceId = BPMEngineRegistry.getLocalId(workflowInstance.getId());
// Check the timer, should have "error" set in it
TimerEntity timer = (TimerEntity) activitiProcessEngine.getManagementService()
.createJobQuery().timers()
.processInstanceId(processInstanceId).singleResult();
int numberOfRetries = 5;
for (int i = 0; i < numberOfRetries; i++)
{
if (timer.getExceptionMessage() != null && timer.getRetries() == 0)
{
break;
}
Thread.sleep(1000);
timer = (TimerEntity) activitiProcessEngine.getManagementService()
.createJobQuery().timers()
.processInstanceId(processInstanceId).singleResult();
}
assertNotNull("Job should have exception message set", timer.getExceptionMessage());
assertEquals(0, timer.getRetries());
// Check if exception is the one we deliberately caused
String fullExceptionStacktrace = activitiProcessEngine.getManagementService().getJobExceptionStacktrace(timer.getId());
assertTrue(fullExceptionStacktrace.contains("Activiti engine rocks!"));
// Check if alfresco-changes that were performed are rolled back
NodeRef personNode = personManager.get(USER1);
NodeRef userHomeNode = (NodeRef)nodeService.getProperty(personNode, ContentModel.PROP_HOMEFOLDER);
String homeFolderName = (String) nodeService.getProperty(userHomeNode, ContentModel.PROP_NAME);
assertNotSame("User home changed", homeFolderName);
}
finally
{
cleanUp();
}
}
/**
* Delete the deployment, cascading all related processes/history
*/
private void cleanUp()
{
transactionHelper .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
try
{
personManager.clearPeople();
}
finally
{
// Make sure process-definition is still deleted, even when clearing people fails.
ProcessDefinition procDef = activitiProcessEngine.getRepositoryService()
.createProcessDefinitionQuery()
.processDefinitionKey("testTimerTransaction")
.latestVersion()
.singleResult();
if (procDef != null)
{
activitiProcessEngine.getRepositoryService().deleteDeployment(procDef.getDeploymentId(), true);
}
}
return null;
}
});
}
@SuppressWarnings("deprecation")
@Override
protected void onSetUpInTransaction() throws Exception
{
ServiceRegistry registry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY);
this.workflowService = registry.getWorkflowService();
this.authenticationComponent = (AuthenticationComponent) applicationContext.getBean("authenticationComponent");
this.nodeService = registry.getNodeService();
this.transactionHelper = (RetryingTransactionHelper) this.applicationContext
.getBean("retryingTransactionHelper");
this.activitiProcessEngine = (ProcessEngine) this.applicationContext.getBean("activitiProcessEngine");
MutableAuthenticationService authenticationService = registry.getAuthenticationService();
PersonService personService = registry.getPersonService();
this.personManager = new TestPersonManager(authenticationService, personService, nodeService);
authenticationComponent.setSystemUserAsCurrentUser();
}
private void waitForTimersToBeExecuted(String workflowInstanceId) throws Exception
{
String processInstanceId = BPMEngineRegistry.getLocalId(workflowInstanceId);
// Job-executor should finish the job, no timers should be available for WF
List<Job> timers = activitiProcessEngine.getManagementService().createJobQuery()
.timers()
.processInstanceId(processInstanceId)
.list();
int numberOfRetries = 5;
for (int i=0; i< numberOfRetries; i++)
{
if (timers.size() == 0)
{
break;
}
Thread.sleep(1000);
timers = activitiProcessEngine.getManagementService().createJobQuery()
.timers()
.processInstanceId(processInstanceId)
.list();
}
if(timers.size() > 0) {
fail("There are still timers available for the process: " + processInstanceId);
}
}
protected WorkflowDefinition deployDefinition(String resource)
{
InputStream input = getInputStream(resource);
WorkflowDeployment deployment = workflowService.deployDefinition(ActivitiConstants.ENGINE_ID, input, MimetypeMap.MIMETYPE_XML);
WorkflowDefinition definition = deployment.getDefinition();
return definition;
}
private InputStream getInputStream(String resource)
{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input= classLoader.getResourceAsStream(resource);
return input;
}
}

View File

@@ -1,779 +0,0 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import java.io.InputStream;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.util.ClockUtil;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.Job;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.repo.workflow.BPMEngineRegistry;
import org.alfresco.repo.workflow.WorkflowConstants;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowNode;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
import org.alfresco.service.cmr.workflow.WorkflowTimer;
import org.alfresco.service.namespace.QName;
import org.junit.Test;
/**
* Spring-configured JUnit 4 test case.
* Uses Spring to load up a test context and runs each test case in a transaction which gets rolled back.
* Loads up the activiti-context.xml and test-database-context.xml.
*
* @author Nick Smith
* @since 3.4.e
*/
public class ActivitiWorkflowComponentTest extends AbstractActivitiComponentTest
{
@Test
public void testDeployDefinition() throws Exception
{
ProcessDefinition defInDB = repo.createProcessDefinitionQuery()
.processDefinitionKey(TEST_TASK_KEY)
.singleResult();
assertNull("The definition is already deployed!", defInDB);
WorkflowDefinition definition = deployTestTaskDefinition();
String localDefId = BPMEngineRegistry.getLocalId(definition.getId());
ProcessDefinition processDef = repo.createProcessDefinitionQuery()
.processDefinitionId(localDefId)
.singleResult();
assertNotNull("Process Definition should have been deployed!", processDef);
ProcessDefinition def2InDB = repo.createProcessDefinitionQuery()
.processDefinitionKey(TEST_ADHOC_KEY)
.singleResult();
assertNull("The definition is already deployed!", def2InDB);
WorkflowDefinition definition2 = deployTestTaskDefinition();
String localDef2Id = BPMEngineRegistry.getLocalId(definition2.getId());
ProcessDefinition processDef2 = repo.createProcessDefinitionQuery()
.processDefinitionId(localDef2Id)
.singleResult();
assertNotNull("Process Definition should have been deployed!", processDef2);
}
@Test
public void testIsDefinitionDeployed() throws Exception
{
InputStream input = getInputStream(TEST_TASK_DEF);
boolean result = workflowEngine.isDefinitionDeployed(input, XML);
assertFalse("Should return false before process def deployed.", result);
deployTestTaskDefinition();
input = getInputStream(TEST_TASK_DEF);
result = workflowEngine.isDefinitionDeployed(input, XML);
assertTrue("Should return true after process def deployed.", result);
// Check doesn't find Adhoc definition.
input = getInputStream(TEST_ADHOC_DEF);
result = workflowEngine.isDefinitionDeployed(input, XML);
assertFalse("Should not find Adhoc definition.", result);
}
@Test
public void testUndeployDefinition() throws Exception
{
WorkflowDefinition definition = deployTestTaskDefinition();
String localId = BPMEngineRegistry.getLocalId(definition.getId());
long defCount = repo.createProcessDefinitionQuery()
.processDefinitionId(localId)
.count();
assertEquals("The deployed process definition should exist!", 1, defCount);
workflowEngine.undeployDefinition(definition.getId());
defCount = repo.createProcessDefinitionQuery()
.processDefinitionId(localId)
.count();
assertEquals("The undeployed process definition should not exist!", 0, defCount);
}
@Test
public void testGetDefinitionById() throws Exception
{
WorkflowDefinition definition = deployTestTaskDefinition();
WorkflowDefinition result = workflowEngine.getDefinitionById(definition.getId());
assertNotNull("The workflow definition was not found!", result);
assertEquals(definition.getId(), result.getId());
assertEquals(definition.getDescription(), result.getDescription());
assertEquals(definition.getName(), result.getName());
assertEquals(definition.getTitle(), result.getTitle());
assertEquals(definition.getVersion(), result.getVersion());
WorkflowTaskDefinition resultStartDef = result.getStartTaskDefinition();
assertNotNull("Start task is null!", resultStartDef);
WorkflowTaskDefinition originalStartDef = definition.getStartTaskDefinition();
assertEquals("Start task Id does not match!", originalStartDef.getId(), resultStartDef.getId());
WorkflowNode resultNode = resultStartDef.getNode();
assertNotNull("Start Task Node is null!", resultNode);
assertEquals("Start Task Node Name does not match!", originalStartDef.getNode().getName(), resultNode.getName());
TypeDefinition metaData = resultStartDef.getMetadata();
assertNotNull("Start Task Metadata is null!", metaData);
assertEquals("Start Task Metadata name does not match!", originalStartDef.getMetadata().getName(), metaData.getName());
workflowEngine.undeployDefinition(definition.getId());
WorkflowDefinition nullResult = workflowEngine.getDefinitionById(definition.getId());
assertNull("The workflow definition was found but should be null!", nullResult);
}
@Test
public void testGetDefinitionByName() throws Exception
{
WorkflowDefinition definition = deployTestTaskDefinition();
WorkflowDefinition result = workflowEngine.getDefinitionByName(definition.getName());
assertNotNull("The workflow definition was not found!", result);
assertEquals(definition.getId(), result.getId());
assertEquals(definition.getDescription(), result.getDescription());
assertEquals(definition.getName(), result.getName());
assertEquals(definition.getTitle(), result.getTitle());
assertEquals(definition.getVersion(), result.getVersion());
workflowEngine.undeployDefinition(definition.getId());
WorkflowDefinition nullResult = workflowEngine.getDefinitionByName(definition.getName());
assertNull("The workflow definition was found but should be null!", nullResult);
}
@Test
public void testGetDefinitions() throws Exception
{
List<WorkflowDefinition> startDefs = workflowEngine.getDefinitions();
WorkflowDefinition defV1 = deployTestTaskDefinition();
List<WorkflowDefinition> definitions = workflowEngine.getDefinitions();
checkDefinitions(definitions, startDefs, defV1);
// Deploy version 2 of testTask def.
WorkflowDefinition defV2 = deployTestTaskDefinition();
// Check new version replaces old version.
definitions = workflowEngine.getDefinitions();
checkDefinitions(definitions, startDefs, defV2);
// Deploy new type of definition.
WorkflowDefinition adhocDef = deployTestAdhocDefinition();
// Check that definitions of a different type are picked up.
definitions = workflowEngine.getDefinitions();
checkDefinitions(definitions, startDefs, defV2, adhocDef);
}
@Test
public void testGetAllDefinitions() throws Exception
{
List<WorkflowDefinition> startDefs = workflowEngine.getAllDefinitions();
WorkflowDefinition defV1 = deployTestTaskDefinition();
List<WorkflowDefinition> definitions = workflowEngine.getAllDefinitions();
checkDefinitions(definitions, startDefs, defV1);
// Deploy version 2 of testTask def.
WorkflowDefinition defV2 = deployTestTaskDefinition();
// Check new version replaces old version.
definitions = workflowEngine.getAllDefinitions();
checkDefinitions(definitions, startDefs, defV1, defV2);
// Deploy new type of definition.
WorkflowDefinition adhocDef = deployTestAdhocDefinition();
// Check that definitions of a different type are picked up.
definitions = workflowEngine.getAllDefinitions();
checkDefinitions(definitions, startDefs, defV1, defV2, adhocDef);
}
@Test
public void testStartWorkflow() throws Exception
{
WorkflowDefinition def = deployTestTaskDefinition();
// Fill a map of default properties to start the workflow with
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
Date dueDate = Calendar.getInstance().getTime();
properties.put(WorkflowModel.PROP_WORKFLOW_DESCRIPTION, "description123");
properties.put(WorkflowModel.PROP_WORKFLOW_DUE_DATE, dueDate);
properties.put(WorkflowModel.PROP_WORKFLOW_PRIORITY, 2);
// properties.put(WorkflowModel.ASSOC_PACKAGE, null);
// properties.put(WorkflowModel.PROP_CONTEXT, null);
// Call the start method
WorkflowPath path = workflowEngine.startWorkflow(def.getId(), properties);
assertNotNull("The workflow path is null!", path);
String executionId = BPMEngineRegistry.getLocalId(path.getId());
Execution execution = runtime.createExecutionQuery()
.executionId(executionId)
.singleResult();
assertNotNull("No execution was created int he DB!", execution);
WorkflowInstance instance = path.getInstance();
assertNotNull("The workflow instance is null!",instance);
String procInstanceId = BPMEngineRegistry.getLocalId(instance.getId());
ProcessInstance procInstance = runtime.createProcessInstanceQuery()
.processInstanceId(procInstanceId)
.singleResult();
assertNotNull("No process instance was created!", procInstance);
WorkflowNode node = path.getNode();
assertNotNull("The workflow node is null!", node);
String nodeName = node.getName();
assertEquals("task", nodeName);
// Check if company home is added as variable and can be fetched
ScriptNode companyHome = (ScriptNode) runtime.getVariable(procInstanceId, "companyhome");
assertNotNull(companyHome);
assertEquals("companyHome", companyHome.getNodeRef().getStoreRef().getIdentifier());
// Check if the initiator is added as variable
ScriptNode initiator = (ScriptNode) runtime.getVariable(procInstanceId, "initiator");
assertNotNull(initiator);
assertEquals("admin", initiator.getNodeRef().getStoreRef().getIdentifier());
// Check if the initiator home is also set as variable
ScriptNode initiatorHome = (ScriptNode) runtime.getVariable(procInstanceId, "initiatorhome");
assertNotNull(initiatorHome);
assertEquals("admin-home", initiatorHome.getNodeRef().getStoreRef().getIdentifier());
// Check if start-date is set and no end-date is set
assertNotNull(path.getInstance().getStartDate());
assertNull(path.getInstance().getEndDate());
// Also check if the task that is created, has all default properties initialised
Task task = taskService.createTaskQuery().processInstanceId(procInstanceId).singleResult();
assertNotNull("Task should have been created", task);
assertEquals("task", task.getTaskDefinitionKey());
String defaultSetVariable = (String) taskService.getVariableLocal(task.getId(), "test_myProp");
assertEquals("Default value", defaultSetVariable);
// Also check default value of task description is taken from WF-porps
assertEquals("description123", task.getDescription());
}
@Test
public void testSignal() throws Exception
{
WorkflowDefinition def = deployTestSignallingDefinition();
ProcessInstance processInstance = runtime.startProcessInstanceById(BPMEngineRegistry.getLocalId(def.getId()));
String procId = processInstance.getId();
List<String> nodeIds = runtime.getActiveActivityIds(procId);
assertEquals(1, nodeIds.size());
assertEquals("task1", nodeIds.get(0));
String pathId = BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, procId);
WorkflowPath path = workflowEngine.signal(pathId, null);
assertEquals(pathId, path.getId());
assertEquals("task2", path.getNode().getName());
assertEquals(pathId, path.getInstance().getId());
assertTrue(path.isActive());
nodeIds = runtime.getActiveActivityIds(procId);
assertEquals(1, nodeIds.size());
assertEquals("task2", nodeIds.get(0));
// Should end the WorkflowInstance
path = workflowEngine.signal(pathId, null);
assertEquals(pathId, path.getId());
assertNull(path.getNode());
assertEquals(pathId, path.getInstance().getId());
assertFalse(path.isActive());
}
@Test
public void testCancelWorkflow() throws Exception
{
WorkflowDefinition def = deployTestAdhocDefinition();
ProcessInstance processInstance = runtime.startProcessInstanceById(BPMEngineRegistry.getLocalId(def.getId()));
// Validate if a workflow exists
List<WorkflowInstance> instances = workflowEngine.getActiveWorkflows(def.getId());
assertNotNull(instances);
assertEquals(1, instances.size());
assertEquals(processInstance.getId(), BPMEngineRegistry.getLocalId(instances.get(0).getId()));
// Call cancel method on component
WorkflowInstance cancelledWorkflow = workflowEngine.cancelWorkflow(instances.get(0).getId());
assertFalse(cancelledWorkflow.isActive());
instances = workflowEngine.getActiveWorkflows(def.getId());
assertNotNull(instances);
assertEquals(0, instances.size());
// Histrotic process instance shouldn't be present
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstance.getProcessInstanceId())
.singleResult();
assertNull(historicProcessInstance);
}
@Test
public void testCancelUnexistingWorkflow() throws Exception
{
try
{
String globalId = workflowEngine.createGlobalId("unexistingWorkflowId");
workflowEngine.cancelWorkflow(globalId);
fail("Exception expected");
}
catch(WorkflowException e)
{
// Inore this
}
}
@Test
public void testDeleteWorkflow() throws Exception
{
WorkflowDefinition def = deployTestAdhocDefinition();
ProcessInstance processInstance = runtime.startProcessInstanceById(BPMEngineRegistry.getLocalId(def.getId()));
// Validate if a workflow exists
List<WorkflowInstance> instances = workflowEngine.getActiveWorkflows(def.getId());
assertNotNull(instances);
assertEquals(1, instances.size());
assertEquals(processInstance.getId(), BPMEngineRegistry.getLocalId(instances.get(0).getId()));
// Call delete method on component
workflowEngine.deleteWorkflow(instances.get(0).getId());
instances = workflowEngine.getActiveWorkflows(def.getId());
assertNotNull(instances);
assertEquals(0, instances.size());
// Historic process instance shouldn't be present
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstance.getProcessInstanceId())
.singleResult();
assertNull(historicProcessInstance);
}
@Test
public void testDeleteFinishedWorkflow() throws Exception
{
WorkflowDefinition def = deployTestAdhocDefinition();
ProcessInstance processInstance = runtime.startProcessInstanceById(BPMEngineRegistry.getLocalId(def.getId()));
// Validate if a workflow exists
List<WorkflowInstance> instances = workflowEngine.getActiveWorkflows(def.getId());
assertNotNull(instances);
assertEquals(1, instances.size());
assertEquals(processInstance.getId(), BPMEngineRegistry.getLocalId(instances.get(0).getId()));
WorkflowInstance instance = instances.get(0);
WorkflowTask startTask = workflowEngine.getStartTask(instance.getId());
workflowEngine.endTask(startTask.getId(), null);
WorkflowTask adhocTask = workflowEngine.getTasksForWorkflowPath(instance.getId()).get(0);
workflowEngine.endTask(adhocTask.getId(), null);
// Validate if workflow is ended and has history
instances = workflowEngine.getActiveWorkflows(def.getId());
assertNotNull(instances);
assertEquals(0, instances.size());
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstance.getProcessInstanceId())
.singleResult();
assertNotNull(historicProcessInstance);
// Call delete method on component
workflowEngine.deleteWorkflow(instance.getId());
// Historic process instance shouldn't be present anymore
historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstance.getProcessInstanceId())
.singleResult();
assertNull(historicProcessInstance);
}
@Test
public void testDeleteUnexistingWorkflow() throws Exception
{
try
{
String globalId = workflowEngine.createGlobalId("unexistingWorkflowId");
workflowEngine.deleteWorkflow(globalId);
fail("Exception expected");
}
catch(WorkflowException e)
{
// Inore this
}
}
@Test
public void testGetActiveWorkflows() throws Exception
{
WorkflowDefinition def = deployTestAdhocDefinition();
String activitiProcessDefinitionId = BPMEngineRegistry.getLocalId(def.getId());
ProcessInstance activeInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
ProcessInstance completedInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
ProcessInstance cancelledInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
ProcessInstance deletedInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
// Complete completedProcessInstance.
String completedId = completedInstance.getId();
boolean isActive = true;
while (isActive)
{
Execution execution = runtime.createExecutionQuery()
.processInstanceId(completedId)
.singleResult();
runtime.signal(execution.getId());
ProcessInstance instance = runtime.createProcessInstanceQuery()
.processInstanceId(completedId)
.singleResult();
isActive = instance != null;
}
// Deleted and canceled instances shouldn't be returned
workflowEngine.cancelWorkflow(workflowEngine.createGlobalId(cancelledInstance.getId()));
workflowEngine.deleteWorkflow(workflowEngine.createGlobalId(deletedInstance.getId()));
// Validate if a workflow exists
List<WorkflowInstance> instances = workflowEngine.getActiveWorkflows(def.getId());
assertNotNull(instances);
assertEquals(1, instances.size());
String instanceId = instances.get(0).getId();
assertEquals(activeInstance.getId(), BPMEngineRegistry.getLocalId(instanceId));
}
@Test
public void testGetCompletedWorkflows() throws Exception
{
WorkflowDefinition def = deployTestAdhocDefinition();
String activitiProcessDefinitionId = BPMEngineRegistry.getLocalId(def.getId());
runtime.startProcessInstanceById(activitiProcessDefinitionId);
ProcessInstance completedInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
ProcessInstance cancelledInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
ProcessInstance deletedInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
// Complete completedProcessInstance.
String completedId = completedInstance.getId();
boolean isActive = true;
while (isActive)
{
Execution execution = runtime.createExecutionQuery()
.processInstanceId(completedId)
.singleResult();
runtime.signal(execution.getId());
ProcessInstance instance = runtime.createProcessInstanceQuery()
.processInstanceId(completedId)
.singleResult();
isActive = instance != null;
}
// Deleted and canceled instances shouldn't be returned
workflowEngine.cancelWorkflow(workflowEngine.createGlobalId(cancelledInstance.getId()));
workflowEngine.deleteWorkflow(workflowEngine.createGlobalId(deletedInstance.getId()));
// Validate if a workflow exists
List<WorkflowInstance> instances = workflowEngine.getCompletedWorkflows(def.getId());
assertNotNull(instances);
assertEquals(1, instances.size());
String instanceId = instances.get(0).getId();
assertEquals(completedId, BPMEngineRegistry.getLocalId(instanceId));
}
@Test
public void testGetWorkflows() throws Exception
{
WorkflowDefinition def = deployTestAdhocDefinition();
String activitiProcessDefinitionId = BPMEngineRegistry.getLocalId(def.getId());
ProcessInstance activeInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
ProcessInstance completedInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
ProcessInstance cancelledInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
ProcessInstance deletedInstance = runtime.startProcessInstanceById(activitiProcessDefinitionId);
// Complete completedProcessInstance.
String completedId = completedInstance.getId();
boolean isActive = true;
while (isActive)
{
Execution execution = runtime.createExecutionQuery()
.processInstanceId(completedId)
.singleResult();
runtime.signal(execution.getId());
ProcessInstance instance = runtime.createProcessInstanceQuery()
.processInstanceId(completedId)
.singleResult();
isActive = instance != null;
}
// Deleted and canceled instances shouldn't be returned
workflowEngine.cancelWorkflow(workflowEngine.createGlobalId(cancelledInstance.getId()));
workflowEngine.deleteWorkflow(workflowEngine.createGlobalId(deletedInstance.getId()));
// Validate if a workflow exists
List<WorkflowInstance> instances = workflowEngine.getWorkflows(def.getId());
assertNotNull(instances);
assertEquals(2, instances.size());
String instanceId = instances.get(0).getId();
assertEquals(activeInstance.getId(), BPMEngineRegistry.getLocalId(instanceId));
instanceId = instances.get(1).getId();
assertEquals(completedId, BPMEngineRegistry.getLocalId(instanceId));
}
@Test
public void testGetWorkflowById() throws Exception
{
WorkflowDefinition def = deployTestAdhocDefinition();
Date startTime = new SimpleDateFormat("dd-MM-yyy hh:mm:ss").parse("01-01-2011 12:11:10");
ClockUtil.setCurrentTime(startTime);
// Add some variables which should be used in the WorkflowInstance
Map<String, Object> variables = new HashMap<String, Object>();
Date dueDate = Calendar.getInstance().getTime();
putVariable(variables, WorkflowModel.PROP_WORKFLOW_DUE_DATE, dueDate);
putVariable(variables, WorkflowModel.PROP_WORKFLOW_DESCRIPTION, "I'm the description");
putVariable(variables, WorkflowModel.PROP_CONTEXT, new ActivitiScriptNode(testWorkflowContext, serviceRegistry));
putVariable(variables, WorkflowModel.ASSOC_PACKAGE, new ActivitiScriptNode(testWorkflowPackage, serviceRegistry));
putVariable(variables, WorkflowModel.PROP_WORKFLOW_PRIORITY, 3);
variables.put(WorkflowConstants.PROP_INITIATOR, new ActivitiScriptNode(adminHomeNode, serviceRegistry));
ProcessInstance processInstance = runtime.startProcessInstanceById(BPMEngineRegistry.getLocalId(def.getId()), variables);
String globalProcessInstanceId = BPMEngineRegistry.createGlobalId(
ActivitiConstants.ENGINE_ID, processInstance.getProcessInstanceId());
WorkflowInstance workflowInstance = workflowEngine.getWorkflowById(globalProcessInstanceId);
assertNotNull(workflowInstance);
assertEquals(globalProcessInstanceId, workflowInstance.getId());
assertNull(workflowInstance.getEndDate());
assertTrue(workflowInstance.isActive());
assertEquals("I'm the description", workflowInstance.getDescription());
assertEquals(dueDate, workflowInstance.getDueDate());
assertEquals(def.getId(), workflowInstance.getDefinition().getId());
assertEquals(adminHomeNode, workflowInstance.getInitiator());
assertEquals(testWorkflowContext, workflowInstance.getContext());
assertEquals(testWorkflowPackage, workflowInstance.getWorkflowPackage());
assertNotNull(workflowInstance.getPriority());
assertEquals(3, workflowInstance.getPriority().intValue());
assertEquals(startTime, workflowInstance.getStartDate());
// Reset current time used in activiti
ClockUtil.setCurrentTime(null);
}
@Test
public void testGetCompletedWorkflowById() throws Exception
{
WorkflowDefinition def = deployTestAdhocDefinition();
Date startTime = new SimpleDateFormat("dd-MM-yyy hh:mm:ss").parse("01-01-2011 01:02:03");
ClockUtil.setCurrentTime(startTime);
// Add some variables which should be used in the WorkflowInstance
Map<String, Object> variables = new HashMap<String, Object>();
Date dueDate = Calendar.getInstance().getTime();
putVariable(variables, WorkflowModel.PROP_WORKFLOW_DUE_DATE, dueDate);
putVariable(variables, WorkflowModel.PROP_WORKFLOW_DESCRIPTION, "I'm the description");
putVariable(variables, WorkflowModel.PROP_CONTEXT, new ActivitiScriptNode(testWorkflowContext, serviceRegistry));
putVariable(variables, WorkflowModel.ASSOC_PACKAGE, new ActivitiScriptNode(testWorkflowPackage, serviceRegistry));
putVariable(variables, WorkflowModel.PROP_WORKFLOW_PRIORITY, 3);
variables.put(WorkflowConstants.PROP_INITIATOR, new ActivitiScriptNode(adminHomeNode, serviceRegistry));
ProcessInstance processInstance = runtime.startProcessInstanceById(BPMEngineRegistry.getLocalId(def.getId()), variables);
String globalProcessInstanceId = BPMEngineRegistry.createGlobalId(
ActivitiConstants.ENGINE_ID, processInstance.getProcessInstanceId());
Date endTime = new SimpleDateFormat("dd-MM-yyy hh:mm:ss").parse("01-01-2011 02:03:04");
ClockUtil.setCurrentTime(endTime);
// Finish the task
Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
taskService.complete(task.getId());
WorkflowInstance workflowInstance = workflowEngine.getWorkflowById(globalProcessInstanceId);
assertNotNull(workflowInstance);
assertEquals(globalProcessInstanceId, workflowInstance.getId());
assertEquals(endTime, workflowInstance.getEndDate());
assertFalse(workflowInstance.isActive());
assertEquals("I'm the description", workflowInstance.getDescription());
assertEquals(dueDate, workflowInstance.getDueDate());
assertEquals(def.getId(), workflowInstance.getDefinition().getId());
assertEquals(adminHomeNode, workflowInstance.getInitiator());
assertEquals(testWorkflowContext, workflowInstance.getContext());
assertEquals(testWorkflowPackage, workflowInstance.getWorkflowPackage());
assertNotNull(workflowInstance.getPriority());
assertEquals(3, workflowInstance.getPriority().intValue());
assertEquals(startTime, workflowInstance.getStartDate());
// Reset current time used in activiti
ClockUtil.setCurrentTime(null);
}
@Test
public void testGetTimers() throws Exception
{
WorkflowDefinition def = deployTestJobDefinition();
ProcessInstance processInstance = runtime.startProcessInstanceById(BPMEngineRegistry.getLocalId(def.getId()));
// One timer should be active on workflow
String workflowInstanceId = BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID,
processInstance.getProcessInstanceId());
// Query the timer in activity to have reference
Job timerJob = managementService.createJobQuery().timers().processInstanceId(processInstance.getId()).singleResult();
String globalJobId = BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, timerJob.getId());
// Ask workflowEngine for timers
List<WorkflowTimer> timers = workflowEngine.getTimers(workflowInstanceId);
assertNotNull(timers);
assertEquals(1, timers.size());
WorkflowTimer timer = timers.get(0);
assertEquals(globalJobId, timer.getId());
assertEquals(timerJob.getDuedate(), timer.getDueDate());
// Check the path of the timer
String expectedTimerPathId = BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, timerJob.getExecutionId());
assertNotNull(timer.getPath());
assertEquals(expectedTimerPathId, timer.getPath().getId());
// Check the workflow-instance associated with the path
assertEquals(workflowInstanceId, timer.getPath().getInstance().getId());
// Check the task returned by the timer
Task waitingTask = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
assertNotNull(timer.getTask());
assertEquals(BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, waitingTask.getId()), timer.getTask().getId());
// When task with boundry-timer on it is finished, no timers should be available
Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
taskService.complete(task.getId());
timers = workflowEngine.getTimers(workflowInstanceId);
assertNotNull(timers);
assertEquals(0, timers.size());
}
@Test
public void testGetWorkflowImage()
{
WorkflowDefinition definitionWithoutImage = deployTestAdhocDefinition();
WorkflowDefinition definitionWithImage = deployTestDiagramDefinition();
// Start process-instance that shouldn't have an image
ProcessInstance processInstance = runtime.startProcessInstanceById(BPMEngineRegistry.getLocalId(definitionWithoutImage.getId()));
String worklfowId = BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, processInstance.getId());
assertFalse(workflowEngine.hasWorkflowImage(worklfowId));
assertNull(workflowEngine.getWorkflowImage(worklfowId));
// Start process-instance that SHOULD have an image
ProcessInstance processInstanceWithImage = runtime.startProcessInstanceById(BPMEngineRegistry.getLocalId(definitionWithImage.getId()));
String worklfowWithImageId = BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, processInstanceWithImage.getId());
assertTrue(workflowEngine.hasWorkflowImage(worklfowWithImageId));
assertNotNull(workflowEngine.getWorkflowImage(worklfowWithImageId));
}
private void putVariable(Map<String, Object> variables, QName varName, Object value)
{
String variableName = mapQNameToName(varName);
variables.put(variableName, value);
}
private void checkDefinitions(List<WorkflowDefinition> actual,List<WorkflowDefinition> startDefs, WorkflowDefinition... expected)
{
assertEquals("The number of process definitions expected does not match the actual number!", startDefs.size() + expected.length, actual.size());
ArrayList<String> ids = new ArrayList<String>(actual.size());
for (WorkflowDefinition def : actual)
{
ids.add(def.getId());
}
for (WorkflowDefinition exp: expected)
{
assertTrue("Results did not contain expected definition: "+exp, ids.contains(exp.getId()));
}
List<String> startIds = new ArrayList<String>(startDefs.size());
for (WorkflowDefinition def : startDefs)
{
startIds.add(def.getId());
}
for (WorkflowDefinition exp: expected)
{
assertFalse("Starting Definitions should not contain expected definition: "+exp, startIds.contains(exp.getId()));
}
}
}

View File

@@ -34,6 +34,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
@@ -48,8 +49,11 @@ import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.bpmn.behavior.ReceiveTaskActivityBehavior;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.bpmn.deployer.BpmnDeployer;
import org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator;
import org.activiti.engine.impl.form.DefaultTaskFormHandler;
import org.activiti.engine.impl.form.TaskFormHandler;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
@@ -74,6 +78,7 @@ import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.repo.workflow.BPMEngine;
import org.alfresco.repo.workflow.WorkflowAuthorityManager;
import org.alfresco.repo.workflow.WorkflowConstants;
import org.alfresco.repo.workflow.WorkflowDeployer;
import org.alfresco.repo.workflow.WorkflowEngine;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.repo.workflow.WorkflowNodeConverter;
@@ -157,7 +162,6 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
private static final String ERR_GET_TASK_BY_ID = "activiti.engine.get.task.by.id.error";
private static final String ERR_END_TASK_INVALID_TRANSITION = "activiti.engine.end.task.invalid.transition";
public static final QName QNAME_INITIATOR = QName.createQName(NamespaceService.DEFAULT_URI, WorkflowConstants.PROP_INITIATOR);
private RepositoryService repoService;
@@ -313,6 +317,25 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
.name(name)
.deploy();
List<ProcessDefinition> definitionList = repoService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
if (definitionList != null && definitionList.size() > 0)
{
boolean internalCategory = true;
for (ProcessDefinition processDefinition : definitionList)
{
if (WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL.equals(processDefinition.getCategory()) == false)
{
internalCategory = false;
break;
}
}
if (internalCategory)
{
repoService.setDeploymentCategory(deployment.getId(), WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL);
}
}
// No problems can be added to the WorkflowDeployment, warnings are
// not exposed
return typeConverter.convert(deployment);
@@ -612,6 +635,26 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
return defs;
}
private String getFormKey(PvmActivity act)
{
if(act instanceof ActivityImpl)
{
ActivityImpl actImpl = (ActivityImpl) act;
if (actImpl.getActivityBehavior() instanceof UserTaskActivityBehavior)
{
UserTaskActivityBehavior uta = (UserTaskActivityBehavior) actImpl.getActivityBehavior();
TaskFormHandler handler = uta.getTaskDefinition().getTaskFormHandler();
if(handler != null && handler instanceof DefaultTaskFormHandler)
{
// We cast to DefaultTaskFormHandler since we do not configure our own
return ((DefaultTaskFormHandler)handler).getFormKey().getExpressionText();
}
}
}
return null;
}
private boolean isReceiveTask(PvmActivity act)
{
@@ -941,12 +984,6 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
}
}
if(!activitiUtil.isMultiTenantWorkflowDeploymentEnabled())
{
// Specify which tenant domain the workflow was started in using a variable
variables.put(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain());
}
// Start the process-instance
ProcessInstance instance = runtimeService.startProcessInstanceById(processDefId, variables);
if(instance.isEnded())
@@ -1047,15 +1084,13 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
// If the process is finished, there is no diagram available
if (pi != null)
{
// Fetch the process-definition. Not using query API, since the returned
// processdefinition isn't initialized with all activities
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repoService)
.getDeployedProcessDefinition(pi.getProcessDefinitionId());
// Fetch the bpmn model
BpmnModel model = repoService.getBpmnModel(pi.getProcessDefinitionId());
if (processDefinition != null && processDefinition.isGraphicalNotationDefined())
if (model != null && model.getLocationMap().size() > 0)
{
return ProcessDiagramGenerator
.generateDiagram(processDefinition,
.generateDiagram(model,
ActivitiConstants.PROCESS_INSTANCE_IMAGE_FORMAT,
runtimeService.getActiveActivityIds(processInstanceId));
}
@@ -1357,7 +1392,7 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
.taskAssignee(authority)
.finished();
if(activitiUtil.isMultiTenantWorkflowDeploymentEnabled())
if(!activitiUtil.isMultiTenantWorkflowDeploymentEnabled())
{
taskQuery.processVariableValueEquals(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain());
}

View File

@@ -1,315 +0,0 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.workflow.AbstractWorkflowServiceIntegrationTest;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* @author Nick Smith
* @since 3.4.e
*/
public class ActivitiWorkflowServiceIntegrationTest extends AbstractWorkflowServiceIntegrationTest
{
public void testOutcome() throws Exception
{
WorkflowDefinition definition = deployDefinition("alfresco/workflow/review.bpmn20.xml");
personManager.setUser(USER1);
// Create workflow parameters
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
Serializable wfPackage = workflowService.createPackage(null);
params.put(WorkflowModel.ASSOC_PACKAGE, wfPackage);
NodeRef assignee = personManager.get(USER2);
params.put(WorkflowModel.ASSOC_ASSIGNEE, assignee); // task instance field
WorkflowPath path = workflowService.startWorkflow(definition.getId(), params);
String instanceId = path.getInstance().getId();
WorkflowTask startTask = workflowService.getStartTask(instanceId);
workflowService.endTask(startTask.getId(), null);
List<WorkflowPath> paths = workflowService.getWorkflowPaths(instanceId);
assertEquals(1, paths.size());
path = paths.get(0);
List<WorkflowTask> tasks = workflowService.getTasksForWorkflowPath(path.getId());
assertEquals(1, tasks.size());
WorkflowTask reviewTask = tasks.get(0);
// Set the transition property
QName outcomePropName = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "reviewOutcome");
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(outcomePropName, "Approve");
workflowService.updateTask(reviewTask.getId(), props, null, null);
// End task and check outcome property
WorkflowTask result = workflowService.endTask(reviewTask.getId(), null);
Serializable outcome = result.getProperties().get(WorkflowModel.PROP_OUTCOME);
assertEquals("Approve", outcome);
}
public void testStartTaskEndsAutomatically()
{
// Deploy the test workflow definition which uses the
// default Start Task type, so it should end automatically.
WorkflowDefinition definition = deployDefinition(getTestDefinitionPath());
// Start the Workflow
WorkflowPath path = workflowService.startWorkflow(definition.getId(), null);
String instanceId = path.getInstance().getId();
// Check the Start Task is completed.
WorkflowTask startTask = workflowService.getStartTask(instanceId);
assertEquals(WorkflowTaskState.COMPLETED, startTask.getState());
List<WorkflowTask> tasks = workflowService.getTasksForWorkflowPath(path.getId());
assertEquals(1, tasks.size());
String taskName = tasks.get(0).getName();
assertEquals("bpm_foo_task", taskName);
}
/**
* Actually tests if the priority is the default value. This is based on the assumption that custom
* tasks are defaulted to a priority of 50 (which is invalid). I'm testing that the code I wrote decides this is an
* invalid number and sets it to the default value (2).
*/
public void testPriorityIsValid()
{
WorkflowDefinition definition = deployDefinition("activiti/testCustomActiviti.bpmn20.xml");
personManager.setUser(USER1);
// Start the Workflow
WorkflowPath path = workflowService.startWorkflow(definition.getId(), null);
String instanceId = path.getInstance().getId();
// Check the Start Task is completed.
WorkflowTask startTask = workflowService.getStartTask(instanceId);
assertEquals(WorkflowTaskState.COMPLETED, startTask.getState());
List<WorkflowTask> tasks = workflowService.getTasksForWorkflowPath(path.getId());
for (WorkflowTask workflowTask : tasks)
{
Map<QName, Serializable> props = workflowTask.getProperties();
TypeDefinition typeDefinition = workflowTask.getDefinition().getMetadata();
Map<QName, PropertyDefinition> propertyDefs = typeDefinition.getProperties();
PropertyDefinition priorDef = propertyDefs.get(WorkflowModel.PROP_PRIORITY);
assertEquals(props.get(WorkflowModel.PROP_PRIORITY),Integer.valueOf(priorDef.getDefaultValue()));
}
}
public void testGetWorkflowTaskDefinitionsWithMultiInstanceTask()
{
// Test added to validate fix for ALF-14224
WorkflowDefinition definition = deployDefinition(getParallelReviewDefinitionPath());
String workflowDefId = definition.getId();
List<WorkflowTaskDefinition> taskDefs = workflowService.getTaskDefinitions(workflowDefId);
assertEquals(4, taskDefs.size());
// The first task is the start-task, the second one is a multi-instance UserTask. This should have the right form-key
WorkflowTaskDefinition taskDef = taskDefs.get(1);
assertEquals("wf:activitiReviewTask", taskDef.getId());
}
public void testAccessStartTaskAsAssigneeFromTaskPartOfProcess()
{
// Test added to validate fix for CLOUD-1929 - start-task can be accesses by assignee of a task
// part of that process
WorkflowDefinition definition = deployDefinition(getAdhocDefinitionPath());
// Start process as USER1
personManager.setUser(USER1);
// Create workflow parameters
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
Serializable wfPackage = workflowService.createPackage(null);
params.put(WorkflowModel.ASSOC_PACKAGE, wfPackage);
NodeRef assignee = personManager.get(USER2);
params.put(WorkflowModel.ASSOC_ASSIGNEE, assignee); // task instance field
WorkflowPath path = workflowService.startWorkflow(definition.getId(), params);
String instanceId = path.getInstance().getId();
WorkflowTask startTask = workflowService.getStartTask(instanceId);
workflowService.endTask(startTask.getId(), null);
List<WorkflowTask> tasks = workflowService.getTasksForWorkflowPath(path.getId());
assertEquals(1, tasks.size());
// Assign task to user3
workflowService.updateTask(tasks.get(0).getId(), Collections.singletonMap(WorkflowModel.ASSOC_ASSIGNEE,
(Serializable) personManager.get(USER3)), null, null);
// Authenticate as user3
personManager.setUser(USER3);
// When fetchin the start-task, no exception should be thrown
startTask = workflowService.getStartTask(instanceId);
assertNotNull(startTask);
startTask = workflowService.getTaskById(startTask.getId());
assertNotNull(startTask);
// Accessing by user4 shouldn't be possible
personManager.setUser(USER4);
try
{
workflowService.getStartTask(instanceId);
fail("AccessDeniedException expected");
}
catch(AccessDeniedException expected)
{
// Expected excaption
}
try
{
workflowService.getTaskById(startTask.getId());
fail("AccessDeniedException expected");
}
catch(AccessDeniedException expected)
{
// Expected exception
}
}
@Override
protected void checkTaskQueryStartTaskCompleted(String workflowInstanceId, WorkflowTask startTask)
{
// In activiti, start-tasks only show up when the taskId or workflowInstanceId is passed in.
List<String> expectedTasks = Arrays.asList(startTask.getId());
checkProcessIdQuery(workflowInstanceId, expectedTasks, WorkflowTaskState.COMPLETED);
checkTaskIdQuery(startTask.getId(), WorkflowTaskState.COMPLETED);
// Check additional filtering, when workflowInstanceId is passed
QName startTaskName = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "submitAdhocTask");
checkTaskNameQuery(startTaskName, expectedTasks, WorkflowTaskState.COMPLETED, workflowInstanceId);
checkActorIdQuery(USER1, expectedTasks, WorkflowTaskState.COMPLETED, workflowInstanceId);
checkIsActiveQuery(expectedTasks, WorkflowTaskState.COMPLETED, workflowInstanceId);
checkTaskPropsQuery(expectedTasks, WorkflowTaskState.COMPLETED, workflowInstanceId);
}
@Override
protected void checkTaskQueryTaskCompleted(String workflowInstanceId, WorkflowTask theTask, WorkflowTask startTask)
{
List<String> withoutStartTask = Arrays.asList(theTask.getId());
List<String> bothTasks= Arrays.asList(theTask.getId(), startTask.getId());
checkProcessIdQuery(workflowInstanceId, bothTasks, WorkflowTaskState.COMPLETED);
// Adhoc task should only be returned
QName taskName = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "adhocTask");
checkTaskNameQuery(taskName, withoutStartTask, WorkflowTaskState.COMPLETED, null);
// Completed adhocTask is assigned to USER2
checkActorIdQuery(USER2, withoutStartTask, WorkflowTaskState.COMPLETED, null);
checkIsActiveQuery(bothTasks, WorkflowTaskState.COMPLETED, workflowInstanceId);
// Task has custom property set
checkTaskPropsQuery(withoutStartTask, WorkflowTaskState.COMPLETED, null);
// Process properties
checkProcessPropsQuery(withoutStartTask, WorkflowTaskState.COMPLETED);
}
@Override
protected void checkQueryTasksInactiveWorkflow(String workflowInstanceId)
{
WorkflowTaskQuery taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
taskQuery.setActive(false);
taskQuery.setProcessId(workflowInstanceId);
List<WorkflowTask> tasks = workflowService.queryTasks(taskQuery);
assertNotNull(tasks);
assertEquals(3, tasks.size());
taskQuery = createWorkflowTaskQuery(WorkflowTaskState.COMPLETED);
taskQuery.setActive(true);
taskQuery.setProcessId(workflowInstanceId);
checkNoTasksFoundUsingQuery(taskQuery);
}
@Override
protected String getEngine()
{
return ActivitiConstants.ENGINE_ID;
}
@Override
protected String getTestDefinitionPath()
{
return "activiti/testTransaction.bpmn20.xml";
}
@Override
protected String getAdhocDefinitionPath()
{
return "alfresco/workflow/adhoc.bpmn20.xml";
}
@Override
protected String getPooledReviewDefinitionPath()
{
return "alfresco/workflow/review-pooled.bpmn20.xml";
}
@Override
protected String getParallelReviewDefinitionPath()
{
return "alfresco/workflow/parallel-review.bpmn20.xml";
}
@Override
protected String getTestTimerDefinitionPath()
{
return "activiti/testTimer.bpmn20.xml";
}
@Override
protected QName getAdhocProcessName()
{
return QName.createQName("activitiAdhoc");
}
}

View File

@@ -1,320 +0,0 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import java.util.List;
import org.activiti.engine.delegate.ExecutionListener;
import org.activiti.engine.delegate.TaskListener;
import org.activiti.engine.impl.bpmn.behavior.CallActivityBehavior;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.bpmn.parser.BpmnParseListener;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.delegate.ActivityBehavior;
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.util.xml.Element;
import org.activiti.engine.impl.variable.VariableDeclaration;
import org.alfresco.repo.tenant.TenantService;
/**
* A {@link BpmnParseListener} that adds a start- and endTaskListener to all
* parsed userTasks.
*
* This is used to wire in custom logic when task is created and completed.
*
* @author Frederik Heremans
* @author Nick Smith
* @since 3.4.e
*/
public class AlfrescoBpmnParseListener implements BpmnParseListener
{
private TaskListener completeTaskListener;
private TaskListener createTaskListener;
private ExecutionListener processCreateListener;
private TenantService tenantService;
private boolean multiTenancyEnabled = true;
@Override
public void parseUserTask(Element userTaskElement, ScopeImpl scope, ActivityImpl activity)
{
ActivityBehavior activitybehaviour = activity.getActivityBehavior();
if (activitybehaviour instanceof UserTaskActivityBehavior)
{
UserTaskActivityBehavior userTaskActivity = (UserTaskActivityBehavior) activitybehaviour;
if (createTaskListener != null)
{
userTaskActivity.getTaskDefinition().addTaskListener(TaskListener.EVENTNAME_CREATE, createTaskListener);
}
if (completeTaskListener != null)
{
userTaskActivity.getTaskDefinition().addTaskListener(TaskListener.EVENTNAME_COMPLETE,
completeTaskListener);
}
}
}
@Override
public void parseProcess(Element processElement, ProcessDefinitionEntity processDefinition)
{
//NOOP
}
@Override
public void parseStartEvent(Element startEventElement, ScopeImpl scope, ActivityImpl startEventActivity)
{
// Nothing to do here
}
@Override
public void parseExclusiveGateway(Element exclusiveGwElement, ScopeImpl scope, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseParallelGateway(Element parallelGwElement, ScopeImpl scope, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseScriptTask(Element scriptTaskElement, ScopeImpl scope, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseServiceTask(Element serviceTaskElement, ScopeImpl scope, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseTask(Element taskElement, ScopeImpl scope, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseManualTask(Element manualTaskElement, ScopeImpl scope, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseEndEvent(Element endEventElement, ScopeImpl scope, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseBoundaryTimerEventDefinition(Element timerEventDefinition, boolean interrupting,
ActivityImpl timerActivity)
{
// Nothing to do here
}
@Override
public void parseSubProcess(Element subProcessElement, ScopeImpl scope, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseCallActivity(Element callActivityElement, ScopeImpl scope, ActivityImpl activity)
{
if (multiTenancyEnabled && tenantService.isEnabled())
{
ActivityBehavior activityBehavior = activity.getActivityBehavior();
if(activityBehavior instanceof CallActivityBehavior)
{
CallActivityBehavior callActivity = (CallActivityBehavior) activityBehavior;
// Make name of process-definition to be called aware of the current tenant
callActivity.setProcessDefinitonKey(tenantService.getName(callActivity.getProcessDefinitonKey()));
}
}
}
@Override
public void parseProperty(Element propertyElement, VariableDeclaration variableDeclaration, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseSequenceFlow(Element sequenceFlowElement, ScopeImpl scopeElement, TransitionImpl transition)
{
// Nothing to do here
}
@Override
public void parseSendTask(Element sendTaskElement, ScopeImpl scope, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseBusinessRuleTask(Element businessRuleTaskElement, ScopeImpl scope, ActivityImpl activity)
{
// Nothing to do here
}
@Override
public void parseBoundaryErrorEventDefinition(Element errorEventDefinition, boolean interrupting,
ActivityImpl activity, ActivityImpl nestedErrorEventActivity)
{
// Nothing to do here
}
@Override
public void parseIntermediateTimerEventDefinition(Element timerEventDefinition, ActivityImpl timerActivity)
{
// Nothing to do here
}
@Override
public void parseRootElement(Element arg0, List<ProcessDefinitionEntity> arg1)
{
for (ProcessDefinitionEntity processDefinition : arg1)
{
processDefinition.addExecutionListener(ExecutionListener.EVENTNAME_START, processCreateListener);
if (multiTenancyEnabled && tenantService.isEnabled())
{
String key = tenantService.getName(processDefinition.getKey());
processDefinition.setKey(key);
}
}
}
@Override
public void parseMultiInstanceLoopCharacteristics(Element activityElement,
Element multiInstanceLoopCharacteristicsElement, ActivityImpl activity)
{
// Nothing to do here
}
public void setCompleteTaskListener(TaskListener completeTaskListener)
{
this.completeTaskListener = completeTaskListener;
}
public void setCreateTaskListener(TaskListener createTaskListener)
{
this.createTaskListener = createTaskListener;
}
public void setProcessCreateListener(ExecutionListener processCreateListener)
{
this.processCreateListener = processCreateListener;
}
/**
* @param tenantService
* the tenantService to set
*/
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
/**
* @param deployInTenant whether or not workflows should be deployed as a tenant-only workflow
* when it's deployed IF the tenantService is enabled and a tenant-context is currently active.
*/
public void setDeployWorkflowsInTenant(boolean deployInTenant) {
this.multiTenancyEnabled = deployInTenant;
}
@Override
public void parseInclusiveGateway(Element inclusiveGwElement,
ScopeImpl scope, ActivityImpl activity) {
// Nothing to do here
}
@Override
public void parseReceiveTask(Element receiveTaskElement, ScopeImpl scope,
ActivityImpl activity) {
// Nothing to do here
}
@Override
public void parseIntermediateSignalCatchEventDefinition(
Element signalEventDefinition, ActivityImpl signalActivity) {
// Nothing to do here
}
@Override
public void parseIntermediateMessageCatchEventDefinition(
Element messageEventDefinition, ActivityImpl nestedActivity) {
// Nothing to do here
}
@Override
public void parseBoundarySignalEventDefinition(
Element signalEventDefinition, boolean interrupting,
ActivityImpl signalActivity) {
// Nothing to do here
}
@Override
public void parseEventBasedGateway(Element eventBasedGwElement,
ScopeImpl scope, ActivityImpl activity) {
// Nothing to do here
}
@Override
public void parseTransaction(Element transactionElement, ScopeImpl scope,
ActivityImpl activity) {
// Nothing to do here
}
@Override
public void parseCompensateEventDefinition(
Element compensateEventDefinition, ActivityImpl compensationActivity) {
// Nothing to do here
}
@Override
public void parseIntermediateThrowEvent(Element intermediateEventElement,
ScopeImpl scope, ActivityImpl activity) {
// Nothing to do here
}
@Override
public void parseIntermediateCatchEvent(Element intermediateEventElement,
ScopeImpl scope, ActivityImpl activity) {
// Nothing to do here
}
@Override
public void parseBoundaryEvent(Element boundaryEventElement,
ScopeImpl scopeElement, ActivityImpl nestedActivity) {
// Nothing to do here
}
@Override
public void parseBoundaryMessageEventDefinition(Element element, boolean interrupting,
ActivityImpl messageActivity)
{
}
}

View File

@@ -0,0 +1,72 @@
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.activiti;
import org.activiti.bpmn.model.BaseElement;
import org.activiti.bpmn.model.CallActivity;
import org.activiti.engine.impl.bpmn.behavior.CallActivityBehavior;
import org.activiti.engine.impl.bpmn.parser.BpmnParse;
import org.activiti.engine.impl.bpmn.parser.handler.AbstractBpmnParseHandler;
import org.activiti.engine.impl.pvm.delegate.ActivityBehavior;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.parse.BpmnParseHandler;
import org.alfresco.repo.tenant.TenantService;
/**
* A {@link BpmnParseHandler} that makes a {@link CallActivity} tenant aware.
*
* @author Joram Barrez
* @author Frederik Heremans
* @author Nick Smith
*/
public class AlfrescoCallActivityBpmnParseHandler extends AbstractBpmnParseHandler<CallActivity>
{
private TenantService tenantService;
private boolean multiTenancyEnabled = true;
protected Class<? extends BaseElement> getHandledType()
{
return CallActivity.class;
}
protected void executeParse(BpmnParse bpmnParse, CallActivity callActivity)
{
if (multiTenancyEnabled && tenantService.isEnabled())
{
ActivityImpl activity = findActivity(bpmnParse, callActivity.getId());
ActivityBehavior activityBehavior = activity.getActivityBehavior();
if(activityBehavior instanceof CallActivityBehavior)
{
CallActivityBehavior callActivityBehavior = (CallActivityBehavior) activityBehavior;
// Make name of process-definition to be called aware of the current tenant
callActivityBehavior.setProcessDefinitonKey(tenantService.getName(callActivityBehavior.getProcessDefinitonKey()));
}
}
}
/**
* @param tenantService
* the tenantService to set
*/
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
public void setMultiTenancyEnabled(boolean multiTenancyEnabled)
{
this.multiTenancyEnabled = multiTenancyEnabled;
}
}

View File

@@ -0,0 +1,73 @@
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.activiti;
import org.activiti.bpmn.model.BaseElement;
import org.activiti.engine.delegate.ExecutionListener;
import org.activiti.engine.impl.bpmn.parser.BpmnParse;
import org.activiti.engine.impl.bpmn.parser.handler.AbstractBpmnParseHandler;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.parse.BpmnParseHandler;
import org.activiti.bpmn.model.Process;
import org.alfresco.repo.tenant.TenantService;
/**
* A {@link BpmnParseHandler} that adds a start listener to the process definition
* and makes the process definition tenant aware.
*
* @author Joram Barrez
* @author Frederik Heremans
* @author Nick Smith
*/
public class AlfrescoProcessBpmnParseHandler extends AbstractBpmnParseHandler<Process>
{
private ExecutionListener processCreateListener;
private TenantService tenantService;
private boolean multiTenancyEnabled = true;
protected Class<? extends BaseElement> getHandledType()
{
return Process.class;
}
protected void executeParse(BpmnParse bpmnParse, Process process)
{
ProcessDefinitionEntity processDefinition = bpmnParse.getCurrentProcessDefinition();
processDefinition.addExecutionListener(ExecutionListener.EVENTNAME_START, processCreateListener);
if (multiTenancyEnabled && tenantService.isEnabled())
{
String key = tenantService.getName(processDefinition.getKey());
processDefinition.setKey(key);
}
}
public void setProcessCreateListener(ExecutionListener processCreateListener)
{
this.processCreateListener = processCreateListener;
}
/**
* @param tenantService
* the tenantService to set
*/
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
public void setMultiTenancyEnabled(boolean multiTenancyEnabled)
{
this.multiTenancyEnabled = multiTenancyEnabled;
}
}

View File

@@ -0,0 +1,82 @@
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.activiti;
import java.util.ArrayList;
import java.util.List;
import org.activiti.bpmn.model.BaseElement;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.delegate.TaskListener;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.bpmn.parser.BpmnParse;
import org.activiti.engine.impl.bpmn.parser.handler.AbstractBpmnParseHandler;
import org.activiti.engine.impl.pvm.delegate.ActivityBehavior;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.parse.BpmnParseHandler;
/**
* A {@link BpmnParseHandler} that adds execution listeners to a
* {@link UserTask} which are specifically for Alfresco usage.
*
* @author Joram Barrez
* @author Frederik Heremans
* @author Nick Smith
*/
public class AlfrescoUserTaskBpmnParseHandler extends AbstractBpmnParseHandler<UserTask>
{
private TaskListener completeTaskListener;
private TaskListener createTaskListener;
protected Class<? extends BaseElement> getHandledType()
{
return UserTask.class;
}
protected void executeParse(BpmnParse bpmnParse, UserTask userTask)
{
ActivityImpl activity = findActivity(bpmnParse, userTask.getId());
ActivityBehavior activitybehaviour = activity.getActivityBehavior();
if (activitybehaviour instanceof UserTaskActivityBehavior)
{
UserTaskActivityBehavior userTaskActivity = (UserTaskActivityBehavior) activitybehaviour;
if (createTaskListener != null)
{
addTaskListenerAsFirst(createTaskListener, TaskListener.EVENTNAME_CREATE, userTaskActivity);
}
if (completeTaskListener != null)
{
addTaskListenerAsFirst(completeTaskListener, TaskListener.EVENTNAME_COMPLETE, userTaskActivity);
}
}
}
public void setCompleteTaskListener(TaskListener completeTaskListener)
{
this.completeTaskListener = completeTaskListener;
}
public void setCreateTaskListener(TaskListener createTaskListener)
{
this.createTaskListener = createTaskListener;
}
protected void addTaskListenerAsFirst(TaskListener taskListener, String eventName, UserTaskActivityBehavior userTask) {
List<TaskListener> taskEventListeners = userTask.getTaskDefinition().getTaskListeners().get(eventName);
if (taskEventListeners == null) {
taskEventListeners = new ArrayList<TaskListener>();
userTask.getTaskDefinition().getTaskListeners().put(eventName, taskEventListeners);
}
taskEventListeners.add(0, taskListener);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.workflow.activiti;
import java.util.Map;
import org.activiti.engine.delegate.ExecutionListener;
import org.alfresco.service.ServiceRegistry;
/**
* Base class for all {@link ExecutionListener}s used in Alfresco-context.
*
* @author Frederik Heremans
*/
public abstract class BaseExecutionListener implements ExecutionListener
{
private ServiceRegistry serviceRegistry;
/**
* Get the service-registry from the current Activiti-context.
*
* @return service registry
*/
protected ServiceRegistry getServiceRegistry()
{
return serviceRegistry;
}
/**
* @param serviceRegistry the serviceRegistry to set
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
public void setBeanRegistry(Map<Object, Object> beanRegistry)
{
beanRegistry.put(getName(), this);
}
/**
* Defaults to the full {@link Class} Name.
* @return
*/
protected String getName()
{
return getClass().getSimpleName();
}
}

View File

@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
<property name="databaseType" value="mysql" />
<property name="jdbcUrl" value="jdbc:mysql:///actint" />
<property name="jdbcDriver" value="org.gjt.mm.mysql.Driver" />
<property name="jdbcUsername" value="alfresco" />
<property name="jdbcPassword" value="alfresco" />
<!-- Database configurations -->
<property name="databaseSchemaUpdate" value="check-version" />
<!-- job executor configurations -->
<property name="jobExecutorActivate" value="false" />
<!-- mail server configurations -->
<property name="mailServerPort" value="5025" />
</bean>
</beans>

View File

@@ -1,235 +0,0 @@
create table ACT_GE_PROPERTY (
NAME_ varchar(255),
VALUE_ varchar(255),
REV_ integer,
primary key (NAME_)
) TYPE=InnoDB;
insert into ACT_GE_PROPERTY
values ('schema.version', '5.0.beta1', 1);
insert into ACT_GE_PROPERTY
values ('next.dbid', '1', 1);
create table ACT_GE_BYTEARRAY (
ID_ varchar(255),
REV_ integer,
NAME_ varchar(255),
DEPLOYMENT_ID_ varchar(255),
BYTES_ LONGBLOB,
primary key (ID_)
) TYPE=InnoDB;
create table ACT_RE_DEPLOYMENT (
ID_ varchar(255),
NAME_ varchar(255),
DEPLOY_TIME_ timestamp,
primary key (ID_)
) TYPE=InnoDB;
create table ACT_RU_EXECUTION (
ID_ varchar(255),
REV_ integer,
PROC_INST_ID_ varchar(255),
PARENT_ID_ varchar(255),
PROC_DEF_ID_ varchar(255),
SUPER_EXEC_ varchar(255),
ACTIVITY_ID_ varchar(255),
IS_ACTIVE_ TINYINT,
IS_CONCURRENT_ TINYINT,
IS_SCOPE_ TINYINT,
primary key (ID_)
) TYPE=InnoDB;
create table ACT_RU_JOB (
ID_ varchar(255) NOT NULL,
REV_ integer,
TYPE_ varchar(255) NOT NULL,
LOCK_EXP_TIME_ timestamp,
LOCK_OWNER_ varchar(255),
EXCLUSIVE_ boolean,
EXECUTION_ID_ varchar(255),
PROCESS_INSTANCE_ID_ varchar(255),
RETRIES_ integer,
EXCEPTION_ varchar(255),
DUEDATE_ timestamp NULL,
REPEAT_ varchar(255),
HANDLER_TYPE_ varchar(255),
HANDLER_CFG_ varchar(255),
primary key (ID_)
) TYPE=InnoDB;
create table ACT_ID_GROUP (
ID_ varchar(255),
REV_ integer,
NAME_ varchar(255),
TYPE_ varchar(255),
primary key (ID_)
) TYPE=InnoDB;
create table ACT_ID_MEMBERSHIP (
USER_ID_ varchar(255),
GROUP_ID_ varchar(255),
primary key (USER_ID_, GROUP_ID_)
) TYPE=InnoDB;
create table ACT_ID_USER (
ID_ varchar(255),
REV_ integer,
FIRST_ varchar(255),
LAST_ varchar(255),
EMAIL_ varchar(255),
PWD_ varchar(255),
primary key (ID_)
) TYPE=InnoDB;
create table ACT_RE_PROC_DEF (
ID_ varchar(255),
NAME_ varchar(255),
KEY_ varchar(255),
VERSION_ integer,
DEPLOYMENT_ID_ varchar(255),
RESOURCE_NAME_ varchar(255),
primary key (ID_)
) TYPE=InnoDB;
create table ACT_RU_TASK (
ID_ varchar(255),
REV_ integer,
EXECUTION_ID_ varchar(255),
PROC_INST_ID_ varchar(255),
PROC_DEF_ID_ varchar(255),
NAME_ varchar(255),
DESCRIPTION_ varchar(255),
FORM_ varchar(255),
ASSIGNEE_ varchar(255),
PRIORITY_ integer,
CREATE_TIME_ timestamp,
START_DEADLINE_ timestamp,
COMPLETION_DEADLINE_ timestamp,
SKIPPABLE_ TINYINT,
primary key (ID_)
) TYPE=InnoDB;
create table ACT_RU_TASKINVOLVEMENT (
ID_ varchar(255),
REV_ integer,
GROUP_ID_ varchar(255),
TYPE_ varchar(255),
USER_ID_ varchar(255),
TASK_ID_ varchar(255),
primary key (ID_)
) TYPE=InnoDB;
create table ACT_RU_VARIABLE (
ID_ varchar(255) not null,
REV_ integer,
TYPE_ varchar(255) not null,
NAME_ varchar(255) not null,
EXECUTION_ID_ varchar(255),
PROC_INST_ID_ varchar(255),
TASK_ID_ varchar(255),
BYTEARRAY_ID_ varchar(255),
DATE_ timestamp,
DOUBLE_ double,
LONG_ bigint,
TEXT_ varchar(255),
primary key (ID_)
) TYPE=InnoDB;
create table ACT_HI_PROC_INST (
ID_ varchar(255) not null,
PROC_INST_ID_ varchar(255) not null,
PROC_DEF_ID_ varchar(255) not null,
START_TIME_ datetime not null,
END_TIME_ datetime,
DURATION_ bigint,
-- TODO: check endStateName length
END_ACT_ID_ varchar(255),
primary key (ID_),
unique (PROC_INST_ID_)
) TYPE=InnoDB;
create table ACT_HI_ACT_INST (
ID_ varchar(255) not null,
ACT_ID_ varchar(255) not null,
ACT_NAME_ varchar(255),
ACT_TYPE_ varchar(255) not null,
PROC_INST_ID_ varchar(255) not null,
PROC_DEF_ID_ varchar(255) not null,
START_TIME_ datetime not null,
END_TIME_ datetime,
DURATION_ bigint,
primary key (ID_),
unique (ACT_ID_, PROC_INST_ID_)
) TYPE=InnoDB;
alter table ACT_GE_BYTEARRAY
add constraint FK_ACT_BYTEARR_DEPL
foreign key (DEPLOYMENT_ID_)
references ACT_RE_DEPLOYMENT (ID_);
alter table ACT_RU_EXECUTION
add constraint FK_ACT_EXE_PROCINST
foreign key (PROC_INST_ID_)
references ACT_RU_EXECUTION (ID_) on delete cascade on update cascade;
alter table ACT_RU_EXECUTION
add constraint FK_ACT_EXE_PARENT
foreign key (PARENT_ID_)
references ACT_RU_EXECUTION (ID_);
alter table ACT_RU_EXECUTION
add constraint FK_ACT_EXE_SUPER
foreign key (SUPER_EXEC_)
references ACT_RU_EXECUTION (ID_);
alter table ACT_ID_MEMBERSHIP
add constraint FK_ACT_MEMB_GROUP
foreign key (GROUP_ID_)
references ACT_ID_GROUP (ID_);
alter table ACT_ID_MEMBERSHIP
add constraint FK_ACT_MEMB_USER
foreign key (USER_ID_)
references ACT_ID_USER (ID_);
alter table ACT_RU_TASKINVOLVEMENT
add constraint FK_ACT_TSKASS_TASK
foreign key (TASK_ID_)
references ACT_RU_TASK (ID_);
alter table ACT_RU_TASK
add constraint FK_ACT_TASK_EXEC
foreign key (EXECUTION_ID_)
references ACT_RU_EXECUTION (ID_);
alter table ACT_RU_TASK
add constraint FK_ACT_TASK_PROCINST
foreign key (PROC_INST_ID_)
references ACT_RU_EXECUTION (ID_);
alter table ACT_RU_TASK
add constraint FK_ACT_TASK_PROCDEF
foreign key (PROC_DEF_ID_)
references ACT_RE_PROC_DEF (ID_);
alter table ACT_RU_VARIABLE
add constraint FK_ACT_VAR_TASK
foreign key (TASK_ID_)
references ACT_RU_TASK (ID_);
alter table ACT_RU_VARIABLE
add constraint FK_ACT_VAR_EXE
foreign key (EXECUTION_ID_)
references ACT_RU_EXECUTION (ID_);
alter table ACT_RU_VARIABLE
add constraint FK_ACT_VAR_PROCINST
foreign key (PROC_INST_ID_)
references ACT_RU_EXECUTION(ID_);
alter table ACT_RU_VARIABLE
add constraint FK_ACT_VAR_BYTEARRAY
foreign key (BYTEARRAY_ID_)
references ACT_GE_BYTEARRAY (ID_);

View File

@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<definitions id="adhoc-definitions"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://activiti.org/bpmn20"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:activiti="http://activiti.org/bpmn">
<process isExecutable="true" id="adhoc" name="Adhoc Process">
<startEvent id="start"
activiti:formKey="adhocStart.form" />
<sequenceFlow id='flow1'
sourceRef='start'
targetRef='adhocTask' />
<userTask id="adhocTask" name="Adhoc Task"
activiti:formKey="adhocTask.form" >
<documentation>
Perform some arbitrary human task.
</documentation>
<humanPerformer>
<resourceAssignmentExpression>
<formalExpression>${taskAssignee}</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
<sequenceFlow id='flow2'
sourceRef='adhocTask'
targetRef='verifyTaskDone' />
<userTask id="verifyTaskDone" name="Verify Adhoc Task Completed."
activiti:formKey="adhocDone.form" >
<documentation>
Verify the arbitrary task was completed.
</documentation>
<humanPerformer>
<resourceAssignmentExpression>
<formalExpression>${initiator}</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
<sequenceFlow id='flow3' sourceRef='verifyTaskDone'
targetRef='theEnd' />
<endEvent id="theEnd" />
</process>
</definitions>

View File

@@ -21,6 +21,8 @@ package org.alfresco.repo.workflow.activiti.listener;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.repo.workflow.BPMEngineRegistry;
import org.alfresco.repo.workflow.WorkflowConstants;
import org.alfresco.repo.workflow.activiti.ActivitiConstants;
@@ -35,10 +37,31 @@ import org.alfresco.repo.workflow.activiti.ActivitiConstants;
public class ProcessStartExecutionListener implements ExecutionListener
{
private static final long serialVersionUID = 1L;
protected TenantService tenantService;
protected boolean deployWorkflowsInTenant;
public void notify(DelegateExecution execution) throws Exception
{
// Add the workflow ID
String instanceId = BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, execution.getId());
execution.setVariable(WorkflowConstants.PROP_WORKFLOW_INSTANCE_ID, instanceId);
if(tenantService.isEnabled() || !deployWorkflowsInTenant)
{
// Add tenant as variable to the process. This will allow task-queries to filter out tasks that
// are not part of the calling user's domain
execution.setVariable(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain());
}
}
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
public void setDeployWorkflowsInTenant(boolean deployWorkflowsInTenant)
{
this.deployWorkflowsInTenant = deployWorkflowsInTenant;
}
}

View File

@@ -22,6 +22,7 @@ package org.alfresco.repo.workflow.activiti.properties;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -394,12 +395,14 @@ public class ActivitiPropertyConverter
}
catch (ConstraintException ce)
{
Integer defaultVal = Integer.valueOf(priorDef.getDefaultValue());
if (logger.isDebugEnabled())
{
logger.debug("Task priority value ("+existingValue+") was invalid so it was set to the default value of "+defaultVal+". Task:"+task.getName());
if(priorDef != null) {
Integer defaultVal = Integer.valueOf(priorDef.getDefaultValue());
if (logger.isDebugEnabled())
{
logger.debug("Task priority value ("+existingValue+") was invalid so it was set to the default value of "+defaultVal+". Task:"+task.getName());
}
defaultValues.put(WorkflowModel.PROP_PRIORITY, defaultVal);
}
defaultValues.put(WorkflowModel.PROP_PRIORITY, defaultVal);
}
// Special case for task description default value
@@ -496,6 +499,10 @@ public class ActivitiPropertyConverter
*/
public Map<String, Object> getStartVariables(HistoricProcessInstance historicProcessInstance)
{
if (historicProcessInstance.getStartActivityId() == null)
{
return Collections.emptyMap();
}
// Get historic variable values for start-event
HistoricActivityInstance startEvent = activitiUtil.getHistoryService()
.createHistoricActivityInstanceQuery()
@@ -945,6 +952,10 @@ public class ActivitiPropertyConverter
return handlerRegistry.handleVariablesToSet(defaultProperties, startTaskType, null, Void.class);
}
public WorkflowObjectFactory getWorkflowObjectFactory() {
return factory;
}
public void checkMandatoryProperties(DelegateTask task)
{
// Check all mandatory properties are set. This is checked here instead of in

View File

@@ -48,6 +48,8 @@ import org.alfresco.service.cmr.repository.ScriptService;
*/
public class ScriptTaskListener extends ActivitiScriptBase implements TaskListener
{
private static final long serialVersionUID = 1L;
private static final String TASK_BINDING_NAME = "task";
@Override

View File

@@ -47,6 +47,8 @@ import org.alfresco.service.namespace.NamespaceService;
*/
public class TaskCompleteListener implements TaskListener
{
private static final long serialVersionUID = 1L;
private ActivitiPropertyConverter propertyConverter;
private WorkflowQNameConverter qNameConverter;

View File

@@ -29,11 +29,14 @@ import org.activiti.engine.impl.form.TaskFormHandler;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.IdentityLinkEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.task.IdentityLinkType;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.workflow.WorkflowConstants;
import org.alfresco.repo.workflow.WorkflowNotificationUtils;
import org.alfresco.repo.workflow.activiti.ActivitiConstants;
import org.alfresco.repo.workflow.activiti.ActivitiScriptNode;
import org.alfresco.repo.workflow.activiti.properties.ActivitiPropertyConverter;
import org.alfresco.repo.workflow.jbpm.JBPMEngine;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
/**
@@ -45,6 +48,8 @@ import org.alfresco.service.cmr.repository.NodeRef;
*/
public class TaskCreateListener implements TaskListener
{
private static final long serialVersionUID = 1L;
private ActivitiPropertyConverter propertyConverter;
private WorkflowNotificationUtils workflowNotificationUtils;
@@ -62,12 +67,20 @@ public class TaskCreateListener implements TaskListener
// Set all default properties, based on the type-definition
propertyConverter.setDefaultTaskProperties(task);
String taskFormKey = getFormKey(task);
// Fetch definition and extract name again. Possible that the default is used if the provided is missing
TypeDefinition typeDefinition = propertyConverter.getWorkflowObjectFactory().getTaskTypeDefinition(taskFormKey, false);
taskFormKey = typeDefinition.getName().toPrefixString();
// The taskDefinition key is set as a variable in order to be available
// in the history
String taskFormKey = getFormKey(task);
if (taskFormKey != null)
{
task.setVariableLocal(ActivitiConstants.PROP_TASK_FORM_KEY, taskFormKey);
task.setVariableLocal(ActivitiConstants.PROP_TASK_FORM_KEY, taskFormKey);
// Add process initiator as involved person
ActivitiScriptNode initiatorNode = (ActivitiScriptNode) task.getExecution().getVariable(WorkflowConstants.PROP_INITIATOR);
if(initiatorNode != null) {
task.addUserIdentityLink((String) initiatorNode.getProperties().get(ContentModel.PROP_USERNAME.toPrefixString()), IdentityLinkType.STARTER);
}
// Determine whether we need to send the workflow notification or not