You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2011/06/30 01:22:31 UTC

svn commit: r1141325 - in /myfaces/core/trunk/impl/src/main/java/org/apache/myfaces: application/ context/ lifecycle/ renderkit/

Author: lu4242
Date: Wed Jun 29 23:22:30 2011
New Revision: 1141325

URL: http://svn.apache.org/viewvc?rev=1141325&view=rev
Log:
MYFACES-3053 Improve error reporting and logging (fix Ajax case, add MyFacesExceptionHandlerWrapperImpl, output multiple exception stack traces, disable MyFaces exception handling if Project Stage is not Development)

Added:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/MyFacesExceptionHandlerWrapperImpl.java
Modified:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerImpl.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/ExceptionHandlerFactoryImpl.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/lifecycle/LifecycleImpl.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/ErrorPageWriter.java

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerImpl.java?rev=1141325&r1=1141324&r2=1141325&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerImpl.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerImpl.java Wed Jun 29 23:22:30 2011
@@ -259,8 +259,8 @@ public class ResourceHandlerImpl extends
     @Override
     public void handleResourceRequest(FacesContext facesContext) throws IOException
     {
-        try
-        {
+        //try
+        //{
             String resourceBasePath = getResourceHandlerSupport()
                     .calculateResourceBasePath(facesContext);
     
@@ -373,14 +373,14 @@ public class ResourceHandlerImpl extends
                             + e.getMessage());
                 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
             }
-        }
-        catch (Throwable ex)
-        {
+        //}
+        //catch (Throwable ex)
+        //{
             // handle the Throwable accordingly. Maybe generate an error page.
             // FIXME we are creating a html error page for a non html request here
             // shouln't we do something better? -=Jakob Korherr=-
-            ErrorPageWriter.handleThrowable(facesContext, ex);
-        }
+            //ErrorPageWriter.handleThrowable(facesContext, ex);
+        //}
     }
 
     /**

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/ExceptionHandlerFactoryImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/ExceptionHandlerFactoryImpl.java?rev=1141325&r1=1141324&r2=1141325&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/ExceptionHandlerFactoryImpl.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/ExceptionHandlerFactoryImpl.java Wed Jun 29 23:22:30 2011
@@ -21,7 +21,9 @@ package org.apache.myfaces.context;
 import javax.faces.context.ExceptionHandler;
 import javax.faces.context.ExceptionHandlerFactory;
 
+import org.apache.myfaces.shared_impl.context.AjaxExceptionHandlerImpl;
 import org.apache.myfaces.shared_impl.context.ExceptionHandlerImpl;
+import org.apache.myfaces.shared_impl.context.SwitchAjaxExceptionHandlerWrapperImpl;
 
 /**
  * DOCUMENT ME!
@@ -37,6 +39,8 @@ public class ExceptionHandlerFactoryImpl
     @Override
     public ExceptionHandler getExceptionHandler()
     {
-        return new ExceptionHandlerImpl();
+        return new SwitchAjaxExceptionHandlerWrapperImpl(
+                new MyFacesExceptionHandlerWrapperImpl(new ExceptionHandlerImpl()) , 
+                new AjaxExceptionHandlerImpl());
     }
 }

Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/MyFacesExceptionHandlerWrapperImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/MyFacesExceptionHandlerWrapperImpl.java?rev=1141325&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/MyFacesExceptionHandlerWrapperImpl.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/MyFacesExceptionHandlerWrapperImpl.java Wed Jun 29 23:22:30 2011
@@ -0,0 +1,344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.context;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.faces.FacesException;
+import javax.faces.application.ProjectStage;
+import javax.faces.context.ExceptionHandler;
+import javax.faces.context.ExceptionHandlerWrapper;
+import javax.faces.context.FacesContext;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ExceptionQueuedEvent;
+import javax.faces.event.ExceptionQueuedEventContext;
+import javax.faces.event.SystemEvent;
+
+import org.apache.myfaces.renderkit.ErrorPageWriter;
+import org.apache.myfaces.shared_impl.util.WebConfigParamUtils;
+import org.apache.myfaces.spi.WebConfigProvider;
+import org.apache.myfaces.spi.WebConfigProviderFactory;
+
+/**
+ * Extended MyFaces-specific ExceptionHandler implementation. 
+ * 
+ * @author Leonardo Uribe
+ *
+ */
+public class MyFacesExceptionHandlerWrapperImpl extends ExceptionHandlerWrapper
+{
+    private static final Logger log = Logger.getLogger(MyFacesExceptionHandlerWrapperImpl.class.getName());
+    
+    private Queue<ExceptionQueuedEvent> handled;
+    private Queue<ExceptionQueuedEvent> unhandled;
+    private ExceptionQueuedEvent handledAndThrown;
+
+    
+    private ExceptionHandler _delegate;
+    private boolean _isErrorPagePresent;
+    private boolean _useMyFacesErrorHandling;
+    private boolean _inited;
+
+    public MyFacesExceptionHandlerWrapperImpl(ExceptionHandler delegate)
+    {
+        this._delegate = delegate;
+        this._inited = false;
+    }
+    
+    protected void init()
+    {
+        if (!_inited)
+        {
+            FacesContext facesContext = FacesContext.getCurrentInstance();
+            WebConfigProvider webConfigProvider = WebConfigProviderFactory.getWebConfigProviderFactory(
+                    facesContext.getExternalContext()).getWebConfigProvider(facesContext.getExternalContext());
+    
+            _isErrorPagePresent = webConfigProvider.isErrorPagePresent(facesContext.getExternalContext());
+            _useMyFacesErrorHandling = WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
+                    ErrorPageWriter.ERROR_HANDLING_PARAMETER, true);
+            _inited = true;
+        }
+    }
+    
+    protected void init(FacesContext facesContext)
+    {
+        if (!_inited)
+        {
+            if (facesContext == null)
+            {
+                facesContext = FacesContext.getCurrentInstance();
+            }
+            WebConfigProvider webConfigProvider = WebConfigProviderFactory.getWebConfigProviderFactory(
+                    facesContext.getExternalContext()).getWebConfigProvider(facesContext.getExternalContext());
+    
+            _isErrorPagePresent = webConfigProvider.isErrorPagePresent(facesContext.getExternalContext());
+            _useMyFacesErrorHandling = WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
+                    ErrorPageWriter.ERROR_HANDLING_PARAMETER, facesContext.isProjectStage(ProjectStage.Development) ? true : false);
+            _inited = true;
+        }
+    }
+    
+    protected void init(SystemEvent exceptionQueuedEvent)
+    {
+        if (!_inited)
+        {
+            if (exceptionQueuedEvent instanceof ExceptionQueuedEvent)
+            {
+                ExceptionQueuedEvent eqe = (ExceptionQueuedEvent)exceptionQueuedEvent;
+                ExceptionQueuedEventContext eqec = eqe.getContext();
+                if (eqec != null)
+                {
+                    FacesContext facesContext = eqec.getContext();
+                    if (facesContext != null)
+                    {
+                        init(facesContext);
+                        return;
+                    }
+                }
+            }
+            init(FacesContext.getCurrentInstance());
+        }
+    }
+    
+    protected boolean isUseMyFacesErrorHandling()
+    {
+        return _useMyFacesErrorHandling;
+    }
+    
+    protected boolean isErrorPagePresent()
+    {
+        return _isErrorPagePresent;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ExceptionQueuedEvent getHandledExceptionQueuedEvent()
+    {
+        init();
+        if (!isUseMyFacesErrorHandling())
+        {
+            return super.getHandledExceptionQueuedEvent();
+        }
+        else
+        {
+            return handledAndThrown;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Iterable<ExceptionQueuedEvent> getHandledExceptionQueuedEvents()
+    {
+        init();
+        if (!isUseMyFacesErrorHandling())
+        {
+            return super.getHandledExceptionQueuedEvents();
+        }
+        else
+        {
+            return handled == null ? Collections.<ExceptionQueuedEvent>emptyList() : handled;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Iterable<ExceptionQueuedEvent> getUnhandledExceptionQueuedEvents()
+    {
+        init();
+        if (!isUseMyFacesErrorHandling())
+        {
+            return super.getUnhandledExceptionQueuedEvents();
+        }
+        else
+        {
+            return unhandled == null ? Collections.<ExceptionQueuedEvent>emptyList() : unhandled;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handle() throws FacesException
+    {
+        init();
+        if (!isUseMyFacesErrorHandling())
+        {
+            if (isErrorPagePresent())
+            {
+                FacesContext facesContext = FacesContext.getCurrentInstance();
+                // save current view in the request map to access it on the error page
+                facesContext.getExternalContext().getRequestMap().put(ErrorPageWriter.VIEW_KEY, facesContext.getViewRoot());
+            }
+            super.handle();
+            return;
+        }
+        else
+        {
+            if (unhandled != null && !unhandled.isEmpty())
+            {
+                if (handled == null)
+                {
+                    handled = new LinkedList<ExceptionQueuedEvent>();
+                }
+                
+                List<Throwable> throwableList = new ArrayList<Throwable>();
+                FacesContext facesContext = null;
+                
+                do
+                {
+                    // For each ExceptionEvent in the list
+                    
+                    // get the event to handle
+                    ExceptionQueuedEvent event = unhandled.peek();
+                    try
+                    {
+                        // call its getContext() method
+                        ExceptionQueuedEventContext context = event.getContext();
+    
+                        if (facesContext == null)
+                        {
+                            facesContext = event.getContext().getContext();
+                        }
+                        
+                        // and call getException() on the returned result
+                        Throwable exception = context.getException();
+                        
+                        // Upon encountering the first such Exception that is not an instance of
+                        // javax.faces.event.AbortProcessingException
+                        if (!shouldSkip(exception))
+                        {
+                            // set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event
+                            handledAndThrown = event;
+                            
+                            Throwable rootCause = getRootCause(exception);
+                            
+                            throwableList.add(rootCause == null ? exception : rootCause);
+                            
+                            //break;
+                        }
+                        else
+                        {
+                            // Testing mojarra it logs a message and the exception
+                            // however, this behaviour is not mentioned in the spec
+                            log.log(Level.SEVERE, exception.getClass().getName() + " occured while processing " +
+                                    (context.inBeforePhase() ? "beforePhase() of " : 
+                                            (context.inAfterPhase() ? "afterPhase() of " : "")) + 
+                                    "phase " + context.getPhaseId() + ": " +
+                                    "UIComponent-ClientId=" + 
+                                    (context.getComponent() != null ? 
+                                            context.getComponent().getClientId(context.getContext()) : "") + ", " +
+                                    "Message=" + exception.getMessage());
+                            
+                            log.log(Level.SEVERE, exception.getMessage(), exception);
+                        }
+                    }
+                    finally
+                    {
+                        // if we will throw the Exception or if we just logged it,
+                        // we handled it in either way --> add to handled
+                        handled.add(event);
+                        unhandled.remove(event);
+                    }
+                } while (!unhandled.isEmpty());
+
+                if (facesContext == null)
+                {
+                    facesContext = FacesContext.getCurrentInstance();
+                }
+                if (throwableList.size() == 1)
+                {
+                    ErrorPageWriter.handle(facesContext, throwableList.get(0));
+                }
+                else
+                {
+                    ErrorPageWriter.handle(facesContext, throwableList.toArray(new Throwable[throwableList.size()]));
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void processEvent(SystemEvent exceptionQueuedEvent) throws AbortProcessingException
+    {
+        init(exceptionQueuedEvent);
+        
+        if (!isUseMyFacesErrorHandling())
+        {
+            super.processEvent(exceptionQueuedEvent);
+        }
+        else
+        {
+            if (unhandled == null)
+            {
+                unhandled = new LinkedList<ExceptionQueuedEvent>();
+            }
+            
+            unhandled.add((ExceptionQueuedEvent)exceptionQueuedEvent);
+        }
+    }
+
+    protected Throwable getRethrownException(Throwable exception)
+    {
+        // Let toRethrow be either the result of calling getRootCause() on the Exception, 
+        // or the Exception itself, whichever is non-null
+        Throwable toRethrow = getRootCause(exception);
+        if (toRethrow == null)
+        {
+            toRethrow = exception;
+        }
+        
+        return toRethrow;
+    }
+    
+    protected FacesException wrap(Throwable exception)
+    {
+        if (exception instanceof FacesException)
+        {
+            return (FacesException) exception;
+        }
+        return new FacesException(exception);
+    }
+    
+    protected boolean shouldSkip(Throwable exception)
+    {
+        return exception instanceof AbortProcessingException;
+    }
+    
+    @Override
+    public ExceptionHandler getWrapped()
+    {
+        return _delegate;
+    }
+}

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/lifecycle/LifecycleImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/lifecycle/LifecycleImpl.java?rev=1141325&r1=1141324&r2=1141325&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/lifecycle/LifecycleImpl.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/lifecycle/LifecycleImpl.java Wed Jun 29 23:22:30 2011
@@ -102,8 +102,8 @@ public class LifecycleImpl extends Lifec
     @Override
     public void execute(FacesContext facesContext) throws FacesException
     {
-        try
-        {
+        //try
+        //{
             // check for updates of web.xml and faces-config descriptors 
             // only if project state is not production
             if(!facesContext.isProjectStage(ProjectStage.Production))
@@ -120,12 +120,12 @@ public class LifecycleImpl extends Lifec
                     return;
                 }
             }
-        }
-        catch (Throwable ex)
-        {
+        //}
+        //catch (Throwable ex)
+        //{
             // handle the Throwable accordingly. Maybe generate an error page.
-            ErrorPageWriter.handleThrowable(facesContext, ex);
-        }
+            //ErrorPageWriter.handleThrowable(facesContext, ex);
+        //}
     }
 
     private boolean executePhase(FacesContext context, PhaseExecutor executor, PhaseListenerManager phaseListenerMgr)
@@ -207,8 +207,8 @@ public class LifecycleImpl extends Lifec
     @Override
     public void render(FacesContext facesContext) throws FacesException
     {
-        try
-        {
+        //try
+        //{
             // if the response is complete we should not be invoking the phase listeners
             if (isResponseComplete(facesContext, renderExecutor.getPhase(), true))
             {
@@ -267,12 +267,12 @@ public class LifecycleImpl extends Lifec
             {
                 log.finest("exiting " + renderExecutor.getPhase() + " in " + LifecycleImpl.class.getName());
             }
-        }
-        catch (Throwable ex)
-        {
+        //}
+        //catch (Throwable ex)
+        //{
             // handle the Throwable accordingly. Maybe generate an error page.
-            ErrorPageWriter.handleThrowable(facesContext, ex);
-        }
+            //ErrorPageWriter.handleThrowable(facesContext, ex);
+        //}
     }
 
     private boolean isResponseComplete(FacesContext facesContext, PhaseId phase, boolean before)

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/ErrorPageWriter.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/ErrorPageWriter.java?rev=1141325&r1=1141324&r2=1141325&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/ErrorPageWriter.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/ErrorPageWriter.java Wed Jun 29 23:22:30 2011
@@ -72,7 +72,6 @@ import org.apache.myfaces.buildtools.mav
 import org.apache.myfaces.shared_impl.renderkit.html.HtmlResponseWriterImpl;
 import org.apache.myfaces.shared_impl.util.ClassUtils;
 import org.apache.myfaces.shared_impl.util.StateUtils;
-import org.apache.myfaces.shared_impl.webapp.webxml.WebXml;
 import org.apache.myfaces.spi.WebConfigProvider;
 import org.apache.myfaces.spi.WebConfigProviderFactory;
 import org.apache.myfaces.view.facelets.component.UIRepeat;
@@ -115,7 +114,7 @@ public final class ErrorPageWriter
             UIViewRoot view = (UIViewRoot) requestMap.get(VIEW_KEY);
             
             StringWriter writer = new StringWriter();
-            ErrorPageWriter.debugHtml(writer, facesContext, t, view);
+            ErrorPageWriter.debugHtml(writer, facesContext, view, t);
             String html = writer.toString();
             
             // change the HTML in the buffer to be included in an existing html page
@@ -187,7 +186,7 @@ public final class ErrorPageWriter
     public static final String ERROR_PAGE_BEAN_KEY = "__myFacesErrorPageBean";
     
     private static final String EXCEPTION_KEY = "javax.servlet.error.exception";
-    private static final String VIEW_KEY = "org.apache.myfaces.error.UIViewRoot";
+    public static final String VIEW_KEY = "org.apache.myfaces.error.UIViewRoot";
 
     private static final Logger log = Logger.getLogger(ErrorPageWriter.class.getName());
 
@@ -229,9 +228,9 @@ public final class ErrorPageWriter
 
     /**
      * Indicate if myfaces is responsible to handle errors. 
-     * See http://wiki.apache.org/myfaces/Handling_Server_Errors for details. 
+     * See http://wiki.apache.org/myfaces/Handling_Server_Errors for details.
      */
-    @JSFWebConfigParam(defaultValue="true",expectedValues="true,false", since="1.2.4")
+    @JSFWebConfigParam(defaultValue="false, on Development Project stage: true",expectedValues="true,false", since="1.2.4")
     public static final String ERROR_HANDLING_PARAMETER = "org.apache.myfaces.ERROR_HANDLING";
 
     public ErrorPageWriter()
@@ -249,9 +248,10 @@ public final class ErrorPageWriter
      */
     public static void debugHtml(Writer writer, FacesContext faces, Throwable e) throws IOException
     {
-        debugHtml(writer, faces, e, faces.getViewRoot());
+        debugHtml(writer, faces, faces.getViewRoot(), e);
     }
     
+    /*
     private static void debugHtml(Writer writer, FacesContext faces, Throwable e, UIViewRoot view) throws IOException
     {
         _init(faces);
@@ -298,6 +298,81 @@ public final class ErrorPageWriter
                 writer.write(ERROR_PARTS[i]);
             }
         }
+    }*/
+    
+    private static void debugHtml(Writer writer, FacesContext faces, UIViewRoot view,  Throwable... exs) throws IOException
+    {
+        _init(faces);
+        Date now = new Date();
+        for (int i = 0; i < ERROR_PARTS.length; i++)
+        {
+            if ("message".equals(ERROR_PARTS[i]))
+            {
+                boolean printed = false;
+                for (Throwable e : exs)
+                {
+                    String msg = e.getMessage();
+                    if (printed)
+                    {
+                        writer.write("<br/>");
+                    }
+                    if (msg != null)
+                    {
+                        writer.write(msg.replaceAll("<", TS));
+                    }
+                    else
+                    {
+                        writer.write(e.getClass().getName());
+                    }
+                    printed = true;
+                }
+            }
+            else if ("trace".equals(ERROR_PARTS[i]))
+            {
+                boolean printed = false;
+                for (Throwable e : exs)
+                {
+                    if (printed)
+                    {
+                        writer.write("\n");
+                    }
+                    _writeException(writer, e);
+                    printed = true;
+                }
+            }
+            else if ("now".equals(ERROR_PARTS[i]))
+            {
+                writer.write(DateFormat.getDateTimeInstance().format(now));
+            }
+            else if ("tree".equals(ERROR_PARTS[i]))
+            {
+                if (view != null)
+                {
+                    _writeComponent(faces, writer, view, _getErrorId(exs));
+                }
+            }
+            else if ("vars".equals(ERROR_PARTS[i]))
+            {
+                _writeVariables(writer, faces, view);
+            }
+            else if ("cause".equals(ERROR_PARTS[i]))
+            {
+                boolean printed = false;
+                for (Throwable e : exs)
+                {
+                    if (printed)
+                    {
+                        writer.write("<br/>");
+                    }
+                    _writeCause(writer, e);
+                    printed = true;
+                }
+            }
+            else
+            {
+                writer.write(ERROR_PARTS[i]);
+            }
+        }
     }
 
     /**
@@ -339,6 +414,37 @@ public final class ErrorPageWriter
             }
         }
     }
+    
+    public static void handle(FacesContext facesContext, Throwable... exs) throws FacesException
+    {
+        for (Throwable ex : exs)
+        {
+            _prepareExceptionStack(ex);
+        }
+        
+        if (!facesContext.getExternalContext().isResponseCommitted())
+        {
+            facesContext.getExternalContext().responseReset();
+        }
+
+        // normal request --> html error page
+        facesContext.getExternalContext().setResponseContentType("text/html");
+        facesContext.getExternalContext().setResponseCharacterEncoding("UTF-8");
+        try
+        {
+            // We need the real one, because the one returned from FacesContext.getResponseWriter()
+            // is configured with the encoding of the view.
+            Writer writer = facesContext.getExternalContext().getResponseOutputWriter();
+            debugHtml(writer, facesContext, facesContext.getViewRoot(), exs);
+        }
+        catch(IOException ioe)
+        {
+            throw new FacesException("Could not write the error page", ioe);
+        }
+
+        // mark the response as complete
+        facesContext.responseComplete();
+    }
 
     /**
      * Handles the given Throwbale in the following way:
@@ -350,7 +456,9 @@ public final class ErrorPageWriter
      * @param facesContext
      * @param ex
      * @throws FacesException
+     * @deprecated Use MyFacesExceptionHandlerWrapperImpl and handle() method
      */
+    @Deprecated
     public static void handleThrowable(FacesContext facesContext, Throwable ex) throws FacesException
     {
         _prepareExceptionStack(ex);
@@ -520,23 +628,32 @@ public final class ErrorPageWriter
         return str.split("@@");
     }
     
-    private static List<String> _getErrorId(Throwable e)
+    private static List<String> _getErrorId(Throwable... exs)
     {
-        String message = e.getMessage();
-
-        if (message == null)
-            return null;
-
-        List<String> list = new ArrayList<String>();
-        Pattern pattern = Pattern.compile(REGEX_PATTERN);
-        Matcher matcher = pattern.matcher(message);
-
-        while (matcher.find())
+        List<String> list = null;
+        for (Throwable e : exs)
         {
-            list.add(matcher.group(1));
+            String message = e.getMessage();
+    
+            if (message == null)
+                continue;
+    
+            Pattern pattern = Pattern.compile(REGEX_PATTERN);
+            Matcher matcher = pattern.matcher(message);
+    
+            while (matcher.find())
+            {
+                if (list == null)
+                {
+                    list = new ArrayList<String>(); 
+                }
+                list.add(matcher.group(1));
+            }
         }
-        if (list.size() > 0)
+        if (list != null && list.size() > 0)
+        {
             return list;
+        }
         return null;
     }