├── .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 | --------------------------------------------------------------------------------