`

Spring, Springmodules, JBPM持久化集成--优雅的回调(转)

阅读更多
【转自:http://ginge.iteye.com/blog/165996】
Spring, Springmodules, JBPM持久化集成--优雅的回调

本系列文章假设阅者具备以下知识:
1,ThreadLocal相关知识
2,Spring持久化,事务管理相关知识
3,Hibernate 持久化相关知识
4,Jbpm 持久化相关知识
5,Springmodules项目相关知识
6,相关J2EE知识


且具备以下材料:
1,Spring源代码
2,Hibernate源代码
3,Jbpm源代码
4,Springmodules源代码


接触JBPM始于两年多前,当时还在广州一个公司实习,公司打算研发ITIL相关的项目,于是就开始知道了工作流,也就开始接触了JBPM,但是对比过不同的工作流,还是觉得JBPM有前途,因为它隶属于JBOSS,社区活跃。当然,和其他JBOSS项目一样缺乏足够的支持,最有用的就是附带的User Guide Reference。


现在受Spring影响太深了,接触什么项目想到的首先就是尝试和Spring集成。接触JBPM也不例外,不过与JBPM和Spring的集成,已经有人做了,这个项目就是SpringModules-jbpm了,而且做的很优雅,充分体现了Spring提倡的Inversion of Control的美。


我尝试将这种美表述出来与大家分享,首先我会摘取Spring Modules手册里与JBPM集成的部分内容。跟着会引用一部分JBPM的手册里的相关章节和阅读一部分相关代码分析SpringModules集成的原理。最后谈一谈JBPM持久化与Spring事务管理的集成。相信能够让困惑于LazyLoading的朋友一些有益的启示。


一,SpringModules与JBPM的集成
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xmlns:jee="http://www.springframework.org/schema/jee"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd   
            http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">  
    <!-- framework's contexts -->  
  
    <bean id="resource.PropertyConfigurer"  
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
        <property name="locations">  
            <list>  
                <value>classpath:jdbc.properties</value>  
            </list>  
        </property>  
    </bean>  
  
  
  
    <!-- JNDI DataSource for J2EE environments -->  
    <!--jee:jndi-lookup id="dataSource" jndi-name="java:/comp/env/jdbc/moss" /-->  
    <bean id="resource.DataSource"  
        class="org.apache.commons.dbcp.BasicDataSource"  
        destroy-method="close">  
        <property name="driverClassName"  
            value="${appserver.jdbc.driverClassName}" />  
        <property name="url" value="${appserver.jdbc.url}" />  
        <property name="username" value="${appserver.jdbc.username}" />  
        <property name="password" value="${appserver.jdbc.password}" />  
        <property name="maxActive" value="${appserver.jdbc.maxActive}" />  
        <property name="maxIdle" value="${appserver.jdbc.maxIdle}" />  
        <property name="maxWait" value="${appserver.jdbc.maxWait}" />  
        <property name="defaultAutoCommit"  
            value="${appserver.jdbc.defaultAutoCommit}" />  
        <property name="removeAbandoned"  
            value="${appserver.jdbc.removeAbandoned}" />  
        <property name="removeAbandonedTimeout"  
            value="${appserver.jdbc.removeAbandonedTimeout}" />  
    </bean>  
  
  
  
    <!-- Hibernate SessionFactory -->  
    <bean id="resource.SessionFactory"  
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
        <property name="dataSource" ref="resource.DataSource" />  
        <property name="configLocations">  
            <list>  
                <value>classpath*:hibernate.cfg.xml</value>  
            </list>  
        </property>  
    </bean>  
       
    <!-- jBPM configuration -->  
    <bean id="resource.JbpmConfiguration"  
        class="org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean">  
        <property name="configuration" value="classpath:jbpm.cfg.xml" />  
        <property name="createSchema" value="false" />  
        <property name="sessionFactory">  
          <ref local="resource.SessionFactory"/>  
        </property>  
    </bean>  
       
    <!-- jBPM template -->  
    <bean id="resource.JbpmTemplate"  
        class="org.springmodules.workflow.jbpm31.JbpmTemplate">  
        <constructor-arg index="0" ref="resource.JbpmConfiguration" />  
    </bean>  
  
</beans>  


LocalJbpmConfigurationFactoryBean

最重要的就是LocalJbpmConfigurationFactoryBean了,相信Spring的用户对这样的Bean都很熟悉。根据jbpmConfiguration配置文件和提供的sessionFactory,它会创建一个可以与提供的流程定义协同工作的jBPM Configuration。sessionFactory属性可有可无,如果提供了,将会使用sessionFactory提供的session进行数据库操作。但是如果注入了sessionFactory,就可以重用Spring提供的事务管理架构,且不需要对jBPM做任何代码修改,还可以结合使用OpenSessionInView模式,好处显而易见吧。


JbpmTemplate and JbpmCallback

像Spring与Hibernate,JDBC的集成有相应的Template, Callback类一样,Spring与Jbpm的集成也有JbpmTemplate和JbpmCallback,且默认提供了一些便利方法供用户使用,例如findProcessInstance,findPooledTaskInstances,saveProcessInstance,signal等。

因为JbpmTemplate继承了JbpmAccessor,父类JbpmAccessor实现了InitializingBean,所以Spring在初始化这个JbpmTemplate时会调用afterPropertiesSet方法来配置JbpmTemplate所使用到的HibernateTemplate。JbpmAccessor 还做了一些访问异常的转换操作。



这两个类协同起来要完成的事情是,如果提供了SessionFactory或者
HibernateTemplate, 在JbpmCallback里的doInJbpm方法之前注入SessionFactory提供的hibernate session。如果没有提供SessionFactory,那么持久化的操作还是交回给Jbpm。如果存在SessionFactory, session的打开,关闭就由SessionFactory管理。否则由JBPM自身的持久化逻辑来维护。
这部分逻辑可以查看HibernateTemplate的execute方法:
public Object execute(final JbpmCallback callback) {   
        final JbpmContext context = getContext();   
  
        try {   
            // use the hibernateTemplate is present and if needed   
            if (hibernateTemplate != null && hasPersistenceService) {   
  
                // use hibernate template   
                return hibernateTemplate.execute(new HibernateCallback() {   
                    /**  
                     * @see org.springframework.orm.hibernate3.HibernateCallback#doInHibernate(org.hibernate.Session)  
                     */  
                    public Object doInHibernate(Session session) throws HibernateException, SQLException {   
                        // inject the session in the context   
                        context.setSession(session);   
                        return callback.doInJbpm(context);   
                    }   
                });   
            }   
  
            // plain callback invocation (no template w/ persistence)   
            return callback.doInJbpm(context);   
  
        }   
        catch (JbpmException ex) {   
            throw convertJbpmException(ex);   
        }   
        finally {   
            releaseContext(context);   
        }   
  
    }  

本篇尝试探讨SpringModules集成的背后逻辑,这涉及到JbpmContext持久化的哲学。 

  JbpmContext持久化管理的哲学是,如果用户提供Context数据库连接,那么用户就要管理该数据库的打开与关闭。如果使用JbpmContext自己产生的持久化服务,那么该服务就要管理与持久化相关的操作。很像各人自扫门前雪,哪管他人瓦上霜。

这里首先引用Jbpm User Guide里面关于持久化的描述:
引用

7.5. User provided stuff

You can also programmatically provide JDBC connections, hibernate sessions and hibernate session factories to jBPM.

When such a resource is provided to jBPM, it will use the provided resources rather then the configured ones.

The JbpmContext class contains some convenience methods to inject resources programmatically. For example, to provide a JDBC connectio to jBPM, use the following code:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
Connection connection = ...;
jbpmContext.setConnection(connection);

// invoke one or more persistence operations

} finally {
jbpmContext.close();
}

the JbpmContext class has following convenience methods for providing resource programmatically:

JbpmContext.setConnection(Connection);
JbpmContext.setSession(Session);
JbpmContext.setSessionFactory(SessionFactory);

上面的文字Jbpm就声明了它只管它自己的持久化逻辑,至于别人塞过来的,对不起,那是别人的事情。我想,很多人在看到Jbpm User guide的这些文字的时候,都会想弄明白藏在这些文字背后的逻辑的。  
  Jbpm中所有数据库操作的入口都是JbpmContext,各种的数据库操作例如save,JbpmContext都会委派给不同的xxxSession,例如GraphSession,TaskMgmtSession等来处理。

假设这个操作是loadTaskInstance,我将整个过程就缩小到我们关心的领域,一个是连接的打开,另外一个是连接的关闭:

第一部分,Session的打开:

【位置JbpmContext】
public TaskInstance loadTaskInstance(long taskInstanceId) {   
    return getTaskMgmtSession().loadTaskInstance(taskInstanceId);   
}   
  
public TaskMgmtSession getTaskMgmtSession() {   
    PersistenceService persistenceService = getPersistenceService();   
    if (persistenceService==null) return null;   
    return (persistenceService!=null ? persistenceService.getTaskMgmtSession() : null);  

这个PersistenceService就是jbpm.cfg.xml里面配置的org.jbpm.persistence.db.DbPersistenceService了,在上面getTaskMgmtSession这个方面里面PersistenceService就返回了一个带有一个Hibernate session的TaskMgmtSession了。

如下所示:
【位置DbPersistenceService】
public TaskMgmtSession getTaskMgmtSession() {   
    if (taskMgmtSession==null) {   
      Session session = getSession();   
      if (session!=null) {   
        taskMgmtSession = new TaskMgmtSession(session);   
      }   
    }   
    return taskMgmtSession;   
  }  


好了,到此我们就差不多达到Jbpm管理Session的环节了,我们再进入到getSession方法,看看它到底做了什么事情:

准备工作:1)以下是会涉及的布尔变量,以及其默认值:
【位置DbPersistenceService】
Connection connection = null;   
  boolean mustConnectionBeClosed = false;   
  
  Transaction transaction = null;   
  boolean isTransactionEnabled = true;   
  boolean isCurrentSessionEnabled = false;   
  boolean isRollbackOnly = false;   
  
  Session session;   
  boolean mustSessionBeFlushed = false;   
boolean mustSessionBeClosed = false;  

准备工作2)由于DbPersistenceService只有一个Constructor,也就是
【位置DbPersistenceService】
public DbPersistenceService(DbPersistenceServiceFactory persistenceServiceFactory) {
    this.persistenceServiceFactory = persistenceServiceFactory;
    this.isTransactionEnabled = persistenceServiceFactory.isTransactionEnabled();
    this.isCurrentSessionEnabled = persistenceServiceFactory.isCurrentSessionEnabled();
  } 

在这里我们就知道了,进行数据库操作所用到Session,SessionFactory就只能通过相关的Setter和Getter方法来取到了。  现在进入这个方法:
【位置DbPersistenceService】
public Session getSession() {   
    if ( (session==null)   
         && (getSessionFactory()!=null)    
       ) {   
      Connection connection = getConnection(false);    
      if (isCurrentSessionEnabled) {   
        session = getSessionFactory().getCurrentSession();   
        mustSessionBeClosed = false;   
        mustSessionBeFlushed = false;   
        mustConnectionBeClosed = false;   
      } else if (connection!=null) {   
        log.debug("creating hibernate session with connection "+connection);   
        session = getSessionFactory().openSession(connection);   
        mustSessionBeClosed = true;   
        mustSessionBeFlushed = true;   
        mustConnectionBeClosed = false;   
      } else {   
        log.debug("creating hibernate session");   
        session = getSessionFactory().openSession();   
        mustSessionBeClosed = true;   
        mustSessionBeFlushed = true;   
        mustConnectionBeClosed = false;   
      }   
         
      if (isTransactionEnabled) {   
        log.debug("beginning hibernate transaction");   
        transaction = session.beginTransaction();   
      }   
    }   
    return session;   
  }  

因为注入到JbpmContext的session和sessionFactory Setter方法都是将其注入到persistenceService里面的 在上面这个方法里
1)如果用户向JbpmContext提供了Session,那么会立刻返回该session,并且不会修改mustConnectionBeClosed,mustSessionBeClosed等的值。

2)如果用户向JbpmContext提供了SessionFactory,在getConnection时将mustConnectionBeClosed设置成了false,另外使用返回的Connection创建了一个Hibernate Session,即session = getSessionFactory().openSession(connection);紧接着将mustSessionBeClosed和mustSessionBeFlushed设置成了true。
分享到:
评论
2 楼 zhu20198838 2008-10-06  
文章很不错,谢谢
1 楼 kencool 2008-03-05  
原文被分成了两部分,特转来合并,方便阅览

相关推荐

Global site tag (gtag.js) - Google Analytics