/**
 *
 */
package org.ajax4jsf.portlet;

import java.io.IOException;
import java.io.InputStream;

import javax.faces.context.FacesContext;
import javax.portlet.faces.BridgeException;
import javax.servlet.ServletException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.ajax4jsf.portlet.application.PortletViewState;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.Seam;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.contexts.FacesLifecycle;
import org.jboss.seam.core.ConversationPropagation;
import org.jboss.seam.core.Manager;
import org.jboss.seam.exception.Exceptions;
import org.jboss.seam.transaction.Transaction;
import org.jboss.seam.transaction.UserTransaction;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 * @author asmirnov
 * @author <a href="mailto:whales@redhat.com">Wesley Hales</a>
 */

public class SeamExceptionHandlerImpl implements ExceptionHandler
{
   protected static final Log log = LogFactory.getLog(SeamExceptionHandlerImpl.class);


   /* (non-Javadoc)
   * @see org.ajax4jsf.portlet.ExceptionHandler#processActionException(javax.faces.context.FacesContext, org.ajax4jsf.portlet.application.PortletViewState, java.lang.Exception)
   */
   public void processActionException(FacesContext context,
                                      PortletViewState windowState, Exception e) throws BridgeException
   {
      log.error("Error processing action lifecycle", e);
      windowState.saveBeans(context);
      windowState.saveMessages(context);
      handleException(context, e);
      windowState.setViewRoot(context.getViewRoot());
   }

   /* (non-Javadoc)
    * @see org.ajax4jsf.portlet.ExceptionHandler#processRenderException(javax.faces.context.FacesContext, org.ajax4jsf.portlet.application.PortletViewState, java.lang.Exception)
    */
   public void processRenderException(FacesContext context,
                                      PortletViewState windowState, Exception e) throws BridgeException
   {
      log.error("Error processing render lifecycle", e);
      handleException(context, e);
   }



   public static Class classForName(String name) throws ClassNotFoundException
   {
      try
      {
         return Thread.currentThread().getContextClassLoader().loadClass(name);
      }
      catch (Exception e)
      {
         return Class.forName(name);
      }
   }
   
   private void handleException(FacesContext facesContext, Exception e) throws BridgeException {
	      //if the event context was cleaned up, fish the conversation id 
	      //directly out of the ServletRequest attributes, else get it from
	      //the event context
	      Manager manager = Contexts.isEventContextActive() ?
	              (Manager) Contexts.getEventContext().get(Manager.class) :
	              null;
	      String conversationId = manager==null ? null : manager.getCurrentConversationId();
	      
	      //Initialize the temporary context objects
	      FacesLifecycle.beginExceptionRecovery( facesContext.getExternalContext() );
	      
	      //If there is an existing long-running conversation on
	      //the failed request, propagate it
	      if (conversationId==null)
	      {
	          Manager.instance().initializeTemporaryConversation();
	      }
	      else
	      {
	          ConversationPropagation.instance().setConversationId(conversationId);
	          Manager.instance().restoreConversation();
	      }
	      
	      //Now do the exception handling
	      try
	      {
	         rollbackTransactionIfNecessary();
	         Exceptions.instance().handle(e);
	      }
	      catch (Exception ehe)
	      {
	         throw new BridgeException(ehe);
	      }
	      finally
	      {
	         //Finally, clean up the contexts
	         try 
	         {
	            FacesLifecycle.endRequest( facesContext.getExternalContext() );
	            log.debug("done running exception handlers");
	         }
	         catch (Exception ere)
	         {
	            log.error("could not destroy contexts", ere);
	         }
	      }

   }

   protected void rollbackTransactionIfNecessary()
   {
      try 
      {
         UserTransaction transaction = Transaction.instance();
         if ( transaction.isActiveOrMarkedRollback() || transaction.isRolledBack() )
         {
            log.debug("killing transaction");
            transaction.rollback();
         }
      }
      catch (Exception te)
      {
         log.error("could not roll back transaction", te);
      }
   }
}
