├── .gitignore
├── src
├── main
│ ├── java
│ │ └── org
│ │ │ └── activiti
│ │ │ └── neo4j
│ │ │ ├── RelTypes.java
│ │ │ ├── behavior
│ │ │ ├── Behavior.java
│ │ │ ├── NoneStartEventBehavior.java
│ │ │ ├── NoneEndEventBehavior.java
│ │ │ ├── BehaviorMapping.java
│ │ │ ├── UserTaskBehavior.java
│ │ │ ├── AbstractBehavior.java
│ │ │ ├── BehaviorMappingImpl.java
│ │ │ ├── ServiceTaskBehaviour.java
│ │ │ ├── ParallelGatewayBehavior.java
│ │ │ └── ExclusiveGatewayBehavior.java
│ │ │ ├── ProcessDefinition.java
│ │ │ ├── Task.java
│ │ │ ├── Constants.java
│ │ │ ├── JavaDelegate.java
│ │ │ ├── Command.java
│ │ │ ├── SequenceFlow.java
│ │ │ ├── EngineOperations.java
│ │ │ ├── manager
│ │ │ ├── ExecutionManager.java
│ │ │ ├── TaskManager.java
│ │ │ ├── NodeBaseExecutionManager.java
│ │ │ └── NodeBasedTaskManager.java
│ │ │ ├── Core.java
│ │ │ ├── Activity.java
│ │ │ ├── PropertyContainer.java
│ │ │ ├── ProcessInstance.java
│ │ │ ├── Execution.java
│ │ │ ├── TaskService.java
│ │ │ ├── helper
│ │ │ └── BpmnModelUtil.java
│ │ │ ├── ProcessEngine.java
│ │ │ ├── CommandContext.java
│ │ │ ├── entity
│ │ │ ├── NodeBasedSequenceFlow.java
│ │ │ ├── NodeBasedActivity.java
│ │ │ ├── NodeBasedProcessInstance.java
│ │ │ └── NodeBasedExecution.java
│ │ │ ├── CoreImpl.java
│ │ │ ├── RuntimeService.java
│ │ │ ├── CommandExecutor.java
│ │ │ ├── ProcessEngineConfiguration.java
│ │ │ └── RepositoryService.java
│ └── resources
│ │ └── log4j.properties
└── test
│ ├── resources
│ ├── one-task-process.bpmn20.xml
│ ├── customJavaLogic.bpmn
│ └── parallel-process.bpmn
│ └── java
│ ├── delegates
│ └── GenerateNumberDelegate.java
│ └── ActivitiNeo4jTest.java
├── pom.xml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .project
3 | .settings/
4 | .shell_history
5 | target/
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/RelTypes.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j;
2 |
3 | import org.neo4j.graphdb.RelationshipType;
4 |
5 | public enum RelTypes implements RelationshipType {
6 | IS_STARTED_FROM,
7 | PROCESS_INSTANCE,
8 | PROCESS_DEFINITION,
9 | EXECUTION,
10 | SEQ_FLOW,
11 | VARIABLE
12 | }
--------------------------------------------------------------------------------
/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.rootLogger=DEBUG, CA
2 |
3 | # ConsoleAppender
4 | log4j.appender.CA=org.apache.log4j.ConsoleAppender
5 | log4j.appender.CA.layout=org.apache.log4j.PatternLayout
6 | log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
7 |
8 |
9 | log4j.logger.org.apache.ibatis.level=INFO
10 | log4j.logger.javax.activation.level=INFO
11 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/behavior/Behavior.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j.behavior;
2 |
3 | import org.activiti.neo4j.EngineOperations;
4 | import org.activiti.neo4j.Execution;
5 |
6 |
7 | public interface Behavior {
8 |
9 | void execute(Execution execution, EngineOperations engineOperations);
10 |
11 | void signal(Execution execution, EngineOperations engineOperations);
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/behavior/NoneStartEventBehavior.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j.behavior;
2 |
3 | import org.activiti.neo4j.EngineOperations;
4 | import org.activiti.neo4j.Execution;
5 |
6 |
7 | public class NoneStartEventBehavior extends AbstractBehavior {
8 |
9 | public void execute(Execution execution, EngineOperations engineOperations) {
10 | leave(execution, engineOperations);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/ProcessDefinition.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j;
2 |
3 |
4 | public class ProcessDefinition {
5 |
6 | protected long id;
7 | protected String key;
8 |
9 | public long getId() {
10 | return id;
11 | }
12 |
13 | public void setId(long id) {
14 | this.id = id;
15 | }
16 |
17 | public String getKey() {
18 | return key;
19 | }
20 |
21 | public void setKey(String key) {
22 | this.key = key;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/Task.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j;
2 |
3 | /**
4 | * @author jbarrez
5 | */
6 | public class Task {
7 |
8 | protected long id;
9 | protected String name;
10 |
11 | public long getId() {
12 | return id;
13 | }
14 |
15 | public void setId(long id) {
16 | this.id = id;
17 | }
18 |
19 | public String getName() {
20 | return name;
21 | }
22 |
23 | public void setName(String name) {
24 | this.name = name;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/behavior/NoneEndEventBehavior.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j.behavior;
2 |
3 | import org.activiti.neo4j.EngineOperations;
4 | import org.activiti.neo4j.Execution;
5 | import org.activiti.neo4j.ProcessInstance;
6 |
7 |
8 | public class NoneEndEventBehavior extends AbstractBehavior {
9 |
10 | public void execute(Execution execution, EngineOperations engineOperations) {
11 | // Remove process instance node
12 | ProcessInstance processInstance = execution.getProcessInstance();
13 | processInstance.delete();
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/Constants.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j;
2 |
3 |
4 | public interface Constants {
5 |
6 | // Indexes
7 | String PROCESS_DEFINITION_INDEX = "process-definitions-index";
8 | String TASK_INDEX = "task-index";
9 |
10 | // Index key
11 | String INDEX_KEY_PROCESS_DEFINITION_KEY = "processDefinitionKey";
12 |
13 | String INDEX_KEY_TASK_ASSIGNEE = "taskAssignee";
14 |
15 | // Behavior types
16 | String TYPE_START_EVENT = "startEvent";
17 | String TYPE_END_EVENT = "endEvent";
18 | String TYPE_USER_TASK = "userTask";
19 | String TYPE_PARALLEL_GATEWAY = "parallelGateway";
20 | String TYPE_EXCLUSIVE_GATEWAY = "exclusiveGateway";
21 | String TYPE_SERVICE_TASK = "serviceTask";
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/resources/one-task-process.bpmn20.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | This is a process for testing purposes
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/JavaDelegate.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 |
16 |
17 |
18 | /**
19 | * @author Joram Barrez
20 | */
21 | public interface JavaDelegate {
22 |
23 | void execute(Execution execution);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/Command.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 |
16 |
17 | /**
18 | * @author Joram Barrez
19 | */
20 | public interface Command {
21 |
22 | void execute(CommandContext commandContext);
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/SequenceFlow.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 |
16 | /**
17 | * @author Joram Barrez
18 | */
19 | public interface SequenceFlow extends PropertyContainer {
20 |
21 | Activity getSourceActivity();
22 | Activity getTargetActivity();
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/EngineOperations.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 |
16 |
17 | /**
18 | * @author Joram Barrez
19 | */
20 | public interface EngineOperations {
21 |
22 | void continueProcess(Execution execution);
23 |
24 | void signal(Execution execution);
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/manager/ExecutionManager.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.manager;
14 |
15 | import org.activiti.neo4j.Execution;
16 |
17 |
18 | /**
19 | * @author Joram Barrez
20 | */
21 | public interface ExecutionManager {
22 |
23 | Execution getExecutionById(long id);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/behavior/BehaviorMapping.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.behavior;
14 |
15 |
16 |
17 | /**
18 | * @author Joram Barrez
19 | */
20 | public interface BehaviorMapping {
21 |
22 | Behavior getBehaviorForType(String type);
23 |
24 | void setBehavior(String type, Behavior behavior);
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/manager/TaskManager.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.manager;
14 |
15 | import java.util.List;
16 |
17 | import org.activiti.neo4j.Task;
18 |
19 |
20 | /**
21 | * @author Joram Barrez
22 | */
23 | public interface TaskManager {
24 |
25 | public List getTasksByAssignee(String assignee);
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/Core.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 |
16 |
17 |
18 | /**
19 | * @author Joram Barrez
20 | */
21 | public interface Core {
22 |
23 | void continueProcess(CommandContext> commandContext, Execution execution);
24 |
25 | void signal(CommandContext> commandContext, Execution execution);
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/Activity.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 | import java.util.List;
16 |
17 |
18 |
19 | /**
20 | * @author Joram Barrez
21 | */
22 | public interface Activity extends PropertyContainer {
23 |
24 | String getId();
25 |
26 | List getIncomingSequenceFlow();
27 | List getOutgoingSequenceFlow();
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/PropertyContainer.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 |
16 | /**
17 | * @author Joram Barrez
18 | */
19 | public interface PropertyContainer {
20 |
21 | boolean hasProperty(String property);
22 | Object getProperty(String property);
23 | void setProperty(String property, Object value);
24 | Object removeProperty(String property);
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/ProcessInstance.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 | import java.util.List;
16 |
17 |
18 | /**
19 | * @author Joram Barrez
20 | */
21 | public interface ProcessInstance {
22 |
23 | void setVariable(String variableName, Object variableValue);
24 |
25 | Execution createNewExecutionInActivity(Activity activity);
26 |
27 | List getExecutions();
28 |
29 | void delete();
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/Execution.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 |
16 | /**
17 | * @author Joram Barrez
18 | */
19 | public interface Execution extends PropertyContainer {
20 |
21 | void delete();
22 |
23 | ProcessInstance getProcessInstance();
24 |
25 | Activity getActivity();
26 |
27 | void setVariable(String name, Object value);
28 |
29 | Object getVariable(String name);
30 |
31 | void addToIndex(String namespace, String key, Object value);
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/behavior/UserTaskBehavior.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j.behavior;
2 |
3 | import org.activiti.neo4j.Activity;
4 | import org.activiti.neo4j.Constants;
5 | import org.activiti.neo4j.EngineOperations;
6 | import org.activiti.neo4j.Execution;
7 |
8 |
9 | public class UserTaskBehavior extends AbstractBehavior {
10 |
11 | public void execute(Execution execution, EngineOperations engineOperations) {
12 |
13 | Activity taskActivity = execution.getActivity();
14 |
15 | execution.setProperty("isTask", true);
16 | execution.setProperty("name", taskActivity.getProperty("name"));
17 |
18 | // Add to task index
19 | execution.addToIndex(Constants.TASK_INDEX, Constants.INDEX_KEY_TASK_ASSIGNEE, taskActivity.getProperty("assignee"));
20 |
21 | // No leave(), task == wait state
22 | }
23 |
24 | @Override
25 | public void signal(Execution execution, EngineOperations engineOperations) {
26 |
27 | // remove properties from execution
28 | // TODO: need to find a better way to manage this
29 | execution.removeProperty("isTask");
30 | execution.removeProperty("name");
31 |
32 | // Leave step
33 | leave(execution, engineOperations);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/java/delegates/GenerateNumberDelegate.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package delegates;
14 |
15 | import java.util.Random;
16 |
17 | import org.activiti.neo4j.Execution;
18 | import org.activiti.neo4j.JavaDelegate;
19 |
20 |
21 | /**
22 | * @author Joram Barrez
23 | */
24 | public class GenerateNumberDelegate implements JavaDelegate {
25 |
26 | public void execute(Execution execution) {
27 | execution.setVariable("number", generateRandomNumber());
28 | }
29 |
30 | protected int generateRandomNumber() {
31 | Random random = new Random();
32 | int number = random.nextInt(100);
33 | System.out.println("Random generated number is " + number);
34 | return number;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/TaskService.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j;
2 |
3 | import java.util.List;
4 |
5 | import org.activiti.neo4j.manager.TaskManager;
6 |
7 | public class TaskService {
8 |
9 | // TODO: can be put in a command service super class
10 | protected CommandExecutor commandExecutor;
11 | protected TaskManager taskManager;
12 |
13 | public TaskService(CommandExecutor commandExecutor) {
14 | this.commandExecutor = commandExecutor;
15 | }
16 |
17 | public List findTasksFor(final String assignee) {
18 | return commandExecutor.execute(new Command>() {
19 |
20 | public void execute(CommandContext> commandContext) {
21 | commandContext.setResult(taskManager.getTasksByAssignee(assignee));
22 | }
23 |
24 | });
25 | }
26 |
27 | public void complete(final long taskId) {
28 | commandExecutor.execute(new Command() {
29 |
30 | public void execute(CommandContext commandContext) {
31 | commandContext.signal(commandContext.getExecutionManager().getExecutionById(taskId));
32 | }
33 |
34 | });
35 | }
36 |
37 | public TaskManager getTaskManager() {
38 | return taskManager;
39 | }
40 |
41 | public void setTaskManager(TaskManager taskManager) {
42 | this.taskManager = taskManager;
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/helper/BpmnModelUtil.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j.helper;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.activiti.bpmn.model.FlowElement;
7 | import org.activiti.bpmn.model.Process;
8 | import org.activiti.bpmn.model.SequenceFlow;
9 |
10 |
11 | public class BpmnModelUtil {
12 |
13 | public static List findSucessorFlowElementsFor(Process process, FlowElement sourceFlowElement) {
14 | List successors = new ArrayList();
15 | for (SequenceFlow sequenceFlow : findFlowElementsOfType(process, SequenceFlow.class)) {
16 | if (sequenceFlow.getSourceRef().equals(sourceFlowElement.getId())) {
17 | successors.add(process.getFlowElement(sequenceFlow.getTargetRef()));
18 | }
19 | }
20 | return successors;
21 | }
22 |
23 | @SuppressWarnings("unchecked")
24 | public static List findFlowElementsOfType(Process process, Class type) {
25 | List flowElements = new ArrayList();
26 | for (FlowElement flowElement : process.getFlowElements()) {
27 | if (type.isInstance(flowElement)) {
28 | flowElements.add((FlowElementType) flowElement);
29 | }
30 | }
31 | return flowElements;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/manager/NodeBaseExecutionManager.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.manager;
14 |
15 | import org.activiti.neo4j.Execution;
16 | import org.activiti.neo4j.entity.NodeBasedExecution;
17 | import org.neo4j.graphdb.GraphDatabaseService;
18 |
19 |
20 | /**
21 | * @author Joram Barrez
22 | */
23 | public class NodeBaseExecutionManager implements ExecutionManager {
24 |
25 | protected GraphDatabaseService graphDb;
26 |
27 | public Execution getExecutionById(long id) {
28 | return new NodeBasedExecution(graphDb.getRelationshipById(id));
29 | }
30 |
31 | public GraphDatabaseService getGraphDb() {
32 | return graphDb;
33 | }
34 |
35 | public void setGraphDb(GraphDatabaseService graphDb) {
36 | this.graphDb = graphDb;
37 | }
38 |
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/ProcessEngine.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j;
2 |
3 | import org.neo4j.graphdb.GraphDatabaseService;
4 |
5 |
6 | public class ProcessEngine {
7 |
8 | protected GraphDatabaseService graphDatabaseService;
9 |
10 | protected RepositoryService repositoryService;
11 | protected RuntimeService runtimeService;
12 | protected TaskService taskService;
13 |
14 | protected CommandExecutor commandExecutor;
15 |
16 | public ProcessEngine() {
17 |
18 | }
19 |
20 | public GraphDatabaseService getGraphDatabaseService() {
21 | return graphDatabaseService;
22 | }
23 |
24 | public void setGraphDatabaseService(GraphDatabaseService graphDatabaseService) {
25 | this.graphDatabaseService = graphDatabaseService;
26 | }
27 |
28 | public RepositoryService getRepositoryService() {
29 | return repositoryService;
30 | }
31 |
32 | public void setRepositoryService(RepositoryService repositoryService) {
33 | this.repositoryService = repositoryService;
34 | }
35 |
36 | public RuntimeService getRuntimeService() {
37 | return runtimeService;
38 | }
39 |
40 | public void setRuntimeService(RuntimeService runtimeService) {
41 | this.runtimeService = runtimeService;
42 | }
43 |
44 | public TaskService getTaskService() {
45 | return taskService;
46 | }
47 |
48 | public void setTaskService(TaskService taskService) {
49 | this.taskService = taskService;
50 | }
51 |
52 | public CommandExecutor getCommandExecutor() {
53 | return commandExecutor;
54 | }
55 |
56 | public void setCommandExecutor(CommandExecutor commandExecutor) {
57 | this.commandExecutor = commandExecutor;
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/behavior/AbstractBehavior.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j.behavior;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.activiti.neo4j.Activity;
7 | import org.activiti.neo4j.EngineOperations;
8 | import org.activiti.neo4j.Execution;
9 | import org.activiti.neo4j.SequenceFlow;
10 |
11 |
12 | public abstract class AbstractBehavior implements Behavior {
13 |
14 | protected void leave(Execution execution, EngineOperations engineOperations) {
15 |
16 | List outgoingExecutions = new ArrayList();
17 | for (SequenceFlow sequenceFlow : execution.getActivity().getOutgoingSequenceFlow()) {
18 | Activity targetActivity = sequenceFlow.getTargetActivity();
19 | outgoingExecutions.add(execution.getProcessInstance().createNewExecutionInActivity(targetActivity));
20 | }
21 |
22 | // TODO: is it possible to reuse this execute for a little bit extra performance?
23 | // Or is this just overengineering? (we did it for exections in regular activiti ...)
24 | execution.delete();
25 |
26 | for (Execution outgoingExecuions : outgoingExecutions) {
27 | engineOperations.continueProcess(outgoingExecuions);
28 | }
29 |
30 | }
31 |
32 | protected void goToNextActivity(Activity activity, Execution execution, EngineOperations engineOperations) {
33 | Execution outgoingExecution = execution.getProcessInstance().createNewExecutionInActivity(activity);
34 | execution.delete();
35 | engineOperations.continueProcess(outgoingExecution);
36 | }
37 |
38 | public void signal(Execution execution, EngineOperations engineOperations) {
39 | throw new RuntimeException("This behavior does not accept signals");
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/behavior/BehaviorMappingImpl.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.behavior;
14 |
15 | import java.util.HashMap;
16 |
17 | import org.activiti.neo4j.Constants;
18 |
19 |
20 | /**
21 | * @author Joram Barrez
22 | */
23 | public class BehaviorMappingImpl implements BehaviorMapping {
24 |
25 | protected static final HashMap behaviorMapping;
26 |
27 | static {
28 | behaviorMapping = new HashMap();
29 |
30 | behaviorMapping.put(Constants.TYPE_START_EVENT, new NoneStartEventBehavior());
31 | behaviorMapping.put(Constants.TYPE_USER_TASK, new UserTaskBehavior());
32 | behaviorMapping.put(Constants.TYPE_END_EVENT, new NoneEndEventBehavior());
33 | behaviorMapping.put(Constants.TYPE_PARALLEL_GATEWAY, new ParallelGatewayBehavior());
34 | behaviorMapping.put(Constants.TYPE_SERVICE_TASK, new ServiceTaskBehaviour());
35 | behaviorMapping.put(Constants.TYPE_EXCLUSIVE_GATEWAY, new ExclusiveGatewayBehavior());
36 | }
37 |
38 | public Behavior getBehaviorForType(String type) {
39 | return behaviorMapping.get(type);
40 | }
41 |
42 | public void setBehavior(String type, Behavior behavior) {
43 | behaviorMapping.put(type, behavior);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/manager/NodeBasedTaskManager.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.manager;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | import org.activiti.neo4j.Constants;
19 | import org.activiti.neo4j.Task;
20 | import org.neo4j.graphdb.GraphDatabaseService;
21 | import org.neo4j.graphdb.Relationship;
22 | import org.neo4j.graphdb.index.Index;
23 |
24 |
25 | /**
26 | * @author Joram Barrez
27 | */
28 | public class NodeBasedTaskManager implements TaskManager {
29 |
30 | protected GraphDatabaseService graphDb;
31 |
32 | public List getTasksByAssignee(String assignee) {
33 | List tasks = new ArrayList();
34 | Index taskIndex = graphDb.index().forRelationships(Constants.TASK_INDEX);
35 | for (Relationship execution : taskIndex.get(Constants.INDEX_KEY_TASK_ASSIGNEE, assignee)) {
36 | Task task = new Task();
37 | task.setId(execution.getId());
38 | task.setName((String) execution.getProperty("name"));
39 | tasks.add(task);
40 | }
41 | return tasks;
42 | }
43 |
44 | public GraphDatabaseService getGraphDb() {
45 | return graphDb;
46 | }
47 |
48 | public void setGraphDb(GraphDatabaseService graphDb) {
49 | this.graphDb = graphDb;
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/behavior/ServiceTaskBehaviour.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.behavior;
14 |
15 | import org.activiti.neo4j.Activity;
16 | import org.activiti.neo4j.EngineOperations;
17 | import org.activiti.neo4j.Execution;
18 | import org.activiti.neo4j.JavaDelegate;
19 |
20 |
21 |
22 | /**
23 | * @author Joram Barrez
24 | */
25 | public class ServiceTaskBehaviour extends AbstractBehavior {
26 |
27 | public void execute(Execution execution, EngineOperations engineOperations) {
28 | executeDelegate(execution);
29 | leave(execution, engineOperations);
30 | }
31 |
32 | protected void executeDelegate(Execution execution) {
33 | Activity activity = execution.getActivity();
34 | String className = (String) activity.getProperty("class");
35 |
36 | try {
37 | Class> clazz = Class.forName(className);
38 |
39 | if (!JavaDelegate.class.isAssignableFrom(clazz)) {
40 | throw new RuntimeException("The referenced class " + clazz.getCanonicalName() + "does not implement a correct delegation class");
41 | }
42 | JavaDelegate javaDelegate = (JavaDelegate) clazz.newInstance();
43 | javaDelegate.execute(execution);
44 |
45 | } catch (Exception e) {
46 | e.printStackTrace();
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/behavior/ParallelGatewayBehavior.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j.behavior;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.activiti.neo4j.Activity;
7 | import org.activiti.neo4j.EngineOperations;
8 | import org.activiti.neo4j.Execution;
9 | import org.activiti.neo4j.ProcessInstance;
10 |
11 |
12 | public class ParallelGatewayBehavior extends AbstractBehavior {
13 |
14 | public void execute(Execution execution, EngineOperations engineOperations) {
15 |
16 | Activity parallelGatewayActivity = execution.getActivity();
17 |
18 | // Check the number of incoming to see if we have to join or fork
19 | int incomingSequenceFlowCount = execution.getActivity().getIncomingSequenceFlow().size();
20 |
21 | if (incomingSequenceFlowCount == 1) {
22 |
23 | // Fork immediately (optimisation)
24 | leave(execution, engineOperations);
25 |
26 | } else {
27 |
28 | // Join: if number of executions arrived here = number of incoming sequenceflow -> continue
29 | List waitingExecutions = new ArrayList();
30 | waitingExecutions.add(execution);
31 |
32 | ProcessInstance processInstance = execution.getProcessInstance();
33 | for (Execution e : processInstance.getExecutions()){
34 | if (e.getActivity().getId().equals(execution.getActivity().getId())) {
35 | waitingExecutions.add(e);
36 | }
37 | }
38 |
39 | if (waitingExecutions.size() == incomingSequenceFlowCount) {
40 |
41 | // Remove all executions
42 | for (Execution e : waitingExecutions) {
43 | e.delete();
44 | }
45 |
46 | // Create new one to leave gateway
47 | Execution outgoingExecution = processInstance.createNewExecutionInActivity(parallelGatewayActivity);
48 | leave(outgoingExecution, engineOperations);
49 |
50 | }
51 |
52 | }
53 |
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.jorambarrez
5 | activiti-neo4j
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 | 1.9.1
10 | 5.13
11 |
12 |
13 |
14 |
15 | org.neo4j
16 | neo4j
17 | ${neo4j.version}
18 |
19 |
20 | org.neo4j
21 | neo4j-kernel
22 | ${neo4j.version}
23 | test-jar
24 | test
25 |
26 |
27 | org.neo4j.app
28 | neo4j-server
29 | ${neo4j.version}
30 |
31 |
32 | org.neo4j.app
33 | neo4j-server
34 | static-web
35 | ${neo4j.version}
36 |
37 |
38 | org.activiti
39 | activiti-bpmn-model
40 | ${activiti.version}
41 |
42 |
43 | org.activiti
44 | activiti-bpmn-converter
45 | ${activiti.version}
46 |
47 |
48 | junit
49 | junit
50 | 4.11
51 | test
52 |
53 |
54 |
55 |
56 |
57 | Alfresco Maven Repository
58 | https://maven.alfresco.com/nexus/content/groups/public/
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/CommandContext.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 | import java.util.LinkedList;
16 |
17 | import org.activiti.neo4j.manager.ExecutionManager;
18 |
19 |
20 | /**
21 | * @author Joram Barrez
22 | */
23 | public class CommandContext implements EngineOperations {
24 |
25 | protected Core core;
26 | protected ExecutionManager executionManager;
27 |
28 | protected LinkedList agenda = new LinkedList();
29 | protected T result = null;
30 |
31 | public void continueProcess(Execution execution) {
32 | core.continueProcess(this, execution);
33 | }
34 |
35 | public void signal(Execution execution) {
36 | core.signal(this, execution);
37 | }
38 |
39 | /* package */ CommandContext() {
40 | this.agenda = new LinkedList();
41 | }
42 |
43 | public LinkedList getAgenda() {
44 | return agenda;
45 | }
46 |
47 | public void setAgenda(LinkedList agenda) {
48 | this.agenda = agenda;
49 | }
50 |
51 | public T getResult() {
52 | return result;
53 | }
54 |
55 | public void setResult(T result) {
56 | this.result = result;
57 | }
58 |
59 | public Core getCore() {
60 | return core;
61 | }
62 |
63 | public void setCore(Core core) {
64 | this.core = core;
65 | }
66 |
67 | public ExecutionManager getExecutionManager() {
68 | return executionManager;
69 | }
70 |
71 | public void setExecutionManager(ExecutionManager executionManager) {
72 | this.executionManager = executionManager;
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/entity/NodeBasedSequenceFlow.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.entity;
14 |
15 | import org.activiti.neo4j.Activity;
16 | import org.activiti.neo4j.SequenceFlow;
17 | import org.neo4j.graphdb.Relationship;
18 |
19 |
20 | /**
21 | * @author Joram Barrez
22 | */
23 | public class NodeBasedSequenceFlow implements SequenceFlow {
24 |
25 | protected Relationship sequenceFlowRelationship;
26 |
27 | protected Activity sourceActivity;
28 | protected Activity targetActivity;
29 |
30 | public NodeBasedSequenceFlow(Relationship sequenceFlowRelationship) {
31 | this.sequenceFlowRelationship = sequenceFlowRelationship;
32 | }
33 |
34 | public Activity getSourceActivity() {
35 | if (sourceActivity == null) {
36 | sourceActivity = new NodeBasedActivity(sequenceFlowRelationship.getStartNode());
37 | }
38 | return sourceActivity;
39 | }
40 |
41 | public Activity getTargetActivity() {
42 | if (targetActivity == null) {
43 | targetActivity = new NodeBasedActivity(sequenceFlowRelationship.getEndNode());
44 | }
45 | return targetActivity;
46 | }
47 |
48 | public boolean hasProperty(String property) {
49 | return sequenceFlowRelationship.hasProperty(property);
50 | }
51 |
52 | public Object getProperty(String property) {
53 | return sequenceFlowRelationship.getProperty(property);
54 | }
55 |
56 | public void setProperty(String property, Object value) {
57 | sequenceFlowRelationship.setProperty(property, value);
58 | }
59 |
60 | public Object removeProperty(String property) {
61 | return sequenceFlowRelationship.removeProperty(property);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/CoreImpl.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j;
2 | import org.activiti.neo4j.behavior.Behavior;
3 | import org.activiti.neo4j.behavior.BehaviorMapping;
4 | import org.activiti.neo4j.entity.NodeBasedExecution;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 |
9 | /* Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 |
22 | /**
23 | * @author Joram Barrez
24 | */
25 | public class CoreImpl implements Core {
26 |
27 | private Logger logger = LoggerFactory.getLogger(CoreImpl.class);
28 |
29 | protected BehaviorMapping behaviorMapping;
30 |
31 | public void continueProcess(final CommandContext> commandContext, final Execution execution) {
32 | commandContext.getAgenda().add(new Runnable() {
33 |
34 | public void run() {
35 | String type = (String) execution.getActivity().getProperty("type");
36 | Behavior behavior = behaviorMapping.getBehaviorForType(type);
37 | System.out.println("Execution behaviour " + behavior);
38 | behavior.execute(execution, commandContext);
39 | }
40 | });
41 | }
42 |
43 | public void signal(final CommandContext> commandContext, final Execution execution) {
44 | commandContext.getAgenda().add(new Runnable() {
45 |
46 | public void run() {
47 | String type = (String) execution.getActivity().getProperty("type");
48 | Behavior behavior = behaviorMapping.getBehaviorForType(type);
49 | System.out.println("Signaling " + behavior);
50 | behavior.signal(execution, commandContext);
51 | }
52 | });
53 | }
54 |
55 | public BehaviorMapping getBehaviorMapping() {
56 | return behaviorMapping;
57 | }
58 |
59 | public void setBehaviorMapping(BehaviorMapping behaviorMapping) {
60 | this.behaviorMapping = behaviorMapping;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/RuntimeService.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j;
2 |
3 | import org.activiti.neo4j.entity.NodeBasedExecution;
4 | import org.neo4j.graphdb.Direction;
5 | import org.neo4j.graphdb.GraphDatabaseService;
6 | import org.neo4j.graphdb.Node;
7 | import org.neo4j.graphdb.Relationship;
8 | import org.neo4j.graphdb.index.Index;
9 |
10 | public class RuntimeService {
11 |
12 | protected GraphDatabaseService graphDb;
13 | protected CommandExecutor commandExecutor;
14 |
15 | public RuntimeService(GraphDatabaseService graphDb, CommandExecutor commandExecutor) {
16 | this.graphDb = graphDb;
17 | this.commandExecutor = commandExecutor;
18 | }
19 |
20 | public void startProcessInstanceByKey(final String key) {
21 | commandExecutor.execute(new Command() {
22 |
23 | public void execute(CommandContext commandContext) {
24 | // Find process definition node
25 |
26 | // TODO: encapsulate in a manager!
27 | Index processDefinitionIndex = graphDb.index().forNodes(Constants.PROCESS_DEFINITION_INDEX);
28 | Node processDefinitionNode = processDefinitionIndex.get(Constants.INDEX_KEY_PROCESS_DEFINITION_KEY, key).getSingle();
29 | Node startEventNode = processDefinitionNode.getRelationships(Direction.OUTGOING, RelTypes.IS_STARTED_FROM).iterator().next().getEndNode();
30 |
31 | // Create process instance node and link it to the process definition
32 | Node processInstanceNode = graphDb.createNode();
33 | processDefinitionNode.createRelationshipTo(processInstanceNode, RelTypes.PROCESS_INSTANCE);
34 |
35 | // // Traverse the process definition
36 | // TraversalDescription traversalDescription = Traversal.description()
37 | // .breadthFirst()
38 | // .relationships( RelTypes.SEQ_FLOW, Direction.OUTGOING )
39 | // .evaluator(Evaluators.all());
40 | // Traverser traverser = traversalDescription.traverse(startEventNode);
41 |
42 | // Add one execution link to the startnode
43 | Relationship relationShipExecution = processInstanceNode.createRelationshipTo(startEventNode, RelTypes.EXECUTION);
44 |
45 | // Execute the process
46 | Execution execution = new NodeBasedExecution(relationShipExecution);
47 | commandContext.continueProcess(execution);
48 | }
49 |
50 | });
51 |
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/behavior/ExclusiveGatewayBehavior.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.behavior;
14 |
15 | import java.util.Iterator;
16 |
17 | import org.activiti.neo4j.Activity;
18 | import org.activiti.neo4j.EngineOperations;
19 | import org.activiti.neo4j.Execution;
20 | import org.activiti.neo4j.SequenceFlow;
21 |
22 |
23 | /**
24 | * @author Joram Barrez
25 | */
26 | public class ExclusiveGatewayBehavior extends AbstractBehavior {
27 |
28 | public void execute(Execution execution, EngineOperations engineOperations) {
29 |
30 | // Evaluate every outgoing sequence flow. The first that evaluates to true is selected.
31 | // Otherwise the default flow is followed.
32 |
33 | Activity currentActivity = execution.getActivity();
34 | String defaultFlowId = (String) currentActivity.getProperty("defaultFlow");
35 |
36 | Activity nextActivity = null;
37 | SequenceFlow defaultSequenceFlow = null;
38 |
39 | boolean found = false;
40 | Iterator sequenceFlowIterator = currentActivity.getOutgoingSequenceFlow().iterator();
41 | while (!found && sequenceFlowIterator.hasNext()) {
42 |
43 | SequenceFlow sequenceFlow = sequenceFlowIterator.next();
44 |
45 | // Get condition, if true -> evaluate
46 | String conditionExpression = null;
47 |
48 | if (sequenceFlow.hasProperty("condition")) {
49 | conditionExpression = (String) sequenceFlow.getProperty("condition");
50 | }
51 |
52 | if (sequenceFlow.getProperty("id").equals(defaultFlowId)) {
53 | defaultSequenceFlow = sequenceFlow;
54 | } else if (conditionExpression != null) {
55 |
56 | // TODO: implement expressions! .... for the moment always true
57 | nextActivity = sequenceFlow.getTargetActivity();
58 | }
59 |
60 | }
61 |
62 | if (nextActivity != null) {
63 | goToNextActivity(nextActivity, execution, engineOperations);
64 | } else if (defaultSequenceFlow != null) {
65 | goToNextActivity(defaultSequenceFlow.getTargetActivity(), execution, engineOperations);
66 | } else {
67 | throw new RuntimeException("Could not find a sequenceflow with true condition nor default flow");
68 | }
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/entity/NodeBasedActivity.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.entity;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | import org.activiti.neo4j.Activity;
19 | import org.activiti.neo4j.RelTypes;
20 | import org.activiti.neo4j.SequenceFlow;
21 | import org.neo4j.graphdb.Direction;
22 | import org.neo4j.graphdb.Node;
23 | import org.neo4j.graphdb.Relationship;
24 |
25 |
26 | /**
27 | * @author Joram Barrez
28 | */
29 | public class NodeBasedActivity implements Activity {
30 |
31 | protected Node activityNode;
32 | protected List incomingSequenceFlows;
33 | protected List outgoingSequenceFlows;
34 |
35 | public NodeBasedActivity(Node activityNode) {
36 | this.activityNode = activityNode;
37 | }
38 |
39 | public List getOutgoingSequenceFlow() {
40 | if (outgoingSequenceFlows == null) {
41 | outgoingSequenceFlows = new ArrayList();
42 | for (Relationship sequenceFlowRelationship : activityNode.getRelationships(Direction.OUTGOING, RelTypes.SEQ_FLOW)) {
43 | outgoingSequenceFlows.add(new NodeBasedSequenceFlow(sequenceFlowRelationship));
44 | }
45 | }
46 | return outgoingSequenceFlows;
47 | }
48 |
49 | public List getIncomingSequenceFlow() {
50 | if (incomingSequenceFlows == null) {
51 | incomingSequenceFlows = new ArrayList();
52 | for (Relationship sequenceFlowRelationship : activityNode.getRelationships(Direction.INCOMING, RelTypes.SEQ_FLOW)) {
53 | incomingSequenceFlows.add(new NodeBasedSequenceFlow(sequenceFlowRelationship));
54 | }
55 | }
56 | return incomingSequenceFlows;
57 | }
58 |
59 | public String getId() {
60 | return (String) activityNode.getProperty("id");
61 | }
62 |
63 | public Object getProperty(String property) {
64 | return activityNode.getProperty(property);
65 | }
66 |
67 | public boolean hasProperty(String property) {
68 | return activityNode.hasProperty(property);
69 | }
70 |
71 | public void setProperty(String property, Object value) {
72 | activityNode.setProperty(property, value);
73 | }
74 |
75 | public Object removeProperty(String property) {
76 | return activityNode.removeProperty(property);
77 | }
78 |
79 | public Node getActivityNode() {
80 | return activityNode;
81 | }
82 |
83 | public void setActivityNode(Node activityNode) {
84 | this.activityNode = activityNode;
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/CommandExecutor.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 | import org.activiti.neo4j.manager.ExecutionManager;
16 | import org.neo4j.graphdb.GraphDatabaseService;
17 | import org.neo4j.graphdb.Transaction;
18 |
19 |
20 | /**
21 | * @author Joram Barrez
22 | *
23 | */
24 | public class CommandExecutor {
25 |
26 | protected GraphDatabaseService graphDatabaseService;
27 | protected Core core;
28 | protected ExecutionManager executionManager;
29 |
30 | public CommandExecutor(GraphDatabaseService graphDatabaseService) {
31 | this.graphDatabaseService = graphDatabaseService;
32 | }
33 |
34 | public T execute(final Command command) {
35 |
36 | // TODO: create interceptor stack analogue to the Activiti interceptor stack
37 | // to separate transaction interceptor from command execution interceptor
38 |
39 | final CommandContext commandContext = initialiseCommandContext(command);
40 |
41 | Transaction tx = graphDatabaseService.beginTx();
42 | try {
43 |
44 | while (!commandContext.getAgenda().isEmpty()) {
45 | Runnable runnable = commandContext.getAgenda().poll();
46 | runnable.run();
47 | }
48 |
49 | tx.success();
50 | } catch (Exception e) {
51 | e.printStackTrace();
52 | tx.failure();
53 | } finally {
54 | tx.finish();
55 | }
56 |
57 | return commandContext.getResult();
58 |
59 | }
60 |
61 | protected CommandContext initialiseCommandContext(final Command command) {
62 | final CommandContext commandContext = new CommandContext();
63 | commandContext.setCore(core);
64 | commandContext.setExecutionManager(executionManager);
65 |
66 | commandContext.getAgenda().add(new Runnable() {
67 |
68 | public void run() {
69 | command.execute(commandContext);
70 | }
71 |
72 | });
73 | return commandContext;
74 | }
75 |
76 |
77 | public GraphDatabaseService getGraphDatabaseService() {
78 | return graphDatabaseService;
79 | }
80 |
81 |
82 | public void setGraphDatabaseService(GraphDatabaseService graphDatabaseService) {
83 | this.graphDatabaseService = graphDatabaseService;
84 | }
85 |
86 | public Core getCore() {
87 | return core;
88 | }
89 |
90 | public void setCore(Core core) {
91 | this.core = core;
92 | }
93 |
94 | public ExecutionManager getExecutionManager() {
95 | return executionManager;
96 | }
97 |
98 | public void setExecutionManager(ExecutionManager executionManager) {
99 | this.executionManager = executionManager;
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/entity/NodeBasedProcessInstance.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.entity;
14 |
15 | import java.util.ArrayList;
16 | import java.util.Iterator;
17 | import java.util.List;
18 |
19 | import org.activiti.neo4j.Activity;
20 | import org.activiti.neo4j.Execution;
21 | import org.activiti.neo4j.ProcessInstance;
22 | import org.activiti.neo4j.RelTypes;
23 | import org.neo4j.graphdb.Direction;
24 | import org.neo4j.graphdb.Node;
25 | import org.neo4j.graphdb.Relationship;
26 |
27 |
28 | /**
29 | * @author Joram Barrez
30 | */
31 | public class NodeBasedProcessInstance implements ProcessInstance {
32 |
33 | protected Node processInstanceNode;
34 |
35 | public NodeBasedProcessInstance(Node processInstanceNode) {
36 | this.processInstanceNode = processInstanceNode;
37 | }
38 |
39 | public void setVariable(String variableName, Object variableValue) {
40 | Node variableNode = getVariableNode();
41 | if (variableNode == null) {
42 | variableNode = processInstanceNode.getGraphDatabase().createNode();
43 | processInstanceNode.createRelationshipTo(variableNode, RelTypes.VARIABLE);
44 | }
45 |
46 | variableNode.setProperty(variableName, variableValue);
47 | }
48 |
49 | protected Node getVariableNode() {
50 | Iterator variableRelationShipIterator =
51 | processInstanceNode.getRelationships(Direction.OUTGOING, RelTypes.VARIABLE).iterator();
52 |
53 | Node variableNode = null;
54 | if (variableRelationShipIterator.hasNext()) {
55 | Relationship variableRelationship = variableRelationShipIterator.next();
56 | variableNode = variableRelationship.getEndNode();
57 | }
58 |
59 | return variableNode;
60 | }
61 |
62 | public Execution createNewExecutionInActivity(Activity activity) {
63 | Relationship executionRelationship = processInstanceNode
64 | .createRelationshipTo(((NodeBasedActivity) activity).getActivityNode(), RelTypes.EXECUTION);
65 | return new NodeBasedExecution(executionRelationship);
66 | }
67 |
68 | public void delete() {
69 | // Executions
70 | for (Execution execution : getExecutions()) {
71 | execution.delete();
72 | }
73 |
74 | // Variables
75 | Node variableNode = getVariableNode();
76 | if (variableNode != null) {
77 | variableNode.delete();
78 | }
79 |
80 | // Delete relationship from process definition to process instance
81 | for (Relationship relationship : processInstanceNode.getRelationships(RelTypes.PROCESS_INSTANCE)) {
82 | relationship.delete();
83 | }
84 |
85 | processInstanceNode.delete();
86 | }
87 |
88 | public List getExecutions() {
89 | List executions = new ArrayList();
90 | for (Relationship executionRelationship : processInstanceNode.getRelationships(Direction.OUTGOING, RelTypes.EXECUTION)) {
91 | executions.add(new NodeBasedExecution(executionRelationship));
92 | }
93 | return executions;
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/src/test/resources/customJavaLogic.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/ProcessEngineConfiguration.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j;
14 |
15 | import org.activiti.neo4j.behavior.BehaviorMapping;
16 | import org.activiti.neo4j.behavior.BehaviorMappingImpl;
17 | import org.activiti.neo4j.manager.ExecutionManager;
18 | import org.activiti.neo4j.manager.NodeBaseExecutionManager;
19 | import org.activiti.neo4j.manager.NodeBasedTaskManager;
20 | import org.activiti.neo4j.manager.TaskManager;
21 | import org.neo4j.graphdb.GraphDatabaseService;
22 |
23 |
24 | /**
25 | * @author Joram Barrez
26 | */
27 | public class ProcessEngineConfiguration {
28 |
29 | protected GraphDatabaseService graphDatabaseService;
30 | protected BehaviorMapping behaviorMapping;
31 | protected Core core;
32 | protected CommandExecutor commandExecutor;
33 | protected ExecutionManager executionManager;
34 | protected TaskManager taskManager;
35 |
36 | public ProcessEngine buildProcessEngine() {
37 | ProcessEngine processEngine = new ProcessEngine();
38 | processEngine.setGraphDatabaseService(graphDatabaseService);
39 |
40 | initBehaviorMapping();
41 | initCore();
42 | initManagers();
43 | initCommandExecutor(processEngine);
44 | initServices(processEngine);
45 |
46 | return processEngine;
47 | }
48 |
49 | protected void initBehaviorMapping() {
50 | this.behaviorMapping = new BehaviorMappingImpl();
51 | }
52 |
53 | protected void initCore() {
54 | CoreImpl core = new CoreImpl();
55 | core.setBehaviorMapping(behaviorMapping);
56 | this.core = core;
57 | }
58 |
59 | protected void initManagers() {
60 | NodeBaseExecutionManager nodeBaseExecutionManager = new NodeBaseExecutionManager();
61 | nodeBaseExecutionManager.setGraphDb(graphDatabaseService);
62 | this.executionManager = nodeBaseExecutionManager;
63 |
64 | NodeBasedTaskManager nodeBasedTaskManager = new NodeBasedTaskManager();
65 | nodeBasedTaskManager.setGraphDb(graphDatabaseService);
66 | this.taskManager = nodeBasedTaskManager;
67 | }
68 |
69 | protected void initCommandExecutor(ProcessEngine processEngine) {
70 | CommandExecutor commandExecutor = new CommandExecutor(graphDatabaseService);
71 | commandExecutor.setCore(core);
72 | commandExecutor.setExecutionManager(executionManager);
73 |
74 | processEngine.setCommandExecutor(commandExecutor);
75 | this.commandExecutor = commandExecutor;
76 | }
77 |
78 | protected void initServices(ProcessEngine processEngine) {
79 | RepositoryService repositoryService = new RepositoryService(graphDatabaseService, commandExecutor);
80 | processEngine.setRepositoryService(repositoryService);
81 |
82 | RuntimeService runtimeService = new RuntimeService(graphDatabaseService, commandExecutor);
83 | processEngine.setRuntimeService(runtimeService);
84 |
85 | TaskService taskService = new TaskService(commandExecutor);
86 | taskService.setTaskManager(taskManager);
87 | processEngine.setTaskService(taskService);
88 | }
89 |
90 | public GraphDatabaseService getGraphDatabaseService() {
91 | return graphDatabaseService;
92 | }
93 |
94 | public void setGraphDatabaseService(GraphDatabaseService graphDatabaseService) {
95 | this.graphDatabaseService = graphDatabaseService;
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Activiti - Neo4J
2 | =================
3 |
4 | This is a prototype on how the Activiti BPM engine (http://activiti.org/) could be written on top of a Graph Database, in this case Neo4J (http://www.neo4j.org/)
5 |
6 | This is a regular Maven project.
7 |
8 | Uses existing Activiti concepts: Executions, Command Context, services, etc. So if we want to ever make it an option in Activiti to choose underlying persistency.
9 |
10 | ## High level
11 |
12 | As with Activiti, the ProcessEngine is the starting class of it all.
13 | Through the ProcessEngine, the different services can be obtained.
14 |
15 | Internally, the ProcessEngine manages the GraphDB connection and a CommandExecutor.
16 | The CommandExecutor is similar to the way it currently is implemented in Activiti.
17 | For the moment, it
18 | * Wraps the logic in a Neo4J transaction
19 | * Maintains an internal *agenda* (linkedlist of Runnables). All internal engine operations *must* put Runnables on this queue. The command executor simply takes Runnables from the queue and executes them.
20 |
21 | Note that the CommandExecutor implements an interface called *InternalActivitiEngine*.
22 | This probably is similar to the AtomicOperations in the Activiti engine, exposing the
23 | operations that are possible in the engine (eg continue process, signal wait state, etc.)
24 |
25 |
26 | ## How it works
27 |
28 | Suppose we have a simple process
29 |
30 | start --> user task --> end
31 |
32 | ### Deploy the process
33 |
34 | The process is defined in the standard BPMN 2.0 XML. The *RepositoryService* takes an *InputStream* to this XML.
35 | The XML is now parsed to a Neo4J model. Neo4J basically has two data types: nodes and relationships. Both can have properties.
36 |
37 | After parsing, the graph model looks as follows. --> means a relationship
38 |
39 | [Node] Process definition (id, key) ---'is started from'--> [Node] Start event --> [Node] user task --> end
40 |
41 | The start, task and end node have properties matching the process definition (name, id, etc.)
42 | Each of these nodes has a *type* property, which internally maps to a certain *Behaviour* class.
43 | This Behaviour class is executed when the execution arrives in the specific node.
44 |
45 | The reason to have a separate Process Definition Node is because we add this node to an index, so
46 | we can easily retrieve it.
47 |
48 | ### Start a Process Instance
49 |
50 | * Query the Process definition using the index
51 | * Create a Process instance node
52 | * Follow the *is started from* relation ship, get the Start event node
53 | * Create a relationship called *execution* to this node
54 | * Put the behaviour of the start event node on the *agenda*
55 |
56 | The start event behaviour is doing nothing more than simplu leaving the node. This is generic behaviour:
57 | * Fetch all next nodes, by following outgoing relationships from the current node
58 | * Put the continuation of these nodes on the *agenda*
59 | *
60 |
61 | ### User task
62 |
63 | The user task is a wait state. When the engine arrives here
64 | * Moves the execution relationship to the task node
65 | * Add this relationship to the task index, so it can be easily queries later
66 |
67 | The user task behaviour has a signal method (similar to Activiti).
68 | The TaskService.complete() method will call this, which will basicvally trigger the generic 'leave' method from above.
69 |
70 |
71 | ### End
72 |
73 | The behavior of the end simply removes all runtime nodes and executions.
74 |
75 |
76 |
77 | ### Variables
78 |
79 | Variables are stored in a *variable node* which is related to the Process Instance node through a relationship called *variables*
80 |
81 | Execution local/task local variables are possible in the same way, simply attach a variable node to that specific node.
82 |
83 | ## Thougths
84 |
85 | * Migrations of process instance (when deploying process definitions) gets really easy with this model. It is a matter of moving execution relationships to the new process definition.
86 | * Neo4J data can be easily shared: eg on machine A you have the data around process definition 1,2,3 and on machine B the process definiton 4. Also multi-tenancy gets easy.
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/test/resources/parallel-process.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/entity/NodeBasedExecution.java:
--------------------------------------------------------------------------------
1 | /* Licensed under the Apache License, Version 2.0 (the "License");
2 | * you may not use this file except in compliance with the License.
3 | * You may obtain a copy of the License at
4 | *
5 | * http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 | package org.activiti.neo4j.entity;
14 |
15 | import java.util.Iterator;
16 |
17 | import org.activiti.neo4j.Activity;
18 | import org.activiti.neo4j.Execution;
19 | import org.activiti.neo4j.ProcessInstance;
20 | import org.activiti.neo4j.RelTypes;
21 | import org.neo4j.graphdb.Direction;
22 | import org.neo4j.graphdb.Node;
23 | import org.neo4j.graphdb.Relationship;
24 | import org.neo4j.graphdb.index.Index;
25 |
26 | /**
27 | * @author Joram Barrez
28 | */
29 | public class NodeBasedExecution implements Execution {
30 |
31 | // An execution is actually a thin wrapper around a Neo4j relationship
32 | // adding al sorts of convenience methods that hide the internal bits
33 | protected Relationship relationship;
34 |
35 | protected NodeBasedProcessInstance processInstance;
36 | protected NodeBasedActivity activity;
37 |
38 | public NodeBasedExecution(Relationship relationship) {
39 | this.relationship = relationship;
40 | }
41 |
42 | public ProcessInstance getProcessInstance() {
43 | if (processInstance == null) {
44 | processInstance = new NodeBasedProcessInstance(getProcessInstanceNode());
45 | }
46 | return processInstance;
47 | }
48 |
49 | protected Node getProcessInstanceNode() {
50 | return relationship.getStartNode();
51 | }
52 |
53 | public Activity getActivity() {
54 | if (activity == null) {
55 | activity = new NodeBasedActivity(relationship.getEndNode());
56 | }
57 | return activity;
58 | }
59 |
60 | public void setVariable(String variableName, Object variableValue) {
61 |
62 | // TODO: need to have variable local, which is a bit trickier,
63 | // since executions are relationships.
64 | // Perhaps this needs to be revised
65 |
66 | Node processInstanceNode = getProcessInstanceNode();
67 |
68 | // Check if the variable node already exists
69 | Iterator variableRelationShipIterator =
70 | processInstanceNode.getRelationships(Direction.OUTGOING, RelTypes.VARIABLE).iterator();
71 |
72 | Node variableNode = null;
73 | if (variableRelationShipIterator.hasNext()) {
74 | Relationship variableRelationship = variableRelationShipIterator.next();
75 | variableNode = variableRelationship.getEndNode();
76 | } else {
77 | variableNode = processInstanceNode.getGraphDatabase().createNode();
78 | processInstanceNode.createRelationshipTo(variableNode, RelTypes.VARIABLE);
79 | }
80 |
81 | variableNode.setProperty(variableName, variableValue);
82 | }
83 |
84 | public Object getVariable(String variableName) {
85 |
86 | Node processInstanceNode = getProcessInstanceNode();
87 | Iterator variableRelationshipIterator = processInstanceNode.getRelationships(RelTypes.VARIABLE).iterator();
88 |
89 | if (variableRelationshipIterator.hasNext()) {
90 | Node variableNode = variableRelationshipIterator.next().getEndNode();
91 | return variableNode.getProperty(variableName);
92 | } else {
93 | // No variable associated with this process instance
94 | return null;
95 | }
96 | }
97 |
98 | public void addToIndex(String namespace, String key, Object value) {
99 | Index index = relationship.getGraphDatabase().index().forRelationships(namespace);
100 | index.add(relationship, key, value);
101 | }
102 |
103 | public Object getProperty(String property) {
104 | return relationship.getProperty(property);
105 | }
106 |
107 | public boolean hasProperty(String property) {
108 | return relationship.hasProperty(property);
109 | }
110 |
111 | public void setProperty(String property, Object value) {
112 | relationship.setProperty(property, value);
113 | }
114 |
115 | public Node getStartNode() {
116 | return relationship.getStartNode();
117 | }
118 |
119 | public Node getEndNode() {
120 | return relationship.getEndNode();
121 | }
122 |
123 | public void delete() {
124 | // Delete actual execution relationship
125 | relationship.delete();
126 | }
127 |
128 | public Object removeProperty(String property) {
129 | return relationship.removeProperty(property);
130 | }
131 |
132 | public Relationship getRelationship() {
133 | return relationship;
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/src/test/java/ActivitiNeo4jTest.java:
--------------------------------------------------------------------------------
1 | import static org.junit.Assert.assertEquals;
2 | import static org.junit.Assert.assertTrue;
3 |
4 | import java.io.InputStream;
5 | import java.util.List;
6 |
7 | import org.activiti.neo4j.ProcessDefinition;
8 | import org.activiti.neo4j.ProcessEngine;
9 | import org.activiti.neo4j.ProcessEngineConfiguration;
10 | import org.activiti.neo4j.Task;
11 | import org.junit.Before;
12 | import org.junit.Test;
13 | import org.neo4j.graphdb.GraphDatabaseService;
14 | import org.neo4j.server.WrappingNeoServerBootstrapper;
15 | import org.neo4j.test.TestGraphDatabaseFactory;
16 |
17 | public class ActivitiNeo4jTest {
18 |
19 | protected GraphDatabaseService graphDb;
20 |
21 | protected WrappingNeoServerBootstrapper server;
22 |
23 | protected ProcessEngine processEngine;
24 |
25 |
26 | @Before
27 | public void setupDatabase() {
28 |
29 | // graphDb = new GraphDatabaseFactory().newEmbeddedDatabase("target/testDB");
30 | graphDb = new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder().newGraphDatabase();
31 | registerShutdownHook(graphDb);
32 |
33 | // server = new WrappingNeoServerBootstrapper((GraphDatabaseAPI) graphDb);
34 | // server.start();
35 |
36 | ProcessEngineConfiguration processEngineConfiguration = new ProcessEngineConfiguration();
37 | processEngineConfiguration.setGraphDatabaseService(graphDb);
38 | processEngine = processEngineConfiguration.buildProcessEngine();
39 | }
40 |
41 | @Test
42 | public void simpleOneTaskProcessTest() throws Exception {
43 | // Deploy process
44 | InputStream inputStream = this.getClass().getResourceAsStream("one-task-process.bpmn20.xml");
45 | ProcessDefinition processDefinition = processEngine.getRepositoryService().deploy(inputStream);
46 |
47 | // Start process instance
48 | processEngine.getRuntimeService().startProcessInstanceByKey("oneTaskProcess");
49 |
50 | // See if there is a task for kermit
51 | List tasks = processEngine.getTaskService().findTasksFor("kermit");
52 | assertEquals(1, tasks.size());
53 |
54 | Task task = tasks.get(0);
55 | assertEquals("My task", task.getName());
56 |
57 | // Complete task
58 | processEngine.getTaskService().complete(task.getId());
59 |
60 | }
61 |
62 | // @Test
63 | // public void startALotOfProcesses() throws Exception {
64 | //
65 | // // Deploy process
66 | // InputStream inputStream = this.getClass().getResourceAsStream("one-task-process.bpmn20.xml");
67 | // processEngine.getRepositoryService().deploy(inputStream);
68 | //
69 | // // Start a few process instances
70 | // int nrOfInstances = 10000;
71 | // long start = System.currentTimeMillis();
72 | // for (int i=0; i tasks = processEngine.getTaskService().findTasksFor("kermit");
80 | // System.out.println("Got " + tasks.size() + " tasks");
81 | // assertEquals(nrOfInstances, tasks.size());
82 | //
83 | // }
84 |
85 | @Test
86 | public void parallelTest() throws Exception {
87 | // Deploy process
88 | InputStream inputStream = this.getClass().getResourceAsStream("parallel-process.bpmn");
89 | ProcessDefinition processDefinition = processEngine.getRepositoryService().deploy(inputStream);
90 |
91 | // Start process instance
92 | processEngine.getRuntimeService().startProcessInstanceByKey("parallelProcess");
93 |
94 | // two task should now be available for kermit
95 | List tasks = processEngine.getTaskService().findTasksFor("kermit");
96 | assertEquals(2, tasks.size());
97 |
98 | boolean foundTask1 = false;
99 | boolean foundTask2 = false;
100 | for (Task task : tasks) {
101 | if (task.getName().equals("myTask1")) {
102 | foundTask1 = true;
103 | } else if (task.getName().equals("myTask2")) {
104 | foundTask2 = true;
105 | }
106 | }
107 | assertTrue(foundTask1 && foundTask2);
108 | }
109 |
110 | @Test
111 | public void startMultipleProcessInstancesTest() throws Exception {
112 |
113 | // Deploy process
114 | InputStream inputStream = this.getClass().getResourceAsStream("one-task-process.bpmn20.xml");
115 | ProcessDefinition processDefinition = processEngine.getRepositoryService().deploy(inputStream);
116 |
117 | // Start a few process instances
118 | for (int i=0; i<20; i++) {
119 | processEngine.getRuntimeService().startProcessInstanceByKey("oneTaskProcess");
120 | }
121 |
122 | // See if there are tasks for kermit
123 | List tasks = processEngine.getTaskService().findTasksFor("kermit");
124 | assertEquals(20, tasks.size());
125 |
126 | }
127 | //
128 | // @Test
129 | // public void delegateCallAndNoStackOverflowTest() {
130 | // // Deploy process
131 | // InputStream inputStream = this.getClass().getResourceAsStream("customJavaLogic.bpmn");
132 | // ProcessDefinition processDefinition = processEngine.getRepositoryService().deploy(inputStream);
133 | //
134 | // // Start process instance
135 | // processEngine.getRuntimeService().startProcessInstanceByKey("customJavaLogic");
136 | // }
137 |
138 |
139 | private void registerShutdownHook(final GraphDatabaseService graphDb) {
140 | Runtime.getRuntime().addShutdownHook(new Thread() {
141 |
142 | @Override
143 | public void run() {
144 | graphDb.shutdown();
145 |
146 | if (server != null) {
147 | server.stop();
148 | }
149 | System.out.println();
150 | System.out.println("Graph database shut down");
151 | }
152 | });
153 | }
154 |
155 | }
156 |
157 |
--------------------------------------------------------------------------------
/src/main/java/org/activiti/neo4j/RepositoryService.java:
--------------------------------------------------------------------------------
1 | package org.activiti.neo4j;
2 |
3 | import java.io.InputStream;
4 | import java.io.InputStreamReader;
5 | import java.util.HashMap;
6 | import java.util.HashSet;
7 | import java.util.Map;
8 | import java.util.Set;
9 |
10 | import javax.xml.stream.XMLInputFactory;
11 | import javax.xml.stream.XMLStreamReader;
12 |
13 | import org.activiti.bpmn.converter.BpmnXMLConverter;
14 | import org.activiti.bpmn.model.BpmnModel;
15 | import org.activiti.bpmn.model.EndEvent;
16 | import org.activiti.bpmn.model.ExclusiveGateway;
17 | import org.activiti.bpmn.model.FlowElement;
18 | import org.activiti.bpmn.model.FlowNode;
19 | import org.activiti.bpmn.model.ParallelGateway;
20 | import org.activiti.bpmn.model.Process;
21 | import org.activiti.bpmn.model.SequenceFlow;
22 | import org.activiti.bpmn.model.ServiceTask;
23 | import org.activiti.bpmn.model.StartEvent;
24 | import org.activiti.bpmn.model.UserTask;
25 | import org.activiti.neo4j.helper.BpmnModelUtil;
26 | import org.neo4j.graphdb.GraphDatabaseService;
27 | import org.neo4j.graphdb.Node;
28 | import org.neo4j.graphdb.Relationship;
29 | import org.neo4j.graphdb.index.Index;
30 |
31 | public class RepositoryService {
32 |
33 | protected GraphDatabaseService graphDb;
34 | protected CommandExecutor commandExecutor;
35 |
36 | protected Map nodeMap;
37 | protected Set sequenceFlows;
38 |
39 | public RepositoryService(GraphDatabaseService graphDb, CommandExecutor commandExecutor) {
40 | this.graphDb = graphDb;
41 | this.commandExecutor = commandExecutor;
42 | }
43 |
44 | public ProcessDefinition deploy(final InputStream inputStream) {
45 |
46 | return commandExecutor.execute(new Command() {
47 |
48 | public void execute(CommandContext commandContext) {
49 | Process process = null;
50 |
51 |
52 | // TODO: extract in parser
53 | try {
54 | XMLInputFactory xif = XMLInputFactory.newInstance();
55 | InputStreamReader in = new InputStreamReader(inputStream, "UTF-8");
56 | XMLStreamReader xtr = xif.createXMLStreamReader(in);
57 | BpmnXMLConverter converter = new BpmnXMLConverter();
58 | BpmnModel bpmnModel = converter.convertToBpmnModel(xtr);
59 | process = bpmnModel.getProcesses().get(0);
60 | } catch (Exception e) {
61 | e.printStackTrace();
62 | }
63 |
64 | // TODO: move the below stuff to a parser / behaviour / BPMNParseHandler thingy
65 |
66 | // Create Node representation
67 | ProcessDefinition processDefinition = null;
68 |
69 | nodeMap = new HashMap();
70 | sequenceFlows = new HashSet();
71 | for (FlowElement flowElement : process.getFlowElements()) {
72 | if (flowElement instanceof StartEvent) {
73 | addStartEvent((StartEvent) flowElement);
74 | } else if (flowElement instanceof UserTask) {
75 | addUserTask((UserTask) flowElement);
76 | } else if (flowElement instanceof EndEvent) {
77 | addEndEvent((EndEvent) flowElement);
78 | } else if (flowElement instanceof ParallelGateway) {
79 | addParallelGateway((ParallelGateway) flowElement);
80 | } else if (flowElement instanceof ExclusiveGateway) {
81 | addExclusiveGateway((ExclusiveGateway) flowElement);
82 | } else if (flowElement instanceof ServiceTask) {
83 | addServiceTask((ServiceTask) flowElement);
84 | } else if (flowElement instanceof SequenceFlow) {
85 | sequenceFlows.add((SequenceFlow) flowElement);
86 | }
87 | }
88 | processSequenceFlows();
89 |
90 | // Create process definition node
91 | Node processDefinitionNode = graphDb.createNode();
92 | processDefinition = new ProcessDefinition();
93 | processDefinition.setId(processDefinitionNode.getId());
94 | processDefinition.setKey(process.getId());
95 |
96 | // Temporary (for visualization)
97 | graphDb.getReferenceNode().createRelationshipTo(processDefinitionNode, RelTypes.PROCESS_DEFINITION);
98 |
99 | // Create relationship from process definition node to start event
100 | StartEvent startEvent = BpmnModelUtil.findFlowElementsOfType(process, StartEvent.class).get(0);
101 | Node startEventNode = nodeMap.get(startEvent.getId());
102 | processDefinitionNode.createRelationshipTo(startEventNode, RelTypes.IS_STARTED_FROM);
103 |
104 | // Add process definition to index
105 | Index processDefinitionIndex = graphDb.index().forNodes(Constants.PROCESS_DEFINITION_INDEX);
106 | processDefinitionIndex.add(processDefinitionNode, Constants.INDEX_KEY_PROCESS_DEFINITION_KEY, processDefinition.getKey());
107 |
108 | commandContext.setResult(processDefinition);
109 | }
110 |
111 | });
112 | }
113 | protected void addStartEvent(StartEvent startEvent) {
114 | Node startEventNode = createNode(startEvent);
115 | startEventNode.setProperty("type", Constants.TYPE_START_EVENT);
116 | }
117 |
118 | protected void addEndEvent(EndEvent endEvent) {
119 | Node endEventNode = createNode(endEvent);
120 | endEventNode.setProperty("type", Constants.TYPE_END_EVENT);
121 | }
122 |
123 | protected void addParallelGateway(ParallelGateway parallelGateway) {
124 | Node parallelGwNode = createNode(parallelGateway);
125 | parallelGwNode.setProperty("type", Constants.TYPE_PARALLEL_GATEWAY);
126 | }
127 |
128 | protected void addExclusiveGateway(ExclusiveGateway exclusiveGateway) {
129 | Node exclusiveGwNode = createNode(exclusiveGateway);
130 | exclusiveGwNode.setProperty("type", Constants.TYPE_EXCLUSIVE_GATEWAY);
131 |
132 | if (exclusiveGateway.getDefaultFlow() != null) {
133 | exclusiveGwNode.setProperty("defaultFlow", exclusiveGateway.getDefaultFlow());
134 | }
135 | }
136 |
137 | protected void addUserTask(UserTask userTask) {
138 | Node userTaskNode = createNode(userTask);
139 | userTaskNode.setProperty("type", Constants.TYPE_USER_TASK);
140 |
141 | if (userTask.getName() != null) {
142 | userTaskNode.setProperty("name", userTask.getName());
143 | }
144 |
145 | if (userTask.getAssignee() != null) {
146 | userTaskNode.setProperty("assignee", userTask.getAssignee());
147 | }
148 | }
149 |
150 | protected void addServiceTask(ServiceTask serviceTask) {
151 | Node serviceTaskNode = createNode(serviceTask);
152 | serviceTaskNode.setProperty("type", Constants.TYPE_SERVICE_TASK);
153 | serviceTaskNode.setProperty("class", serviceTask.getImplementation());
154 | }
155 |
156 | protected Node createNode(FlowNode flowNode) {
157 | Node node = graphDb.createNode();
158 | node.setProperty("id", flowNode.getId());
159 |
160 | nodeMap.put(flowNode.getId(), node);
161 |
162 | return node;
163 | }
164 |
165 | protected void processSequenceFlows() {
166 | for (SequenceFlow sequenceFlow : sequenceFlows) {
167 | Node sourceNode = nodeMap.get(sequenceFlow.getSourceRef());
168 | Node targetNode = nodeMap.get(sequenceFlow.getTargetRef());
169 |
170 | Relationship sequenceflowRelationship = sourceNode.createRelationshipTo(targetNode, RelTypes.SEQ_FLOW);
171 | sequenceflowRelationship.setProperty("id", sequenceFlow.getId());
172 | if (sequenceFlow.getConditionExpression() != null) {
173 | sequenceflowRelationship.setProperty("condition", sequenceFlow.getConditionExpression());
174 | }
175 | }
176 | }
177 |
178 | }
179 |
--------------------------------------------------------------------------------