diff --git a/config/alfresco/activiti-context.xml b/config/alfresco/activiti-context.xml index e243ce6985..f4fbd8a48a 100644 --- a/config/alfresco/activiti-context.xml +++ b/config/alfresco/activiti-context.xml @@ -33,6 +33,8 @@ class="org.alfresco.repo.workflow.activiti.AddTaskListenerParseListener"> + + @@ -177,6 +179,8 @@ + + diff --git a/config/alfresco/content-publishing-context.xml b/config/alfresco/content-publishing-context.xml index 86f5ad4271..1de1929f1a 100644 --- a/config/alfresco/content-publishing-context.xml +++ b/config/alfresco/content-publishing-context.xml @@ -77,7 +77,7 @@ - + diff --git a/config/alfresco/web-publishing-context.xml b/config/alfresco/web-publishing-context.xml new file mode 100644 index 0000000000..1244520a8f --- /dev/null +++ b/config/alfresco/web-publishing-context.xml @@ -0,0 +1,119 @@ + + + + + + + + alfresco/model/publishingModel.xml + + + + + + + + + + jbpm + alfresco/workflow/publish_web_content_processdefinition.xml + text/xml + true + + + activiti + alfresco/workflow/publish-web-content.bpmn20.xml + text/xml + true + + + + + + alfresco/workflow/publishingWorkflowModel.xml + + + + + + + + alfresco.messages.publishing-service + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/workflow/publish-web-content.bpmn20.xml b/config/alfresco/workflow/publish-web-content.bpmn20.xml index 61ba4474cc..5a3d5a2138 100644 --- a/config/alfresco/workflow/publish-web-content.bpmn20.xml +++ b/config/alfresco/workflow/publish-web-content.bpmn20.xml @@ -6,13 +6,19 @@ xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://alfresco.org"> - + + + + + @@ -20,7 +26,7 @@ // Check if the publish should be scheduled or performed immediatly - if (typeof pubwf_scheduledPublishDate != 'undefined') { + if (typeof pubwf_scheduledPublishDate != 'undefined' && pubwf_scheduledPublishDate != null) { execution.setVariable("schedule", true); } else { execution.setVariable("schedule", false); @@ -39,7 +45,7 @@ - + ${schedule} @@ -60,15 +66,15 @@ - + - + ${iso8601PublishDate} - . + */ + +package org.alfresco.repo.publishing; + +import java.util.List; + +import org.alfresco.service.cmr.workflow.WorkflowPath; + + +/** + * @author Nick Smith + * @author Frederik Heremans + * @since 4.0 + * + */ +public class PublishWebContentActivitiTest extends PublishWebContentProcessTest +{ + private static final String DEF_NAME = "activiti$publishWebContent"; + + @Override + protected String getWorkflowDefinitionName() + { + return DEF_NAME; + } + + /** + * Activiti has 2 paths: a timer-scope-path and the main execution-path + */ + protected void checkNode(String expNode) + { + List paths = workflowService.getWorkflowPaths(instanceId); + assertEquals(2, paths.size()); + WorkflowPath path = paths.get(0); + assertEquals(expNode, path.getNode().getName()); + } +} diff --git a/source/java/org/alfresco/repo/publishing/PublishWebContentJbpmTest.java b/source/java/org/alfresco/repo/publishing/PublishWebContentJbpmTest.java index 4fd383a97c..c4af40551a 100644 --- a/source/java/org/alfresco/repo/publishing/PublishWebContentJbpmTest.java +++ b/source/java/org/alfresco/repo/publishing/PublishWebContentJbpmTest.java @@ -19,224 +19,21 @@ package org.alfresco.repo.publishing; -import static org.alfresco.model.ContentModel.ASSOC_CONTAINS; -import static org.alfresco.model.ContentModel.PROP_NAME; -import static org.alfresco.repo.publishing.PublishingModel.PROP_PUBLISHING_EVENT_STATUS; -import static org.alfresco.repo.publishing.PublishingModel.PROP_WF_PUBLISHING_EVENT; -import static org.alfresco.repo.publishing.PublishingModel.PROP_WF_SCHEDULED_PUBLISH_DATE; -import static org.alfresco.repo.publishing.PublishingModel.TYPE_PUBLISHING_EVENT; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.Serializable; -import java.util.Calendar; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.alfresco.repo.action.executer.ActionExecuter; -import org.alfresco.repo.model.Repository; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.repo.workflow.WorkflowModel; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.action.Action; -import org.alfresco.service.cmr.action.ActionDefinition; -import org.alfresco.service.cmr.publishing.Status; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.workflow.WorkflowDefinition; -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.ApplicationContextHelper; -import org.alfresco.util.BaseSpringTest; -import org.alfresco.util.GUID; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; /** * @author Nick Smith + * @author Frederik Heremans * @since 4.0 * */ -public class PublishWebContentJbpmTest extends BaseSpringTest +public class PublishWebContentJbpmTest extends PublishWebContentProcessTest { private static final String DEF_NAME = "jbpm$publishWebContent"; - - private ServiceRegistry serviceRegistry; - private Repository repositoryHelper; - private ActionExecuter publishEventAction; - private NodeService nodeService; - private WorkflowService workflowService; - private RetryingTransactionHelper transactionHelper; - private NodeRef event; - private String instanceId; - private long threadId; - - @Test - public void testProcessTimers() throws Exception - { - final Calendar scheduledTime = Calendar.getInstance(); - scheduledTime.add(Calendar.SECOND, 5); - - startWorkflowAndCommit(scheduledTime); - - // Should be waiting for scheduled time. - checkNode("waitForScheduledTime"); - - // Wait for scheduled time to elapse. - Thread.sleep(10000); - - // Should have ended - checkEnded(instanceId); - - // Check the Publish Event Action was called - verify(publishEventAction).execute(any(Action.class), any(NodeRef.class)); - - assertFalse("The action should be run from a different Thread!", Thread.currentThread().getId()==threadId); - } - - public void testProcessNoSchedule() throws Exception - { - startWorkflowAndCommit(null); - - // Wait for async action to execute - Thread.sleep(500); - - // Should have ended - checkEnded(instanceId); - - // Check the Publish Event Action was called - verify(publishEventAction).execute(any(Action.class), any(NodeRef.class)); - - assertFalse("The action should be run from a different Thread!", Thread.currentThread().getId()==threadId); - } - - private void checkEnded(String instanceId2) - { - WorkflowInstance instance = workflowService.getWorkflowById(instanceId2); - if(instance.isActive()) - { - List paths = workflowService.getWorkflowPaths(instance.getId()); - String nodeName = paths.get(0).getNode().getName(); - fail("Workflow should have ended! At node: " +nodeName); - } - } - - private void startWorkflowAndCommit(final Calendar scheduledTime) - { - RetryingTransactionCallback startWorkflowCallback = new RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - WorkflowPath path = startWorkflow(scheduledTime); - - // End the Start task. - WorkflowTask startTask = workflowService.getStartTask(path.getInstance().getId()); - workflowService.endTask(startTask.getId(), null); - return null; - } - }; - transactionHelper.doInTransaction(startWorkflowCallback); - } - - private WorkflowPath startWorkflow(Calendar scheduledTime) - { - WorkflowDefinition definition = workflowService.getDefinitionByName(DEF_NAME); - assertNotNull("The definition is null!", definition); - - NodeRef pckg = workflowService.createPackage(null); - - Map params = new HashMap(); - params.put(PROP_WF_PUBLISHING_EVENT, event); - params.put(PROP_WF_SCHEDULED_PUBLISH_DATE, scheduledTime); - params.put(WorkflowModel.ASSOC_PACKAGE, pckg); - - WorkflowPath path = workflowService.startWorkflow(definition.getId(), params); - assertNotNull(path); - this.instanceId = path.getInstance().getId(); - return path; - } - - private void checkNode(String expNode) - { - - List paths = workflowService.getWorkflowPaths(instanceId); - assertEquals(1, paths.size()); - WorkflowPath path = paths.get(0); - assertEquals(expNode, path.getNode().getName()); - } - - @Before - public void onSetUp() - { - serviceRegistry = (ServiceRegistry)getApplicationContext().getBean("ServiceRegistry"); - repositoryHelper = (Repository) getApplicationContext().getBean("repositoryHelper"); - publishEventAction = (ActionExecuter) getApplicationContext().getBean("pub_publishEvent"); - - reset(publishEventAction); - ActionDefinition actionDef = mock(ActionDefinition.class); - when(publishEventAction.getActionDefinition()).thenReturn(actionDef); - // Record thread action is run in. - Mockito.doAnswer(new Answer() - { - public Void answer(InvocationOnMock invocation) throws Throwable - { - threadId = Thread.currentThread().getId(); - return null; - } - }).when(publishEventAction).execute(any(Action.class), any(NodeRef.class)); - - this.workflowService = serviceRegistry.getWorkflowService(); - this.nodeService = serviceRegistry.getNodeService(); - this.transactionHelper = serviceRegistry.getRetryingTransactionHelper(); - AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName()); - NodeRef companyHome = repositoryHelper.getCompanyHome(); - String name = GUID.generate(); - Map props = new HashMap(); - props.put(PROP_NAME, name); - props.put(PROP_PUBLISHING_EVENT_STATUS, Status.SCHEDULED.name()); - - QName assocName = QName.createQNameWithValidLocalName(PublishingModel.NAMESPACE, name); - ChildAssociationRef eventAssoc = nodeService.createNode(companyHome, ASSOC_CONTAINS, assocName, TYPE_PUBLISHING_EVENT, props); - this.event = eventAssoc.getChildRef(); - } - @Override - protected String[] getConfigLocations() + protected String getWorkflowDefinitionName() { - return new String[] - { - ApplicationContextHelper.CONFIG_LOCATIONS[0], "classpath:test/alfresco/test-web-publishing--workflow-context.xml" - }; - } - - @After - public void onTearDown() - { - try - { - workflowService.cancelWorkflow(instanceId); - } - catch (Exception e) - { - // NOOP - } - nodeService.deleteNode(event); - AuthenticationUtil.clearCurrentSecurityContext(); + return DEF_NAME; } } diff --git a/source/java/org/alfresco/repo/publishing/PublishWebContentProcessTest.java b/source/java/org/alfresco/repo/publishing/PublishWebContentProcessTest.java new file mode 100644 index 0000000000..42fc1826dc --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/PublishWebContentProcessTest.java @@ -0,0 +1,246 @@ +/* + * 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 . + */ + +package org.alfresco.repo.publishing; + +import static org.alfresco.model.ContentModel.ASSOC_CONTAINS; +import static org.alfresco.model.ContentModel.PROP_NAME; +import static org.alfresco.repo.publishing.PublishingModel.PROP_PUBLISHING_EVENT_STATUS; +import static org.alfresco.repo.publishing.PublishingModel.PROP_WF_PUBLISHING_EVENT; +import static org.alfresco.repo.publishing.PublishingModel.PROP_WF_SCHEDULED_PUBLISH_DATE; +import static org.alfresco.repo.publishing.PublishingModel.TYPE_PUBLISHING_EVENT; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.Serializable; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.action.executer.ActionExecuter; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.workflow.WorkflowModel; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionDefinition; +import org.alfresco.service.cmr.publishing.Status; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.workflow.WorkflowDefinition; +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.ApplicationContextHelper; +import org.alfresco.util.BaseSpringTest; +import org.alfresco.util.GUID; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +/** + * @author Nick Smith + * @author Frederik Heremans + * + * @since 4.0 + * + */ +public abstract class PublishWebContentProcessTest extends BaseSpringTest +{ + protected ServiceRegistry serviceRegistry; + protected Repository repositoryHelper; + protected ActionExecuter publishEventAction; + + protected NodeService nodeService; + protected WorkflowService workflowService; + protected RetryingTransactionHelper transactionHelper; + protected NodeRef event; + protected String instanceId; + protected long threadId; + + /** + * Get definition to use + */ + protected abstract String getWorkflowDefinitionName(); + + @Test + public void testProcessTimers() throws Exception + { + final Calendar scheduledTime = Calendar.getInstance(); + scheduledTime.add(Calendar.SECOND, 5); + + startWorkflowAndCommit(scheduledTime); + + // Should be waiting for scheduled time. + checkNode("waitForScheduledTime"); + + // Wait for scheduled time to elapse. + Thread.sleep(10000); + + // Should have ended + checkEnded(instanceId); + + // Check the Publish Event Action was called + verify(publishEventAction).execute(any(Action.class), any(NodeRef.class)); + + assertFalse("The action should be run from a different Thread!", Thread.currentThread().getId()==threadId); + } + + public void testProcessNoSchedule() throws Exception + { + startWorkflowAndCommit(null); + + // Wait for async action to execute + Thread.sleep(500); + + // Should have ended + checkEnded(instanceId); + + // Check the Publish Event Action was called + verify(publishEventAction).execute(any(Action.class), any(NodeRef.class)); + + assertFalse("The action should be run from a different Thread!", Thread.currentThread().getId()==threadId); + } + + private void checkEnded(String instanceId2) + { + WorkflowInstance instance = workflowService.getWorkflowById(instanceId2); + if(instance.isActive()) + { + List paths = workflowService.getWorkflowPaths(instance.getId()); + String nodeName = paths.get(0).getNode().getName(); + fail("Workflow should have ended! At node: " +nodeName); + } + } + + private void startWorkflowAndCommit(final Calendar scheduledTime) + { + RetryingTransactionCallback startWorkflowCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + WorkflowPath path = startWorkflow(scheduledTime); + + // End the Start task. + WorkflowTask startTask = workflowService.getStartTask(path.getInstance().getId()); + workflowService.endTask(startTask.getId(), null); + return null; + } + }; + transactionHelper.doInTransaction(startWorkflowCallback); + } + + private WorkflowPath startWorkflow(Calendar scheduledTime) + { + WorkflowDefinition definition = workflowService.getDefinitionByName(getWorkflowDefinitionName()); + assertNotNull("The definition is null!", definition); + + NodeRef pckg = workflowService.createPackage(null); + + Map params = new HashMap(); + params.put(PROP_WF_PUBLISHING_EVENT, event); + params.put(PROP_WF_SCHEDULED_PUBLISH_DATE, scheduledTime); + params.put(WorkflowModel.ASSOC_PACKAGE, pckg); + + WorkflowPath path = workflowService.startWorkflow(definition.getId(), params); + assertNotNull(path); + this.instanceId = path.getInstance().getId(); + return path; + } + + protected void checkNode(String expNode) + { + List paths = workflowService.getWorkflowPaths(instanceId); + assertEquals(1, paths.size()); + WorkflowPath path = paths.get(0); + assertEquals(expNode, path.getNode().getName()); + } + + @Before + public void onSetUp() + { + serviceRegistry = (ServiceRegistry)getApplicationContext().getBean("ServiceRegistry"); + repositoryHelper = (Repository) getApplicationContext().getBean("repositoryHelper"); + publishEventAction = (ActionExecuter) getApplicationContext().getBean("pub_publishEvent"); + + reset(publishEventAction); + ActionDefinition actionDef = mock(ActionDefinition.class); + when(publishEventAction.getActionDefinition()).thenReturn(actionDef); + // Record thread action is run in. + Mockito.doAnswer(new Answer() + { + public Void answer(InvocationOnMock invocation) throws Throwable + { + threadId = Thread.currentThread().getId(); + return null; + } + }).when(publishEventAction).execute(any(Action.class), any(NodeRef.class)); + + this.workflowService = serviceRegistry.getWorkflowService(); + this.nodeService = serviceRegistry.getNodeService(); + this.transactionHelper = serviceRegistry.getRetryingTransactionHelper(); + AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName()); + NodeRef companyHome = repositoryHelper.getCompanyHome(); + String name = GUID.generate(); + Map props = new HashMap(); + props.put(PROP_NAME, name); + props.put(PROP_PUBLISHING_EVENT_STATUS, Status.SCHEDULED.name()); + + QName assocName = QName.createQNameWithValidLocalName(PublishingModel.NAMESPACE, name); + ChildAssociationRef eventAssoc = nodeService.createNode(companyHome, ASSOC_CONTAINS, assocName, TYPE_PUBLISHING_EVENT, props); + this.event = eventAssoc.getChildRef(); + } + + @Override + protected String[] getConfigLocations() + { + return new String[] + { + ApplicationContextHelper.CONFIG_LOCATIONS[0], "classpath:test/alfresco/test-web-publishing--workflow-context.xml" + }; + } + + @After + public void onTearDown() + { + try + { + workflowService.cancelWorkflow(instanceId); + } + catch (Exception e) + { + // NOOP + } + nodeService.deleteNode(event); + AuthenticationUtil.clearCurrentSecurityContext(); + } + +} diff --git a/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java b/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java index f60367620e..062122c1ea 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java +++ b/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java @@ -132,7 +132,7 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest WorkflowInstance instance = workflowService.getWorkflowById(wfId); assertNotNull(instance); List paths = workflowService.getWorkflowPaths(wfId); - assertEquals(1, paths.size()); + assertEquals(2, paths.size()); Map props = workflowService.getPathProperties(paths.get(0).getId()); assertEquals(eventNode, props.get(PROP_WF_PUBLISHING_EVENT)); assertEquals(schedule, props.get(PROP_WF_SCHEDULED_PUBLISH_DATE)); diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java b/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java index 2369442a7f..17a3eed923 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java +++ b/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java @@ -46,6 +46,7 @@ import org.activiti.engine.history.HistoricProcessInstance; 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; @@ -597,6 +598,16 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine } return null; } + + private boolean isReceiveTask(PvmActivity act) + { + if(act instanceof ActivityImpl) + { + ActivityImpl actImpl = (ActivityImpl) act; + return (actImpl.getActivityBehavior() instanceof ReceiveTaskActivityBehavior); + } + return false; + } private Collection findUserTasks(PvmActivity startEvent) { @@ -608,6 +619,17 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine return userTasks.values(); } + + private boolean isFirstActivity(PvmActivity activity, ReadOnlyProcessDefinition procDef) + { + if(procDef.getInitial().getOutgoingTransitions().size() == 1) + { + if(procDef.getInitial().getOutgoingTransitions().get(0).getDestination().equals(activity)) { + return true; + } + } + return false; + } private void findUserTasks(PvmActivity currentActivity, Map userTasks) { @@ -974,10 +996,14 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine // Start the process-instance ProcessInstance instance = runtimeService.startProcessInstanceById(processDefId, variables); - - // Set ID of workflowinstance after ProcessInstance is created - runtimeService.setVariable(instance.getId(), WorkflowConstants.PROP_WORKFLOW_INSTANCE_ID, createGlobalId(instance.getId())); - return typeConverter.convert((Execution)instance); + if(instance.isEnded()) + { + return typeConverter.buildCompletedPath(instance.getId(), instance.getId()); + } + else + { + return typeConverter.convert((Execution)instance); + } } catch (ActivitiException ae) { @@ -1299,8 +1325,35 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine // Set start task end date on the process runtimeService.setVariable(processInstanceId, ActivitiConstants.PROP_START_TASK_END_DATE, new Date()); - // Return virtual start task for the execution, it's safe to use the processInstanceId - return typeConverter.getVirtualStartTask(processInstanceId, false); + // Check if the current activity is a signalTask and the first activity in the process, + // this is a workaround for processes without any task/waitstates that should otherwise end + // when they are started. + ProcessInstance processInstance = activitiUtil.getProcessInstance(processInstanceId); + String currentActivity = ((ExecutionEntity)processInstance).getActivityId(); + + ReadOnlyProcessDefinition procDef = activitiUtil.getDeployedProcessDefinition(processInstance.getProcessDefinitionId()); + PvmActivity activity = procDef.findActivity(currentActivity); + if(isReceiveTask(activity) && isFirstActivity(activity, procDef)) + { + // Signal the process to start flowing, beginning from the recieve task + runtimeService.signal(processInstanceId); + + // It's possible the process has ended after signalling the receive task + processInstance = activitiUtil.getProcessInstance(processInstanceId); + if(processInstance != null) { + return typeConverter.getVirtualStartTask(processInstanceId, false); + } + else + { + return typeConverter.getVirtualStartTask(activitiUtil.getHistoricProcessInstance(processInstanceId)); + } + } + else + { + // Return virtual start task for the execution, it's safe to use the processInstanceId + return typeConverter.getVirtualStartTask(processInstanceId, false); + } + } /** diff --git a/source/java/org/alfresco/repo/workflow/activiti/AddTaskListenerParseListener.java b/source/java/org/alfresco/repo/workflow/activiti/AddTaskListenerParseListener.java index e835f7bf41..3c102458b5 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/AddTaskListenerParseListener.java +++ b/source/java/org/alfresco/repo/workflow/activiti/AddTaskListenerParseListener.java @@ -18,6 +18,7 @@ */ package org.alfresco.repo.workflow.activiti; +import org.activiti.engine.delegate.ExecutionListener; import org.activiti.engine.delegate.TaskListener; import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior; import org.activiti.engine.impl.bpmn.parser.BpmnParseListener; @@ -42,6 +43,7 @@ public class AddTaskListenerParseListener implements BpmnParseListener { private TaskListener completeTaskListener; private TaskListener createTaskListener; + private ExecutionListener processCreateListener; @Override public void parseUserTask(Element userTaskElement, ScopeImpl scope, ActivityImpl activity) @@ -64,7 +66,7 @@ public class AddTaskListenerParseListener implements BpmnParseListener @Override public void parseProcess(Element processElement, ProcessDefinitionEntity processDefinition) { - // Nothing to do here + processDefinition.addExecutionListener(ExecutionListener.EVENTNAME_START, processCreateListener); } @Override @@ -196,5 +198,10 @@ public class AddTaskListenerParseListener implements BpmnParseListener { this.createTaskListener = createTaskListener; } + + public void setProcessCreateListener(ExecutionListener processCreateListener) + { + this.processCreateListener = processCreateListener; + } } diff --git a/source/java/org/alfresco/repo/workflow/activiti/listener/ConvertDateToISO8601.java b/source/java/org/alfresco/repo/workflow/activiti/listener/ConvertDateToISO8601.java index 57d1224d28..eb3c79cc16 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/listener/ConvertDateToISO8601.java +++ b/source/java/org/alfresco/repo/workflow/activiti/listener/ConvertDateToISO8601.java @@ -19,6 +19,7 @@ package org.alfresco.repo.workflow.activiti.listener; +import java.util.Calendar; import java.util.Date; import org.activiti.engine.delegate.DelegateExecution; @@ -61,7 +62,19 @@ public class ConvertDateToISO8601 implements ExecutionListener throw new IllegalArgumentException("Both fields 'source' and 'target' shoudl be set"); } - Date dateToConvert = (Date) execution.getVariable(sourceVarName); + Object dateVar = execution.getVariable(sourceVarName); + Date dateToConvert = null; + + // Accept null, Date or Calendar as value + if(dateVar != null) { + if(dateVar instanceof Date) { + dateToConvert = (Date) execution.getVariable(sourceVarName); + } else if(dateVar instanceof Calendar) { + dateToConvert = ((Calendar) execution.getVariable(sourceVarName)).getTime(); + } else { + throw new IllegalArgumentException("Variable with name: " + sourceVarName + " must be a Date or a Calendar"); + } + } if(dateToConvert != null) { // Convert the date to ISO-8601 format String convertedDate = ISO8601DateFormat.format(dateToConvert); diff --git a/source/java/org/alfresco/repo/workflow/activiti/listener/ProcessStartExecutionListener.java b/source/java/org/alfresco/repo/workflow/activiti/listener/ProcessStartExecutionListener.java new file mode 100644 index 0000000000..c72cdd4f83 --- /dev/null +++ b/source/java/org/alfresco/repo/workflow/activiti/listener/ProcessStartExecutionListener.java @@ -0,0 +1,44 @@ +/* + * 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 . + */ + +package org.alfresco.repo.workflow.activiti.listener; + +import org.activiti.engine.delegate.DelegateExecution; +import org.activiti.engine.delegate.ExecutionListener; +import org.alfresco.repo.workflow.BPMEngineRegistry; +import org.alfresco.repo.workflow.WorkflowConstants; +import org.alfresco.repo.workflow.activiti.ActivitiConstants; + +/** + * An {@link ExecutionListener} that set all additional variables that are needed + * when process starts. + * + * @author Frederik Heremans + */ +public class ProcessStartExecutionListener implements ExecutionListener +{ + + public void notify(DelegateExecution execution) throws Exception + { + // Add the workflow ID + execution.setVariable(WorkflowConstants.PROP_WORKFLOW_INSTANCE_ID, BPMEngineRegistry + .createGlobalId(ActivitiConstants.ENGINE_ID, execution.getId())); + + } +}