A few weeks back I was investigating a problem for a customer, related
to the integration of seam applications within the SOA platform. I
managed to chase down the issues but wanted to discuss them further in
this forum. Apologies to Pete and Marek for taking so long over this.
First the relevant configuration
Within the SOA platform we have a jBPM/hibernate configuration that
results in the use of the hibernate JTASessionContext to manage the
lifecycle of sessions, tying those sessions into the encompassing JTA
transaction. We are also using the jBPM JtaDbPersistenceService in
order to guarantee the existence of a transactional context for the jBPM
context.
The codebase I have used in this investigation is from the following tag
https://svn.jboss.org/repos/seam/branches/enterprise/JBPAPP_4_3_FP01
When running a seam example on the SOA platform we were seeing an
exception being thrown as a result of a hibernate session being used
after it has previously been closed, this was a SessionException with
the text "Session is closed!".
In chasing this down I discovered two issues
- an interleaving of the transaction synchronizations between jBPM
and hibernate (now removed in jBPM, certainly in 3.2.5-SP5). This
could cause the session to be closed twice, depending on which order
the Synchronizations were invoked by the transaction manager.
- hibernate objects being used outside of a transactional context
It is the latter that I would like to understand further as I was
surprised to see this given that some areas in the seam/jBPM integration
explicitly test for the existence of transactions and even create a
transactional context. Is there a reason why this is not completely
done within a transaction?
I appreciate that I am a seam novice and there may be something obvious
that I am missing. I do have some suggestions for a fix, however, and
would like some feedback as to whether they would be appropriate. I
have attached the diff file to this message, again based on the previous
tag.
Thanks in advance for any help that you can give,
Kev
--
JBoss, a Division of Red Hat
Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod
Street, Windsor, Berkshire,
SI4 1TE, United Kingdom.
Registered in UK and Wales under Company Registration No. 3798903
Directors: Michael Cunningham (USA), Charlie Peters (USA), Matt Parsons
(USA) and Brendan Lane (Ireland)
Index: src/main/org/jboss/seam/bpm/ProcessInstance.java
===================================================================
--- src/main/org/jboss/seam/bpm/ProcessInstance.java (revision 10421)
+++ src/main/org/jboss/seam/bpm/ProcessInstance.java (working copy)
@@ -36,26 +36,16 @@
{
if ( !Contexts.isConversationContextActive() ) return null;
- return new Work<org.jbpm.graph.exe.ProcessInstance>()
+ Long processId = BusinessProcess.instance().getProcessId();
+ if (processId!=null)
{
-
- @Override
- protected org.jbpm.graph.exe.ProcessInstance work() throws Exception
- {
- Long processId = BusinessProcess.instance().getProcessId();
- if (processId!=null)
- {
- //TODO: do we need to cache this??
- return
ManagedJbpmContext.instance().getProcessInstanceForUpdate(processId);
- }
- else
- {
- return null;
- }
- }
-
- }.workInTransaction();
-
+ //TODO: do we need to cache this??
+ return ManagedJbpmContext.instance().getProcessInstanceForUpdate(processId);
+ }
+ else
+ {
+ return null;
+ }
}
public static org.jbpm.graph.exe.ProcessInstance instance()
Index: src/main/org/jboss/seam/bpm/ManagedJbpmContext.java
===================================================================
--- src/main/org/jboss/seam/bpm/ManagedJbpmContext.java (revision 10421)
+++ src/main/org/jboss/seam/bpm/ManagedJbpmContext.java (working copy)
@@ -83,6 +83,12 @@
throw new IllegalStateException("JbpmContext may only be used inside a
transaction");
}
+ if (jbpmContext == null)
+ {
+ log.debug( "recreating seam managed jBPM context" );
+ jbpmContext = Jbpm.instance().getJbpmConfiguration().createJbpmContext() ;
+ }
+
if ( !synchronizationRegistered && !Lifecycle.isDestroying() &&
transaction.isActive() )
{
jbpmContext.getSession().isOpen();
@@ -100,7 +106,7 @@
public void beforeCompletion()
{
- log.debug( "flushing seam managed jBPM context" );
+ log.debug( "closing seam managed jBPM context" );
/*org.jbpm.graph.exe.ProcessInstance processInstance = ProcessInstance.instance();
if (processInstance!=null)
{
@@ -113,47 +119,30 @@
//destroyed, flush here:
Contexts.getBusinessProcessContext().flush();
}
- jbpmContext.getSession().flush();
- log.debug( "done flushing seam managed jBPM context" );
+ closeContext();
+ log.debug( "closed seam managed jBPM context" );
}
public void afterCompletion(int status)
{
synchronizationRegistered = false;
- if ( !Contexts.isEventContextActive() )
- {
- //in calls to MDBs and remote calls to SBs, the
- //transaction doesn't commit until after contexts
- //are destroyed, so wait until the transaction
- //completes before closing the session
- //on the other hand, if we still have an active
- //event context, leave it open
- closeContext();
- }
}
@Destroy
public void destroy()
{
- if ( !synchronizationRegistered )
- {
- //in requests that come through SeamPhaseListener,
- //there can be multiple transactions per request,
- //but they are all completed by the time contexts
- //are dstroyed
- //so wait until the end of the request to close
- //the session
- //on the other hand, if we are still waiting for
- //the transaction to commit, leave it open
- closeContext();
- }
+ closeContext();
}
private void closeContext()
{
- log.debug( "destroying seam managed jBPM context" );
- jbpmContext.close();
- log.debug( "done destroying seam managed jBPM context" );
+ if (jbpmContext != null)
+ {
+ log.debug( "destroying seam managed jBPM context" );
+ jbpmContext.close();
+ log.debug( "done destroying seam managed jBPM context" );
+ jbpmContext = null;
+ }
}
public static JbpmContext instance()
Index: src/main/org/jboss/seam/bpm/TaskInstance.java
===================================================================
--- src/main/org/jboss/seam/bpm/TaskInstance.java (revision 10421)
+++ src/main/org/jboss/seam/bpm/TaskInstance.java (working copy)
@@ -36,25 +36,16 @@
{
if ( !Contexts.isConversationContextActive() ) return null;
- return new Work<org.jbpm.taskmgmt.exe.TaskInstance>()
+ Long taskId = BusinessProcess.instance().getTaskId();
+ if (taskId!=null)
{
-
- @Override
- protected org.jbpm.taskmgmt.exe.TaskInstance work() throws Exception
- {
- Long taskId = BusinessProcess.instance().getTaskId();
- if (taskId!=null)
- {
- //TODO: do we need to cache this??
- return ManagedJbpmContext.instance().getTaskInstanceForUpdate(taskId);
- }
- else
- {
- return null;
- }
- }
-
- }.workInTransaction();
+ //TODO: do we need to cache this??
+ return ManagedJbpmContext.instance().getTaskInstanceForUpdate(taskId);
+ }
+ else
+ {
+ return null;
+ }
}
public static org.jbpm.taskmgmt.exe.TaskInstance instance()
Index: src/main/org/jboss/seam/contexts/Contexts.java
===================================================================
--- src/main/org/jboss/seam/contexts/Contexts.java (revision 10421)
+++ src/main/org/jboss/seam/contexts/Contexts.java (working copy)
@@ -18,6 +18,7 @@
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.transaction.Transaction;
+import org.jboss.seam.util.Work;
import org.jboss.seam.web.Session;
/**
@@ -344,17 +345,32 @@
//TODO: it would be nice if BP context spanned redirects along with the
conversation
// this would also require changes to BusinessProcessContext
- boolean destroyBusinessProcessContext = !Init.instance().isJbpmInstalled()
||
- !BusinessProcess.instance().hasActiveProcess();
- if (destroyBusinessProcessContext)
+ try
{
- //TODO: note that this occurs from Lifecycle.endRequest(), after
- // the Seam-managed txn was committed, but Contexts.destroy()
- // calls BusinessProcessContext.getNames(), which hits the
- // database!
- log.debug("destroying business process context");
- destroy( getBusinessProcessContext() );
+ new Work<Object>()
+ {
+ @Override
+ protected Object work() throws Exception
+ {
+ boolean destroyBusinessProcessContext =
!Init.instance().isJbpmInstalled() ||
+ !BusinessProcess.instance().hasActiveProcess();
+ if (destroyBusinessProcessContext)
+ {
+ //TODO: note that this occurs from Lifecycle.endRequest(), after
+ // the Seam-managed txn was committed, but
Contexts.destroy()
+ // calls BusinessProcessContext.getNames(), which hits the
+ // database!
+ log.debug("destroying business process context");
+ destroy( getBusinessProcessContext() );
+ }
+ return null;
+ }
+ }.workInTransaction();
}
+ catch (final Exception ex)
+ {
+ log.warn("Exception destroying context ", ex);
+ }
}
if ( !Manager.instance().isLongRunningConversation() )
Index: build/root.pom.xml
===================================================================
--- build/root.pom.xml (revision 10421)
+++ build/root.pom.xml (working copy)
@@ -250,7 +250,7 @@
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-jpdl</artifactId>
- <version>3.2.2.GA_SOA-P</version>
+ <version>3.2.5.SP5</version>
</dependency>
<dependency>