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:
+ *
+ * - If the message name is associated with a message start event, a new
+ * process instance is started.
+ * - If no subscription to a message with the given name exists, {@link ActivitiException}
+ * is thrown
+ *
+ *
+ *
+ * @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