当我们的业务流程被设计开发完毕之后,会有许多相关的文件"散落"在工程中,其中包括:
- 定义流程的JPDL文件
- 根据图形化流程定义同步生成的流程图片文件(PNG格式)
- 业务流程中用于人机交互的表单页面文件
- 事件监听器等用户自定义代码的Java类文件
- 其他流程资源文件,例如小图标、css样式表、脚本文件、属性文件等
jbpm4支持将流程定义及其相关资源打包一个JAR(Java归档)格式的文件,部署到服务器上(其实就是服务所连接的JBPM数据库中),然后流程定义就可以被执行了。
一、如何将流程定义和流程相关资源部署到jbpm数据库中?
jbpm4提供了一个基于Ant任务的API来部署业务流程归档——
org.jbpm.pvm.internal.ant.JbpmDeployTask。
JbpmDeployTask不仅可以部署单个业务流程归档,也可以部署一组业务流程归档到服务器上。通过读取jbpm.cfg.xml中的jdbc数据连接信息将业务流程归档部署到数据库中。因此,在使用JbpmDeployTask部署流程定义之前,先确保部署的数据库正在运行。
创建和部署业务流程归档的例子位于jbpm4发布包中的examples目录下的Ant build.xml中,任务名称是create.and.deploy.examples。
该Ant所解决的问题如下:在examples/build.xml中
1>需要声明一个path任务来指定包含JbpmDeployTask的jbpm.jar及其所有的依赖库。
2>需要创建一个业务流程归档,可以使用Ant的jar任务
注意:工作流引擎扫描业务流程归档中所有以.jpdl.xml结尾的文件,所有这些文件的内容都会被当做JPDL流程定义来解析,然后被用来发起流程实例,业务流程归档中所有其他资源也会在部署过程中持久化到数据库中。
所有这些资源被统一编号保存在数据库表jbpm4_lob中,因此可以很方便地通过jbpm4提供的RepositoryService.getResourceAsStream API随时访问这些资源。
3>部署,需要先在Ant中将JbpmDeployTask生命一个jbpm-deploy的自定义任务
4>调用这个自定义的Ant任务
通过查看org.jbpm.pvm.internal.ant.JbpmDeployTask类的源码,可以发现为我们预留了3个接口参数
private String jbpmCfg; private File file; private List<FileSet> fileSets = new ArrayList<FileSet>();
file:指定需要被部署的业务流程定义文件;以.xml结尾的文件会被作为流程定义文件直接部署;
以"*ar"结尾的文件,比如.bar或.jar文件,则会作为业务流程归档部署。
jbpmCfg:指定jbpm配置文件,默认寻找classpath根目录下的jbpm.cfg.xml如果需要自定义,这个路径位于jbpm-deploy任务定义的classpath范围内。
fileSets:指定需要被部署的业务流程定义文件集合。
二、部署流程Java类的2种方式
- 把Java类部署到应用服务器类库中,例如在Tomcat服务器的lib目录中部署流程Java类的jar包。这种方式加载类的优先级最高。
- 把流程Java类部署到Web应用或企业应用相应目录中,例如WEB-INF、classes目录或WEB-INF/lib目录。Tomcat在运行时会找到流程所需的Java类并调用。这种方式加载类的优先级为次高。
三、示例:部署业务流程定义
首先配置好JBPM运行环境,即在classpath根目录下的jbpm.hibernate.cfg.xml中设置好数据库连接,并确保数据库服务正常。
第一种流程定义方式:
1>通过执行Ant脚本的方式进行部署,这需要将要部署的流程定义及其相关资源按照classpath的层次结构打包归档,假设业务流程归档为process.jar,则部署process.jar的Ant脚本如下:
<project name="jbpm.examples.process.deployment"> <!--使用jbpm.home表示JBPM4的安装目录--> <property file="jbpm.home" value="D:/jbpm-4.4" /> <!--这个Ant任务专门用来定义JBPM的运行库、依赖库以及配置文件的classpath路径--> <target name="jbpm.libs.path"> <path id="jbpm.libs.incl.dependencies"> <pathelement location="${jbpm.home}/examples/bin" /> <pathelement location="${jbpm.home}/jbpm.jar" /> <fileset dir="${jbpm.home}/lib" includes="*.jar" /> </path> </target> <target name="process.jar" depends="jbpm.libs.path"> </target> <!--使用JBPM4工作流引擎提供的Ant API——JbpmDeployTask来执行部署--> <target name="create.and.deploy.example" depends="jbpm.libs.path,process.jar"> <taskdef name="jbpm-deploy" classname="org.jbpm.pvm.internal.ant.JbpmDeployTask" classpathref="jbpm.libs.incl.dependencies"/> <!--指定流程定义打包文件--> <jbpm-deploy file="process.jar"/> </target> </project>
2>执行Ant任务create.and.deploy.examples即可部署流程定义打包process.jar至jbpm数据库中
第二种流程定义方式:
通过编写Java代码直接调用JBPM工作流引擎提供的部署服务API完成流程定义部署。
//JbpmTestCase继承了JUnit的TestCase类,是JBPM对Junit框架的扩展 public class Test extends JbpmTestCase { //保存流程定义的部署ID String deploymentId; @Override protected void setUp() throws Exception { super.setUp(); //使用RepositoryService提供的API方法从classpath中部署流程定义 deploymentId = repositoryService.createDeployment() .addResourceFromClasspath("produce.jpdl.xml").deploy(); } @Override protected void tearDown() throws Exception { //该方法会物理清楚deployementId对应的流程定义及其所有相关资源, //并关联清除基于此流程定义的流程实例、活动实例、任务、历史流程实例等所有运行时及历史的流程实体记录 repositoryService.deleteDeployment(deploymentId); super.tearDown(); } public void test(){ //@Todo } }
注意:
在以上单元测试中流程引擎服务(RepositoryService)的初始化工作室由JbpmTestCase.setUp方法完成的,为此定义了6个流程引擎服务:
protected ProcessEngine processEngine; //资源库服务 protected RepositoryService repositoryService; //执行服务 protected ExecutionService executionService; //管理服务 protected ManagementService managementService; //任务服务 protected TaskService taskService; //历史服务 protected HistoryService historyService; //身份认证服务 protected IdentityService identityService;
而JbpmTestCase在其setUp方法中初始化:
@Override protected void setUp() throws Exception { super.setUp(); processEngine = buildProcessEngine(); repositoryService = processEngine.getRepositoryService(); executionService = processEngine.getExecutionService(); historyService = processEngine.getHistoryService(); managementService = processEngine.getManagementService(); taskService = processEngine.getTaskService(); identityService = processEngine.getIdentityService(); }