/*
 * JBoss, Home of Professional Open Source
 * Copyright 2010, Red Hat, Inc., and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.seam.faces.transaction;

import static javax.faces.event.PhaseId.ANY_PHASE;
import static javax.faces.event.PhaseId.RENDER_RESPONSE;

import javax.annotation.PostConstruct;
import javax.enterprise.inject.spi.BeanManager;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.inject.Inject;
import javax.transaction.UserTransaction;

import org.jboss.logging.Logger;
import org.jboss.seam.faces.util.BeanManagerUtils;
import org.jboss.seam.faces.viewdata.ViewDataStore;
import org.jboss.seam.persistence.PersistenceContexts;
import org.jboss.seam.persistence.transaction.SeamTransaction;

/**
 * Phase listener that is responsible for seam managed transactions. It is also
 * responsible for setting the correct flush mode on the persistence context
 * during the render response phase
 * 
 * @author Stuart Douglas
 * 
 */
public class TransactionPhaseListener implements PhaseListener
{
   private static final long serialVersionUID = -9127555729455066493L;

   private static final Logger log = Logger.getLogger(TransactionPhaseListener.class);

   @Inject
   private ViewDataStore dataStore;

   @Inject
   private BeanManager manager;

   // TODO this is a weld bug that causes deployment to fail if inspected fields
   // classes cannot be found:-- https://jira.jboss.org/browse/WELD-740
   private Object persistenceContexts;
   private Object transaction;

   @PostConstruct
   public void init()
   {
      transaction = BeanManagerUtils.getContextualInstance(manager, SeamTransaction.class);
      persistenceContexts = BeanManagerUtils.getContextualInstance(manager, PersistenceContexts.class);
   }

   public PhaseId getPhaseId()
   {
      return ANY_PHASE;
   }

   public void beforePhase(final PhaseEvent event)
   {
      log.trace("before phase: " + event.getPhaseId());
      handleTransactionsBeforePhase(event);
   }

   public void afterPhase(final PhaseEvent event)
   {
      if (event.getPhaseId() == RENDER_RESPONSE)
      {
         ((org.jboss.seam.persistence.PersistenceContexts) persistenceContexts).afterRender();
      }
      handleTransactionsAfterPhase(event);
   }

   public void handleTransactionsBeforePhase(final PhaseEvent event)
   {
      PhaseId phaseId = event.getPhaseId();
      if (seamManagedTransactionStatus(phaseId))
      {
         if (phaseId == RENDER_RESPONSE)
         {
            ((PersistenceContexts) persistenceContexts).beforeRender();
         }
         boolean beginTran = ((phaseId == PhaseId.RENDER_RESPONSE) || (phaseId == PhaseId.RESTORE_VIEW));
         if (beginTran)
         {
            begin(phaseId);
         }
      }
   }

   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() || (phaseId == PhaseId.RENDER_RESPONSE);

         if (commitTran)
         {
            commitOrRollback(phaseId); // we commit before destroying contexts,
                                       // cos the contexts have the PC in them
         }
      }
   }

   void begin(final PhaseId phaseId)
   {
      begin("prior to phase: " + phaseId);
   }

   void begin(final String phaseString)
   {
      try
      {
         if (!((SeamTransaction) transaction).isActiveOrMarkedRollback())
         {
            log.debug("beginning transaction " + phaseString);
            ((UserTransaction) transaction).begin();
         }
      }
      catch (Exception e)
      {
         throw new IllegalStateException("Could not start transaction", e);
      }
   }

   void commitOrRollback(final PhaseId phaseId)
   {
      commitOrRollback("after phase: " + phaseId);
   }

   void commitOrRollback(final String phaseString)
   {
      try
      {
         if (((SeamTransaction) transaction).isActive())
         {
            try
            {
               log.debug("committing transaction " + phaseString);
               ((UserTransaction) transaction).commit();

            }
            catch (IllegalStateException e)
            {
               log.warn("TX commit failed with illegal state exception. This may be because the tx timed out and was rolled back in the background.",
                        e);
            }
         }
         else if (((SeamTransaction) transaction).isRolledBackOrMarkedRollback())
         {
            log.debug("rolling back transaction " + phaseString);
            ((UserTransaction) transaction).rollback();
         }

      }
      catch (Exception e)
      {
         throw new IllegalStateException("Could not commit transaction", e);
      }
   }

   private boolean seamManagedTransactionStatus(final PhaseId phase)
   {
      SeamManagedTransaction an = dataStore.getDataForCurrentViewId(SeamManagedTransaction.class);
      SeamManagedTransactionType config;
      if (an == null)
      {
         // enable seam managed transactions by default
         config = SeamManagedTransactionType.ENABLED;
      }
      else
      {
         config = an.value();
      }
      if (config == SeamManagedTransactionType.DISABLED)
      {
         return false;
      }
      else if ((config == SeamManagedTransactionType.RENDER_RESPONSE) && (phase != PhaseId.RENDER_RESPONSE))
      {
         return false;
      }
      return true;
   }
}

@@ -1154,95 +1154,8 @@
 E;%0A%0A
-import javax.annotation.PostConstruct;%0Aimport javax.enterprise.inject.spi.BeanManager;%0A
 impo
@@ -1369,59 +1369,8 @@
 er;%0A
-import org.jboss.seam.faces.util.BeanManagerUtils;%0A
 impo
@@ -1472,16 +1472,82 @@
 ntexts;%0A
+import org.jboss.seam.persistence.transaction.DefaultTransaction;%0A
 import o
@@ -2128,476 +2128,112 @@
 %0A   
-private BeanManager manager;%0A%0A   // TODO this is a weld bug that causes deployment to fail if inspected fields%0A   // classes cannot be found:-- https://jira.jboss.org/browse/WELD-740%0A   private Object persistenceContexts;%0A   private Object transaction;%0A%0A   @PostConstruct%0A   public void init()%0A   %7B%0A      transaction = BeanManagerUtils.getContextualInstance(manager, SeamTransaction.class);%0A      persistenceContexts = BeanManagerUtils.getContextualInstance(manager, P
+@DefaultTransaction%0A   private SeamTransaction transaction;%0A%0A   @Inject%0A   private PersistenceContexts p
 ersi
@@ -2250,21 +2250,9 @@
 exts
-.class);%0A   %7D
+;
 %0A%0A  
@@ -2603,57 +2603,8 @@
    (
-(org.jboss.seam.persistence.PersistenceContexts) 
 pers
@@ -2936,30 +2936,8 @@
    (
-(PersistenceContexts) 
 pers
@@ -3963,34 +3963,16 @@
   if (!(
-(SeamTransaction) 
 transact
@@ -4472,34 +4472,16 @@
    if ((
-(SeamTransaction) 
 transact
@@ -4961,26 +4961,8 @@
 f ((
-(SeamTransaction) 
 tran

fix for NPE w/persistence