4 Replies Latest reply on Sep 30, 2009 5:53 AM by mohreece

    Hibernate, Sessions, and Sync - Oh my!

    michaelholtzman

      Greetings -

      We are plagued with StaleObjectStateExceptions that I have been unable to resolve. We are running jBPM (derived from version 3.1.2) in an application which launches and manages our workflows and also executes commands targetting specific workflow instances (for example, a signal from our main application, an external system wrt the jBPM app).

      We have a number of custom nodes that call out to our system and enter a wait state. Our system processes the request and sends back a command to the jbpm application. The command will include the process instance and token that made the request, and possibly data that will be saved in jbpm instance variables.

      Our custom nodes use the JbpmContext attached to the current ExecutionContext. The 'out of band' command processing creates a new JbpmContext from a singleton JbpmConfiguration, loads the specified token by id and modifies it, then saves and closes the JbpmContext.

      In general this works great, but I have one case that consistently causes SOSE's:

      A custom node (Wait) creates a timer with a 10 second due date. When the timer fires, it transitions to a custom node (Script) that messages our system and goes into a wait state. Our system responds with a 'Results' command that essentially does this:

      gProcessInstance = jbpmContext.loadProcessInstance(gProcessInstanceId);
      if (gProcessInstance == null)
       throw new Exception("Unable to load process instance id " + gProcessInstanceId);
      
      if (gTokenId > 0) {
       gToken = jbpmContext.loadToken(gTokenId);
      } else {
       gToken = gProcessInstance.getRootToken();
      }
      
      // Get new JbpmContext from static configuration
      jbpmContext = Manager.jbpmConfiguration.createJbpmContext();
      try {
       // Set some instance variables
       ...
      
       // Save and close the jbpmContext
       jbpmContext.save(gToken);
      } catch (Exception e) {
       ...
      } finally {
       jbpmContext.close();
      }
      


      Usually this works correctly, advancing the token from the 'Script' node to the next node in the process. However, sometimes the signal advances the token from the 'Wait' node -- the token's previous node. The next execution of jbpmContext.close() throws the SOSE.

      My theory du jour is that somehow the transition in the workflow from 'Wait' to 'Script' has not been committed at the time the 'Result' command arrives, so that the loadToken() call is getting the state of the token before it moved to the 'Script' node and the Token.getNode() is returning the wrong node as the source of the transition.

      I have tried adding
      jbpmContext.save(token);
      jbpmContext.getSession().flush();
      

      when the 'Wait' and 'Script' nodes go into their respective wait states, as well as inserting
      jbpmContext.getSession().refresh(gToken)'
      

      after loading the token, but the problem persists.

      Any ideas?

      Thanx.

        • 1. Re: Hibernate, Sessions, and Sync - Oh my!
          sebastian.s

          Have you already searched JIRA for an issue like this?
          What about trying a more recent version of 3.x to see if the issue persists?

          You also should give some more information about your environment like the database used. This post helps:



          Would it be possible to supply a unit test representing the situation?

          Just my 2 cents.

          • 2. Re: Hibernate, Sessions, and Sync - Oh my!
            mohreece

            Michael, it looks like you're doing some custom asynchronous continuation, and the moment in which the new transaction is started (i.e. the jBPM context created) isn't related at all of the old transaction, which in a sense 'spawned' it - am I reading that correctly?

            We have been doing something similar in our system (we're still using 3.1.4 for now, I guess you have a similar predicament in which upgrading an existing system just is easier said than done), and we encountered the same inconsistent and unpredictable SOSE occurrences.

            The problem was that we used an indeterministic strategy for creating a new jBPM context in a new thread - we didn't wait for the old one to be closed and were 'hoping for the best' - unknowingly at first, of course.

            We found a solution by making sure the new transaction is started only after the old one has ended: digging a little beyond the jBPM API into the Hibernate API, in which the org.hibernate.Transaction interface has the possibility to register a callback object (which must implement the javax.transaction.Synchronization interface). The latter object then is called before and after the two-phase-commit, and we used this last call as a starting point for the new transaction.

            From within an opened transaction we use something like this:

            jbpmContext.getSession().getTransaction().registerSynchronization(new Synchronization() {
             public void beforeCompletion() {
             // We're not using this one ourselves...
             }
            
             public void afterCompletion(int status) {
             // Here we spawn a new thread which can safely open its own jBPM context...
             }
            });
            


            Hope this helps you out a little bit!

            • 3. Re: Hibernate, Sessions, and Sync - Oh my!
              michaelholtzman

              I think this may help us out a lot!

              You've got the right idea - a custom node sends off an async request to our central application and waits. Some time later, our jBPM app receives the results of that request, then creates a new jbpmContext, loads the token, signals it, and closes the context.

              One additional detail ... our jBPM java app is multi-threaded for performance reasons, which adds another wrinkle to the hibernate problem.

              Can you supply some more details about the interlock between the sessions? This is new territory for me.

              Thanx.

              • 4. Re: Hibernate, Sessions, and Sync - Oh my!
                mohreece

                If your app wasn't multi-threaded, you wouldn't be running into these SOSE's - because then you would have total control over when you start a transaction and when you end it. There would be no overlap possible (unless you would program it that way accidentally, of course).

                You can find a little more about the mechanism I describe in the javadocs for the Synchronization interface (standard JEE):

                http://java.sun.com/javaee/5/docs/api/javax/transaction/Synchronization.html

                and the Hibernate Transaction Interface:

                https://www.hibernate.org/hib_docs/v3/api/org/hibernate/Transaction.html#registerSynchronization(javax.transaction.Synchronization)

                The latter allows you to register your own implementation of the former.

                I actually couldn't find anything about this mechanism in the Hibernate docs, and a quick search in Google doesn't turn up much more than javadocs and forum questions about ClassNotFoundErrors (if your environment isn't a JEE app server, you'll need the jta.jar).
                However, if you have more specific questions, i'll be glad to try to help out.