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   * @since 2.0
39   */
40  public class ExceptionHandlerImpl extends ExceptionHandler
41  {
42      /*
43       * PLEASE NOTE!!!
44       * javax.faces.webapp.PreJsf2ExceptionHandlerFactory uses most parts of this implementation
45       * for its private static inner class, only the handle method differs a bit.
46       * Thus, any changes made here should also be applied to PreJsf2ExceptionHandlerFactory
47       * in the right way (you can copy everything except handle(), this method needs special treatment).
48       */
49      
50      private static final Logger log = Logger.getLogger(ExceptionHandlerImpl.class.getName());
51      
52      private Queue<ExceptionQueuedEvent> handled;
53      private Queue<ExceptionQueuedEvent> unhandled;
54      private ExceptionQueuedEvent handledAndThrown;
55  
56      public ExceptionHandlerImpl()
57      {
58          
59      }
60      
61      /**
62       * {@inheritDoc}
63       */
64      @Override
65      public ExceptionQueuedEvent getHandledExceptionQueuedEvent()
66      {
67          return handledAndThrown;
68      }
69  
70      /**
71       * {@inheritDoc}
72       */
73      @Override
74      public Iterable<ExceptionQueuedEvent> getHandledExceptionQueuedEvents()
75      {
76          return handled == null ? Collections.<ExceptionQueuedEvent>emptyList() : handled;
77      }
78  
79      /**
80       * {@inheritDoc}
81       */
82      @Override
83      public Throwable getRootCause(Throwable t)
84      {
85          if (t == null)
86          {
87              throw new NullPointerException("t");
88          }
89          
90          while (t != null)
91          {
92              Class<?> clazz = t.getClass();
93              if (!clazz.equals(FacesException.class) && !clazz.equals(ELException.class))
94              {
95                  return t;
96              }
97              
98              t = t.getCause();
99          }
100         
101         return null;
102     }
103 
104     /**
105      * {@inheritDoc}
106      */
107     @Override
108     public Iterable<ExceptionQueuedEvent> getUnhandledExceptionQueuedEvents()
109     {
110         return unhandled == null ? Collections.<ExceptionQueuedEvent>emptyList() : unhandled;
111     }
112 
113     /**
114      * {@inheritDoc}
115      */
116     @Override
117     public void handle() throws FacesException
118     {
119         if (unhandled != null && !unhandled.isEmpty())
120         {
121             if (handled == null)
122             {
123                 handled = new LinkedList<ExceptionQueuedEvent>();
124             }
125             
126             FacesException toThrow = null;
127             
128             do
129             {
130                 // For each ExceptionEvent in the list
131                 
132                 // get the event to handle
133                 ExceptionQueuedEvent event = unhandled.peek();
134                 try
135                 {
136                     // call its getContext() method
137                     ExceptionQueuedEventContext context = event.getContext();
138                     
139                     // and call getException() on the returned result
140                     Throwable exception = context.getException();
141                     
142                     // Upon encountering the first such Exception that is not an instance of
143                     // javax.faces.event.AbortProcessingException
144                     if (!shouldSkip(exception))
145                     {
146                         // set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event
147                         handledAndThrown = event;
148                         
149                         // Re-wrap toThrow in a ServletException or (PortletException, if in a portlet environment) 
150                         // and throw it
151                         // FIXME: The spec says to NOT use a FacesException to propagate the exception, but I see
152                         //        no other way as ServletException is not a RuntimeException
153                         toThrow = wrap(getRethrownException(exception));
154                         break;
155                     }
156                     else
157                     {
158                         // Testing mojarra it logs a message and the exception
159                         // however, this behaviour is not mentioned in the spec
160                         log.log(Level.SEVERE, exception.getClass().getName() + " occured while processing " +
161                                 (context.inBeforePhase() ? "beforePhase() of " : 
162                                         (context.inAfterPhase() ? "afterPhase() of " : "")) + 
163                                 "phase " + context.getPhaseId() + ": " +
164                                 "UIComponent-ClientId=" + 
165                                 (context.getComponent() != null ? 
166                                         context.getComponent().getClientId(context.getContext()) : "") + ", " +
167                                 "Message=" + exception.getMessage());
168                         
169                         log.log(Level.SEVERE, exception.getMessage(), exception);
170                         
171                     }
172                 }
173                 catch (Throwable t)
174                 {
175                     // A FacesException must be thrown if a problem occurs while performing
176                     // the algorithm to handle the exception
177                     throw new FacesException("Could not perform the algorithm to handle the Exception", t);
178                 }
179                 finally
180                 {
181                     // if we will throw the Exception or if we just logged it,
182                     // we handled it in either way --> add to handled
183                     handled.add(event);
184                     unhandled.remove(event);
185                 }
186             } while (!unhandled.isEmpty());
187             
188             // do we have to throw an Exception?
189             if (toThrow != null)
190             {
191                 throw toThrow;
192             }
193         }
194     }
195 
196     /**
197      * {@inheritDoc}
198      */
199     @Override
200     public boolean isListenerForSource(Object source)
201     {
202         return source instanceof ExceptionQueuedEventContext;
203     }
204 
205     /**
206      * {@inheritDoc}
207      */
208     @Override
209     public void processEvent(SystemEvent exceptionQueuedEvent) throws AbortProcessingException
210     {
211         if (unhandled == null)
212         {
213             unhandled = new LinkedList<ExceptionQueuedEvent>();
214         }
215         
216         unhandled.add((ExceptionQueuedEvent)exceptionQueuedEvent);
217     }
218     
219     protected Throwable getRethrownException(Throwable exception)
220     {
221         // Let toRethrow be either the result of calling getRootCause() on the Exception, 
222         // or the Exception itself, whichever is non-null
223         Throwable toRethrow = getRootCause(exception);
224         if (toRethrow == null)
225         {
226             toRethrow = exception;
227         }
228         
229         return toRethrow;
230     }
231     
232     protected FacesException wrap(Throwable exception)
233     {
234         if (exception instanceof FacesException)
235         {
236             return (FacesException) exception;
237         }
238         return new FacesException(exception);
239     }
240     
241     protected boolean shouldSkip(Throwable exception)
242     {
243         return exception instanceof AbortProcessingException;
244     }
245 }