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) ? true : false);
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) ? true : false);
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, facesContext.getViewRoot());
203             }
204             try
205             {
206                 super.handle();
207             }
208             catch (FacesException e)
209             {
210                 FacesContext facesContext = FacesContext.getCurrentInstance();
211                 if (e.getCause() instanceof ViewNotFoundException)
212                 {
213                     facesContext.getExternalContext().setResponseStatus(HttpServletResponse.SC_NOT_FOUND);
214                 }
215                 else
216                 {
217                     facesContext.getExternalContext().setResponseStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
218                 }
219                 throw e;
220             }
221             return;
222         }
223         else
224         {
225             if (unhandled != null && !unhandled.isEmpty())
226             {
227                 if (handled == null)
228                 {
229                     handled = new LinkedList<ExceptionQueuedEvent>();
230                 }
231                 
232                 List<Throwable> throwableList = new ArrayList<Throwable>();
233                 List<UIComponent> components = new ArrayList<UIComponent>();
234                 FacesContext facesContext = null;
235                 
236                 do
237                 {
238                     // For each ExceptionEvent in the list
239                     
240                     // get the event to handle
241                     ExceptionQueuedEvent event = unhandled.peek();
242                     try
243                     {
244                         // call its getContext() method
245                         ExceptionQueuedEventContext context = event.getContext();
246     
247                         if (facesContext == null)
248                         {
249                             facesContext = event.getContext().getContext();
250                         }
251                         
252                         // and call getException() on the returned result
253                         Throwable exception = context.getException();
254                         
255                         // Upon encountering the first such Exception that is not an instance of
256                         // javax.faces.event.AbortProcessingException
257                         if (!shouldSkip(exception))
258                         {
259                             // set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event
260                             handledAndThrown = event;
261                             
262                             Throwable rootCause = getRootCause(exception);
263                             
264                             throwableList.add(rootCause == null ? exception : rootCause);
265                             components.add(event.getContext().getComponent());
266                             
267                             //break;
268                         }
269                         else
270                         {
271                             // Testing mojarra it logs a message and the exception
272                             // however, this behaviour is not mentioned in the spec
273                             log.log(Level.SEVERE, exception.getClass().getName() + " occured while processing " +
274                                     (context.inBeforePhase() ? "beforePhase() of " : 
275                                             (context.inAfterPhase() ? "afterPhase() of " : "")) + 
276                                     "phase " + context.getPhaseId() + ": " +
277                                     "UIComponent-ClientId=" + 
278                                     (context.getComponent() != null ? 
279                                             context.getComponent().getClientId(context.getContext()) : "") + ", " +
280                                     "Message=" + exception.getMessage());
281                             
282                             log.log(Level.SEVERE, exception.getMessage(), exception);
283                         }
284                     }
285                     finally
286                     {
287                         // if we will throw the Exception or if we just logged it,
288                         // we handled it in either way --> add to handled
289                         handled.add(event);
290                         unhandled.remove(event);
291                     }
292                 } while (!unhandled.isEmpty());
293 
294                 if (facesContext == null)
295                 {
296                     facesContext = FacesContext.getCurrentInstance();
297                 }
298                 if (throwableList.size() == 1)
299                 {
300                     ErrorPageWriter.handle(facesContext, components, throwableList.get(0));
301                 }
302                 else if (throwableList.size() > 1)
303                 {
304                     ErrorPageWriter.handle(facesContext, components, throwableList.toArray(new Throwable[throwableList.size()]));
305                 }
306             }
307         }
308     }
309 
310     /**
311      * {@inheritDoc}
312      */
313     @Override
314     public void processEvent(SystemEvent exceptionQueuedEvent) throws AbortProcessingException
315     {
316         init(exceptionQueuedEvent);
317         
318         if (!isUseMyFacesErrorHandling())
319         {
320             super.processEvent(exceptionQueuedEvent);
321         }
322         else
323         {
324             if (unhandled == null)
325             {
326                 unhandled = new LinkedList<ExceptionQueuedEvent>();
327             }
328             
329             unhandled.add((ExceptionQueuedEvent)exceptionQueuedEvent);
330         }
331     }
332 
333     protected Throwable getRethrownException(Throwable exception)
334     {
335         // Let toRethrow be either the result of calling getRootCause() on the Exception, 
336         // or the Exception itself, whichever is non-null
337         Throwable toRethrow = getRootCause(exception);
338         if (toRethrow == null)
339         {
340             toRethrow = exception;
341         }
342         
343         return toRethrow;
344     }
345     
346     protected FacesException wrap(Throwable exception)
347     {
348         if (exception instanceof FacesException)
349         {
350             return (FacesException) exception;
351         }
352         return new FacesException(exception);
353     }
354     
355     protected boolean shouldSkip(Throwable exception)
356     {
357         return exception instanceof AbortProcessingException;
358     }
359     
360     @Override
361     public ExceptionHandler getWrapped()
362     {
363         return _delegate;
364     }
365 }