1 Reply Latest reply on Mar 23, 2012 5:10 PM by mbell697

    Faces 3.1.0 - TransactionPhaseListener Issue

    mbell697

      I've run into a bit of an issue with the TransactionPhaseListener in faces 3.1.0, not sure if this is a bug with Face (feels like it) or something else (perhaps primefaces).

       

      Setup:

       

      WELD 1.1.5

      JSF 2 (mojarra 2.1.7)

      Primefaces 3.1.1

      Seam 3.1.0 FInal (security, peristence, transaction, faces, jms)

      Hibernate 4.1.1

       

      running on Tomcat 7.0.25

       

      I have disabled the servlet listener from seam transaction, we use explicit demarcation in the service layer.

       

      However I am using the TransactionPhaseListener from faces with the goal of wrapping any lazy loaded associations in a transaction.

       

      The SMPC is ConversationScoped, most backing beans are ViewScoped and with a fairly large amount of ajax calls that lazy load data association conversations are often long running and bound to the lifecycle of the ViewScoped backing bean.

       

      The Issue:

       

      It appears that the TransactionPhaseListener leaves transactions open on certain types of requests, most notibly it appears to be ajax requests that don't trigger a render during the render response phase (I think).

       

      With debug logging enabled looking at a normal request I see what I would expect, something like this:

       

      15:32:43.490 DEBUG [http-apr-8080-exec-4] org.jboss.seam.faces.transaction.TransactionPhaseListener  beginning transaction prior to phase: RENDER_RESPONSE 6

      15:32:43.491 DEBUG [http-apr-8080-exec-4] org.jboss.seam.transaction.EntityTransaction  beginning JPA resource-local transaction

      15:32:43.507 DEBUG [http-apr-8080-exec-4] org.jboss.seam.transaction.EntityTransaction  registering synchronization: org.jboss.seam.persistence.ManagedPersistenceContextProxyHandler@742f8ad4

      15:32:43.777 DEBUG [http-apr-8080-exec-4] org.jboss.seam.faces.transaction.TransactionPhaseListener  committing transaction after phase: RENDER_RESPONSE 6

      15:32:43.777 DEBUG [http-apr-8080-exec-4] org.jboss.seam.transaction.EntityTransaction  committing JPA resource-local transaction

       

      However with some ajax calls I get the following:

       

      15:33:57.982 DEBUG [http-apr-8080-exec-8] org.jboss.seam.faces.transaction.TransactionPhaseListener  beginning transaction prior to phase: RENDER_RESPONSE 6

      15:33:57.982 DEBUG [http-apr-8080-exec-8] org.jboss.seam.transaction.EntityTransaction  beginning JPA resource-local transaction

      15:33:57.987 DEBUG [http-apr-8080-exec-8] org.jboss.seam.transaction.EntityTransaction  registering synchronization: org.jboss.seam.persistence.ManagedPersistenceContextProxyHandler@742f8ad4

       

      Upon the next request I then get:

       

      15:34:34.463 DEBUG [http-apr-8080-exec-4] org.jboss.seam.faces.transaction.TransactionPhaseListener  committing transaction after phase: INVOKE_APPLICATION 5

      15:34:34.464 DEBUG [http-apr-8080-exec-4] org.jboss.seam.transaction.EntityTransaction  committing JPA resource-local transaction

      **snip, opens another transaction in render response phase***

       

      It appears that a transaction is started prior to the RENDER_RESPONSE phase but never commited, well, until the next request.  This appears to be creating various issues for us.

       

      From tracing this I think the issue is in this bit of code from the TransactionPhaseListener:

       

      public void handleTransactionsAfterPhase(final PhaseEvent event) {

              PhaseId phaseId = event.getPhaseId();

              if (seamManagedTransactionStatus(phaseId)) {

                  boolean commitTran = (phaseId == PhaseId.INVOKE_APPLICATION) || event.getFacesContext().getRenderResponse()

                          || event.getFacesContext().getResponseComplete();

       

                  if (commitTran) {

                      commitOrRollback(phaseId);  // we commit before destroying contexts,

                                                  // cos the contexts have the PC in them

                  }

              }

          }

       

      Notibly the expression for the commitTran boolean returns false on these requests, leaving the transaction open.

       

      As for what types of requests trigger this, it appears to be almost any ajax request made via primefaces, too many examples to list fully but ajax onchange listeners of all types, changing tabs in a tab view, data table row expansion, etc all appear to trigger this.

       

      Anyone have thoughts on this being a bug or maybe I'm not configuring something correctly?

       

      Cheers!

        • 1. Re: Faces 3.1.0 - TransactionPhaseListener Issue
          mbell697

          I've sorted a workaround for this problem.  I just created a JSF PhaseListener that hooks on the RENDER_RESPONSE phase and either commits or rolls back the transaction in afterPhase().  Just make sure your PhaseListener executes before the Seam Faces DeligatePhaseListener or you may have context issues.

           

          In case anyone runs into this, the symtoms will be a lot of "Transaction begin not detected" exceptions which usually bubble up into exceptions in the markTransactionRollback method of SimpleTransactionExceptionHandler from Seam Transaction.

           

          Also be sure to test under load.  The exceptions above will only appear if the subsequent request that tries to commit the transaction is handled by a different thread than the one that started the transaction and the syncronizations in Seam Persistence don't get moved over to the new thread.  This is much more likely if there are lots of requests going on.  Conciquently, if you don't use many ajax calls that trigger this or are under light load this may only appear sporatically and will seem random.

           

          I do think this is a bug in Seam Faces.  It think it would be far safer to execute commitOrRollback() in the above code any time Seam Faces started a transaction in the beforePhase() method.  Perhaps that is already the goal but rather than trying to infer this from the boolean expression used wouldn't it be safer to just set a flag in the render context in beforePhase() if a transaction is started and check it in the afterPhase() code?