From 36555986e8b9df1567868ace2f7c759719348258 Mon Sep 17 00:00:00 2001 From: meyerd Date: Wed, 15 Feb 2012 13:46:26 +0000 Subject: [PATCH] ACT-1103 adding support for a single message start event --- .../org/activiti/engine/RuntimeService.java | 51 ++++++++++- .../impl/EventSubscriptionQueryImpl.java | 2 +- .../impl/EventSubscriptionQueryProperty.java | 42 +++++++++ .../engine/impl/RuntimeServiceImpl.java | 9 ++ .../impl/bpmn/deployer/BpmnDeployer.java | 54 ++++++++++++ .../engine/impl/bpmn/parser/BpmnParse.java | 55 ++++++++++-- .../bpmn/parser/MessageEventDefinition.java | 50 +++++++++++ .../bpmn/webservice/MessageDefinition.java | 18 +++- .../cmd/StartProcessInstanceByMessageCmd.java | 85 +++++++++++++++++++ .../impl/event/MessageEventHandler.java | 34 ++++++++ .../persistence/entity/DeploymentManager.java | 47 ++++++++-- .../entity/EventSubscriptionEntity.java | 8 +- .../entity/EventSubscriptionManager.java | 22 ++++- .../persistence/entity/ExecutionEntity.java | 4 +- .../MessageEventSubscriptionEntity.java | 1 + .../entity/ProcessDefinitionEntity.java | 15 +++- .../pvm/process/ProcessDefinitionImpl.java | 31 +++++++ .../db/mapping/entity/EventSubscription.xml | 27 ++++++ .../event/message/MessageStartEventTest.java | 41 +++++++++ ...est.testSingleMessageStartEvent.bpmn20.xml | 24 ++++++ 20 files changed, 589 insertions(+), 31 deletions(-) create mode 100644 modules/activiti-engine/src/main/java/org/activiti/engine/impl/EventSubscriptionQueryProperty.java create mode 100644 modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/MessageEventDefinition.java create mode 100644 modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/StartProcessInstanceByMessageCmd.java create mode 100644 modules/activiti-engine/src/main/java/org/activiti/engine/impl/event/MessageEventHandler.java create mode 100644 modules/activiti-engine/src/test/java/org/activiti/engine/test/bpmn/event/message/MessageStartEventTest.java create mode 100644 modules/activiti-engine/src/test/resources/org/activiti/engine/test/bpmn/event/message/MessageStartEventTest.testSingleMessageStartEvent.bpmn20.xml diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/RuntimeService.java b/modules/activiti-engine/src/main/java/org/activiti/engine/RuntimeService.java index e1016ca721..017f196dc8 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/RuntimeService.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/RuntimeService.java @@ -29,6 +29,7 @@ import org.activiti.engine.runtime.ProcessInstanceQuery; * * @author Tom Baeyens * @author Joram Barrez + * @author Daniel Meyer */ public interface RuntimeService { @@ -149,6 +150,53 @@ public interface RuntimeService { * @throws ActivitiException when no process definition is deployed with the given key. */ ProcessInstance startProcessInstanceById(String processDefinitionId, String businessKey, Map variables); + + /** + *

Signals the process engine that a message is received and starts a new + * {@link ProcessInstance}.

+ * + *

Calling this method can have two different outcomes: + *

+ *

+ * + * @param messageName + * the 'name' of the message as specified as an attribute on the + * bpmn20 {@code } element. + * @param processVariables + * the 'payload' of the message. The variables are added as processes + * variables to the started process instance. + * @return the {@link ProcessInstance} object representing the started process instance + * + * @throws ActivitiExeception if no subscription to a message with the given name exists + * + * @since 5.9 + */ + ProcessInstance startProcessInstanceByMessage(String messageName, Map processVariables); + + /** + * See {@link #startProcessInstanceByMessage(String, Map)}. In addition, this method allows + * specifying a business key. + * + * @param messageName + * the 'name' of the message as specified as an attribute on the + * bpmn20 {@code } element. + * @param businessKey + * the business key which is added to the started process instance + * @param processVariables + * the 'payload' of the message. The variables are added as processes + * variables to the started process instance. + * @return the {@link ProcessInstance} object representing the started process instance + * + * @throws ActivitiExeception if no subscription to a message with the given name exists + * + * @since 5.9 + */ + ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey, Map processVariables); /** Delete an existing runtime process instance. * @param processInstanceId id of process instance to delete, cannot be null. @@ -286,6 +334,5 @@ public interface RuntimeService { * @throws ActivitiException if no such processInstance can be found or if the process instance is already in state active. */ void activateProcessInstanceById(String processInstanceId); - - + } \ No newline at end of file diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/EventSubscriptionQueryImpl.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/EventSubscriptionQueryImpl.java index 626c1151cc..08d24d9b47 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/EventSubscriptionQueryImpl.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/EventSubscriptionQueryImpl.java @@ -105,7 +105,7 @@ public class EventSubscriptionQueryImpl } public EventSubscriptionQuery orderByCreated() { - return this; + return orderBy(EventSubscriptionQueryProperty.CREATED); } //results ////////////////////////////////////////// diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/EventSubscriptionQueryProperty.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/EventSubscriptionQueryProperty.java new file mode 100644 index 0000000000..7a20e54193 --- /dev/null +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/EventSubscriptionQueryProperty.java @@ -0,0 +1,42 @@ +/* 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.activiti.engine.impl; + +import org.activiti.engine.query.QueryProperty; + + +/** + * @author Daniel Meyer + */ +public class EventSubscriptionQueryProperty implements QueryProperty { + + private static final long serialVersionUID = 1L; + + // properties used in event subscription queries: + + public final static EventSubscriptionQueryProperty CREATED = new EventSubscriptionQueryProperty("E.CREATED_"); + + ///////////////////////////////////////////////// + + private final String propertyName; + + public EventSubscriptionQueryProperty(String propertyName) { + this.propertyName = propertyName; + } + + public String getName() { + return propertyName; + } + +} diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/RuntimeServiceImpl.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/RuntimeServiceImpl.java index b37fe818b4..4dc1f702c8 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/RuntimeServiceImpl.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/RuntimeServiceImpl.java @@ -28,6 +28,7 @@ import org.activiti.engine.impl.cmd.GetExecutionVariablesCmd; import org.activiti.engine.impl.cmd.GetStartFormCmd; import org.activiti.engine.impl.cmd.SetExecutionVariablesCmd; import org.activiti.engine.impl.cmd.SignalCmd; +import org.activiti.engine.impl.cmd.StartProcessInstanceByMessageCmd; import org.activiti.engine.impl.cmd.StartProcessInstanceCmd; import org.activiti.engine.impl.cmd.SuspendProcessInstanceCmd; import org.activiti.engine.runtime.EventSubscriptionQuery; @@ -158,5 +159,13 @@ public class RuntimeServiceImpl extends ServiceImpl implements RuntimeService { public void activateProcessInstanceById(String processInstanceId) { commandExecutor.execute(new ActivateProcessInstanceCmd(processInstanceId)); } + + public ProcessInstance startProcessInstanceByMessage(String messageName, Map processVariables) { + return commandExecutor.execute(new StartProcessInstanceByMessageCmd(messageName, null, processVariables)); + } + + public ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey, Map processVariables) { + return commandExecutor.execute(new StartProcessInstanceByMessageCmd(messageName, businessKey, processVariables)); + } } diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/deployer/BpmnDeployer.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/deployer/BpmnDeployer.java index 0cf7aa658a..c521ccea22 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/deployer/BpmnDeployer.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/deployer/BpmnDeployer.java @@ -20,20 +20,25 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import org.activiti.engine.ActivitiException; import org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator; import org.activiti.engine.impl.bpmn.parser.BpmnParse; import org.activiti.engine.impl.bpmn.parser.BpmnParser; +import org.activiti.engine.impl.bpmn.parser.MessageEventDefinition; import org.activiti.engine.impl.cfg.IdGenerator; import org.activiti.engine.impl.cmd.DeleteJobsCmd; import org.activiti.engine.impl.context.Context; import org.activiti.engine.impl.db.DbSqlSession; import org.activiti.engine.impl.el.ExpressionManager; +import org.activiti.engine.impl.event.MessageEventHandler; import org.activiti.engine.impl.interceptor.CommandContext; import org.activiti.engine.impl.jobexecutor.TimerDeclarationImpl; import org.activiti.engine.impl.jobexecutor.TimerStartEventJobHandler; import org.activiti.engine.impl.persistence.deploy.Deployer; import org.activiti.engine.impl.persistence.deploy.DeploymentCache; import org.activiti.engine.impl.persistence.entity.DeploymentEntity; +import org.activiti.engine.impl.persistence.entity.EventSubscriptionEntity; +import org.activiti.engine.impl.persistence.entity.MessageEventSubscriptionEntity; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionManager; import org.activiti.engine.impl.persistence.entity.ResourceEntity; @@ -136,6 +141,9 @@ public class BpmnDeployer implements Deployer { removeObsoleteTimers(processDefinition); addTimerDeclarations(processDefinition); + + removeObsoleteMessageEventSubscriptions(processDefinition); + addMessageEventSubscriptions(processDefinition); dbSqlSession.insert(processDefinition); deploymentCache.addProcessDefinition(processDefinition); @@ -180,6 +188,52 @@ public class BpmnDeployer implements Deployer { new DeleteJobsCmd(job.getId()).execute(Context.getCommandContext()); } } + + protected void removeObsoleteMessageEventSubscriptions(ProcessDefinitionEntity processDefinition) { + // delete message event subscriptions: + List subscriptionsToDelete = Context.getCommandContext() + .getEventSubscriptionManager() + .findEventSubscriptionsByConfiguration(MessageEventHandler.TYPE, processDefinition.getKey()); + for (EventSubscriptionEntity eventSubscriptionEntity : subscriptionsToDelete) { + eventSubscriptionEntity.delete(); + } + } + + @SuppressWarnings("unchecked") + protected void addMessageEventSubscriptions(ProcessDefinitionEntity processDefinition) { + List messageEventDefinitions = (List) processDefinition.getProperty(BpmnParse.PROPERTYNAME_MESSAGE_EVENT_DEFINITIONS); + if(messageEventDefinitions == null) { + // this process has no subscriptions + return; + } + for (MessageEventDefinition messageEventDefinition : messageEventDefinitions) { + List subscriptionsForSameMessageName = Context.getCommandContext() + .getEventSubscriptionManager() + .findEventSubscriptionByName(MessageEventHandler.TYPE, messageEventDefinition.getName()); + List cachedSubscriptions = Context.getCommandContext() + .getDbSqlSession() + .findInCache(MessageEventSubscriptionEntity.class); + // also look for subscriptions created in the same command: + for (MessageEventSubscriptionEntity cachedSubscription : cachedSubscriptions) { + if(messageEventDefinition.getName().equals(cachedSubscription.getEventName())) { + subscriptionsForSameMessageName.add(cachedSubscription); + } + } + + if(!subscriptionsForSameMessageName.isEmpty()) { + throw new ActivitiException("Cannot deploy process definition '" + processDefinition.getDiagramResourceName() + + "': there already is a message event subscription for the message with name '" + messageEventDefinition.getName() + "'."); + } + + MessageEventSubscriptionEntity newSubscription = new MessageEventSubscriptionEntity(); + newSubscription.setEventName(messageEventDefinition.getName()); + newSubscription.setActivityId(messageEventDefinition.getActivityId()); + newSubscription.setConfiguration(processDefinition.getKey()); + + newSubscription.insert(); + } + + } /** diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/BpmnParse.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/BpmnParse.java index 512858f252..d257ab371e 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/BpmnParse.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/BpmnParse.java @@ -138,6 +138,7 @@ public class BpmnParse extends Parse { public static final String PROPERTYNAME_COMPENSATION_HANDLER_ID = "compensationHandler"; public static final String PROPERTYNAME_IS_FOR_COMPENSATION = "isForCompensation"; public static final String PROPERTYNAME_ERROR_EVENT_DEFINITIONS = "errorEventDefinitions"; + public static final String PROPERTYNAME_MESSAGE_EVENT_DEFINITIONS = "messageEventDefinitions"; /** The deployment to which the parsed process definitions will be added. */ protected DeploymentEntity deployment; @@ -364,14 +365,20 @@ public class BpmnParse extends Parse { for (Element messageElement : rootElement.elements("message")) { String id = messageElement.attribute("id"); String itemRef = this.resolveName(messageElement.attribute("itemRef")); - - if (!this.itemDefinitions.containsKey(itemRef)) { - addError(itemRef + " does not exist", messageElement); - } else { + String name = messageElement.attribute("name"); + + MessageDefinition messageDefinition = new MessageDefinition(id, name); + + if(itemRef != null) { ItemDefinition itemDefinition = this.itemDefinitions.get(itemRef); - MessageDefinition message = new MessageDefinition(this.targetNamespace + ":" + id, itemDefinition); - this.messages.put(message.getId(), message); + if(itemDefinition == null) { + addError(itemRef + " does not exist", messageElement); + } else { + messageDefinition.setItemDefinition(itemDefinition); + } } + + this.messages.put(messageDefinition.getId(), messageDefinition); } } @@ -683,15 +690,15 @@ public class BpmnParse extends Parse { */ public void parseStartEvents(Element parentElement, ScopeImpl scope) { List startEventElements = parentElement.elements("startEvent"); - if (startEventElements.size() > 1) { - addError("Multiple start events are currently unsupported", parentElement); - } else if (startEventElements.size() > 0) { + + if (startEventElements.size() > 0) { Element startEventElement = startEventElements.get(0); ActivityImpl startEventActivity = createActivityOnScope(startEventElement, scope); if (scope instanceof ProcessDefinitionEntity) { + ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) scope; if (processDefinition.getInitial() != null) { // in order to support this, the initial should here be replaced with @@ -718,8 +725,11 @@ public class BpmnParse extends Parse { } // only allowed for process... Element timerEventDefinition = startEventElement.element("timerEventDefinition"); + Element messageEventDefinition = startEventElement.element("messageEventDefinition"); if (timerEventDefinition != null) { parseTimerStartEventDefinition(timerEventDefinition, startEventActivity, processDefinition); + } else if(messageEventDefinition != null) { + parseMessageStartEventDefinition(messageEventDefinition, startEventActivity, processDefinition); } } else { scope.setProperty(PROPERTYNAME_INITIAL, startEventActivity); @@ -755,6 +765,33 @@ public class BpmnParse extends Parse { } } + protected void parseMessageStartEventDefinition(Element messageEventDefinition, ActivityImpl startEventActivity, ProcessDefinitionEntity processDefinition) { + String messageRef = messageEventDefinition.attribute("messageRef"); + if(messageRef == null) { + addError("attriute 'messageRef' is required", messageEventDefinition); + } + MessageDefinition messageDefinition = messages.get(messageRef); + if(messageDefinition == null) { + addError("Invalid 'messageRef': no message with id '"+messageRef+"' found.", messageEventDefinition); + } + + startEventActivity.setProperty("type", "messageStartEvent"); + + // create message event subscription: + MessageEventDefinition subscription = new MessageEventDefinition(messageDefinition.getId(), messageDefinition.getName(), startEventActivity.getId()); + addMessageEventDefinition(subscription, processDefinition); + } + + @SuppressWarnings("unchecked") + protected void addMessageEventDefinition(MessageEventDefinition subscription, ScopeImpl scope) { + List messageEventDefinitions = (List) scope.getProperty(PROPERTYNAME_MESSAGE_EVENT_DEFINITIONS); + if(messageEventDefinitions == null) { + messageEventDefinitions = new ArrayList(); + scope.setProperty(PROPERTYNAME_MESSAGE_EVENT_DEFINITIONS, messageEventDefinitions); + } + messageEventDefinitions.add(subscription); + } + /** * Parses the activities of a certain level in the process (process, * subprocess or another scope). diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/MessageEventDefinition.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/MessageEventDefinition.java new file mode 100644 index 0000000000..f0d4fea833 --- /dev/null +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/MessageEventDefinition.java @@ -0,0 +1,50 @@ +/* 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.activiti.engine.impl.bpmn.parser; + +import java.io.Serializable; + + +/** + * A subscription to a message event persisted in the process definition + * + * @author Daniel Meyer + */ +public class MessageEventDefinition implements Serializable { + + private static final long serialVersionUID = 1L; + + protected final String name; + protected final String id; + protected final String activityId; + + public MessageEventDefinition(String id, String name, String activityId) { + this.id = id; + this.name = name; + this.activityId = activityId; + } + + public String getName() { + return name; + } + + public String getId() { + return id; + } + + public String getActivityId() { + return activityId; + } + +} diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/webservice/MessageDefinition.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/webservice/MessageDefinition.java index 4a2a9703cf..ca4d7cc1ab 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/webservice/MessageDefinition.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/webservice/MessageDefinition.java @@ -25,10 +25,12 @@ public class MessageDefinition { protected String id; protected ItemDefinition itemDefinition; + + protected String name; - public MessageDefinition(String id, ItemDefinition itemDefinition) { + public MessageDefinition(String id, String name) { this.id = id; - this.itemDefinition = itemDefinition; + this.name = name; } public MessageInstance createInstance() { @@ -42,8 +44,20 @@ public class MessageDefinition { public StructureDefinition getStructureDefinition() { return this.itemDefinition.getStructureDefinition(); } + + public void setItemDefinition(ItemDefinition itemDefinition) { + this.itemDefinition = itemDefinition; + } public String getId() { return this.id; } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/StartProcessInstanceByMessageCmd.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/StartProcessInstanceByMessageCmd.java new file mode 100644 index 0000000000..59a46ecc74 --- /dev/null +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/StartProcessInstanceByMessageCmd.java @@ -0,0 +1,85 @@ +/* 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.activiti.engine.impl.cmd; + +import java.util.HashMap; +import java.util.Map; + +import org.activiti.engine.ActivitiException; +import org.activiti.engine.impl.context.Context; +import org.activiti.engine.impl.interceptor.Command; +import org.activiti.engine.impl.interceptor.CommandContext; +import org.activiti.engine.impl.persistence.deploy.DeploymentCache; +import org.activiti.engine.impl.persistence.entity.ExecutionEntity; +import org.activiti.engine.impl.persistence.entity.MessageEventSubscriptionEntity; +import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.activiti.engine.impl.pvm.process.ActivityImpl; +import org.activiti.engine.runtime.ProcessInstance; + + +/** + * @author Daniel Meyer + */ +public class StartProcessInstanceByMessageCmd implements Command { + + protected final String messageName; + protected final String businessKey; + protected final Map processVariables; + + public StartProcessInstanceByMessageCmd(String messageName, String businessKey, Map processVariables) { + this.messageName = messageName; + this.businessKey = businessKey; + this.processVariables = processVariables; + } + + public ProcessInstance execute(CommandContext commandContext) { + + if(messageName == null) { + throw new ActivitiException("Cannot start process instance by message: message name is null"); + } + + MessageEventSubscriptionEntity messageEventSubscription = commandContext.getEventSubscriptionManager() + .findMessageStartEventSubscriptionByName(messageName); + + if(messageEventSubscription == null) { + throw new ActivitiException("Cannot start process instance by message: no subscription to message with name '"+messageName+"' found."); + } + + String processDefinitionKey = messageEventSubscription.getConfiguration(); + if(processDefinitionKey == null) { + throw new ActivitiException("Cannot start process instance by message: subscription to message with name '"+messageName+"' is not a message start event."); + } + + DeploymentCache deploymentCache = Context + .getProcessEngineConfiguration() + .getDeploymentCache(); + + ProcessDefinitionEntity processDefinition = deploymentCache.findDeployedLatestProcessDefinitionByKey(processDefinitionKey); + if (processDefinition == null) { + throw new ActivitiException("No process definition found for key '" + processDefinitionKey + "'"); + } + + ActivityImpl startActivity = processDefinition.findActivity(messageEventSubscription.getActivityId()); + ExecutionEntity processInstance = processDefinition.createProcessInstance(businessKey, startActivity); + + if (processVariables != null) { + processInstance.setVariables(processVariables); + } + + processInstance.start(); + + return processInstance; + } + +} diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/event/MessageEventHandler.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/event/MessageEventHandler.java new file mode 100644 index 0000000000..7e4a741c36 --- /dev/null +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/event/MessageEventHandler.java @@ -0,0 +1,34 @@ +/* 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.activiti.engine.impl.event; + +import org.activiti.engine.impl.interceptor.CommandContext; +import org.activiti.engine.impl.persistence.entity.EventSubscriptionEntity; + + +/** + * @author Daniel Meyer + */ +public class MessageEventHandler implements EventHandler { + + public final static String TYPE = "message"; + + public String getEventHandlerType() { + return TYPE; + } + + public void handleEvent(EventSubscriptionEntity eventSubscription, Object payload, CommandContext commandContext) { + } + +} diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/DeploymentManager.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/DeploymentManager.java index 3b4e53a098..0a0377400c 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/DeploymentManager.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/DeploymentManager.java @@ -18,9 +18,12 @@ import java.util.List; import org.activiti.engine.impl.DeploymentQueryImpl; import org.activiti.engine.impl.Page; import org.activiti.engine.impl.context.Context; +import org.activiti.engine.impl.event.MessageEventHandler; +import org.activiti.engine.impl.jobexecutor.TimerStartEventJobHandler; import org.activiti.engine.impl.persistence.AbstractManager; import org.activiti.engine.repository.Deployment; import org.activiti.engine.repository.ProcessDefinition; +import org.activiti.engine.runtime.Job; /** @@ -43,28 +46,54 @@ public class DeploymentManager extends AbstractManager { } public void deleteDeployment(String deploymentId, boolean cascade) { + List processDefinitions = getDbSqlSession() + .createProcessDefinitionQuery() + .deploymentId(deploymentId) + .list(); + if (cascade) { - List processDefinitions = getDbSqlSession() - .createProcessDefinitionQuery() - .deploymentId(deploymentId) - .list(); + // delete process instances for (ProcessDefinition processDefinition: processDefinitions) { String processDefinitionId = processDefinition.getId(); getProcessInstanceManager() .deleteProcessInstancesByProcessDefinition(processDefinitionId, "deleted deployment", cascade); - - Context - .getProcessEngineConfiguration() - .getDeploymentCache() - .removeProcessDefinition(processDefinitionId); + } } + // delete process definitions from db getProcessDefinitionManager() .deleteProcessDefinitionsByDeploymentId(deploymentId); + for (ProcessDefinition processDefinition : processDefinitions) { + String processDefinitionId = processDefinition.getId(); + + // remove process definitions from cache: + Context + .getProcessEngineConfiguration() + .getDeploymentCache() + .removeProcessDefinition(processDefinitionId); + + // remove timer start events: + List timerStartJobs = Context.getCommandContext() + .getJobManager() + .findJobsByConfiguration(TimerStartEventJobHandler.TYPE, processDefinition.getKey()); + for (Job job : timerStartJobs) { + ((JobEntity)job).delete(); + } + + // remove message event subscriptions: + List findEventSubscriptionsByConfiguration = Context + .getCommandContext() + .getEventSubscriptionManager() + .findEventSubscriptionsByConfiguration(MessageEventHandler.TYPE, processDefinition.getKey()); + for (EventSubscriptionEntity eventSubscriptionEntity : findEventSubscriptionsByConfiguration) { + eventSubscriptionEntity.delete(); + } + } + getResourceManager() .deleteResourcesByDeploymentId(deploymentId); diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/EventSubscriptionEntity.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/EventSubscriptionEntity.java index d4a115fbec..a52d444ae1 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/EventSubscriptionEntity.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/EventSubscriptionEntity.java @@ -50,13 +50,15 @@ public abstract class EventSubscriptionEntity implements PersistentObject, Event ///////////////////////////////////////////// - public EventSubscriptionEntity() { } + public EventSubscriptionEntity() { + this.created = ClockUtil.getCurrentTime(); + } public EventSubscriptionEntity(ExecutionEntity executionEntity) { + this(); setExecution(executionEntity); setActivity(execution.getActivity()); - this.processInstanceId = executionEntity.getProcessDefinitionId(); - this.created = ClockUtil.getCurrentTime(); + this.processInstanceId = executionEntity.getProcessInstanceId(); } // processing ///////////////////////////// diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/EventSubscriptionManager.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/EventSubscriptionManager.java index 69e12d584f..741afcbcfb 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/EventSubscriptionManager.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/EventSubscriptionManager.java @@ -22,7 +22,6 @@ import java.util.Set; import org.activiti.engine.impl.EventSubscriptionQueryImpl; import org.activiti.engine.impl.Page; -import org.activiti.engine.impl.db.PersistentObject; import org.activiti.engine.impl.persistence.AbstractManager; import org.activiti.engine.runtime.EventSubscription; @@ -111,5 +110,26 @@ public class EventSubscriptionManager extends AbstractManager { params.put("activityId", activityId); return getDbSqlSession().selectList(query, params); } + + public List findEventSubscriptionsByConfiguration(String type, String configuration) { + final String query = "selectEventSubscriptionsByConfiguration"; + Map params = new HashMap(); + params.put("eventType", type); + params.put("configuration", configuration); + return getDbSqlSession().selectList(query, params); + } + + public List findEventSubscriptionByName(String type, String eventName) { + final String query = "selectEventSubscriptionsByName"; + Map params = new HashMap(); + params.put("eventType", type); + params.put("eventName", eventName); + return getDbSqlSession().selectList(query, params); + } + + public MessageEventSubscriptionEntity findMessageStartEventSubscriptionByName(String messageName) { + MessageEventSubscriptionEntity entity = (MessageEventSubscriptionEntity) getDbSqlSession().selectOne("selectMessageStartEventSubscriptionByName", messageName); + return entity; + } } diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ExecutionEntity.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ExecutionEntity.java index 727a5126cf..c29dd32fdb 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ExecutionEntity.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ExecutionEntity.java @@ -842,7 +842,7 @@ public class ExecutionEntity extends VariableScopeImpl implements ActivityExecut for (EventSubscriptionEntity eventSubscription : eventSubscriptions) { if (replacedBy!=null) { - eventSubscription.setExecutionId(replacedBy.getId()); + eventSubscription.setExecution(replacedBy); } else { eventSubscription.delete(); } @@ -850,7 +850,7 @@ public class ExecutionEntity extends VariableScopeImpl implements ActivityExecut for (CompensateEventSubscriptionEntity compensateEventSubscription : getCompensateEventSubscriptions()) { if (replacedBy!=null) { - compensateEventSubscription.setExecutionId(replacedBy.getId()); + compensateEventSubscription.setExecution(replacedBy); } else { removeCompensateEventSubscription(compensateEventSubscription); compensateEventSubscription.delete(); diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/MessageEventSubscriptionEntity.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/MessageEventSubscriptionEntity.java index bb003747de..2eb7b68d31 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/MessageEventSubscriptionEntity.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/MessageEventSubscriptionEntity.java @@ -13,6 +13,7 @@ package org.activiti.engine.impl.persistence.entity; + /** * @author Daniel Meyer */ diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ProcessDefinitionEntity.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ProcessDefinitionEntity.java index 4402eb792a..53394d0786 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ProcessDefinitionEntity.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ProcessDefinitionEntity.java @@ -26,6 +26,7 @@ import org.activiti.engine.impl.db.PersistentObject; import org.activiti.engine.impl.form.StartFormHandler; import org.activiti.engine.impl.identity.Authentication; import org.activiti.engine.impl.interceptor.CommandContext; +import org.activiti.engine.impl.pvm.process.ActivityImpl; import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl; import org.activiti.engine.impl.pvm.runtime.InterpretableExecution; import org.activiti.engine.impl.task.TaskDefinition; @@ -59,8 +60,14 @@ public class ProcessDefinitionEntity extends ProcessDefinitionImpl implements Pr super(null); } - public ExecutionEntity createProcessInstance(String businessKey) { - ExecutionEntity processInstance = (ExecutionEntity) super.createProcessInstance(); + public ExecutionEntity createProcessInstance(String businessKey, ActivityImpl initial) { + ExecutionEntity processInstance = null; + + if(initial == null) { + processInstance = (ExecutionEntity) super.createProcessInstance(); + }else { + processInstance = (ExecutionEntity) super.createProcessInstanceForInitial(initial); + } CommandContext commandContext = Context.getCommandContext(); @@ -115,11 +122,15 @@ public class ProcessDefinitionEntity extends ProcessDefinitionImpl implements Pr return processInstance; } + public ExecutionEntity createProcessInstance(String businessKey) { + return createProcessInstance(businessKey, null); + } public ExecutionEntity createProcessInstance() { return createProcessInstance(null); } + @Override protected InterpretableExecution newProcessInstance() { ExecutionEntity processInstance = new ExecutionEntity(); diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/pvm/process/ProcessDefinitionImpl.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/pvm/process/ProcessDefinitionImpl.java index eb4e331b1b..1dbb80fad5 100644 --- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/pvm/process/ProcessDefinitionImpl.java +++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/pvm/process/ProcessDefinitionImpl.java @@ -62,6 +62,37 @@ public class ProcessDefinitionImpl extends ScopeImpl implements PvmProcessDefini return processInstance; } + + /** creates a process instance using the provided activity as initial */ + public PvmProcessInstance createProcessInstanceForInitial(ActivityImpl startActivity) { + InterpretableExecution processInstance = newProcessInstance(); + processInstance.setProcessDefinition(this); + processInstance.setProcessInstance(processInstance); + processInstance.initialize(); + + InterpretableExecution scopeInstance = processInstance; + + ArrayList initialActivityStack = new ArrayList(); + ActivityImpl activity = startActivity; + while (activity!=null) { + initialActivityStack.add(0, activity); + activity = activity.getParentActivity(); + } + + for (ActivityImpl initialActivity: initialActivityStack) { + if (initialActivity.isScope()) { + scopeInstance = (InterpretableExecution) scopeInstance.createExecution(); + scopeInstance.setActivity(initialActivity); + if (initialActivity.isScope()) { + scopeInstance.initialize(); + } + } + } + + scopeInstance.setActivity(startActivity); + + return processInstance; + } public synchronized List getInitialActivityStack() { if (initialActivityStack==null) { diff --git a/modules/activiti-engine/src/main/resources/org/activiti/db/mapping/entity/EventSubscription.xml b/modules/activiti-engine/src/main/resources/org/activiti/db/mapping/entity/EventSubscription.xml index e2e5418bb5..5dfed2e8be 100644 --- a/modules/activiti-engine/src/main/resources/org/activiti/db/mapping/entity/EventSubscription.xml +++ b/modules/activiti-engine/src/main/resources/org/activiti/db/mapping/entity/EventSubscription.xml @@ -14,6 +14,10 @@ delete from ACT_RU_EVENT_SUBSCR where ID_ = #{id} + + delete from ACT_RU_EVENT_SUBSCR where ID_ = #{id} + + @@ -115,6 +119,29 @@ and (ACTIVITY_ID_ = #{activityId}) + + + + + + + diff --git a/modules/activiti-engine/src/test/java/org/activiti/engine/test/bpmn/event/message/MessageStartEventTest.java b/modules/activiti-engine/src/test/java/org/activiti/engine/test/bpmn/event/message/MessageStartEventTest.java new file mode 100644 index 0000000000..675753270e --- /dev/null +++ b/modules/activiti-engine/src/test/java/org/activiti/engine/test/bpmn/event/message/MessageStartEventTest.java @@ -0,0 +1,41 @@ +/* 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.activiti.engine.test.bpmn.event.message; + +import org.activiti.engine.impl.test.PluggableActivitiTestCase; +import org.activiti.engine.runtime.ProcessInstance; +import org.activiti.engine.task.Task; +import org.activiti.engine.test.Deployment; + + +/** + * @author Daniel Meyer + */ +public class MessageStartEventTest extends PluggableActivitiTestCase { + + @Deployment + public void testSingleMessageStartEvent() { + + ProcessInstance processInstance = runtimeService.startProcessInstanceByMessage("newInvoiceMessage", null); + + assertFalse(processInstance.isEnded()); + + Task task = taskService.createTaskQuery().singleResult(); + assertNotNull(task); + + taskService.complete(task.getId()); + + } + +} diff --git a/modules/activiti-engine/src/test/resources/org/activiti/engine/test/bpmn/event/message/MessageStartEventTest.testSingleMessageStartEvent.bpmn20.xml b/modules/activiti-engine/src/test/resources/org/activiti/engine/test/bpmn/event/message/MessageStartEventTest.testSingleMessageStartEvent.bpmn20.xml new file mode 100644 index 0000000000..0ae245846c --- /dev/null +++ b/modules/activiti-engine/src/test/resources/org/activiti/engine/test/bpmn/event/message/MessageStartEventTest.testSingleMessageStartEvent.bpmn20.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- GitLab