JBPM 4.3 + Spring 3 + jBoss + JPA + JTA

2 篇文章 0 订阅
来源:http://captaincaveman.posterous.com/jbpm-43-spring-3-jboss-jpa-jta-configuration 

Many an hour was spent trying to get jBPM 4.3 to run in a Spring application while using a container managed persistence unit defined in persistence.xml.  There's both good news and bad news for those of you wanting to accomplish this. 

The bad news is you cannot rely solely on the hibernate session factory created by the container when using jBPM 4.3.  This is because the SpringProcessEngine meant to integrate jBPM 4.3 with Spring assumes the presence of a LocalSessionFactoryBean in the Spring application context. 

The good news is you can get jBPM 4.3 to work simply by providing a LocalSessionFactoryBean.  This does not mean your application needs to use this bean.  It only means that jBPM needs to use it.  Your application can still use the container managed JPA persistence unit defined in persistence.xml.  And as long as you are using JTA transactions, everything will be transactional as expected.  Meaning if you complete some task in a task node, and then an exception is thrown by your application code later in the chain, both the changes made by your application code and the changes made by jBPM will be rolled back appropriately. 

This solution has been tested on jBoss AS 5.1 GA, 6.0.0.M1, and 6.0.0.M2. 

Here is how you configure this to work. 

1.  First, the persistence.xml.  Notice that there are no jBPM mapping files specified here: 

<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> 

    <!-- Define the container managed persistence unit using JTA.  --> 
    <persistence-unit name="persistenceUnit" transaction-type="JTA"> 
        <provider>org.hibernate.ejb.HibernatePersistence</provider> 
         <jta-data-source>java:/SquareOneDataSource</jta-data-source> 
        
        <!-- Any mapping files for the application go here. --> 
        <mapping-file>so.filter.hbm.xml</mapping-file> 
         
        <!-- 
            These have been put in the hibernate session factory 
            inside the applicationContext.xml spring configuration. 
        --> 
        <!-- <mapping-file>jbpm.repository.hbm.xml</mapping-file> 
         <mapping-file>jbpm.execution.hbm.xml</mapping-file> 
        <mapping-file>jbpm.history.hbm.xml</mapping-file> 
        <mapping-file>jbpm.task.hbm.xml</mapping-file> 
        <mapping-file>jbpm.identity.hbm.xml</mapping-file> --> 
         
        <!-- 
            These hibernate properties will be used by your application 
            code since we will inject this persistence unit with @PersistenceContext 
            using Spring.  Spring will use JNDI to lookup the persistence 
             unit and inject it. 
        -->        
        <properties> 
            <property name="jboss.entity.manager.jndi.name" 
                value="java:SquareOnePersistenceUnit" /> 
             <property name="jboss.entity.manager.factory.jndi.name" 
                value="java:SquareOnePersistenceUnitFactory" /> 
             <property name="hibernate.session_factory_name" 
                value="java:SquareOneHibernateSessionFactory" /> 
            
            <!-- Hibernate Configuration Properties --> 
               <property name="hibernate.archive.autodetection" value="class, hbm" /> 
            <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" /> 
             <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect" /> 
            <property name="hibernate.hbm2ddl.auto" value="update" /> 
             <property name="hibernate.show_sql" value="true" /> 
            <property name="hibernate.format_sql" value="true" /> 
            <property name="hibernate.use_sql_comments" value="true" /> 
             <property name="hibernate.default_batch_fetch_size" value="25" /> 
            <property name="hibernate.order_updates" value="true" /> 
            <property name="hibernate.generate_statistics" value="true" /> 
             
            <!-- Hibernate JDBC and Connection Properties --> 
            <property name="hibernate.jdbc.batch_size" value="25" /> 
            <property name="hibernate.connection.autocommit" value="false" /> 
             <property name="hibernate.connection.release_mode" value="auto" /> 
            
            <!-- Hibernate Cache Properties --> 
            <!-- <entry key="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" /> --> 
             <!-- <entry key="net.sf.ehcache.configurationResourceName" value="ehcache.xml" /> --> 
            <property name="hibernate.cache.use_query_cache" value="true" /> 
             <property name="hibernate.cache.use_second_level_cache" value="true" /> 
            
            <!-- Hibernate Transaction Properties --> 
            <!-- <property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory" /> --> 
             <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup" /> 
            <property name="hibernate.transaction.auto_close_session" value="false" /> 
             <property name="hibernate.transaction.flush_before_completion" value="false" /> 
            <property name="hibernate.current_session_context_class" value="org.hibernate.context.JTASessionContext" /> 
             
            <!-- Hibernate Search --> 
            <!-- <entry key="hibernate.search.default.directory_provider" value="false" /> 
            <entry key="hibernate.search.default.indexBase" value="C:/Temp/hibernate_test_indexes" /> 
             <entry key="hibernate.search.default.batch.merge_factor" value="10" /> 
            <entry key="hibernate.search.default.batch.max_buffered_docs" value="10" /> 
             <entry key="hibernate.search.default.optimizer.operation_limit" value="500" /> 
            <entry key="hibernate.search.default.optimizer.transaction_limit.max" value="100" /> --> 
         </properties> 
    </persistence-unit> 
</persistence> 

2.  Next, the Spring applicationContext.xml.  The LocalSessionFactoryBean will only be used by jBPM: 

<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
     xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:jee="http://www.springframework.org/schema/jee" 
     xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:ehcache="http://www.springmodules.org/schema/ehcache" 
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd   
         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
         http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd"> 

    <!-- 
        Activates a load-time weaver for the context. Any bean within the context that 
        implements LoadTimeWeaverAware (such as LocalContainerEntityManagerFactoryBean) 
        will receive a reference to the auto-detected load-time weaver. 
     --> 
    <context:load-time-weaver aspectj-weaving="on" /> 
    
    <!-- Use this to specify exactly which load-time weaver should be used, but 
        it should get auto-detected. --> 
     <!-- <context:load-time-weaver aspectj-weaving="on" weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver" /> --> 
    
    <!-- 
        This will automatically locate any and all property files you have 
         within your classpath, provided they fall under the META-INF/spring 
        directory. The located property files are parsed and their values can 
        then be used within application context files in the form of 
         ${propertyKey}. 
    --> 
    <context:property-placeholder location="classpath*:META-INF/*.properties" /> 

    <!-- 
        Turn on AspectJ @Configurable support. As a result, any time you 
         instantiate an object, Spring will attempt to perform dependency 
        injection on that object. This occurs for instantiation via the "new" 
        keyword, as well as via reflection. This is possible because AspectJ 
         is used to "weave" Roo-based applications at compile time. In effect 
        this feature allows dependency injection of any object at all in your 
        system, which is a very useful feature (without @Configurable you'd 
         only be able to dependency inject objects acquired from Spring or 
        subsequently presented to a specific Spring dependency injection 
        method). Roo applications use this useful feature in a number of 
         areas, such as @PersistenceContext injection into entities. 
    --> 
    <context:spring-configured /> 

    <!-- 
        This declaration will cause Spring to locate every @Component, 
         @Repository and @Service in your application. In practical terms this 
        allows you to write a POJO and then simply annotate the new POJO as an 
        @Service and Spring will automatically detect, instantiate and 
         dependency inject your service at startup time. Importantly, you can 
        then also have your new service injected into any other class that 
        requires it simply by declaring a field for your service inside the 
         relying class and Spring will inject it. 
        
        Furthermore, this turns on @Autowired, @PostConstruct etc support. These 
        annotations allow you to use common Spring and Java Enterprise Edition 
         annotations in your classes without needing to do any special configuration. 
        The most commonly used annotation is @Autowired, which instructs Spring to 
        dependency inject an object into your class. 
     --> 
    <context:component-scan base-package="com.so" /> 
            
    <!-- enables interpretation of the @Required annotation to ensure that dependency injection actually occures --> 
     <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>  

    <!-- enables interpretation of the @PersistenceUnit/@PersistenceContext annotations providing convenient 
         access to EntityManagerFactory/EntityManager --> 
    <!-- <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/> --> 
    
    <!-- 
        Activates various annotations to be detected in bean classes: Spring's 
         @Required and @Autowired, as well as JSR 250's @PostConstruct, 
        @PreDestroy and @Resource (if available) and JPA's @PersistenceContext 
        and @PersistenceUnit (if available). 
    --> 
     <context:annotation-config transaction-manager="transactionManager" /> 

    <!-- 
        Instruct Spring to perform declarative transaction management 
        automatically on annotated classes. 
     --> 
    <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager" /> 
    
    <!-- 
        Instruct Spring to retrieve and apply @AspectJ aspects which are defined 
         as beans in this context (such as the CallMonitoringAspect below). 
    --> 
    <aop:aspectj-autoproxy proxy-target-class="true" /> 
    
    <!-- 
        Post-processor to perform exception translation on @Repository classes (from native 
         exceptions such as JPA PersistenceExceptions to Spring's DataAccessException hierarchy). 
    --> 
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" /> 

    <!--  ========================================================================= --> 
    <!-- Entity Manager --> 
    <!--  ========================================================================= --> 
         
    <!-- Container managed data source. -->    
    <jee:jndi-lookup id="dataSource" jndi-name="java:/SquareOneDataSource"/> 
    
    <!-- 
        Spring will inject this container managed persistence 
         unit anywhere you use @PersistenceContext. 
    --> 
    <jee:jndi-lookup id="entityManagerFactory" jndi-name="java:/SquareOnePersistenceUnitFactory" /> 
        
    <!-- JTA transaction manager to be used by everyone! --> 
     <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 
        <property name="transactionManagerName" value="java:/TransactionManager" /> 
         <property name="userTransactionName" value="UserTransaction" /> 
    </bean> 
    
    <!-- 
        The JNDI Hibernate session factory will not work for jBPM 4.3 as jBPM is expecting 
         a Spring LocalSessionFactoryBean. So we specify our mappings here.  This will only 
        be used by jBPM.  Spring will still inject the container managed persistence unit 
        anywhere you use @PersistenceContext. 
     --> 
    <!-- <jee:jndi-lookup id="sessionFactory" jndi-name="java:/SquareOneHibernateSessionFactory" /> --> 
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
         <!-- <property name="configLocation" value="classpath:/jbpm/jbpm.hibernate.cfg.xml" /> --> 
        <property name="dataSource" ref="dataSource" /> 
        <property name="mappingResources"> 
             <list> 
                <value>jbpm.repository.hbm.xml</value> 
                <value>jbpm.execution.hbm.xml</value> 
                <value>jbpm.history.hbm.xml</value> 
                 <value>jbpm.task.hbm.xml</value> 
                <value>jbpm.identity.hbm.xml</value> 
            </list> 
        </property> 
        <!-- 
            Hibernate properties needed to configure the session factory to use JTA.  This 
             will ensure that jBPM can participate in the application's JTA transactions. 
        --> 
        <property name="hibernateProperties"> 
            <props> 
                <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop> 
                 <prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</prop> 
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop> 
                 <prop key="hibernate.connection.datasource">java:/SquareOneDataSource</prop> 
                <prop key="jta.UserTransaction">UserTransaction</prop> 
                <prop key="hibernate.hbm2ddl.auto">update</prop> 
             </props> 
        </property> 
    </bean> 
    
    <!-- jBPM beans. --> 
    <bean id="springHelper" class="org.jbpm.pvm.internal.processengine.SpringHelper" /> 

      <bean id="processEngine" factory-bean="springHelper" factory-method="createProcessEngine" /> 
    
</beans> 

3.  The jbpm.cfg.xml: 

<?xml version="1.0" encoding="UTF-8"?> 

<jbpm-configuration> 

  <import resource="jbpm.default.cfg.xml" /> 
  <import resource="jbpm.tx.spring.cfg.xml" /> 
  <import resource="jbpm.jpdl.cfg.xml" /> 
   <import resource="jbpm.bpmn.cfg.xml" /> 
  <import resource="jbpm.identity.cfg.xml" /> 
  <import resource="jbpm.businesscalendar.cfg.xml" /> 
  <import resource="jbpm.console.cfg.xml" /> 
   <!-- Commented out for dev environment only. --> 
  <import resource="jbpm.jobexecutor.cfg.xml" /> 
  
  <!-- 
      This is not needed. 
      <process-engine-context> 
        <string name="spring.cfg" value="applicationContext.xml" /> 
       </process-engine-context> 
  --> 

</jbpm-configuration> 

Comments (16) 
Mar 04, 2010 
Captain Caveman liked this post. 
Mar 04, 2010 
Twitter said... 
Could you please share this sample code with us 
Mar 05, 2010 
Captain Caveman said... 
I'm sorry, what code are you referring to? This post is about how to configure jBPM 4.3 with Spring and a container managed persistence unit. Following the configuration above, you should be able to accomplish this. 
Apr 13, 2010 
Marcel said... 
I see you're using <context:component-scan /> in your configuration. How do you get that to work in jboss? We were not able to get it working due to the VFS. 
Apr 13, 2010 
Captain Caveman said... 
Sorry Marcel, I'm not sure what problems you are having getting it to work. What version of jboss? It works for me on 5.1, 6M1 and 6M2. I do remember an issue with Spring 2.5 and JBoss's classloader that was supposed to have been fixed in Spring 3. Could this be related? I'm using Spring 3. 
Apr 14, 2010 
Marcel said... 
You just answered the question. I guess for spring 3 it is not a problem. We were using spring 2.5.6. 
Apr 14, 2010 
Marcel said... 
When I run your configuration example against spring 2.5.6 with jbpm4.3, jbpm is still looking for the jbpm.hibernate.cfg.xml, eventhough it is configured in the application context in spring. Do we still need this file. Meaning we now have 3 places where we configure hibernate: 

org.hibernate.HibernateException: jbpm.hibernate.cfg.xml not found 
at org.hibernate.util.ConfigHelper.getResourceAsStream(ConfigHelper.java:170) 
at org.hibernate.cfg.Configuration.getConfigurationInputStream(Configuration.java:1439) 
at org.hibernate.cfg.Configuration.configure(Configuration.java:1461) 
at org.jbpm.pvm.internal.wire.descriptor.HibernateConfigurationDescriptor$AddCfgResource.apply(HibernateConfigurationDescriptor.java:151)
at org.jbpm.pvm.internal.wire.descriptor.HibernateConfigurationDescriptor.apply(HibernateConfigurationDescriptor.java:90) 
at org.jbpm.pvm.internal.wire.descriptor.HibernateConfigurationDescriptor.initialize(HibernateConfigurationDescriptor.java:76)
at org.jbpm.pvm.internal.wire.WireContext.performInitialization(WireContext.java:533) 
Apr 14, 2010 
Marcel said... 
Sorry, I seem to have following in my jbpm.cfg.xml: 

<import /> 

Of course this should be removed, then it works 
Apr 14, 2010 
Marcel said... 
Sorry, removed by blog: 

<import> 
Apr 14, 2010 
Marcel said... 
RRRRR, again removed.... 

import resource="jbpm.tx.spring.cfg.xml" 
Apr 14, 2010 
Marcel said... 
This is not going very well, it should be: 

import resource="jbpm.tx.hibernate.cfg.xml" 
Apr 14, 2010 
Captain Caveman said... 
I'm glad to hear you got it working Marcel. 
Jun 22, 2010 
Cyril said... 
Hello, I would like to know whether you are able to call Spring beans directly from your process using a JUEL value, as in : 

<java> 
expr="#{serviceRequest}" method="sendRequestForProduction" 
continue="async" g="442,84,136,52" 
var="previousActionResult"> 
<arg><object /></arg> 
<transition /> 
</java> 

jBPM recognizes "serviceRequest" s a Spring bean name, as long as I work with local transactions. 

But when I try to use JTA, then jBPM is no longer able to parse "#{serviceRequest}" as a Spring bean. I get the error : 

2010-06-22 17:09:44,531 ERROR [org.jbpm.pvm.internal.cmd.ExecuteJobCmd] (pool-9-thread-1) exception while executing 'ExecuteActivityMessage[140003]' 
org.jbpm.api.JbpmException: script evaluation error: javax.el.PropertyNotFoundException: Cannot find property serviceRequest 
at org.jbpm.pvm.internal.script.ScriptManager.evaluate(ScriptManager.java:130) 
at org.jbpm.pvm.internal.script.ScriptManager.evaluate(ScriptManager.java:118) 
at org.jbpm.pvm.internal.script.ScriptManager.evaluateExpression(ScriptManager.java:90) 
at org.jbpm.pvm.internal.wire.descriptor.ObjectDescriptor.construct(ObjectDescriptor.java:180) 
at org.jbpm.pvm.internal.wire.WireContext.construct(WireContext.java:469) 
... 

I tried to inject JTA by two different methods : 
1. injected jtaTransactionManager (provided by JBoss) into my LocalSessionFactoryBean : 

<bean> 
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
<property> 
value="classpath:jbpm.hibernate.cfg.xml" /> 
<property /> 
<property> 
ref="jtaTransactionManagerJBPM"/> 
</bean> 

<jee:jndi-lookup /> 

2. tried a config closer to yours (especially the LocalSessionFactoryBean. 

Both attempts produced the same result : everything works, except "serviceRequest" bean is no longer recognized by jBPM. 

Other nodes (not injected that way) work properly, JTA transactions do what they are supposed to do, etc. 

Thank you if you can help! 
Jun 22, 2010 
Cyril said... 
Oops, XML came out a bit messed up. Hope the problem remains understandable. 
Jan 22, 2011 
Michael said... 
Hi, I want to integarte jbpm3 with jpa, spring and jboss - do you have any example with this maybe? 
Feb 16, 2011 
Andrew Swan said... 
Thanks, this was useful for learning about the JBoss JPA config options. 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值