JBPM 4.3 + Spring 3 + jBoss + JPA + JTA
潘振国
2023-12-01
来源: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.