myfaces-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lu4...@apache.org
Subject svn commit: r1141310 - in /myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context: AjaxExceptionHandlerImpl.java SwitchAjaxExceptionHandlerWrapperImpl.java
Date Wed, 29 Jun 2011 23:04:31 GMT
Author: lu4242
Date: Wed Jun 29 23:04:30 2011
New Revision: 1141310

URL: http://svn.apache.org/viewvc?rev=1141310&view=rev
Log:
MYFACES-3053 Improve error reporting and logging (Fix Ajax Case)

Added:
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context/AjaxExceptionHandlerImpl.java
    myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context/SwitchAjaxExceptionHandlerWrapperImpl.java

Added: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context/AjaxExceptionHandlerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context/AjaxExceptionHandlerImpl.java?rev=1141310&view=auto
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context/AjaxExceptionHandlerImpl.java
(added)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context/AjaxExceptionHandlerImpl.java
Wed Jun 29 23:04:30 2011
@@ -0,0 +1,311 @@
+/*
+ *  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.shared.context;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.el.ELException;
+import javax.faces.FacesException;
+import javax.faces.context.ExceptionHandler;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.PartialResponseWriter;
+import javax.faces.context.PartialViewContext;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ExceptionQueuedEvent;
+import javax.faces.event.ExceptionQueuedEventContext;
+import javax.faces.event.SystemEvent;
+
+/**
+ * 
+ * Specialized Ajax Handler, according to JSF 2.0 rev A section 13.3.7.
+ * 
+ * @author Leonardo Uribe
+ *
+ */
+public class AjaxExceptionHandlerImpl extends ExceptionHandler
+{
+    //The spec says this related to exception handling in ajax requests:
+    //
+    //"... JavaServer Faces Ajax frameworks must ensure exception information 
+    // is written to the response in the format: 
+    //
+    //<partial-response>
+    //  <error>
+    //    <error-name>...</error-name>
+    //    <error-message>...</error-message>
+    //  </error>
+    //</partial-response>
+    //
+    //Implementations must ensure that an ExceptionHandler suitable for writing 
+    //exceptions to the partial response is installed if the current request 
+    //required an Ajax response (PartialViewContext.isAjaxRequest() returns true)
+    //
+    //Implementations may choose to include a specialized ExceptionHandler for 
+    //Ajax that extends from javax.faces.context.ExceptionHandlerWrapper, and 
+    //have the javax.faces.context.ExceptionHandlerFactory implementation 
+    //install it if the environment requires it. ..."
+    
+    private static final Logger log = Logger.getLogger(AjaxExceptionHandlerImpl.class.getName());
+    
+    private Queue<ExceptionQueuedEvent> handled;
+    private Queue<ExceptionQueuedEvent> unhandled;
+    private ExceptionQueuedEvent handledAndThrown;
+    
+    public AjaxExceptionHandlerImpl()
+    {
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ExceptionQueuedEvent getHandledExceptionQueuedEvent()
+    {
+        return handledAndThrown;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Iterable<ExceptionQueuedEvent> getHandledExceptionQueuedEvents()
+    {
+        return handled == null ? Collections.<ExceptionQueuedEvent>emptyList() : handled;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Throwable getRootCause(Throwable t)
+    {
+        if (t == null)
+        {
+            throw new NullPointerException("t");
+        }
+        
+        while (t != null)
+        {
+            Class<?> clazz = t.getClass();
+            if (!clazz.equals(FacesException.class) && !clazz.equals(ELException.class))
+            {
+                return t;
+            }
+            
+            t = t.getCause();
+        }
+        
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Iterable<ExceptionQueuedEvent> getUnhandledExceptionQueuedEvents()
+    {
+        return unhandled == null ? Collections.<ExceptionQueuedEvent>emptyList() :
unhandled;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handle() throws FacesException
+    {
+        if (unhandled != null && !unhandled.isEmpty())
+        {
+            if (handled == null)
+            {
+                handled = new LinkedList<ExceptionQueuedEvent>();
+            }
+            
+            boolean init = false;
+            PartialResponseWriter partialWriter = null;
+
+            try
+            {
+                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 (!init)
+                        {
+                            //Retrieve the partial writer used to render the exception
+                            FacesContext facesContext = event.getContext().getContext();
+                            facesContext = (facesContext == null) ? FacesContext.getCurrentInstance()
: facesContext;
+                            ExternalContext externalContext = facesContext.getExternalContext();
+                            
+                            //Clean up the response if by some reason something has been
written before.
+                            if (!facesContext.getExternalContext().isResponseCommitted())
+                            {
+                                facesContext.getExternalContext().responseReset();
+                            }
+                            
+                            PartialViewContext partialViewContext = facesContext.getPartialViewContext();
+                            partialWriter = partialViewContext.getPartialResponseWriter();
+        
+                            // ajax request --> xml error page 
+                            externalContext.setResponseContentType("text/xml");
+                            externalContext.setResponseCharacterEncoding("UTF-8");
+                            externalContext.addResponseHeader("Cache-control", "no-cache");
+                            partialWriter.startDocument();
+                            init = true;
+                        }
+                        
+                        // 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);
+                            
+                            renderAjaxError(event, partialWriter, 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());
+            }
+            catch (IOException ioe)
+            {
+                throw new FacesException("Could not write the error page", ioe);
+            }
+            finally
+            {
+                if (init)
+                {
+                    try
+                    {
+                        partialWriter.endDocument();
+                    }
+                    catch (IOException ioe)
+                    {
+                        throw new FacesException("Could not write the error page", ioe);
+                    }
+                }
+            }
+        }
+    }
+    
+    protected void renderAjaxError(ExceptionQueuedEvent event, PartialResponseWriter partialWriter,
Throwable ex) throws IOException
+    {
+        partialWriter.startError(ex.getClass().getName());
+        if (ex.getCause() != null)
+        {
+            partialWriter.write(ex.getCause().toString());
+        }
+        else if (ex.getMessage() != null)
+        {
+            partialWriter.write(ex.getMessage());
+        }
+        partialWriter.endError();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isListenerForSource(Object source)
+    {
+        return source instanceof ExceptionQueuedEventContext;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void processEvent(SystemEvent exceptionQueuedEvent) throws AbortProcessingException
+    {
+        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;
+    }
+
+}

Added: myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context/SwitchAjaxExceptionHandlerWrapperImpl.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context/SwitchAjaxExceptionHandlerWrapperImpl.java?rev=1141310&view=auto
==============================================================================
--- myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context/SwitchAjaxExceptionHandlerWrapperImpl.java
(added)
+++ myfaces/shared/trunk_4.0.x/core/src/main/java/org/apache/myfaces/shared/context/SwitchAjaxExceptionHandlerWrapperImpl.java
Wed Jun 29 23:04:30 2011
@@ -0,0 +1,115 @@
+/*
+ *  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.shared.context;
+
+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;
+
+/**
+ * This wrapper is a switch to choose in a lazy way between ajax and
+ * normal exceptionHandler wrapping, because FacesContext is initialized after
+ * ExceptionHandler, so it is not safe to get it when
+ * ExceptionHandlerFactory.getExceptionHandler() is called.
+ * 
+ * @author Leonardo Uribe
+ *
+ */
+public class SwitchAjaxExceptionHandlerWrapperImpl extends ExceptionHandlerWrapper
+{
+    private ExceptionHandler _requestExceptionHandler;
+    private ExceptionHandler _ajaxExceptionHandler;
+    private Boolean _isAjaxRequest;
+    
+    public SwitchAjaxExceptionHandlerWrapperImpl(
+            ExceptionHandler requestExceptionHandler,
+            ExceptionHandler ajaxExceptionHandler)
+    {
+        _requestExceptionHandler = requestExceptionHandler;
+        _ajaxExceptionHandler = ajaxExceptionHandler;
+    }
+    
+    @Override
+    public void processEvent(SystemEvent exceptionQueuedEvent)
+            throws AbortProcessingException
+    {
+        //Check if this is an ajax request, but take advantage of exceptionQueuedEvent facesContext
+        isAjaxRequest(exceptionQueuedEvent);
+        super.processEvent(exceptionQueuedEvent);
+    }
+
+    protected boolean isAjaxRequest(SystemEvent exceptionQueuedEvent)
+    {
+        if (_isAjaxRequest == null)
+        {
+            if (exceptionQueuedEvent instanceof ExceptionQueuedEvent)
+            {
+                ExceptionQueuedEvent eqe = (ExceptionQueuedEvent)exceptionQueuedEvent;
+                ExceptionQueuedEventContext eqec = eqe.getContext();
+                if (eqec != null)
+                {
+                    FacesContext facesContext = eqec.getContext();
+                    if (facesContext != null)
+                    {
+                        return isAjaxRequest(facesContext);
+                    }
+                }
+            }
+            return isAjaxRequest();
+        }
+        return _isAjaxRequest;
+    }
+    
+    protected boolean isAjaxRequest(FacesContext facesContext)
+    {
+        if (_isAjaxRequest == null)
+        {
+            facesContext = (facesContext == null) ? FacesContext.getCurrentInstance() : facesContext;
+            _isAjaxRequest = facesContext.getPartialViewContext().isAjaxRequest();
+        }
+        return _isAjaxRequest;
+    }
+    
+    protected boolean isAjaxRequest()
+    {
+        if (_isAjaxRequest == null)
+        {
+            FacesContext facesContext = FacesContext.getCurrentInstance();
+            _isAjaxRequest = facesContext.getPartialViewContext().isAjaxRequest();
+        }
+        return _isAjaxRequest;
+    }
+    
+    @Override
+    public ExceptionHandler getWrapped()
+    {
+        if (isAjaxRequest())
+        {
+            return _ajaxExceptionHandler;
+        }
+        else
+        {
+            return _requestExceptionHandler;
+        }
+    }
+}



Mime
View raw message