Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
ViewPhaseListener |
|
| 4.222222222222222;4.222 |
1 | /* |
|
2 | * Licensed to the Apache Software Foundation (ASF) under one or more |
|
3 | * contributor license agreements. See the NOTICE file distributed with |
|
4 | * this work for additional information regarding copyright ownership. |
|
5 | * The ASF licenses this file to you under the Apache License, Version 2.0 |
|
6 | * (the "License"); you may not use this file except in compliance with |
|
7 | * the License. You may obtain a copy of the License at |
|
8 | * |
|
9 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
10 | * |
|
11 | * Unless required by applicable law or agreed to in writing, software |
|
12 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 | * See the License for the specific language governing permissions and |
|
15 | * limitations under the License. |
|
16 | */ |
|
17 | ||
18 | package org.apache.shale.view.faces; |
|
19 | ||
20 | import java.io.IOException; |
|
21 | import java.util.Iterator; |
|
22 | import java.util.List; |
|
23 | import java.util.Map; |
|
24 | import javax.faces.context.ExternalContext; |
|
25 | import javax.faces.context.FacesContext; |
|
26 | import javax.faces.el.ValueBinding; |
|
27 | ||
28 | import javax.faces.event.PhaseEvent; |
|
29 | import javax.faces.event.PhaseId; |
|
30 | import javax.faces.event.PhaseListener; |
|
31 | ||
32 | import org.apache.commons.logging.Log; |
|
33 | import org.apache.commons.logging.LogFactory; |
|
34 | import org.apache.shale.view.ApplicationException; |
|
35 | import org.apache.shale.view.Constants; |
|
36 | import org.apache.shale.view.ExceptionHandler; |
|
37 | import org.apache.shale.view.ViewController; |
|
38 | ||
39 | /** |
|
40 | * <p>{@link ViewPhaseListener} is a JavaServer Faces <code>PhaseListener</code> |
|
41 | * that implements phase related functionality for the view controller |
|
42 | * portion of Shale.</p> |
|
43 | * |
|
44 | * $Id: ViewPhaseListener.java 477032 2006-11-20 04:17:39Z gvanmatre $ |
|
45 | */ |
|
46 | ||
47 | 0 | public class ViewPhaseListener implements PhaseListener { |
48 | ||
49 | ||
50 | // -------------------------------------------------------- Static Variables |
|
51 | ||
52 | ||
53 | /** |
|
54 | * Serial version UID. |
|
55 | */ |
|
56 | private static final long serialVersionUID = 2096731130324222021L; |
|
57 | ||
58 | ||
59 | /** |
|
60 | * <p>The <code>Log</code> instance for this class.</p> |
|
61 | */ |
|
62 | 0 | private static final Log log = LogFactory.getLog(ViewPhaseListener.class); |
63 | ||
64 | ||
65 | /** |
|
66 | * <p>HTTP status to report in the exception attributes we set up.</p> |
|
67 | */ |
|
68 | private static final int HTTP_STATUS = 200; |
|
69 | ||
70 | ||
71 | /** |
|
72 | * <p>Request scope attribute under which the {@link PhaseId} for the |
|
73 | * current phase is exposed.</p> |
|
74 | */ |
|
75 | public static final String PHASE_ID = "org.apache.shale.view.PHASE_ID"; |
|
76 | ||
77 | ||
78 | // --------------------------------------------------- PhaseListener Methods |
|
79 | ||
80 | ||
81 | /** |
|
82 | * <p>Perform after-phase processing for the phase defined in the |
|
83 | * specified event.</p> |
|
84 | * |
|
85 | * @param event <code>PhaseEvent</code> for the current event |
|
86 | */ |
|
87 | public void afterPhase(PhaseEvent event) { |
|
88 | ||
89 | 0 | if (log.isTraceEnabled()) { |
90 | 0 | log.trace("afterPhase(" + event.getFacesContext() |
91 | + "," + event.getPhaseId() + ")"); |
|
92 | } |
|
93 | ||
94 | 0 | if (afterPhaseExceptionCheck(event)) { |
95 | // dispatched to the target error page, stop further processing |
|
96 | 0 | return; |
97 | } |
|
98 | ||
99 | 0 | PhaseId phaseId = event.getPhaseId(); |
100 | 0 | if (PhaseId.RESTORE_VIEW.equals(phaseId)) { |
101 | 0 | afterRestoreView(event); |
102 | 0 | } else if (PhaseId.RENDER_RESPONSE.equals(phaseId) |
103 | || event.getFacesContext().getResponseComplete()) { |
|
104 | 0 | afterRenderResponse(event); |
105 | } |
|
106 | 0 | event.getFacesContext().getExternalContext().getRequestMap().remove(PHASE_ID); |
107 | ||
108 | 0 | } |
109 | ||
110 | ||
111 | /** |
|
112 | * <p>Perform before-phase processing for the phase defined in the |
|
113 | * specified event.</p> |
|
114 | * |
|
115 | * @param event <code>PhaseEvent</code> for the current event |
|
116 | */ |
|
117 | public void beforePhase(PhaseEvent event) { |
|
118 | ||
119 | 0 | if (log.isTraceEnabled()) { |
120 | 0 | log.trace("beforePhase(" + event.getFacesContext() |
121 | + "," + event.getPhaseId() + ")"); |
|
122 | } |
|
123 | 0 | PhaseId phaseId = event.getPhaseId(); |
124 | 0 | event.getFacesContext().getExternalContext().getRequestMap().put(PHASE_ID, phaseId); |
125 | 0 | if (PhaseId.RENDER_RESPONSE.equals(phaseId)) { |
126 | 0 | beforeRenderResponse(event); |
127 | } |
|
128 | ||
129 | 0 | } |
130 | ||
131 | ||
132 | /** |
|
133 | * <p>Return <code>PhaseId.ANY_PHASE</code>, indicating our interest |
|
134 | * in all phases of the request processing lifecycle.</p> |
|
135 | */ |
|
136 | public PhaseId getPhaseId() { |
|
137 | ||
138 | 0 | return PhaseId.ANY_PHASE; |
139 | ||
140 | } |
|
141 | ||
142 | ||
143 | // --------------------------------------------------------- Private Methods |
|
144 | ||
145 | ||
146 | /** |
|
147 | * <p>If any exceptions have been accumulated, and the user has specified |
|
148 | * a forwarding URL, forward to that URL instead of allowing rendering |
|
149 | * to proceed.</p> |
|
150 | * |
|
151 | * @param event <code>PhaseEvent</code> for the current event |
|
152 | * @return <code>true</code> if exceptions have been handled |
|
153 | * and dispatched to the specified path |
|
154 | */ |
|
155 | private boolean afterPhaseExceptionCheck(PhaseEvent event) { |
|
156 | ||
157 | // Have we accumulated any exceptions during the current request? |
|
158 | // Have we already logged the exception? |
|
159 | 0 | FacesContext context = event.getFacesContext(); |
160 | 0 | ExternalContext econtext = context.getExternalContext(); |
161 | 0 | List list = (List) |
162 | econtext.getRequestMap().get(FacesConstants.EXCEPTIONS_LIST); |
|
163 | 0 | if (list == null |
164 | || econtext.getRequestMap().get("javax.servlet.error.exception") != null) { |
|
165 | 0 | return false; |
166 | } |
|
167 | ||
168 | // Has the user specified a forwarding URL for handling exceptions? |
|
169 | 0 | String path = |
170 | econtext.getInitParameter(Constants.EXCEPTION_DISPATCH_PATH); |
|
171 | 0 | if (path == null) { |
172 | 0 | return false; |
173 | } |
|
174 | ||
175 | // Forward control to the specified path instead of allowing |
|
176 | // rendering to complete, while simulating container error handling |
|
177 | try { |
|
178 | // Set up request attributes reflecting the error conditions, |
|
179 | // similar to what is passed to an error handler by the servlet |
|
180 | // container (see Section 9.9.1 of the Servlet Specification) |
|
181 | 0 | ApplicationException exception = new ApplicationException(list); |
182 | 0 | Map map = econtext.getRequestMap(); |
183 | 0 | map.put("javax.servlet.error.status_code", new Integer(HTTP_STATUS)); // Not an HTTP error |
184 | 0 | map.put("javax.servlet.error.exception_type", ApplicationException.class); |
185 | 0 | map.put("javax.servlet.error.message", exception.getMessage()); |
186 | 0 | map.put("javax.servlet.error.exception", exception); |
187 | 0 | StringBuffer sb = new StringBuffer(""); |
188 | 0 | if (econtext.getRequestServletPath() != null) { |
189 | 0 | sb.append(econtext.getRequestServletPath()); |
190 | } |
|
191 | 0 | if (econtext.getRequestPathInfo() != null) { |
192 | 0 | sb.append(econtext.getRequestPathInfo()); |
193 | } |
|
194 | 0 | map.put("javax.servlet.error.request_uri", sb.toString()); |
195 | 0 | map.put("javax.servlet.error.servlet_name", "javax.faces.webapp.FacesServlet"); // Best we can do ... |
196 | // Dispatch to the specified error handler |
|
197 | 0 | context.responseComplete(); |
198 | // force a destroy before dispatching to the error page |
|
199 | 0 | econtext.getRequestMap().remove(FacesConstants.VIEWS_INITIALIZED); |
200 | 0 | econtext.dispatch(path); |
201 | ||
202 | 0 | } catch (IOException e) { |
203 | 0 | handleException(context, e); |
204 | 0 | } |
205 | ||
206 | 0 | return true; |
207 | } |
|
208 | ||
209 | ||
210 | /** |
|
211 | * <p>Call the <code>destroy()</code> method of the {@link ViewController}s |
|
212 | * that have been registered during this request (if any).</p> |
|
213 | * |
|
214 | * @param event <code>PhaseEvent</code> for the current event |
|
215 | */ |
|
216 | private void afterRenderResponse(PhaseEvent event) { |
|
217 | ||
218 | 0 | Map map = event.getFacesContext().getExternalContext().getRequestMap(); |
219 | 0 | List list = (List) map.get(FacesConstants.VIEWS_INITIALIZED); |
220 | 0 | if (list == null) { |
221 | 0 | return; |
222 | } |
|
223 | //Iterator vcs = list.iterator(); |
|
224 | //while (vcs.hasNext()) { |
|
225 | // Object vc = vcs.next(); |
|
226 | // viewControllerCallbacks(event.getFacesContext()).destroy(vc); |
|
227 | //} |
|
228 | ||
229 | 0 | map.remove(FacesConstants.VIEWS_INITIALIZED); |
230 | ||
231 | 0 | } |
232 | ||
233 | ||
234 | /** |
|
235 | * <p>Call the <code>preprocess()</code> method of the {@link ViewController} |
|
236 | * that has been restored, if this is a postback.</p> |
|
237 | * |
|
238 | * @param event <code>PhaseEvent</code> for the current event |
|
239 | */ |
|
240 | private void afterRestoreView(PhaseEvent event) { |
|
241 | ||
242 | 0 | Map map = event.getFacesContext().getExternalContext().getRequestMap(); |
243 | 0 | List list = (List) map.get(FacesConstants.VIEWS_INITIALIZED); |
244 | 0 | if (list == null) { |
245 | 0 | return; |
246 | } |
|
247 | 0 | if (!event.getFacesContext().getExternalContext().getRequestMap().containsKey(FacesConstants.VIEW_POSTBACK)) { |
248 | 0 | return; |
249 | } |
|
250 | 0 | Iterator vcs = list.iterator(); |
251 | 0 | while (vcs.hasNext()) { |
252 | 0 | Object vc = vcs.next(); |
253 | try { |
|
254 | 0 | getViewControllerCallbacks(event.getFacesContext()).preprocess(vc); |
255 | 0 | } catch (Exception e) { |
256 | 0 | handleException(event.getFacesContext(), e); |
257 | 0 | } |
258 | 0 | } |
259 | ||
260 | 0 | } |
261 | ||
262 | ||
263 | ||
264 | /** |
|
265 | * <p>Call the <code>prerender()</code> method of the {@link ViewController} |
|
266 | * for the view about to be rendered (if any).</p> |
|
267 | * |
|
268 | * @param event <code>PhaseEvent</code> for the current event |
|
269 | */ |
|
270 | private void beforeRenderResponse(PhaseEvent event) { |
|
271 | ||
272 | 0 | Map map = event.getFacesContext().getExternalContext().getRequestMap(); |
273 | 0 | String viewName = (String) map.get(FacesConstants.VIEW_NAME_RENDERED); |
274 | 0 | if (viewName == null) { |
275 | 0 | return; |
276 | } |
|
277 | 0 | Object vc = map.get(viewName); |
278 | 0 | if (vc == null) { |
279 | 0 | return; |
280 | } |
|
281 | try { |
|
282 | 0 | getViewControllerCallbacks(event.getFacesContext()).prerender(vc); |
283 | 0 | } catch (Exception e) { |
284 | 0 | handleException(event.getFacesContext(), e); |
285 | 0 | } |
286 | 0 | map.remove(FacesConstants.VIEW_NAME_RENDERED); |
287 | ||
288 | 0 | } |
289 | ||
290 | ||
291 | /** |
|
292 | * <p>Return the {@link ViewControllerCallbacks} instance we |
|
293 | * will use.</p> |
|
294 | * |
|
295 | * @param context <code>FacesContext</code> for the current request |
|
296 | */ |
|
297 | private ViewControllerCallbacks getViewControllerCallbacks(FacesContext context) { |
|
298 | ||
299 | 0 | ValueBinding vb = context.getApplication().createValueBinding |
300 | ("#{" + FacesConstants.VIEW_CALLBACKS + "}"); |
|
301 | 0 | return (ViewControllerCallbacks) vb.getValue(context); |
302 | ||
303 | } |
|
304 | ||
305 | ||
306 | /** |
|
307 | * <p>Handle the specified exception according to the strategy |
|
308 | * defined by our current {@link ExceptionHandler}.</p> |
|
309 | * |
|
310 | * @param context FacesContext for the current request |
|
311 | * @param exception Exception to be handled |
|
312 | */ |
|
313 | private void handleException(FacesContext context, Exception exception) { |
|
314 | ||
315 | 0 | if (context == null) { |
316 | 0 | exception.printStackTrace(System.out); |
317 | 0 | return; |
318 | } |
|
319 | 0 | ExceptionHandler handler = (ExceptionHandler) |
320 | context.getApplication().getVariableResolver().resolveVariable |
|
321 | (context, Constants.EXCEPTION_HANDLER); |
|
322 | 0 | handler.handleException(exception); |
323 | ||
324 | 0 | } |
325 | ||
326 | ||
327 | } |