For whoever is interested, I determined why StatefulKnowledgeSession.dispose threw an
IllegalStateException for a persisted session. The error message refers to the thread,
which led me to discover that the dispose would work if it was executed on the same thread
where it had been created. The
org.springframework.transaction.support.TransactionSynchronizationManager.unbindResource
method at the top of the stack trace attempts to look up the resource in ThreadLocal
storage. My shutdown hook was running on a different thread, so this lookup was failing.
I used the standard Java wait/notify mechanism to fix this. I now suspend my main thread
after it creates the knowledge session with Object.wait. When the shutdown hook thread
runs, it calls Object.notifyAll to release the main thread, which then disposes of the
session successfully. The rest of my application runs in other threads, so suspending the
main thread causes no harm.
I still would appreciate some clarification of how the "Configuring JTA
DataSource"
code<http://docs.jboss.org/drools/release/5.5.0.Final/drools-expert-do...
in the documentation is meant to be used.
Thanks,
Tom
From: rules-users-bounces(a)lists.jboss.org [mailto:rules-users-bounces@lists.jboss.org] On
Behalf Of Thomas Grayson
Sent: Wednesday, June 12, 2013 3:42 PM
To: Rules Users List
Subject: [rules-users] IllegalStateException when disposing of a persisted session
We are using Drools persistence with a StatefulKnowledgeSession. The persistence itself
works fine, but I am encountering an IllegalStateException when disposing of the
StatefulKnowledgeSession. This happens with either a brand new session with no facts or
one that has had facts added to it. It also happens with sessions that have been restored
from the persistent store. The code does no explicit transaction management and relies on
Drools to do this under the covers. We are using Drools 5.5.0 Final. How do I dispose of
a session correctly?
The
documentation<http://docs.jboss.org/drools/release/5.5.0.Final/drools-...
includes Example 3.66, "Configuring JTA DataSource." My code does not include
code like this anywhere. Do I need it? It's not clear from the example where the
PoolingDataSource instance would be used or when this code should be called. If this
configuration is required, can it be done via Spring instead of programmatically?
I'll provide some supporting information below. I've edited these excerpts to
redact some private details and eliminate distracting code, but the substance is intact.
Here is the stack trace created by invoking the "dispose" method from a shutdown
hook:
java.lang.IllegalStateException: No value for key
[org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@732e3e73] bound to
thread [Thread-3]
at
org.springframework.transaction.support.TransactionSynchronizationManager.unbindResource(TransactionSynchronizationManager.java:209)
at
org.drools.container.spring.beans.persistence.DroolsSpringJpaManager.dispose(DroolsSpringJpaManager.java:135)
at
org.drools.persistence.SingleSessionCommandService.execute(SingleSessionCommandService.java:345)
at
org.drools.command.impl.CommandBasedStatefulKnowledgeSession.dispose(CommandBasedStatefulKnowledgeSession.java:241)
at MyClass1.stop
at MyClass2$1.run
at java.lang.Thread.run(Unknown Source)
The code uses Drools-Spring to configure the knowledge base and Java code to initialize
the knowledge session. We're using JPA, the Bitronix transaction manager, and the H2
database, basically as described in the
documentation<http://docs.jboss.org/drools/release/5.5.0.Final/droolsj...;.
Here is the code for creating the session:
ApplicationContext context = new
ClassPathXmlApplicationContext(APPLICATION_CONTEXT_XML);
KnowledgeStoreService kstore = (KnowledgeStoreService)
context.getBean("myAppKnowledgeStore");
KnowledgeBase kbase = ...; // initialized by Spring configuration
Environment env = KnowledgeBaseFactory.newEnvironment();
env.set(EnvironmentName.ENTITY_MANAGER_FACTORY,
context.getBean("entityManagerFactory"));
env.set(EnvironmentName.TRANSACTION_MANAGER,
context.getBean("txManager"));
StatefulKnowledgeSession ksession = kstore.newStatefulKnowledgeSession(kbase,
null, env);
Here is the relevant portion of the Spring ApplicationContext.xml file:
<bean id="dataSourceH2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="org.h2.Driver" />
<property name="url"
value="jdbc:h2:tcp://localhost/~/myApp" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceH2" />
<property name="persistenceUnitName" value="myAppH2"
/>
</bean>
<bean id="txManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory"
ref="entityManagerFactory" />
</bean>
<drools:grid-node id="myAppEngineNode" />
<drools:kstore id="myAppKnowledgeStore" />
<drools:kbase id="myAppEngineKBase"
node="MyAppEngineNode">
<drools:configuration>
<drools:assert-behavior mode="EQUALITY" />
</drools:configuration>
<drools:resources>
<drools:resource type="DRL"
source="classpath:MyAppInternalRules.drl" />
<drools:resource type="DTABLE"
source="classpath:MyAppRules.xls" >
<drools:decisiontable-conf input-type="XLS"
worksheet-name="Sheet 1" />
</drools:resource>
<drools:resource type="DTABLE"
source="classpath:MyAppRules.xls" >
<drools:decisiontable-conf input-type="XLS"
worksheet-name="Sheet 2" />
</drools:resource>
</drools:resources>
</drools:kbase>
Here is the relevant part of the persistence.xml file:
<persistence-unit name="myAppH2">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.drools.persistence.info.SessionInfo</class>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.H2Dialect" />
<property name="hibernate.max_fetch_depth"
value="3" />
<property name="hibernate.hbm2ddl.auto"
value="UPDATE" />
<property name="hibernate.show_sql"
value="true" />
<property name="hibernate.connection.autocommit"
value="true" />
<property
name="hibernate.transaction.manager_lookup_class" value=
"org.hibernate.transaction.BTMTransactionManagerLookup"/>
</properties>
</persistence-unit>
I have a jndi.properties that's identical to what's in the
documentation<http://docs.jboss.org/drools/release/5.5.0.Final/drools-...;.
I noticed in the documentation (Example 3.65, Configuring JPA) that the persistence-unit
block is defined a bit differently. In particular, transaction-type is specified and a
jta-data-source are defined, unlike in my file:
<persistence-unit name="org.drools.persistence.jpa"
transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/BitronixJTADataSource</jta-data-source>
...
</persistence-unit>
I tried defining these as shown, but then the code failed on startup when it tried to
create a transaction:
org.drools.container.spring.beans.persistence.DroolsSpringTransactionManager: Unable to
begin transaction
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA
EntityManager for transaction; nested exception is java.lang.IllegalStateException: A JTA
EntityManager cannot use getTransaction()
at
org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:427)
at
org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
at
org.drools.container.spring.beans.persistence.DroolsSpringTransactionManager.begin(DroolsSpringTransactionManager.java:48)
at
org.drools.persistence.SingleSessionCommandService.<init>(SingleSessionCommandService.java:190)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at
org.drools.persistence.jpa.KnowledgeStoreServiceImpl.buildCommandService(KnowledgeStoreServiceImpl.java:100)
at
org.drools.persistence.jpa.KnowledgeStoreServiceImpl.loadStatefulKnowledgeSession(KnowledgeStoreServiceImpl.java:83)
at MyClass1.initializeKnowledgeSession
at MyClass1.initializeKnowledgeEngine
at MyClass2.main
Caused by: java.lang.IllegalStateException: A JTA EntityManager cannot use
getTransaction()
at
org.hibernate.ejb.AbstractEntityManagerImpl.getTransaction(AbstractEntityManagerImpl.java:996)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at
org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:365)
at com.sun.proxy.$Proxy16.getTransaction(Unknown Source)
at
org.springframework.orm.jpa.DefaultJpaDialect.beginTransaction(DefaultJpaDialect.java:70)
at
org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:377)
... 12 more
Thanks,
Tom