浏览代码

add:初始化

yonge 5 年之前
父节点
当前提交
c081e46dd3
共有 100 个文件被更改,包括 12028 次插入44 次删除
  1. 0 36
      mec-workflow/pom.xml
  2. 7 8
      pom.xml
  3. 101 0
      workflowy/pom.xml
  4. 29 0
      workflowy/src/main/java/org/snaker/SnakerFlowyApplication.java
  5. 30 0
      workflowy/src/main/java/org/snaker/engine/Action.java
  6. 40 0
      workflowy/src/main/java/org/snaker/engine/Assignment.java
  7. 33 0
      workflowy/src/main/java/org/snaker/engine/AssignmentHandler.java
  8. 40 0
      workflowy/src/main/java/org/snaker/engine/Completion.java
  9. 67 0
      workflowy/src/main/java/org/snaker/engine/Context.java
  10. 379 0
      workflowy/src/main/java/org/snaker/engine/DBAccess.java
  11. 31 0
      workflowy/src/main/java/org/snaker/engine/DecisionHandler.java
  12. 34 0
      workflowy/src/main/java/org/snaker/engine/Expression.java
  13. 73 0
      workflowy/src/main/java/org/snaker/engine/IManagerService.java
  14. 32 0
      workflowy/src/main/java/org/snaker/engine/INoGenerator.java
  15. 126 0
      workflowy/src/main/java/org/snaker/engine/IOrderService.java
  16. 127 0
      workflowy/src/main/java/org/snaker/engine/IProcessService.java
  17. 180 0
      workflowy/src/main/java/org/snaker/engine/IQueryService.java
  18. 162 0
      workflowy/src/main/java/org/snaker/engine/ITaskService.java
  19. 179 0
      workflowy/src/main/java/org/snaker/engine/SnakerEngine.java
  20. 45 0
      workflowy/src/main/java/org/snaker/engine/SnakerException.java
  21. 30 0
      workflowy/src/main/java/org/snaker/engine/SnakerInterceptor.java
  22. 35 0
      workflowy/src/main/java/org/snaker/engine/TaskAccessStrategy.java
  23. 1105 0
      workflowy/src/main/java/org/snaker/engine/access/AbstractDBAccess.java
  24. 184 0
      workflowy/src/main/java/org/snaker/engine/access/Page.java
  25. 294 0
      workflowy/src/main/java/org/snaker/engine/access/QueryFilter.java
  26. 23 0
      workflowy/src/main/java/org/snaker/engine/access/dialect/Db2Dialect.java
  27. 32 0
      workflowy/src/main/java/org/snaker/engine/access/dialect/Dialect.java
  28. 35 0
      workflowy/src/main/java/org/snaker/engine/access/dialect/H2Dialect.java
  29. 37 0
      workflowy/src/main/java/org/snaker/engine/access/dialect/MySqlDialect.java
  30. 39 0
      workflowy/src/main/java/org/snaker/engine/access/dialect/OracleDialect.java
  31. 47 0
      workflowy/src/main/java/org/snaker/engine/access/dialect/PostgresqlDialect.java
  32. 88 0
      workflowy/src/main/java/org/snaker/engine/access/dialect/SQLServerDialect.java
  33. 307 0
      workflowy/src/main/java/org/snaker/engine/access/jdbc/BeanPropertyHandler.java
  34. 153 0
      workflowy/src/main/java/org/snaker/engine/access/jdbc/SpringJdbcAccess.java
  35. 53 0
      workflowy/src/main/java/org/snaker/engine/cache/Cache.java
  36. 60 0
      workflowy/src/main/java/org/snaker/engine/cache/CacheException.java
  37. 36 0
      workflowy/src/main/java/org/snaker/engine/cache/CacheManager.java
  38. 28 0
      workflowy/src/main/java/org/snaker/engine/cache/CacheManagerAware.java
  39. 57 0
      workflowy/src/main/java/org/snaker/engine/cache/memory/MemoryCache.java
  40. 60 0
      workflowy/src/main/java/org/snaker/engine/cache/memory/MemoryCacheManager.java
  41. 82 0
      workflowy/src/main/java/org/snaker/engine/core/AccessService.java
  42. 257 0
      workflowy/src/main/java/org/snaker/engine/core/Execution.java
  43. 87 0
      workflowy/src/main/java/org/snaker/engine/core/ManagerService.java
  44. 267 0
      workflowy/src/main/java/org/snaker/engine/core/OrderService.java
  45. 363 0
      workflowy/src/main/java/org/snaker/engine/core/ProcessService.java
  46. 150 0
      workflowy/src/main/java/org/snaker/engine/core/QueryService.java
  47. 138 0
      workflowy/src/main/java/org/snaker/engine/core/ServiceContext.java
  48. 373 0
      workflowy/src/main/java/org/snaker/engine/core/SnakerEngineImpl.java
  49. 102 0
      workflowy/src/main/java/org/snaker/engine/core/SpringContext.java
  50. 550 0
      workflowy/src/main/java/org/snaker/engine/core/TaskService.java
  51. 74 0
      workflowy/src/main/java/org/snaker/engine/entity/CCOrder.java
  52. 229 0
      workflowy/src/main/java/org/snaker/engine/entity/HistoryOrder.java
  53. 276 0
      workflowy/src/main/java/org/snaker/engine/entity/HistoryTask.java
  54. 50 0
      workflowy/src/main/java/org/snaker/engine/entity/HistoryTaskActor.java
  55. 206 0
      workflowy/src/main/java/org/snaker/engine/entity/Order.java
  56. 192 0
      workflowy/src/main/java/org/snaker/engine/entity/Process.java
  57. 109 0
      workflowy/src/main/java/org/snaker/engine/entity/Surrogate.java
  58. 294 0
      workflowy/src/main/java/org/snaker/engine/entity/Task.java
  59. 50 0
      workflowy/src/main/java/org/snaker/engine/entity/TaskActor.java
  60. 306 0
      workflowy/src/main/java/org/snaker/engine/entity/WorkItem.java
  61. 30 0
      workflowy/src/main/java/org/snaker/engine/handlers/IHandler.java
  62. 78 0
      workflowy/src/main/java/org/snaker/engine/handlers/impl/AbstractMergeHandler.java
  63. 68 0
      workflowy/src/main/java/org/snaker/engine/handlers/impl/CreateTaskHandler.java
  64. 73 0
      workflowy/src/main/java/org/snaker/engine/handlers/impl/EndProcessHandler.java
  65. 43 0
      workflowy/src/main/java/org/snaker/engine/handlers/impl/MergeActorHandler.java
  66. 62 0
      workflowy/src/main/java/org/snaker/engine/handlers/impl/MergeBranchHandler.java
  67. 106 0
      workflowy/src/main/java/org/snaker/engine/handlers/impl/StartSubProcessHandler.java
  68. 98 0
      workflowy/src/main/java/org/snaker/engine/helper/AssertHelper.java
  69. 112 0
      workflowy/src/main/java/org/snaker/engine/helper/ClassHelper.java
  70. 77 0
      workflowy/src/main/java/org/snaker/engine/helper/DateHelper.java
  71. 65 0
      workflowy/src/main/java/org/snaker/engine/helper/JsonHelper.java
  72. 141 0
      workflowy/src/main/java/org/snaker/engine/helper/ReflectHelper.java
  73. 172 0
      workflowy/src/main/java/org/snaker/engine/helper/StreamHelper.java
  74. 140 0
      workflowy/src/main/java/org/snaker/engine/helper/StringHelper.java
  75. 81 0
      workflowy/src/main/java/org/snaker/engine/helper/XmlHelper.java
  76. 38 0
      workflowy/src/main/java/org/snaker/engine/impl/DefaultNoGenerator.java
  77. 59 0
      workflowy/src/main/java/org/snaker/engine/impl/GeneralAccessStrategy.java
  78. 43 0
      workflowy/src/main/java/org/snaker/engine/impl/GeneralCompletion.java
  79. 53 0
      workflowy/src/main/java/org/snaker/engine/impl/LogInterceptor.java
  80. 99 0
      workflowy/src/main/java/org/snaker/engine/impl/SchedulerInterceptor.java
  81. 43 0
      workflowy/src/main/java/org/snaker/engine/impl/SpelExpression.java
  82. 50 0
      workflowy/src/main/java/org/snaker/engine/impl/SurrogateInterceptor.java
  83. 62 0
      workflowy/src/main/java/org/snaker/engine/model/BaseModel.java
  84. 128 0
      workflowy/src/main/java/org/snaker/engine/model/CustomModel.java
  85. 106 0
      workflowy/src/main/java/org/snaker/engine/model/DecisionModel.java
  86. 44 0
      workflowy/src/main/java/org/snaker/engine/model/EndModel.java
  87. 72 0
      workflowy/src/main/java/org/snaker/engine/model/FieldModel.java
  88. 33 0
      workflowy/src/main/java/org/snaker/engine/model/ForkModel.java
  89. 35 0
      workflowy/src/main/java/org/snaker/engine/model/JoinModel.java
  90. 206 0
      workflowy/src/main/java/org/snaker/engine/model/NodeModel.java
  91. 197 0
      workflowy/src/main/java/org/snaker/engine/model/ProcessModel.java
  92. 43 0
      workflowy/src/main/java/org/snaker/engine/model/StartModel.java
  93. 68 0
      workflowy/src/main/java/org/snaker/engine/model/SubProcessModel.java
  94. 256 0
      workflowy/src/main/java/org/snaker/engine/model/TaskModel.java
  95. 124 0
      workflowy/src/main/java/org/snaker/engine/model/TransitionModel.java
  96. 37 0
      workflowy/src/main/java/org/snaker/engine/model/WorkModel.java
  97. 85 0
      workflowy/src/main/java/org/snaker/engine/parser/AbstractNodeParser.java
  98. 108 0
      workflowy/src/main/java/org/snaker/engine/parser/ModelParser.java
  99. 78 0
      workflowy/src/main/java/org/snaker/engine/parser/NodeParser.java
  100. 42 0
      workflowy/src/main/java/org/snaker/engine/parser/impl/CustomParser.java

+ 0 - 36
mec-workflow/pom.xml

@@ -1,36 +0,0 @@
-<?xml version="1.0"?>
-<project
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
-	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-	<modelVersion>4.0.0</modelVersion>
-	<parent>
-		<groupId>com.ym</groupId>
-		<artifactId>mec</artifactId>
-		<version>1.0</version>
-	</parent>
-	<artifactId>mec-workflow</artifactId>
-	<name>mec-workflow</name>
-	<url>http://maven.apache.org</url>
-
-	<properties>
-		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-	</properties>
-
-	<dependencies>
-		<dependency>
-			<groupId>com.github.snakerflow</groupId>
-			<artifactId>snaker-core</artifactId>
-			<version>2.5.1</version>
-		</dependency>
-		<dependency>
-			<groupId>com.github.snakerflow</groupId>
-			<artifactId>snaker-mybatis</artifactId>
-			<version>2.5.1</version>
-		</dependency>
-		<dependency>
-			<groupId>com.github.snakerflow</groupId>
-			<artifactId>snaker-spring</artifactId>
-			<version>2.5.1</version>
-		</dependency>
-	</dependencies>
-</project>

+ 7 - 8
pom.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 
 	<groupId>com.ym</groupId>
@@ -227,10 +228,8 @@
 			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
 		</dependency>
 
-		<!-- <dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-security</artifactId>
-		</dependency> -->
+		<!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> 
+			</dependency> -->
 
 		<!--集群监控消息队列 -->
 		<!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-hystrix-stream</artifactId> 
@@ -311,7 +310,7 @@
 		<module>codegen</module>
 		<module>mec-web</module>
 		<module>cms</module>
-	  <module>mec-im</module>
-    <module>mec-workflow</module>
-  </modules>
+		<module>mec-im</module>
+		<module>workflowy</module>
+	</modules>
 </project>

+ 101 - 0
workflowy/pom.xml

@@ -0,0 +1,101 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.ym</groupId>
+		<artifactId>mec</artifactId>
+		<version>1.0</version>
+	</parent>
+	<artifactId>snakerflowy</artifactId>
+	<name>snakerflowy</name>
+	<url>http://maven.apache.org</url>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+
+	<dependencies>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.cloud</groupId>
+			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.cloud</groupId>
+			<artifactId>spring-cloud-starter-oauth2</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.cloud</groupId>
+			<artifactId>spring-cloud-starter-security</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.cloud</groupId>
+			<artifactId>spring-cloud-sleuth-zipkin</artifactId>
+		</dependency>
+
+		<!-- swagger-spring-boot -->
+		<dependency>
+			<groupId>com.spring4all</groupId>
+			<artifactId>swagger-spring-boot-starter</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>druid-spring-boot-starter</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>com.ym</groupId>
+			<artifactId>common-core</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>de.odysseus.juel</groupId>
+			<artifactId>juel</artifactId>
+			<version>2.1.2</version>
+			<scope>provided</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>commons-dbutils</groupId>
+			<artifactId>commons-dbutils</artifactId>
+			<version>1.7</version>
+			<scope>provided</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>javax.transaction</groupId>
+			<artifactId>jta</artifactId>
+			<version>1.1</version>
+			<scope>provided</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>cglib</groupId>
+			<artifactId>cglib-nodep</artifactId>
+			<version>3.2.12</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-dbcp2</artifactId>
+			<scope>provided</scope>
+		</dependency>
+	</dependencies>
+</project>

+ 29 - 0
workflowy/src/main/java/org/snaker/SnakerFlowyApplication.java

@@ -0,0 +1,29 @@
+package org.snaker;
+
+import javax.sql.DataSource;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+@SpringBootApplication
+@ComponentScan(basePackages = "org.snaker")
+@Configuration
+public class SnakerFlowyApplication {
+
+	@Autowired
+	private DataSource dataSource;
+
+	public static void main(String[] args) {
+		SpringApplication.run(SnakerFlowyApplication.class, args);
+	}
+
+	@Bean
+	public JdbcTemplate getJdbcTemplate() {
+		return new JdbcTemplate(dataSource);
+	}
+}

+ 30 - 0
workflowy/src/main/java/org/snaker/engine/Action.java

@@ -0,0 +1,30 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import org.snaker.engine.core.Execution;
+
+/**
+ * 所有的模型对象需要实现的接口,需要实现execute方法,每个节点的执行方式不一样
+ * @author yuqs
+ * @since 1.0
+ */
+public interface Action {
+	/**
+	 * 根据当前的执行对象所维持的process、order、model、args对所属流程实例进行执行
+	 * @param execution 执行对象
+	 */
+	public void execute(Execution execution);
+}

+ 40 - 0
workflowy/src/main/java/org/snaker/engine/Assignment.java

@@ -0,0 +1,40 @@
+/*
+ *  Copyright 2013-2015 www.snakerflow.com.
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package org.snaker.engine;
+
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.model.TaskModel;
+
+/**
+ * 分配参与者的处理抽象类
+ * @author yuqs
+ * @since 2.1.0
+ */
+public abstract class Assignment implements AssignmentHandler {
+    /**
+     * 分配参与者方法,可获取到当前的任务模型、执行对象
+     * @param model 任务模型
+     * @param execution 执行对象
+     * @return Object 参与者对象
+     */
+    public abstract Object assign(TaskModel model, Execution execution);
+
+    public Object assign(Execution execution) {
+        return assign(null, execution);
+    }
+}

+ 33 - 0
workflowy/src/main/java/org/snaker/engine/AssignmentHandler.java

@@ -0,0 +1,33 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import org.snaker.engine.core.Execution;
+
+/**
+ * 分配参与者的处理接口
+ * 建议使用Assignment接口
+ * @author yuqs
+ * @since 1.2.1
+ * @see org.snaker.engine.Assignment
+ */
+public interface AssignmentHandler {
+	/**
+	 * 分配参与者方法,可获取到当前的执行对象
+	 * @param execution 执行对象
+	 * @return Object 参与者对象
+	 */
+	Object assign(Execution execution);
+}

+ 40 - 0
workflowy/src/main/java/org/snaker/engine/Completion.java

@@ -0,0 +1,40 @@
+/*
+ *  Copyright 2013-2015 www.snakerflow.com.
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package org.snaker.engine;
+
+import org.snaker.engine.entity.HistoryOrder;
+import org.snaker.engine.entity.HistoryTask;
+
+/**
+ * 任务、实例完成时触发动作的接口
+ * @author yuqs
+ * @since 2.2.0
+ */
+public interface Completion {
+    /**
+     * 任务完成触发执行
+     * @param task 任务对象
+     */
+    public void complete(HistoryTask task);
+
+    /**
+     * 实例完成触发执行
+     * @param order 实例对象
+     */
+    public void complete(HistoryOrder order);
+}

+ 67 - 0
workflowy/src/main/java/org/snaker/engine/Context.java

@@ -0,0 +1,67 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import java.util.List;
+
+/**
+ * 服务查找接口
+ * @author yuqs
+ * @since 1.5
+ */
+public interface Context {
+	/**
+	 * 根据服务名称、实例向服务工厂注册
+	 * @param name 服务名称
+	 * @param object 服务实例
+	 */
+	void put(String name, Object object);
+	
+	/**
+	 * 根据服务名称、类型向服务工厂注册
+	 * @param name 服务名称
+	 * @param clazz 类型
+	 */
+	void put(String name, Class<?> clazz);
+	
+	/**
+	 * 判断是否存在给定的服务名称
+	 * @param name 服务名称
+	 * @return
+	 */
+	boolean exist(String name);
+	
+	/**
+	 * 根据给定的类型查找服务实例
+	 * @param clazz 类型
+	 * @return
+	 */
+	<T> T find(Class<T> clazz);
+	
+	/**
+	 * 根据给定的类型查找所有此类型的服务实例
+	 * @param clazz 类型
+	 * @return
+	 */
+	<T> List<T> findList(Class<T> clazz);
+	
+	/**
+	 * 根据给定的服务名称、类型查找服务实例
+	 * @param name 服务名称
+	 * @param clazz 类型
+	 * @return
+	 */
+	<T> T findByName(String name, Class<T> clazz);
+}

+ 379 - 0
workflowy/src/main/java/org/snaker/engine/DBAccess.java

@@ -0,0 +1,379 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import java.util.List;
+
+import org.snaker.engine.access.Page;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.entity.CCOrder;
+import org.snaker.engine.entity.HistoryOrder;
+import org.snaker.engine.entity.HistoryTask;
+import org.snaker.engine.entity.HistoryTaskActor;
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Process;
+import org.snaker.engine.entity.Surrogate;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.entity.TaskActor;
+import org.snaker.engine.entity.WorkItem;
+
+/**
+ * 数据库访问接口
+ * 主要提供保存、更新、查询流程的相关table
+ * @author yuqs
+ * @since 1.0
+ */
+public interface DBAccess {
+	/**
+	 * 根据访问对象,设置具体的实现类
+	 * @param accessObject 数据库访问对象(Connection等)
+	 */
+	public void initialize(Object accessObject);
+	/**
+	 * 保存任务对象
+	 * @param task 任务对象
+	 */
+	public void saveTask(Task task);
+	
+	/**
+	 * 保存流程实例对象
+	 * @param order 流程实例对象
+	 */
+	public void saveOrder(Order order);
+	
+	/**
+	 * 保存抄送实例
+	 * @param ccorder 抄送实体
+	 * @since 1.5
+	 */
+	public void saveCCOrder(CCOrder ccorder);
+	
+	/**
+	 * 保存流程定义对象
+	 * @param process 流程定义对象
+	 */
+	public void saveProcess(Process process);
+	
+	/**
+	 * 保存任务参与者对象
+	 * @param taskActor 任务参与者对象
+	 */
+	public void saveTaskActor(TaskActor taskActor);
+	
+	/**
+	 * 更新任务对象
+	 * @param task 任务对象
+	 */
+	public void updateTask(Task task);
+	
+	/**
+	 * 更新流程实例对象
+	 * @param order 流程实例对象
+	 */
+	public void updateOrder(Order order);
+	
+	/**
+	 * 更新抄送状态
+	 * @param ccorder 抄送实体对象
+	 */
+	public void updateCCOrder(CCOrder ccorder);
+	
+	/**
+	 * 更新流程定义对象
+	 * @param process 流程定义对象
+	 */
+	public void updateProcess(Process process);
+
+	/**
+	 * 删除流程定义对象
+	 * @param process 流程定义对象
+	 */
+	public void deleteProcess(Process process);
+	
+	/**
+	 * 更新流程定义类别
+	 * @param type 类别
+	 * @since 1.5
+	 */
+	public void updateProcessType(String id, String type);
+	
+	/**
+	 * 删除任务、任务参与者对象
+	 * @param task 任务对象
+	 */
+	public void deleteTask(Task task);
+	
+	/**
+	 * 删除流程实例对象
+	 * @param order 流程实例对象
+	 */
+	public void deleteOrder(Order order);
+	
+	/**
+	 * 删除抄送记录
+	 * @param ccorder 抄送实体对象
+	 */
+	public void deleteCCOrder(CCOrder ccorder);
+	
+	/**
+	 * 删除参与者
+	 * @param taskId 任务id
+	 * @param actors 参与者集合
+	 */
+	public void removeTaskActor(String taskId, String... actors);
+	
+	/**
+	 * 迁移活动实例
+	 * @param order 历史流程实例对象
+	 */
+	public void saveHistory(HistoryOrder order);
+	
+	/**
+	 * 更新历史流程实例状态
+	 * @param order 历史流程实例对象
+	 */
+	public void updateHistory(HistoryOrder order);
+	
+	/**
+	 * 迁移活动任务
+	 * @param task 历史任务对象
+	 */
+	public void saveHistory(HistoryTask task);
+
+	/**
+	 * 删除历史实例记录
+	 * @param historyOrder 历史实例
+	 */
+	public void deleteHistoryOrder(HistoryOrder historyOrder);
+
+	/**
+	 * 删除历史任务记录
+	 * @param historyTask 历史任务
+	 */
+	public void deleteHistoryTask(HistoryTask historyTask);
+
+    /**
+     * 更新实例变量(包括历史实例表)
+     * @param order 实例对象
+     */
+    public void updateOrderVariable(Order order);
+	
+	/**
+	 * 保存委托代理对象
+	 * @param surrogate 委托代理对象
+	 */
+	public void saveSurrogate(Surrogate surrogate);
+	
+	/**
+	 * 更新委托代理对象
+	 * @param surrogate 委托代理对象
+	 */
+	public void updateSurrogate(Surrogate surrogate);
+	
+	/**
+	 * 删除委托代理对象
+	 * @param surrogate 委托代理对象
+	 */
+	public void deleteSurrogate(Surrogate surrogate);
+	
+	/**
+	 * 根据主键id查询委托代理对象
+	 * @param id 主键id
+	 * @return surrogate 委托代理对象
+	 */
+	public Surrogate getSurrogate(String id);
+	
+	/**
+	 * 根据授权人、流程名称查询委托代理对象
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<Surrogate> 委托代理对象集合
+	 */
+	public List<Surrogate> getSurrogate(Page<Surrogate> page, QueryFilter filter);
+	
+	/**
+	 * 根据任务id查询任务对象
+	 * @param taskId 任务id
+	 * @return Task 任务对象
+	 */
+	public Task getTask(String taskId);
+	
+	/**
+	 * 根据任务ID获取历史任务对象
+	 * @param taskId 历史任务id
+	 * @return 历史任务对象
+	 */
+	HistoryTask getHistTask(String taskId);
+	
+	/**
+	 * 根据父任务id查询所有子任务
+	 * @param parentTaskId 父任务id
+	 * @return List<Task> 活动任务集合
+	 */
+	public List<Task> getNextActiveTasks(String parentTaskId);
+	
+	/**
+	 * 根据流程实例id、任务名称获取
+	 * @param orderId 流程实例id
+	 * @param taskName 任务名称
+	 * @param parentTaskId 父任务id
+	 * @return List<Task> 活动任务集合
+	 */
+	public List<Task> getNextActiveTasks(String orderId, String taskName, String parentTaskId);
+	
+	/**
+	 * 根据任务id查询所有活动任务参与者集合
+	 * @param taskId 活动任务id
+	 * @return List<TaskActor> 活动任务参与者集合
+	 */
+	public List<TaskActor> getTaskActorsByTaskId(String taskId);
+	
+	/**
+	 * 根据任务id查询所有历史任务参与者集合
+	 * @param taskId 历史任务id
+	 * @return List<HistoryTaskActor> 历史任务参与者集合
+	 */
+	public List<HistoryTaskActor> getHistTaskActorsByTaskId(String taskId);
+	
+	/**
+	 * 根据流程实例id查询实例对象
+	 * @param orderId 活动流程实例id
+	 * @return Order 活动流程实例对象
+	 */
+	public Order getOrder(String orderId);
+	
+	/**
+	 * 根据流程实例id、参与者id获取抄送记录
+	 * @param orderId 活动流程实例id
+	 * @param actorIds 参与者id
+	 * @return 传送记录列表
+	 */
+	public List<CCOrder> getCCOrder(String orderId, String... actorIds);
+	
+	/**
+	 * 根据流程实例ID获取历史流程实例对象
+	 * @param orderId 历史流程实例id
+	 * @return HistoryOrder 历史流程实例对象
+	 */
+	HistoryOrder getHistOrder(String orderId);
+	
+	/**
+	 * 根据流程定义id查询流程定义对象
+	 * @param id 流程定义id
+	 * @return Process 流程定义对象
+	 */
+	public Process getProcess(String id);
+	
+	/**
+	 * 根据流程名称查询最近的版本号
+	 * @param name 流程名称
+	 * @return Integer 流程定义版本号
+	 */
+	public Integer getLatestProcessVersion(String name);
+	
+	/**
+	 * 根据查询的参数,分页对象,返回分页后的查询结果
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<Process> 流程定义集合
+	 */
+	public List<Process> getProcesss(Page<Process> page, QueryFilter filter);
+	
+	/**
+	 * 分页查询流程实例
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<Order> 活动流程实例集合
+	 */
+	public List<Order> getActiveOrders(Page<Order> page, QueryFilter filter);
+	
+	/**
+	 * 分页查询活动任务列表
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<Task> 活动任务集合
+	 */
+	public List<Task> getActiveTasks(Page<Task> page, QueryFilter filter);
+	
+	/**
+	 * 分页查询历史流程实例
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<HistoryOrder> 历史流程实例集合
+	 */
+	public List<HistoryOrder> getHistoryOrders(Page<HistoryOrder> page, QueryFilter filter);
+	
+	/**
+	 * 根据参与者分页查询已完成的历史任务
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<HistoryTask> 历史任务集合
+	 */
+	public List<HistoryTask> getHistoryTasks(Page<HistoryTask> page, QueryFilter filter);
+	
+	/**
+	 * 根据查询的参数,分页对象,返回分页后的活动工作项
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<WorkItem> 活动工作项
+	 */
+	public List<WorkItem> getWorkItems(Page<WorkItem> page, QueryFilter filter);
+	
+	/**
+	 * 根据查询的参数,分页对象,返回分页后的抄送任务项
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<WorkItem> 活动工作项
+	 */
+	public List<HistoryOrder> getCCWorks(Page<HistoryOrder> page, QueryFilter filter);
+	
+	/**
+	 * 根据流程定义ID、参与者分页查询已完成的历史任务项
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<WorkItem> 历史工作项
+	 */
+	public List<WorkItem> getHistoryWorkItems(Page<WorkItem> page, QueryFilter filter);
+	
+	/**
+	 * 根据类型clazz、Sql语句、参数查询单个对象
+	 * @param clazz 类型
+	 * @param sql sql语句
+	 * @param args 参数列表
+	 * @return 结果对象
+	 */
+	public <T> T queryObject(Class<T> clazz, String sql, Object... args);
+	
+	/**
+	 * 根据类型clazz、Sql语句、参数查询列表对象
+	 * @param clazz 类型
+	 * @param sql sql语句
+	 * @param args 参数列表
+	 * @return 结果对象列表
+	 */
+	public <T> List<T> queryList(Class<T> clazz, String sql, Object... args);
+	
+	/**
+	 * 根据类型clazz、Sql语句、参数分页查询列表对象
+	 * @param page 分页对象
+     * @param filter 查询过滤器
+	 * @param clazz 类型
+	 * @param sql sql语句
+	 * @param args 参数列表
+	 * @return 结果对象列表
+	 */
+	public <T> List<T> queryList(Page<T> page, QueryFilter filter, Class<T> clazz, String sql, Object... args);
+
+}

+ 31 - 0
workflowy/src/main/java/org/snaker/engine/DecisionHandler.java

@@ -0,0 +1,31 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import org.snaker.engine.core.Execution;
+
+/**
+ * 决策处理器接口
+ * @author yuqs
+ * @since 1.0
+ */
+public interface DecisionHandler {
+	/**
+	 * 定义决策方法,实现类需要根据执行对象做处理,并返回后置流转的name
+	 * @param execution
+	 * @return String 后置流转的name
+	 */
+	String decide(Execution execution);
+}

+ 34 - 0
workflowy/src/main/java/org/snaker/engine/Expression.java

@@ -0,0 +1,34 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import java.util.Map;
+
+/**
+ * 表达式解析接口
+ * @author yuqs
+ * @since 1.0
+ * @since 1.2.1
+ */
+public interface Expression {
+	/**
+	 * 根据表达式串、参数解析表达式并返回指定类型
+	 * @param T 返回类型
+	 * @param expr 表达式串
+	 * @param args 参数列表
+	 * @return T 返回对象
+	 */
+	public <T> T eval(Class<T> T, String expr, Map<String, Object> args);
+}

+ 73 - 0
workflowy/src/main/java/org/snaker/engine/IManagerService.java

@@ -0,0 +1,73 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import java.util.List;
+
+import org.snaker.engine.access.Page;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.entity.Surrogate;
+
+/**
+ * 管理服务接口,用于流程管理控制服务
+ * 委托管理
+ * 时限控制
+ * @author yuqs
+ * @since 1.4
+ */
+public interface IManagerService {
+	/**
+	 * 保存或更新委托代理对象
+	 * @param surrogate 委托代理对象
+	 */
+	public void saveOrUpdate(Surrogate surrogate);
+	
+	/**
+	 * 删除委托代理对象
+	 * @param id 委托代理主键id
+	 */
+	public void deleteSurrogate(String id);
+	
+	/**
+	 * 根据主键id查询委托代理对象
+	 * @param id 主键id
+	 * @return surrogate 委托代理对象
+	 */
+	public Surrogate getSurrogate(String id);
+	
+	/**
+	 * 根据过滤条件查询委托代理对象
+	 * @param filter 查询过滤器
+	 * @return List<Surrogate> 委托代理对象集合
+	 */
+	public List<Surrogate> getSurrogate(QueryFilter filter);
+	
+	/**
+	 * 根据授权人、流程名称获取最终代理人
+	 * 如存在user1->user2->user3,那么最终返回user3
+	 * @param operator 授权人
+	 * @param processName 流程名称
+	 * @return String 代理人
+	 */
+	public String getSurrogate(String operator, String processName);
+	
+	/**
+	 * 根据过滤条件查询委托代理对象
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<Surrogate> 委托代理对象集合
+	 */
+	public List<Surrogate> getSurrogate(Page<Surrogate> page, QueryFilter filter);
+}

+ 32 - 0
workflowy/src/main/java/org/snaker/engine/INoGenerator.java

@@ -0,0 +1,32 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import org.snaker.engine.model.ProcessModel;
+
+/**
+ * 编号生成器接口
+ * 流程实例的编号字段使用该接口实现类来产生对应的编号
+ * @author yuqs
+ * @since 1.0
+ */
+public interface INoGenerator {
+	/**
+	 * 生成器方法
+	 * @param model
+	 * @return String 编号
+	 */
+	String generate(ProcessModel model);
+}

+ 126 - 0
workflowy/src/main/java/org/snaker/engine/IOrderService.java

@@ -0,0 +1,126 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import java.util.Map;
+
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Process;
+
+/**
+ * 流程实例业务类
+ * @author yuqs
+ * @since 1.0
+ */
+public interface IOrderService {
+	/**
+	 * 根据流程、操作人员、父流程实例ID创建流程实例
+	 * @param process 流程定义对象
+	 * @param operator 操作人员ID
+	 * @param args 参数列表
+	 * @return Order 活动流程实例对象
+	 */
+	Order createOrder(Process process, String operator, Map<String, Object> args);
+	
+	/**
+	 * 根据流程、操作人员、父流程实例ID创建流程实例
+	 * @param process 流程定义对象
+	 * @param operator 操作人员ID
+	 * @param args 参数列表
+	 * @param parentId 父流程实例ID
+	 * @param parentNodeName 父流程节点模型
+	 * @return 活动流程实例对象
+	 */
+	Order createOrder(Process process, String operator, Map<String, Object> args, String parentId, String parentNodeName);
+
+    /**
+     * 向指定实例id添加全局变量数据
+     * @param orderId 实例id
+     * @param args 变量数据
+     */
+    void addVariable(String orderId, Map<String ,Object> args);
+	
+	/**
+	 * 创建抄送实例
+	 * @param orderId 流程实例id
+	 * @param actorIds 参与者id
+     * @param creator 创建人id
+	 * @since 1.5
+	 */
+	void createCCOrder(String orderId, String creator, String... actorIds);
+	
+	/**
+	 * 流程实例正常完成
+	 * @param orderId 流程实例id
+	 */
+	void complete(String orderId);
+	
+	/**
+	 * 保存流程实例
+	 * @param order 流程实例对象
+	 */
+	void saveOrder(Order order);
+	
+	/**
+	 * 流程实例强制终止
+	 * @param orderId 流程实例id
+	 */
+	void terminate(String orderId);
+	
+	/**
+	 * 流程实例强制终止
+	 * @param orderId 流程实例id
+	 * @param operator 处理人员
+	 */
+	void terminate(String orderId, String operator);
+
+    /**
+     * 唤醒历史流程实例
+     * @param orderId 流程实例id
+     * @return 活动实例对象
+     */
+    Order resume(String orderId);
+	
+	/**
+	 * 更新流程实例
+	 * @param order 流程实例对象
+	 */
+	void updateOrder(Order order);
+	
+	/**
+	 * 更新抄送记录为已阅
+	 * @param orderId 流程实例id
+	 * @param actorIds 参与者id
+	 */
+	void updateCCStatus(String orderId, String... actorIds);
+	
+	/**
+	 * 删除抄送记录
+	 * @param orderId 流程实例id
+	 * @param actorId 参与者id
+	 */
+	void deleteCCOrder(String orderId, String actorId);
+
+	/**
+	 * 谨慎使用.数据恢复非常痛苦,你懂得~~
+	 * 级联删除指定流程实例的所有数据:
+	 * 1.wf_order,wf_hist_order
+	 * 2.wf_task,wf_hist_task
+	 * 3.wf_task_actor,wf_hist_task_actor
+	 * 4.wf_cc_order
+	 * @param id
+	 */
+	void cascadeRemove(String id);
+}

+ 127 - 0
workflowy/src/main/java/org/snaker/engine/IProcessService.java

@@ -0,0 +1,127 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.snaker.engine.access.Page;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.entity.Process;
+
+/**
+ * 流程定义业务类
+ * @author yuqs
+ * @since 1.0
+ */
+public interface IProcessService {
+	/**
+	 * 检查流程定义对象
+	 * @param process 流程定义对象
+	 * @param idOrName 流程定义id/name
+	 */
+	void check(Process process, String idOrName);
+	
+	/**
+	 * 保存流程定义
+	 * @param process 流程定义对象
+	 */
+	void saveProcess(Process process);
+	
+	/**
+	 * 更新流程定义的类别
+	 * @param id 流程定义id
+	 * @param type 类别
+	 * @since 1.5
+	 */
+	void updateType(String id, String type);
+	
+	/**
+	 * 根据主键ID获取流程定义对象
+	 * @param id 流程定义id
+	 * @return Process 流程定义对象
+	 */
+	Process getProcessById(String id);
+	
+	/**
+	 * 根据流程name获取流程定义对象
+	 * @param name 流程定义名称
+	 * @return Process 流程定义对象
+	 */
+	Process getProcessByName(String name);
+	
+	/**
+	 * 根据流程name、version获取流程定义对象
+	 * @param name 流程定义名称
+	 * @param version 版本号
+	 * @return Process 流程定义对象
+	 */
+	Process getProcessByVersion(String name, Integer version);
+
+	/**
+	 * 根据给定的参数列表args查询process
+	 * @param filter 查询过滤器
+	 * @return List<Process> 流程定义对象集合
+	 */
+	List<Process> getProcesss(QueryFilter filter);
+
+	/**
+	 * 根据给定的参数列表args分页查询process
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<Process> 流程定义对象集合
+	 */
+	List<Process> getProcesss(Page<Process> page, QueryFilter filter);
+	
+	/**
+	 * 根據InputStream輸入流,部署流程定义
+	 * @param input 流程定义输入流
+	 * @return String 流程定义id
+	 */
+	String deploy(InputStream input);
+	
+	/**
+	 * 根據InputStream輸入流,部署流程定义
+	 * @param input 流程定义输入流
+	 * @param creator 创建人
+	 * @return String 流程定义id
+	 */
+	String deploy(InputStream input, String creator);
+	
+	/**
+	 * 根據InputStream輸入流,部署流程定义
+	 * @param id 流程定义id
+	 * @param input 流程定义输入流
+	 */
+	void redeploy(String id, InputStream input);
+	
+	/**
+	 * 卸载指定的流程定义,只更新状态
+	 * @param id 流程定义id
+	 */
+	void undeploy(String id);
+	
+	/**
+	 * 谨慎使用.数据恢复非常痛苦,你懂得~~
+	 * 级联删除指定流程定义的所有数据:
+	 * 1.wf_process
+	 * 2.wf_order,wf_hist_order
+	 * 3.wf_task,wf_hist_task
+	 * 4.wf_task_actor,wf_hist_task_actor
+	 * 5.wf_cc_order
+	 * @param id
+	 */
+	void cascadeRemove(String id);
+}

+ 180 - 0
workflowy/src/main/java/org/snaker/engine/IQueryService.java

@@ -0,0 +1,180 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import java.util.List;
+
+import org.snaker.engine.access.Page;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.entity.HistoryOrder;
+import org.snaker.engine.entity.HistoryTask;
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.entity.WorkItem;
+
+/**
+ * 流程相关的查询服务
+ * @author yuqs
+ * @since 1.0
+ */
+public interface IQueryService {
+	/**
+	 * 根据流程实例ID获取流程实例对象
+	 * @param orderId 流程实例id
+	 * @return Order 流程实例对象
+	 */
+	Order getOrder(String orderId);
+	/**
+	 * 根据流程实例ID获取历史流程实例对象
+	 * @param orderId 历史流程实例id
+	 * @return HistoryOrder 历史流程实例对象
+	 */
+	HistoryOrder getHistOrder(String orderId);
+	/**
+	 * 根据任务ID获取任务对象
+	 * @param taskId 任务id
+	 * @return Task 任务对象
+	 */
+	Task getTask(String taskId);
+	/**
+	 * 根据任务ID获取历史任务对象
+	 * @param taskId 历史任务id
+	 * @return HistoryTask 历史任务对象
+	 */
+	HistoryTask getHistTask(String taskId);
+	/**
+	 * 根据任务ID获取活动任务参与者数组
+	 * @param taskId 任务id
+	 * @return String[] 参与者id数组
+	 */
+	String[] getTaskActorsByTaskId(String taskId);
+	/**
+	 * 根据任务ID获取历史任务参与者数组
+	 * @param taskId 历史任务id
+	 * @return String[] 历史参与者id数组
+	 */
+	String[] getHistoryTaskActorsByTaskId(String taskId);
+	
+	/**
+	 * 根据filter查询活动任务
+	 * @param filter 查询过滤器
+	 * @return List<Task> 活动任务集合
+	 */
+	List<Task> getActiveTasks(QueryFilter filter);
+	
+	/**
+	 * 根据filter分页查询活动任务
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<Task> 活动任务集合
+	 */
+	List<Task> getActiveTasks(Page<Task> page, QueryFilter filter);
+	
+	/**
+	 * 根据filter查询流程实例列表
+	 * @param filter 查询过滤器
+	 * @return List<Order> 活动实例集合
+	 */
+	List<Order> getActiveOrders(QueryFilter filter);
+	
+	/**
+	 * 根据filter分页查询流程实例列表
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<Order> 活动实例集合
+	 */
+	List<Order> getActiveOrders(Page<Order> page, QueryFilter filter);
+	
+	/**
+	 * 根据filter查询历史流程实例
+	 * @param filter 查询过滤器
+	 * @return List<HistoryOrder> 历史实例集合
+	 */
+	List<HistoryOrder> getHistoryOrders(QueryFilter filter);
+	
+	/**
+	 * 根据filter分页查询历史流程实例
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<HistoryOrder> 历史实例集合
+	 */
+	List<HistoryOrder> getHistoryOrders(Page<HistoryOrder> page, QueryFilter filter);
+	
+	/**
+	 * 根据filter查询所有已完成的任务
+	 * @param filter 查询过滤器
+	 * @return List<HistoryTask> 历史任务集合
+	 */
+	List<HistoryTask> getHistoryTasks(QueryFilter filter);
+	
+	/**
+	 * 根据filter分页查询已完成的历史任务
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<HistoryTask> 历史任务集合
+	 */
+	List<HistoryTask> getHistoryTasks(Page<HistoryTask> page, QueryFilter filter);
+	
+	/**
+	 * 根据filter分页查询工作项(包含process、order、task三个实体的字段集合)
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<WorkItem> 活动工作项集合
+	 */
+	List<WorkItem> getWorkItems(Page<WorkItem> page, QueryFilter filter);
+	
+	/**
+	 * 根据filter分页查询抄送工作项(包含process、order)
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<WorkItem> 抄送工作项集合
+	 */
+	List<HistoryOrder> getCCWorks(Page<HistoryOrder> page, QueryFilter filter);
+	
+	/**
+	 * 根据filter分页查询已完成的历史任务项
+	 * @param page 分页对象
+	 * @param filter 查询过滤器
+	 * @return List<WorkItem> 历史工作项集合
+	 */
+	List<WorkItem> getHistoryWorkItems(Page<WorkItem> page, QueryFilter filter);
+	
+	/**
+	 * 根据类型T、Sql语句、参数查询单个对象
+	 * @param T 类型
+	 * @param sql sql语句
+	 * @param args 参数列表
+	 * @return
+	 */
+	public <T> T nativeQueryObject(Class<T> T, String sql, Object... args);
+	/**
+	 * 根据类型T、Sql语句、参数查询列表对象
+	 * @param T 类型
+	 * @param sql sql语句
+	 * @param args 参数列表
+	 * @return
+	 */
+	public <T> List<T> nativeQueryList(Class<T> T, String sql, Object... args);
+	
+	/**
+	 * 根据类型T、Sql语句、参数分页查询列表对象
+	 * @param page 分页对象
+	 * @param T 类型
+	 * @param sql sql语句
+	 * @param args 参数列表
+	 * @return
+	 */
+	public <T> List<T> nativeQueryList(Page<T> page, Class<T> T, String sql, Object... args);
+}

+ 162 - 0
workflowy/src/main/java/org/snaker/engine/ITaskService.java

@@ -0,0 +1,162 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import java.util.List;
+import java.util.Map;
+
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.entity.HistoryTask;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.model.CustomModel;
+import org.snaker.engine.model.ProcessModel;
+import org.snaker.engine.model.TaskModel;
+
+/**
+ * 任务业务类,包括以下服务:
+ * 1、创建任务
+ * 2、添加、删除参与者
+ * 3、完成任务
+ * 4、撤回任务
+ * 5、回退任务
+ * 6、提取任务
+ * @author yuqs
+ * @since 1.0
+ */
+public interface ITaskService {
+	/**
+	 * 完成指定的任务,删除活动任务记录,创建历史任务
+	 * @param taskId 任务id
+	 * @return Task 任务对象
+	 */
+	Task complete(String taskId);
+	/**
+	 * 完成指定的任务,删除活动任务记录,创建历史任务
+	 * @param taskId 任务id
+	 * @param operator 操作人
+	 * @return Task 任务对象
+	 */
+	Task complete(String taskId, String operator);
+	
+	/**
+	 * 根据任务主键ID,操作人ID完成任务
+	 * @param taskId 任务id
+	 * @param operator 操作人id
+	 * @param args 参数集合
+	 * @return Task 任务对象
+	 */
+	Task complete(String taskId, String operator, Map<String, Object> args);
+
+	/**
+	 * 更新任务对象
+	 * @param task 任务对象
+	 */
+	void updateTask(Task task);
+	/**
+	 * 根据执行对象、自定义节点模型创建历史任务记录
+	 * @param execution 执行对象
+	 * @param model 自定义节点模型
+	 * @return 历史任务
+	 */
+	HistoryTask history(Execution execution, CustomModel model);
+	
+	/**
+	 * 根据任务主键ID,操作人ID提取任务
+	 * 提取任务相当于预受理操作,仅仅标识此任务只能由此操作人处理
+	 * @param taskId 任务id
+	 * @param operator 操作人id
+	 * @return Task 任务对象
+	 */
+	Task take(String taskId, String operator);
+
+    /**
+     * 根据历史任务主键id,操作人唤醒历史任务
+     * 该方法会导致流程状态不可控,请慎用
+     * @param taskId 历史任务id
+     * @param operator 操作人id
+     * @return Task 唤醒后的任务对象
+     */
+    Task resume(String taskId, String operator);
+	
+	/**
+	 * 向指定的任务id添加参与者
+	 * @param taskId 任务id
+	 * @param actors 参与者
+	 */
+	void addTaskActor(String taskId, String... actors);
+	
+	/**
+	 * 向指定的任务id添加参与者
+	 * @param taskId 任务id
+	 * @param performType 参与类型
+	 * @param actors 参与者
+	 */
+	void addTaskActor(String taskId, Integer performType, String... actors);
+	
+	/**
+	 * 对指定的任务id删除参与者
+	 * @param taskId 任务id
+	 * @param actors 参与者
+	 */
+	void removeTaskActor(String taskId, String... actors);
+	
+	/**
+	 * 根据任务主键id、操作人撤回任务
+	 * @param taskId 任务id
+	 * @param operator 操作人
+	 * @return Task 任务对象
+	 */
+	Task withdrawTask(String taskId, String operator);
+	
+	/**
+	 * 根据当前任务对象驳回至上一步处理
+	 * @param model 流程定义模型,用以获取上一步模型对象
+	 * @param currentTask 当前任务对象
+	 * @return Task 任务对象
+	 */
+	Task rejectTask(ProcessModel model, Task currentTask);
+	
+	/**
+	 * 根据taskId、operator,判断操作人operator是否允许执行任务
+	 * @param task 任务对象
+	 * @param operator 操作人
+	 * @return boolean 是否允许操作
+	 */
+	boolean isAllowed(Task task, String operator);
+	
+	/**
+	 * 根据任务模型、执行对象创建新的任务
+	 * @param taskModel 任务模型
+	 * @param execution 执行对象
+	 * @return List<Task> 创建任务集合
+	 */
+	List<Task> createTask(TaskModel taskModel, Execution execution);
+	
+	/**
+	 * 根据已有任务id、任务类型、参与者创建新的任务
+	 * @param taskId 主办任务id
+	 * @param taskType 任务类型
+	 * @param actors 参与者集合
+	 * @return List<Task> 创建任务集合
+	 */
+	List<Task> createNewTask(String taskId, int taskType, String... actors);
+
+    /**
+     * 根据任务id获取任务模型
+     * @param taskId 任务id
+     * @return
+     */
+    TaskModel getTaskModel(String taskId);
+}

+ 179 - 0
workflowy/src/main/java/org/snaker/engine/SnakerEngine.java

@@ -0,0 +1,179 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import java.util.List;
+import java.util.Map;
+
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.model.TaskModel;
+
+/**
+ * 流程引擎接口
+ * @author yuqs
+ * @since 1.0
+ */
+public interface SnakerEngine {
+	public static final String ADMIN = "snaker.admin";
+	public static final String AUTO = "snaker.auto";
+    public static final String ID = "snaker.orderNo";
+	
+	/**
+	 * 获取process服务
+	 * @return IProcessService 流程定义服务
+	 */
+	public IProcessService process();
+	
+	/**
+	 * 获取查询服务
+	 * @return IQueryService 常用查询服务
+	 */
+	public IQueryService query();
+	
+	/**
+	 * 获取实例服务
+	 * @return IQueryService 流程实例服务
+	 */
+	public IOrderService order();
+	
+	/**
+	 * 获取任务服务
+	 * @return ITaskService 任务服务
+	 */
+	public ITaskService task();
+	
+	/**
+	 * 获取管理服务
+	 * @return IManagerService 管理服务
+	 */
+	public IManagerService manager();
+	
+	/**
+	 * 根据流程定义ID启动流程实例
+	 * @param id 流程定义ID
+	 * @return Order 流程实例
+	 * @see #startInstanceById(String, String, Map)
+	 */
+	public Order startInstanceById(String id);
+	
+	/**
+	 * 根据流程定义ID,操作人ID启动流程实例
+	 * @param id 流程定义ID
+	 * @param operator 操作人ID
+	 * @return Order 流程实例
+	 * @see #startInstanceById(String, String, Map)
+	 */
+	public Order startInstanceById(String id, String operator);
+	
+	/**
+	 * 根据流程定义ID,操作人ID,参数列表启动流程实例
+	 * @param id 流程定义ID
+	 * @param operator 操作人ID
+	 * @param args 参数列表
+	 * @return Order 流程实例
+	 */
+	public Order startInstanceById(String id, String operator, Map<String, Object> args);
+	
+	/**
+	 * 根据流程名称启动流程实例
+	 * @param name 流程定义名称
+	 * @return Order 流程实例
+	 */
+	public Order startInstanceByName(String name);
+	
+	/**
+	 * 根据流程名称、版本号启动流程实例
+	 * @param name 流程定义名称
+	 * @param version 版本号
+	 * @return Order 流程实例
+	 */
+	public Order startInstanceByName(String name, Integer version);
+	
+	/**
+	 * 根据流程名称、版本号、操作人启动流程实例
+	 * @param name 流程定义名称
+	 * @param version 版本号
+	 * @param operator 操作人
+	 * @return Order 流程实例
+	 */
+	public Order startInstanceByName(String name, Integer version, String operator);
+	
+	/**
+	 * 根据流程名称、版本号、操作人、参数列表启动流程实例
+	 * @param name 流程定义名称
+	 * @param version 版本号
+	 * @param operator 操作人
+	 * @param args 参数列表
+	 * @return Order 流程实例
+	 */
+	public Order startInstanceByName(String name, Integer version, String operator, Map<String, Object> args);
+	
+	/**
+	 * 根据父执行对象启动子流程实例
+	 * @param execution 执行对象
+	 * @return Order 流程实例
+	 */
+	public Order startInstanceByExecution(Execution execution);
+	
+	/**
+	 * 根据任务主键ID执行任务
+	 * @param taskId 任务主键ID
+	 * @return List<Task> 任务集合
+	 * @see #executeTask(String, String, Map)
+	 */
+	public List<Task> executeTask(String taskId);
+	
+	/**
+	 * 根据任务主键ID,操作人ID执行任务
+	 * @param taskId 任务主键ID
+	 * @param operator 操作人主键ID
+	 * @return List<Task> 任务集合
+	 * @see #executeTask(String, String, Map)
+	 */
+	public List<Task> executeTask(String taskId, String operator);
+	
+	/**
+	 * 根据任务主键ID,操作人ID,参数列表执行任务
+	 * @param taskId 任务主键ID
+	 * @param operator 操作人主键ID
+	 * @param args 参数列表
+	 * @return List<Task> 任务集合
+	 */
+	public List<Task> executeTask(String taskId, String operator, Map<String, Object> args);
+	
+	/**
+	 * 根据任务主键ID,操作人ID,参数列表执行任务,并且根据nodeName跳转到任意节点
+	 * 1、nodeName为null时,则跳转至上一步处理
+	 * 2、nodeName不为null时,则任意跳转,即动态创建转移
+	 * @param taskId 任务主键ID
+	 * @param operator 操作人主键ID
+	 * @param args 参数列表
+	 * @param nodeName 跳转的节点名称
+	 * @return List<Task> 任务集合
+	 */
+	public List<Task> executeAndJumpTask(String taskId, String operator, Map<String, Object> args, String nodeName);
+	
+	/**
+	 * 根据流程实例ID,操作人ID,参数列表按照节点模型model创建新的自由任务
+	 * @param orderId 流程实例id
+	 * @param operator 操作人id
+	 * @param args 参数列表
+	 * @param model 节点模型
+	 * @return List<Task> 任务集合
+	 */
+	public List<Task> createFreeTask(String orderId, String operator, Map<String, Object> args, TaskModel model);
+}

+ 45 - 0
workflowy/src/main/java/org/snaker/engine/SnakerException.java

@@ -0,0 +1,45 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+/**
+ * 框架抛出的所有异常都是此类(unchecked exception)
+ * @author yuqs
+ * @since 1.0
+ */
+public class SnakerException extends RuntimeException {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -5220859421440167454L;
+
+	public SnakerException() {
+		super();
+	}
+
+	public SnakerException(String msg, Throwable cause) {
+		super(msg);
+		super.initCause(cause);
+	}
+
+	public SnakerException(String msg) {
+		super(msg);
+	}
+
+	public SnakerException(Throwable cause) {
+		super();
+		super.initCause(cause);
+	}
+}

+ 30 - 0
workflowy/src/main/java/org/snaker/engine/SnakerInterceptor.java

@@ -0,0 +1,30 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import org.snaker.engine.core.Execution;
+
+/**
+ * 任务拦截器,对产生的任务结果进行拦截
+ * @author yuqs
+ * @since 1.2
+ */
+public interface SnakerInterceptor {
+	/**
+	 * 拦截方法,参数为执行对象
+	 * @param execution 执行对象。可从中获取执行的数据
+	 */
+	public void intercept(Execution execution);
+}

+ 35 - 0
workflowy/src/main/java/org/snaker/engine/TaskAccessStrategy.java

@@ -0,0 +1,35 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine;
+
+import java.util.List;
+
+import org.snaker.engine.entity.TaskActor;
+
+/**
+ * 任务访问策略类
+ * 用于判断给定的操作人员是否允许执行某个任务
+ * @author yuqs
+ * @since 1.4
+ */
+public interface TaskAccessStrategy {
+	/**
+	 * 根据操作人id、参与者集合判断是否允许访问所属任务
+	 * @param operator 操作人id
+	 * @param actors 参与者列表 传递至该接口的实现类中的参与者都是为非空
+	 * @return boolean 是否允许访问
+	 */
+	boolean isAllowed(String operator, List<TaskActor> actors); 
+}

+ 1105 - 0
workflowy/src/main/java/org/snaker/engine/access/AbstractDBAccess.java

@@ -0,0 +1,1105 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.DBAccess;
+import org.snaker.engine.access.dialect.Db2Dialect;
+import org.snaker.engine.access.dialect.Dialect;
+import org.snaker.engine.access.dialect.H2Dialect;
+import org.snaker.engine.access.dialect.MySqlDialect;
+import org.snaker.engine.access.dialect.OracleDialect;
+import org.snaker.engine.access.dialect.PostgresqlDialect;
+import org.snaker.engine.access.dialect.SQLServerDialect;
+import org.snaker.engine.core.ServiceContext;
+import org.snaker.engine.entity.CCOrder;
+import org.snaker.engine.entity.HistoryOrder;
+import org.snaker.engine.entity.HistoryTask;
+import org.snaker.engine.entity.HistoryTaskActor;
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Process;
+import org.snaker.engine.entity.Surrogate;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.entity.TaskActor;
+import org.snaker.engine.entity.WorkItem;
+import org.snaker.engine.helper.ClassHelper;
+import org.snaker.engine.helper.StringHelper;
+
+/**
+ * 抽象数据库访问类
+ * 封装SQL语句的构造
+ * @author yuqs
+ * @since 1.0
+ */
+public abstract class AbstractDBAccess implements DBAccess {
+    private static final Logger log = LoggerFactory.getLogger(AbstractDBAccess.class);
+    protected static final String KEY_SQL = "SQL";
+	protected static final String KEY_ARGS = "ARGS";
+	protected static final String KEY_TYPE = "TYPE";
+	protected static final String KEY_ENTITY = "ENTITY";
+	
+	protected static final String KEY_SU = "SU";
+	protected static final String SAVE = "SAVE";
+	protected static final String UPDATE = "UPDATE";
+	
+	protected static final String PROCESS_INSERT = "insert into wf_process (id,name,display_Name,type,instance_Url,state,version,create_Time,creator) values (?,?,?,?,?,?,?,?,?)";
+	protected static final String PROCESS_UPDATE = "update wf_process set name=?, display_Name=?,state=?,instance_Url=?,create_Time=?,creator=? where id=? ";
+	protected static final String PROCESS_DELETE = "delete from wf_process where id = ?";
+	protected static final String PROCESS_UPDATE_BLOB = "update wf_process set content=? where id=?";
+	protected static final String PROCESS_UPDATE_TYPE = "update wf_process set type=? where id=?";
+	
+	protected static final String ORDER_INSERT = "insert into wf_order (id,process_Id,creator,create_Time,parent_Id,parent_Node_Name,expire_Time,last_Update_Time,last_Updator,order_No,variable,version) values (?,?,?,?,?,?,?,?,?,?,?,?)";
+	protected static final String ORDER_UPDATE = "update wf_order set last_Updator=?, last_Update_Time=?, variable = ?, expire_Time=?, version = version + 1 where id=? and version = ?";
+	protected static final String ORDER_DELETE = "delete from wf_order where id = ?";
+	protected static final String ORDER_HISTORY_INSERT = "insert into wf_hist_order (id,process_Id,order_State,creator,create_Time,end_Time,parent_Id,expire_Time,order_No,variable) values (?,?,?,?,?,?,?,?,?,?)";
+	protected static final String ORDER_HISTORY_UPDATE = "update wf_hist_order set order_State = ?, end_Time = ?, variable = ? where id = ? ";
+	protected static final String ORDER_HISTORY_DELETE = "delete from wf_hist_order where id = ?";
+	
+	protected static final String CCORDER_INSERT = "insert into wf_cc_order (order_Id, actor_Id, creator, create_Time, status) values (?, ?, ?, ?, ?)";
+	protected static final String CCORDER_UPDATE = "update wf_cc_order set status = ?, finish_Time = ? where order_Id = ? and actor_Id = ?";
+	protected static final String CCORDER_DELETE = "delete from wf_cc_order where order_Id = ? and actor_Id = ?";
+	
+	protected static final String TASK_INSERT = "insert into wf_task (id,order_Id,task_Name,display_Name,task_Type,perform_Type,operator,create_Time,finish_Time,expire_Time,action_Url,parent_Task_Id,variable,version) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
+	protected static final String TASK_UPDATE = "update wf_task set finish_Time=?, operator=?, variable=?, expire_Time=?, action_Url=?, version = version + 1 where id=? and version = ?";
+	protected static final String TASK_DELETE = "delete from wf_task where id = ?";
+	protected static final String TASK_HISTORY_INSERT = "insert into wf_hist_task (id,order_Id,task_Name,display_Name,task_Type,perform_Type,task_State,operator,create_Time,finish_Time,expire_Time,action_Url,parent_Task_Id,variable) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
+	protected static final String TASK_HISTORY_DELETE = "delete from wf_hist_task where id = ?";
+	
+	protected static final String TASK_ACTOR_INSERT = "insert into wf_task_actor (task_Id, actor_Id) values (?, ?)";
+	protected static final String TASK_ACTOR_DELETE = "delete from wf_task_actor where task_Id = ?";
+	protected static final String TASK_ACTOR_REDUCE = "delete from wf_task_actor where task_Id = ? and actor_Id = ?";
+	protected static final String TASK_ACTOR_HISTORY_INSERT = "insert into wf_hist_task_actor (task_Id, actor_Id) values (?, ?)";
+	protected static final String TASK_ACTOR_HISTORY_DELETE = "delete from wf_hist_task_actor where task_Id = ?";
+	
+	protected static final String QUERY_VERSION = "select max(version) from wf_process ";
+	protected static final String QUERY_PROCESS = "select id,name,display_Name,type,instance_Url,state, content, version,create_Time,creator from wf_process ";
+	protected static final String QUERY_ORDER = "select o.id,o.process_Id,o.creator,o.create_Time,o.parent_Id,o.parent_Node_Name,o.expire_Time,o.last_Update_Time,o.last_Updator,o.priority,o.order_No,o.variable, o.version from wf_order o ";
+	protected static final String QUERY_TASK = "select id,order_Id,task_Name,display_Name,task_Type,perform_Type,operator,create_Time,finish_Time,expire_Time,action_Url,parent_Task_Id,variable, version from wf_task ";
+	protected static final String QUERY_TASK_ACTOR = "select task_Id, actor_Id from wf_task_actor ";
+	protected static final String QUERY_CCORDER = "select order_Id, actor_Id, creator, create_Time, finish_Time, status from wf_cc_order ";
+	
+	protected static final String QUERY_HIST_ORDER = "select o.id,o.process_Id,o.order_State,o.priority,o.creator,o.create_Time,o.end_Time,o.parent_Id,o.expire_Time,o.order_No,o.variable from wf_hist_order o ";
+	protected static final String QUERY_HIST_TASK = "select id,order_Id,task_Name,display_Name,task_Type,perform_Type,task_State,operator,create_Time,finish_Time,expire_Time,action_Url,parent_Task_Id,variable from wf_hist_task ";
+	protected static final String QUERY_HIST_TASK_ACTOR = "select task_Id, actor_Id from wf_hist_task_actor ";
+	
+	/**委托代理CRUD*/
+	protected static final String SURROGATE_INSERT = "insert into wf_surrogate (id, process_Name, operator, surrogate, odate, sdate, edate, state) values (?,?,?,?,?,?,?,?)";
+	protected static final String SURROGATE_UPDATE = "update wf_surrogate set process_Name=?, surrogate=?, odate=?, sdate=?, edate=?, state=? where id = ?";
+	protected static final String SURROGATE_DELETE = "delete from wf_surrogate where id = ?";
+	protected static final String SURROGATE_QUERY = "select id, process_Name, operator, surrogate, odate, sdate, edate, state from wf_surrogate";
+	
+	protected Dialect dialect;
+	
+	/**
+	 * 是否为ORM框架,用以标识对象直接持久化
+	 * @return boolean
+	 */
+	public abstract boolean isORM();
+	/**
+	 * 保存或更新对象
+	 * isORM为true,则参数map只存放对象
+	 * isORM为false,则参数map需要放入SQL、ARGS、TYPE
+	 * @param map 需要持久化的数据
+	 */
+	public abstract void saveOrUpdate(Map<String, Object> map);
+	
+	public void initialize(Object accessObject) {
+		
+	}
+	
+	/**
+	 * isORM为false,需要构造map传递给实现类
+	 * @param sql 需要执行的sql语句
+	 * @param args sql语句中的参数列表
+	 * @param type sql语句中的参数类型
+	 * @return 构造的map
+	 */
+	private Map<String, Object> buildMap(String sql, Object[] args, int[] type) {
+		Map<String, Object> map = new HashMap<String, Object>();
+		map.put(KEY_SQL, sql);
+		map.put(KEY_ARGS, args);
+		map.put(KEY_TYPE, type);
+		return map;
+	}
+	
+	/**
+	 * isORM为true,只存放对象传递给orm框架
+	 * @param entity 实体对象
+     * @param su 保存或更新的标识
+	 * @return 构造的map
+	 */
+	private Map<String, Object> buildMap(Object entity, String su) {
+		Map<String, Object> map = new HashMap<String, Object>();
+		map.put(KEY_ENTITY, entity);
+		map.put(KEY_SU, su);
+		return map;
+	}
+	
+	/**
+	 * 获取数据库方言
+     * 根据数据库连接的DatabaseMetaData获取数据库厂商,自动适配具体的方言
+     * 当数据库类型未提供支持时无法自动获取方言,建议通过配置完成
+	 * @return 方言对象
+	 */
+	protected Dialect getDialect() {
+        if(dialect != null) return dialect;
+		dialect = ServiceContext.getContext().find(Dialect.class);
+		if(dialect == null) {
+			try {
+				dialect = getDialect(getConnection());
+			} catch (Exception e) {
+				log.error("Unable to find the available dialect.Please configure dialect to snaker.xml");
+			}
+		}
+		return dialect;
+	}
+	
+	/**
+	 * 由于process中涉及blob字段,未对各种框架统一,所以process操作交给具体的实现类处理
+	 */
+	public void saveProcess(Process process) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(process, SAVE));
+		} else {
+			Object[] args = new Object[]{process.getId(), process.getName(), process.getDisplayName(), process.getType(), 
+					process.getInstanceUrl(), process.getState(), process.getVersion(), process.getCreateTime(), process.getCreator()};
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, 
+					Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.VARCHAR, Types.VARCHAR};
+			saveOrUpdate(buildMap(PROCESS_INSERT, args, type));
+		}
+	}
+	/**
+	 * 由于process中涉及blob字段,未对各种框架统一,所以process操作交给具体的实现类处理
+	 */
+	public void updateProcess(Process process) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(process, UPDATE));
+		} else {
+			Object[] args = new Object[]{process.getName(), process.getDisplayName(), process.getState(), 
+					process.getInstanceUrl(), process.getCreateTime(), process.getCreator(), process.getId()};
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR};
+			saveOrUpdate(buildMap(PROCESS_UPDATE, args, type));
+		}
+	}
+
+	public void deleteProcess(Process process) {
+		if(!isORM()) {
+			Object[] args = new Object[]{process.getId()};
+			int[] type = new int[]{Types.VARCHAR};
+			saveOrUpdate(buildMap(PROCESS_DELETE, args, type));
+		}
+	}
+	
+	public void updateProcessType(String id, String type) {
+		if(isORM()) {
+			Process process = getProcess(id);
+			process.setType(type);
+			saveOrUpdate(buildMap(process, UPDATE));
+		} else {
+			Object[] args = new Object[]{type, id};
+			int[] types = new int[]{Types.VARCHAR, Types.VARCHAR};
+			saveOrUpdate(buildMap(PROCESS_UPDATE_TYPE, args, types));
+		}
+	}
+
+	public void saveTask(Task task) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(task, SAVE));
+		} else {
+			Object[] args = new Object[]{task.getId(), task.getOrderId(), task.getTaskName(), task.getDisplayName(), task.getTaskType(), 
+					task.getPerformType(), task.getOperator(), task.getCreateTime(), task.getFinishTime(), 
+					task.getExpireTime(), task.getActionUrl(), task.getParentTaskId(), task.getVariable(), task.getVersion()};
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, 
+					Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
+					Types.VARCHAR, Types.VARCHAR, Types.INTEGER};
+			saveOrUpdate(buildMap(TASK_INSERT, args, type));
+		}
+	}
+	
+	public void saveOrder(Order order) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(order, SAVE));
+		} else {
+			Object[] args = new Object[]{order.getId(), order.getProcessId(), order.getCreator(), order.getCreateTime(), order.getParentId(), 
+					order.getParentNodeName(), order.getExpireTime(), order.getLastUpdateTime(), order.getLastUpdator(), order.getOrderNo(),
+                    order.getVariable(), order.getVersion()};
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, 
+					Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER};
+			saveOrUpdate(buildMap(ORDER_INSERT, args, type));
+		}
+	}
+	
+	public void saveCCOrder(CCOrder ccorder) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(ccorder, SAVE));
+		} else {
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER};
+			saveOrUpdate(buildMap(CCORDER_INSERT, new Object[]{ccorder.getOrderId(), ccorder.getActorId(),
+                    ccorder.getCreator(), ccorder.getCreateTime(), ccorder.getStatus()}, type));
+		}
+	}
+
+	public void saveTaskActor(TaskActor taskActor) {
+        if(isORM()) {
+            saveOrUpdate(buildMap(taskActor, SAVE));
+        } else {
+            int[] type = new int[]{Types.VARCHAR, Types.VARCHAR};
+            saveOrUpdate(buildMap(TASK_ACTOR_INSERT, new Object[]{taskActor.getTaskId(), taskActor.getActorId() }, type));
+        }
+    }
+
+	public void updateTask(Task task) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(task, UPDATE));
+		} else {
+			Object[] args = new Object[]{task.getFinishTime(), task.getOperator(), task.getVariable(), task.getExpireTime(), task.getActionUrl(), task.getId(), task.getVersion() };
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,  Types.VARCHAR, Types.VARCHAR,  Types.VARCHAR, Types.INTEGER};
+			saveOrUpdate(buildMap(TASK_UPDATE, args, type));
+		}
+	}
+
+	public void updateOrder(Order order) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(order, UPDATE));
+		} else {
+			Object[] args = new Object[]{order.getLastUpdator(), order.getLastUpdateTime(), order.getVariable(), order.getExpireTime(), order.getId(), order.getVersion() };
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER};
+			saveOrUpdate(buildMap(ORDER_UPDATE, args, type));
+		}
+	}
+	
+	public void updateCCOrder(CCOrder ccorder) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(ccorder, UPDATE));
+		} else {
+			Object[] args = new Object[]{ccorder.getStatus(), ccorder.getFinishTime(), ccorder.getOrderId(), ccorder.getActorId() };
+			int[] type = new int[]{Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR};
+			saveOrUpdate(buildMap(CCORDER_UPDATE, args, type));
+		}
+	}
+	
+	public void deleteTask(Task task) {
+		if(!isORM()) {
+			Object[] args = new Object[]{task.getId()};
+			int[] type = new int[]{Types.VARCHAR};
+			saveOrUpdate(buildMap(TASK_ACTOR_DELETE, args, type));
+			saveOrUpdate(buildMap(TASK_DELETE, args, type));
+		}
+	}
+
+	public void deleteOrder(Order order) {
+		if(!isORM()) {
+			int[] type = new int[]{Types.VARCHAR};
+			saveOrUpdate(buildMap(ORDER_DELETE, new Object[]{order.getId()}, type));
+		}
+	}
+	
+	public void deleteCCOrder(CCOrder ccorder) {
+		if(!isORM()) {
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR};
+			saveOrUpdate(buildMap(CCORDER_DELETE, new Object[]{ccorder.getOrderId(), ccorder.getActorId()}, type));
+		}
+	}
+	
+	public void removeTaskActor(String taskId, String... actors) {
+		if(!isORM()) {
+			for(String actorId : actors) {
+				int[] type = new int[]{Types.VARCHAR, Types.VARCHAR};
+				saveOrUpdate(buildMap(TASK_ACTOR_REDUCE, new Object[]{taskId, actorId}, type));
+			}
+		}
+	}
+	
+	public void saveHistory(HistoryOrder order) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(order, SAVE));
+		} else {
+			Object[] args = new Object[]{order.getId(), order.getProcessId(), order.getOrderState(), order.getCreator(), 
+					order.getCreateTime(), order.getEndTime(), order.getParentId(), order.getExpireTime(), order.getOrderNo(), order.getVariable()};
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.VARCHAR, 
+					Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR};
+			saveOrUpdate(buildMap(ORDER_HISTORY_INSERT, args, type));
+		}
+	}
+
+	public void updateHistory(HistoryOrder order) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(order, UPDATE));
+		} else {
+			Object[] args = new Object[]{order.getOrderState(), order.getEndTime(), order.getVariable(), order.getId()};
+			int[] type = new int[]{Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR};
+			saveOrUpdate(buildMap(ORDER_HISTORY_UPDATE, args, type));
+		}
+	}
+
+	public void deleteHistoryOrder(HistoryOrder historyOrder) {
+		if(!isORM()) {
+			Object[] args = new Object[]{historyOrder.getId()};
+			int[] type = new int[]{Types.VARCHAR};
+			saveOrUpdate(buildMap(ORDER_HISTORY_DELETE, args, type));
+		}
+	}
+
+	public void saveHistory(HistoryTask task) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(task, SAVE));
+            if(task.getActorIds() != null) {
+                for(String actorId : task.getActorIds()) {
+                    if(StringHelper.isEmpty(actorId)) continue;
+                    HistoryTaskActor hist = new HistoryTaskActor();
+                    hist.setActorId(actorId);
+                    hist.setTaskId(task.getId());
+                    saveOrUpdate(buildMap(hist, SAVE));
+                }
+            }
+		} else {
+			Object[] args = new Object[]{task.getId(), task.getOrderId(), task.getTaskName(), task.getDisplayName(), task.getTaskType(), 
+					task.getPerformType(), task.getTaskState(), task.getOperator(), task.getCreateTime(), task.getFinishTime(), 
+					task.getExpireTime(), task.getActionUrl(), task.getParentTaskId(), task.getVariable()};
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, 
+					Types.INTEGER, Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR};
+			saveOrUpdate(buildMap(TASK_HISTORY_INSERT, args, type));
+            if(task.getActorIds() != null) {
+                for(String actorId : task.getActorIds()) {
+                    if(StringHelper.isEmpty(actorId)) continue;
+                    saveOrUpdate(buildMap(TASK_ACTOR_HISTORY_INSERT, new Object[]{task.getId(), actorId}, new int[]{Types.VARCHAR, Types.VARCHAR}));
+                }
+            }
+		}
+	}
+
+	public void deleteHistoryTask(HistoryTask historyTask) {
+		if(!isORM()) {
+			Object[] args = new Object[]{historyTask.getId()};
+			int[] type = new int[]{Types.VARCHAR};
+			saveOrUpdate(buildMap(TASK_ACTOR_HISTORY_DELETE, args, type));
+			saveOrUpdate(buildMap(TASK_HISTORY_DELETE, args, type));
+		}
+	}
+
+    public void updateOrderVariable(Order order) {
+        updateOrder(order);
+        HistoryOrder hist = getHistOrder(order.getId());
+        hist.setVariable(order.getVariable());
+        updateHistory(hist);
+    }
+	
+	public void saveSurrogate(Surrogate surrogate) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(surrogate, SAVE));
+		} else {
+			Object[] args = new Object[]{surrogate.getId(), surrogate.getProcessName(), surrogate.getOperator(),
+					surrogate.getSurrogate(), surrogate.getOdate(), surrogate.getSdate(), surrogate.getEdate(),
+					surrogate.getState()};
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
+					Types.VARCHAR, Types.INTEGER};
+			saveOrUpdate(buildMap(SURROGATE_INSERT, args, type));
+		}
+	}
+	
+	public void updateSurrogate(Surrogate surrogate) {
+		if(isORM()) {
+			saveOrUpdate(buildMap(surrogate, UPDATE));
+		} else {
+			Object[] args = new Object[]{surrogate.getProcessName(), surrogate.getSurrogate(), surrogate.getOdate(), 
+					surrogate.getSdate(), surrogate.getEdate(), surrogate.getState(), surrogate.getId()};
+			int[] type = new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,Types.VARCHAR, Types.VARCHAR, 
+					Types.INTEGER, Types.VARCHAR};
+			saveOrUpdate(buildMap(SURROGATE_UPDATE, args, type));
+		}
+	}
+	
+	public void deleteSurrogate(Surrogate surrogate) {
+		if(!isORM()) {
+			Object[] args = new Object[]{surrogate.getId()};
+			int[] type = new int[]{Types.VARCHAR};
+			saveOrUpdate(buildMap(SURROGATE_DELETE, args, type));
+		}
+	}
+	
+	public Surrogate getSurrogate(String id) {
+		String where = " where id = ?";
+		return queryObject(Surrogate.class, SURROGATE_QUERY + where, id);
+	}
+	
+	public List<Surrogate> getSurrogate(Page<Surrogate> page, QueryFilter filter) {
+		StringBuilder sql = new StringBuilder(SURROGATE_QUERY);
+		sql.append(" where 1=1 and state = 1 ");
+		List<Object> paramList = new ArrayList<Object>();
+		if(filter.getNames() != null && filter.getNames().length > 0) {
+			sql.append(" and process_Name in(");
+			for(int i = 0; i < filter.getNames().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getNames()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(filter.getOperators() != null && filter.getOperators().length > 0) {
+			sql.append(" and operator in (");
+			for(String actor : filter.getOperators()) {
+				sql.append("?,");
+				paramList.add(actor);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(StringHelper.isNotEmpty(filter.getOperateTime())) {
+			sql.append(" and sdate <= ? and edate >= ? ");
+			paramList.add(filter.getOperateTime());
+			paramList.add(filter.getOperateTime());
+		}
+        if(!filter.isOrderBySetted()) {
+            filter.setOrder(QueryFilter.DESC);
+            filter.setOrderBy("sdate");
+        }
+        return queryList(page, filter, Surrogate.class, sql.toString(), paramList.toArray());
+	}
+
+	public Task getTask(String taskId) {
+		String where = " where id = ?";
+		return queryObject(Task.class, QUERY_TASK + where, taskId);
+	}
+	
+	public List<Task> getNextActiveTasks(String parentTaskId) {
+		String where = " where parent_Task_Id = ?";
+		return queryList(Task.class, QUERY_TASK + where, parentTaskId);
+	}
+	
+	public List<Task> getNextActiveTasks(String orderId, String taskName, String parentTaskId) {
+		String sql = QUERY_TASK + " where parent_task_id in ( select ht.id from wf_hist_task ht where ht.order_id=? and ht.task_name=? and ht.parent_task_id=? )";
+		return queryList(Task.class, sql, orderId, taskName, parentTaskId);
+	}
+	
+	public HistoryTask getHistTask(String taskId) {
+		String where = " where id = ?";
+		return queryObject(HistoryTask.class, QUERY_HIST_TASK + where, taskId);
+	}
+	
+	public HistoryOrder getHistOrder(String orderId) {
+		String where = " where id = ?";
+		return queryObject(HistoryOrder.class, QUERY_HIST_ORDER + where, orderId);
+	}
+
+	public List<TaskActor> getTaskActorsByTaskId(String taskId) {
+		String where = " where task_Id = ?";
+		return queryList(TaskActor.class, QUERY_TASK_ACTOR + where, taskId);
+	}
+	
+	public List<HistoryTaskActor> getHistTaskActorsByTaskId(String taskId) {
+		String where = " where task_Id = ?";
+		return queryList(HistoryTaskActor.class, QUERY_HIST_TASK_ACTOR + where, taskId);
+	}
+
+	public Order getOrder(String orderId) {
+		String where = " where id = ?";
+		return queryObject(Order.class, QUERY_ORDER + where, orderId);
+	}
+	
+	public List<CCOrder> getCCOrder(String orderId, String... actorIds) {
+        StringBuilder where = new StringBuilder(QUERY_CCORDER);
+		where.append(" where 1 = 1 ");
+
+		if(StringHelper.isNotEmpty(orderId)) {
+			where.append(" and order_Id = ?");
+		}
+		if(actorIds != null && actorIds.length > 0) {
+			where.append(" and actor_Id in (");
+			where.append(StringUtils.repeat("?,", actorIds.length));
+			where.deleteCharAt(where.length() - 1);
+			where.append(") ");
+		}
+		return queryList(CCOrder.class, where.toString(), ArrayUtils.add(actorIds, 0, orderId));
+	}
+
+	public Process getProcess(String id) {
+		String where = " where id = ?";
+		return queryObject(Process.class, QUERY_PROCESS + where, id);
+	}
+	
+	public List<Process> getProcesss(Page<Process> page, QueryFilter filter) {
+        StringBuilder sql = new StringBuilder(QUERY_PROCESS);
+		sql.append(" where 1=1 ");
+		List<Object> paramList = new ArrayList<Object>();
+		if(filter.getNames() != null && filter.getNames().length > 0) {
+			sql.append(" and name in(");
+			for(int i = 0; i < filter.getNames().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getNames()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(filter.getVersion() != null) {
+			sql.append(" and version = ? ");
+			paramList.add(filter.getVersion());
+		}
+		if(filter.getState() != null) {
+			sql.append(" and state = ? ");
+			paramList.add(filter.getState());
+		}
+		if(StringHelper.isNotEmpty(filter.getDisplayName())) {
+			sql.append(" and display_Name like ? ");
+			paramList.add("%" + filter.getDisplayName() + "%");
+		}
+		if(StringHelper.isNotEmpty(filter.getProcessType())) {
+			sql.append(" and type = ? ");
+			paramList.add(filter.getProcessType());
+		}
+		if(filter.getOperators() != null && filter.getOperators().length > 0) {
+			sql.append(" and creator in(");
+			for(int i = 0; i < filter.getOperators().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getOperators()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+        if(!filter.isOrderBySetted()) {
+            filter.setOrder(QueryFilter.ASC);
+            filter.setOrderBy("name");
+        }
+
+		return queryList(page, filter, Process.class, sql.toString(), paramList.toArray());
+	}
+
+	public List<Order> getActiveOrders(Page<Order> page, QueryFilter filter) {
+        StringBuilder sql = new StringBuilder(QUERY_ORDER);
+        sql.append(" left join wf_process p on p.id = o.process_id ");
+		sql.append(" where 1=1 ");
+		List<Object> paramList = new ArrayList<Object>();
+		if(filter.getOperators() != null && filter.getOperators().length > 0) {
+			sql.append(" and o.creator in(");
+			for(int i = 0; i < filter.getOperators().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getOperators()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+        if(filter.getNames() != null && filter.getNames().length > 0) {
+            sql.append(" and p.name in(");
+            for(int i = 0; i < filter.getNames().length; i++) {
+                sql.append("?,");
+                paramList.add(filter.getNames()[i]);
+            }
+            sql.deleteCharAt(sql.length() - 1);
+            sql.append(") ");
+        }
+        if(StringHelper.isNotEmpty(filter.getProcessId())) {
+            sql.append(" and o.process_Id = ? ");
+            paramList.add(filter.getProcessId());
+        }
+        if(StringHelper.isNotEmpty(filter.getDisplayName())) {
+            sql.append(" and p.display_Name like ?");
+            paramList.add("%" + filter.getDisplayName() + "%");
+        }
+        if(StringHelper.isNotEmpty(filter.getProcessType())) {
+            sql.append(" and p.type = ? ");
+            paramList.add(filter.getProcessType());
+        }
+		if(StringHelper.isNotEmpty(filter.getParentId())) {
+			sql.append(" and o.parent_Id = ? ");
+			paramList.add(filter.getParentId());
+		}
+		if(filter.getExcludedIds() != null && filter.getExcludedIds().length > 0) {
+			sql.append(" and o.id not in(");
+			for(int i = 0; i < filter.getExcludedIds().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getExcludedIds()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeStart())) {
+			sql.append(" and o.create_Time >= ? ");
+			paramList.add(filter.getCreateTimeStart());
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeEnd())) {
+			sql.append(" and o.create_Time <= ? ");
+			paramList.add(filter.getCreateTimeEnd());
+		}
+		if(StringHelper.isNotEmpty(filter.getOrderNo())) {
+			sql.append(" and o.order_No = ? ");
+			paramList.add(filter.getOrderNo());
+		}
+
+        if(!filter.isOrderBySetted()) {
+            filter.setOrder(QueryFilter.DESC);
+            filter.setOrderBy("o.create_Time");
+        }
+        return queryList(page, filter, Order.class, sql.toString(), paramList.toArray());
+	}
+
+	public List<Task> getActiveTasks(Page<Task> page, QueryFilter filter) {
+        StringBuilder sql = new StringBuilder(QUERY_TASK);
+		boolean isFetchActor = filter.getOperators() != null && filter.getOperators().length > 0;
+		if(isFetchActor) {
+			sql.append(" left join wf_task_actor ta on ta.task_id = id ");
+		}
+		sql.append(" where 1=1 ");
+		List<Object> paramList = new ArrayList<Object>();
+		if(StringHelper.isNotEmpty(filter.getOrderId())) {
+			sql.append(" and order_Id = ? ");
+			paramList.add(filter.getOrderId());
+		}
+		if(filter.getExcludedIds() != null && filter.getExcludedIds().length > 0) {
+			sql.append(" and id not in(");
+			for(int i = 0; i < filter.getExcludedIds().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getExcludedIds()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(isFetchActor) {
+			sql.append(" and ta.actor_Id in (");
+			for(int i = 0; i < filter.getOperators().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getOperators()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(filter.getNames() != null && filter.getNames().length > 0) {
+			sql.append(" and task_Name in (");
+			for(int i = 0; i < filter.getNames().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getNames()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeStart())) {
+			sql.append(" and create_Time >= ? ");
+			paramList.add(filter.getCreateTimeStart());
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeEnd())) {
+			sql.append(" and create_Time <= ? ");
+			paramList.add(filter.getCreateTimeEnd());
+		}
+        if(!filter.isOrderBySetted()) {
+            filter.setOrder(QueryFilter.DESC);
+            filter.setOrderBy("create_Time");
+        }
+        return queryList(page, filter, Task.class, sql.toString(), paramList.toArray());
+	}
+
+	public List<HistoryOrder> getHistoryOrders(Page<HistoryOrder> page, QueryFilter filter) {
+        StringBuilder sql = new StringBuilder(QUERY_HIST_ORDER);
+        sql.append(" left join wf_process p on p.id = o.process_id ");
+		sql.append(" where 1=1 ");
+		List<Object> paramList = new ArrayList<Object>();
+		if(filter.getOperators() != null && filter.getOperators().length > 0) {
+			sql.append(" and o.creator in(");
+			for(int i = 0; i < filter.getOperators().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getOperators()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+        if(filter.getNames() != null && filter.getNames().length > 0) {
+            sql.append(" and p.name in(");
+            for(int i = 0; i < filter.getNames().length; i++) {
+                sql.append("?,");
+                paramList.add(filter.getNames()[i]);
+            }
+            sql.deleteCharAt(sql.length() - 1);
+            sql.append(") ");
+        }
+		if(StringHelper.isNotEmpty(filter.getProcessId())) {
+			sql.append(" and o.process_Id = ? ");
+			paramList.add(filter.getProcessId());
+		}
+        if(StringHelper.isNotEmpty(filter.getProcessType())) {
+            sql.append(" and p.type = ? ");
+            paramList.add(filter.getProcessType());
+        }
+        if(StringHelper.isNotEmpty(filter.getDisplayName())) {
+            sql.append(" and p.display_Name like ?");
+            paramList.add("%" + filter.getDisplayName() + "%");
+        }
+		if(StringHelper.isNotEmpty(filter.getParentId())) {
+			sql.append(" and o.parent_Id = ? ");
+			paramList.add(filter.getParentId());
+		}
+        if(filter.getState() != null) {
+            sql.append(" and o.order_State = ? ");
+            paramList.add(filter.getState());
+        }
+		if(StringHelper.isNotEmpty(filter.getCreateTimeStart())) {
+			sql.append(" and o.create_Time >= ? ");
+			paramList.add(filter.getCreateTimeStart());
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeEnd())) {
+			sql.append(" and o.create_Time <= ? ");
+			paramList.add(filter.getCreateTimeEnd());
+		}
+		if(StringHelper.isNotEmpty(filter.getOrderNo())) {
+			sql.append(" and o.order_No = ? ");
+			paramList.add(filter.getOrderNo());
+		}
+        if(!filter.isOrderBySetted()) {
+            filter.setOrder(QueryFilter.DESC);
+            filter.setOrderBy("o.create_Time");
+        }
+        return queryList(page, filter, HistoryOrder.class, sql.toString(), paramList.toArray());
+	}
+	
+	public List<HistoryTask> getHistoryTasks(Page<HistoryTask> page, QueryFilter filter) {
+        StringBuilder sql = new StringBuilder(QUERY_HIST_TASK);
+		boolean isFetchActor = filter.getOperators() != null && filter.getOperators().length > 0;
+		if(isFetchActor) {
+			sql.append(" left join wf_hist_task_actor ta on ta.task_id = id ");
+		}
+		sql.append(" where 1=1 ");
+		List<Object> paramList = new ArrayList<Object>();
+		if(StringHelper.isNotEmpty(filter.getOrderId())) {
+			sql.append(" and order_Id = ? ");
+			paramList.add(filter.getOrderId());
+		}
+		if(isFetchActor) {
+			sql.append(" and ta.actor_Id in (");
+			for(int i = 0; i < filter.getOperators().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getOperators()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(filter.getNames() != null && filter.getNames().length > 0) {
+			sql.append(" and task_Name in (");
+			for(int i = 0; i < filter.getNames().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getNames()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeStart())) {
+			sql.append(" and create_Time >= ? ");
+			paramList.add(filter.getCreateTimeStart());
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeEnd())) {
+			sql.append(" and create_Time <= ? ");
+			paramList.add(filter.getCreateTimeEnd());
+		}
+        if(!filter.isOrderBySetted()) {
+            filter.setOrder(QueryFilter.DESC);
+            filter.setOrderBy("finish_Time");
+        }
+		return queryList(page, filter, HistoryTask.class, sql.toString(), paramList.toArray());
+	}
+	
+	public List<WorkItem> getWorkItems(Page<WorkItem> page, QueryFilter filter) {
+        StringBuilder sql = new StringBuilder();
+		sql.append(" select distinct o.process_Id, t.order_Id, t.id as id, t.id as task_Id, p.display_Name as process_Name, p.instance_Url, o.parent_Id, o.creator, ");
+		sql.append(" o.create_Time as order_Create_Time, o.expire_Time as order_Expire_Time, o.order_No, o.variable as order_Variable, ");
+		sql.append(" t.display_Name as task_Name, t.task_Name as task_Key, t.task_Type, t.perform_Type, t.operator, t.action_Url, ");
+		sql.append(" t.create_Time as task_Create_Time, t.finish_Time as task_End_Time, t.expire_Time as task_Expire_Time, t.variable as task_Variable ");
+		sql.append(" from wf_task t ");
+		sql.append(" left join wf_order o on t.order_id = o.id ");
+		sql.append(" left join wf_task_actor ta on ta.task_id=t.id ");
+		sql.append(" left join wf_process p on p.id = o.process_id ");
+		sql.append(" where 1=1 ");
+		
+		/**
+		 * 查询条件构造sql的where条件
+		 */
+		List<Object> paramList = new ArrayList<Object>();
+		if(filter.getOperators() != null && filter.getOperators().length > 0) {
+			sql.append(" and ta.actor_Id in (");
+			for(String actor : filter.getOperators()) {
+				sql.append("?,");
+				paramList.add(actor);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		
+		if(StringHelper.isNotEmpty(filter.getProcessId())) {
+			sql.append(" and o.process_Id = ?");
+			paramList.add(filter.getProcessId());
+		}
+		if(StringHelper.isNotEmpty(filter.getDisplayName())) {
+			sql.append(" and p.display_Name like ?");
+			paramList.add("%" + filter.getDisplayName() + "%");
+		}
+		if(StringHelper.isNotEmpty(filter.getParentId())) {
+			sql.append(" and o.parent_Id = ? ");
+			paramList.add(filter.getParentId());
+		}
+		if(StringHelper.isNotEmpty(filter.getOrderId())) {
+			sql.append(" and t.order_id = ? ");
+			paramList.add(filter.getOrderId());
+		}
+		if(filter.getNames() != null && filter.getNames().length > 0) {
+			sql.append(" and t.task_Name in (");
+			for(int i = 0; i < filter.getNames().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getNames()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(filter.getTaskType() != null) {
+			sql.append(" and t.task_Type = ? ");
+			paramList.add(filter.getTaskType());
+		}
+		if(filter.getPerformType() != null) {
+			sql.append(" and t.perform_Type = ? ");
+			paramList.add(filter.getPerformType());
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeStart())) {
+			sql.append(" and t.create_Time >= ? ");
+			paramList.add(filter.getCreateTimeStart());
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeEnd())) {
+			sql.append(" and t.create_Time <= ? ");
+			paramList.add(filter.getCreateTimeEnd());
+		}
+		if(!filter.isOrderBySetted()) {
+            filter.setOrder(QueryFilter.DESC);
+            filter.setOrderBy("t.create_Time");
+		}
+
+		return queryList(page, filter, WorkItem.class, sql.toString(), paramList.toArray());
+	}
+	
+	public List<HistoryOrder> getCCWorks(Page<HistoryOrder> page, QueryFilter filter) {
+        StringBuilder sql = new StringBuilder();
+		sql.append(" select id,process_Id,order_State,priority,cc.creator,cc.create_Time,end_Time,parent_Id,expire_Time,order_No,variable ");
+		sql.append(" from wf_cc_order cc ");
+		sql.append(" left join wf_hist_order o on cc.order_id = o.id ");
+		sql.append(" where 1=1 ");
+		
+		/**
+		 * 查询条件构造sql的where条件
+		 */
+		List<Object> paramList = new ArrayList<Object>();
+		if(filter.getOperators() != null && filter.getOperators().length > 0) {
+			sql.append(" and cc.actor_Id in(");
+			for(int i = 0; i < filter.getOperators().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getOperators()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(filter.getState() != null) {
+			sql.append(" and cc.status = ? ");
+			paramList.add(filter.getState());
+		}
+		if(StringHelper.isNotEmpty(filter.getProcessId())) {
+			sql.append(" and process_Id = ? ");
+			paramList.add(filter.getProcessId());
+		}
+		if(StringHelper.isNotEmpty(filter.getParentId())) {
+			sql.append(" and parent_Id = ? ");
+			paramList.add(filter.getParentId());
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeStart())) {
+			sql.append(" and cc.create_Time >= ? ");
+			paramList.add(filter.getCreateTimeStart());
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeEnd())) {
+			sql.append(" and cc.create_Time <= ? ");
+			paramList.add(filter.getCreateTimeEnd());
+		}
+		if(StringHelper.isNotEmpty(filter.getOrderNo())) {
+			sql.append(" and order_No = ? ");
+			paramList.add(filter.getOrderNo());
+		}
+        if(!filter.isOrderBySetted()) {
+            filter.setOrder(QueryFilter.DESC);
+            filter.setOrderBy("cc.create_Time");
+        }
+        return queryList(page, filter, HistoryOrder.class, sql.toString(), paramList.toArray());
+	}
+	
+	public List<WorkItem> getHistoryWorkItems(Page<WorkItem> page, QueryFilter filter) {
+        StringBuilder sql = new StringBuilder();
+		sql.append(" select distinct o.process_Id, t.order_Id, t.id as id, t.id as task_Id, p.display_Name as process_Name, p.instance_Url, o.parent_Id, o.creator, ");
+		sql.append(" o.create_Time as order_Create_Time, o.expire_Time as order_Expire_Time, o.order_No, o.variable as order_Variable, ");
+		sql.append(" t.display_Name as task_Name, t.task_Name as task_Key, t.task_Type, t.perform_Type,t.operator, t.action_Url, ");
+		sql.append(" t.create_Time as task_Create_Time, t.finish_Time as task_End_Time, t.expire_Time as task_Expire_Time, t.variable as task_Variable ");
+		sql.append(" from wf_hist_task t ");
+		sql.append(" left join wf_hist_order o on t.order_id = o.id ");
+		sql.append(" left join wf_hist_task_actor ta on ta.task_id=t.id ");
+		sql.append(" left join wf_process p on p.id = o.process_id ");
+		sql.append(" where 1=1 ");
+		/**
+		 * 查询条件构造sql的where条件
+		 */
+		List<Object> paramList = new ArrayList<Object>();
+		if(filter.getOperators() != null && filter.getOperators().length > 0) {
+			sql.append(" and ta.actor_Id in (");
+			for(String actor : filter.getOperators()) {
+				sql.append("?,");
+				paramList.add(actor);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		
+		if(StringHelper.isNotEmpty(filter.getProcessId())) {
+			sql.append(" and o.process_Id = ?");
+			paramList.add(filter.getProcessId());
+		}
+		if(StringHelper.isNotEmpty(filter.getDisplayName())) {
+			sql.append(" and p.display_Name like ?");
+			paramList.add("%" + filter.getDisplayName() + "%");
+		}
+		if(StringHelper.isNotEmpty(filter.getParentId())) {
+			sql.append(" and o.parent_Id = ? ");
+			paramList.add(filter.getParentId());
+		}
+		if(StringHelper.isNotEmpty(filter.getOrderId())) {
+			sql.append(" and t.order_id = ? ");
+			paramList.add(filter.getOrderId());
+		}
+		if(filter.getNames() != null && filter.getNames().length > 0) {
+			sql.append(" and t.task_Name in (");
+			for(int i = 0; i < filter.getNames().length; i++) {
+				sql.append("?,");
+				paramList.add(filter.getNames()[i]);
+			}
+			sql.deleteCharAt(sql.length() - 1);
+			sql.append(") ");
+		}
+		if(filter.getTaskType() != null) {
+			sql.append(" and t.task_Type = ? ");
+			paramList.add(filter.getTaskType());
+		}
+		if(filter.getPerformType() != null) {
+			sql.append(" and t.perform_Type = ? ");
+			paramList.add(filter.getPerformType());
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeStart())) {
+			sql.append(" and t.create_Time >= ? ");
+			paramList.add(filter.getCreateTimeStart());
+		}
+		if(StringHelper.isNotEmpty(filter.getCreateTimeEnd())) {
+			sql.append(" and t.create_Time <= ? ");
+			paramList.add(filter.getCreateTimeEnd());
+		}
+		
+		if(!filter.isOrderBySetted()) {
+            filter.setOrder(QueryFilter.DESC);
+            filter.setOrderBy("t.create_Time");
+		}
+		return queryList(page, filter, WorkItem.class, sql.toString(), paramList.toArray());
+	}
+
+    public <T> List<T> queryList(Page<T> page, QueryFilter filter, Class<T> clazz, String sql, Object... args) {
+        String orderby = StringHelper.buildPageOrder(filter.getOrder(), filter.getOrderBy());
+        String querySQL = sql + orderby;
+        if(page == null) {
+            return queryList(clazz, querySQL, args);
+        }
+        String countSQL = "select count(1) from (" + sql + ") c ";
+        querySQL = getDialect().getPageSql(querySQL, page);
+        if(log.isDebugEnabled()) {
+            log.debug("查询分页countSQL=\n" + countSQL);
+            log.debug("查询分页querySQL=\n" + querySQL);
+        }
+        try {
+            Object count = queryCount(countSQL, args);
+            List<T> list = queryList(clazz, querySQL, args);
+            if(list == null) list = Collections.emptyList();
+            page.setResult(list);
+            page.setTotalCount(ClassHelper.castLong(count));
+            return list;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * 根据连接对象获取数据库方言
+     * @param conn 数据库连接
+     * @return Dialect 方言
+     * @throws Exception
+     */
+    private Dialect getDialect(Connection conn) throws Exception {
+        DatabaseMetaData databaseMetaData = conn.getMetaData();
+        String databaseProductName = databaseMetaData.getDatabaseProductName();
+        
+        Properties databaseTypeMappings = new Properties();
+        databaseTypeMappings.setProperty("H2","h2");
+        databaseTypeMappings.setProperty("MySQL","mysql");
+        databaseTypeMappings.setProperty("Oracle","oracle");
+        databaseTypeMappings.setProperty("PostgreSQL","postgres");
+        databaseTypeMappings.setProperty("Microsoft SQL Server","mssql");
+        databaseTypeMappings.setProperty("DB2","db2");
+        databaseTypeMappings.setProperty("DB2","db2");
+        databaseTypeMappings.setProperty("DB2/NT","db2");
+        databaseTypeMappings.setProperty("DB2/NT64","db2");
+        databaseTypeMappings.setProperty("DB2 UDP","db2");
+        databaseTypeMappings.setProperty("DB2/LINUX","db2");
+        databaseTypeMappings.setProperty("DB2/LINUX390","db2");
+        databaseTypeMappings.setProperty("DB2/LINUXX8664","db2");
+        databaseTypeMappings.setProperty("DB2/LINUXZ64","db2");
+        databaseTypeMappings.setProperty("DB2/400 SQL","db2");
+        databaseTypeMappings.setProperty("DB2/6000","db2");
+        databaseTypeMappings.setProperty("DB2 UDB iSeries","db2");
+        databaseTypeMappings.setProperty("DB2/AIX64","db2");
+        databaseTypeMappings.setProperty("DB2/HPUX","db2");
+        databaseTypeMappings.setProperty("DB2/HP64","db2");
+        databaseTypeMappings.setProperty("DB2/SUN","db2");
+        databaseTypeMappings.setProperty("DB2/SUN64","db2");
+        databaseTypeMappings.setProperty("DB2/PTX","db2");
+        databaseTypeMappings.setProperty("DB2/2","db2");
+        String dbType = databaseTypeMappings.getProperty(databaseProductName);
+		if(StringHelper.isEmpty(dbType)) return null;
+        if(dbType.equalsIgnoreCase("mysql")) return new MySqlDialect();
+        else if(dbType.equalsIgnoreCase("oracle")) return new OracleDialect();
+        else if(dbType.equalsIgnoreCase("postgres")) return new PostgresqlDialect();
+        else if(dbType.equalsIgnoreCase("mssql")) return new SQLServerDialect();
+		else if(dbType.equalsIgnoreCase("db2")) return new Db2Dialect();
+		else if(dbType.equalsIgnoreCase("h2")) return new H2Dialect();
+        else return null;
+    }
+
+    /**
+     * 分页查询时,符合条件的总记录数
+     * @param sql sql语句
+     * @param args 参数数组
+     * @return 总记录数
+     */
+    protected abstract Object queryCount(String sql, Object... args);
+
+    /**
+     * 获取数据库连接
+     * @return Connection
+     */
+    protected abstract Connection getConnection() throws SQLException;
+}

+ 184 - 0
workflowy/src/main/java/org/snaker/engine/access/Page.java

@@ -0,0 +1,184 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access;
+
+import java.util.List;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.springframework.beans.factory.annotation.Value;
+
+/**
+ * 与具体DBAccess实现无关的分页参数及查询结果封装.
+ * @param <T> Page中对象的类型.
+ * @author yuqs
+ * @since 1.0
+ */
+public class Page<T> {
+	public static final int NON_PAGE = -1;
+	public static final int PAGE_SIZE = 15;
+
+	// 当前页
+	private int pageNo = 1;
+	
+	// 每页记录数
+	@Value("jdbc.pageSize:15")
+	private int pageSize = -1;
+	
+	// 总记录数
+	private long totalCount = 0;
+	// 查询结果集
+	private List<T> result;
+
+	public Page() {
+		if (pageSize <= 0)
+			pageSize = PAGE_SIZE;
+	}
+
+	public Page(int pageSize) {
+		this.pageSize = pageSize;
+	}
+
+	/**
+	 * 获得当前页的页号,默认为1.
+	 */
+	public int getPageNo() {
+		return pageNo;
+	}
+
+	/**
+	 * 设置当前页的页号,小于1时自动设置为1.
+	 */
+	public void setPageNo(int pageNo) {
+		this.pageNo = pageNo;
+		if (pageNo < 1) {
+			this.pageNo = 1;
+		}
+	}
+
+	/**
+	 * 返回Page对象自身的setPageNo函数,可用于连续设置。
+	 */
+	public Page<T> pageNo(int thePageNo) {
+		setPageNo(thePageNo);
+		return this;
+	}
+
+	/**
+	 * 获得每页记录数.
+	 */
+	public int getPageSize() {
+		return pageSize;
+	}
+
+	/**
+	 * 设置每页的记录数.
+	 */
+	public void setPageSize(int pageSize) {
+		this.pageSize = pageSize;
+	}
+
+	/**
+	 * 返回Page对象自身的setPageSize函数,用于连续设置。
+	 */
+	public Page<T> pageSize(int thePageSize) {
+		setPageSize(thePageSize);
+		return this;
+	}
+
+	/**
+	 * 获得页内的记录列表.
+	 */
+	public List<T> getResult() {
+		return result;
+	}
+
+	/**
+	 * 设置页内的记录列表.
+	 */
+	public void setResult(List<T> result) {
+		this.result = result;
+	}
+
+	/**
+	 * 获得总记录数, 默认值为0.
+	 */
+	public long getTotalCount() {
+		return totalCount < 0 ? 0 : totalCount;
+	}
+
+	/**
+	 * 设置总记录数.
+	 */
+	public void setTotalCount(long totalCount) {
+		this.totalCount = totalCount;
+	}
+
+	/**
+	 * 根据pageSize与totalCount计算总页数, 默认值为-1.
+	 */
+	public long getTotalPages() {
+		if (totalCount < 0) {
+			return 0;
+		}
+
+		long count = totalCount / pageSize;
+		if (totalCount % pageSize > 0) {
+			count++;
+		}
+		return count;
+	}
+
+	/**
+	 * 是否还有下一页.
+	 */
+	public boolean isHasNext() {
+		return (pageNo + 1 <= getTotalPages());
+	}
+
+	/**
+	 * 取得下页的页号, 序号从1开始.
+	 * 当前页为尾页时仍返回尾页序号.
+	 */
+	public int getNextPage() {
+		if (isHasNext()) {
+			return pageNo + 1;
+		} else {
+			return pageNo;
+		}
+	}
+
+	/**
+	 * 是否还有上一页.
+	 */
+	public boolean isHasPre() {
+		return (pageNo - 1 >= 1);
+	}
+
+	/**
+	 * 取得上页的页号, 序号从1开始.
+	 * 当前页为首页时返回首页序号.
+	 */
+	public int getPrePage() {
+		if (isHasPre()) {
+			return pageNo - 1;
+		} else {
+			return pageNo;
+		}
+	}
+
+	public String toString() {
+		return ToStringBuilder.reflectionToString(this);
+	}
+}

+ 294 - 0
workflowy/src/main/java/org/snaker/engine/access/QueryFilter.java

@@ -0,0 +1,294 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access;
+
+import java.io.Serializable;
+
+import org.apache.commons.lang.StringUtils;
+import org.snaker.engine.helper.AssertHelper;
+
+/**
+ * 通用查询过滤器
+ * @author yuqs
+ * @since 1.2.5
+ */
+public class QueryFilter implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -8155136377911571881L;
+    public static final String ASC = "asc";
+    public static final String DESC = "desc";
+
+    //排序字段
+    private String orderBy;
+    //排序类型ASC/DESC
+    private String order;
+
+	/*********common parameters***********/
+	/**
+	 * 流程定义id
+	 */
+	private String processId;
+	/**
+	 * 流程定义版本号
+	 */
+	private Integer version;
+	/**
+	 * 流程实例id
+	 */
+	private String orderId;
+	/**
+	 * 任务id
+	 */
+	private String taskId;
+	/**
+	 * 创建时间范围
+	 */
+	private String createTimeStart;
+	private String createTimeEnd;
+	private String operateTime;
+	/**
+	 * 操作人员id
+	 */
+	private String[] operators;
+	/**
+	 * 名称
+	 */
+	private String[] names;
+	/**
+	 * 显示名称
+	 */
+	private String displayName;
+	/**
+	 * 状态
+	 */
+	private Integer state;
+	/**
+	 * 流程类型
+	 */
+	private String processType;
+	/**
+	 * exclude ids
+	 */
+	private String[] excludedIds;
+	
+	/*********order parameters***********/
+	/**
+	 * 父实例id
+	 */
+	private String parentId;
+	/**
+	 * 实例编号
+	 */
+	private String orderNo;
+	
+	/*********task parameters***********/
+	/**
+	 * 任务类型
+	 */
+	private Integer taskType;
+	/**
+	 * 任务参与类型
+	 */
+	private Integer performType;
+	
+	public String getProcessId() {
+		return processId;
+	}
+	public QueryFilter setProcessId(String processId) {
+		AssertHelper.notEmpty(processId);
+		this.processId = processId;
+		return this;
+	}
+	public String getOrderId() {
+		return orderId;
+	}
+	public QueryFilter setOrderId(String orderId) {
+		AssertHelper.notEmpty(orderId);
+		this.orderId = orderId;
+		return this;
+	}
+	public String getTaskId() {
+		return taskId;
+	}
+	public QueryFilter setTaskId(String taskId) {
+		AssertHelper.notEmpty(taskId);
+		this.taskId = taskId;
+		return this;
+	}
+	public String getCreateTimeStart() {
+		return createTimeStart;
+	}
+	public QueryFilter setCreateTimeStart(String createTimeStart) {
+		AssertHelper.notEmpty(createTimeStart);
+		this.createTimeStart = createTimeStart;
+		return this;
+	}
+	public String getCreateTimeEnd() {
+		return createTimeEnd;
+	}
+	public QueryFilter setCreateTimeEnd(String createTimeEnd) {
+		AssertHelper.notEmpty(createTimeEnd);
+		this.createTimeEnd = createTimeEnd;
+		return this;
+	}
+	public String[] getOperators() {
+		return operators;
+	}
+	public QueryFilter setOperators(String[] operators) {
+		AssertHelper.notNull(operators);
+		this.operators = operators;
+		return this;
+	}
+	public QueryFilter setOperator(String operator) {
+		AssertHelper.notEmpty(operator);
+		this.operators = new String[1];
+		this.operators[0] = operator;
+		return this;
+	}
+	public String[] getNames() {
+		return names;
+	}
+	public QueryFilter setNames(String[] names) {
+		AssertHelper.notNull(names);
+		this.names = names;
+		return this;
+	}
+	public QueryFilter setName(String name) {
+		AssertHelper.notEmpty(name);
+		this.names = new String[1];
+		this.names[0] = name;
+		return this;
+	}
+	public String getDisplayName() {
+		return displayName;
+	}
+	public QueryFilter setDisplayName(String displayName) {
+		AssertHelper.notEmpty(displayName);
+		this.displayName = displayName;
+		return this;
+	}
+	public Integer getState() {
+		return state;
+	}
+	public QueryFilter setState(Integer state) {
+		AssertHelper.notNull(state);
+		this.state = state;
+		return this;
+	}
+	public String getParentId() {
+		return parentId;
+	}
+	public QueryFilter setParentId(String parentId) {
+		AssertHelper.notEmpty(parentId);
+		this.parentId = parentId;
+		return this;
+	}
+	public String getOrderNo() {
+		return orderNo;
+	}
+	public QueryFilter setOrderNo(String orderNo) {
+		AssertHelper.notEmpty(orderNo);
+		this.orderNo = orderNo;
+		return this;
+	}
+	public Integer getTaskType() {
+		return taskType;
+	}
+	public QueryFilter setTaskType(Integer taskType) {
+		AssertHelper.notNull(taskType);
+		this.taskType = taskType;
+		return this;
+	}
+	public Integer getPerformType() {
+		return performType;
+	}
+	public QueryFilter setPerformType(Integer performType) {
+		AssertHelper.notNull(performType);
+		this.performType = performType;
+		return this;
+	}
+	public String[] getExcludedIds() {
+		return excludedIds;
+	}
+	public QueryFilter setExcludedIds(String[] excludedIds) {
+		AssertHelper.notNull(excludedIds);
+		this.excludedIds = excludedIds;
+		return this;
+	}
+	public Integer getVersion() {
+		return version;
+	}
+	public QueryFilter setVersion(Integer version) {
+		AssertHelper.notNull(version);
+		this.version = version;
+		return this;
+	}
+	public String getOperateTime() {
+		return operateTime;
+	}
+	public QueryFilter setOperateTime(String operateTime) {
+		AssertHelper.notEmpty(operateTime);
+		this.operateTime = operateTime;
+		return this;
+	}
+	public String getProcessType() {
+		return processType;
+	}
+	public QueryFilter setProcessType(String processType) {
+		AssertHelper.notEmpty(processType);
+		this.processType = processType;
+		return this;
+	}
+    public String getOrderBy() {
+        return orderBy;
+    }
+    public void setOrderBy(String orderBy) {
+        this.orderBy = orderBy;
+    }
+    public QueryFilter orderBy(String theOrderBy) {
+        setOrderBy(theOrderBy);
+        return this;
+    }
+    public String getOrder() {
+        return order;
+    }
+    /**
+     * 设置排序类型.
+     * @param order 可选值为desc或asc,多个排序字段时用','分隔.
+     */
+    public void setOrder(String order) {
+        String lowcaseOrder = StringUtils.lowerCase(order);
+        //检查order字符串的合法值
+        String[] orders = StringUtils.split(lowcaseOrder, ',');
+        for (String orderStr : orders) {
+            if (!StringUtils.equals(DESC, orderStr) && !StringUtils.equals(ASC, orderStr)) {
+                throw new IllegalArgumentException("排序类型[" + orderStr + "]不是合法值");
+            }
+        }
+        this.order = lowcaseOrder;
+    }
+    public QueryFilter order(String theOrder) {
+        setOrder(theOrder);
+        return this;
+    }
+    /**
+     * 是否已设置排序字段,无默认值.
+     */
+    public boolean isOrderBySetted() {
+        return (StringUtils.isNotBlank(orderBy) && StringUtils.isNotBlank(order));
+    }
+}

+ 23 - 0
workflowy/src/main/java/org/snaker/engine/access/dialect/Db2Dialect.java

@@ -0,0 +1,23 @@
+package org.snaker.engine.access.dialect;
+
+import org.snaker.engine.access.Page;
+import org.snaker.engine.access.dialect.Dialect;
+
+/**
+ * db2方言
+ * @author yuqs
+ * @since 2.5
+ */
+public class Db2Dialect implements Dialect {
+    public String getPageSql(String sql, Page<?> page) {
+        StringBuffer pageSql = new StringBuffer(sql.length() + 100);
+        pageSql.append("SELECT * FROM  ( SELECT B.*, ROWNUMBER() OVER() AS RN FROM ( ");
+        pageSql.append(sql);
+        long start = (page.getPageNo() - 1) * page.getPageSize() + 1;
+        pageSql.append(" ) AS B )AS A WHERE A.RN BETWEEN ");
+        pageSql.append(start);
+        pageSql.append(" AND ");
+        pageSql.append(start + page.getPageSize());
+        return pageSql.toString();
+    }
+}

+ 32 - 0
workflowy/src/main/java/org/snaker/engine/access/dialect/Dialect.java

@@ -0,0 +1,32 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access.dialect;
+
+import org.snaker.engine.access.Page;
+
+/**
+ * 数据库差异的方言接口
+ * @author yuqs
+ * @since 1.0
+ */
+public interface Dialect {
+	/**
+	 * 根据分页对象获取分页sql语句
+	 * @param sql 未分页sql语句
+	 * @param page 分页对象
+	 * @return
+	 */
+	String getPageSql(String sql, Page<?> page);
+}

+ 35 - 0
workflowy/src/main/java/org/snaker/engine/access/dialect/H2Dialect.java

@@ -0,0 +1,35 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access.dialect;
+
+import org.snaker.engine.access.Page;
+
+/**
+ * H2数据库方言实现
+ * @author yuqs
+ * @since 1.0
+ */
+public class H2Dialect implements Dialect {
+	/**
+	 * mysql分页通过limit实现
+	 */
+	public String getPageSql(String sql, Page<?> page) {
+		StringBuffer pageSql = new StringBuffer(sql.length() + 100);
+		pageSql.append(sql);
+		long start = (page.getPageNo() - 1) * page.getPageSize();
+		pageSql.append(" limit ").append(start).append(",").append(page.getPageSize());
+		return pageSql.toString();
+	}
+}

+ 37 - 0
workflowy/src/main/java/org/snaker/engine/access/dialect/MySqlDialect.java

@@ -0,0 +1,37 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access.dialect;
+
+import org.snaker.engine.access.Page;
+import org.springframework.stereotype.Component;
+
+/**
+ * Mysql数据库方言实现
+ * @author yuqs
+ * @since 1.0
+ */
+@Component
+public class MySqlDialect implements Dialect {
+	/**
+	 * mysql分页通过limit实现
+	 */
+	public String getPageSql(String sql, Page<?> page) {
+		StringBuffer pageSql = new StringBuffer(sql.length() + 100);
+		pageSql.append(sql);
+		long start = (page.getPageNo() - 1) * page.getPageSize();
+		pageSql.append(" limit ").append(start).append(",").append(page.getPageSize());
+		return pageSql.toString();
+	}
+}

+ 39 - 0
workflowy/src/main/java/org/snaker/engine/access/dialect/OracleDialect.java

@@ -0,0 +1,39 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access.dialect;
+
+import org.snaker.engine.access.Page;
+
+/**
+ * Oracle数据库方言实现
+ * @author yuqs
+ * @since 1.0
+ */
+public class OracleDialect implements Dialect {
+	/**
+	 * oracle分页通过rownum实现
+	 */
+	public String getPageSql(String sql, Page<?> page) {
+		StringBuffer pageSql = new StringBuffer(sql.length() + 100);
+		pageSql.append("select * from ( select row_.*, rownum rownum_ from ( ");
+		pageSql.append(sql);
+		long start = (page.getPageNo() - 1) * page.getPageSize() + 1;
+		pageSql.append(" ) row_ where rownum < ");
+		pageSql.append(start + page.getPageSize());
+		pageSql.append(" ) where rownum_ >= ");
+		pageSql.append(start);
+		return pageSql.toString();
+	}
+}

+ 47 - 0
workflowy/src/main/java/org/snaker/engine/access/dialect/PostgresqlDialect.java

@@ -0,0 +1,47 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access.dialect;
+
+import org.snaker.engine.access.Page;
+
+/**
+ * Postgresql数据库方言实现
+ * @author yuqs
+ * @since 1.3
+ */
+public class PostgresqlDialect implements Dialect {
+	/**
+	 * Postgresql分页通过limit实现
+	 */
+	public String getPageSql(String sql, Page<?> page) {
+		StringBuffer pageSql = new StringBuffer(sql.length() + 100);
+		pageSql.append(getPageBefore(sql, page));
+		pageSql.append(sql);
+		pageSql.append(getPageAfter(sql, page));
+		return pageSql.toString();
+	}
+
+	
+	public String getPageBefore(String sql, Page<?> page) {
+		return "";
+	}
+
+	public String getPageAfter(String sql, Page<?> page) {
+		long start = (page.getPageNo() - 1) * page.getPageSize();
+		StringBuffer sb = new StringBuffer();
+		sb.append(" limit ").append(page.getPageSize()).append(" offset ").append(start);
+		return sb.toString();
+	}
+}

+ 88 - 0
workflowy/src/main/java/org/snaker/engine/access/dialect/SQLServerDialect.java

@@ -0,0 +1,88 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access.dialect;
+
+import org.apache.commons.lang.StringUtils;
+import org.snaker.engine.access.Page;
+import org.snaker.engine.helper.StringHelper;
+
+/**
+ * SQLServer数据库方言实现
+ * @author yuqs
+ * @since 1.0
+ */
+public class SQLServerDialect implements Dialect {
+    private static final String STR_ORDERBY = " order by ";
+
+	public String getPageSql(String sql, Page<?> page) {
+		int orderIdx = sql.indexOf(STR_ORDERBY);
+        String orderStr = null;
+		if(orderIdx != -1) {
+            orderStr = sql.substring(orderIdx + 10);
+			sql = sql.substring(0, orderIdx);
+		}
+		StringBuffer pageSql = new StringBuffer();
+		pageSql.append("select top ");
+		pageSql.append(page.getPageSize());
+		pageSql.append(" * from (select row_number() over (");
+		String orderBy = getOrderBy(sql, orderStr);
+		pageSql.append(orderBy);
+		pageSql.append(") row_number, * from (");
+		pageSql.append(sql);
+		int start = (page.getPageNo() - 1) * page.getPageSize();
+		pageSql.append(") aa ) a where row_number > ");
+		pageSql.append(start);
+		pageSql.append(" order by row_number");
+		return pageSql.toString();
+	}
+
+    public String getOrderBy(String sql, String orderBy) {
+        if(StringHelper.isEmpty(orderBy)) {
+            return STR_ORDERBY + " id desc ";
+        }
+        StringBuffer orderBuffer = new StringBuffer(30);
+        String[] orderByArray = StringUtils.split(orderBy, ',');
+        for (int i = 0; i < orderByArray.length; i++) {
+            String orderByItem = orderByArray[i].trim();
+            String orderByName = null;
+            String orderByDirect = "";
+            if(orderByItem.indexOf(" ") == -1) {
+                orderByName = orderByItem;
+            } else {
+                orderByName = orderByItem.substring(0, orderByItem.indexOf(" "));
+                orderByDirect = orderByItem.substring(orderByItem.indexOf(" ") + 1);
+            }
+            if(orderByName.indexOf(".") > -1) {
+                orderByName = orderByName.substring(orderByName.indexOf(".") + 1);
+            }
+            String columnAlias = orderByName + " as ";
+            int columnIndex = sql.indexOf(columnAlias);
+            if(columnIndex == -1) {
+                orderBuffer.append(orderByName).append(" ").append(orderByDirect).append(" ,");
+            } else {
+                String after = sql.substring(columnIndex + columnAlias.length());
+                String aliasName = null;
+                if(after.indexOf(",") != -1 && after.indexOf(" from") > after.indexOf(",")) {
+                    aliasName = after.substring(0, after.indexOf(","));
+                } else {
+                    aliasName = after.substring(0, after.indexOf(" "));
+                }
+                orderBuffer.append(aliasName).append(" ").append(orderByDirect).append(" ,");
+            }
+        }
+        orderBuffer.deleteCharAt(orderBuffer.length() - 1);
+        return STR_ORDERBY + orderBuffer.toString();
+    }
+}

+ 307 - 0
workflowy/src/main/java/org/snaker/engine/access/jdbc/BeanPropertyHandler.java

@@ -0,0 +1,307 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access.jdbc;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.math.BigDecimal;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.beanutils.BeanUtils;
+import org.apache.commons.dbutils.handlers.AbstractListHandler;
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.helper.ClassHelper;
+
+/**
+ * 该类主要解决数据库字段与类属性之间的转换存在下划线的情况(如:taskId->task_id)
+ * 该类主要参考Spring JDBC的rowmapper方式
+ * dbutils使用BeanHandler、BeanListHandler来处理返回集与bean的转换
+ * 这里统一使用BeanPropertyHandler,当返回单条记录时,使用JdbcHelper的requiredSingleResult做处理
+ * @author yuqs
+ * @since 1.0
+ * @param <T>
+ */
+public class BeanPropertyHandler<T> extends AbstractListHandler<T> {
+	/**
+	 * 需要映射的bean对象的class类型
+	 */
+	private Class<T> mappedClass;
+	/**
+	 * 映射的字段
+	 */
+	private Map<String, PropertyDescriptor> mappedFields;
+
+	/**
+	 * 构造函数,根据bean对象的class类型初始化mappedFields
+	 * @param mappedClass
+	 */
+	public BeanPropertyHandler(Class<T> mappedClass) {
+		initialize(mappedClass);
+	}
+
+	/**
+	 * ResultSet结果集处理
+	 */
+	protected T handleRow(ResultSet rs) throws SQLException {
+		/**
+		 * 根据bean的class类型实例化为对象
+		 */
+		T mappedObject = ClassHelper.instantiate(mappedClass);
+		ResultSetMetaData rsmd = rs.getMetaData();
+		int columnCount = rsmd.getColumnCount();
+		/**
+		 * 对ResultSet结果集字段进行循环
+		 */
+		for (int index = 1; index <= columnCount; index++) {
+			/**
+			 * 根据字段索引index获取字段名称
+			 */
+			String column = lookupColumnName(rsmd, index);
+			/**
+			 * 根据映射字段集合返回字段名称对应的属性描述符对象
+			 */
+			PropertyDescriptor pd = this.mappedFields.get(column.replaceAll(" ", "").toLowerCase());
+			if (pd != null) {
+				try {
+					/**
+					 * 根据字段index、属性类型返回字段值
+					 */
+					Object value = getResultSetValue(rs, index, pd.getPropertyType());
+					try {
+						/**
+						 * 使用apache-beanutils设置对象的属性
+						 */
+						BeanUtils.setProperty(mappedObject, pd.getName(), value);
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				} catch (Exception ex) {
+					ex.printStackTrace();
+				}
+			}
+		}
+		return mappedObject;
+	}
+
+	/**
+	 * 根据bean对象的class初始化字段映射集合
+	 * @param mappedClass
+	 */
+	protected void initialize(Class<T> mappedClass) {
+		this.mappedClass = mappedClass;
+		this.mappedFields = new HashMap<String, PropertyDescriptor>();
+		PropertyDescriptor[] pds = null;
+		try {
+			/**
+			 * 返回bean的属性描述对象数组
+			 */
+			pds = propertyDescriptors(mappedClass);
+		} catch (SQLException e) {
+			throw new SnakerException(e.getMessage(), e.getCause());
+		}
+		for (PropertyDescriptor pd : pds) {
+			if (pd.getWriteMethod() != null) {
+				this.mappedFields.put(pd.getName().toLowerCase(), pd);
+				String underscoredName = underscoreName(pd.getName());
+				if (!pd.getName().toLowerCase().equals(underscoredName)) {
+					this.mappedFields.put(underscoredName, pd);
+				}
+			}
+		}
+	}
+
+	/**
+	 * 属性名称转换为下划线,如taskId->task_id
+	 * @param name
+	 * @return
+	 */
+	private static String underscoreName(String name) {
+		StringBuilder result = new StringBuilder();
+		if (name != null && name.length() > 0) {
+			result.append(name.substring(0, 1).toLowerCase());
+			for (int i = 1; i < name.length(); i++) {
+				String s = name.substring(i, i + 1);
+				if (s.equals(s.toUpperCase())) {
+					result.append("_");
+					result.append(s.toLowerCase());
+				} else {
+					result.append(s);
+				}
+			}
+		}
+		return result.toString();
+	}
+
+	/**
+	 * 由Introspector返回指定类型的BeanInfo对象,再返回需要的属性描述对象数组PropertyDescriptor[]
+	 * @param c
+	 * @return PropertyDescriptor[]
+	 * @throws SQLException
+	 */
+	private PropertyDescriptor[] propertyDescriptors(Class<?> c)
+			throws SQLException {
+		BeanInfo beanInfo = null;
+		try {
+			beanInfo = Introspector.getBeanInfo(c);
+		} catch (IntrospectionException e) {
+			throw new SQLException("Bean introspection failed: "
+					+ e.getMessage());
+		}
+
+		return beanInfo.getPropertyDescriptors();
+	}
+	
+	/**
+	 * 根据元数据ResultSetMetaData、列索引columIndex获取列名称
+	 * @param resultSetMetaData
+	 * @param columnIndex
+	 * @return
+	 * @throws SQLException
+	 */
+	private String lookupColumnName(ResultSetMetaData resultSetMetaData, int columnIndex) throws SQLException {
+		String name = resultSetMetaData.getColumnLabel(columnIndex);
+		if (name == null || name.length() < 1) {
+			name = resultSetMetaData.getColumnName(columnIndex);
+		}
+		return name;
+	}/**
+	 * 根据ResultSet结果集、index列索引、字段类型requiredType获取指定类型的对象值
+	 * @param rs
+	 * @param index
+	 * @param requiredType
+	 * @return
+	 * @throws SQLException
+	 */
+	private Object getResultSetValue(ResultSet rs, int index, Class<?> requiredType) throws SQLException {
+		if (requiredType == null) {
+			return getResultSetValue(rs, index);
+		}
+
+		Object value = null;
+		boolean wasNullCheck = false;
+
+		if (String.class.equals(requiredType)) {
+			value = rs.getString(index);
+		}
+		else if (boolean.class.equals(requiredType) || Boolean.class.equals(requiredType)) {
+			value = rs.getBoolean(index);
+			wasNullCheck = true;
+		}
+		else if (byte.class.equals(requiredType) || Byte.class.equals(requiredType)) {
+			value = rs.getByte(index);
+			wasNullCheck = true;
+		}
+		else if (short.class.equals(requiredType) || Short.class.equals(requiredType)) {
+			value = rs.getShort(index);
+			wasNullCheck = true;
+		}
+		else if (int.class.equals(requiredType) || Integer.class.equals(requiredType)) {
+			value = rs.getInt(index);
+			wasNullCheck = true;
+		}
+		else if (long.class.equals(requiredType) || Long.class.equals(requiredType)) {
+			value = rs.getLong(index);
+			wasNullCheck = true;
+		}
+		else if (float.class.equals(requiredType) || Float.class.equals(requiredType)) {
+			value = rs.getFloat(index);
+			wasNullCheck = true;
+		}
+		else if (double.class.equals(requiredType) || Double.class.equals(requiredType) ||
+				Number.class.equals(requiredType)) {
+			value = rs.getDouble(index);
+			wasNullCheck = true;
+		}
+		else if (byte[].class.equals(requiredType)) {
+			value = rs.getBytes(index);
+		}
+		else if (java.sql.Date.class.equals(requiredType)) {
+			value = rs.getDate(index);
+		}
+		else if (java.sql.Time.class.equals(requiredType)) {
+			value = rs.getTime(index);
+		}
+		else if (java.sql.Timestamp.class.equals(requiredType) || java.util.Date.class.equals(requiredType)) {
+			value = rs.getTimestamp(index);
+		}
+		else if (BigDecimal.class.equals(requiredType)) {
+			value = rs.getBigDecimal(index);
+		}
+		else if (Blob.class.equals(requiredType)) {
+			value = rs.getBlob(index);
+		}
+		else if (Clob.class.equals(requiredType)) {
+			value = rs.getClob(index);
+		}
+		else {
+			value = getResultSetValue(rs, index);
+		}
+
+		if (wasNullCheck && value != null && rs.wasNull()) {
+			value = null;
+		}
+		return value;
+	}
+	
+	/**
+	 * 对于特殊字段类型做特殊处理
+	 * @param rs
+	 * @param index
+	 * @return
+	 * @throws SQLException
+	 */
+	private Object getResultSetValue(ResultSet rs, int index) throws SQLException {
+		Object obj = rs.getObject(index);
+		String className = null;
+		if (obj != null) {
+			className = obj.getClass().getName();
+		}
+		if (obj instanceof Blob) {
+			obj = rs.getBytes(index);
+		}
+		else if (obj instanceof Clob) {
+			obj = rs.getString(index);
+		}
+		else if (className != null &&
+				("oracle.sql.TIMESTAMP".equals(className) ||
+				"oracle.sql.TIMESTAMPTZ".equals(className))) {
+			obj = rs.getTimestamp(index);
+		}
+		else if (className != null && className.startsWith("oracle.sql.DATE")) {
+			String metaDataClassName = rs.getMetaData().getColumnClassName(index);
+			if ("java.sql.Timestamp".equals(metaDataClassName) ||
+					"oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
+				obj = rs.getTimestamp(index);
+			}
+			else {
+				obj = rs.getDate(index);
+			}
+		}
+		else if (obj != null && obj instanceof java.sql.Date) {
+			if ("java.sql.Timestamp".equals(rs.getMetaData().getColumnClassName(index))) {
+				obj = rs.getTimestamp(index);
+			}
+		}
+		return obj;
+	}
+}

+ 153 - 0
workflowy/src/main/java/org/snaker/engine/access/jdbc/SpringJdbcAccess.java

@@ -0,0 +1,153 @@
+/* Copyright 2012-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.access.jdbc;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.List;
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.DBAccess;
+import org.snaker.engine.access.AbstractDBAccess;
+import org.snaker.engine.entity.Process;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Primary;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.StatementCreatorUtils;
+import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback;
+import org.springframework.jdbc.support.lob.LobCreator;
+import org.springframework.jdbc.support.lob.LobHandler;
+import org.springframework.stereotype.Component;
+
+import com.alibaba.druid.support.spring.DruidLobHandler;
+
+/**
+ * Spring jdbc方式的数据库访问操作
+ * @author yuqs
+ * @since 1.0
+ */
+@Component
+@Primary
+public class SpringJdbcAccess extends AbstractDBAccess implements DBAccess {
+	private static final Logger log = LoggerFactory.getLogger(SpringJdbcAccess.class);
+	
+	private LobHandler lobHandler = new DruidLobHandler();
+	
+	@Autowired
+	private JdbcTemplate template;
+
+	public void saveProcess(final Process process) {
+		super.saveProcess(process);
+		if (process.getBytes() != null) {
+			template.execute(PROCESS_UPDATE_BLOB, new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
+
+				protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException, DataAccessException {
+					try {
+						lobCreator.setBlobAsBytes(ps, 1, process.getBytes());
+						StatementCreatorUtils.setParameterValue(ps, 2, Types.VARCHAR, process.getId());
+					} catch (SQLException e) {
+						e.printStackTrace();
+					}
+				}
+			});
+		}
+	}
+
+	public void updateProcess(final Process process) {
+		super.updateProcess(process);
+		if (process.getBytes() != null) {
+			template.execute(PROCESS_UPDATE_BLOB, new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
+
+				protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException, DataAccessException {
+					try {
+						lobCreator.setBlobAsBytes(ps, 1, process.getBytes());
+						StatementCreatorUtils.setParameterValue(ps, 2, Types.VARCHAR, process.getId());
+					} catch (SQLException e) {
+						e.printStackTrace();
+					}
+				}
+			});
+		}
+	}
+
+	public boolean isORM() {
+		return false;
+	}
+
+	public void saveOrUpdate(Map<String, Object> map) {
+		String sql = (String) map.get(KEY_SQL);
+		Object[] args = (Object[]) map.get(KEY_ARGS);
+		int[] type = (int[]) map.get(KEY_TYPE);
+		if (log.isDebugEnabled()) {
+			log.debug("增删改数据(Spring托管事务)=\n" + sql);
+		}
+		template.update(sql, args, type);
+	}
+
+	public Integer getLatestProcessVersion(String name) {
+		String where = " where name = ?";
+		Number number = template.queryForObject(QUERY_VERSION + where, new Object[] { name }, Integer.class);
+		return (number != null ? number.intValue() : -1);
+	}
+
+	public <T> T queryObject(Class<T> clazz, String sql, Object... args) {
+		if (log.isDebugEnabled()) {
+			log.debug("查询单条数据=\n" + sql);
+		}
+		try {
+			return (T) template.queryForObject(sql, args, new BeanPropertyRowMapper<T>(clazz));
+		} catch (Exception e) {
+			log.error("查询单条数据=\n" + e.getMessage());
+			return null;
+		}
+	}
+
+	public <T> List<T> queryList(Class<T> clazz, String sql, Object... args) {
+		if (log.isDebugEnabled()) {
+			log.debug("查询多条数据=\n" + sql);
+		}
+		return (List<T>) template.query(sql, args, new BeanPropertyRowMapper<T>(clazz));
+	}
+
+	public Object queryCount(String sql, Object... args) {
+		return template.queryForObject(sql, args, Long.class);
+	}
+
+	public void setLobHandler(LobHandler lobHandler) {
+		this.lobHandler = lobHandler;
+	}
+
+	public DataSource getDataSource() {
+		return (this.template != null ? this.template.getDataSource() : null);
+	}
+
+	public void setDataSource(DataSource dataSource) {
+		if (this.template == null || dataSource != this.template.getDataSource()) {
+			this.template = new JdbcTemplate(dataSource);
+		}
+	}
+
+	protected Connection getConnection() throws SQLException {
+		return getDataSource().getConnection();
+	}
+}

+ 53 - 0
workflowy/src/main/java/org/snaker/engine/cache/Cache.java

@@ -0,0 +1,53 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.cache;
+
+/**
+ * 缓存接口
+ * @author yuqs
+ * @since 1.3
+ */
+public interface Cache<K, V> {
+	/**
+	 * 根据key从缓存中获取对应的值
+	 * @param key
+	 * @return
+	 * @throws CacheException
+	 */
+	public V get(K key) throws CacheException;
+
+	/**
+	 * 添加缓存键值对
+	 * @param key
+	 * @param value
+	 * @return
+	 * @throws CacheException
+	 */
+    public V put(K key, V value) throws CacheException;
+
+    /**
+     * 根据key从缓存中删除对象
+     * @param key
+     * @return
+     * @throws CacheException
+     */
+    public V remove(K key) throws CacheException;
+
+    /**
+     * 从缓存中清除所有的对象
+     * @throws CacheException
+     */
+    public void clear() throws CacheException;
+}

+ 60 - 0
workflowy/src/main/java/org/snaker/engine/cache/CacheException.java

@@ -0,0 +1,60 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.cache;
+
+import org.snaker.engine.SnakerException;
+
+/**
+ * cache异常
+ * @author yuqs
+ * @since 1.3
+ */
+public class CacheException extends SnakerException {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -5329674600226403430L;
+	/**
+     * 创建cache异常
+     */
+    public CacheException() {
+        super();
+    }
+
+    /**
+     * 创建cache异常
+     * @param message
+     */
+    public CacheException(String message) {
+        super(message);
+    }
+
+    /**
+     * 创建cache异常
+     * @param cause
+     */
+    public CacheException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * 创建cache异常
+     * @param message
+     * @param cause
+     */
+    public CacheException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 36 - 0
workflowy/src/main/java/org/snaker/engine/cache/CacheManager.java

@@ -0,0 +1,36 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.cache;
+
+/**
+ * 缓存管理器接口,该接口提供具体的cache实现
+ * @author yuqs
+ * @since 1.3
+ */
+public interface CacheManager {
+	/**
+	 * 根据cache的名称获取cache。如果不存在,默认新建并返回
+	 * @param name
+	 * @return Cache
+	 * @throws CacheException
+	 */
+	public <K, V> Cache<K, V> getCache(String name) throws CacheException;
+
+    /**
+     * 销毁cache
+     * @throws CacheException
+     */
+    public void destroy() throws CacheException;
+}

+ 28 - 0
workflowy/src/main/java/org/snaker/engine/cache/CacheManagerAware.java

@@ -0,0 +1,28 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.cache;
+
+/**
+ * 该接口的实现类,需要设置cache管理器
+ * @author yuqs
+ * @since 1.3
+ */
+public interface CacheManagerAware {
+	/**
+	 * 设置cache管理器
+	 * @param cacheManager
+	 */
+	void setCacheManager(CacheManager cacheManager);
+}

+ 57 - 0
workflowy/src/main/java/org/snaker/engine/cache/memory/MemoryCache.java

@@ -0,0 +1,57 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.cache.memory;
+
+import java.util.Map;
+
+import org.snaker.engine.cache.Cache;
+import org.snaker.engine.cache.CacheException;
+import org.snaker.engine.helper.AssertHelper;
+
+/**
+ * 基于内存管理cache
+ * @author yuqs
+ * @since 1.3
+ */
+public class MemoryCache<K, V> implements Cache<K, V> {
+	/**
+	 * map cache
+	 */
+	private final Map<K, V> map;
+	/**
+	 * 通过Map实现类构造MemoryCache
+	 * @param backingMap
+	 */
+	public MemoryCache(Map<K, V> backingMap) {
+		AssertHelper.notNull(backingMap);
+		this.map = backingMap;
+	}
+	
+	public V get(K key) throws CacheException {
+		return map.get(key);
+	}
+
+	public V put(K key, V value) throws CacheException {
+		return map.put(key, value);
+	}
+
+	public V remove(K key) throws CacheException {
+		return map.remove(key);
+	}
+
+	public void clear() throws CacheException {
+		map.clear();
+	}
+}

+ 60 - 0
workflowy/src/main/java/org/snaker/engine/cache/memory/MemoryCacheManager.java

@@ -0,0 +1,60 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.cache.memory;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.snaker.engine.cache.Cache;
+import org.snaker.engine.cache.CacheException;
+import org.snaker.engine.cache.CacheManager;
+import org.snaker.engine.helper.StringHelper;
+import org.springframework.stereotype.Component;
+
+/**
+ * 基于虚拟机内存的cache管理器
+ * @author yuqs
+ * @since 1.3
+ */
+@Component
+public class MemoryCacheManager implements CacheManager {
+	private final ConcurrentMap<String, Cache> caches;
+	
+	public MemoryCacheManager() {
+		this.caches = new ConcurrentHashMap<String, Cache>();
+	}
+	public <K, V> Cache<K, V> getCache(String name) throws CacheException {
+		if(StringHelper.isEmpty(name)) {
+			throw new IllegalArgumentException("Cache名称不能为空.");
+		}
+        Cache cache;
+
+        cache = caches.get(name);
+        if (cache == null) {
+            cache = new MemoryCache<Object, Object>(new ConcurrentHashMap<Object, Object>());
+            Cache existing = caches.putIfAbsent(name, cache);
+            if (existing != null) {
+                cache = existing;
+            }
+        }
+        return cache;
+	}
+
+    public void destroy() throws CacheException {
+        while (!caches.isEmpty()) {
+            caches.clear();
+        }
+    }
+}

+ 82 - 0
workflowy/src/main/java/org/snaker/engine/core/AccessService.java

@@ -0,0 +1,82 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.core;
+
+import org.snaker.engine.Completion;
+import org.snaker.engine.DBAccess;
+import org.snaker.engine.impl.GeneralCompletion;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 作为抽象父类,提供给子类access实现方式
+ * @author yuqs
+ * @since 1.0
+ */
+public abstract class AccessService {
+	/**
+	 * 状态;活动状态
+	 */
+	public static final Integer STATE_ACTIVE = 1;
+	/**
+	 * 状态:结束状态
+	 */
+	public static final Integer STATE_FINISH = 0;
+	/**
+	 * 状态:终止状态
+	 */
+	public static final Integer STATE_TERMINATION = 2;
+	/**
+	 * 数据库的access
+	 */
+	@Autowired
+	protected DBAccess access;
+    /**
+     * 完成触发接口
+     */
+    private Completion completion = null;
+	/**
+	 * 获取DBAccess,供子类使用
+	 */
+	public DBAccess access() {
+		return access;
+	}
+	/**
+	 * setter
+	 * @param access 访问对象
+	 */
+	public void setAccess(DBAccess access) {
+		this.access = access; 
+	}
+
+    /**
+     * setter
+     * @param completion 完成对象
+     */
+    public void setCompletion(Completion completion) {
+        this.completion = completion;
+    }
+    public Completion getCompletion() {
+        if(completion != null) {
+            return completion;
+        }
+
+        completion = ServiceContext.find(Completion.class);
+        if(completion == null) {
+            ServiceContext.put(Completion.class.getName(), GeneralCompletion.class);
+            completion = ServiceContext.find(Completion.class);
+        }
+        return completion;
+    }
+}

+ 257 - 0
workflowy/src/main/java/org/snaker/engine/core/Execution.java

@@ -0,0 +1,257 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.core;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.snaker.engine.SnakerEngine;
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Process;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.model.ProcessModel;
+
+/**
+ * 流程执行过程中所传递的执行对象,其中包含流程定义、流程模型、流程实例对象、执行参数、返回的任务列表
+ * @author yuqs
+ * @since 1.0
+ */
+public class Execution implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 3730741790729624400L;
+	/**
+	 * SnakerEngine holder
+	 */
+	private SnakerEngine engine;
+	/**
+	 * 流程定义对象
+	 */
+	private Process process;
+	/**
+	 * 流程实例对象
+	 */
+	private Order order;
+	/**
+	 * 父流程实例
+	 */
+	private Order parentOrder;
+	/**
+	 * 父流程实例节点名称
+	 */
+	private String parentNodeName;
+	/**
+	 * 子流程实例节点名称
+	 */
+	private String childOrderId;
+	/**
+	 * 执行参数
+	 */
+	private Map<String, Object> args;
+	/**
+	 * 操作人
+	 */
+	private String operator;
+	/**
+	 * 任务
+	 */
+	private Task task;
+	/**
+	 * 返回的任务列表
+	 */
+	private List<Task> tasks = new ArrayList<Task>();
+	/**
+	 * 是否已合并
+	 * 针对join节点的处理
+	 */
+	private boolean isMerged = false;
+	
+	/**
+	 * 用于产生子流程执行对象使用
+	 * @param execution
+	 * @param process
+	 * @param parentNodeName
+	 */
+	Execution(Execution execution, Process process, String parentNodeName) {
+		if(execution == null || process == null || parentNodeName == null) {
+			throw new SnakerException("构造Execution对象失败,请检查execution、process、parentNodeName是否为空");
+		}
+		this.engine = execution.getEngine();
+		this.process = process;
+		this.args = execution.getArgs();
+		this.parentOrder = execution.getOrder();
+		this.parentNodeName = parentNodeName;
+		this.operator = execution.getOperator();
+	}
+	
+	/**
+	 * 构造函数,接收流程定义、流程实例对象、执行参数
+	 * @param process
+	 * @param order
+	 * @param args
+	 */
+	public Execution(SnakerEngine engine, Process process, Order order, Map<String, Object> args) {
+		if(process == null || order == null) {
+			throw new SnakerException("构造Execution对象失败,请检查process、order是否为空");
+		}
+		this.engine = engine;
+		this.process = process;
+		this.order = order;
+		this.args = args;
+	}
+	
+	/**
+	 * 根据当前执行对象execution、子流程定义process、当前节点名称产生子流程的执行对象
+	 * @param execution
+	 * @param process
+	 * @param parentNodeName
+	 * @return
+	 */
+	public Execution createSubExecution(Execution execution, Process process, String parentNodeName) {
+		return new Execution(execution, process, parentNodeName);
+	}
+	
+	/**
+	 * 获取流程定义对象
+	 * @return
+	 */
+	public Process getProcess() {
+		return process;
+	}
+	
+	/**
+	 * 获取流程模型对象
+	 * @return
+	 */
+	public ProcessModel getModel() {
+		return process.getModel();
+	}
+	
+	/**
+	 * 获取流程实例对象
+	 * @return
+	 */
+	public Order getOrder() {
+		return order;
+	}
+	
+	/**
+	 * 获取执行参数
+	 * @return
+	 */
+	public Map<String, Object> getArgs() {
+		return args;
+	}
+	
+	/**
+	 * 返回任务结果集
+	 * @return
+	 */
+	public List<Task> getTasks() {
+		return tasks;
+	}
+	
+	/**
+	 * 添加任务集合
+	 * @param tasks
+	 */
+	public void addTasks(List<Task> tasks) {
+		this.tasks.addAll(tasks);
+	}
+	
+	/**
+	 * 添加任务
+	 * @param task
+	 */
+	public void addTask(Task task) {
+		this.tasks.add(task);
+	}
+
+	/**
+	 * 返回当前操作人ID
+	 * @return
+	 */
+	public String getOperator() {
+		return operator;
+	}
+
+	/**
+	 * 设置当前操作人ID
+	 * @param operator
+	 */
+	public void setOperator(String operator) {
+		this.operator = operator;
+	}
+
+	/**
+	 * 返回任务
+	 * @return
+	 */
+	public Task getTask() {
+		return task;
+	}
+
+	/**
+	 * 设置任务
+	 * @param task
+	 */
+	public void setTask(Task task) {
+		this.task = task;
+	}
+
+	/**
+	 * 判断是否已经成功合并
+	 * @return
+	 */
+	public boolean isMerged() {
+		return isMerged;
+	}
+
+	/**
+	 * 设置是否为已合并
+	 * @param isMerged
+	 */
+	public void setMerged(boolean isMerged) {
+		this.isMerged = isMerged;
+	}
+
+	/**
+	 * 获取引擎
+	 * @return
+	 */
+	public SnakerEngine getEngine() {
+		return engine;
+	}
+
+	public Order getParentOrder() {
+		return parentOrder;
+	}
+
+	public String getParentNodeName() {
+		return parentNodeName;
+	}
+
+	public String getChildOrderId() {
+		return childOrderId;
+	}
+
+	public void setChildOrderId(String childOrderId) {
+		this.childOrderId = childOrderId;
+	}
+}

+ 87 - 0
workflowy/src/main/java/org/snaker/engine/core/ManagerService.java

@@ -0,0 +1,87 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.core;
+
+import java.util.List;
+
+import org.snaker.engine.IManagerService;
+import org.snaker.engine.access.Page;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.entity.Surrogate;
+import org.snaker.engine.helper.AssertHelper;
+import org.snaker.engine.helper.DateHelper;
+import org.snaker.engine.helper.StringHelper;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 管理服务类
+ * @author yuqs
+ * @since 1.4
+ */
+@Component
+public class ManagerService extends AccessService implements IManagerService {
+	public void saveOrUpdate(Surrogate surrogate) {
+		AssertHelper.notNull(surrogate);
+		surrogate.setState(STATE_ACTIVE);
+		if(StringHelper.isEmpty(surrogate.getId())) {
+			surrogate.setId(StringHelper.getPrimaryKey());
+			access().saveSurrogate(surrogate);
+		} else {
+			access().updateSurrogate(surrogate);
+		}
+	}
+
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void deleteSurrogate(String id) {
+		Surrogate surrogate = getSurrogate(id);
+		AssertHelper.notNull(surrogate);
+		access().deleteSurrogate(surrogate);
+	}
+
+	public Surrogate getSurrogate(String id) {
+		return access().getSurrogate(id);
+	}
+	
+	public List<Surrogate> getSurrogate(QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getSurrogate(null, filter);
+	}
+
+	public List<Surrogate> getSurrogate(Page<Surrogate> page, QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getSurrogate(page, filter);
+	}
+	
+	public String getSurrogate(String operator, String processName) {
+		AssertHelper.notEmpty(operator);
+		QueryFilter filter = new QueryFilter().
+				setOperator(operator).
+				setOperateTime(DateHelper.getTime());
+		if(StringHelper.isNotEmpty(processName)) {
+			filter.setName(processName);
+		}
+		List<Surrogate> surrogates = getSurrogate(filter);
+		if(surrogates == null || surrogates.isEmpty()) return operator;
+		StringBuffer buffer = new StringBuffer(50);
+		for(Surrogate surrogate : surrogates) {
+			String result = getSurrogate(surrogate.getSurrogate(), processName);
+			buffer.append(result).append(",");
+		}
+		buffer.deleteCharAt(buffer.length() - 1);
+		return buffer.toString();
+	}
+}

+ 267 - 0
workflowy/src/main/java/org/snaker/engine/core/OrderService.java

@@ -0,0 +1,267 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.core;
+
+import java.util.List;
+import java.util.Map;
+
+import org.snaker.engine.Completion;
+import org.snaker.engine.IOrderService;
+import org.snaker.engine.SnakerEngine;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.entity.*;
+import org.snaker.engine.entity.Process;
+import org.snaker.engine.helper.AssertHelper;
+import org.snaker.engine.helper.DateHelper;
+import org.snaker.engine.helper.JsonHelper;
+import org.snaker.engine.helper.StringHelper;
+import org.snaker.engine.model.ProcessModel;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 流程实例业务类
+ * @author yuqs
+ * @since 1.0
+ */
+@Component
+public class OrderService extends AccessService implements IOrderService {
+	
+	/**
+	 * 创建活动实例
+	 * @see org.snaker.engine.core.OrderService#createOrder(Process, String, Map, String, String)
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Order createOrder(Process process, String operator, Map<String, Object> args) {
+		return createOrder(process, operator, args, null, null);
+	}
+	
+	/**
+	 * 创建活动实例
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Order createOrder(Process process, String operator, Map<String, Object> args, 
+			String parentId, String parentNodeName) {
+		Order order = new Order();
+		order.setId(StringHelper.getPrimaryKey());
+		order.setParentId(parentId);
+		order.setParentNodeName(parentNodeName);
+		order.setCreateTime(DateHelper.getTime());
+		order.setLastUpdateTime(order.getCreateTime());
+		order.setCreator(operator);
+		order.setLastUpdator(order.getCreator());
+		order.setProcessId(process.getId());
+		ProcessModel model = process.getModel();
+		if(model != null && args != null) {
+			if(StringHelper.isNotEmpty(model.getExpireTime())) {
+				String expireTime = DateHelper.parseTime(args.get(model.getExpireTime()));
+				order.setExpireTime(expireTime);
+			}
+            String orderNo = (String)args.get(SnakerEngine.ID);
+            if(StringHelper.isNotEmpty(orderNo)) {
+                order.setOrderNo(orderNo);
+            } else {
+                order.setOrderNo(model.getGenerator().generate(model));
+            }
+		}
+
+		order.setVariable(JsonHelper.toJson(args));
+		saveOrder(order);
+		return order;
+	}
+
+    /**
+     * 向活动实例临时添加全局变量数据
+     * @param orderId 实例id
+     * @param args 变量数据
+     */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+    public void addVariable(String orderId, Map<String, Object> args) {
+        Order order = access().getOrder(orderId);
+        Map<String, Object> data = order.getVariableMap();
+        data.putAll(args);
+        order.setVariable(JsonHelper.toJson(data));
+        access().updateOrderVariable(order);
+    }
+
+    /**
+	 * 创建实例的抄送
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void createCCOrder(String orderId, String creator, String... actorIds) {
+		for(String actorId : actorIds) {
+			CCOrder ccorder = new CCOrder();
+			ccorder.setOrderId(orderId);
+			ccorder.setActorId(actorId);
+            ccorder.setCreator(creator);
+			ccorder.setStatus(STATE_ACTIVE);
+            ccorder.setCreateTime(DateHelper.getTime());
+			access().saveCCOrder(ccorder);
+		}
+	}
+	
+	/**
+	 * 流程实例数据会保存至活动实例表、历史实例表
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void saveOrder(Order order) {
+		HistoryOrder history = new HistoryOrder(order);
+		history.setOrderState(STATE_ACTIVE);
+		access().saveOrder(order);
+		access().saveHistory(history);
+	}
+	
+	/**
+	 * 更新活动实例的last_Updator、last_Update_Time、expire_Time、version、variable
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void updateOrder(Order order) {
+		access().updateOrder(order);
+	}
+	
+	/**
+	 * 更新抄送记录状态为已阅
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void updateCCStatus(String orderId, String... actorIds) {
+        List<CCOrder> ccorders = access().getCCOrder(orderId, actorIds);
+        AssertHelper.notNull(ccorders);
+        for(CCOrder ccorder : ccorders) {
+            ccorder.setStatus(STATE_FINISH);
+            ccorder.setFinishTime(DateHelper.getTime());
+            access().updateCCOrder(ccorder);
+        }
+	}
+	
+	/**
+	 * 删除指定的抄送记录
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void deleteCCOrder(String orderId, String actorId) {
+        List<CCOrder> ccorders = access().getCCOrder(orderId, actorId);
+		AssertHelper.notNull(ccorders);
+        for(CCOrder ccorder : ccorders) {
+		    access().deleteCCOrder(ccorder);
+        }
+	}
+
+	/**
+	 * 删除活动流程实例数据,更新历史流程实例的状态、结束时间
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void complete(String orderId) {
+		Order order = access().getOrder(orderId);
+		HistoryOrder history = access().getHistOrder(orderId);
+		history.setOrderState(STATE_FINISH);
+		history.setEndTime(DateHelper.getTime());
+		
+		access().updateHistory(history);
+		access().deleteOrder(order);
+        Completion completion = getCompletion();
+        if(completion != null) {
+            completion.complete(history);
+        }
+	}
+	
+	/**
+	 * 强制中止流程实例
+	 * @see org.snaker.engine.core.OrderService#terminate(String, String)
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void terminate(String orderId) {
+		terminate(orderId, null);
+	}
+
+	/**
+	 * 强制中止活动实例,并强制完成活动任务
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void terminate(String orderId, String operator) {
+		SnakerEngine engine = ServiceContext.getEngine();
+		List<Task> tasks = engine
+				.query()
+				.getActiveTasks(new QueryFilter().setOrderId(orderId));
+		for(Task task : tasks) {
+			engine.task().complete(task.getId(), operator);
+		}
+		Order order = access().getOrder(orderId);
+		HistoryOrder history = new HistoryOrder(order);
+		history.setOrderState(STATE_TERMINATION);
+		history.setEndTime(DateHelper.getTime());
+		
+		access().updateHistory(history);
+		access().deleteOrder(order);
+        Completion completion = getCompletion();
+        if(completion != null) {
+            completion.complete(history);
+        }
+	}
+
+    /**
+     * 激活已完成的历史流程实例
+     * @param orderId 实例id
+     * @return 活动实例对象
+     */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+    public Order resume(String orderId) {
+        HistoryOrder historyOrder = access().getHistOrder(orderId);
+        Order order = historyOrder.undo();
+        access().saveOrder(order);
+        historyOrder.setOrderState(STATE_ACTIVE);
+        access().updateHistory(historyOrder);
+
+        SnakerEngine engine = ServiceContext.getEngine();
+        List<HistoryTask> histTasks = access().getHistoryTasks(null,
+                new QueryFilter().setOrderId(orderId));
+        if(histTasks != null && !histTasks.isEmpty()) {
+            HistoryTask histTask = histTasks.get(0);
+            engine.task().resume(histTask.getId(), histTask.getOperator());
+        }
+        return order;
+    }
+
+	/**
+	 * 级联删除指定流程实例的所有数据:
+	 * 1.wf_order,wf_hist_order
+	 * 2.wf_task,wf_hist_task
+	 * 3.wf_task_actor,wf_hist_task_actor
+	 * 4.wf_cc_order
+	 * @param id 实例id
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void cascadeRemove(String id) {
+		HistoryOrder historyOrder = access().getHistOrder(id);
+		AssertHelper.notNull(historyOrder);
+		List<Task> activeTasks = access().getActiveTasks(null, new QueryFilter().setOrderId(id));
+		List<HistoryTask> historyTasks = access().getHistoryTasks(null, new QueryFilter().setOrderId(id));
+		for(Task task : activeTasks) {
+			access().deleteTask(task);
+		}
+		for(HistoryTask historyTask : historyTasks) {
+			access().deleteHistoryTask(historyTask);
+		}
+		List<CCOrder> ccOrders = access().getCCOrder(id);
+		for(CCOrder ccOrder : ccOrders) {
+			access().deleteCCOrder(ccOrder);
+		}
+
+		Order order = access().getOrder(id);
+		access().deleteHistoryOrder(historyOrder);
+		if(order != null) {
+			access().deleteOrder(order);
+		}
+	}
+}

+ 363 - 0
workflowy/src/main/java/org/snaker/engine/core/ProcessService.java

@@ -0,0 +1,363 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.core;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.IProcessService;
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.access.Page;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.cache.Cache;
+import org.snaker.engine.cache.CacheManager;
+import org.snaker.engine.cache.CacheManagerAware;
+import org.snaker.engine.entity.HistoryOrder;
+import org.snaker.engine.entity.Process;
+import org.snaker.engine.helper.AssertHelper;
+import org.snaker.engine.helper.DateHelper;
+import org.snaker.engine.helper.StreamHelper;
+import org.snaker.engine.helper.StringHelper;
+import org.snaker.engine.model.ProcessModel;
+import org.snaker.engine.parser.ModelParser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 流程定义业务类
+ * @author yuqs
+ * @since 1.0
+ */
+@Component
+public class ProcessService extends AccessService implements IProcessService, CacheManagerAware {
+	private static final Logger log = LoggerFactory.getLogger(ProcessService.class);
+	private static final String DEFAULT_SEPARATOR = ".";
+	/**
+	 * 流程定义对象cache名称
+	 */
+	private static final String CACHE_ENTITY = "snaker.process.entity";
+	/**
+	 * 流程id、name的cache名称
+	 */
+	private static final String CACHE_NAME = "snaker.process.name";
+	/**
+	 * cache manager
+	 */
+	@Autowired
+	private CacheManager cacheManager;
+	/**
+	 * 实体cache(key=name,value=entity对象)
+	 */
+	private Cache<String, Process> entityCache;
+	/**
+	 * 名称cache(key=id,value=name对象)
+	 */
+	private Cache<String, String> nameCache;
+	
+	public void check(Process process, String idOrName) {
+		AssertHelper.notNull(process, "指定的流程定义[id/name=" + idOrName + "]不存在");
+		if(process.getState() != null && process.getState() == 0) {
+			throw new IllegalArgumentException("指定的流程定义[id/name=" + idOrName + 
+					",version=" + process.getVersion() + "]为非活动状态");
+		}
+	}
+	
+	/**
+	 * 保存process实体对象
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void saveProcess(Process process) {
+		access().saveProcess(process);
+	}
+	
+	/**
+	 * 更新process的类别
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void updateType(String id, String type) {
+        Process entity = getProcessById(id);
+        entity.setType(type);
+		access().updateProcessType(id, type);
+        cache(entity);
+	}
+	
+	/**
+	 * 根据id获取process对象
+	 * 先通过cache获取,如果返回空,就从数据库读取并put
+	 */
+	public Process getProcessById(String id) {
+		AssertHelper.notEmpty(id);
+		Process entity = null;
+		String processName;
+		Cache<String, String> nameCache = ensureAvailableNameCache();
+		Cache<String, Process> entityCache = ensureAvailableEntityCache();
+		if(nameCache != null && entityCache != null) {
+			processName = nameCache.get(id);
+			if(StringHelper.isNotEmpty(processName)) {
+				entity = entityCache.get(processName);
+			}
+		}
+		if(entity != null) {
+			if(log.isDebugEnabled()) {
+				log.debug("obtain process[id={}] from cache.", id);
+			}
+			return entity;
+		}
+		entity = access().getProcess(id);
+		if(entity != null) {
+			if(log.isDebugEnabled()) {
+				log.debug("obtain process[id={}] from database.", id);
+			}
+			cache(entity);
+		}
+		return entity;
+	}
+	
+	/**
+	 * 根据name获取process对象
+	 * 先通过cache获取,如果返回空,就从数据库读取并put
+	 */
+	public Process getProcessByName(String name) {
+		return getProcessByVersion(name, null);
+	}
+
+	/**
+	 * 根据name获取process对象
+	 * 先通过cache获取,如果返回空,就从数据库读取并put
+	 */
+	public Process getProcessByVersion(String name, Integer version) {
+		AssertHelper.notEmpty(name);
+		if(version == null) {
+			version = access().getLatestProcessVersion(name);
+		}
+		if(version == null) {
+			version = 0;
+		}
+		Process entity = null;
+		String processName = name + DEFAULT_SEPARATOR + version;
+		Cache<String, Process> entityCache = ensureAvailableEntityCache();
+		if(entityCache != null) {
+			entity = entityCache.get(processName);
+		}
+		if(entity != null) {
+			if(log.isDebugEnabled()) {
+				log.debug("obtain process[name={}] from cache.", processName);
+			}
+			return entity;
+		}
+
+		List<Process> processs = access().getProcesss(null, new QueryFilter().setName(name).setVersion(version));
+		if(processs != null && !processs.isEmpty()) {
+			if(log.isDebugEnabled()) {
+				log.debug("obtain process[name={}] from database.", processName);
+			}
+			entity = processs.get(0);
+			cache(entity);
+		}
+		return entity;
+	}
+	
+	/**
+	 * 根据流程定义xml的输入流解析为字节数组,保存至数据库中,并且put到缓存中
+	 * @param input 定义输入流
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public String deploy(InputStream input) {
+		return deploy(input, null);
+	}
+	
+	/**
+	 * 根据流程定义xml的输入流解析为字节数组,保存至数据库中,并且put到缓存中
+	 * @param input 定义输入流
+	 * @param creator 创建人
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public String deploy(InputStream input, String creator) {
+		AssertHelper.notNull(input);
+		try {
+			byte[] bytes = StreamHelper.readBytes(input);
+			ProcessModel model = ModelParser.parse(bytes);
+			Integer version = access().getLatestProcessVersion(model.getName());
+			Process entity = new Process();
+			entity.setId(StringHelper.getPrimaryKey());
+			if(version == null || version < 0) {
+				entity.setVersion(0);
+			} else {
+				entity.setVersion(version + 1);
+			}
+			entity.setState(STATE_ACTIVE);
+			entity.setModel(model);
+			entity.setBytes(bytes);
+			entity.setCreateTime(DateHelper.getTime());
+			entity.setCreator(creator);
+			saveProcess(entity);
+			cache(entity);
+			return entity.getId();
+		} catch (Exception e) {
+			log.error("流程定义部署失败", e);
+			throw new SnakerException(e.getMessage(), e.getCause());
+		}
+	}
+	
+	/**
+	 * 根据流程定义id、xml的输入流解析为字节数组,保存至数据库中,并且重新put到缓存中
+	 * @param input 定义输入流
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void redeploy(String id, InputStream input) {
+		AssertHelper.notNull(input);
+		Process entity = access().getProcess(id);
+		AssertHelper.notNull(entity);
+		try {
+			byte[] bytes = StreamHelper.readBytes(input);
+			ProcessModel model = ModelParser.parse(bytes);
+			String oldProcessName = entity.getName();
+			entity.setModel(model);
+			entity.setBytes(bytes);
+			access().updateProcess(entity);
+			if(!oldProcessName.equalsIgnoreCase(entity.getName())) {
+				Cache<String, Process> entityCache = ensureAvailableEntityCache();
+				if(entityCache != null) {
+					entityCache.remove(oldProcessName + DEFAULT_SEPARATOR + entity.getVersion());
+				}
+			}
+			cache(entity);
+		} catch(Exception e) {
+			e.printStackTrace();
+			log.error(e.getMessage());
+			throw new SnakerException(e.getMessage(), e.getCause());
+		}
+	}
+
+	/**
+	 * 根据processId卸载流程
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void undeploy(String id) {
+		Process entity = access().getProcess(id);
+		entity.setState(STATE_FINISH);
+		access().updateProcess(entity);
+		cache(entity);
+	}
+	
+	/**
+	 * 级联删除指定流程定义的所有数据
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void cascadeRemove(String id) {
+		Process entity = access().getProcess(id);
+		List<HistoryOrder> historyOrders = access().getHistoryOrders(null, new QueryFilter().setProcessId(id));
+
+		for(HistoryOrder historyOrder : historyOrders) {
+			ServiceContext.getEngine().order().cascadeRemove(historyOrder.getId());
+		}
+		access().deleteProcess(entity);
+		clear(entity);
+	}
+
+	/**
+	 * 查询流程定义
+	 */
+	public List<Process> getProcesss(QueryFilter filter) {
+		if(filter == null) filter = new QueryFilter();
+		return access().getProcesss(null, filter);
+	}
+	
+	/**
+	 * 分页查询流程定义
+	 */
+	public List<Process> getProcesss(Page<Process> page, QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getProcesss(page, filter);
+	}
+	
+	/**
+	 * 缓存实体
+	 * @param entity 流程定义对象
+	 */
+	private void cache(Process entity) {
+		Cache<String, String> nameCache = ensureAvailableNameCache();
+		Cache<String, Process> entityCache = ensureAvailableEntityCache();
+		if(entity.getModel() == null && entity.getDBContent() != null) {
+			entity.setModel(ModelParser.parse(entity.getDBContent()));
+		}
+		String processName = entity.getName() + DEFAULT_SEPARATOR + entity.getVersion();
+		if(nameCache != null && entityCache != null) {
+			if(log.isDebugEnabled()) {
+				log.debug("cache process id is[{}],name is[{}]", entity.getId(), processName);
+			}
+			entityCache.put(processName, entity);
+			nameCache.put(entity.getId(), processName);
+		} else {
+			if(log.isDebugEnabled()) {
+				log.debug("no cache implementation class");
+			}
+		}
+	}
+
+	/**
+	 * 清除实体
+	 * @param entity 流程定义对象
+	 */
+	private void clear(Process entity) {
+		Cache<String, String> nameCache = ensureAvailableNameCache();
+		Cache<String, Process> entityCache = ensureAvailableEntityCache();
+		String processName = entity.getName() + DEFAULT_SEPARATOR + entity.getVersion();
+		if(nameCache != null && entityCache != null) {
+			nameCache.remove(entity.getId());
+			entityCache.remove(processName);
+		}
+	}
+
+	public void setCacheManager(CacheManager cacheManager) {
+		this.cacheManager = cacheManager;
+	}
+	
+    private Cache<String, Process> ensureAvailableEntityCache() {
+        Cache<String, Process> entityCache = ensureEntityCache();
+		if(entityCache == null && this.cacheManager != null) {
+			entityCache = this.cacheManager.getCache(CACHE_ENTITY);
+		}
+        return entityCache;
+    }
+    
+    private Cache<String, String> ensureAvailableNameCache() {
+        Cache<String, String> nameCache = ensureNameCache();
+		if(nameCache == null && this.cacheManager != null) {
+			nameCache = this.cacheManager.getCache(CACHE_NAME);
+		}
+        return nameCache;
+    }
+
+	public Cache<String, Process> ensureEntityCache() {
+		return entityCache;
+	}
+
+	public void setEntityCache(Cache<String, Process> entityCache) {
+		this.entityCache = entityCache;
+	}
+
+	public Cache<String, String> ensureNameCache() {
+		return nameCache;
+	}
+
+	public void setNameCache(Cache<String, String> nameCache) {
+		this.nameCache = nameCache;
+	}
+}

+ 150 - 0
workflowy/src/main/java/org/snaker/engine/core/QueryService.java

@@ -0,0 +1,150 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.core;
+
+import java.util.List;
+
+import org.snaker.engine.IQueryService;
+import org.snaker.engine.access.Page;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.entity.HistoryOrder;
+import org.snaker.engine.entity.HistoryTask;
+import org.snaker.engine.entity.HistoryTaskActor;
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.entity.TaskActor;
+import org.snaker.engine.entity.WorkItem;
+import org.snaker.engine.helper.AssertHelper;
+import org.springframework.stereotype.Component;
+
+/**
+ * 查询服务实现类
+ * @author yuqs
+ * @since 1.0
+ */
+@Component
+public class QueryService extends AccessService implements IQueryService {
+	public Order getOrder(String orderId) {
+		return access().getOrder(orderId);
+	}
+	
+	public Task getTask(String taskId) {
+		return access().getTask(taskId);
+	}
+	
+	public String[] getTaskActorsByTaskId(String taskId) {
+		List<TaskActor> actors = access().getTaskActorsByTaskId(taskId);
+		if(actors == null || actors.isEmpty()) return null;
+		String[] actorIds = new String[actors.size()];
+		for(int i = 0; i < actors.size(); i++) {
+			TaskActor ta = actors.get(i);
+			actorIds[i] = ta.getActorId();
+		}
+		return actorIds;
+	}
+	
+	public String[] getHistoryTaskActorsByTaskId(String taskId) {
+		List<HistoryTaskActor> actors = access().getHistTaskActorsByTaskId(taskId);
+		if(actors == null || actors.isEmpty()) return null;
+		String[] actorIds = new String[actors.size()];
+		for(int i = 0; i < actors.size(); i++) {
+			HistoryTaskActor ta = actors.get(i);
+			actorIds[i] = ta.getActorId();
+		}
+		return actorIds;
+	}
+
+	public HistoryOrder getHistOrder(String orderId) {
+		return access().getHistOrder(orderId);
+	}
+
+	public HistoryTask getHistTask(String taskId) {
+		return access().getHistTask(taskId);
+	}
+	
+	public List<Task> getActiveTasks(QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getActiveTasks(null, filter);
+	}
+	
+	public List<Task> getActiveTasks(Page<Task> page, QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getActiveTasks(page, filter);
+	}
+	
+	public List<Order> getActiveOrders(QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getActiveOrders(null, filter);
+	}
+	
+	public List<Order> getActiveOrders(Page<Order> page, QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getActiveOrders(page, filter);
+	}
+	
+	public List<HistoryOrder> getHistoryOrders(QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getHistoryOrders(null, filter);
+	}
+
+	public List<HistoryOrder> getHistoryOrders(Page<HistoryOrder> page, QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getHistoryOrders(page, filter);
+	}
+
+	public List<HistoryTask> getHistoryTasks(QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getHistoryTasks(null, filter);
+	}
+
+	public List<HistoryTask> getHistoryTasks(Page<HistoryTask> page, QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getHistoryTasks(page, filter);
+	}
+	
+	public List<WorkItem> getWorkItems(Page<WorkItem> page, QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getWorkItems(page, filter);
+	}
+	
+	public List<HistoryOrder> getCCWorks(Page<HistoryOrder> page, QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getCCWorks(page, filter);
+	}
+
+	public List<WorkItem> getHistoryWorkItems(Page<WorkItem> page, QueryFilter filter) {
+		AssertHelper.notNull(filter);
+		return access().getHistoryWorkItems(page, filter);
+	}
+
+	public <T> T nativeQueryObject(Class<T> T, String sql, Object... args) {
+		AssertHelper.notEmpty(sql);
+		AssertHelper.notNull(T);
+		return access().queryObject(T, sql, args);
+	}
+
+	public <T> List<T> nativeQueryList(Class<T> T, String sql, Object... args) {
+		AssertHelper.notEmpty(sql);
+		AssertHelper.notNull(T);
+		return access().queryList(T, sql, args);
+	}
+
+	public <T> List<T> nativeQueryList(Page<T> page, Class<T> T, String sql,
+			Object... args) {
+		AssertHelper.notEmpty(sql);
+		AssertHelper.notNull(T);
+		return access().queryList(page, new QueryFilter(), T, sql, args);
+	}
+}

+ 138 - 0
workflowy/src/main/java/org/snaker/engine/core/ServiceContext.java

@@ -0,0 +1,138 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.core;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.Context;
+import org.snaker.engine.SnakerEngine;
+import org.snaker.engine.helper.AssertHelper;
+
+/**
+ * 单实例的服务上下文
+ * 具体的上下文查找服务交给Context的实现类
+ * @author yuqs
+ * @since 1.5
+ */
+public abstract class ServiceContext {
+	private static final Logger log = LoggerFactory.getLogger(ServiceContext.class);
+	
+	/**
+	 * 上下文接口服务{@link Context}
+	 */
+	private static Context context;
+	
+	/**
+	 * 流程引擎的引用
+	 */
+	private static SnakerEngine engine;
+	
+	/**
+	 * 获取Context实现类
+	 * @return
+	 */
+	public static Context getContext() {
+		return context;
+	}
+	
+	/**
+	 * 设置Context实现类
+	 * @param context
+	 */
+	public static void setContext(Context context) {
+		ServiceContext.context = context;
+	}
+	
+	/**
+	 * 获取注册的引擎实例
+	 * @return
+	 */
+	public static SnakerEngine getEngine() {
+		AssertHelper.notNull(context, "未注册服务上下文");
+		if(engine == null) {
+			engine = context.find(SnakerEngine.class);
+		}
+		return engine;
+	}
+	
+	/**
+	 * 向上下文添加服务实例
+	 * @param name 服务名称
+	 * @param object 服务实例
+	 */
+	public static void put(String name, Object object) {
+		AssertHelper.notNull(context, "未注册服务上下文");
+		if(log.isInfoEnabled()) {
+			log.info("put new instance[name=" + name + "][object=" + object + "]");
+		}
+		context.put(name, object);
+	}
+	
+	/**
+	 * 向上下文添加服务实例
+	 * @param name 服务名称
+	 * @param clazz 服务类型
+	 */
+	public static void put(String name, Class<?> clazz) {
+		AssertHelper.notNull(context, "未注册服务上下文");
+		if(log.isInfoEnabled()) {
+			log.info("put new instance[name=" + name + "][clazz=" + clazz.getName() + "]");
+		}
+		context.put(name, clazz);
+	}
+	
+	/**
+	 * 根据服务名称判断是否存在服务实例
+	 * @param name 服务名称
+	 * @return
+	 */
+	public static boolean exist(String name) {
+		AssertHelper.notNull(context, "未注册服务上下文");
+		return context.exist(name);
+	}
+
+	/**
+	 * 对外部提供的查找对象方法,根据class类型查找
+	 * @param clazz 服务类型
+	 * @return
+	 */
+	public static <T> T find(Class<T> clazz) {
+		AssertHelper.notNull(context, "未注册服务上下文");
+		return context.find(clazz);
+	}
+	
+	/**
+	 * 对外部提供的查找对象实例列表方法,根据class类型查找集合
+	 * @param clazz 服务类型
+	 * @return
+	 */
+	public static <T> List<T> findList(Class<T> clazz) {
+		AssertHelper.notNull(context, "未注册服务上下文");
+		return context.findList(clazz);
+	}
+	
+	/**
+	 * 对外部提供的查找对象方法,根据名称、class类型查找
+	 * @param name 服务名称
+	 * @param clazz 服务类型
+	 * @return
+	 */
+	public static <T> T findByName(String name, Class<T> clazz) {
+		AssertHelper.notNull(context, "未注册服务上下文");
+		return context.findByName(name, clazz);
+	}
+}

+ 373 - 0
workflowy/src/main/java/org/snaker/engine/core/SnakerEngineImpl.java

@@ -0,0 +1,373 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.core;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.IManagerService;
+import org.snaker.engine.IOrderService;
+import org.snaker.engine.IProcessService;
+import org.snaker.engine.IQueryService;
+import org.snaker.engine.ITaskService;
+import org.snaker.engine.SnakerEngine;
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Process;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.helper.AssertHelper;
+import org.snaker.engine.helper.DateHelper;
+import org.snaker.engine.helper.StringHelper;
+import org.snaker.engine.model.NodeModel;
+import org.snaker.engine.model.ProcessModel;
+import org.snaker.engine.model.StartModel;
+import org.snaker.engine.model.TaskModel;
+import org.snaker.engine.model.TransitionModel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 基本的流程引擎实现类
+ * @author yuqs
+ * @since 1.0
+ */
+@Component
+public class SnakerEngineImpl implements SnakerEngine {
+	
+	private static final Logger log = LoggerFactory.getLogger(SnakerEngineImpl.class);
+	/**
+	 * 流程定义业务类
+	 */
+	@Autowired
+	protected IProcessService processService;
+	/**
+	 * 流程实例业务类
+	 */
+	@Autowired
+	protected IOrderService orderService;
+	/**
+	 * 任务业务类
+	 */
+	@Autowired
+	protected ITaskService taskService;
+	/**
+	 * 查询业务类
+	 */
+	@Autowired
+	protected IQueryService queryService;
+	/**
+	 * 管理业务类
+	 */
+	@Autowired
+	protected IManagerService managerService;
+	
+	/**
+	 * 获取流程定义服务
+	 */
+	public IProcessService process() {
+		AssertHelper.notNull(processService);
+		return processService;
+	}
+
+	/**
+	 * 获取查询服务
+	 */
+	public IQueryService query() {
+		AssertHelper.notNull(queryService);
+		return queryService;
+	}
+	
+	/**
+	 * 获取实例服务
+	 * @since 1.2.2
+	 */
+	public IOrderService order() {
+		AssertHelper.notNull(orderService);
+		return orderService;
+	}
+
+	/**
+	 * 获取任务服务
+	 * @since 1.2.2
+	 */
+	public ITaskService task() {
+		AssertHelper.notNull(taskService);
+		return taskService;
+	}
+	
+	/**
+	 * 获取管理服务
+	 * @since 1.4
+	 */
+	public IManagerService manager() {
+		AssertHelper.notNull(managerService);
+		return managerService;
+	}
+	
+	/**
+	 * 根据流程定义ID启动流程实例
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Order startInstanceById(String id) {
+		return startInstanceById(id, null, null);
+	}
+
+	/**
+	 * 根据流程定义ID,操作人ID启动流程实例
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Order startInstanceById(String id, String operator) {
+		return startInstanceById(id, operator, null);
+	}
+
+	/**
+	 * 根据流程定义ID,操作人ID,参数列表启动流程实例
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Order startInstanceById(String id, String operator, Map<String, Object> args) {
+		if(args == null) args = new HashMap<String, Object>();
+		Process process = process().getProcessById(id);
+		process().check(process, id);
+		return startProcess(process, operator, args);
+	}
+	
+	/**
+	 * 根据流程名称启动流程实例
+	 * @since 1.3
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Order startInstanceByName(String name) {
+		return startInstanceByName(name, null, null, null);
+	}
+
+	/**
+	 * 根据流程名称、版本号启动流程实例
+	 * @since 1.3
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Order startInstanceByName(String name, Integer version) {
+		return startInstanceByName(name, version, null, null);
+	}
+
+	/**
+	 * 根据流程名称、版本号、操作人启动流程实例
+	 * @since 1.3
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Order startInstanceByName(String name, Integer version,
+			String operator) {
+		return startInstanceByName(name, version, operator, null);
+	}
+
+	/**
+	 * 根据流程名称、版本号、操作人、参数列表启动流程实例
+	 * @since 1.3
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Order startInstanceByName(String name, Integer version,
+			String operator, Map<String, Object> args) {
+		if(args == null) args = new HashMap<String, Object>();
+		Process process = process().getProcessByVersion(name, version);
+		process().check(process, name);
+		return startProcess(process, operator, args);
+	}
+
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	private Order startProcess(Process process, String operator, Map<String, Object> args) {
+		Execution execution = execute(process, operator, args, null, null);
+		if(process.getModel() != null) {
+			StartModel start = process.getModel().getStart();
+			AssertHelper.notNull(start, "流程定义[name=" + process.getName() + ", version=" + process.getVersion() + "]没有开始节点");
+			start.execute(execution);
+		}
+
+		return execution.getOrder();
+	}
+	
+	/**
+	 * 根据父执行对象启动子流程实例(用于启动子流程)
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Order startInstanceByExecution(Execution execution) {
+		Process process = execution.getProcess();
+		StartModel start = process.getModel().getStart();
+		AssertHelper.notNull(start, "流程定义[id=" + process.getId() + "]没有开始节点");
+		
+		Execution current = execute(process, execution.getOperator(), execution.getArgs(), 
+				execution.getParentOrder().getId(), execution.getParentNodeName());
+		start.execute(current);
+		return current.getOrder();
+	}
+	
+	/**
+	 * 创建流程实例,并返回执行对象
+	 * @param process 流程定义
+	 * @param operator 操作人
+	 * @param args 参数列表
+	 * @param parentId 父流程实例id
+	 * @param parentNodeName 启动子流程的父流程节点名称
+	 * @return Execution
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	private Execution execute(Process process, String operator, Map<String, Object> args, 
+			String parentId, String parentNodeName) {
+		Order order = order().createOrder(process, operator, args, parentId, parentNodeName);
+		if(log.isDebugEnabled()) {
+			log.debug("创建流程实例对象:" + order);
+		}
+		Execution current = new Execution(this, process, order, args);
+		current.setOperator(operator);
+		return current;
+	}
+
+	/**
+	 * 根据任务主键ID执行任务
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public List<Task> executeTask(String taskId) {
+		return executeTask(taskId, null);
+	}
+
+	/**
+	 * 根据任务主键ID,操作人ID执行任务
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public List<Task> executeTask(String taskId, String operator) {
+		return executeTask(taskId, operator, null);
+	}
+
+	/**
+	 * 根据任务主键ID,操作人ID,参数列表执行任务
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public List<Task> executeTask(String taskId, String operator, Map<String, Object> args) {
+		//完成任务,并且构造执行对象
+		Execution execution = execute(taskId, operator, args);
+		if(execution == null) return Collections.emptyList();
+		ProcessModel model = execution.getProcess().getModel();
+		if(model != null) {
+			NodeModel nodeModel = model.getNode(execution.getTask().getTaskName());
+			//将执行对象交给该任务对应的节点模型执行
+			nodeModel.execute(execution);
+		}
+		return execution.getTasks();
+	}
+	
+	/**
+	 * 根据任务主键ID,操作人ID,参数列表执行任务,并且根据nodeName跳转到任意节点
+	 * 1、nodeName为null时,则驳回至上一步处理
+	 * 2、nodeName不为null时,则任意跳转,即动态创建转移
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public List<Task> executeAndJumpTask(String taskId, String operator, Map<String, Object> args, String nodeName) {
+		Execution execution = execute(taskId, operator, args);
+		if(execution == null) return Collections.emptyList();
+		ProcessModel model = execution.getProcess().getModel();
+		AssertHelper.notNull(model, "当前任务未找到流程定义模型");
+		if(StringHelper.isEmpty(nodeName)) {
+			Task newTask = task().rejectTask(model, execution.getTask());
+			execution.addTask(newTask);
+		} else {
+			NodeModel nodeModel = model.getNode(nodeName);
+			AssertHelper.notNull(nodeModel, "根据节点名称[" + nodeName + "]无法找到节点模型");
+			//动态创建转移对象,由转移对象执行execution实例
+			TransitionModel tm = new TransitionModel();
+			tm.setTarget(nodeModel);
+			tm.setEnabled(true);
+			tm.execute(execution);
+		}
+
+		return execution.getTasks();
+	}
+	
+	/**
+	 * 根据流程实例ID,操作人ID,参数列表按照节点模型model创建新的自由任务
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public List<Task> createFreeTask(String orderId, String operator, Map<String, Object> args, TaskModel model) {
+		Order order = query().getOrder(orderId);
+		AssertHelper.notNull(order, "指定的流程实例[id=" + orderId + "]已完成或不存在");
+		order.setLastUpdator(operator);
+		order.setLastUpdateTime(DateHelper.getTime());
+		Process process = process().getProcessById(order.getProcessId());
+		Execution execution = new Execution(this, process, order, args);
+		execution.setOperator(operator);
+		return task().createTask(model, execution);
+	}
+	
+	/**
+	 * 根据任务主键ID,操作人ID,参数列表完成任务,并且构造执行对象
+	 * @param taskId 任务id
+	 * @param operator 操作人
+	 * @param args 参数列表
+	 * @return Execution
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	private Execution execute(String taskId, String operator, Map<String, Object> args) {
+		if(args == null) args = new HashMap<String, Object>();
+		Task task = task().complete(taskId, operator, args);
+		if(log.isDebugEnabled()) {
+			log.debug("任务[taskId=" + taskId + "]已完成");
+		}
+		Order order = query().getOrder(task.getOrderId());
+		AssertHelper.notNull(order, "指定的流程实例[id=" + task.getOrderId() + "]已完成或不存在");
+		order.setLastUpdator(operator);
+		order.setLastUpdateTime(DateHelper.getTime());
+		order().updateOrder(order);
+		//协办任务完成不产生执行对象
+		if(!task.isMajor()) {
+			return null;
+		}
+		Map<String, Object> orderMaps = order.getVariableMap();
+		if(orderMaps != null) {
+			for(Map.Entry<String, Object> entry : orderMaps.entrySet()) {
+				if(args.containsKey(entry.getKey())) {
+					continue;
+				}
+				args.put(entry.getKey(), entry.getValue());
+			}
+		}
+		Process process = process().getProcessById(order.getProcessId());
+		Execution execution = new Execution(this, process, order, args);
+		execution.setOperator(operator);
+		execution.setTask(task);
+		return execution;
+	}
+
+	public void setProcessService(IProcessService processService) {
+		this.processService = processService;
+	}
+
+	public void setOrderService(IOrderService orderService) {
+		this.orderService = orderService;
+	}
+
+	public void setTaskService(ITaskService taskService) {
+		this.taskService = taskService;
+	}
+
+	public void setQueryService(IQueryService queryService) {
+		this.queryService = queryService;
+	}
+
+	public void setManagerService(IManagerService managerService) {
+		this.managerService = managerService;
+	}
+}

+ 102 - 0
workflowy/src/main/java/org/snaker/engine/core/SpringContext.java

@@ -0,0 +1,102 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.Context;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * spring的服务查找实现
+ * @author yuqs
+ * @since 1.5
+ */
+@Configuration
+public class SpringContext implements Context, ApplicationContextAware {
+
+	private static final Logger log = LoggerFactory.getLogger(SpringContext.class);
+	/**
+	 * Spring context
+	 */
+	private ApplicationContext applicationContext;
+
+	private DefaultListableBeanFactory beanFactory;
+
+	@Override
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		this.applicationContext = applicationContext;
+		beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
+		ServiceContext.setContext(this);
+	}
+
+	@SuppressWarnings("unchecked")
+	public <T> T find(Class<T> clazz) {
+		String[] names = applicationContext.getBeanNamesForType(clazz);
+		if (names.length > 1 && log.isWarnEnabled()) {
+			log.warn("重复定义类型:" + clazz);
+		}
+
+		if (names.length >= 1) {
+			return (T) applicationContext.getBean(names[0]);
+		}
+		return null;
+	}
+
+	@SuppressWarnings("unchecked")
+	public <T> List<T> findList(Class<T> clazz) {
+		String[] names = applicationContext.getBeanNamesForType(clazz);
+		List<T> beans = new ArrayList<T>();
+		for (String name : names) {
+			beans.add((T) applicationContext.getBean(name));
+		}
+		return beans;
+	}
+
+	public <T> T findByName(String name, Class<T> clazz) {
+		return applicationContext.getBean(name, clazz);
+	}
+
+	/**
+	 * spring不支持向applicationContext中直接添加对象
+	 */
+	public void put(String name, Object object) {
+		log.warn("spring不支持向applicationContext中直接添加对象");
+	}
+
+	/**
+	 * 向spring添加bean的定义
+	 */
+	public void put(String name, Class<?> clazz) {
+		BeanDefinition definition = new RootBeanDefinition(clazz);
+		beanFactory.registerBeanDefinition(name, definition);
+	}
+
+	/**
+	 * 判断是否存在服务
+	 */
+	public boolean exist(String name) {
+		return applicationContext.containsBean(name);
+	}
+}

+ 550 - 0
workflowy/src/main/java/org/snaker/engine/core/TaskService.java

@@ -0,0 +1,550 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.core;
+
+import java.util.*;
+
+import org.snaker.engine.*;
+import org.snaker.engine.entity.*;
+import org.snaker.engine.entity.Process;
+import org.snaker.engine.helper.AssertHelper;
+import org.snaker.engine.helper.DateHelper;
+import org.snaker.engine.helper.JsonHelper;
+import org.snaker.engine.helper.StringHelper;
+import org.snaker.engine.impl.GeneralAccessStrategy;
+import org.snaker.engine.model.CustomModel;
+import org.snaker.engine.model.NodeModel;
+import org.snaker.engine.model.ProcessModel;
+import org.snaker.engine.model.TaskModel;
+import org.snaker.engine.model.TaskModel.PerformType;
+import org.snaker.engine.model.TaskModel.TaskType;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 任务执行业务类
+ * @author yuqs
+ * @since 1.0
+ */
+@Component
+public class TaskService extends AccessService implements ITaskService {
+	private static final String START = "start";
+
+	//访问策略接口
+	private TaskAccessStrategy strategy = null;
+	/**
+	 * 完成指定任务
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Task complete(String taskId) {
+		return complete(taskId, null, null);
+	}
+
+	/**
+	 * 完成指定任务
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Task complete(String taskId, String operator) {
+		return complete(taskId, operator, null);
+	}
+	
+	/**
+	 * 完成指定任务
+	 * 该方法仅仅结束活动任务,并不能驱动流程继续执行
+	 * @see SnakerEngineImpl#executeTask(String, String, java.util.Map)
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Task complete(String taskId, String operator, Map<String, Object> args) {
+		Task task = access().getTask(taskId);
+		AssertHelper.notNull(task, "指定的任务[id=" + taskId + "]不存在");
+		task.setVariable(JsonHelper.toJson(args));
+		if(!isAllowed(task, operator)) {
+			throw new SnakerException("当前参与者[" + operator + "]不允许执行任务[taskId=" + taskId + "]");
+		}
+		HistoryTask history = new HistoryTask(task);
+		history.setFinishTime(DateHelper.getTime());
+		history.setTaskState(STATE_FINISH);
+		history.setOperator(operator);
+		if(history.getActorIds() == null) {
+			List<TaskActor> actors = access().getTaskActorsByTaskId(task.getId());
+			String[] actorIds = new String[actors.size()];
+			for(int i = 0; i < actors.size(); i++) {
+				actorIds[i] = actors.get(i).getActorId();
+			}
+			history.setActorIds(actorIds);
+		}
+		access().saveHistory(history);
+		access().deleteTask(task);
+        Completion completion = getCompletion();
+        if(completion != null) {
+            completion.complete(history);
+        }
+		return task;
+	}
+
+	/**
+	 * 更新任务对象的finish_Time、operator、expire_Time、version、variable
+	 * @param task 任务对象
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void updateTask(Task task) {
+		access().updateTask(task);
+	}
+
+	/**
+	 * 任务历史记录方法
+	 * @param execution 执行对象
+	 * @param model 自定义节点模型
+	 * @return 历史任务对象
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public HistoryTask history(Execution execution, CustomModel model) {
+		HistoryTask historyTask = new HistoryTask();
+		historyTask.setId(StringHelper.getPrimaryKey());
+		historyTask.setOrderId(execution.getOrder().getId());
+		String currentTime = DateHelper.getTime();
+		historyTask.setCreateTime(currentTime);
+		historyTask.setFinishTime(currentTime);
+		historyTask.setDisplayName(model.getDisplayName());
+		historyTask.setTaskName(model.getName());
+		historyTask.setTaskState(STATE_FINISH);
+		historyTask.setTaskType(TaskType.Record.ordinal());
+		historyTask.setParentTaskId(execution.getTask() == null ?
+				START : execution.getTask().getId());
+		historyTask.setVariable(JsonHelper.toJson(execution.getArgs()));
+		access().saveHistory(historyTask);
+		return historyTask;
+	}
+	
+	/**
+	 * 提取指定任务,设置完成时间及操作人,状态不改变
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Task take(String taskId, String operator) {
+		Task task = access().getTask(taskId);
+		AssertHelper.notNull(task, "指定的任务[id=" + taskId + "]不存在");
+		if(!isAllowed(task, operator)) {
+			throw new SnakerException("当前参与者[" + operator + "]不允许提取任务[taskId=" + taskId + "]");
+		}
+		task.setOperator(operator);
+		task.setFinishTime(DateHelper.getTime());
+		access().updateTask(task);
+		return task;
+	}
+
+    /**
+     * 唤醒指定的历史任务
+     */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+    public Task resume(String taskId, String operator) {
+        HistoryTask histTask = access().getHistTask(taskId);
+        AssertHelper.notNull(histTask, "指定的历史任务[id=" + taskId + "]不存在");
+        boolean isAllowed = true;
+        if(StringHelper.isNotEmpty(histTask.getOperator())) {
+            isAllowed = histTask.getOperator().equals(operator);
+        }
+        if(isAllowed) {
+            Task task = histTask.undoTask();
+            task.setId(StringHelper.getPrimaryKey());
+            task.setCreateTime(DateHelper.getTime());
+            access().saveTask(task);
+            assignTask(task.getId(), task.getOperator());
+            return task;
+        } else {
+            throw new SnakerException("当前参与者[" + operator + "]不允许唤醒历史任务[taskId=" + taskId + "]");
+        }
+    }
+	
+	/**
+	 * 向指定任务添加参与者
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void addTaskActor(String taskId, String... actors) {
+		addTaskActor(taskId, null, actors);
+	}
+	
+	/**
+	 * 向指定任务添加参与者
+	 * 该方法根据performType类型判断是否需要创建新的活动任务
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void addTaskActor(String taskId, Integer performType, String... actors) {
+		Task task = access().getTask(taskId);
+		AssertHelper.notNull(task, "指定的任务[id=" + taskId + "]不存在");
+		if(!task.isMajor()) return;
+		if(performType == null) performType = task.getPerformType();
+		if(performType == null) performType = 0;
+		switch(performType) {
+		case 0:
+			assignTask(task.getId(), actors);
+			Map<String, Object> data = task.getVariableMap();
+			String oldActor = (String)data.get(Task.KEY_ACTOR);
+			data.put(Task.KEY_ACTOR, oldActor + "," + StringHelper.getStringByArray(actors));
+			task.setVariable(JsonHelper.toJson(data));
+			access().updateTask(task);
+			break;
+		case 1:
+			try {
+				for(String actor : actors) {
+					Task newTask = (Task)task.clone();
+					newTask.setId(StringHelper.getPrimaryKey());
+					newTask.setCreateTime(DateHelper.getTime());
+					newTask.setOperator(actor);
+					Map<String, Object> taskData = task.getVariableMap();
+					taskData.put(Task.KEY_ACTOR, actor);
+					task.setVariable(JsonHelper.toJson(taskData));
+					access().saveTask(newTask);
+					assignTask(newTask.getId(), actor);
+				}
+			} catch(CloneNotSupportedException ex) {
+				throw new SnakerException("任务对象不支持复制", ex.getCause());
+			}
+			break;
+		default :
+			break;
+		}
+	}
+	
+	/**
+	 * 向指定任务移除参与者
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public void removeTaskActor(String taskId, String... actors) {
+		Task task = access().getTask(taskId);
+		AssertHelper.notNull(task, "指定的任务[id=" + taskId + "]不存在");
+		if(actors == null || actors.length == 0) return;
+		if(task.isMajor()) {
+			access().removeTaskActor(task.getId(), actors);
+			Map<String, Object> taskData = task.getVariableMap();
+			String actorStr = (String)taskData.get(Task.KEY_ACTOR);
+			if(StringHelper.isNotEmpty(actorStr)) {
+				String[] actorArray = actorStr.split(",");
+				StringBuilder newActor = new StringBuilder(actorStr.length());
+				boolean isMatch;
+				for(String actor : actorArray) {
+					isMatch = false;
+					if(StringHelper.isEmpty(actor)) continue;
+					for(String removeActor : actors) {
+						if(actor.equals(removeActor)) {
+							isMatch = true;
+							break;
+						}
+					}
+					if(isMatch) continue;
+					newActor.append(actor).append(",");
+				}
+				newActor.deleteCharAt(newActor.length() - 1);
+				taskData.put(Task.KEY_ACTOR, newActor.toString());
+				task.setVariable(JsonHelper.toJson(taskData));
+				access().updateTask(task);
+			}
+		}
+	}
+	
+	/**
+	 * 撤回指定的任务
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Task withdrawTask(String taskId, String operator) {
+		HistoryTask hist = access().getHistTask(taskId);
+		AssertHelper.notNull(hist, "指定的历史任务[id=" + taskId + "]不存在");
+		List<Task> tasks;
+		if(hist.isPerformAny()) {
+			tasks = access().getNextActiveTasks(hist.getId());
+		} else {
+			tasks = access().getNextActiveTasks(hist.getOrderId(), 
+					hist.getTaskName(), hist.getParentTaskId());
+		}
+		if(tasks == null || tasks.isEmpty()) {
+			throw new SnakerException("后续活动任务已完成或不存在,无法撤回.");
+		}
+		for(Task task : tasks) {
+			access().deleteTask(task);
+		}
+		
+		Task task = hist.undoTask();
+		task.setId(StringHelper.getPrimaryKey());
+		task.setCreateTime(DateHelper.getTime());
+		access().saveTask(task);
+		assignTask(task.getId(), task.getOperator());
+		return task;
+	}
+	
+	/**
+	 * 驳回任务
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public Task rejectTask(ProcessModel model, Task currentTask) {
+		String parentTaskId = currentTask.getParentTaskId();
+		if(StringHelper.isEmpty(parentTaskId) || parentTaskId.equals(START)) {
+			throw new SnakerException("上一步任务ID为空,无法驳回至上一步处理");
+		}
+		NodeModel current = model.getNode(currentTask.getTaskName());
+		HistoryTask history = access().getHistTask(parentTaskId);
+		NodeModel parent = model.getNode(history.getTaskName());
+		if(!NodeModel.canRejected(current, parent)) {
+			throw new SnakerException("无法驳回至上一步处理,请确认上一步骤并非fork、join、suprocess以及会签任务");
+		}
+
+		Task task = history.undoTask();
+		task.setId(StringHelper.getPrimaryKey());
+		task.setCreateTime(DateHelper.getTime());
+		task.setOperator(history.getOperator());
+		access().saveTask(task);
+		assignTask(task.getId(), task.getOperator());
+		return task;
+	}
+
+	/**
+	 * 对指定的任务分配参与者。参与者可以为用户、部门、角色
+	 * @param taskId 任务id
+	 * @param actorIds 参与者id集合
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	private void assignTask(String taskId, String... actorIds) {
+		if(actorIds == null || actorIds.length == 0) return;
+		for(String actorId : actorIds) {
+			//修复当actorId为null的bug
+			if(StringHelper.isEmpty(actorId)) continue;
+			TaskActor taskActor = new TaskActor();
+			taskActor.setTaskId(taskId);
+			taskActor.setActorId(actorId);
+			access().saveTaskActor(taskActor);
+		}
+	}
+	
+	/**
+	 * 根据已有任务、任务类型、参与者创建新的任务
+	 * 适用于转派,动态协办处理
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public List<Task> createNewTask(String taskId, int taskType, String... actors) {
+		Task task = access().getTask(taskId);
+		AssertHelper.notNull(task, "指定的任务[id=" + taskId + "]不存在");
+		List<Task> tasks = new ArrayList<Task>();
+		try {
+			Task newTask = (Task)task.clone();
+			newTask.setTaskType(taskType);
+			newTask.setCreateTime(DateHelper.getTime());
+			newTask.setParentTaskId(taskId);
+			tasks.add(saveTask(newTask, actors));
+		} catch (CloneNotSupportedException e) {
+			throw new SnakerException("任务对象不支持复制", e.getCause());
+		}
+		return tasks;
+	}
+
+    /**
+     * 获取任务模型
+     * @param taskId 任务id
+     * @return TaskModel
+     */
+    public TaskModel getTaskModel(String taskId) {
+        Task task = access().getTask(taskId);
+        AssertHelper.notNull(task);
+        Order order = access().getOrder(task.getOrderId());
+        AssertHelper.notNull(order);
+        Process process = ServiceContext.getEngine().process().getProcessById(order.getProcessId());
+        ProcessModel model = process.getModel();
+        NodeModel nodeModel = model.getNode(task.getTaskName());
+        AssertHelper.notNull(nodeModel, "任务id无法找到节点模型.");
+        if(nodeModel instanceof TaskModel) {
+            return (TaskModel)nodeModel;
+        } else {
+            throw new IllegalArgumentException("任务id找到的节点模型不匹配");
+        }
+    }
+
+    /**
+	 * 由DBAccess实现类创建task,并根据model类型决定是否分配参与者
+	 * @param taskModel 模型
+	 * @param execution 执行对象
+	 * @return List<Task> 任务列表
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	public List<Task> createTask(TaskModel taskModel, Execution execution) {
+		List<Task> tasks = new ArrayList<Task>();
+		
+		Map<String, Object> args = execution.getArgs();
+		if(args == null) args = new HashMap<String, Object>();
+		Date expireDate = DateHelper.processTime(args, taskModel.getExpireTime());
+		Date remindDate = DateHelper.processTime(args, taskModel.getReminderTime());
+		String form = (String)args.get(taskModel.getForm());
+		String actionUrl = StringHelper.isEmpty(form) ? taskModel.getForm() : form;
+		
+		String[] actors = getTaskActors(taskModel, execution);
+		args.put(Task.KEY_ACTOR, StringHelper.getStringByArray(actors));
+		Task task = createTaskBase(taskModel, execution);
+		task.setActionUrl(actionUrl);
+		task.setExpireDate(expireDate);
+		task.setExpireTime(DateHelper.parseTime(expireDate));
+        task.setVariable(JsonHelper.toJson(args));
+		
+		if(taskModel.isPerformAny()) {
+			//任务执行方式为参与者中任何一个执行即可驱动流程继续流转,该方法只产生一个task
+			task = saveTask(task, actors);
+			task.setRemindDate(remindDate);
+			tasks.add(task);
+		} else if(taskModel.isPerformAll()){
+			//任务执行方式为参与者中每个都要执行完才可驱动流程继续流转,该方法根据参与者个数产生对应的task数量
+			for(String actor : actors) {
+                Task singleTask;
+                try {
+                    singleTask = (Task) task.clone();
+                } catch (CloneNotSupportedException e) {
+                    singleTask = task;
+                }
+                singleTask = saveTask(singleTask, actor);
+                singleTask.setRemindDate(remindDate);
+                tasks.add(singleTask);
+			}
+		}
+		return tasks;
+	}
+	
+	/**
+	 * 根据模型、执行对象、任务类型构建基本的task对象
+	 * @param model 模型
+	 * @param execution 执行对象
+	 * @return Task任务对象
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	private Task createTaskBase(TaskModel model, Execution execution) {
+		Task task = new Task();
+		task.setOrderId(execution.getOrder().getId());
+		task.setTaskName(model.getName());
+		task.setDisplayName(model.getDisplayName());
+		task.setCreateTime(DateHelper.getTime());
+		if(model.isMajor()) {
+			task.setTaskType(TaskType.Major.ordinal());
+		} else {
+			task.setTaskType(TaskType.Aidant.ordinal());
+		}
+		task.setParentTaskId(execution.getTask() == null ? 
+				START : execution.getTask().getId());
+		task.setModel(model);
+		return task;
+	}
+	
+	/**
+	 * 由DBAccess实现类持久化task对象
+	 */
+	@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
+	private Task saveTask(Task task, String... actors) {
+		task.setId(StringHelper.getPrimaryKey());
+		task.setPerformType(PerformType.ANY.ordinal());
+		access().saveTask(task);
+		assignTask(task.getId(), actors);
+		task.setActorIds(actors);
+		return task;
+	}
+
+	/**
+	 * 根据Task模型的assignee、assignmentHandler属性以及运行时数据,确定参与者
+	 * @param model 模型
+	 * @param execution 执行对象
+	 * @return 参与者数组
+	 */
+	private String[] getTaskActors(TaskModel model, Execution execution) {
+		Object assigneeObject = null;
+        AssignmentHandler handler = model.getAssignmentHandlerObject();
+		if(StringHelper.isNotEmpty(model.getAssignee())) {
+			assigneeObject = execution.getArgs().get(model.getAssignee());
+		} else if(handler != null) {
+            if(handler instanceof Assignment) {
+                assigneeObject = ((Assignment)handler).assign(model, execution);
+            } else {
+                assigneeObject = handler.assign(execution);
+            }
+		}
+		return getTaskActors(assigneeObject == null ? model.getAssignee() : assigneeObject);
+	}
+
+	/**
+	 * 根据taskmodel指定的assignee属性,从args中取值
+	 * 将取到的值处理为String[]类型。
+	 * @param actors 参与者对象
+	 * @return 参与者数组
+	 */
+	private String[] getTaskActors(Object actors) {
+		if(actors == null) return null;
+		String[] results;
+		if(actors instanceof String) {
+			//如果值为字符串类型,则使用逗号,分隔
+			return ((String)actors).split(",");
+        } else if(actors instanceof List){
+            //jackson会把stirng[]转成arraylist,此处增加arraylist的逻辑判断,by 红豆冰沙2014.11.21
+			List<?> list = (List<?>)actors;
+			results = new String[list.size()];
+			for(int i = 0; i < list.size(); i++) {
+				results[i] = (String)list.get(i);
+			}
+            return results;
+		} else if(actors instanceof Long) {
+			//如果为Long类型,则返回1个元素的String[]
+			results = new String[1];
+			results[0] = String.valueOf((Long)actors);
+			return results;
+		} else if(actors instanceof Integer) {
+			//如果为Integer类型,则返回1个元素的String[]
+			results = new String[1];
+			results[0] = String.valueOf((Integer)actors);
+			return results;
+		} else if(actors instanceof String[]) {
+			//如果为String[]类型,则直接返回
+			return (String[])actors;
+		} else {
+			//其它类型,抛出不支持的类型异常
+			throw new SnakerException("任务参与者对象[" + actors + "]类型不支持."
+					+ "合法参数示例:Long,Integer,new String[]{},'10000,20000',List<String>");
+		}
+	}
+
+	/**
+	 * 判断当前操作人operator是否允许执行taskId指定的任务
+	 */
+	public boolean isAllowed(Task task, String operator) {
+		if(StringHelper.isNotEmpty(operator)) {
+			if(SnakerEngine.ADMIN.equalsIgnoreCase(operator)
+					|| SnakerEngine.AUTO.equalsIgnoreCase(operator)) {
+				return true;
+			}
+			if(StringHelper.isNotEmpty(task.getOperator())) {
+				return operator.equals(task.getOperator());
+			}
+		}
+		List<TaskActor> actors = access().getTaskActorsByTaskId(task.getId());
+		if(actors == null || actors.isEmpty()) return true;
+		return !StringHelper.isEmpty(operator)
+				&& getStrategy().isAllowed(operator, actors);
+	}
+
+	public void setStrategy(TaskAccessStrategy strategy) {
+		this.strategy = strategy;
+	}
+
+	public TaskAccessStrategy getStrategy() {
+        if(strategy != null) {
+            return strategy;
+        }
+		strategy = ServiceContext.find(TaskAccessStrategy.class);
+		if(strategy == null) {
+			ServiceContext.put(TaskAccessStrategy.class.getName(), GeneralAccessStrategy.class);
+            strategy = ServiceContext.find(TaskAccessStrategy.class);
+		}
+		return strategy;
+	}
+}

+ 74 - 0
workflowy/src/main/java/org/snaker/engine/entity/CCOrder.java

@@ -0,0 +1,74 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.entity;
+
+import java.io.Serializable;
+
+/**
+ * 抄送实例实体
+ * @author yuqs
+ * @since 1.5
+ */
+public class CCOrder implements Serializable {
+	private static final long serialVersionUID = -7596174225209412843L;
+	private String orderId;
+	private String actorId;
+    private String creator;
+    private String createTime;
+    private String finishTime;
+	private Integer status;
+	public String getOrderId() {
+		return orderId;
+	}
+	public void setOrderId(String orderId) {
+		this.orderId = orderId;
+	}
+
+    public String getCreator() {
+        return creator;
+    }
+
+    public void setCreator(String creator) {
+        this.creator = creator;
+    }
+	public String getActorId() {
+		return actorId;
+	}
+	public void setActorId(String actorId) {
+		this.actorId = actorId;
+	}
+	public Integer getStatus() {
+		return status;
+	}
+	public void setStatus(Integer status) {
+		this.status = status;
+	}
+
+    public String getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(String createTime) {
+        this.createTime = createTime;
+    }
+
+    public String getFinishTime() {
+        return finishTime;
+    }
+
+    public void setFinishTime(String finishTime) {
+        this.finishTime = finishTime;
+    }
+}

+ 229 - 0
workflowy/src/main/java/org/snaker/engine/entity/HistoryOrder.java

@@ -0,0 +1,229 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.entity;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
+
+import org.snaker.engine.SnakerEngine;
+import org.snaker.engine.core.ServiceContext;
+import org.snaker.engine.helper.JsonHelper;
+
+/**
+ * 历史流程实例实体类
+ * @author yuqs
+ * @since 1.0
+ */
+public class HistoryOrder implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5853727929104539328L;
+	/**
+	 * 主键ID
+	 */
+	private String id;
+	/**
+	 * 流程定义ID
+	 */
+    private String processId;
+    /**
+     * 流程实例状态(0:结束;1:活动)
+     */
+    private Integer orderState;
+    /**
+     * 流程实例创建者ID
+     */
+    private String creator;
+    /**
+     * 流程实例创建时间
+     */
+    private String createTime;
+    /**
+     * 流程实例结束时间
+     */
+    private String endTime;
+    /**
+     * 流程实例为子流程时,该字段标识父流程实例ID
+     */
+    private String parentId;
+    /**
+     * 流程实例期望完成时间
+     */
+    private String expireTime;
+    /**
+     * 流程实例优先级
+     */
+    private Integer priority;
+    /**
+     * 流程实例编号
+     */
+    private String orderNo;
+	/**
+     * 流程实例附属变量
+     */
+    private String variable;
+
+	public HistoryOrder() {
+    	
+    }
+    
+    public HistoryOrder(Order order) {
+    	this.id = order.getId();
+    	this.processId = order.getProcessId();
+    	this.createTime = order.getCreateTime();
+    	this.expireTime = order.getExpireTime();
+    	this.creator = order.getCreator();
+    	this.parentId = order.getParentId();
+    	this.priority = order.getPriority();
+    	this.orderNo = order.getOrderNo();
+    	this.variable = order.getVariable();
+    }
+
+    /**
+     * 根据历史实例撤回活动实例
+     * @return 活动实例对象
+     */
+    public Order undo() {
+        Order order = new Order();
+        order.setId(this.id);
+        order.setProcessId(this.processId);
+        order.setParentId(this.parentId);
+        order.setCreator(this.creator);
+        order.setCreateTime(this.createTime);
+        order.setLastUpdator(this.creator);
+        order.setLastUpdateTime(this.endTime);
+        order.setExpireTime(this.expireTime);
+        order.setOrderNo(this.orderNo);
+        order.setPriority(this.priority);
+        order.setVariable(this.variable);
+        order.setVersion(0);
+        return order;
+    }
+
+	public String getProcessId() {
+		return processId;
+	}
+
+	public void setProcessId(String processId) {
+		this.processId = processId;
+	}
+
+	public Integer getOrderState() {
+		return orderState;
+	}
+
+	public void setOrderState(Integer orderState) {
+		this.orderState = orderState;
+	}
+
+	public String getCreator() {
+		return creator;
+	}
+
+	public void setCreator(String creator) {
+		this.creator = creator;
+	}
+
+	public String getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(String createTime) {
+		this.createTime = createTime;
+	}
+
+	public String getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(String endTime) {
+		this.endTime = endTime;
+	}
+
+	public String getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(String parentId) {
+		this.parentId = parentId;
+	}
+
+	public String getExpireTime() {
+		return expireTime;
+	}
+
+	public void setExpireTime(String expireTime) {
+		this.expireTime = expireTime;
+	}
+
+	public Integer getPriority() {
+		return priority;
+	}
+
+	public void setPriority(Integer priority) {
+		this.priority = priority;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+    
+    public String getOrderNo() {
+		return orderNo;
+	}
+
+	public void setOrderNo(String orderNo) {
+		this.orderNo = orderNo;
+	}
+
+	public String getVariable() {
+		return variable;
+	}
+
+	public void setVariable(String variable) {
+		this.variable = variable;
+	}
+
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> getVariableMap() {
+        Map<String, Object> map = JsonHelper.fromJson(this.variable, Map.class);
+        if(map == null) return Collections.emptyMap();
+        return map;
+    }
+	
+	public String getProcessName() {
+		SnakerEngine engine = ServiceContext.getEngine();
+		Process process = engine.process().getProcessById(this.processId);
+		if(process == null) return this.processId;
+		return process.getDisplayName();
+	}
+	
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("HistoryOrder(id=").append(this.id);
+		sb.append(",processId=").append(this.processId);
+		sb.append(",creator=").append(this.creator);
+		sb.append(",createTime").append(this.createTime);
+		sb.append(",orderNo=").append(this.orderNo).append(")");
+		return sb.toString();
+	}
+}

+ 276 - 0
workflowy/src/main/java/org/snaker/engine/entity/HistoryTask.java

@@ -0,0 +1,276 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.entity;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
+
+import org.snaker.engine.helper.JsonHelper;
+import org.snaker.engine.model.TaskModel.PerformType;
+
+/**
+ * 历史任务实体类
+ * @author yuqs
+ * @since 1.0
+ */
+public class HistoryTask implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 6814632180050362450L;
+	/**
+	 * 主键ID
+	 */
+	private String id;
+    /**
+     * 流程实例ID
+     */
+    private String orderId;
+    /**
+     * 任务名称
+     */
+	private String taskName;
+	/**
+	 * 任务显示名称
+	 */
+	private String displayName;
+	/**
+	 * 参与方式(0:普通任务;1:参与者fork任务[即:如果10个参与者,需要每个人都要完成,才继续流转])
+	 */
+	private Integer performType;
+	/**
+	 * 任务类型
+	 */
+    private Integer taskType;
+    /**
+     * 任务状态(0:结束;1:活动)
+     */
+    private Integer taskState;
+    /**
+     * 任务处理者ID
+     */
+    private String operator;
+    /**
+     * 任务创建时间
+     */
+    private String createTime;
+    /**
+     * 任务完成时间
+     */
+    private String finishTime;
+    /**
+     * 期望任务完成时间
+     */
+    private String expireTime;
+    /**
+     * 任务关联的表单url
+     */
+    private String actionUrl;
+    /**
+     * 任务参与者列表
+     */
+    private String[] actorIds;
+    /**
+     * 父任务Id
+     */
+    private String parentTaskId;
+	/**
+     * 任务附属变量
+     */
+    private String variable;
+    
+    public String getParentTaskId() {
+		return parentTaskId;
+	}
+
+	public void setParentTaskId(String parentTaskId) {
+		this.parentTaskId = parentTaskId;
+	}
+
+	public String getVariable() {
+		return variable;
+	}
+
+	public void setVariable(String variable) {
+		this.variable = variable;
+	}
+
+	public HistoryTask() {
+    	
+    }
+    
+    public HistoryTask(Task task) {
+    	this.id = task.getId();
+    	this.orderId = task.getOrderId();
+    	this.createTime = task.getCreateTime();
+    	this.displayName = task.getDisplayName();
+    	this.taskName = task.getTaskName();
+    	this.taskType = task.getTaskType();
+    	this.expireTime = task.getExpireTime();
+    	this.actionUrl = task.getActionUrl();
+    	this.actorIds = task.getActorIds();
+    	this.parentTaskId = task.getParentTaskId();
+    	this.variable = task.getVariable();
+    	this.performType = task.getPerformType();
+    }
+    
+    /**
+     * 根据历史任务产生撤回的任务对象
+     * @return 任务对象
+     */
+    public Task undoTask() {
+    	Task task = new Task();
+    	task.setOrderId(this.getOrderId());;
+    	task.setTaskName(this.getTaskName());
+    	task.setDisplayName(this.getDisplayName());
+    	task.setTaskType(this.getTaskType());
+    	task.setExpireTime(this.getExpireTime());
+    	task.setActionUrl(this.getActionUrl());
+    	task.setParentTaskId(this.getParentTaskId());
+    	task.setVariable(this.getVariable());
+    	task.setPerformType(this.getPerformType());
+    	task.setOperator(this.getOperator());
+    	return task;
+    }
+    
+    public boolean isPerformAny() {
+    	return this.performType.intValue() == PerformType.ANY.ordinal();
+    }
+    
+	public String getTaskName() {
+		return taskName;
+	}
+
+	public void setTaskName(String taskName) {
+		this.taskName = taskName;
+	}
+
+	public Integer getTaskType() {
+		return taskType;
+	}
+
+	public void setTaskType(Integer taskType) {
+		this.taskType = taskType;
+	}
+
+	public Integer getTaskState() {
+		return taskState;
+	}
+
+	public void setTaskState(Integer taskState) {
+		this.taskState = taskState;
+	}
+
+	public String getOperator() {
+		return operator;
+	}
+
+	public void setOperator(String operator) {
+		this.operator = operator;
+	}
+
+	public String getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(String createTime) {
+		this.createTime = createTime;
+	}
+
+	public String getFinishTime() {
+		return finishTime;
+	}
+
+	public void setFinishTime(String finishTime) {
+		this.finishTime = finishTime;
+	}
+
+	public String getExpireTime() {
+		return expireTime;
+	}
+
+	public void setExpireTime(String expireTime) {
+		this.expireTime = expireTime;
+	}
+
+	public String getActionUrl() {
+		return actionUrl;
+	}
+
+	public void setActionUrl(String actionUrl) {
+		this.actionUrl = actionUrl;
+	}
+
+	public String getDisplayName() {
+		return displayName;
+	}
+
+	public void setDisplayName(String displayName) {
+		this.displayName = displayName;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getOrderId() {
+		return orderId;
+	}
+
+	public void setOrderId(String orderId) {
+		this.orderId = orderId;
+	}
+	
+	public Integer getPerformType() {
+		return performType;
+	}
+
+	public void setPerformType(Integer performType) {
+		this.performType = performType;
+	}
+
+	public String[] getActorIds() {
+		return actorIds;
+	}
+
+	public void setActorIds(String[] actorIds) {
+		this.actorIds = actorIds;
+	}
+	
+	@SuppressWarnings("unchecked")
+	public Map<String, Object> getVariableMap() {
+        Map<String, Object> map = JsonHelper.fromJson(this.variable, Map.class);
+        if(map == null) return Collections.emptyMap();
+        return map;
+	}
+	
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("HistoryTask(id=").append(this.id);
+		sb.append(",orderId=").append(this.orderId);
+		sb.append(",taskName=").append(this.taskName);
+		sb.append(",displayName").append(this.displayName);
+		sb.append(",taskType=").append(this.taskType);
+		sb.append(",createTime").append(this.createTime);
+		sb.append(",performType=").append(this.performType).append(")");
+		return sb.toString();
+	}
+}

+ 50 - 0
workflowy/src/main/java/org/snaker/engine/entity/HistoryTaskActor.java

@@ -0,0 +1,50 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.entity;
+
+import java.io.Serializable;
+
+/**
+ * 历史任务参与者实体类
+ * @author yuqs
+ * @since 1.0
+ */
+public class HistoryTaskActor implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -998098931519373599L;
+	/**
+	 * 关联的任务ID
+	 */
+    private String taskId;
+    /**
+     * 关联的参与者ID(参与者可以为用户、部门、角色)
+     */
+    private String actorId;
+	public String getTaskId() {
+		return taskId;
+	}
+	public void setTaskId(String taskId) {
+		this.taskId = taskId;
+	}
+	public String getActorId() {
+		return actorId;
+	}
+	public void setActorId(String actorId) {
+		this.actorId = actorId;
+	}
+}

+ 206 - 0
workflowy/src/main/java/org/snaker/engine/entity/Order.java

@@ -0,0 +1,206 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.entity;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
+
+import org.snaker.engine.helper.JsonHelper;
+
+/**
+ * 流程工作单实体类(一般称为流程实例)
+ * @author yuqs
+ * @since 1.0
+ */
+public class Order implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -8335779448165343933L;
+	/**
+	 * 主键ID
+	 */
+	private String id;
+	/**
+	 * 版本
+	 */
+	private Integer version = 0;
+	/**
+	 * 流程定义ID
+	 */
+    private String processId;
+    /**
+     * 流程实例创建者ID
+     */
+    private String creator;
+    /**
+     * 流程实例创建时间
+     */
+    private String createTime;
+    /**
+     * 流程实例为子流程时,该字段标识父流程实例ID
+     */
+    private String parentId;
+    /**
+     * 流程实例为子流程时,该字段标识父流程哪个节点模型启动的子流程
+     */
+    private String parentNodeName;
+    /**
+     * 流程实例期望完成时间
+     */
+    private String expireTime;
+    /**
+     * 流程实例上一次更新时间
+     */
+    private String lastUpdateTime;
+    /**
+     * 流程实例上一次更新人员ID
+     */
+    private String lastUpdator;
+    /**
+     * 流程实例优先级
+     */
+    private Integer priority;
+    /**
+     * 流程实例编号
+     */
+    private String orderNo;
+	/**
+     * 流程实例附属变量
+     */
+    private String variable;
+
+	public String getProcessId() {
+		return processId;
+	}
+
+	public void setProcessId(String processId) {
+		this.processId = processId;
+	}
+
+	public String getCreator() {
+		return creator;
+	}
+
+	public void setCreator(String creator) {
+		this.creator = creator;
+	}
+
+	public String getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(String createTime) {
+		this.createTime = createTime;
+	}
+
+	public String getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(String parentId) {
+		this.parentId = parentId;
+	}
+
+	public String getExpireTime() {
+		return expireTime;
+	}
+
+	public void setExpireTime(String expireTime) {
+		this.expireTime = expireTime;
+	}
+
+	public String getLastUpdateTime() {
+		return lastUpdateTime;
+	}
+
+	public void setLastUpdateTime(String lastUpdateTime) {
+		this.lastUpdateTime = lastUpdateTime;
+	}
+
+	public String getLastUpdator() {
+		return lastUpdator;
+	}
+
+	public void setLastUpdator(String lastUpdator) {
+		this.lastUpdator = lastUpdator;
+	}
+
+	public Integer getPriority() {
+		return priority;
+	}
+
+	public void setPriority(Integer priority) {
+		this.priority = priority;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getParentNodeName() {
+		return parentNodeName;
+	}
+
+	public void setParentNodeName(String parentNodeName) {
+		this.parentNodeName = parentNodeName;
+	}
+
+	public String getVariable() {
+		return variable;
+	}
+	
+	@SuppressWarnings("unchecked")
+	public Map<String, Object> getVariableMap() {
+        Map<String, Object> map = JsonHelper.fromJson(this.variable, Map.class);
+        if(map == null) return Collections.emptyMap();
+        return map;
+	}
+
+	public void setVariable(String variable) {
+		this.variable = variable;
+	}
+	
+    public String getOrderNo() {
+		return orderNo;
+	}
+
+	public void setOrderNo(String orderNo) {
+		this.orderNo = orderNo;
+	}
+
+	public Integer getVersion() {
+		return version;
+	}
+
+	public void setVersion(Integer version) {
+		this.version = version;
+	}
+	
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("Order(id=").append(this.id);
+		sb.append(",processId=").append(this.processId);
+		sb.append(",creator=").append(this.creator);
+		sb.append(",createTime").append(this.createTime);
+		sb.append(",orderNo=").append(this.orderNo).append(")");
+		return sb.toString();
+	}
+}

+ 192 - 0
workflowy/src/main/java/org/snaker/engine/entity/Process.java

@@ -0,0 +1,192 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.entity;
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.sql.Blob;
+
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.helper.StreamHelper;
+import org.snaker.engine.model.ProcessModel;
+
+/**
+ * 流程定义实体类
+ * @author yuqs
+ * @since 1.0
+ */
+public class Process implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 6541688543201014542L;
+	/**
+	 * 主键ID
+	 */
+	private String id;
+	/**
+	 * 版本
+	 */
+	private Integer version;
+    /**
+     * 流程定义名称
+     */
+	private String name;
+    /**
+     * 流程定义显示名称
+     */
+	private String displayName;
+    /**
+     * 流程定义类型(预留字段)
+     */
+	private String type;
+	/**
+	 * 当前流程的实例url(一般为流程第一步的url)
+	 * 该字段可以直接打开流程申请的表单
+	 */
+	private String instanceUrl;
+    /**
+     * 是否可用的开关
+     */
+	private Integer state;
+	/**
+	 * 创建时间
+	 */
+	private String createTime;
+	/**
+	 * 创建人
+	 */
+	private String creator;
+	/**
+	 * 流程定义模型
+	 */
+    private ProcessModel model;
+    /**
+     * 流程定义xml
+     */
+    private Blob content;
+    /**
+     * 流程定义字节数组
+     */
+    private byte[] bytes;
+    
+	public String getName() {
+		return name;
+	}
+	public void setName(String name) {
+		this.name = name;
+	}
+	public String getDisplayName() {
+		return displayName;
+	}
+	public void setDisplayName(String displayName) {
+		this.displayName = displayName;
+	}
+	public String getType() {
+		return type;
+	}
+	public void setType(String type) {
+		this.type = type;
+	}
+	public Integer getState() {
+		return state;
+	}
+	public void setState(Integer state) {
+		this.state = state;
+	}
+	public String getId() {
+		return id;
+	}
+	public void setId(String id) {
+		this.id = id;
+	}
+	
+	public ProcessModel getModel() {
+		return model;
+	}
+	
+	/**
+	 * setter name/displayName/instanceUrl
+	 * @param processModel
+	 */
+	public void setModel(ProcessModel processModel) {
+		this.model = processModel;
+    	this.name = processModel.getName();
+    	this.displayName = processModel.getDisplayName();
+    	this.instanceUrl = processModel.getInstanceUrl();
+	}
+	public String getInstanceUrl() {
+		return instanceUrl;
+	}
+	public void setInstanceUrl(String instanceUrl) {
+		this.instanceUrl = instanceUrl;
+	}
+	public byte[] getDBContent() {
+		if(this.content != null) {
+			try {
+				return this.content.getBytes(1L, Long.valueOf(this.content.length()).intValue());
+			} catch (Exception e) {
+				try {
+					InputStream is = content.getBinaryStream();
+					return StreamHelper.readBytes(is);
+				} catch (Exception e1) {
+					throw new SnakerException("couldn't extract stream out of blob", e1);
+				}
+			}
+		}
+		
+		return bytes;
+	}
+	public Blob getContent() {
+		return content;
+	}
+	public void setContent(Blob content) {
+		this.content = content;
+	}
+	public byte[] getBytes() {
+		return bytes;
+	}
+	public void setBytes(byte[] bytes) {
+		this.bytes = bytes;
+	}
+    public Integer getVersion() {
+		return version;
+	}
+	public void setVersion(Integer version) {
+		this.version = version;
+	}
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("Process(id=").append(this.id);
+		sb.append(",name=").append(this.name);
+		sb.append(",displayName=").append(this.displayName);
+		sb.append(",version=").append(this.version);
+		sb.append(",state=").append(this.state).append(")");
+		return sb.toString();
+	}
+	public String getCreateTime() {
+		return createTime;
+	}
+	public void setCreateTime(String createTime) {
+		this.createTime = createTime;
+	}
+	public String getCreator() {
+		return creator;
+	}
+	public void setCreator(String creator) {
+		this.creator = creator;
+	}
+}

+ 109 - 0
workflowy/src/main/java/org/snaker/engine/entity/Surrogate.java

@@ -0,0 +1,109 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.entity;
+
+import java.io.Serializable;
+
+/**
+ * 委托代理实体类
+ * @author yuqs
+ * @since 1.4
+ */
+public class Surrogate implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -7359321877096338448L;
+	/**
+	 * 主键ID
+	 */
+	private String id;
+	/**
+	 * 流程name
+	 */
+	private String processName;
+	/**
+	 * 授权人
+	 */
+	private String operator;
+	/**
+	 * 代理人
+	 */
+	private String surrogate;
+	/**
+	 * 操作时间
+	 */
+	private String odate;
+	/**
+	 * 开始时间
+	 */
+	private String sdate;
+	/**
+	 * 结束时间
+	 */
+	private String edate;
+	/**
+	 * 状态
+	 */
+	private Integer state;
+	public String getId() {
+		return id;
+	}
+	public void setId(String id) {
+		this.id = id;
+	}
+	public String getProcessName() {
+		return processName;
+	}
+	public void setProcessName(String processName) {
+		this.processName = processName;
+	}
+	public String getOperator() {
+		return operator;
+	}
+	public void setOperator(String operator) {
+		this.operator = operator;
+	}
+	public String getSurrogate() {
+		return surrogate;
+	}
+	public void setSurrogate(String surrogate) {
+		this.surrogate = surrogate;
+	}
+	public String getOdate() {
+		return odate;
+	}
+	public void setOdate(String odate) {
+		this.odate = odate;
+	}
+	public String getSdate() {
+		return sdate;
+	}
+	public void setSdate(String sdate) {
+		this.sdate = sdate;
+	}
+	public String getEdate() {
+		return edate;
+	}
+	public void setEdate(String edate) {
+		this.edate = edate;
+	}
+	public Integer getState() {
+		return state;
+	}
+	public void setState(Integer state) {
+		this.state = state;
+	}
+}

+ 294 - 0
workflowy/src/main/java/org/snaker/engine/entity/Task.java

@@ -0,0 +1,294 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.entity;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Map;
+
+import org.snaker.engine.helper.JsonHelper;
+import org.snaker.engine.model.TaskModel;
+import org.snaker.engine.model.TaskModel.TaskType;
+
+/**
+ * 任务实体类
+ * @author yuqs
+ * @since 1.0
+ */
+public class Task implements Serializable, Cloneable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -189094546633914087L;
+	public static final String KEY_ACTOR = "S-ACTOR";
+	/**
+	 * 主键ID
+	 */
+	private String id;
+	/**
+	 * 版本
+	 */
+	private Integer version = 0;
+    /**
+     * 流程实例ID
+     */
+    private String orderId;
+    /**
+     * 任务名称
+     */
+	private String taskName;
+	/**
+	 * 任务显示名称
+	 */
+	private String displayName;
+	/**
+	 * 参与方式(0:普通任务;1:参与者会签任务)
+	 */
+	private Integer performType;
+	/**
+	 * 任务类型(0:主办任务;1:协办任务)
+	 */
+    private Integer taskType;
+    /**
+     * 任务处理者ID
+     */
+    private String operator;
+    /**
+     * 任务创建时间
+     */
+    private String createTime;
+    /**
+     * 任务完成时间
+     */
+    private String finishTime;
+    /**
+     * 期望任务完成时间
+     */
+    private String expireTime;
+    /**
+     * 期望的完成时间date类型
+     */
+    private Date expireDate;
+    /**
+     * 提醒时间date类型
+     */
+    private Date remindDate;
+    /**
+     * 任务关联的表单url
+     */
+    private String actionUrl;
+    /**
+     * 任务参与者列表
+     */
+    private String[] actorIds;
+    /**
+     * 父任务Id
+     */
+    private String parentTaskId;
+	/**
+     * 任务附属变量
+     */
+    private String variable;
+    /**
+     * 保持模型对象
+     */
+    private TaskModel model;
+    
+    public Task() {
+    	
+    }
+    
+    public Task(String id) {
+    	this.id = id;
+    }
+    
+    public boolean isMajor() {
+    	return this.taskType == TaskType.Major.ordinal();
+    }
+    
+	public String getParentTaskId() {
+		return parentTaskId;
+	}
+
+	public void setParentTaskId(String parentTaskId) {
+		this.parentTaskId = parentTaskId;
+	}
+
+	public String getVariable() {
+		return variable;
+	}
+
+	public void setVariable(String variable) {
+		this.variable = variable;
+	}
+
+	public String getTaskName() {
+		return taskName;
+	}
+
+	public void setTaskName(String taskName) {
+		this.taskName = taskName;
+	}
+
+	public Integer getTaskType() {
+		return taskType;
+	}
+
+	public void setTaskType(Integer taskType) {
+		this.taskType = taskType;
+	}
+
+	public String getOperator() {
+		return operator;
+	}
+
+	public void setOperator(String operator) {
+		this.operator = operator;
+	}
+
+	public String getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(String createTime) {
+		this.createTime = createTime;
+	}
+
+	public String getFinishTime() {
+		return finishTime;
+	}
+
+	public void setFinishTime(String finishTime) {
+		this.finishTime = finishTime;
+	}
+
+	public String getExpireTime() {
+		return expireTime;
+	}
+
+	public void setExpireTime(String expireTime) {
+		this.expireTime = expireTime;
+	}
+
+	public String getActionUrl() {
+		return actionUrl;
+	}
+
+	public void setActionUrl(String actionUrl) {
+		this.actionUrl = actionUrl;
+	}
+
+	public String getDisplayName() {
+		return displayName;
+	}
+
+	public void setDisplayName(String displayName) {
+		this.displayName = displayName;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getOrderId() {
+		return orderId;
+	}
+
+	public void setOrderId(String orderId) {
+		this.orderId = orderId;
+	}
+
+	public String[] getActorIds() {
+		if(actorIds == null) {
+			String actorStr = (String)getVariableMap().get(KEY_ACTOR);
+			if(actorStr != null) {
+				actorIds = actorStr.split(",");
+			}
+		}
+		return actorIds;
+	}
+
+	public void setActorIds(String[] actorIds) {
+		this.actorIds = actorIds;
+	}
+	
+	public Integer getPerformType() {
+		return performType;
+	}
+
+	public void setPerformType(Integer performType) {
+		this.performType = performType;
+	}
+
+	public Integer getVersion() {
+		return version;
+	}
+
+	public void setVersion(Integer version) {
+		this.version = version;
+	}
+	
+	public Date getExpireDate() {
+		return expireDate;
+	}
+
+	public void setExpireDate(Date expireDate) {
+		this.expireDate = expireDate;
+	}
+
+	public Date getRemindDate() {
+		return remindDate;
+	}
+
+	public void setRemindDate(Date remindDate) {
+		this.remindDate = remindDate;
+	}
+
+	public TaskModel getModel() {
+		return model;
+	}
+
+	public void setModel(TaskModel model) {
+		this.model = model;
+	}
+	
+	@SuppressWarnings("unchecked")
+	public Map<String, Object> getVariableMap() {
+        Map<String, Object> map = JsonHelper.fromJson(this.variable, Map.class);
+        if(map == null) return Collections.emptyMap();
+        return map;
+	}
+	
+	public Object clone() throws CloneNotSupportedException {
+		return super.clone();
+	}
+
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("Task(id=").append(this.id);
+		sb.append(",orderId=").append(this.orderId);
+		sb.append(",taskName=").append(this.taskName);
+		sb.append(",displayName").append(this.displayName);
+		sb.append(",taskType=").append(this.taskType);
+		sb.append(",createTime=").append(this.createTime);
+		sb.append(",performType=").append(this.performType).append(")");
+		return sb.toString();
+	}
+}

+ 50 - 0
workflowy/src/main/java/org/snaker/engine/entity/TaskActor.java

@@ -0,0 +1,50 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.entity;
+
+import java.io.Serializable;
+
+/**
+ * 任务参与者实体类
+ * @author yuqs
+ * @since 1.0
+ */
+public class TaskActor implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 2969915022122094614L;
+	/**
+	 * 关联的任务ID
+	 */
+    private String taskId;
+    /**
+     * 关联的参与者ID(参与者可以为用户、部门、角色)
+     */
+    private String actorId;
+	public String getTaskId() {
+		return taskId;
+	}
+	public void setTaskId(String taskId) {
+		this.taskId = taskId;
+	}
+	public String getActorId() {
+		return actorId;
+	}
+	public void setActorId(String actorId) {
+		this.actorId = actorId;
+	}
+}

+ 306 - 0
workflowy/src/main/java/org/snaker/engine/entity/WorkItem.java

@@ -0,0 +1,306 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.entity;
+
+import org.snaker.engine.helper.JsonHelper;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * 工作项(待办、已处理任务的查询结果实体)
+ * @author yuqs
+ * @since 1.0
+ */
+public class WorkItem implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 2630386406754942892L;
+	private String id;
+    /**
+     * 流程定义ID
+     */
+    private String processId;
+    /**
+     * 流程实例ID
+     */
+    private String orderId;
+	/**
+	 * 任务ID
+	 */
+	private String taskId;
+    /**
+     * 流程名称
+     */
+    private String processName;
+    /**
+     * 流程实例url
+     */
+    private String instanceUrl;
+	/**
+     * 流程实例为子流程时,该字段标识父流程实例ID
+     */
+    private String parentId;
+    /**
+     * 流程实例创建者ID
+     */
+    private String creator;
+    /**
+     * 流程实例创建时间
+     */
+    private String orderCreateTime;
+    /**
+     * 流程实例结束时间
+     */
+    private String orderEndTime;
+    /**
+     * 流程实例期望完成时间
+     */
+    private String orderExpireTime;
+    /**
+     * 流程实例编号
+     */
+    private String orderNo;
+	/**
+     * 流程实例附属变量
+     */
+    private String orderVariable;
+    /**
+     * 任务名称
+     */
+	private String taskName;
+	/**
+	 * 任务标识名称
+	 */
+	private String taskKey;
+	/**
+	 * 参与类型(0:普通任务;1:参与者fork任务[即:如果10个参与者,需要每个人都要完成,才继续流转])
+	 */
+	private Integer performType;
+	/**
+	 * 任务类型
+	 */
+    private Integer taskType;
+    /**
+     * 任务状态(0:结束;1:活动)
+     */
+    private Integer taskState;
+    /**
+     * 任务创建时间
+     */
+    private String taskCreateTime;
+    /**
+     * 任务完成时间
+     */
+    private String taskEndTime;
+    /**
+     * 期望任务完成时间
+     */
+    private String taskExpireTime;
+	/**
+     * 任务附属变量
+     */
+    private String taskVariable;
+    /**
+     * 任务处理者ID
+     */
+    private String operator;
+    /**
+     * 任务关联的表单url
+     */
+    private String actionUrl;
+    /**
+     * 任务参与者列表
+     */
+    private String[] actorIds;
+
+	public String getOrderNo() {
+		return orderNo;
+	}
+	public void setOrderNo(String orderNo) {
+		this.orderNo = orderNo;
+	}
+	public String getProcessId() {
+		return processId;
+	}
+	public void setProcessId(String processId) {
+		this.processId = processId;
+	}
+	public String getOrderId() {
+		return orderId;
+	}
+	public void setOrderId(String orderId) {
+		this.orderId = orderId;
+	}
+	public String getTaskId() {
+		return taskId;
+	}
+	public void setTaskId(String taskId) {
+		this.taskId = taskId;
+	}
+	public String getProcessName() {
+		return processName;
+	}
+	public void setProcessName(String processName) {
+		this.processName = processName;
+	}
+    public String getInstanceUrl() {
+		return instanceUrl;
+	}
+	public void setInstanceUrl(String instanceUrl) {
+		this.instanceUrl = instanceUrl;
+	}
+	public String getParentId() {
+		return parentId;
+	}
+	public void setParentId(String parentId) {
+		this.parentId = parentId;
+	}
+	public String getCreator() {
+		return creator;
+	}
+	public void setCreator(String creator) {
+		this.creator = creator;
+	}
+	public String getOrderCreateTime() {
+		return orderCreateTime;
+	}
+	public void setOrderCreateTime(String orderCreateTime) {
+		this.orderCreateTime = orderCreateTime;
+	}
+	public String getOrderEndTime() {
+		return orderEndTime;
+	}
+	public void setOrderEndTime(String orderEndTime) {
+		this.orderEndTime = orderEndTime;
+	}
+	public String getOrderExpireTime() {
+		return orderExpireTime;
+	}
+	public void setOrderExpireTime(String orderExpireTime) {
+		this.orderExpireTime = orderExpireTime;
+	}
+	public String getTaskName() {
+		return taskName;
+	}
+	public void setTaskName(String taskName) {
+		this.taskName = taskName;
+	}
+
+	public String getTaskKey() {
+		return taskKey;
+	}
+
+	public void setTaskKey(String taskKey) {
+		this.taskKey = taskKey;
+	}
+	public Integer getTaskType() {
+		return taskType;
+	}
+	public void setTaskType(Integer taskType) {
+		this.taskType = taskType;
+	}
+	public Integer getPerformType() {
+		return performType;
+	}
+	public void setPerformType(Integer performType) {
+		this.performType = performType;
+	}
+	public Integer getTaskState() {
+		return taskState;
+	}
+	public void setTaskState(Integer taskState) {
+		this.taskState = taskState;
+	}
+	public String getTaskCreateTime() {
+		return taskCreateTime;
+	}
+	public void setTaskCreateTime(String taskCreateTime) {
+		this.taskCreateTime = taskCreateTime;
+	}
+	public String getTaskEndTime() {
+		return taskEndTime;
+	}
+	public void setTaskEndTime(String taskEndTime) {
+		this.taskEndTime = taskEndTime;
+	}
+	public String getTaskExpireTime() {
+		return taskExpireTime;
+	}
+	public void setTaskExpireTime(String taskExpireTime) {
+		this.taskExpireTime = taskExpireTime;
+	}
+	public String getOperator() {
+		return operator;
+	}
+	public void setOperator(String operator) {
+		this.operator = operator;
+	}
+	public String getActionUrl() {
+		return actionUrl;
+	}
+	public void setActionUrl(String actionUrl) {
+		this.actionUrl = actionUrl;
+	}
+	public String[] getActorIds() {
+		return actorIds;
+	}
+	public void setActorIds(String[] actorIds) {
+		this.actorIds = actorIds;
+	}
+	public String getOrderVariable() {
+		return orderVariable;
+	}
+	public void setOrderVariable(String orderVariable) {
+		this.orderVariable = orderVariable;
+	}
+	public String getTaskVariable() {
+		return taskVariable;
+	}
+	public void setTaskVariable(String taskVariable) {
+		this.taskVariable = taskVariable;
+	}
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> getOrderVariableMap() {
+        Map<String, Object> map = JsonHelper.fromJson(this.orderVariable, Map.class);
+        if(map == null) return Collections.emptyMap();
+        return map;
+    }
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> getTaskVariableMap() {
+        Map<String, Object> map = JsonHelper.fromJson(this.taskVariable, Map.class);
+        if(map == null) return Collections.emptyMap();
+        return map;
+    }
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("WorkItem(processId=").append(this.processId);
+		sb.append(",orderId=").append(this.orderId);
+		sb.append(",taskId=").append(this.taskId);
+		sb.append(",processName").append(this.processName);
+		sb.append(",taskType=").append(this.taskType);
+		sb.append(",taskName").append(this.taskName);
+		sb.append(",performType=").append(this.performType).append(")");
+		return sb.toString();
+	}
+	public String getId() {
+		return id;
+	}
+	public void setId(String id) {
+		this.id = id;
+	}
+}

+ 30 - 0
workflowy/src/main/java/org/snaker/engine/handlers/IHandler.java

@@ -0,0 +1,30 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.handlers;
+
+import org.snaker.engine.core.Execution;
+
+/**
+ * 流程各模型操作处理接口
+ * @author yuqs
+ * @since 1.0
+ */
+public interface IHandler {
+	/**
+	 * 子类需要实现的方法,来处理具体的操作
+	 * @param execution 执行对象
+	 */
+	public void handle(Execution execution);
+}

+ 78 - 0
workflowy/src/main/java/org/snaker/engine/handlers/impl/AbstractMergeHandler.java

@@ -0,0 +1,78 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.handlers.impl;
+
+import java.util.List;
+
+import org.snaker.engine.IQueryService;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.handlers.IHandler;
+import org.snaker.engine.model.ProcessModel;
+import org.snaker.engine.model.SubProcessModel;
+import org.snaker.engine.model.TaskModel;
+
+/**
+ * 合并处理的抽象处理器
+ * 需要子类提供查询无法合并的task集合的参数map
+ * @author yuqs
+ * @since 1.0
+ */
+public abstract class AbstractMergeHandler implements IHandler {
+	public void handle(Execution execution) {
+		/**
+		 * 查询当前流程实例的无法参与合并的node列表
+		 * 若所有中间node都完成,则设置为已合并状态,告诉model可继续执行join的输出变迁
+		 */
+		IQueryService queryService = execution.getEngine().query();
+		Order order = execution.getOrder();
+		ProcessModel model = execution.getModel();
+		String[] activeNodes = findActiveNodes();
+		boolean isSubProcessMerged = false;
+		boolean isTaskMerged = false;
+		
+		if(model.containsNodeNames(SubProcessModel.class, activeNodes)) {
+			QueryFilter filter = new QueryFilter().setParentId(order.getId())
+					.setExcludedIds(new String[]{execution.getChildOrderId()});
+			List<Order> orders = queryService.getActiveOrders(filter);
+			//如果所有子流程都已完成,则表示可合并
+			if(orders == null || orders.isEmpty()) {
+				isSubProcessMerged = true;
+			}
+		} else {
+			isSubProcessMerged = true;
+		}
+		if(isSubProcessMerged && model.containsNodeNames(TaskModel.class, activeNodes)) {
+			QueryFilter filter = new QueryFilter().
+					setOrderId(order.getId()).
+					setExcludedIds(new String[]{execution.getTask().getId() }).
+					setNames(activeNodes);
+			List<Task> tasks = queryService.getActiveTasks(filter);
+			if(tasks == null || tasks.isEmpty()) {
+				//如果所有task都已完成,则表示可合并
+				isTaskMerged = true;
+			}
+		}
+		execution.setMerged(isSubProcessMerged && isTaskMerged);
+	}
+
+	/**
+	 * 子类需要提供如何查询未合并任务的参数map
+	 * @return
+	 */
+	protected abstract String[] findActiveNodes();
+}

+ 68 - 0
workflowy/src/main/java/org/snaker/engine/handlers/impl/CreateTaskHandler.java

@@ -0,0 +1,68 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.handlers.impl;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.SnakerInterceptor;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.core.ServiceContext;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.handlers.IHandler;
+import org.snaker.engine.model.TaskModel;
+
+/**
+ * 任务创建操作的处理器
+ * @author yuqs
+ * @since 1.0
+ */
+public class CreateTaskHandler implements IHandler {
+	private static final Logger log = LoggerFactory.getLogger(CreateTaskHandler.class);
+	/**
+	 * 任务模型
+	 */
+	private TaskModel model;
+	
+	/**
+	 * 调用者需要提供任务模型
+	 * @param model 模型
+	 */
+	public CreateTaskHandler(TaskModel model) {
+		this.model = model;
+	}
+	
+	/**
+	 * 根据任务模型、执行对象,创建下一个任务,并添加到execution对象的tasks集合中
+	 */
+	public void handle(Execution execution) {
+		List<Task> tasks = execution.getEngine().task().createTask(model, execution);
+		execution.addTasks(tasks);
+		/**
+		 * 从服务上下文中查找任务拦截器列表,依次对task集合进行拦截处理
+		 */
+		List<SnakerInterceptor> interceptors = ServiceContext.getContext().findList(SnakerInterceptor.class);
+		try {
+			for(SnakerInterceptor interceptor : interceptors) {
+				interceptor.intercept(execution);
+			}
+		} catch(Exception e) {
+			log.error("拦截器执行失败=" + e.getMessage());
+			throw new SnakerException(e);
+		}
+	}
+}

+ 73 - 0
workflowy/src/main/java/org/snaker/engine/handlers/impl/EndProcessHandler.java

@@ -0,0 +1,73 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.handlers.impl;
+
+import java.util.List;
+
+import org.snaker.engine.SnakerEngine;
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Process;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.handlers.IHandler;
+import org.snaker.engine.helper.StringHelper;
+import org.snaker.engine.model.ProcessModel;
+import org.snaker.engine.model.SubProcessModel;
+
+/**
+ * 结束流程实例的处理器
+ * @author yuqs
+ * @since 1.0
+ */
+public class EndProcessHandler implements IHandler {
+	/**
+	 * 结束当前流程实例,如果存在父流程,则触发父流程继续执行
+	 */
+	public void handle(Execution execution) {
+		SnakerEngine engine = execution.getEngine();
+		Order order = execution.getOrder();
+		List<Task> tasks = engine.query().getActiveTasks(new QueryFilter().setOrderId(order.getId()));
+		for(Task task : tasks) {
+			if(task.isMajor()) throw new SnakerException("存在未完成的主办任务,请确认.");
+			engine.task().complete(task.getId(), SnakerEngine.AUTO);
+		}
+		/**
+		 * 结束当前流程实例
+		 */
+		engine.order().complete(order.getId());
+		
+		/**
+		 * 如果存在父流程,则重新构造Execution执行对象,交给父流程的SubProcessModel模型execute
+		 */
+		if(StringHelper.isNotEmpty(order.getParentId())) {
+			Order parentOrder = engine.query().getOrder(order.getParentId());
+			if(parentOrder == null) return;
+			Process process = engine.process().getProcessById(parentOrder.getProcessId());
+			ProcessModel pm = process.getModel();
+			if(pm == null) return;
+			SubProcessModel spm = (SubProcessModel)pm.getNode(order.getParentNodeName());
+            Execution newExecution = new Execution(engine, process, parentOrder, execution.getArgs());
+            newExecution.setChildOrderId(order.getId());
+            newExecution.setTask(execution.getTask());
+			spm.execute(newExecution);
+			/**
+			 * SubProcessModel执行结果的tasks合并到当前执行对象execution的tasks列表中
+			 */
+			execution.addTasks(newExecution.getTasks());
+		}
+	}
+}

+ 43 - 0
workflowy/src/main/java/org/snaker/engine/handlers/impl/MergeActorHandler.java

@@ -0,0 +1,43 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.handlers.impl;
+
+/**
+ * actor all方式的合并处理器
+ * @author yuqs
+ * @since 1.0
+ */
+public class MergeActorHandler extends AbstractMergeHandler {
+	/**
+	 * 调用者需要提供actor all的任务名称
+	 */
+	private String taskName;
+	
+	/**
+	 * 构造函数,由调用者提供taskName
+	 * @param taskName
+	 */
+	public MergeActorHandler(String taskName) {
+		this.taskName = taskName;
+	}
+
+	/**
+	 * actor all方式,查询参数为:orderId、taskName
+	 * @see org.snaker.engine.handlers.impl.AbstractMergeHandler#findActiveNodes()
+	 */
+	protected String[] findActiveNodes() {
+		return new String[]{taskName};
+	}
+}

+ 62 - 0
workflowy/src/main/java/org/snaker/engine/handlers/impl/MergeBranchHandler.java

@@ -0,0 +1,62 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.handlers.impl;
+
+import java.util.List;
+
+import org.snaker.engine.model.ForkModel;
+import org.snaker.engine.model.JoinModel;
+import org.snaker.engine.model.NodeModel;
+import org.snaker.engine.model.TransitionModel;
+import org.snaker.engine.model.WorkModel;
+
+/**
+ * 合并分支操作的处理器
+ * @author yuqs
+ * @since 1.0
+ */
+public class MergeBranchHandler extends AbstractMergeHandler {
+	private JoinModel model;
+	public MergeBranchHandler(JoinModel model) {
+		this.model = model;
+	}
+	
+	/**
+	 * 对join节点的所有输入变迁进行递归,查找join至fork节点的所有中间task元素
+	 * @param node
+	 * @param buffer
+	 */
+	private void findForkTaskNames(NodeModel node, StringBuilder buffer) {
+		if(node instanceof ForkModel) return;
+		List<TransitionModel> inputs = node.getInputs();
+		for(TransitionModel tm : inputs) {
+			if(tm.getSource() instanceof WorkModel) {
+				buffer.append(tm.getSource().getName()).append(",");
+			}
+			findForkTaskNames(tm.getSource(), buffer);
+		}
+	}
+
+	/**
+	 * 对join节点的所有输入变迁进行递归,查找join至fork节点的所有中间task元素
+	 * @see org.snaker.engine.handlers.impl.AbstractMergeHandler#findActiveNodes()
+	 */
+	protected String[] findActiveNodes() {
+		StringBuilder buffer = new StringBuilder(20);
+		findForkTaskNames(model, buffer);
+		String[] taskNames = buffer.toString().split(",");
+		return taskNames;
+	}
+}

+ 106 - 0
workflowy/src/main/java/org/snaker/engine/handlers/impl/StartSubProcessHandler.java

@@ -0,0 +1,106 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.handlers.impl;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.snaker.engine.SnakerEngine;
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.access.QueryFilter;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.entity.Order;
+import org.snaker.engine.entity.Process;
+import org.snaker.engine.handlers.IHandler;
+import org.snaker.engine.helper.AssertHelper;
+import org.snaker.engine.model.SubProcessModel;
+
+/**
+ * 启动子流程的处理器
+ * @author yuqs
+ * @since 1.0
+ */
+public class StartSubProcessHandler implements IHandler {
+	private SubProcessModel model;
+	/**
+	 * 是否以future方式执行启动子流程任务
+	 */
+	private boolean isFutureRunning = false;
+	public StartSubProcessHandler(SubProcessModel model) {
+		this.model = model;
+	}
+	
+	public StartSubProcessHandler(SubProcessModel model, boolean isFutureRunning) {
+		this.model = model;
+		this.isFutureRunning = isFutureRunning;
+	}
+	
+	/**
+	 * 子流程执行的处理
+	 */
+	public void handle(Execution execution) {
+		//根据子流程模型名称获取子流程定义对象
+		SnakerEngine engine = execution.getEngine();
+		Process process = engine.process().getProcessByVersion(model.getProcessName(), model.getVersion());
+		
+		Execution child = execution.createSubExecution(execution, process, model.getName());
+		Order order = null;
+		if(isFutureRunning) {
+			//创建单个线程执行器来执行启动子流程的任务
+			ExecutorService es = Executors.newSingleThreadExecutor();
+			//提交执行任务,并返回future
+			Future<Order> future = es.submit(new ExecuteTask(execution, process, model.getName()));
+			try {
+				es.shutdown();
+				order = future.get();
+			} catch (InterruptedException e) {
+				throw new SnakerException("创建子流程线程被强制终止执行", e.getCause());
+			} catch (ExecutionException e) {
+				throw new SnakerException("创建子流程线程执行异常.", e.getCause());
+			}
+		} else {
+			order  = engine.startInstanceByExecution(child);
+		}
+		AssertHelper.notNull(order, "子流程创建失败");
+		execution.addTasks(engine.query().getActiveTasks(new QueryFilter().setOrderId(order.getId())));
+	}
+
+	/**
+	 * Future模式的任务执行。通过call返回任务结果集
+	 * @author yuqs
+	 * @since 1.0
+	 */
+	class ExecuteTask implements Callable<Order> {
+		private SnakerEngine engine;
+		private Execution child;
+		/**
+		 * 构造函数
+		 * @param execution
+		 * @param process
+		 * @param parentNodeName
+		 */
+		public ExecuteTask(Execution execution, Process process,String parentNodeName) {
+			this.engine = execution.getEngine();
+			child = execution.createSubExecution(execution, process, parentNodeName);
+		}
+		
+		public Order call() throws Exception {
+			return engine.startInstanceByExecution(child);
+		}
+	}
+}

+ 98 - 0
workflowy/src/main/java/org/snaker/engine/helper/AssertHelper.java

@@ -0,0 +1,98 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.helper;
+
+/**
+ * 断言帮助类
+ * @author yuqs
+ * @since 1.0
+ */
+public abstract class AssertHelper {
+	/**
+	 * 断言表达式为true
+	 * @param expression
+	 * @param message 异常打印信息
+	 */
+	public static void isTrue(boolean expression, String message) {
+		if (!expression) {
+			throw new IllegalArgumentException(message);
+		}
+	}
+
+	/**
+	 * 断言表达式为true
+	 * @param expression
+	 */
+	public static void isTrue(boolean expression) {
+		isTrue(expression, "[Assertion failed] - this expression must be true");
+	}
+
+	/**
+	 * 断言给定的object对象为空
+	 * @param object
+	 * @param message 异常打印信息
+	 */
+	public static void isNull(Object object, String message) {
+		if (object != null) {
+			throw new IllegalArgumentException(message);
+		}
+	}
+
+	/**
+	 * 断言给定的object对象为空
+	 * @param object
+	 */
+	public static void isNull(Object object) {
+		isNull(object, "[Assertion failed] - the object argument must be null");
+	}
+
+	/**
+	 * 断言给定的object对象为非空
+	 * @param object
+	 * @param message 异常打印信息
+	 */
+	public static void notNull(Object object, String message) {
+		if (object == null) {
+			throw new IllegalArgumentException(message);
+		}
+	}
+
+	/**
+	 * 断言给定的object对象为非空
+	 * @param object
+	 */
+	public static void notNull(Object object) {
+		notNull(object, "[Assertion failed] - this argument is required; it must not be null");
+	}
+	
+	/**
+	 * 断言给定的字符串为非空
+	 * @param str
+	 */
+	public static void notEmpty(String str) {
+		notEmpty(str, "[Assertion failed] - this argument is required; it must not be null or empty");
+	}
+	
+	/**
+	 * 断言给定的字符串为非空
+	 * @param str
+	 * @param message
+	 */
+	public static void notEmpty(String str, String message) {
+		if (str == null || str.length() == 0) {
+			throw new IllegalArgumentException(message);
+		}
+	}
+}

+ 112 - 0
workflowy/src/main/java/org/snaker/engine/helper/ClassHelper.java

@@ -0,0 +1,112 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.helper;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 类操作帮助类
+ * @author yuqs
+ * @since 1.0
+ */
+public class ClassHelper {
+    private static final Logger log = LoggerFactory.getLogger(ClassHelper.class);
+	/**
+	 * 查询结果总记录数的类型转换
+	 * @param count
+	 * @return
+	 */
+	public static long castLong(Object count) {
+		if(count == null) return -1L;
+		if(count instanceof Long) {
+			return (Long)count;
+		} else if(count instanceof BigDecimal) {
+			return ((BigDecimal)count).longValue();
+		} else if(count instanceof Integer) {
+			return ((Integer)count).longValue();
+		} else if(count instanceof BigInteger) {
+			return ((BigInteger)count).longValue();
+		} else if(count instanceof Byte) {
+			return ((Byte)count).longValue();
+        } else if(count instanceof Short) {
+            return ((Short)count).longValue();
+		} else {
+			return -1L;
+		}
+	}
+    
+    /**
+     * 根据指定的类名称加载类
+     * @param className
+     * @return
+     * @throws ClassNotFoundException
+     */
+    public static Class<?> loadClass(String className) throws ClassNotFoundException {
+        try {
+            return Thread.currentThread().getContextClassLoader().loadClass(className);
+        } catch (ClassNotFoundException e) {
+            try {
+                return Class.forName(className);
+            } catch (ClassNotFoundException ex) {
+                try {
+                    return ClassLoader.class.getClassLoader().loadClass(className);
+                } catch (ClassNotFoundException exc) {
+                    throw exc;
+                }
+            }
+        }
+    }
+    
+    /**
+     * 实例化指定的类名称(全路径)
+     * @param clazzStr
+     * @return
+     * @throws Exception
+     */
+    public static Object newInstance(String clazzStr) {
+        try {
+        	log.debug("loading class:" + clazzStr);
+            Class<?> clazz = loadClass(clazzStr);
+            return instantiate(clazz);
+        } catch (ClassNotFoundException e) {
+            log.error("Class not found.", e);
+        } catch (Exception ex) {
+        	log.error("类型实例化失败[class=" + clazzStr + "]\n" + ex.getMessage());
+        }
+        return null;
+    }
+    
+    /**
+     * 根据类的class实例化对象
+     * @param clazz
+     * @return
+     */
+	public static <T> T instantiate(Class<T> clazz) {
+		if (clazz.isInterface()) {
+			log.error("所传递的class类型参数为接口,无法实例化");
+			return null;
+		}
+		try {
+			return clazz.newInstance();
+		} catch (Exception ex) {
+			log.error("检查传递的class类型参数是否为抽象类?", ex.getCause());
+		}
+		return null;
+	}
+}

+ 77 - 0
workflowy/src/main/java/org/snaker/engine/helper/DateHelper.java

@@ -0,0 +1,77 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.helper;
+
+import java.util.Date;
+import java.util.Map;
+
+import org.joda.time.DateTime;
+
+/**
+ * 日期帮助类
+ * @author yuqs
+ * @since 1.0
+ */
+public class DateHelper {
+	private static final String DATE_FORMAT_DEFAULT = "yyyy-MM-dd HH:mm:ss";
+	
+	/**
+	 * 返回标准格式的当前时间
+	 * @return
+	 */
+	public static String getTime() {
+		return new DateTime().toString(DATE_FORMAT_DEFAULT);
+	}
+	
+	/**
+	 * 解析日期时间对象
+	 * @param date
+	 * @return
+	 */
+	public static String parseTime(Object date) {
+		if(date == null) return null;
+		if(date instanceof Date) {
+			return new DateTime((Date)date).toString(DATE_FORMAT_DEFAULT);
+		} else if(date instanceof String) {
+			return String.valueOf(date);
+		}
+		return "";
+	}
+	
+	/**
+	 * 对时限数据进行处理
+	 * 1、运行时设置的date型数据直接返回
+	 * 2、模型设置的需要特殊转换成date类型
+	 * 3、运行时设置的转换为date型
+	 * @param args 运行时参数
+	 * @param parameter 模型参数
+	 * @return Date类型
+	 */
+	public static Date processTime(Map<String, Object> args, String parameter) {
+		if(StringHelper.isEmpty(parameter)) return null;
+		Object data = args.get(parameter);
+		if(data == null) data = parameter;
+		
+		Date result = null;
+		if(data instanceof Date) {
+			return (Date)data;
+		} else if(data instanceof Long) {
+			return new Date((Long)data);
+		} else if(data instanceof String) {
+			//TODO 1.4-dev ignore
+		}
+		return result;
+	}
+}

+ 65 - 0
workflowy/src/main/java/org/snaker/engine/helper/JsonHelper.java

@@ -0,0 +1,65 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.helper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.codehaus.jackson.map.ObjectMapper;
+
+/**
+ * json处理帮助类
+ * @author yuqs
+ * @since 1.0
+ */
+public class JsonHelper {
+	private static final Logger log = LoggerFactory.getLogger(JsonHelper.class);
+	/**
+	 * jackson的ObjectMapper对象
+	 */
+	private static ObjectMapper mapper = new ObjectMapper();
+	/**
+	 * 将对象转换为json字符串
+	 * @param object
+	 * @return
+	 */
+	public static String toJson(Object object) {
+		if(object == null) return "";
+		try {
+			return mapper.writeValueAsString(object);
+		} catch (Exception e) {
+			log.warn("write to json string error:" + object, e);
+			return "";
+		}
+	}
+	
+	/**
+	 * 根据指定类型解析json字符串,并返回该类型的对象
+	 * @param jsonString
+	 * @param clazz
+	 * @return
+	 */
+	public static <T> T fromJson(String jsonString, Class<T> clazz) {
+		if (StringHelper.isEmpty(jsonString)) {
+			return null;
+		}
+
+		try {
+			return mapper.readValue(jsonString, clazz);
+		} catch (Exception e) {
+			log.warn("parse json string error:" + jsonString, e);
+			return null;
+		}
+	}
+}

+ 141 - 0
workflowy/src/main/java/org/snaker/engine/helper/ReflectHelper.java

@@ -0,0 +1,141 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.helper;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import org.snaker.engine.SnakerException;
+
+/**
+ * 反射帮助类
+ * @author yuqs
+ * @since 1.0
+ */
+public class ReflectHelper {
+	/**
+	 * 利用反射获取指定对象的指定属性
+	 * @param obj 目标对象
+	 * @param fieldName 目标属性
+	 * @return 目标属性的值
+	 */
+	public static Object getFieldValue(Object obj, String fieldName) {
+		Object result = null;
+		Field field = getField(obj, fieldName);
+		if (field != null) {
+			field.setAccessible(true);
+			try {
+				result = field.get(obj);
+			} catch (IllegalArgumentException e) {
+				e.printStackTrace();
+			} catch (IllegalAccessException e) {
+				e.printStackTrace();
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * 利用反射获取指定对象里面的指定属性
+	 * @param obj 目标对象
+	 * @param fieldName 目标属性
+	 * @return 目标字段
+	 */
+	private static Field getField(Object obj, String fieldName) {
+		Field field = null;
+		for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz
+				.getSuperclass()) {
+			try {
+				field = clazz.getDeclaredField(fieldName);
+				break;
+			} catch (NoSuchFieldException e) {
+				//ignore exception
+			}
+		}
+		return field;
+	}
+
+	/**
+	 * 利用反射设置指定对象的指定属性为指定的值
+	 * @param obj 目标对象
+	 * @param fieldName 目标属性
+	 * @param fieldValue 目标值
+	 */
+	public static void setFieldValue(Object obj, String fieldName,
+			Object fieldValue) {
+		Field field = getField(obj, fieldName);
+		if (field != null) {
+			try {
+				field.setAccessible(true);
+				field.set(obj, fieldValue);
+			} catch (IllegalArgumentException e) {
+				e.printStackTrace();
+			} catch (IllegalAccessException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+	
+	/**
+	 * 根据指定的对象、方法、参数反射调用,并返回调用结果
+	 * @param method 方法
+	 * @param target 对象
+	 * @param args 参数数组
+	 * @return 方法调用的返回数据
+	 */
+	public static Object invoke(Method method, Object target, Object[] args) {
+		if (method == null) {
+			throw new SnakerException("方法不能为空");
+		}
+		try {
+			if (!method.isAccessible()) {
+				method.setAccessible(true);
+			}
+			return method.invoke(target, args);
+		} catch (InvocationTargetException e) {
+			Throwable targetException = e.getTargetException();
+			throw new SnakerException("不能调用 '" + method.getName() + "' with "
+					+ Arrays.toString(args) + " on " + target + ": "
+					+ targetException.getMessage(), targetException);
+		} catch (Exception e) {
+			throw new SnakerException("不能调用 '" + method.getName() + "' with "
+					+ Arrays.toString(args) + " on " + target + ": "
+					+ e.getMessage(), e);
+		}
+	}
+	
+	/**
+	 * 根据class类型、methodName方法名称,返回Method对象。
+	 * 注意:这里不检查参数类型,所以自定义的java类应该避免使用重载方法
+	 * @param clazz
+	 * @param methodName
+	 * @return
+	 */
+	public static Method findMethod(Class<?> clazz, String methodName) {
+		Method[] candidates = clazz.getDeclaredMethods();
+		for (int i = 0; i < candidates.length; i++) {
+			Method candidate = candidates[i];
+			if (candidate.getName().equals(methodName)) {
+				return candidate;
+			}
+		}
+		if (clazz.getSuperclass() != null) {
+			return findMethod(clazz.getSuperclass(), methodName);
+		}
+		return null;
+	}
+}

+ 172 - 0
workflowy/src/main/java/org/snaker/engine/helper/StreamHelper.java

@@ -0,0 +1,172 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.helper;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URL;
+
+import org.snaker.engine.SnakerException;
+
+/**
+ * 流数据帮助类
+ * @author yuqs
+ * @since 1.0
+ */
+public class StreamHelper {
+	public static final int DEFAULT_CHUNK_SIZE = 1024;
+	public static final int BUFFERSIZE = 4096;
+	/**
+	 * 根据文件名称resource打开输入流,并返回
+	 * @param resource
+	 * @return
+	 */
+	public static InputStream openStream(String resource) {
+		ClassLoader classLoader = Thread.currentThread()
+				.getContextClassLoader();
+		InputStream stream = classLoader.getResourceAsStream(resource);
+
+		if (stream == null) {
+			stream = StreamHelper.class.getClassLoader().getResourceAsStream(resource);
+		}
+
+		return stream;
+	}
+	
+	public static InputStream getStreamFromString(String text) {
+		try {
+			byte[] bytes = text.getBytes("UTF-8");
+			return new ByteArrayInputStream(bytes);
+		} catch (Exception e) {
+			throw new AssertionError(e);
+		}
+	}
+
+	public static InputStream getStreamFromFile(File file) {
+		InputStream stream = null;
+		try {
+			if (!file.exists()) {
+				throw new SnakerException("file " + file + " doesn't exist");
+			}
+			if (file.isDirectory()) {
+				throw new SnakerException("file " + file + " is a directory");
+			}
+			stream = new FileInputStream(file);
+
+		} catch (Exception e) {
+			throw new SnakerException("couldn't access file " + file + ": "
+					+ e.getMessage(), e);
+		}
+		return stream;
+	}
+
+	public static InputStream getStreamFromClasspath(String resourceName) {
+		ClassLoader classLoader = Thread.currentThread()
+				.getContextClassLoader();
+		InputStream stream = classLoader.getResourceAsStream(resourceName);
+
+		if (stream == null) {
+			stream = StreamHelper.class.getClassLoader().getResourceAsStream(
+					resourceName);
+		}
+
+		if (stream == null) {
+			throw new SnakerException("resource " + resourceName
+					+ " does not exist");
+		}
+		return stream;
+	}
+
+	public static InputStream getStreamFromUrl(URL url) {
+		InputStream stream = null;
+		try {
+			stream = url.openStream();
+		} catch (IOException e) {
+			throw new SnakerException("couldn't open URL stream", e);
+		}
+		return stream;
+	}
+	
+	public static byte[] readBytes(InputStream in) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		transfer(in, out);
+		return out.toByteArray();
+	}
+
+	public static long transfer(InputStream in, OutputStream out)
+			throws IOException {
+		long total = 0;
+		byte[] buffer = new byte[BUFFERSIZE];
+		for (int count; (count = in.read(buffer)) != -1;) {
+			out.write(buffer, 0, count);
+			total += count;
+		}
+		return total;
+	}
+	
+	/**
+	 * input->output字节流copy
+	 * @param inputStream
+	 * @param outputStream
+	 * @return
+	 * @throws IOException
+	 */
+	public static long copy(InputStream inputStream, OutputStream outputStream) throws IOException {
+		return copy( inputStream, outputStream, DEFAULT_CHUNK_SIZE );
+	}
+
+	/**
+	 * input->output字节流copy
+	 * @param inputStream
+	 * @param outputStream
+	 * @param bufferSize
+	 * @return
+	 * @throws IOException
+	 */
+	public static long copy(InputStream inputStream, OutputStream outputStream, int bufferSize) throws IOException {
+		byte[] buffer = new byte[bufferSize];
+		long count = 0;
+		int n;
+		while ( -1 != ( n = inputStream.read( buffer ) ) ) {
+			outputStream.write( buffer, 0, n );
+			count += n;
+		}
+		return count;
+
+	}
+
+	public static long copy(Reader reader, Writer writer) throws IOException {
+		return copy( reader, writer, DEFAULT_CHUNK_SIZE );
+	}
+
+	public static long copy(Reader reader, Writer writer, int bufferSize) throws IOException {
+		char[] buffer = new char[bufferSize];
+		long count = 0;
+		int n;
+		while ( -1 != ( n = reader.read( buffer ) ) ) {
+			writer.write( buffer, 0, n );
+			count += n;
+		}
+		return count;
+
+	}
+}

+ 140 - 0
workflowy/src/main/java/org/snaker/engine/helper/StringHelper.java

@@ -0,0 +1,140 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.helper;
+
+import org.apache.commons.lang.StringUtils;
+import org.snaker.engine.SnakerException;
+
+/**
+ * 字符串处理帮助类
+ * @author yuqs
+ * @since 1.0
+ */
+public class StringHelper {
+	/**
+	 * 获取uuid类型的字符串
+	 * @return uuid字符串
+	 */
+	public static String getPrimaryKey() {
+		return java.util.UUID.randomUUID().toString().replace("-", "");
+	}
+	
+	/**
+	 * 判断字符串是否为空
+	 * @param str 字符串
+	 * @return 是否为空标识
+	 */
+	public static boolean isEmpty(String str) {
+		return str == null || str.length() == 0;
+	}
+	
+	/**
+	 * 判断字符串是否为非空
+	 * @param str 字符串
+	 * @return 是否为非空标识
+	 */
+	public static boolean isNotEmpty(String str) {
+		return !isEmpty(str);
+	}
+	
+	/**
+	 * 根据字符串数组返回逗号分隔的字符串值
+	 * @param strArray 字符串数组
+	 * @return 逗号分隔的字符串
+	 */
+	public static String getStringByArray(String... strArray) {
+		if(strArray == null) return "";
+		StringBuilder buffer = new StringBuilder(strArray.length * 10);
+		for(String str : strArray) {
+			buffer.append(str).append(",");
+		}
+		buffer.deleteCharAt(buffer.length() - 1);
+		return buffer.toString();
+	}
+	
+	/**
+	 * xml内容特殊符号替换
+	 * @param xml xml字符串
+	 * @return 替换后的xml
+	 */
+	public static String textXML(String xml) {
+		if(xml == null) return "";
+		String content = xml;
+		content = content.replaceAll("<", "&lt;");
+		content = content.replaceAll(">", "&gt;");
+		content = content.replaceAll("\"", "&quot;");
+		content = content.replaceAll("\n", "</br>");
+		return content;
+	}
+	
+	/**
+	 * 构造排序条件
+	 * @param order 排序方向
+	 * @param orderby 排序字段
+	 * @return 组装好的排序sql
+	 */
+	public static String buildPageOrder(String order, String orderby) {
+		if(isEmpty(orderby) || isEmpty(order)) return "";
+		String[] orderByArray = StringUtils.split(orderby, ',');
+		String[] orderArray = StringUtils.split(order, ',');
+		if(orderArray.length != orderByArray.length) throw new SnakerException("分页多重排序参数中,排序字段与排序方向的个数不相等");
+		StringBuilder orderStr = new StringBuilder(30);
+		orderStr.append(" order by ");
+
+		for (int i = 0; i < orderByArray.length; i++) {
+			orderStr.append(orderByArray[i]).append(" ").append(orderArray[i]).append(" ,");
+		}
+		orderStr.deleteCharAt(orderStr.length() - 1);
+		return orderStr.toString();
+	}
+	
+	/**
+	 * 简单字符串匹配方法,支持匹配类型为:
+	 * *what *what* what*
+	 * @param pattern 匹配模式
+	 * @param str 字符串
+	 * @return 是否匹配
+	 */
+	public static boolean simpleMatch(String pattern, String str) {
+		if (pattern == null || str == null) {
+			return false;
+		}
+		int firstIndex = pattern.indexOf('*');
+		if (firstIndex == -1) {
+			return pattern.equals(str);
+		}
+		if (firstIndex == 0) {
+			if (pattern.length() == 1) {
+				return true;
+			}
+			int nextIndex = pattern.indexOf('*', firstIndex + 1);
+			if (nextIndex == -1) {
+				return str.endsWith(pattern.substring(1));
+			}
+			String part = pattern.substring(1, nextIndex);
+			int partIndex = str.indexOf(part);
+			while (partIndex != -1) {
+				if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()))) {
+					return true;
+				}
+				partIndex = str.indexOf(part, partIndex + 1);
+			}
+			return false;
+		}
+		return (str.length() >= firstIndex &&
+				pattern.substring(0, firstIndex).equals(str.substring(0, firstIndex)) &&
+				simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex)));
+	}
+}

+ 81 - 0
workflowy/src/main/java/org/snaker/engine/helper/XmlHelper.java

@@ -0,0 +1,81 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.helper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * xml解析的帮助类
+ * @author yuqs
+ * @since 1.0
+ */
+public class XmlHelper {
+	/**
+	 * DocumentBuilderFactory实例
+	 */
+	private static DocumentBuilderFactory documentBuilderFactory = createDocumentBuilderFactory();
+	
+	/**
+	 * 获取DocumentBuilderFactory
+	 * @return
+	 */
+	private static DocumentBuilderFactory createDocumentBuilderFactory() {
+		return DocumentBuilderFactory.newInstance();
+	}
+
+	/**
+	 * 由DocumentBuilderFactory产生DocumentBuilder实例
+	 * @return
+	 */
+	public static DocumentBuilder createDocumentBuilder() {
+		try {
+			return documentBuilderFactory.newDocumentBuilder();
+		} catch (ParserConfigurationException e) {
+			return null;
+		}
+	}
+	
+	/**
+	 * 从element元素查找所有tagName指定的子节点元素集合
+	 * @param element
+	 * @param tagName
+	 * @return
+	 */
+	public static List<Element> elements(Element element, String tagName) {
+		if (element == null || !element.hasChildNodes()) {
+			return Collections.emptyList();
+		}
+
+		List<Element> elements = new ArrayList<Element>();
+		for (Node child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
+			if (child.getNodeType() == Node.ELEMENT_NODE) {
+				Element childElement = (Element) child;
+				String childTagName = childElement.getNodeName();
+				if (tagName.equals(childTagName))
+					elements.add(childElement);
+			}
+		}
+		return elements;
+	}
+}

+ 38 - 0
workflowy/src/main/java/org/snaker/engine/impl/DefaultNoGenerator.java

@@ -0,0 +1,38 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.impl;
+
+import java.util.Random;
+
+import org.joda.time.DateTime;
+import org.snaker.engine.INoGenerator;
+import org.snaker.engine.model.ProcessModel;
+import org.springframework.stereotype.Component;
+
+/**
+ * 默认的流程实例编号生成器
+ * 编号生成规则为:yyyyMMdd-HH:mm:ss-SSS-random
+ * @author yuqs
+ * @since 1.0
+ */
+@Component
+public class DefaultNoGenerator implements INoGenerator {
+	public String generate(ProcessModel model) {
+		DateTime dateTime = new DateTime();
+		String time = dateTime.toString("yyyyMMdd-HH:mm:ss-SSS");
+		Random random = new Random();
+		return time + "-" + random.nextInt(1000);
+	}
+}

+ 59 - 0
workflowy/src/main/java/org/snaker/engine/impl/GeneralAccessStrategy.java

@@ -0,0 +1,59 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.snaker.engine.TaskAccessStrategy;
+import org.snaker.engine.entity.TaskActor;
+import org.springframework.stereotype.Component;
+
+/**
+ * 基于用户或组(角色、部门等)的访问策略类
+ * 该策略类适合组作为参与者的情况
+ * @author yuqs
+ * @since 1.4
+ */
+@Component
+public class GeneralAccessStrategy implements TaskAccessStrategy {
+	/**
+	 * 根据操作人id确定所有的组集合
+	 * @param operator 操作人id
+	 * @return List<String> 确定的组集合[如操作人属于多个部门、拥有多个角色]
+	 */
+	protected List<String> ensureGroup(String operator) {
+		return null;
+	}
+	
+	/**
+	 * 如果操作人id所属的组只要有一项存在于参与者集合中,则表示可访问
+	 */
+	public boolean isAllowed(String operator, List<TaskActor> actors) {
+		List<String> assignees = ensureGroup(operator);
+		if(assignees == null) assignees = new ArrayList<String>();
+		assignees.add(operator);
+		boolean isAllowed = false;
+		for (TaskActor actor : actors) {
+			for (String assignee : assignees) {
+				if (actor.getActorId().equals(assignee)) {
+					isAllowed = true;
+					break;
+				}
+			}
+		}
+		return isAllowed;
+	}
+}

+ 43 - 0
workflowy/src/main/java/org/snaker/engine/impl/GeneralCompletion.java

@@ -0,0 +1,43 @@
+/*
+ *  Copyright 2013-2015 www.snakerflow.com.
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package org.snaker.engine.impl;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.Completion;
+import org.snaker.engine.entity.HistoryOrder;
+import org.snaker.engine.entity.HistoryTask;
+import org.springframework.stereotype.Component;
+
+/**
+ * 默认的任务、实例完成时触发的动作
+ * @author yuqs
+ * @since 2.2.0
+ */
+@Component
+public class GeneralCompletion implements Completion {
+    private static final Logger log = LoggerFactory.getLogger(GeneralCompletion.class);
+
+    public void complete(HistoryTask task) {
+        log.info("The task[{}] has been user[{}] has completed", task.getId(), task.getOperator());
+    }
+
+    public void complete(HistoryOrder order) {
+        log.info("The order[{}] has completed", order.getId());
+    }
+}

+ 53 - 0
workflowy/src/main/java/org/snaker/engine/impl/LogInterceptor.java

@@ -0,0 +1,53 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.impl;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.SnakerInterceptor;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.entity.Task;
+import org.springframework.stereotype.Component;
+
+/**
+ * 日志拦截器
+ * @author yuqs
+ * @since 1.0
+ */
+@Component
+public class LogInterceptor implements SnakerInterceptor {
+	private static final Logger log = LoggerFactory.getLogger(LogInterceptor.class);
+	/**
+	 * 拦截产生的任务对象,打印日志
+	 */
+	public void intercept(Execution execution) {
+		if(log.isInfoEnabled()) {
+			for(Task task : execution.getTasks()) {
+				StringBuffer buffer = new StringBuffer(100);
+				buffer.append("创建任务[标识=").append(task.getId());
+				buffer.append(",名称=").append(task.getDisplayName());
+				buffer.append(",创建时间=").append(task.getCreateTime());
+				buffer.append(",参与者={");
+				if(task.getActorIds() != null) {
+					for(String actor : task.getActorIds()) {
+						buffer.append(actor).append(";");
+					}
+				}
+				buffer.append("}]");
+				log.info(buffer.toString());
+			}
+		}
+	}
+}

+ 99 - 0
workflowy/src/main/java/org/snaker/engine/impl/SchedulerInterceptor.java

@@ -0,0 +1,99 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.impl;
+
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.commons.lang.math.NumberUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.SnakerInterceptor;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.core.ServiceContext;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.model.TaskModel;
+import org.snaker.engine.scheduling.IScheduler;
+import org.snaker.engine.scheduling.JobEntity;
+import org.snaker.engine.scheduling.JobEntity.JobType;
+import org.springframework.stereotype.Component;
+
+/**
+ * 时限控制拦截器
+ * 主要拦截任务的expireDate(期望完成时间)
+ * 再交给具体的调度器完成调度处理
+ * @author yuqs
+ * @since 1.4
+ */
+@Component
+public class SchedulerInterceptor implements SnakerInterceptor {
+	private static final Logger log = LoggerFactory.getLogger(SchedulerInterceptor.class);
+	/**
+	 * 调度器接口
+	 */
+	private IScheduler scheduler;
+	/**
+	 * 是否调度
+	 */
+	private boolean isScheduled = true;
+	/**
+	 * 时限控制拦截方法
+	 */
+	public void intercept(Execution execution) {
+		if(!isScheduled) return;
+		for(Task task : execution.getTasks()) {
+			String id = execution.getProcess().getId() 
+					+ "-" + execution.getOrder().getId() 
+					+ "-" + task.getId();
+			Date expireDate = task.getExpireDate();
+			if(expireDate != null) {
+				schedule(id, task, expireDate, JobType.EXECUTER.ordinal(), execution.getArgs());
+			}
+			Date remindDate = task.getRemindDate();
+			if(remindDate != null) {
+				schedule(id, task, remindDate, JobType.REMINDER.ordinal(), execution.getArgs());
+			}
+		}
+	}
+	
+	public void schedule(String id, Task task, Date startDate, int jobType, Map<String, Object> args) {
+		try {
+		    JobEntity entity = new JobEntity(id, task, startDate, args);
+		    entity.setModelName(task.getTaskName());
+		    entity.setJobType(jobType);
+		    if(jobType == JobType.REMINDER.ordinal()) {
+		    	TaskModel model = (TaskModel)task.getModel();
+		    	if(model != null && NumberUtils.isNumber(model.getReminderRepeat())) {
+		    		entity.setPeriod(Integer.parseInt(model.getReminderRepeat()));
+		    	}
+		    }
+		    schedule(entity);
+		} catch (Exception e) {
+			log.error(e.getMessage());
+			log.info("scheduler failed.task is:" + task);
+		}
+	}
+	
+	private void schedule(JobEntity entity) {
+	    if(scheduler == null) {
+	    	scheduler = ServiceContext.getContext().find(IScheduler.class);
+	    }
+	    if(scheduler != null) {
+	    	scheduler.schedule(entity);
+	    } else {
+	    	isScheduled = false;
+	    }
+	}
+}

+ 43 - 0
workflowy/src/main/java/org/snaker/engine/impl/SpelExpression.java

@@ -0,0 +1,43 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.impl;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.snaker.engine.Expression;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring el表达式解析器
+ * @author yuqs
+ * @since 1.2.2
+ */
+@Component
+public class SpelExpression implements Expression {
+	ExpressionParser parser = new SpelExpressionParser();
+	
+	public <T> T eval(Class<T> T, String expr, Map<String, Object> args) {
+		EvaluationContext context = new StandardEvaluationContext();
+		for(Entry<String, Object> entry : args.entrySet()) {
+			context.setVariable(entry.getKey(), entry.getValue());
+		}
+		return parser.parseExpression(expr).getValue(context, T);
+	}
+}

+ 50 - 0
workflowy/src/main/java/org/snaker/engine/impl/SurrogateInterceptor.java

@@ -0,0 +1,50 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.impl;
+
+import org.snaker.engine.ITaskService;
+import org.snaker.engine.SnakerEngine;
+import org.snaker.engine.SnakerInterceptor;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.entity.Task;
+import org.snaker.engine.helper.StringHelper;
+import org.springframework.stereotype.Component;
+
+/**
+ * 委托代理拦截器
+ * 负责查询wf_surrogate表获取委托代理人,并通过addTaskActor设置为参与者
+ * 这里是对新创建的任务通过添加参与者进行委托代理(即授权人、代理人都可处理任务)
+ * 对于运行中且未处理的待办任务,可调用engine.task().addTaskActor方法
+ * {@link ITaskService#addTaskActor(String, String...)}
+ * @author yuqs
+ * @since 1.4
+ */
+@Component
+public class SurrogateInterceptor implements SnakerInterceptor {
+	public void intercept(Execution execution) {
+		SnakerEngine engine = execution.getEngine();
+		for(Task task : execution.getTasks()) {
+			if(task.getActorIds() == null) continue;
+			for(String actor : task.getActorIds()) {
+				if(StringHelper.isEmpty(actor)) continue;
+				String agent = engine.manager().getSurrogate(actor, execution.getProcess().getName());
+				if(StringHelper.isNotEmpty(agent) && !actor.equals(agent)) {
+					engine.task().addTaskActor(task.getId(), agent);
+				}
+			}
+		}
+	}
+
+}

+ 62 - 0
workflowy/src/main/java/org/snaker/engine/model/BaseModel.java

@@ -0,0 +1,62 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import java.io.Serializable;
+
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.handlers.IHandler;
+
+/**
+ * 模型元素基类
+ * @author yuqs
+ * @since 1.0
+ */
+public class BaseModel implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 3082741431225739241L;
+	/**
+	 * 元素名称
+	 */
+	private String name;
+	/**
+	 * 显示名称
+	 */
+	private String displayName;
+	
+	/**
+	 * 将执行对象execution交给具体的处理器处理
+	 * @param handler
+	 * @param execution
+	 */
+	protected void fire(IHandler handler, Execution execution) {
+		handler.handle(execution);
+	}
+	
+	public String getName() {
+		return name;
+	}
+	public void setName(String name) {
+		this.name = name;
+	}
+	public String getDisplayName() {
+		return displayName;
+	}
+	public void setDisplayName(String displayName) {
+		this.displayName = displayName;
+	}
+}

+ 128 - 0
workflowy/src/main/java/org/snaker/engine/model/CustomModel.java

@@ -0,0 +1,128 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.handlers.IHandler;
+import org.snaker.engine.helper.ClassHelper;
+import org.snaker.engine.helper.ReflectHelper;
+import org.snaker.engine.helper.StringHelper;
+
+/**
+ * 自定义模型
+ * @author yuqs
+ * @since 1.0
+ */
+public class CustomModel extends WorkModel {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 8796192915721758769L;
+
+	/**
+	 * 需要执行的class类路径
+	 */
+	private String clazz;
+	/**
+	 * 需要执行的class对象的方法名称
+	 */
+	private String methodName;
+	/**
+	 * 执行方法时传递的参数表达式变量名称
+	 */
+	private String args;
+	/**
+	 * 执行的返回值变量
+	 */
+	private String var;
+	/**
+	 * 加载模型时初始化的对象实例
+	 */
+	private Object invokeObject;
+	
+	public void exec(Execution execution) {
+		if(invokeObject == null) {
+			invokeObject = ClassHelper.newInstance(clazz);
+		}
+		if(invokeObject == null) {
+			throw new SnakerException("自定义模型[class=" + clazz + "]实例化对象失败");
+		}
+		
+		if(invokeObject instanceof IHandler) {
+			IHandler handler = (IHandler)invokeObject;
+			handler.handle(execution);
+		} else {
+			Method method = ReflectHelper.findMethod(invokeObject.getClass(), methodName);
+			if(method == null) {
+				throw new SnakerException("自定义模型[class=" + clazz + "]无法找到方法名称:" + methodName);
+			}
+			Object[] objects = getArgs(execution.getArgs(), args);
+			Object returnValue = ReflectHelper.invoke(method, invokeObject, objects);
+			if(StringHelper.isNotEmpty(var)) {
+				execution.getArgs().put(var, returnValue);
+			}
+		}
+		execution.getEngine().task().history(execution, this);
+		runOutTransition(execution);
+	}
+	
+	/**
+	 * 根据传递的执行参数、模型的参数列表返回实际的参数对象数组
+	 * @param execArgs 运行时传递的参数数据
+	 * @param args 自定义节点需要的参数
+	 * @return 调用自定义节点类方法的参数数组
+	 */
+	private Object[] getArgs(Map<String, Object> execArgs, String args) {
+		Object[] objects = null;
+		if(StringHelper.isNotEmpty(args)) {
+			String[] argArray = args.split(",");
+			objects = new Object[argArray.length];
+			for(int i = 0; i < argArray.length; i++) {
+				objects[i] = execArgs.get(argArray[i]);
+			}
+		}
+		return objects;
+	}
+	
+	public String getClazz() {
+		return clazz;
+	}
+	public void setClazz(String clazz) {
+		this.clazz = clazz;
+	}
+	public String getMethodName() {
+		return methodName;
+	}
+	public void setMethodName(String methodName) {
+		this.methodName = methodName;
+	}
+	public String getArgs() {
+		return args;
+	}
+	public void setArgs(String args) {
+		this.args = args;
+	}
+	public String getVar() {
+		return var;
+	}
+	public void setVar(String var) {
+		this.var = var;
+	}
+}

+ 106 - 0
workflowy/src/main/java/org/snaker/engine/model/DecisionModel.java

@@ -0,0 +1,106 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.DecisionHandler;
+import org.snaker.engine.Expression;
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.core.ServiceContext;
+import org.snaker.engine.helper.ClassHelper;
+import org.snaker.engine.helper.StringHelper;
+
+/**
+ * 决策定义decision元素
+ * @author yuqs
+ * @since 1.0
+ */
+public class DecisionModel extends NodeModel {
+	private static final Logger log = LoggerFactory.getLogger(DecisionModel.class);
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -806621814645169999L;
+	/**
+	 * 决策选择表达式串(需要表达式引擎解析)
+	 */
+	private String expr;
+	/**
+	 * 决策处理类,对于复杂的分支条件,可通过handleClass来处理
+	 */
+	private String handleClass;
+	/**
+	 * 决策处理类实例
+	 */
+	private DecisionHandler decide;
+	/**
+	 * 表达式解析器
+	 */
+	private transient Expression expression;
+	
+	public void exec(Execution execution) {
+		log.info(execution.getOrder().getId() + "->decision execution.getArgs():" + execution.getArgs());
+		if(expression == null) {
+			expression = ServiceContext.getContext().find(Expression.class);
+		}
+		log.info("expression is " + expression);
+		if(expression == null) throw new SnakerException("表达式解析器为空,请检查配置.");
+		String next = null;
+		if(StringHelper.isNotEmpty(expr)) {
+			next = expression.eval(String.class, expr, execution.getArgs());
+		} else if(decide != null) {
+			next = decide.decide(execution);
+		}
+		log.info(execution.getOrder().getId() + "->decision expression[expr=" + expr + "] return result:" + next);
+		boolean isfound = false;
+		for(TransitionModel tm : getOutputs()) {
+			if(StringHelper.isEmpty(next)) {
+				String expr = tm.getExpr();
+				if(StringHelper.isNotEmpty(expr) && expression.eval(Boolean.class, expr, execution.getArgs())) {
+					tm.setEnabled(true);
+					tm.execute(execution);
+					isfound = true;
+				}
+			} else {
+				if(tm.getName().equals(next)) {
+					tm.setEnabled(true);
+					tm.execute(execution);
+					isfound = true;
+				}
+			}
+		}
+		if(!isfound) throw new SnakerException(execution.getOrder().getId() + "->decision节点无法确定下一步执行路线");
+	}
+	
+	public String getExpr() {
+		return expr;
+	}
+	public void setExpr(String expr) {
+		this.expr = expr;
+	}
+
+	public String getHandleClass() {
+		return handleClass;
+	}
+
+	public void setHandleClass(String handleClass) {
+		this.handleClass = handleClass;
+		if(StringHelper.isNotEmpty(handleClass)) {
+			decide = (DecisionHandler)ClassHelper.newInstance(handleClass);
+		}
+	}
+}

+ 44 - 0
workflowy/src/main/java/org/snaker/engine/model/EndModel.java

@@ -0,0 +1,44 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.handlers.impl.EndProcessHandler;
+
+/**
+ * 结束节点end元素
+ * @author yuqs
+ * @since 1.0
+ */
+public class EndModel extends NodeModel {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -7793175180140842894L;
+
+	public void exec(Execution execution) {
+		fire(new EndProcessHandler(), execution);
+	}
+	
+	/**
+	 * 结束节点无输出变迁
+	 */
+	public List<TransitionModel> getOutputs() {
+		return Collections.emptyList();
+	}
+}

+ 72 - 0
workflowy/src/main/java/org/snaker/engine/model/FieldModel.java

@@ -0,0 +1,72 @@
+/*
+ *  Copyright 2013-2015 www.snakerflow.com.
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package org.snaker.engine.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 字段模型类
+ * @author yuqs
+ * @since 2.0
+ */
+public class FieldModel extends BaseModel {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -3439646328045814844L;
+	/**
+     * 字段类型
+     */
+    private String type;
+    /**
+     * 字段模型对应的属性key/value数据
+     */
+    private Map<String, String> attrMap = new HashMap<String, String>();
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+    /**
+     * 向属性集合添加key/value数据
+     * @param name 属性名称
+     * @param value 属性值
+     */
+    public void addAttr(String name, String value) {
+        this.attrMap.put(name, value);
+    }
+
+    /**
+     * 获取属性集合
+     * @return 属性集合
+     */
+    public Map<String, String> getAttrMap() {
+        return this.attrMap;
+    }
+
+    /**
+     * 设置属性集合
+     * @param attrMap 属性集合
+     */
+    public void setAttrMap(Map<String, String> attrMap) {
+        this.attrMap = attrMap;
+    }
+}

+ 33 - 0
workflowy/src/main/java/org/snaker/engine/model/ForkModel.java

@@ -0,0 +1,33 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import org.snaker.engine.core.Execution;
+
+/**
+ * 分支定义fork元素
+ * @author yuqs
+ * @since 1.0
+ */
+public class ForkModel extends NodeModel {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 2030281774771653617L;
+
+	protected void exec(Execution execution) {
+		runOutTransition(execution);
+	}
+}

+ 35 - 0
workflowy/src/main/java/org/snaker/engine/model/JoinModel.java

@@ -0,0 +1,35 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.handlers.impl.MergeBranchHandler;
+
+/**
+ * 合并定义join元素
+ * @author yuqs
+ * @since 1.0
+ */
+public class JoinModel extends NodeModel {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5296621319088076775L;
+	
+	public void exec(Execution execution) {
+		fire(new MergeBranchHandler(this), execution);
+		if(execution.isMerged()) runOutTransition(execution);
+	}
+}

+ 206 - 0
workflowy/src/main/java/org/snaker/engine/model/NodeModel.java

@@ -0,0 +1,206 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snaker.engine.Action;
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.SnakerInterceptor;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.helper.ClassHelper;
+import org.snaker.engine.helper.StringHelper;
+
+/**
+ * 节点元素(存在输入输出的变迁)
+ * @author yuqs
+ * @since 1.0
+ */
+public abstract class NodeModel extends BaseModel implements Action {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -2377317472320109317L;
+	private static final Logger log = LoggerFactory.getLogger(NodeModel.class);
+	/**
+	 * 输入变迁集合
+	 */
+	private List<TransitionModel> inputs = new ArrayList<TransitionModel>();
+	/**
+	 * 输出变迁集合
+	 */
+	private List<TransitionModel> outputs = new ArrayList<TransitionModel>();
+	/**
+	 * layout
+	 */
+	private String layout;
+	/**
+	 * 局部前置拦截器
+	 */
+	private String preInterceptors;
+	/**
+	 * 局部后置拦截器
+	 */
+	private String postInterceptors;
+	/**
+	 * 前置局部拦截器实例集合
+	 */
+	private List<SnakerInterceptor> preInterceptorList = new ArrayList<SnakerInterceptor>();
+	/**
+	 * 后置局部拦截器实例集合
+	 */
+	private List<SnakerInterceptor> postInterceptorList = new ArrayList<SnakerInterceptor>();
+	
+	/**
+	 * 具体节点模型需要完成的执行逻辑
+	 * @param execution 执行对象
+	 */
+	protected abstract void exec(Execution execution);
+	
+	/**
+	 * 对执行逻辑增加前置、后置拦截处理
+	 * @param execution 执行对象
+	 */
+	public void execute(Execution execution) {
+		intercept(preInterceptorList, execution);
+		exec(execution);
+		intercept(postInterceptorList, execution);
+	}
+	
+	/**
+	 * 运行变迁继续执行
+	 * @param execution 执行对象
+	 */
+	protected void runOutTransition(Execution execution) {
+		for (TransitionModel tm : getOutputs()) {
+			tm.setEnabled(true);
+			tm.execute(execution);
+		}
+	}
+	
+	/**
+	 * 拦截方法
+	 * @param interceptorList 拦截器列表
+	 * @param execution 执行对象
+	 */
+	private void intercept(List<SnakerInterceptor> interceptorList, Execution execution) {
+		try {
+			for(SnakerInterceptor interceptor : interceptorList) {
+				interceptor.intercept(execution);
+			}
+		} catch(Exception e) {
+			log.error("拦截器执行失败=" + e.getMessage());
+            throw new SnakerException(e);
+		}
+	}
+	
+	/**
+	 * 根据父节点模型、当前节点模型判断是否可退回。可退回条件:
+	 * 1、满足中间无fork、join、subprocess模型
+	 * 2、满足父节点模型如果为任务模型时,参与类型为any
+	 * @param parent 父节点模型
+	 * @return 是否可以退回
+	 */
+	public static boolean canRejected(NodeModel current, NodeModel parent) {
+		if(parent instanceof TaskModel && !((TaskModel)parent).isPerformAny()) {
+			return false;
+		}
+        boolean result = false;
+		for(TransitionModel tm : current.getInputs()) {
+			NodeModel source = tm.getSource();
+			if(source == parent) {
+				return true;
+			}
+			if(source instanceof ForkModel
+					|| source instanceof JoinModel
+					|| source instanceof SubProcessModel
+					|| source instanceof StartModel) {
+				continue;
+			}
+			result = result || canRejected(source, parent);
+		}
+		return result;
+	}
+
+    public <T> List<T> getNextModels(Class<T> clazz) {
+        List<T> models = new ArrayList<T>();
+        for(TransitionModel tm : this.getOutputs()) {
+            addNextModels(models, tm, clazz);
+        }
+        return models;
+    }
+
+    protected <T> void addNextModels(List<T> models, TransitionModel tm, Class<T> clazz) {
+        if(clazz.isInstance(tm.getTarget())) {
+            models.add((T)tm.getTarget());
+        } else {
+            for(TransitionModel tm2 : tm.getTarget().getOutputs()) {
+                addNextModels(models, tm2, clazz);
+            }
+        }
+    }
+	
+	public List<TransitionModel> getInputs() {
+		return inputs;
+	}
+	public void setInputs(List<TransitionModel> inputs) {
+		this.inputs = inputs;
+	}
+	public List<TransitionModel> getOutputs() {
+		return outputs;
+	}
+	public void setOutputs(List<TransitionModel> outputs) {
+		this.outputs = outputs;
+	}
+
+	public String getLayout() {
+		return layout;
+	}
+
+	public void setLayout(String layout) {
+		this.layout = layout;
+	}
+
+	public String getPreInterceptors() {
+		return preInterceptors;
+	}
+
+	public void setPreInterceptors(String preInterceptors) {
+		this.preInterceptors = preInterceptors;
+		if(StringHelper.isNotEmpty(preInterceptors)) {
+			for(String interceptor : preInterceptors.split(",")) {
+				SnakerInterceptor instance = (SnakerInterceptor)ClassHelper.newInstance(interceptor);
+				if(instance != null) this.preInterceptorList.add(instance);
+			}
+		}
+	}
+
+	public String getPostInterceptors() {
+		return postInterceptors;
+	}
+
+	public void setPostInterceptors(String postInterceptors) {
+		this.postInterceptors = postInterceptors;
+		if(StringHelper.isNotEmpty(postInterceptors)) {
+			for(String interceptor : postInterceptors.split(",")) {
+				SnakerInterceptor instance = (SnakerInterceptor)ClassHelper.newInstance(interceptor);
+				if(instance != null) this.postInterceptorList.add(instance);
+			}
+		}
+	}
+}

+ 197 - 0
workflowy/src/main/java/org/snaker/engine/model/ProcessModel.java

@@ -0,0 +1,197 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.snaker.engine.INoGenerator;
+import org.snaker.engine.helper.ClassHelper;
+import org.snaker.engine.helper.StringHelper;
+import org.snaker.engine.impl.DefaultNoGenerator;
+
+/**
+ * 流程定义process元素
+ * @author yuqs
+ * @since 1.0
+ */
+public class ProcessModel extends BaseModel {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -9000210138346445915L;
+	/**
+	 * 节点元素集合
+	 */
+	private List<NodeModel> nodes = new ArrayList<NodeModel>();
+    private List<TaskModel> taskModels = new ArrayList<TaskModel>();
+	/**
+	 * 流程实例启动url
+	 */
+	private String instanceUrl;
+	/**
+	 * 期望完成时间
+	 */
+	private String expireTime;
+	/**
+	 * 实例编号生成的class
+	 */
+	private String instanceNoClass;
+	/**
+	 * 实例编号生成器对象
+	 */
+	private INoGenerator generator;
+    /**
+     * lock
+     */
+    private final Object lock = new Object();
+	
+	/**
+	 * 返回当前流程定义的所有工作任务节点模型
+	 * @return
+     * @deprecated
+	 */
+	public List<WorkModel> getWorkModels() {
+		List<WorkModel> models = new ArrayList<WorkModel>();
+		for(NodeModel node : nodes) {
+			if(node instanceof WorkModel) {
+				models.add((WorkModel)node);
+			}
+		}
+		return models;
+	}
+
+    /**
+     * 获取所有的有序任务模型集合
+     * @return List<TaskModel> 任务模型集合
+     */
+    public List<TaskModel> getTaskModels() {
+        if(taskModels.isEmpty()) {
+            synchronized (lock) {
+                if(taskModels.isEmpty())
+                    buildModels(taskModels, getStart().getNextModels(TaskModel.class), TaskModel.class);
+            }
+        }
+        return taskModels;
+    }
+
+    /**
+     * 根据指定的节点类型返回流程定义中所有模型对象
+     * @param clazz 节点类型
+     * @param <T> 泛型
+     * @return 节点列表
+     */
+    public <T> List<T> getModels(Class<T> clazz) {
+        List<T> models = new ArrayList<T>();
+        buildModels(models, getStart().getNextModels(clazz), clazz);
+        return models;
+    }
+
+    private <T> void buildModels(List<T> models, List<T> nextModels, Class<T> clazz) {
+        for(T nextModel : nextModels) {
+            if(!models.contains(nextModel)) {
+                models.add(nextModel);
+                buildModels(models, ((NodeModel)nextModel).getNextModels(clazz), clazz);
+            }
+        }
+    }
+
+	/**
+	 * 获取process定义的start节点模型
+	 * @return
+	 */
+	public StartModel getStart() {
+		for(NodeModel node : nodes) {
+			if(node instanceof StartModel) {
+				return (StartModel)node;
+			}
+		}
+		return null;
+	}
+	
+	/**
+	 * 获取process定义的指定节点名称的节点模型
+	 * @param nodeName 节点名称
+	 * @return
+	 */
+	public NodeModel getNode(String nodeName) {
+		for(NodeModel node : nodes) {
+			if(node.getName().equals(nodeName)) {
+				return node;
+			}
+		}
+		return null;
+	}
+	
+	/**
+	 * 判断当前模型的节点是否包含给定的节点名称参数
+	 * @param nodeNames 节点名称数组
+	 * @return
+	 */
+	public <T> boolean containsNodeNames(Class<T> T, String... nodeNames) {
+		for(NodeModel node : nodes) {
+			if(!T.isInstance(node)) {
+				continue;
+			}
+			for(String nodeName : nodeNames) {
+				if(node.getName().equals(nodeName)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+	
+	public List<NodeModel> getNodes() {
+		return nodes;
+	}
+	public void setNodes(List<NodeModel> nodes) {
+		this.nodes = nodes;
+	}
+
+	public String getExpireTime() {
+		return expireTime;
+	}
+
+	public void setExpireTime(String expireTime) {
+		this.expireTime = expireTime;
+	}
+
+	public String getInstanceUrl() {
+		return instanceUrl;
+	}
+
+	public void setInstanceUrl(String instanceUrl) {
+		this.instanceUrl = instanceUrl;
+	}
+	public String getInstanceNoClass() {
+		return instanceNoClass;
+	}
+
+	public void setInstanceNoClass(String instanceNoClass) {
+		this.instanceNoClass = instanceNoClass;
+		if(StringHelper.isNotEmpty(instanceNoClass)) {
+			generator = (INoGenerator)ClassHelper.newInstance(instanceNoClass);
+		}
+	}
+	
+	public INoGenerator getGenerator() {
+		return generator == null ? new DefaultNoGenerator() : generator;
+	}
+
+	public void setGenerator(INoGenerator generator) {
+		this.generator = generator;
+	}
+}

+ 43 - 0
workflowy/src/main/java/org/snaker/engine/model/StartModel.java

@@ -0,0 +1,43 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.snaker.engine.core.Execution;
+
+/**
+ * 开始节点定义start元素
+ * @author yuqs
+ * @since 1.0
+ */
+public class StartModel extends NodeModel {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -4550530562581330477L;
+
+	/**
+	 * 开始节点无输入变迁
+	 */
+	public List<TransitionModel> getInputs() {
+		return Collections.emptyList();
+	}
+
+	protected void exec(Execution execution) {
+		runOutTransition(execution);
+	}
+}

+ 68 - 0
workflowy/src/main/java/org/snaker/engine/model/SubProcessModel.java

@@ -0,0 +1,68 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import org.snaker.engine.core.Execution;
+
+/**
+ * 子流程定义subprocess元素
+ * @author yuqs
+ * @since 1.0
+ */
+public class SubProcessModel extends WorkModel {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3923955459202018147L;
+	/**
+	 * 子流程名称
+	 */
+	private String processName;
+	/**
+	 * 子流程版本号
+	 */
+	private Integer version;
+	/**
+	 * 子流程定义引用
+	 */
+	private ProcessModel subProcess;
+
+	protected void exec(Execution execution) {
+		runOutTransition(execution);	
+	}
+	
+	public ProcessModel getSubProcess() {
+		return subProcess;
+	}
+	public void setSubProcess(ProcessModel subProcess) {
+		this.subProcess = subProcess;
+	}
+
+	public String getProcessName() {
+		return processName;
+	}
+
+	public void setProcessName(String processName) {
+		this.processName = processName;
+	}
+
+	public Integer getVersion() {
+		return version;
+	}
+
+	public void setVersion(Integer version) {
+		this.version = version;
+	}
+}

+ 256 - 0
workflowy/src/main/java/org/snaker/engine/model/TaskModel.java

@@ -0,0 +1,256 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import org.snaker.engine.AssignmentHandler;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.handlers.impl.MergeActorHandler;
+import org.snaker.engine.helper.AssertHelper;
+import org.snaker.engine.helper.ClassHelper;
+import org.snaker.engine.helper.StringHelper;
+import org.snaker.engine.scheduling.JobCallback;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 任务定义task元素
+ * @author yuqs
+ * @since 1.0
+ */
+public class TaskModel extends WorkModel {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1775545243233990922L;
+	/**
+	 * 类型:普通任务
+	 */
+	public static final String PERFORMTYPE_ANY = "ANY";
+	/**
+	 * 类型:参与者fork任务
+	 */
+	public static final String PERFORMTYPE_ALL = "ALL";
+	/**
+	 * 类型:主办任务
+	 */
+	public static final String TASKTYPE_MAJOR = "Major";
+	/**
+	 * 类型:协办任务
+	 */
+	public static final String TASKTYPE_AIDANT = "Aidant";
+	/**
+	 * 参与类型
+	 */
+	public enum PerformType {
+		ANY, ALL;
+	}
+	/**
+	 * 任务类型(Major:主办的,Aidant:协助的,Record:仅仅作为记录的)
+	 */
+	public enum TaskType {
+		Major, Aidant, Record;
+	}
+	/**
+	 * 参与者变量名称
+	 */
+	private String assignee;
+	/**
+	 * 参与方式
+	 * any:任何一个参与者处理完即执行下一步
+	 * all:所有参与者都完成,才可执行下一步
+	 */
+	private String performType = PERFORMTYPE_ANY;
+	/**
+	 * 任务类型
+	 * major:主办任务
+	 * aidant:协办任务
+	 */
+	private String taskType = TASKTYPE_MAJOR;
+	/**
+	 * 期望完成时间
+	 */
+	private String expireTime;
+	/**
+	 * 提醒时间
+	 */
+	private String reminderTime;
+	/**
+	 * 提醒间隔(分钟)
+	 */
+	private String reminderRepeat;
+	/**
+	 * 是否自动执行
+	 */
+	private String autoExecute;
+	/**
+	 * 任务执行后回调类
+	 */
+	private String callback;
+	/**
+	 * 分配参与者处理类型
+	 */
+	private String assignmentHandler;
+	/**
+	 * 任务执行后回调对象
+	 */
+	private JobCallback callbackObject;
+	/**
+	 * 分配参与者处理对象
+	 */
+	private AssignmentHandler assignmentHandlerObject;
+    /**
+     * 字段模型集合
+     */
+    private List<FieldModel> fields = null;
+
+	protected void exec(Execution execution) {
+		if(performType == null || performType.equalsIgnoreCase(PERFORMTYPE_ANY)) {
+			/**
+			 * any方式,直接执行输出变迁
+			 */
+			runOutTransition(execution);
+		} else {
+			/**
+			 * all方式,需要判断是否已全部合并
+			 * 由于all方式分配任务,是每人一个任务
+			 * 那么此时需要判断之前分配的所有任务都执行完成后,才可执行下一步,否则不处理
+			 */
+			fire(new MergeActorHandler(getName()), execution);
+			if(execution.isMerged()) runOutTransition(execution);
+		}
+	}
+	
+	public boolean isPerformAny() {
+		return PERFORMTYPE_ANY.equalsIgnoreCase(this.performType);
+	}
+	
+	public boolean isPerformAll() {
+		return PERFORMTYPE_ALL.equalsIgnoreCase(this.performType);
+	}
+	
+	public boolean isMajor() {
+		return TASKTYPE_MAJOR.equalsIgnoreCase(this.taskType);
+	}
+	
+	public String getAssignee() {
+		return assignee;
+	}
+	
+	public void setAssignee(String assignee) {
+		this.assignee = assignee;
+	}
+
+	public String getExpireTime() {
+		return expireTime;
+	}
+
+	public void setExpireTime(String expireTime) {
+		this.expireTime = expireTime;
+	}
+
+	public String getTaskType() {
+		return taskType;
+	}
+
+	public void setTaskType(String taskType) {
+		this.taskType = (StringHelper.isEmpty(taskType) ? TASKTYPE_MAJOR : taskType);
+	}
+
+	public String getPerformType() {
+		return performType;
+	}
+
+	public void setPerformType(String performType) {
+		this.performType = (StringHelper.isEmpty(performType) ? PERFORMTYPE_ANY : performType);
+	}
+
+	public String getReminderTime() {
+		return reminderTime;
+	}
+
+	public void setReminderTime(String reminderTime) {
+		this.reminderTime = reminderTime;
+	}
+
+	public String getReminderRepeat() {
+		return reminderRepeat;
+	}
+
+	public void setReminderRepeat(String reminderRepeat) {
+		this.reminderRepeat = reminderRepeat;
+	}
+
+	public String getAutoExecute() {
+		return autoExecute;
+	}
+
+	public void setAutoExecute(String autoExecute) {
+		this.autoExecute = autoExecute;
+	}
+
+	public AssignmentHandler getAssignmentHandlerObject() {
+		return assignmentHandlerObject;
+	}
+
+	public void setAssignmentHandler(String assignmentHandlerStr) {
+		if(StringHelper.isNotEmpty(assignmentHandlerStr)) {
+			this.assignmentHandler = assignmentHandlerStr;
+			assignmentHandlerObject = (AssignmentHandler)ClassHelper.newInstance(assignmentHandlerStr);
+			AssertHelper.notNull(assignmentHandlerObject, "分配参与者处理类实例化失败");
+		}
+	}
+
+	public String getAssignmentHandler() {
+		return assignmentHandler;
+	}
+
+	public String getCallback() {
+		return callback;
+	}
+	
+	public JobCallback getCallbackObject() {
+		return callbackObject;
+	}
+
+	public void setCallback(String callbackStr) {
+		if(StringHelper.isNotEmpty(callbackStr)) {
+			this.callback = callbackStr;
+			callbackObject = (JobCallback)ClassHelper.newInstance(callbackStr);
+			AssertHelper.notNull(callbackObject, "回调处理类实例化失败");
+		}
+	}
+
+    public List<FieldModel> getFields() {
+        return fields;
+    }
+
+    public void setFields(List<FieldModel> fields) {
+        this.fields = fields;
+    }
+
+    /**
+     * 获取后续任务模型集合(方便预处理)
+     * @return 模型集合
+     * @deprecated
+     */
+    public List<TaskModel> getNextTaskModels() {
+        List<TaskModel> models = new ArrayList<TaskModel>();
+        for(TransitionModel tm : this.getOutputs()) {
+            addNextModels(models, tm, TaskModel.class);
+        }
+        return models;
+    }
+}

+ 124 - 0
workflowy/src/main/java/org/snaker/engine/model/TransitionModel.java

@@ -0,0 +1,124 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+import org.snaker.engine.Action;
+import org.snaker.engine.core.Execution;
+import org.snaker.engine.handlers.impl.CreateTaskHandler;
+import org.snaker.engine.handlers.impl.StartSubProcessHandler;
+
+/**
+ * 变迁定义transition元素
+ * @author yuqs
+ * @since 1.0
+ */
+public class TransitionModel extends BaseModel implements Action {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 3688123410411321158L;
+	/**
+	 * 变迁的源节点引用
+	 */
+	private NodeModel source;
+	/**
+	 * 变迁的目标节点引用
+	 */
+	private NodeModel target;
+	/**
+	 * 变迁的目标节点name名称
+	 */
+	private String to;
+	/**
+	 * 变迁的条件表达式,用于decision
+	 */
+	private String expr;
+	/**
+	 * 转折点图形数据
+	 */
+	private String g;
+	/**
+	 * 描述便宜位置
+	 */
+	private String offset;
+	/**
+	 * 当前变迁路径是否可用
+	 */
+	private boolean enabled = false;
+	
+	public void execute(Execution execution) {
+		if(!enabled) return;
+		if(target instanceof TaskModel) {
+			//如果目标节点模型为TaskModel,则创建task
+			fire(new CreateTaskHandler((TaskModel)target), execution);
+		} else if(target instanceof SubProcessModel) {
+			//如果目标节点模型为SubProcessModel,则启动子流程
+			fire(new StartSubProcessHandler((SubProcessModel)target), execution);
+		} else {
+			//如果目标节点模型为其它控制类型,则继续由目标节点执行
+			target.execute(execution);
+		}
+	}
+	
+	public NodeModel getSource() {
+		return source;
+	}
+	public void setSource(NodeModel source) {
+		this.source = source;
+	}
+	public NodeModel getTarget() {
+		return target;
+	}
+	public void setTarget(NodeModel target) {
+		this.target = target;
+	}
+	public String getTo() {
+		return to;
+	}
+	public void setTo(String to) {
+		this.to = to;
+	}
+	public boolean isEnabled() {
+		return enabled;
+	}
+	public void setEnabled(boolean enabled) {
+		this.enabled = enabled;
+	}
+
+	public String getExpr() {
+		return expr;
+	}
+
+	public void setExpr(String expr) {
+		this.expr = expr;
+	}
+
+	public String getG() {
+		return g;
+	}
+
+	public void setG(String g) {
+		this.g = g;
+	}
+
+	public String getOffset() {
+		return offset;
+	}
+
+	public void setOffset(String offset) {
+		this.offset = offset;
+	}
+}

+ 37 - 0
workflowy/src/main/java/org/snaker/engine/model/WorkModel.java

@@ -0,0 +1,37 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.model;
+
+/**
+ * 工作元素
+ * @author yuqs
+ * @since 1.0
+ */
+public abstract class WorkModel extends NodeModel {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 761102386160546149L;
+	/**
+	 * form
+	 */
+	private String form;
+	public String getForm() {
+		return form;
+	}
+	public void setForm(String form) {
+		this.form = form;
+	}
+}

+ 85 - 0
workflowy/src/main/java/org/snaker/engine/parser/AbstractNodeParser.java

@@ -0,0 +1,85 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.parser;
+
+import java.util.List;
+
+import org.snaker.engine.helper.XmlHelper;
+import org.snaker.engine.model.NodeModel;
+import org.snaker.engine.model.TransitionModel;
+import org.w3c.dom.Element;
+
+/**
+ * 抽象dom节点解析类
+ * 完成通用的属性、变迁解析
+ * @author yuqs
+ * @since 1.0
+ */
+public abstract class AbstractNodeParser implements NodeParser {
+	/**
+	 * 模型对象
+	 */
+	protected NodeModel model;
+	
+	/**
+	 * 实现NodeParser接口的parse函数
+	 * 由子类产生各自的模型对象,设置常用的名称属性,并且解析子节点transition,构造TransitionModel模型对象
+	 */
+	public void parse(Element element) {
+		model = newModel();
+		model.setName(element.getAttribute(ATTR_NAME));
+		model.setDisplayName(element.getAttribute(ATTR_DISPLAYNAME));
+		model.setLayout(element.getAttribute(ATTR_LAYOUT));
+		model.setPreInterceptors(element.getAttribute(ATTR_PREINTERCEPTORS));
+		model.setPostInterceptors(element.getAttribute(ATTR_POSTINTERCEPTORS));
+		
+		List<Element> transitions = XmlHelper.elements(element, NODE_TRANSITION);
+		for(Element te : transitions) {
+			TransitionModel transition = new TransitionModel();
+			transition.setName(te.getAttribute(ATTR_NAME));
+			transition.setDisplayName(te.getAttribute(ATTR_DISPLAYNAME));
+			transition.setTo(te.getAttribute(ATTR_TO));
+			transition.setExpr(te.getAttribute(ATTR_EXPR));
+			transition.setG(te.getAttribute(ATTR_G));
+			transition.setOffset(te.getAttribute(ATTR_OFFSET));
+			transition.setSource(model);
+			model.getOutputs().add(transition);
+		}
+		
+		parseNode(model, element);
+	}
+	
+	/**
+	 * 子类可覆盖此方法,完成特定的解析
+	 * @param model
+	 * @param element
+	 */
+	protected void parseNode(NodeModel model, Element element) {
+		
+	}
+	
+	/**
+	 * 抽象方法,由子类产生各自的模型对象
+	 * @return
+	 */
+	protected abstract NodeModel newModel();
+
+	/**
+	 * 返回模型对象
+	 */
+	public NodeModel getModel() {
+		return model;
+	}
+}

+ 108 - 0
workflowy/src/main/java/org/snaker/engine/parser/ModelParser.java

@@ -0,0 +1,108 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.parser;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.xml.parsers.DocumentBuilder;
+
+import org.snaker.engine.SnakerException;
+import org.snaker.engine.core.ServiceContext;
+import org.snaker.engine.helper.XmlHelper;
+import org.snaker.engine.model.NodeModel;
+import org.snaker.engine.model.ProcessModel;
+import org.snaker.engine.model.TransitionModel;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * 流程定义xml文件的模型解析器
+ * @author yuqs
+ * @since 1.0
+ */
+public class ModelParser {
+	/**
+	 * 解析流程定义文件,并将解析后的对象放入模型容器中
+	 * @param bytes
+	 */
+	public static ProcessModel parse(byte[] bytes) {
+		DocumentBuilder documentBuilder = XmlHelper.createDocumentBuilder();
+		if(documentBuilder != null) {
+			Document doc = null;
+			try {
+				doc = documentBuilder.parse(new ByteArrayInputStream(bytes));
+				Element processE = doc.getDocumentElement();
+				ProcessModel process = new ProcessModel();
+				process.setName(processE.getAttribute(NodeParser.ATTR_NAME));
+				process.setDisplayName(processE.getAttribute(NodeParser.ATTR_DISPLAYNAME));
+				process.setExpireTime(processE.getAttribute(NodeParser.ATTR_EXPIRETIME));
+				process.setInstanceUrl(processE.getAttribute(NodeParser.ATTR_INSTANCEURL));
+				process.setInstanceNoClass(processE.getAttribute(NodeParser.ATTR_INSTANCENOCLASS));
+				NodeList nodeList = processE.getChildNodes();
+				int nodeSize = nodeList.getLength();
+				for(int i = 0; i < nodeSize; i++) {
+					Node node = nodeList.item(i);
+					if (node.getNodeType() == Node.ELEMENT_NODE) {
+						NodeModel model = parseModel(node);
+						process.getNodes().add(model);
+					}
+				}
+				
+				//循环节点模型,构造变迁输入、输出的source、target
+				for(NodeModel node : process.getNodes()) {
+					for(TransitionModel transition : node.getOutputs()) {
+						String to = transition.getTo();
+						for(NodeModel node2 : process.getNodes()) {
+							if(to.equalsIgnoreCase(node2.getName())) {
+								node2.getInputs().add(transition);
+								transition.setTarget(node2);
+							}
+						}
+					}
+				}
+				return process;
+			} catch (SAXException e) {
+				e.printStackTrace();
+				throw new SnakerException(e);
+			} catch (IOException e) {
+				throw new SnakerException(e);
+			}
+		} else {
+			throw new SnakerException("documentBuilder is null");
+		}
+	}
+	
+	/**
+	 * 对流程定义xml的节点,根据其节点对应的解析器解析节点内容
+	 * @param node
+	 * @return
+	 */
+	private static NodeModel parseModel(Node node) {
+		String nodeName = node.getNodeName();
+		Element element = (Element)node;
+		NodeParser nodeParser = null;
+		try {
+			nodeParser = ServiceContext.getContext().findByName(nodeName, NodeParser.class);
+			nodeParser.parse(element);
+			return nodeParser.getModel();
+		} catch (RuntimeException e) {
+			throw new SnakerException(e);
+		}
+	}
+}

+ 78 - 0
workflowy/src/main/java/org/snaker/engine/parser/NodeParser.java

@@ -0,0 +1,78 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.parser;
+
+import org.snaker.engine.model.NodeModel;
+import org.w3c.dom.Element;
+
+/**
+ * 节点解析接口
+ * @author yuqs
+ * @since 1.0
+ */
+public interface NodeParser {
+	/**
+	 * 变迁节点名称
+	 */
+	public static final String NODE_TRANSITION = "transition";
+	
+	/**
+	 * 节点属性名称
+	 */
+	public static final String ATTR_NAME = "name";
+	public static final String ATTR_DISPLAYNAME = "displayName";
+	public static final String ATTR_INSTANCEURL = "instanceUrl";
+	public static final String ATTR_INSTANCENOCLASS = "instanceNoClass";
+	public static final String ATTR_EXPR = "expr";
+	public static final String ATTR_HANDLECLASS = "handleClass";
+	public static final String ATTR_FORM = "form";
+    public static final String ATTR_FIELD = "field";
+    public static final String ATTR_VALUE = "value";
+    public static final String ATTR_ATTR = "attr";
+    public static final String ATTR_TYPE= "type";
+	public static final String ATTR_ASSIGNEE = "assignee";
+	public static final String ATTR_ASSIGNEE_HANDLER = "assignmentHandler";
+	public static final String ATTR_PERFORMTYPE = "performType";
+	public static final String ATTR_TASKTYPE = "taskType";
+	public static final String ATTR_TO = "to";
+	public static final String ATTR_PROCESSNAME = "processName";
+	public static final String ATTR_VERSION = "version";
+	public static final String ATTR_EXPIRETIME = "expireTime";
+	public static final String ATTR_AUTOEXECUTE = "autoExecute";
+	public static final String ATTR_CALLBACK = "callback";
+	public static final String ATTR_REMINDERTIME = "reminderTime";
+	public static final String ATTR_REMINDERREPEAT = "reminderRepeat";
+    public static final String ATTR_CLAZZ = "clazz";
+    public static final String ATTR_METHODNAME = "methodName";
+    public static final String ATTR_ARGS = "args";
+    public static final String ATTR_VAR = "var";
+    public static final String ATTR_LAYOUT = "layout";
+    public static final String ATTR_G = "g";
+    public static final String ATTR_OFFSET = "offset";
+    public static final String ATTR_PREINTERCEPTORS = "preInterceptors";
+    public static final String ATTR_POSTINTERCEPTORS = "postInterceptors";
+	
+	/**
+	 * 节点dom元素解析方法,由实现类完成解析
+	 * @param element dom元素
+	 */
+	public void parse(Element element);
+	
+	/**
+	 * 解析成功后,提供返回NodeModel模型对象
+	 * @return 节点模型
+	 */
+	public NodeModel getModel();
+}

+ 42 - 0
workflowy/src/main/java/org/snaker/engine/parser/impl/CustomParser.java

@@ -0,0 +1,42 @@
+/* Copyright 2013-2015 www.snakerflow.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.snaker.engine.parser.impl;
+
+import org.snaker.engine.model.CustomModel;
+import org.snaker.engine.model.NodeModel;
+import org.snaker.engine.parser.AbstractNodeParser;
+import org.springframework.stereotype.Component;
+import org.w3c.dom.Element;
+
+/**
+ * 自定义节点解析器
+ * @author yuqs
+ * @since 1.0
+ */
+@Component("custom")
+public class CustomParser extends AbstractNodeParser {
+	protected void parseNode(NodeModel node, Element element) {
+		CustomModel custom = (CustomModel)node;
+		custom.setClazz(element.getAttribute(ATTR_CLAZZ));
+		custom.setMethodName(element.getAttribute(ATTR_METHODNAME));
+		custom.setArgs(element.getAttribute(ATTR_ARGS));
+		custom.setVar(element.getAttribute(ATTR_VAR));
+	}
+	
+	protected NodeModel newModel() {
+		return new CustomModel();
+	}
+
+}

部分文件因为文件数量过多而无法显示