I believe that there is an assumption in the Seam jBPM integration that Seam is going to control transactions for the Hibernate Session used by jBPM. That is why the following jBPM configuration is required:

    <service name="persistence">
      <factory>
        <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">
          <field name="isTransactionEnabled"><false/></field>
        </bean>
      </factory>
    </service>

Of course, then you must have Seam doing all calls to jBPM or else you will be outside of a transaction. I'm unaware of whether this was considered (not saying it wasn't). That explains, though, why Seam has the workInTransaction callbacks.

-Dan

On Wed, May 6, 2009 at 10:08 AM, Kevin Conner <kconner@redhat.com> wrote:
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>

_______________________________________________
seam-dev mailing list
seam-dev@lists.jboss.org
https://lists.jboss.org/mailman/listinfo/seam-dev




--
Dan Allen
Senior Software Engineer, Red Hat | Author of Seam in Action

http://mojavelinux.com
http://mojavelinux.com/seaminaction
http://in.relation.to/Bloggers/Dan

NOTE: While I make a strong effort to keep up with my email on a daily
basis, personal or other work matters can sometimes keep me away
from my email. If you contact me, but don't hear back for more than a week,
it is very likely that I am excessively backlogged or the message was
caught in the spam filters.  Please don't hesitate to resend a message if
you feel that it did not reach my attention.