View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.shared.context;
20  
21  import java.util.Collections;
22  import java.util.LinkedList;
23  import java.util.Queue;
24  import java.util.logging.Level;
25  import java.util.logging.Logger;
26  
27  import javax.el.ELException;
28  import javax.faces.FacesException;
29  import javax.faces.context.ExceptionHandler;
30  import javax.faces.event.AbortProcessingException;
31  import javax.faces.event.ExceptionQueuedEvent;
32  import javax.faces.event.ExceptionQueuedEventContext;
33  import javax.faces.event.SystemEvent;
34  
35  /**
36   * DOCUMENT ME!
37   * 
38   * @author (latest modification by $Author: bommel $)
39   * @version $Revision: 1187701 $ $Date: 2011-10-22 07:21:54 -0500 (Sat, 22 Oct 2011) $
40   * 
41   * @since 2.0
42   */
43  public class ExceptionHandlerImpl extends ExceptionHandler
44  {
45      /*
46       * PLEASE NOTE!!!
47       * javax.faces.webapp.PreJsf2ExceptionHandlerFactory uses most parts of this implementation
48       * for its private static inner class, only the handle method differs a bit.
49       * Thus, any changes made here should also be applied to PreJsf2ExceptionHandlerFactory
50       * in the right way (you can copy everything except handle(), this method needs special treatment).
51       */
52      
53      private static final Logger log = Logger.getLogger(ExceptionHandlerImpl.class.getName());
54      
55      private Queue<ExceptionQueuedEvent> handled;
56      private Queue<ExceptionQueuedEvent> unhandled;
57      private ExceptionQueuedEvent handledAndThrown;
58  
59      public ExceptionHandlerImpl()
60      {
61          
62      }
63      
64      /**
65       * {@inheritDoc}
66       */
67      @Override
68      public ExceptionQueuedEvent getHandledExceptionQueuedEvent()
69      {
70          return handledAndThrown;
71      }
72  
73      /**
74       * {@inheritDoc}
75       */
76      @Override
77      public Iterable<ExceptionQueuedEvent> getHandledExceptionQueuedEvents()
78      {
79          return handled == null ? Collections.<ExceptionQueuedEvent>emptyList() : handled;
80      }
81  
82      /**
83       * {@inheritDoc}
84       */
85      @Override
86      public Throwable getRootCause(Throwable t)
87      {
88          if (t == null)
89          {
90              throw new NullPointerException("t");
91          }
92          
93          while (t != null)
94          {
95              Class<?> clazz = t.getClass();
96              if (!clazz.equals(FacesException.class) && !clazz.equals(ELException.class))
97              {
98                  return t;
99              }
100             
101             t = t.getCause();
102         }
103         
104         return null;
105     }
106 
107     /**
108      * {@inheritDoc}
109      */
110     @Override
111     public Iterable<ExceptionQueuedEvent> getUnhandledExceptionQueuedEvents()
112     {
113         return unhandled == null ? Collections.<ExceptionQueuedEvent>emptyList() : unhandled;
114     }
115 
116     /**
117      * {@inheritDoc}
118      */
119     @Override
120     public void handle() throws FacesException
121     {
122         if (unhandled != null && !unhandled.isEmpty())
123         {
124             if (handled == null)
125             {
126                 handled = new LinkedList<ExceptionQueuedEvent>();
127             }
128             
129             FacesException toThrow = null;
130             
131             do
132             {
133                 // For each ExceptionEvent in the list
134                 
135                 // get the event to handle
136                 ExceptionQueuedEvent event = unhandled.peek();
137                 try
138                 {
139                     // call its getContext() method
140                     ExceptionQueuedEventContext context = event.getContext();
141                     
142                     // and call getException() on the returned result
143                     Throwable exception = context.getException();
144                     
145                     // Upon encountering the first such Exception that is not an instance of
146                     // javax.faces.event.AbortProcessingException
147                     if (!shouldSkip(exception))
148                     {
149                         // set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event
150                         handledAndThrown = event;
151                         
152                         // Re-wrap toThrow in a ServletException or (PortletException, if in a portlet environment) 
153                         // and throw it
154                         // FIXME: The spec says to NOT use a FacesException to propagate the exception, but I see
155                         //        no other way as ServletException is not a RuntimeException
156                         toThrow = wrap(getRethrownException(exception));
157                         break;
158                     }
159                     else
160                     {
161                         // Testing mojarra it logs a message and the exception
162                         // however, this behaviour is not mentioned in the spec
163                         log.log(Level.SEVERE, exception.getClass().getName() + " occured while processing " +
164                                 (context.inBeforePhase() ? "beforePhase() of " : 
165                                         (context.inAfterPhase() ? "afterPhase() of " : "")) + 
166                                 "phase " + context.getPhaseId() + ": " +
167                                 "UIComponent-ClientId=" + 
168                                 (context.getComponent() != null ? 
169                                         context.getComponent().getClientId(context.getContext()) : "") + ", " +
170                                 "Message=" + exception.getMessage());
171                         
172                         log.log(Level.SEVERE, exception.getMessage(), exception);
173                         
174                     }
175                 }
176                 catch (Throwable t)
177                 {
178                     // A FacesException must be thrown if a problem occurs while performing
179                     // the algorithm to handle the exception
180                     throw new FacesException("Could not perform the algorithm to handle the Exception", t);
181                 }
182                 finally
183                 {
184                     // if we will throw the Exception or if we just logged it,
185                     // we handled it in either way --> add to handled
186                     handled.add(event);
187                     unhandled.remove(event);
188                 }
189             } while (!unhandled.isEmpty());
190             
191             // do we have to throw an Exception?
192             if (toThrow != null)
193             {
194                 throw toThrow;
195             }
196         }
197     }
198 
199     /**
200      * {@inheritDoc}
201      */
202     @Override
203     public boolean isListenerForSource(Object source)
204     {
205         return source instanceof ExceptionQueuedEventContext;
206     }
207 
208     /**
209      * {@inheritDoc}
210      */
211     @Override
212     public void processEvent(SystemEvent exceptionQueuedEvent) throws AbortProcessingException
213     {
214         if (unhandled == null)
215         {
216             unhandled = new LinkedList<ExceptionQueuedEvent>();
217         }
218         
219         unhandled.add((ExceptionQueuedEvent)exceptionQueuedEvent);
220     }
221     
222     protected Throwable getRethrownException(Throwable exception)
223     {
224         // Let toRethrow be either the result of calling getRootCause() on the Exception, 
225         // or the Exception itself, whichever is non-null
226         Throwable toRethrow = getRootCause(exception);
227         if (toRethrow == null)
228         {
229             toRethrow = exception;
230         }
231         
232         return toRethrow;
233     }
234     
235     protected FacesException wrap(Throwable exception)
236     {
237         if (exception instanceof FacesException)
238         {
239             return (FacesException) exception;
240         }
241         return new FacesException(exception);
242     }
243     
244     protected boolean shouldSkip(Throwable exception)
245     {
246         return exception instanceof AbortProcessingException;
247     }
248 }