咏叹调

我的路途没有风景

Skip to: Content | Sidebar | Footer

目录

  1. jBPM学习笔记




jBPM学习笔记

12 六月, 2009 (15:54) | JBoss, jBPM, 流程控制 | By: 黎子

该文章不是一篇结构完整的文章,是学习过程的随时记录,所以有的地方可能是上下矛盾的,前面说了这样做,后面发现不行又把它否决了,这是一个学习的过程,希望对读者你有帮助

jBPM是什么?

http://www.jboss.org/jbossjbpm/

安装(jboss4.2.2.GA,jbpm-3.2.6.SP1,jbpm-4.0.CR1)

jBPM3提供了一个安装文件,运行java -jar jbpm-installer-3.2.6.SP1.jar,按照指示做便可
但我安装完成后,提示不能jbpmtest不能访问数据库,数据库的访问要自己配置:

  1. 下载jdbc驱动,放到deploy/lib目录
  2. deploy/jbpm下修改jbpm-mysql-ds.xml(我安装时选择的数据库是mysql),把里面的数据库连接信息修改正确就行,
    jbpm默认使用的是数据源<xa-datasource>,访问时出现:
    11:49:12,609 INFO [CachedConnectionManager] Closing a connection for you. Please close them yourself: org.jboss.resource.adapter.jdbc.WrappedConnection@14096e6
    不知道是不是由于这个数据源的问题,所以我换成了<local-tx-datasource>,这个问题就没有了,关于数据源的问题,请看:
    http://www.jboss.org/file-access/default/members/jbossas/freezone/docs/Server_Configuration_Guide/4/html/Connectors_on_JBoss-Configuring_JDBC_DataSources.html.

    <datasources>
      <local-tx-datasource>
        <jndi-name>JbpmDS</jndi-name>
        <connection-url>jdbc:mysql://localhost:3306/jbpmdb</connection-url>
        <driver-class>com.mysql.jdbc.Driver</driver-class>
        <user-name>root</user-name>
        <password>root</password>
        <metadata>
          <type-mapping>mySQL</type-mapping>
        </metadata>
      </local-tx-datasource>
    </datasources>


  3. 因为安装时不能成功访问数据库,所以数据库是没有建立成功的,手工建立一个数据库,到jbpm的安装路径下的database目录,运行相应的sql脚本就建立好数据库了。
  4. 但数据库中空的,需要再执行以下的sql:
    INSERT INTO JBPM_ID_GROUP VALUES(1,'G','sales','organisation',NULL);
    INSERT INTO JBPM_ID_GROUP VALUES(2,'G','admin','security-role',NULL);
    INSERT INTO JBPM_ID_GROUP VALUES(3,'G','user','security-role',NULL);
    INSERT INTO JBPM_ID_GROUP VALUES(4,'G','hr','organisation',NULL);
    INSERT INTO JBPM_ID_GROUP VALUES(5,'G','manager','security-role',NULL);
    INSERT INTO JBPM_ID_USER VALUES(1,'U','user','user@sample.domain','user');
    INSERT INTO JBPM_ID_USER VALUES(2,'U','manager','manager@sample.domain','manager');
    INSERT INTO JBPM_ID_USER VALUES(3,'U','admin','admin@sample.domain','admin');
    INSERT INTO JBPM_ID_USER VALUES(4,'U','shipper','shipper@sample.domain','shipper');
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(1,'M',NULL,NULL,2,4);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(2,'M',NULL,NULL,3,4);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(3,'M',NULL,NULL,4,4);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(4,'M',NULL,NULL,4,3);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(5,'M',NULL,NULL,1,3);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(6,'M',NULL,NULL,2,3);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(7,'M',NULL,NULL,3,3);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(8,'M',NULL,NULL,3,2);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(9,'M',NULL,NULL,2,2);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(10,'M',NULL,NULL,2,5);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(11,'M',NULL,'boss',2,1);
    INSERT INTO JBPM_ID_MEMBERSHIP VALUES(12,'M',NULL,NULL,1,1);
  5. deploy/jbpm/jbpm-service.sar/hibernate.cfg.xml把其中的数据库方言修改正确:org.hibernate.dialect.MySQLDialect

以上是jbpm3的安装,它提供了安装程序,要方便点,但我感觉jbpm4比jbpm3出现的大的变化,例如在jbpm3中jpdl是存放在一个processdefinition.xml文件中,gpd存放在一个单独的文件gpd.xml中,但在jbpm4中所有的都是存放在一个.jpdl.xml文件中。似乎结构变了。

jbpm4的安装
jbpm-4.0.CR1没有提供安装程序,只提供了ant脚本,而且提供的ant脚本是把jboss 5,jbpm及eclipse全部一起安装,对于我们已经有了jboss4.2.2GA的情况,它的脚本肯定不行,所以我们要修改一下它的ant脚本

  1. 修改jboss/build.xml文件:
    • <property name=”jboss.version” value=”4.2.2.GA” /><!–我们使用的是4.2.2.GA–>
    • <property name=”database” value=”mysql” /><!–jBPM默认使用hsqldb,我们修改为使用 mysql–>
    • <target name=”demo.setup” depends=”install.jbpm.into.jboss” ><!– 我们需要在jboss中安装jBPM所以删除depends中的install.jboss, start.jboss–>
    • <ant antfile=”${jbpm.home}/gpd/build.xml” target=”install.eclipse” />
      <ant antfile=”${jbpm.home}/gpd/build.xml” target=”start.eclipse” />
      删除这两项,我们不需要下载eclipse,
    • jBPM默认使用hsqldb,我们修改为使用mysql,到jbpm解压目录/db /jdbc目录中,修改mysql.properties文件(其中连接的数据库事先要建立),
    • 到jbpm解压目录/db目录中,修改build.sql 文件: <property name=”database” value=”mysql” />
    • 如果要安装example,也需要把修改example下面的build.xml,修改数据库及jboss版本
    • 从命令行中进行到jboss目录,运行:ant demo.setup
    • 由于我们设置的jboss版本是4.2.2.GA,ant执行完成后会在jBPM目录下生成一个jboss-4.2.2.GA目录,现在我们只需要把这只目录的内容拷贝到jboss的安装目录覆盖其中的目录即可。
    • 由于jbpm的ant脚本只考虑了jboss5,所以对于jboss4,我们还需要手动拷贝:
      1) 在jbpm/jbpm-service.sar下建立jbpm.deployer目录,结构如下:
      jbpm12
      2) 拷贝config.jboss4\deploy\jbpm\jbpm-service.sar\jboss-beans.xml文件到jbpm.deployer\jbpm.beans\META-INF中.
      2) 拷贝config.jboss4\deploy\jbpm\jbpm-service.sar\META-INF\jboss-service.xml到jbpm.deployer\META-INF\中
      3)把jbpm/lib目录中的jbpm-spi.jar,jbpm-jboss4.jar拷贝到jbpm.deployer下面
    • 如果安装了例子,则在example/target中有两个文件:examples.bar和examples.jar,部署到jboss的部署目录里

更多的细节可以查看jbpm中的doc
安装下来,jbpm4与jbpm3改变还是很大的
至此,jbpm就安装完成,接下来就看是如何开发及部署jbpm的。如果在jboss启动报错:

— MBEANS THAT ARE THE ROOT CAUSE OF THE PROBLEM —
ObjectName: jboss.j2ee:service=EjbModule,module=jbpm-enterprise.jar
State: FAILED
Reason: org.jboss.deployment.DeploymentException: type-mapping is not initialized: java:/JbpmDS was not deployed or type-mapping was not configured.

同时你也确定jbpm/jbpm-mysql-ds.xml中的jndi-name确实是JbpmDS。那就把<type-mapping>MySQL</type-mapping>修改为<type-mapping>mySQL</type-mapping>。因为在default/conf/standardjbosscmp-jdbc.xml定义的就是:<name>mySQL</name>,区分大小写,所以要一至,可能笔误写成了Mysql,jbpm-3.2.6.SP1的文档中也是大写的,可能也是笔误

在eclipse中安装gpd参考jBPM文档,这不会有什么问题,唯一的问题可能是安装gdp时eclipse没有一些需要的插件,这可能是eclipse的版本问题,下载个for java ee的版本。

部署jbpm应用

我们先来看如何部署,这里以jbpm4为例,之前运行的ant demo.setup在example中生成了一个target目录,把其中的examples.jar(代码),examples.bar(jpdl定义)部署到deploy下面得到的提示是:Packages waiting for a deployer。jbpm是安装好的,也就是说JBPMDeployer是准备好的,那就有可能是不认识.bar文件,jbpm 用户文档上说,部署jbpm只需要所.jpdl.xml文件及代码打包成一个.jpbl的压缩文件就可以(但不知道例子为什么要把.jpdl.xml与代码分开),所以把example.bar修改成example.jpdl就可以部署了,到gwt-console就能看到部署的process:
jbpm2

然后我选择Custom-9,点击Process Instances,进去后是没有Instance的,可以点击Start开始执行一个process,这时候我想看控件台有什么反应,一看吓我一跳,看下图:
jbpm3

也为我两天来的学习找到点乐子

EJB与jBPM集成的例子

jBPM已经在jboss中配置好了,也成功部署了例子,现在要试试如何把jBPM与EJB3集合起来,使用jBPM定义流程,使用EJB3完成任务。那我们分成两个项目:定义jBPM的项目及ejb3项目,把它们分别部署,jbpm的例子中把java代码及jpdl.xml分开打开可能就是这样的考虑。ejb3作为提供业务服务,在没有jbpm的情况下它也能服务其它(如提供远程访问),jbpm项目只是定义business process,使用已有的ejb3提供的服务来完成。这样它们可能分别开发,分别部署,都是在jboss下,这很容易做到。

做为例子假想一个业务流程:订单请求,该订单请求需要salesman和admin都同意

先来建立一个ejb项目,EJB3项目建立一个简单的,如下图:

ejb3demo

部分代码如下(只节选部分):

@Stateless
public class AdminCheckoutFacadeBean implements CheckoutFacade {
	@Override
	public boolean agreeOrderFor(Integer userid) {
		System.out.println("------------假设admin除了1外,所有人都同意,传入的用户是:"+userid);
		return !userid.equals(1);
	}
}
@Stateless
public class AutoCheckoutFacadeBean implements CheckoutFacade {
	@EJB(beanName="OrderFacadeBean")
	OrderFacade bean;
 
	@Override
	public boolean agreeOrderFor(Integer userid) {
		//这里只是试验流程自动调用java(ejb)代码
		System.out.println("------------订单请求的自动检测假设所有人都同意,传入的用户是:"+userid);
		//为了验证AutoCheckoutFacadeBean被调用时是作为一个POJO调用,还是一个EJB SLSB组件调用
		//这里把依赖注入的session打印出来,如果有值,应该就是被作为一个SLSB调用的
		//如果没有值,那么说明只是作为一个POJO被调用,虽然是在jboss环境中,但其中的EJB注册符号没有被处理
		System.out.println("------------通过@EJB依赖注入的是:"+bean);
		//如果依赖注入不行,直接使用jndi试试
		InitialContext ctx = null;
			try {
				ctx = new InitialContext();
				bean = (OrderFacade) ctx.lookup("OrderFacadeBean/remote");
			}catch (NamingException e) {
				e.printStackTrace();
			}
			System.out.println("------------直接通过jndi找的是:"+bean);
		return true;
	}
}
@Stateless
public class StartJBPMFacadeBean implements StartJBPMFacade {
	@EJB(beanName="AdminCheckoutFacadeBean")
	CheckoutFacade adminCheck;
 
	/**
	 * 启动jBPM,并返回process id,这里只是为了测试,后面会用到该id
	 */
	@Override
	public String start() {
		System.out.println("-------------(StartJBPMFacadeBean)do it-------------");
		ProcessEngine processEngine;
		InitialContext ctx = null;
		try {
			ctx = new InitialContext();
			processEngine = (ProcessEngine) ctx.lookup("java:/ProcessEngine");
 
			RepositoryService repositoryService = processEngine.getRepositoryService();
			ExecutionService executionService = processEngine.getExecutionService();
 
			//部署jbpm
			long deploymentDbid = repositoryService.createDeployment()
		    .addResourceFromClasspath("cn/li_zone/Order.jpdl.xml")
		    .deploy();
 
			int checkUserId = 20002;//just for test
 
			//variables中的map值可以在jpdl中通过#{userid}取到
			Map variables = new HashMap();
			variables.put("userid", checkUserId);
			//开始jpdl定义的过程,该过程会到wait for admin agree这一步停下来
			//直到admin来处理,也就是要调用下面的adminCheckOrderRequest,过程才往前走
			ProcessInstance processInstance = executionService.startProcessInstanceByKey("OrderRequire",variables);
			//检查自动AutoCheckFacadeBean的返回结果,一个方法的返回结果在jpdl中通过var属性定义
			System.out.println("----------- AutoCheckFacadeBean.agreeOrderFor:"
					+executionService.getVariable(processInstance.getId(),"autocheckIsPass"));
			return processInstance.getId();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("just for test,something wrong");
		}
	}
 
	@Override
	public void adminCheckOrderRequest(String processid) {
		//admin处理用户的order请求,这个处理前得让admin知道有这么一个事件你要去处理,
		//然后admin调用到该接口(比如通过gui上的操作)
		int userid = 20002;//just for test
		String result = /*业务处理*/adminCheck.agreeOrderFor(userid) ? "admin accept" : "admin reject";//该值是信号值
		ProcessEngine processEngine;
		InitialContext ctx = null;
		try {
			ctx = new InitialContext();
			processEngine = (ProcessEngine) ctx.lookup("java:/ProcessEngine");
			ExecutionService executionService = processEngine.getExecutionService();
			ProcessInstance processInstance = executionService.findProcessInstanceById(processid);
		    String executionId = processInstance.findActiveExecutionIn("wait for admin agree").getId();
		    processInstance = executionService.signalExecutionById(executionId, result);
		    System.out.println("----------"+result+",so the process is "+processInstance.getState());
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("just for test,something wrong");
		}
	}
 
}
public class TestCase {
	...
	@Test
	public void orderRequest(){
		//先用户请求
		try{
			System.out.println(remote.start());
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	@Test
	public void adminCheck(){
		//jpdl中定义了要等admin确认
		//所以这里测试admin确认过程
		//并给process发送信号,使其根据admin处理结果走后面的流程
		try{
			remote.adminCheckOrderRequest("OrderRequire.10");//为了测试分开执行,两个test分开执行,所以这里手工把上一个测试结果拷贝过来
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

然后是jBPM项目,它很简单,只是定义一个流程,如下图:
jbpmejb3

jpdl图如下:
jpdl

xml定义如下:

<?xml version=”1.0″ encoding=”UTF-8″?>

<process description=”order business process” name=”OrderRequire” xmlns=”http://jbpm.org/4.0/jpdl”>
<start g=”238,28,62,48″>
<transition g=”-84,-18″ name=”to order request” to=”order request”/>
</start>
<java g=”217,118,92,52″ method=”orderRequest” name=”order request” var=”userid”>
<arg><object expr=”#{userid}”/></arg>
<transition name=”to auto check” to=”auto check” g=”-70,-18″/>
</java>
<java g=”166,205,193,52″ method=”agreeOrderFor” name=”auto check” var=”autocheckIsPass”>
<arg><object expr=”#{userid}”/></arg>
<transition g=”-117,-18″ name=”to wait for admin agree” to=”wait for admin agree”/>
</java>
<state g=”182,310,161,52″ name=”wait for admin agree”>
<transition g=”-42,-18″ name=”admin accept” to=”end1″/>
<transition g=”-48,-18″ name=”admin reject” to=”error1″/>
</state>
<end g=”239,449,48,48″ name=”end1″/>
<end-error g=”478,312,48,48″ name=”error1″/>
</process>

测试

先运行orderRequest,模拟一个客户的业务请求,jboss控件台输出如下:
jbpmejb

这说明:

  • 流程根据jpdl的定义,走到wait for admin agree就停下来了。
  • AutoCheckoutFacadeBean被当作POJO运行,而不是ejb的slsb运行,所以EJB注解不被处理,如果要使用其它ejb资源只有使用jndi去找了,不知道是不是什么地方可以设置
  • <java var=”return”>该jpdl定义中的rturn将保存调用的java类的方法的返回值

接下来运行adminCheck,模拟admin的操作,该操作将接着处理上面流程,上面在等待admin作返回,该测试就作出相应的反应,调用后运行结果如下:
jbpmejb1

总结

  • 到此,只一个简单的安装配置,及写了一个简单的例子并部署,试着与ejb集成一下
  • 要真正把jBPM用起来,先要了解jBPM的各种概念,方法,也就是它的思想
  • 同样的流程目的,使用不同的jpdl activity都可以做到,后面再慢慢了解jpdl。
  • 前一个状态与后一状态没有直接的信息传递关系或者调用关系,如例子中的order request到auto check并不是说order request调用了auto check,虽然它们都定义的是两个java代码,流程不同状态间的过度关系是由jbpm的ExecutionService负责的,流程的每个状态表示了一个流程的关键点,在这些关系点是可能会执行一些事件,处理。或者等待决定走那上部
  • jpdl定义了一个流程的可能有的走向,但具体实现情况流程怎么走,是由代码决定的,比如“wait for admin agree”,如果AdminCheckoutFacadeBean得到的结果是“admin accept”和“admin reject”将走不同的流程
  • 流程不一定是顺序执行的,同一个流程也可以有几个过程是同步并发执行的
  • 流程也可以异步跨时间空间的执行,比如例子中的“wait for admin agree”,流程走到这里就停下来,等待admin来处理

Comments

Comment from 过客
Time 2009年06月18日 at 5:07 下午

不错,帮我挺大忙的,呵呵

Comment from xijunhu2008
Time 2009年07月16日 at 3:03 下午

兄弟,请问你的jbpm4的安装是自己研究出来的吗?我用的是jboss5.0,按你方法安装了之后,进入http://localhost:8080/gwt-console后输入用户名和密码提交后,是一个白白的页面,什么也没有,而且鼠标一直是漏斗状,不知道什么原因?

Comment from 黎子
Time 2009年07月20日 at 10:45 上午

是的,但我用的是jboss4.2.2GA,所以要手动修改很多地方,如果是jboss5.0的话,应该没有这么麻烦,如果你的jboss5.0已经安装好的话,直接运行jbpm的ant脚本,把它生成的文件拷贝到你的jboss里面去。
或者不要修改jbpm的任何内容,直接运行它的ant脚本,它会直接下载jboss5.0和eclipse并把它们安装好,下载eclipse时可以中断它,然后进gwt-console看看行不行

另外要注意数据库的配置
你的情况我猜可能是jboss出现异常了,但gwt-console并不知道,它一直在等待,把jboss的日志修改成debug看看jboss的反应

祝你早日解决问题@_@

Write a comment