欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

struts2,spring,jbpm实战——spring和jbpm的详细配置

程序员文章站 2022-03-14 08:26:30
...

<!----><!----> <!---->

Spring jbpm 整合还是挺复杂的,我把整合的过程以及遇到的一些问题和大家共同分享一下,有不当之处,希望大家指正。我的开发环境是xp3,我所用到的框架版本如下:

 

struts2.0.11并且降级支持JDK1.4

spring2.0,

spring-modules-jbpm31-0.6.jar
jbpm3.1.4

hibernate.3.2.3

 

 

附件就是源码,请参照里面的readme.txt进行相应的配置。

 

<!----><!----> <!---->

Spring 中配置 jbpm 所使用的 SessionFactory

 

Spring 其它该怎么配还是怎么配置,对于 jbpm 所依赖的 sessionFactory ,我是这么配置的:

 

 

 

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource">
			<ref bean="dataSource" />
		</property>
		<property name="mappingLocations">
			<list>       
			    <!-- jbpm's hbm.xml -->
    				<value>classpath*:/org/jbpm/**/*.hbm.xml</value>
    			
    			<!-- other hbm.xml -->
    		<value>classpath*:/com/resoft/system/domain/*.hbm.xml</value>      
  		 	</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
				<prop key="show_sql">true</prop>
				<prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
			</props>
		</property>
	</bean>

 

<!----><!----> <!---->

我看有的喜欢将配置

 

 

<property name="mappingLocations">
			<list>       
			    <!-- jbpm's 的hbm.xml -->
    				<value>classpath*:/org/jbpm/**/*.hbm.xml</value>
    			
    			<!-- 其它地方自己配置的 hbm.xml -->
    				<value>classpath*:/com/resoft/system/domain/*.hbm.xml</value>      
  		 	</list>
		</property>
 

 

<!----><!----> <!---->

配置成:

 

 

<property name="configLocations">       
 			 <list>       
    				<value>classpath:/hibernate.cfg.xml</value>  
  		   		......
  		 	</list>       
		</property>
 

 

<!----><!----> <!---->

其实也行,不过hibernate.cfg.xml 文件对于后续的 spring jbpm 整合也没有多大用处,尽量争取统一在一处配置即可。下面还会有详细说明。

还有一点就是本 demo 是一个很简单的整合实例,所以其它的 PO 对象所对应的表也和 jbpm 放在一起。实际用的时候,可能需要配置多个数据源,多个 sessionFactory ,并用 JTA 来进行全局事务处理。

 

 

<!----><!----> <!---->

开始在 Spring 中配置 jbpm

 

请注意,现在才是配置的开始,继续在spring的xml中配置

 

 

 

 

<!-- 关于jbpmConfiguration的配置 -->
	<bean id="jbpmConfiguration" class="org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean">
		<property name="sessionFactory" ref="sessionFactory" />
		<property name="configuration" value="classpath:jbpm.cfg.xml" />
		<!-- 第一处需要注意的地方 -->
		<property name="processDefinitions">
			<list>
				<ref local="holidayWorkflow" />
			</list>
		</property>
		<!-- 没什么好说的,是否需要重新建表 -->
		<property name="createSchema" value="${jbpmConfiguration.createSchema}" />
	</bean>


<bean id="holidayWorkflow" class="org.springmodules.workflow.jbpm31.definition.ProcessDefinitionFactoryBean">
	<!-- 第二处需要注意的地方 -->
		<property name="definitionLocation" value="classpath:jbmp/workflow/holiday/holiday.zip" />
	</bean>

	<bean id="holidayTemplate" class="org.springmodules.workflow.jbpm31.JbpmTemplate">
		<constructor-arg index="0" ref="jbpmConfiguration" />
		<!-- 第三处需要注意的地方 -->
		<constructor-arg index="1" ref="holidayWorkflow" />
	</bean>

 

 

<!----><!----> <!---->

先看

 

 

<!-- 第一处需要注意的地方 -->
		<property name="processDefinitions">
			<list>
				<ref local="holidayWorkflow" />
			</list>
		</property>

 

<!----><!----> <!---->

这样配置后,每次重新启动的时候,都会重新发布 list 里的流程,开发的时候建议这么做。

 

再看

 

 

<!-- 第二处需要注意的地方 -->
	<property name="definitionLocation" value="classpath:jbmp/workflow/holiday/holiday.zip" />
 

 

<!----><!----> <!---->

以前整合过的人非常奇怪我这个地方怎么是一个 zip ,而不是什么 processdefinition.xml 文件之类的。最开始的时候,我下载了 javaeye 中某个人写的代码,里面说流程发布一定要通过插件发布,不能通过代码, 后来慢慢在自己摸索中发现,这是 springmodule 的一个不完善的地方。所谓的插件形式发布,我看了代码,就是调用一个执行上传 zip 包的 servlet 。然后再通过 jbpm API zip 包中的 gpd.xml processdefinition.xml processimage.jpg 解析发布,最后可以在页面使用 jbmp 自带的 jsp tag 标签 ( 可以随便修改 ) ,将 processimage.jpg 所在流程的正确位置显示出来。

 

好了,下载 spring-module-jbmp3.1 的源码打开

org.springmodules.workflow.jbpm31.definition. ProcessDefinitionFactoryBean 类,大概在第 56 行左右的地方,将

 

 

InputStream inputStream = null;
		try {
			inputStream = this.definitionLocation.getInputStream();
			this.processDefinition = ProcessDefinition.parseXmlInputStream(inputStream);

		}
 

<!----><!----> <!---->

改成

 

 

ZipInputStream inputStream = null;
		try {
			inputStream = new ZipInputStream(definitionLocation.getURL().openStream());
			this.processDefinition = ProcessDefinition.parseParZipInputStream(inputStream);

		}

 

<!----><!----> <!---->

这样,就可以注入 zip 包了。

 

最后看看

 

 

<!-- 第三处需要注意的地方 -->
		<constructor-arg index="1" ref="holidayWorkflow" />
 

 

<!----><!----> <!---->

这里的 holidayWorkflow 就是上面配置的一个 processDefinition 。有的人说此处可有可无。我看了 JbpmTemplate 的源码,也确实存在有一个参数的构造函数:

 

 

public JbpmTemplate(JbpmConfiguration jbpmConfiguration) {
		this.jbpmConfiguration = jbpmConfiguration;
	}
 

 

<!----><!----> <!---->

但是如果你想使用这个 JbpmTemplate ,如果某个方法涉及到要使用 processDefinition ,那么就会抛出空指针异常。比如在 JbpmTemplate 源码中,大概第 230 行的如下方法:

 

 

 

public List findProcessInstances() {
		return (List) execute(new JbpmCallback() {

			public Object doInJbpm(JbpmContext context) {
				return context.getGraphSession().findProcessInstances(processDefinition.getId());//这里就使用到了processDefinition
			}
		});
	}
 

 

 

<!----><!----> <!---->

 

至于 jbpm 其它方面的配置可以参考 spring-module 的文档中第 9 章的  jBPM 3.1.x 。我这里简单说说。

 

比如我在 spring 中配置了如下 ApplyAssignmentHandler

 

 

<bean id="applyAssignmentHandler" class="com.resoft.workflow.ApplyAssignmentHandler">
		<property name="userService" ref="userService" />
	</bean>

 

<!----><!----> <!---->

这个时候在我的 ApplyAssignmentHandler 就可以享受 spring IoC 优势,帮我把我需要用的那个 userService 注入进去,供我调用:

 

 

public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception {
   	userService.find(hql); // 现在可以使用注入后的userService
		assignable.setActorId(Constants.USER);// 将任务分配给单个用户
	}
 

 

<!----><!----> <!---->

但是可别忘记了还得在你的流程定义配置文件 processdefinition.xml 中,也配置上

 

 

<assignment class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy" config-type="bean">
				<factoryKey>jbpmConfiguration</factoryKey>
				<targetBean>applyAssignmentHandler</targetBean>
			</assignment>

 

 

<!----><!----><!----> <!---->

两边配置自然觉得还是有点麻烦,希望以后再简化争取一个地方配置就完事了。

 

 

 

关于 hibernate.cfg.xml 是否需要的详细分析

 

是时候谈谈 hibernate.cfg.xml 的时候了。

 

查看 jbpm-3.1.4 的源码。找到 org.jbpm.persistence.db.DbPersistenceServiceFactory 类,在大概 82 行左右的地方,有如下方法:

 

//得到jbpm的SessionFactory
public synchronized SessionFactory getSessionFactory() {
    if (sessionFactory==null) { //与spring整合后,这里永远不会为null

      if (sessionFactoryJndiName!=null) {
        log.debug("looking up hibernate session factory in jndi '"+sessionFactoryJndiName+"'");
        sessionFactory = (SessionFactory) JndiUtil.lookup(sessionFactoryJndiName, SessionFactory.class);
        
      } else {
        log.debug("building hibernate session factory");
        sessionFactory = getConfiguration().buildSessionFactory(); //注意这一句中的getConfiguration()
      }
    }
    return sessionFactory;
  }
 

<!----><!----> <!---->

该方法在上方第 59 行左右,我们看到了 getConfiguration() 方法:

 

 

public synchronized Configuration getConfiguration() {
    if (configuration==null) {
      String hibernateCfgXmlResource = null;
      if (JbpmConfiguration.Configs.hasObject("resource.hibernate.cfg.xml")) {
        hibernateCfgXmlResource = JbpmConfiguration.Configs.getString("resource.hibernate.cfg.xml"); //取得jbpm.cfg.xml中配置的hibernate.cfg.xml
      }
      String hibernatePropertiesResource = null;
      if (JbpmConfiguration.Configs.hasObject("resource.hibernate.properties")) {
        hibernatePropertiesResource = JbpmConfiguration.Configs.getString("resource.hibernate.properties"); //取得jbpm.cfg.xml中配置的hibernate.properties
      }
      configuration = HibernateHelper.createConfiguration(hibernateCfgXmlResource, hibernatePropertiesResource);
    }
    return configuration;
  }

 

 

<!----><!----> <!---->

因为 spring 已经为注入好了 SessionFactory ,所以 if (sessionFactory==null) 永远为 false ,那这又意味着什么呢?

还记得 classpath 下的 jbpm.cfg.xml ? 里面就有关于 hibernate.cfg.xml 的配置:

 

 

 

<string name="resource.hibernate.cfg.xml" value="hibernate.cfg.xml" />

 

<!----><!----> <!---->

但通过上面的源码,咱们还发现其实 hibernate.properties 也可以类似的定义如下:

 

 

<string name="resource.hibernate.properties" value="hibernate.properties" />
 

 

<!----><!----> <!---->

只不过大家一般都是直接照着把 default.jbpm.cfg.xml 的内容直接复制出来,而且一般配置了 hibernate.cfg.xml 就足够了。

 

最后因为 if (sessionFactory==null) 永远为 false ,所以 getConfiguration() 方法自始至终都不会被调用到,就算你在 jbpm.cfg.xml 配置了 jbpm.cfg.xmlresource.hibernate.cfg.xml,resource.hibernate.properties 也是没有作用的。因此 jbpm spring 整合后, hibernate.cfg.xml hibernate.propties 两个文件可以不要。 ( 有其它用途除外 )

 

一切似乎至此可以结束了,但我们还是忽悠了一个小的细节。

 

<!----><!----> <!---->

当我删除测试数据很多的数据库,想重新利用 jbpm 自动建库建表功能时 ( 只需要将下面的配置 value 设置为 true ) ,问题出现了。

 

 

<!-- 没什么好说的,是否需要重新建表 -->
		<property name="createSchema" value="true" />
 

 

 

<!----><!----> <!---->

控制台上出现:

 

 

Caused by: org.hibernate.HibernateException: /hibernate.cfg.xml not found

 

<!----><!----> <!---->

难道上面我分析的不对, hibernate.cfg.xml 始终不能少。带着疑问,我继续打着断点,终于依次看到下述代码(注意,以下的跟踪需要 spring-module jbpm hibernate 的源码支持 ):

 

org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean 150

org.jbpm.JbpmConfiguration 410

org.jbpm.persistence.db.DbPersistenceServiceFactory 108

org.jbpm.persistence.db.DbPersistenceServiceFactory 69

最后来到 org.jbpm.db.hibernate.HibernateHelper 83 行,看到如下代码:

 

 

  if (cfgXmlResource!=null) {
      // use the configured file name
      log.debug("creating hibernate configuration resource '"+cfgXmlResource+"'");
      configuration.configure(cfgXmlResource);
    } else {
      log.debug("using default hibernate configuration resource (hibernate.cfg.xml)");
      configuration.configure();
    }
 

 

 

 

<!----><!----> <!---->

因为没有配置 hibernate.cfg.xml( jbpm.cfg.xml 中可以配置该文件路径 ) ,所以自然会调用 else 中的 configuration.configure() 。当我再次跟进去后,一切真相大白。在 org.hibernate.cfg.Configuration 类中的 1413 行出现了下面代码:

 

 

public Configuration configure() throws HibernateException {
		configure( "/hibernate.cfg.xml" );
		return this;
	}
 

 

<!----><!----> <!---->

可见,如果你想依赖于 jbpm 的建库功能, hibernate.cfg.xml 不能少。

 

最后结论,如果你的 jbpm 建库不依赖于它的 API hibernate.cfg.xml 可以不要,否则目前还是必须配置一下。

 

 

至此, spring jbpm 整合结束。

 

 

 

文章太长了,结合struts2的应用部分下次再补发吧。