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

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

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的应用部分下次再补发吧。

 

 

 

你可能感兴趣的:(spring,xml,Hibernate,workflow,jbpm)