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: 1187700 $ $Date: 2011-10-22 07:19:37 -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 }