Source code: org/apache/myfaces/lifecycle/LifecycleImpl.java
1 /*
2 * Copyright 2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.apache.myfaces.lifecycle;
17
18 import org.apache.myfaces.util.DebugUtils;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22
23 import javax.faces.FacesException;
24 import javax.faces.application.Application;
25 import javax.faces.application.ViewHandler;
26 import javax.faces.component.UIComponent;
27 import javax.faces.component.UIInput;
28 import javax.faces.component.UIViewRoot;
29 import javax.faces.context.ExternalContext;
30 import javax.faces.context.FacesContext;
31 import javax.faces.el.ValueBinding;
32 import javax.faces.event.PhaseEvent;
33 import javax.faces.event.PhaseId;
34 import javax.faces.event.PhaseListener;
35 import javax.faces.lifecycle.Lifecycle;
36 import java.io.IOException;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Iterator;
40 import java.util.List;
41 import javax.portlet.PortletRequest;
42 import org.apache.myfaces.portlet.MyFacesGenericPortlet;
43 import org.apache.myfaces.portlet.PortletUtil;
44
45 /**
46 * Implements the lifecycle as described in Spec. 1.0 PFD Chapter 2
47 * @author Manfred Geiler (latest modification by $Author: bdudney $)
48 * @version $Revision: 294841 $ $Date: 2005-10-04 13:13:34 -0400 (Tue, 04 Oct 2005) $
49 */
50 public class LifecycleImpl
51 extends Lifecycle
52 {
53 private static final Log log = LogFactory.getLog(LifecycleImpl.class);
54
55 private List _phaseListenerList = new ArrayList();
56 private PhaseListener[] _phaseListenerArray = null;
57
58 public LifecycleImpl()
59 {
60 // hide from public access
61 }
62
63 public void execute(FacesContext facesContext)
64 throws FacesException
65 {
66 if (restoreView(facesContext))
67 {
68 return;
69 }
70
71 if (applyRequestValues(facesContext))
72 {
73 return;
74 }
75
76 if (processValidations(facesContext))
77 {
78 return;
79 }
80
81 if (updateModelValues(facesContext))
82 {
83 return;
84 }
85
86 if (invokeApplication(facesContext))
87 {
88 return;
89 }
90 }
91
92
93 // Phases
94
95 /**
96 * Restore View (JSF.2.2.1)
97 * @return true, if immediate rendering should occur
98 */
99 private boolean restoreView(FacesContext facesContext)
100 throws FacesException
101 {
102 boolean skipFurtherProcessing = false;
103 if (log.isTraceEnabled()) log.trace("entering restoreView in " + LifecycleImpl.class.getName());
104
105 informPhaseListenersBefore(facesContext, PhaseId.RESTORE_VIEW);
106
107 if(isResponseComplete(facesContext, "restoreView", true))
108 {
109 // have to skips this phase
110 return true;
111 }
112 if (shouldRenderResponse(facesContext, "restoreView", true))
113 {
114 skipFurtherProcessing = true;
115 }
116
117 // Derive view identifier
118 String viewId = deriveViewId(facesContext);
119
120 Application application = facesContext.getApplication();
121 ViewHandler viewHandler = application.getViewHandler();
122
123 //boolean viewCreated = false;
124 UIViewRoot viewRoot = viewHandler.restoreView(facesContext, viewId);
125 if (viewRoot == null)
126 {
127 viewRoot = viewHandler.createView(facesContext, viewId);
128 viewRoot.setViewId(viewId);
129 facesContext.renderResponse();
130 //viewCreated = true;
131 }
132
133 facesContext.setViewRoot(viewRoot);
134
135 /* This section has been disabled because it causes some bug.
136 * Be careful if you need to re-enable it.
137 * Furthermore, for an unknown reason, it seems that by default
138 * it is executed (i.e. log.isTraceEnabled() is true).
139 * Bug example :
140 * This traceView causes DebugUtil.printComponent to print all the attributes
141 * of the view components.
142 * And if you have a data table within an aliasBean, this causes the data table
143 * to initialize it's value attribute while the alias isn't set.
144 * So, the value initializes with an UIData.EMPTY_DATA_MODEL, and not with the aliased one.
145 * But as it's initialized, it will not try to get the value from the ValueBinding next
146 * time it needs to.
147 * I expect this to cause more similar bugs.
148 * TODO : Completely remove or be SURE by default it's not executed, and it has no more side-effects.
149
150 if (log.isTraceEnabled())
151 {
152 //Note: DebugUtils Logger must also be in trace level
153 DebugUtils.traceView(viewCreated ? "Newly created view" : "Restored view");
154 }*/
155
156 if (facesContext.getExternalContext().getRequestParameterMap().isEmpty())
157 {
158 //no POST or query parameters --> set render response flag
159 facesContext.renderResponse();
160 }
161
162 recursivelyHandleComponentReferencesAndSetValid(facesContext, viewRoot);
163
164 informPhaseListenersAfter(facesContext, PhaseId.RESTORE_VIEW);
165
166 if (isResponseComplete(facesContext, "restoreView", false)
167 || shouldRenderResponse(facesContext, "restoreView", false))
168 {
169 // since this phase is completed we don't need to return right away even if the response is completed
170 skipFurtherProcessing = true;
171 }
172
173 if (!skipFurtherProcessing && log.isTraceEnabled()) log.trace("exiting restoreView in " + LifecycleImpl.class.getName());
174 return skipFurtherProcessing;
175 }
176
177
178 /**
179 * Apply Request Values (JSF.2.2.2)
180 * @return true, if response is complete
181 */
182 private boolean applyRequestValues(FacesContext facesContext)
183 throws FacesException
184 {
185 boolean skipFurtherProcessing = false;
186 if (log.isTraceEnabled()) log.trace("entering applyRequestValues in " + LifecycleImpl.class.getName());
187
188 informPhaseListenersBefore(facesContext, PhaseId.APPLY_REQUEST_VALUES);
189
190 if(isResponseComplete(facesContext, "applyRequestValues", true))
191 {
192 // have to return right away
193 return true;
194 }
195 if(shouldRenderResponse(facesContext, "applyRequestValues", true))
196 {
197 skipFurtherProcessing = true;
198 }
199
200 facesContext.getViewRoot().processDecodes(facesContext);
201
202 informPhaseListenersAfter(facesContext, PhaseId.APPLY_REQUEST_VALUES);
203
204
205 if (isResponseComplete(facesContext, "applyRequestValues", false)
206 || shouldRenderResponse(facesContext, "applyRequestValues", false))
207 {
208 // since this phase is completed we don't need to return right away even if the response is completed
209 skipFurtherProcessing = true;
210 }
211
212 if (!skipFurtherProcessing && log.isTraceEnabled())
213 log.trace("exiting applyRequestValues in "
214 + LifecycleImpl.class.getName());
215 return skipFurtherProcessing;
216 }
217
218
219 /**
220 * Process Validations (JSF.2.2.3)
221 * @return true, if response is complete
222 */
223 private boolean processValidations(FacesContext facesContext) throws FacesException
224 {
225 boolean skipFurtherProcessing = false;
226 if (log.isTraceEnabled()) log.trace("entering processValidations in " + LifecycleImpl.class.getName());
227
228 informPhaseListenersBefore(facesContext, PhaseId.PROCESS_VALIDATIONS);
229
230 if(isResponseComplete(facesContext, "processValidations", true))
231 {
232 // have to return right away
233 return true;
234 }
235 if(shouldRenderResponse(facesContext, "processValidations", true))
236 {
237 skipFurtherProcessing = true;
238 }
239
240 facesContext.getViewRoot().processValidators(facesContext);
241
242 informPhaseListenersAfter(facesContext, PhaseId.PROCESS_VALIDATIONS);
243
244 if (isResponseComplete(facesContext, "processValidations", false)
245 || shouldRenderResponse(facesContext, "processValidations", false))
246 {
247 // since this phase is completed we don't need to return right away even if the response is completed
248 skipFurtherProcessing = true;
249 }
250
251 if (!skipFurtherProcessing && log.isTraceEnabled()) log.trace("exiting processValidations in " + LifecycleImpl.class.getName());
252 return skipFurtherProcessing;
253 }
254
255
256 /**
257 * Update Model Values (JSF.2.2.4)
258 * @return true, if response is complete
259 */
260 private boolean updateModelValues(FacesContext facesContext) throws FacesException
261 {
262 boolean skipFurtherProcessing = false;
263 if (log.isTraceEnabled()) log.trace("entering updateModelValues in " + LifecycleImpl.class.getName());
264
265 informPhaseListenersBefore(facesContext, PhaseId.UPDATE_MODEL_VALUES);
266
267 if(isResponseComplete(facesContext, "updateModelValues", true))
268 {
269 // have to return right away
270 return true;
271 }
272 if(shouldRenderResponse(facesContext, "updateModelValues", true))
273 {
274 skipFurtherProcessing = true;
275 }
276
277 facesContext.getViewRoot().processUpdates(facesContext);
278
279 informPhaseListenersAfter(facesContext, PhaseId.UPDATE_MODEL_VALUES);
280
281 if (isResponseComplete(facesContext, "updateModelValues", false)
282 || shouldRenderResponse(facesContext, "updateModelValues", false))
283 {
284 // since this phase is completed we don't need to return right away even if the response is completed
285 skipFurtherProcessing = true;
286 }
287
288 if (!skipFurtherProcessing && log.isTraceEnabled()) log.trace("exiting updateModelValues in " + LifecycleImpl.class.getName());
289
290 return skipFurtherProcessing;
291 }
292
293
294 /**
295 * Invoke Application (JSF.2.2.5)
296 * @return true, if response is complete
297 */
298 private boolean invokeApplication(FacesContext facesContext)
299 throws FacesException
300 {
301 boolean skipFurtherProcessing = false;
302 if (log.isTraceEnabled()) log.trace("entering invokeApplication in " + LifecycleImpl.class.getName());
303
304 informPhaseListenersBefore(facesContext, PhaseId.INVOKE_APPLICATION);
305
306 if(isResponseComplete(facesContext, "invokeApplication", true))
307 {
308 // have to return right away
309 return true;
310 }
311 if(shouldRenderResponse(facesContext, "invokeApplication", true))
312 {
313 skipFurtherProcessing = true;
314 }
315
316 facesContext.getViewRoot().processApplication(facesContext);
317
318 informPhaseListenersAfter(facesContext, PhaseId.INVOKE_APPLICATION);
319
320 if (isResponseComplete(facesContext, "invokeApplication", false)
321 || shouldRenderResponse(facesContext, "invokeApplication", false))
322 {
323 // since this phase is completed we don't need to return right away even if the response is completed
324 skipFurtherProcessing = true;
325 }
326
327 if (!skipFurtherProcessing && log.isTraceEnabled()) log.trace("exiting invokeApplication in " + LifecycleImpl.class.getName());
328
329 return skipFurtherProcessing;
330 }
331
332
333 public void render(FacesContext facesContext) throws FacesException
334 {
335 // if the response is complete we should not be invoking the phase listeners
336 if(isResponseComplete(facesContext, "render", true))
337 {
338 return;
339 }
340 if (log.isTraceEnabled()) log.trace("entering renderResponse in " + LifecycleImpl.class.getName());
341
342 informPhaseListenersBefore(facesContext, PhaseId.RENDER_RESPONSE);
343 // also possible that one of the listeners completed the response
344 if(isResponseComplete(facesContext, "render", true))
345 {
346 return;
347 }
348 Application application = facesContext.getApplication();
349 ViewHandler viewHandler = application.getViewHandler();
350 try
351 {
352 viewHandler.renderView(facesContext, facesContext.getViewRoot());
353 }
354 catch (IOException e)
355 {
356 throw new FacesException(e.getMessage(), e);
357 }
358
359 informPhaseListenersAfter(facesContext, PhaseId.RENDER_RESPONSE);
360 if (log.isTraceEnabled())
361 {
362 //Note: DebugUtils Logger must also be in trace level
363 DebugUtils.traceView("View after rendering");
364 }
365
366 if (log.isTraceEnabled()) log.trace("exiting renderResponse in " + LifecycleImpl.class.getName());
367 }
368
369
370 private boolean isResponseComplete(FacesContext facesContext, String phase, boolean before) {
371 boolean flag = false;
372 if (facesContext.getResponseComplete())
373 {
374 if (log.isDebugEnabled())
375 log.debug("exiting from lifecycle.execute in " + phase
376 + " because getResponseComplete is true from one of the " +
377 (before ? "before" : "after") + " listeners");
378 flag = true;
379 }
380 return flag;
381 }
382
383 private boolean shouldRenderResponse(FacesContext facesContext, String phase, boolean before) {
384 boolean flag = false;
385 if (facesContext.getRenderResponse())
386 {
387 if (log.isDebugEnabled())
388 log.debug("exiting from lifecycle.execute in " + phase
389 + " because getRenderResponse is true from one of the " +
390 (before ? "before" : "after") + " listeners");
391 flag = true;
392 }
393 return flag;
394 }
395
396 private static String deriveViewId(FacesContext facesContext)
397 {
398 ExternalContext externalContext = facesContext.getExternalContext();
399
400 if (PortletUtil.isPortletRequest(facesContext))
401 {
402 PortletRequest request = (PortletRequest)externalContext.getRequest();
403 return request.getParameter(MyFacesGenericPortlet.VIEW_ID);
404 }
405
406 String viewId = externalContext.getRequestPathInfo(); //getPathInfo
407 if (viewId == null)
408 {
409 //No extra path info found, so it is propably extension mapping
410 viewId = externalContext.getRequestServletPath(); //getServletPath
411 DebugUtils.assertError(viewId != null,
412 log, "RequestServletPath is null, cannot determine viewId of current page.");
413 //TODO: JSF Spec 2.2.1 - what do they mean by "if the default ViewHandler implementation is used..." ?
414 String defaultSuffix = externalContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
415 String suffix = defaultSuffix != null ? defaultSuffix : ViewHandler.DEFAULT_SUFFIX;
416 DebugUtils.assertError(suffix.charAt(0) == '.',
417 log, "Default suffix must start with a dot!");
418
419 int dot = viewId.lastIndexOf('.');
420 if (dot == -1)
421 {
422 log.error("Assumed extension mapping, but there is no extension in " + viewId);
423 }
424 else
425 {
426 viewId = viewId.substring(0, dot) + suffix;
427 }
428 }
429
430 return viewId;
431 }
432
433
434 private static void recursivelyHandleComponentReferencesAndSetValid(FacesContext facesContext,
435 UIComponent root)
436 {
437 for (Iterator it = root.getFacetsAndChildren(); it.hasNext(); )
438 {
439 UIComponent component = (UIComponent)it.next();
440
441 ValueBinding binding = component.getValueBinding("binding"); //TODO: constant
442 if (binding != null && !binding.isReadOnly(facesContext))
443 {
444 binding.setValue(facesContext, component);
445 }
446
447 if (component instanceof UIInput)
448 {
449 ((UIInput)component).setValid(true);
450 }
451
452 recursivelyHandleComponentReferencesAndSetValid(facesContext, component);
453 }
454 }
455
456 public void addPhaseListener(PhaseListener phaseListener)
457 {
458 if(phaseListener == null)
459 {
460 throw new NullPointerException("PhaseListener must not be null.");
461 }
462 if (_phaseListenerList == null)
463 {
464 _phaseListenerList = new ArrayList();
465 if (_phaseListenerArray != null)
466 {
467 _phaseListenerList.addAll(Arrays.asList(_phaseListenerArray));
468 _phaseListenerArray = null;
469 }
470 }
471 _phaseListenerList.add(phaseListener);
472 }
473
474 public void removePhaseListener(PhaseListener phaseListener)
475 {
476 if(phaseListener == null)
477 {
478 throw new NullPointerException("PhaseListener must not be null.");
479 }
480 if (_phaseListenerList == null)
481 {
482 _phaseListenerList = new ArrayList();
483 if (_phaseListenerArray != null)
484 {
485 _phaseListenerList.addAll(Arrays.asList(_phaseListenerArray));
486 _phaseListenerArray = null;
487 }
488 }
489 _phaseListenerList.remove(phaseListener);
490 }
491
492 public PhaseListener[] getPhaseListeners()
493 {
494 if (_phaseListenerArray == null)
495 {
496 if (_phaseListenerList == null)
497 {
498 _phaseListenerArray = new PhaseListener[0];
499 }
500 else
501 {
502 _phaseListenerArray = (PhaseListener[])_phaseListenerList.toArray(new PhaseListener[_phaseListenerList.size()]);
503 _phaseListenerList = null;
504 }
505 }
506 return _phaseListenerArray;
507 }
508
509
510 private void informPhaseListenersBefore(FacesContext facesContext, PhaseId phaseId)
511 {
512 PhaseListener[] phaseListeners = getPhaseListeners();
513 for (int i = 0; i < phaseListeners.length; i++)
514 {
515 PhaseListener phaseListener = phaseListeners[i];
516 int listenerPhaseId = phaseListener.getPhaseId().getOrdinal();
517 if (listenerPhaseId == PhaseId.ANY_PHASE.getOrdinal() ||
518 listenerPhaseId == phaseId.getOrdinal())
519 {
520 phaseListener.beforePhase(new PhaseEvent(facesContext, phaseId, this));
521 }
522 }
523
524 }
525
526 private void informPhaseListenersAfter(FacesContext facesContext, PhaseId phaseId)
527 {
528 PhaseListener[] phaseListeners = getPhaseListeners();
529 for (int i = 0; i < phaseListeners.length; i++)
530 {
531 PhaseListener phaseListener = phaseListeners[i];
532 int listenerPhaseId = phaseListener.getPhaseId().getOrdinal();
533 if (listenerPhaseId == PhaseId.ANY_PHASE.getOrdinal() ||
534 listenerPhaseId == phaseId.getOrdinal())
535 {
536 phaseListener.afterPhase(new PhaseEvent(facesContext, phaseId, this));
537 }
538 }
539
540 }
541
542 }