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.context;
20  
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Queue;
26  import java.util.logging.Level;
27  import java.util.logging.Logger;
28  
29  import javax.faces.FacesException;
30  import javax.faces.application.ProjectStage;
31  import javax.faces.component.UIComponent;
32  import javax.faces.context.ExceptionHandler;
33  import javax.faces.context.ExceptionHandlerWrapper;
34  import javax.faces.context.FacesContext;
35  import javax.faces.event.AbortProcessingException;
36  import javax.faces.event.ExceptionQueuedEvent;
37  import javax.faces.event.ExceptionQueuedEventContext;
38  import javax.faces.event.SystemEvent;
39  import javax.servlet.http.HttpServletResponse;
40  
41  import org.apache.myfaces.lifecycle.ViewNotFoundException;
42  import org.apache.myfaces.renderkit.ErrorPageWriter;
43  import org.apache.myfaces.shared.util.WebConfigParamUtils;
44  import org.apache.myfaces.spi.WebConfigProvider;
45  import org.apache.myfaces.spi.WebConfigProviderFactory;
46  
47  /**
48   * Extended MyFaces-specific ExceptionHandler implementation. 
49   * 
50   * @author Leonardo Uribe
51   *
52   */
53  public class MyFacesExceptionHandlerWrapperImpl extends ExceptionHandlerWrapper
54  {
55      private static final Logger log = Logger.getLogger(MyFacesExceptionHandlerWrapperImpl.class.getName());
56      
57      private Queue<ExceptionQueuedEvent> handled;
58      private Queue<ExceptionQueuedEvent> unhandled;
59      private ExceptionQueuedEvent handledAndThrown;
60  
61      
62      private ExceptionHandler _delegate;
63      private boolean _isErrorPagePresent;
64      private boolean _useMyFacesErrorHandling;
65      private boolean _inited;
66  
67      public MyFacesExceptionHandlerWrapperImpl(ExceptionHandler delegate)
68      {
69          this._delegate = delegate;
70          this._inited = false;
71      }
72      
73      protected void init()
74      {
75          if (!_inited)
76          {
77              FacesContext facesContext = FacesContext.getCurrentInstance();
78              WebConfigProvider webConfigProvider = WebConfigProviderFactory.getWebConfigProviderFactory(
79                      facesContext.getExternalContext()).getWebConfigProvider(facesContext.getExternalContext());
80      
81              _isErrorPagePresent = webConfigProvider.isErrorPagePresent(facesContext.getExternalContext());
82              _useMyFacesErrorHandling = WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
83                      ErrorPageWriter.ERROR_HANDLING_PARAMETER, facesContext.isProjectStage(ProjectStage.Development));
84              _inited = true;
85          }
86      }
87      
88      protected void init(FacesContext facesContext)
89      {
90          if (!_inited)
91          {
92              if (facesContext == null)
93              {
94                  facesContext = FacesContext.getCurrentInstance();
95              }
96              WebConfigProvider webConfigProvider = WebConfigProviderFactory.getWebConfigProviderFactory(
97                      facesContext.getExternalContext()).getWebConfigProvider(facesContext.getExternalContext());
98      
99              _isErrorPagePresent = webConfigProvider.isErrorPagePresent(facesContext.getExternalContext());
100             _useMyFacesErrorHandling = WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
101                     ErrorPageWriter.ERROR_HANDLING_PARAMETER, facesContext.isProjectStage(ProjectStage.Development));
102             _inited = true;
103         }
104     }
105     
106     protected void init(SystemEvent exceptionQueuedEvent)
107     {
108         if (!_inited)
109         {
110             if (exceptionQueuedEvent instanceof ExceptionQueuedEvent)
111             {
112                 ExceptionQueuedEvent eqe = (ExceptionQueuedEvent)exceptionQueuedEvent;
113                 ExceptionQueuedEventContext eqec = eqe.getContext();
114                 if (eqec != null)
115                 {
116                     FacesContext facesContext = eqec.getContext();
117                     if (facesContext != null)
118                     {
119                         init(facesContext);
120                         return;
121                     }
122                 }
123             }
124             init(FacesContext.getCurrentInstance());
125         }
126     }
127     
128     protected boolean isUseMyFacesErrorHandling()
129     {
130         return _useMyFacesErrorHandling;
131     }
132     
133     protected boolean isErrorPagePresent()
134     {
135         return _isErrorPagePresent;
136     }
137     
138     /**
139      * {@inheritDoc}
140      */
141     @Override
142     public ExceptionQueuedEvent getHandledExceptionQueuedEvent()
143     {
144         init();
145         if (!isUseMyFacesErrorHandling())
146         {
147             return super.getHandledExceptionQueuedEvent();
148         }
149         else
150         {
151             return handledAndThrown;
152         }
153     }
154 
155     /**
156      * {@inheritDoc}
157      */
158     @Override
159     public Iterable<ExceptionQueuedEvent> getHandledExceptionQueuedEvents()
160     {
161         init();
162         if (!isUseMyFacesErrorHandling())
163         {
164             return super.getHandledExceptionQueuedEvents();
165         }
166         else
167         {
168             return handled == null ? Collections.<ExceptionQueuedEvent>emptyList() : handled;
169         }
170     }
171 
172     /**
173      * {@inheritDoc}
174      */
175     @Override
176     public Iterable<ExceptionQueuedEvent> getUnhandledExceptionQueuedEvents()
177     {
178         init();
179         if (!isUseMyFacesErrorHandling())
180         {
181             return super.getUnhandledExceptionQueuedEvents();
182         }
183         else
184         {
185             return unhandled == null ? Collections.<ExceptionQueuedEvent>emptyList() : unhandled;
186         }
187     }
188 
189     /**
190      * {@inheritDoc}
191      */
192     @Override
193     public void handle() throws FacesException
194     {
195         init();
196         if (!isUseMyFacesErrorHandling())
197         {
198             if (isErrorPagePresent())
199             {
200                 FacesContext facesContext = FacesContext.getCurrentInstance();
201                 // save current view in the request map to access it on the error page
202                 facesContext.getExternalContext().getRequestMap().put(ErrorPageWriter.VIEW_KEY,
203                                                                       facesContext.getViewRoot());
204             }
205             try
206             {
207                 super.handle();
208             }
209             catch (FacesException e)
210             {
211                 FacesContext facesContext = FacesContext.getCurrentInstance();
212                 if (e.getCause() instanceof ViewNotFoundException)
213                 {
214                     facesContext.getExternalContext().setResponseStatus(HttpServletResponse.SC_NOT_FOUND);
215                 }
216                 else
217                 {
218                     facesContext.getExternalContext().setResponseStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
219                 }
220                 throw e;
221             }
222             return;
223         }
224         else
225         {
226             if (unhandled != null && !unhandled.isEmpty())
227             {
228                 if (handled == null)
229                 {
230                     handled = new LinkedList<ExceptionQueuedEvent>();
231                 }
232                 
233                 List<Throwable> throwableList = new ArrayList<Throwable>();
234                 List<UIComponent> components = new ArrayList<UIComponent>();
235                 FacesContext facesContext = null;
236                 
237                 do
238                 {
239                     // For each ExceptionEvent in the list
240                     
241                     // get the event to handle
242                     ExceptionQueuedEvent event = unhandled.peek();
243                     try
244                     {
245                         // call its getContext() method
246                         ExceptionQueuedEventContext context = event.getContext();
247     
248                         if (facesContext == null)
249                         {
250                             facesContext = event.getContext().getContext();
251                         }
252                         
253                         // and call getException() on the returned result
254                         Throwable exception = context.getException();
255                         
256                         // Upon encountering the first such Exception that is not an instance of
257                         // javax.faces.event.AbortProcessingException
258                         if (!shouldSkip(exception))
259                         {
260                             // set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event
261                             handledAndThrown = event;
262                             
263                             Throwable rootCause = getRootCause(exception);
264                             
265                             throwableList.add(rootCause == null ? exception : rootCause);
266                             components.add(event.getContext().getComponent());
267                             
268                             //break;
269                         }
270                         else
271                         {
272                             // Testing mojarra it logs a message and the exception
273                             // however, this behaviour is not mentioned in the spec
274                             log.log(Level.SEVERE, exception.getClass().getName() + " occured while processing " +
275                                     (context.inBeforePhase() ? "beforePhase() of " : 
276                                             (context.inAfterPhase() ? "afterPhase() of " : "")) + 
277                                     "phase " + context.getPhaseId() + ": " +
278                                     "UIComponent-ClientId=" + 
279                                     (context.getComponent() != null ? 
280                                             context.getComponent().getClientId(context.getContext()) : "") + ", " +
281                                     "Message=" + exception.getMessage());
282                             
283                             log.log(Level.SEVERE, exception.getMessage(), exception);
284                         }
285                     }
286                     finally
287                     {
288                         // if we will throw the Exception or if we just logged it,
289                         // we handled it in either way --> add to handled
290                         handled.add(event);
291                         unhandled.remove(event);
292                     }
293                 } while (!unhandled.isEmpty());
294 
295                 if (facesContext == null)
296                 {
297                     facesContext = FacesContext.getCurrentInstance();
298                 }
299                 if (throwableList.size() == 1)
300                 {
301                     ErrorPageWriter.handle(facesContext, components, throwableList.get(0));
302                 }
303                 else if (throwableList.size() > 1)
304                 {
305                     ErrorPageWriter.handle(facesContext, components,
306                                            throwableList.toArray(new Throwable[throwableList.size()]));
307                 }
308             }
309         }
310     }
311 
312     /**
313      * {@inheritDoc}
314      */
315     @Override
316     public void processEvent(SystemEvent exceptionQueuedEvent) throws AbortProcessingException
317     {
318         init(exceptionQueuedEvent);
319         
320         if (!isUseMyFacesErrorHandling())
321         {
322             super.processEvent(exceptionQueuedEvent);
323         }
324         else
325         {
326             if (unhandled == null)
327             {
328                 unhandled = new LinkedList<ExceptionQueuedEvent>();
329             }
330             
331             unhandled.add((ExceptionQueuedEvent)exceptionQueuedEvent);
332         }
333     }
334 
335     protected Throwable getRethrownException(Throwable exception)
336     {
337         // Let toRethrow be either the result of calling getRootCause() on the Exception, 
338         // or the Exception itself, whichever is non-null
339         Throwable toRethrow = getRootCause(exception);
340         if (toRethrow == null)
341         {
342             toRethrow = exception;
343         }
344         
345         return toRethrow;
346     }
347     
348     protected FacesException wrap(Throwable exception)
349     {
350         if (exception instanceof FacesException)
351         {
352             return (FacesException) exception;
353         }
354         return new FacesException(exception);
355     }
356     
357     protected boolean shouldSkip(Throwable exception)
358     {
359         return exception instanceof AbortProcessingException;
360     }
361     
362     @Override
363     public ExceptionHandler getWrapped()
364     {
365         return _delegate;
366     }
367 }