Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
PreJsf2ExceptionHandlerFactory |
|
| 3.1538461538461537;3.154 | ||||
PreJsf2ExceptionHandlerFactory$PreJsf2ExceptionHandlerImpl |
|
| 3.1538461538461537;3.154 |
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 javax.faces.webapp; | |
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.application.FacesMessage; | |
30 | import javax.faces.component.UIComponent; | |
31 | import javax.faces.component.UpdateModelException; | |
32 | import javax.faces.context.ExceptionHandler; | |
33 | import javax.faces.context.ExceptionHandlerFactory; | |
34 | import javax.faces.event.AbortProcessingException; | |
35 | import javax.faces.event.ExceptionQueuedEvent; | |
36 | import javax.faces.event.ExceptionQueuedEventContext; | |
37 | import javax.faces.event.SystemEvent; | |
38 | ||
39 | import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam; | |
40 | ||
41 | /** | |
42 | * @since 2.0 | |
43 | */ | |
44 | public class PreJsf2ExceptionHandlerFactory extends ExceptionHandlerFactory | |
45 | { | |
46 | ||
47 | public PreJsf2ExceptionHandlerFactory() | |
48 | 0 | { |
49 | 0 | } |
50 | ||
51 | /** | |
52 | * {@inheritDoc} | |
53 | */ | |
54 | @Override | |
55 | public ExceptionHandler getExceptionHandler() | |
56 | { | |
57 | 0 | return new PreJsf2ExceptionHandlerImpl(); |
58 | } | |
59 | ||
60 | private static class PreJsf2ExceptionHandlerImpl extends ExceptionHandler | |
61 | { | |
62 | /* | |
63 | * PLEASE NOTE!!! | |
64 | * This is a copy of the ExceptionHandler implementation of myfaces-impl | |
65 | * (org.apache.myfaces.context.ExceptionHandlerImpl), only handle() differs a bit. | |
66 | * Any changes made here should also be applied to ExceptionHandlerImpl in the right way. | |
67 | * | |
68 | * This is really ugly, but I think we have to do this due to the fact that PreJsf2ExceptionHandlerFactory | |
69 | * can be declared directly as an exception handler factory, so it's not as if we can make the methods | |
70 | * in the factory abstract and have a concrete impl in the impl project (and therefore be able to | |
71 | * extend ExceptionHandlerImpl). If this is not the case, please change accordingly. | |
72 | */ | |
73 | ||
74 | 0 | private static final Logger log = Logger.getLogger(PreJsf2ExceptionHandlerImpl.class.getName()); |
75 | ||
76 | /** | |
77 | * Since JSF 2.0 there is a standard way to deal with unexpected Exceptions: the ExceptionHandler. | |
78 | * Due to backwards compatibility MyFaces 2.0 also supports the init parameter | |
79 | * org.apache.myfaces.ERROR_HANDLER, introduced in MyFaces 1.2.4. However, the given error handler | |
80 | * now only needs to include the following method: | |
81 | * <ul> | |
82 | * <li>handleException(FacesContext fc, Exception ex)</li> | |
83 | * </ul> | |
84 | * Furthermore, the init parameter only works when using the PreJsf2ExceptionHandlerFactory. | |
85 | * | |
86 | * @deprecated | |
87 | */ | |
88 | @Deprecated | |
89 | @JSFWebConfigParam(since="1.2.4",desc="Deprecated: use JSF 2.0 ExceptionHandler", deprecated=true) | |
90 | private static final String ERROR_HANDLER_PARAMETER = "org.apache.myfaces.ERROR_HANDLER"; | |
91 | ||
92 | private Queue<ExceptionQueuedEvent> handled; | |
93 | private Queue<ExceptionQueuedEvent> unhandled; | |
94 | private ExceptionQueuedEvent handledAndThrown; | |
95 | ||
96 | public PreJsf2ExceptionHandlerImpl() | |
97 | 0 | { |
98 | 0 | } |
99 | ||
100 | /** | |
101 | * {@inheritDoc} | |
102 | */ | |
103 | @Override | |
104 | public ExceptionQueuedEvent getHandledExceptionQueuedEvent() | |
105 | { | |
106 | 0 | return handledAndThrown; |
107 | } | |
108 | ||
109 | /** | |
110 | * {@inheritDoc} | |
111 | */ | |
112 | @Override | |
113 | public Iterable<ExceptionQueuedEvent> getHandledExceptionQueuedEvents() | |
114 | { | |
115 | 0 | return handled == null ? Collections.<ExceptionQueuedEvent>emptyList() : handled; |
116 | } | |
117 | ||
118 | /** | |
119 | * {@inheritDoc} | |
120 | */ | |
121 | @Override | |
122 | public Throwable getRootCause(Throwable t) | |
123 | { | |
124 | 0 | if (t == null) |
125 | { | |
126 | 0 | throw new NullPointerException("t"); |
127 | } | |
128 | ||
129 | 0 | while (t != null) |
130 | { | |
131 | 0 | Class<?> clazz = t.getClass(); |
132 | 0 | if (!clazz.equals(FacesException.class) && !clazz.equals(ELException.class)) |
133 | { | |
134 | 0 | return t; |
135 | } | |
136 | ||
137 | 0 | t = t.getCause(); |
138 | 0 | } |
139 | ||
140 | 0 | return null; |
141 | } | |
142 | ||
143 | /** | |
144 | * {@inheritDoc} | |
145 | */ | |
146 | @Override | |
147 | public Iterable<ExceptionQueuedEvent> getUnhandledExceptionQueuedEvents() | |
148 | { | |
149 | 0 | return unhandled == null ? Collections.<ExceptionQueuedEvent>emptyList() : unhandled; |
150 | } | |
151 | ||
152 | /** | |
153 | * {@inheritDoc} | |
154 | * | |
155 | * Differs from ExceptionHandlerImpl.handle() in three points: | |
156 | * - Any exceptions thrown before or after phase execution will be logged and swallowed. | |
157 | * - If the Exception is an instance of UpdateModelException, extract the | |
158 | * FacesMessage from the UpdateModelException. | |
159 | * Log a SEVERE message to the log and queue the FacesMessage on the | |
160 | * FacesContext, using the clientId of the source | |
161 | * component in a call to FacesContext.addMessage(java.lang.String, javax.faces.application.FacesMessage). | |
162 | * - Checks org.apache.myfaces.ERROR_HANDLER for backwards compatibility to myfaces-1.2's error handling | |
163 | */ | |
164 | @Override | |
165 | public void handle() throws FacesException | |
166 | { | |
167 | 0 | if (unhandled != null && !unhandled.isEmpty()) |
168 | { | |
169 | 0 | if (handled == null) |
170 | { | |
171 | 0 | handled = new LinkedList<ExceptionQueuedEvent>(); |
172 | } | |
173 | ||
174 | 0 | FacesException toThrow = null; |
175 | ||
176 | do | |
177 | { | |
178 | // For each ExceptionEvent in the list | |
179 | ||
180 | // get the event to handle | |
181 | 0 | ExceptionQueuedEvent event = unhandled.peek(); |
182 | try | |
183 | { | |
184 | // call its getContext() method | |
185 | 0 | ExceptionQueuedEventContext context = event.getContext(); |
186 | ||
187 | // and call getException() on the returned result | |
188 | 0 | Throwable exception = context.getException(); |
189 | ||
190 | // spec described behaviour of PreJsf2ExceptionHandler | |
191 | ||
192 | // UpdateModelException needs special treatment here | |
193 | 0 | if (exception instanceof UpdateModelException) |
194 | { | |
195 | 0 | FacesMessage message = ((UpdateModelException) exception).getFacesMessage(); |
196 | // Log a SEVERE message to the log | |
197 | 0 | log.log(Level.SEVERE, message.getSummary(), exception.getCause()); |
198 | // queue the FacesMessage on the FacesContext | |
199 | 0 | UIComponent component = context.getComponent(); |
200 | 0 | String clientId = null; |
201 | 0 | if (component != null) |
202 | { | |
203 | 0 | clientId = component.getClientId(context.getContext()); |
204 | } | |
205 | 0 | context.getContext().addMessage(clientId, message); |
206 | 0 | } |
207 | 0 | else if (!shouldSkip(exception) && !context.inBeforePhase() && !context.inAfterPhase()) |
208 | { | |
209 | // set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event | |
210 | 0 | handledAndThrown = event; |
211 | ||
212 | // Re-wrap toThrow in a ServletException or | |
213 | // (PortletException, if in a portlet environment) | |
214 | // and throw it | |
215 | // FIXME: The spec says to NOT use a FacesException | |
216 | // to propagate the exception, but I see | |
217 | // no other way as ServletException is not a RuntimeException | |
218 | 0 | toThrow = wrap(getRethrownException(exception)); |
219 | break; | |
220 | } | |
221 | else | |
222 | { | |
223 | // Testing mojarra it logs a message and the exception | |
224 | // however, this behaviour is not mentioned in the spec | |
225 | 0 | log.log(Level.SEVERE, exception.getClass().getName() + " occured while processing " + |
226 | (context.inBeforePhase() ? "beforePhase() of " : | |
227 | (context.inAfterPhase() ? "afterPhase() of " : "")) + | |
228 | "phase " + context.getPhaseId() + ": " + | |
229 | "UIComponent-ClientId=" + | |
230 | (context.getComponent() != null ? | |
231 | context.getComponent().getClientId(context.getContext()) : "") + ", " + | |
232 | "Message=" + exception.getMessage()); | |
233 | ||
234 | 0 | log.log(Level.SEVERE, exception.getMessage(), exception); |
235 | } | |
236 | } | |
237 | 0 | catch (Throwable t) |
238 | { | |
239 | // A FacesException must be thrown if a problem occurs while performing | |
240 | // the algorithm to handle the exception | |
241 | 0 | throw new FacesException("Could not perform the algorithm to handle the Exception", t); |
242 | } | |
243 | finally | |
244 | { | |
245 | // if we will throw the Exception or if we just logged it, | |
246 | // we handled it in either way --> add to handled | |
247 | 0 | handled.add(event); |
248 | 0 | unhandled.remove(event); |
249 | 0 | } |
250 | 0 | } while (!unhandled.isEmpty()); |
251 | ||
252 | // do we have to throw an Exception? | |
253 | 0 | if (toThrow != null) |
254 | { | |
255 | 0 | throw toThrow; |
256 | } | |
257 | } | |
258 | 0 | } |
259 | ||
260 | /** | |
261 | * {@inheritDoc} | |
262 | */ | |
263 | @Override | |
264 | public boolean isListenerForSource(Object source) | |
265 | { | |
266 | 0 | return source instanceof ExceptionQueuedEventContext; |
267 | } | |
268 | ||
269 | /** | |
270 | * {@inheritDoc} | |
271 | */ | |
272 | @Override | |
273 | public void processEvent(SystemEvent exceptionQueuedEvent) throws AbortProcessingException | |
274 | { | |
275 | 0 | if (unhandled == null) |
276 | { | |
277 | 0 | unhandled = new LinkedList<ExceptionQueuedEvent>(); |
278 | } | |
279 | ||
280 | 0 | unhandled.add((ExceptionQueuedEvent)exceptionQueuedEvent); |
281 | 0 | } |
282 | ||
283 | protected Throwable getRethrownException(Throwable exception) | |
284 | { | |
285 | // Let toRethrow be either the result of calling getRootCause() on the Exception, | |
286 | // or the Exception itself, whichever is non-null | |
287 | 0 | Throwable toRethrow = getRootCause(exception); |
288 | 0 | if (toRethrow == null) |
289 | { | |
290 | 0 | toRethrow = exception; |
291 | } | |
292 | ||
293 | 0 | return toRethrow; |
294 | } | |
295 | ||
296 | protected FacesException wrap(Throwable exception) | |
297 | { | |
298 | 0 | if (exception instanceof FacesException) |
299 | { | |
300 | 0 | return (FacesException) exception; |
301 | } | |
302 | 0 | return new FacesException(exception); |
303 | } | |
304 | ||
305 | protected boolean shouldSkip(Throwable exception) | |
306 | { | |
307 | 0 | return exception instanceof AbortProcessingException; |
308 | } | |
309 | } | |
310 | } |