2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shale.view.faces;
19
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import javax.faces.context.ExternalContext;
26 import javax.faces.context.FacesContext;
27 import javax.faces.el.ValueBinding;
28
29 import javax.faces.event.PhaseEvent;
30 import javax.faces.event.PhaseId;
31 import javax.faces.event.PhaseListener;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.shale.view.AbstractRequestBean;
36 import org.apache.shale.view.ApplicationException;
37 import org.apache.shale.view.Constants;
38 import org.apache.shale.view.ExceptionHandler;
39 import org.apache.shale.view.ViewController;
40
41 /***
42 * <p>{@link ViewPhaseListener} is a JavaServer Faces <code>PhaseListener</code>
43 * that implements phase related functionality for the view controller
44 * portion of Shale.</p>
45 *
46 * $Id: ViewPhaseListener.java 497775 2007-01-19 11:09:07Z matzew $
47 */
48
49 public class ViewPhaseListener implements PhaseListener {
50
51
52
53
54
55 /***
56 * Serial version UID.
57 */
58 private static final long serialVersionUID = 2096731130324222021L;
59
60
61 /***
62 * <p>The <code>Log</code> instance for this class.</p>
63 */
64 private static final Log log = LogFactory.getLog(ViewPhaseListener.class);
65
66
67 /***
68 * <p>HTTP status to report in the exception attributes we set up.</p>
69 */
70 private static final int HTTP_STATUS = 200;
71
72
73 /***
74 * <p>Request scope attribute under which the {@link PhaseId} for the
75 * current phase is exposed.</p>
76 */
77 public static final String PHASE_ID = "org.apache.shale.view.PHASE_ID";
78
79
80
81
82
83 /***
84 * <p>Perform after-phase processing for the phase defined in the
85 * specified event.</p>
86 *
87 * @param event <code>PhaseEvent</code> for the current event
88 */
89 public void afterPhase(PhaseEvent event) {
90
91 if (log.isTraceEnabled()) {
92 log.trace("afterPhase(" + event.getFacesContext()
93 + "," + event.getPhaseId() + ")");
94 }
95
96 if (afterPhaseExceptionCheck(event)) {
97
98 return;
99 }
100
101 PhaseId phaseId = event.getPhaseId();
102 if (PhaseId.RESTORE_VIEW.equals(phaseId)) {
103 afterRestoreView(event);
104 } else if (PhaseId.RENDER_RESPONSE.equals(phaseId)
105 || event.getFacesContext().getResponseComplete()) {
106 afterRenderResponse(event);
107 }
108 event.getFacesContext().getExternalContext().getRequestMap().remove(PHASE_ID);
109
110 }
111
112
113 /***
114 * <p>Perform before-phase processing for the phase defined in the
115 * specified event.</p>
116 *
117 * @param event <code>PhaseEvent</code> for the current event
118 */
119 public void beforePhase(PhaseEvent event) {
120
121 if (log.isTraceEnabled()) {
122 log.trace("beforePhase(" + event.getFacesContext()
123 + "," + event.getPhaseId() + ")");
124 }
125 PhaseId phaseId = event.getPhaseId();
126 event.getFacesContext().getExternalContext().getRequestMap().put(PHASE_ID, phaseId);
127 if (PhaseId.RENDER_RESPONSE.equals(phaseId)) {
128 beforeRenderResponse(event);
129 }
130
131 }
132
133
134 /***
135 * <p>Return <code>PhaseId.ANY_PHASE</code>, indicating our interest
136 * in all phases of the request processing lifecycle.</p>
137 */
138 public PhaseId getPhaseId() {
139
140 return PhaseId.ANY_PHASE;
141
142 }
143
144
145
146
147
148 /***
149 * <p>If any exceptions have been accumulated, and the user has specified
150 * a forwarding URL, forward to that URL instead of allowing rendering
151 * to proceed.</p>
152 *
153 * @param event <code>PhaseEvent</code> for the current event
154 * @return <code>true</code> if exceptions have been handled
155 * and dispatched to the specified path
156 */
157 private boolean afterPhaseExceptionCheck(PhaseEvent event) {
158
159
160
161 FacesContext context = event.getFacesContext();
162 ExternalContext econtext = context.getExternalContext();
163 List list = (List)
164 econtext.getRequestMap().get(FacesConstants.EXCEPTIONS_LIST);
165 if (list == null
166 || econtext.getRequestMap().get("javax.servlet.error.exception") != null) {
167 return false;
168 }
169
170
171 String path =
172 econtext.getInitParameter(Constants.EXCEPTION_DISPATCH_PATH);
173 if (path == null) {
174 return false;
175 }
176
177
178
179 try {
180
181
182
183 ApplicationException exception = new ApplicationException(list);
184 Map map = econtext.getRequestMap();
185 map.put("javax.servlet.error.status_code", new Integer(HTTP_STATUS));
186 map.put("javax.servlet.error.exception_type", ApplicationException.class);
187 map.put("javax.servlet.error.message", exception.getMessage());
188 map.put("javax.servlet.error.exception", exception);
189 StringBuffer sb = new StringBuffer("");
190 if (econtext.getRequestServletPath() != null) {
191 sb.append(econtext.getRequestServletPath());
192 }
193 if (econtext.getRequestPathInfo() != null) {
194 sb.append(econtext.getRequestPathInfo());
195 }
196 map.put("javax.servlet.error.request_uri", sb.toString());
197 map.put("javax.servlet.error.servlet_name", "javax.faces.webapp.FacesServlet");
198
199 context.responseComplete();
200
201 econtext.getRequestMap().remove(FacesConstants.VIEWS_INITIALIZED);
202 econtext.dispatch(path);
203
204 } catch (IOException e) {
205 handleException(context, e);
206 }
207
208 return true;
209 }
210
211
212 /***
213 * <p>Remove all request scoped attributes that implement the
214 * <code>ViewController</code> interface or extend the
215 * <code>AbstractRequestBean</code> base class. This will trigger
216 * a callback to the <code>destroy()</code> method of such beans,
217 * while we are still in the context of a JSF request.</p>
218 *
219 * @param event <code>PhaseEvent</code> for the current event
220 */
221 private void afterRenderResponse(PhaseEvent event) {
222
223
224 Map map = event.getFacesContext().getExternalContext().getRequestMap();
225
226 map.remove(FacesConstants.VIEWS_INITIALIZED);
227
228 List list = new ArrayList();
229 Iterator entries = map.entrySet().iterator();
230
231
232
233 while (entries.hasNext()) {
234 Map.Entry entry = (Map.Entry) entries.next();
235 if ((entry.getValue() instanceof ViewController)
236 || (entry.getValue() instanceof AbstractRequestBean)) {
237 list.add(entry.getKey());
238 }
239 }
240
241
242
243 entries = map.entrySet().iterator();
244 while (entries.hasNext()) {
245 Map.Entry entry = (Map.Entry) entries.next();
246 if (!list.contains(entry.getKey())) {
247 list.add(entry.getKey());
248 }
249 }
250
251
252
253 Iterator keys = list.iterator();
254 while (keys.hasNext()) {
255 String key = (String) keys.next();
256 try {
257 map.remove(key);
258 } catch (Exception e) {
259 handleException(event.getFacesContext(), e);
260 }
261 }
262
263 }
264
265
266 /***
267 * <p>Call the <code>preprocess()</code> method of the {@link ViewController}
268 * that has been restored, if this is a postback.</p>
269 *
270 * @param event <code>PhaseEvent</code> for the current event
271 */
272 private void afterRestoreView(PhaseEvent event) {
273
274 Map map = event.getFacesContext().getExternalContext().getRequestMap();
275 List list = (List) map.get(FacesConstants.VIEWS_INITIALIZED);
276 if (list == null) {
277 return;
278 }
279 if (!event.getFacesContext().getExternalContext().getRequestMap().containsKey(FacesConstants.VIEW_POSTBACK)) {
280 return;
281 }
282 Iterator vcs = list.iterator();
283 while (vcs.hasNext()) {
284 Object vc = vcs.next();
285 try {
286 getViewControllerCallbacks(event.getFacesContext()).preprocess(vc);
287 } catch (Exception e) {
288 handleException(event.getFacesContext(), e);
289 }
290 }
291
292 }
293
294
295
296 /***
297 * <p>Call the <code>prerender()</code> method of the {@link ViewController}
298 * for the view about to be rendered (if any).</p>
299 *
300 * @param event <code>PhaseEvent</code> for the current event
301 */
302 private void beforeRenderResponse(PhaseEvent event) {
303
304 Map map = event.getFacesContext().getExternalContext().getRequestMap();
305 String viewName = (String) map.get(FacesConstants.VIEW_NAME_RENDERED);
306 if (viewName == null) {
307 return;
308 }
309 Object vc = map.get(viewName);
310 if (vc == null) {
311 return;
312 }
313 try {
314 getViewControllerCallbacks(event.getFacesContext()).prerender(vc);
315 } catch (Exception e) {
316 handleException(event.getFacesContext(), e);
317 }
318 map.remove(FacesConstants.VIEW_NAME_RENDERED);
319
320 }
321
322
323 /***
324 * <p>Return the {@link ViewControllerCallbacks} instance we
325 * will use.</p>
326 *
327 * @param context <code>FacesContext</code> for the current request
328 */
329 private ViewControllerCallbacks getViewControllerCallbacks(FacesContext context) {
330
331 ValueBinding vb = context.getApplication().createValueBinding
332 ("#{" + FacesConstants.VIEW_CALLBACKS + "}");
333 return (ViewControllerCallbacks) vb.getValue(context);
334
335 }
336
337
338 /***
339 * <p>Handle the specified exception according to the strategy
340 * defined by our current {@link ExceptionHandler}.</p>
341 *
342 * @param context FacesContext for the current request
343 * @param exception Exception to be handled
344 */
345 private void handleException(FacesContext context, Exception exception) {
346
347 if (context == null) {
348 exception.printStackTrace(System.out);
349 return;
350 }
351 ExceptionHandler handler = (ExceptionHandler)
352 context.getApplication().getVariableResolver().resolveVariable
353 (context, Constants.EXCEPTION_HANDLER);
354 handler.handleException(exception);
355
356 }
357
358
359 }