Author: lu4242
Date: Wed Jun 29 23:04:13 2011
New Revision: 1141309
URL: http://svn.apache.org/viewvc?rev=1141309&view=rev
Log:
MYFACES-3053 Improve error reporting and logging (Fix Ajax Case)
Added:
myfaces/shared/trunk/core/src/main/java/org/apache/myfaces/shared/context/AjaxExceptionHandlerImpl.java
myfaces/shared/trunk/core/src/main/java/org/apache/myfaces/shared/context/SwitchAjaxExceptionHandlerWrapperImpl.java
Added: myfaces/shared/trunk/core/src/main/java/org/apache/myfaces/shared/context/AjaxExceptionHandlerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk/core/src/main/java/org/apache/myfaces/shared/context/AjaxExceptionHandlerImpl.java?rev=1141309&view=auto
==============================================================================
--- myfaces/shared/trunk/core/src/main/java/org/apache/myfaces/shared/context/AjaxExceptionHandlerImpl.java
(added)
+++ myfaces/shared/trunk/core/src/main/java/org/apache/myfaces/shared/context/AjaxExceptionHandlerImpl.java
Wed Jun 29 23:04:13 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/core/src/main/java/org/apache/myfaces/shared/context/SwitchAjaxExceptionHandlerWrapperImpl.java
URL: http://svn.apache.org/viewvc/myfaces/shared/trunk/core/src/main/java/org/apache/myfaces/shared/context/SwitchAjaxExceptionHandlerWrapperImpl.java?rev=1141309&view=auto
==============================================================================
--- myfaces/shared/trunk/core/src/main/java/org/apache/myfaces/shared/context/SwitchAjaxExceptionHandlerWrapperImpl.java
(added)
+++ myfaces/shared/trunk/core/src/main/java/org/apache/myfaces/shared/context/SwitchAjaxExceptionHandlerWrapperImpl.java
Wed Jun 29 23:04:13 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;
+ }
+ }
+}
|