001 package org.apache.myfaces.tobago.lifecycle;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one or more
005 * contributor license agreements. See the NOTICE file distributed with
006 * this work for additional information regarding copyright ownership.
007 * The ASF licenses this file to You under the Apache License, Version 2.0
008 * (the "License"); you may not use this file except in compliance with
009 * the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020 import org.apache.commons.logging.Log;
021 import org.apache.commons.logging.LogFactory;
022 import org.apache.myfaces.tobago.component.ComponentUtil;
023 import org.apache.myfaces.tobago.util.RequestUtils;
024
025 import javax.faces.FacesException;
026 import javax.faces.context.FacesContext;
027 import javax.faces.event.PhaseId;
028 import javax.faces.event.PhaseListener;
029 import javax.faces.lifecycle.Lifecycle;
030 import java.util.ArrayList;
031 import java.util.List;
032
033 /**
034 * Implements the lifecycle as described in Spec. 1.0 PFD Chapter 2
035 */
036 public class TobagoLifecycle extends Lifecycle {
037
038 private static final Log LOG = LogFactory.getLog(TobagoLifecycle.class);
039
040 public static final String VIEW_ROOT_KEY = TobagoLifecycle.class.getName() + ".VIEW_ROOT_KEY";
041 public static final String FACES_MESSAGES_KEY = TobagoLifecycle.class.getName() + ".FACES_MESSAGES_KEY";
042
043 private PhaseExecutor[] lifecycleExecutors;
044 private PhaseExecutor renderExecutor;
045
046 private final List<PhaseListener> phaseListenerList = new ArrayList<PhaseListener>();
047
048 /**
049 * Lazy cache for returning phaseListenerList as an Array.
050 */
051 private PhaseListener[] phaseListenerArray = null;
052
053 public TobagoLifecycle() {
054 // hide from public access
055 lifecycleExecutors = new PhaseExecutor[]{
056 new RestoreViewExecutor(),
057 new ApplyRequestValuesExecutor(),
058 new ProcessValidationsExecutor(),
059 new UpdateModelValuesExecutor(),
060 new InvokeApplicationExecutor()
061 };
062
063 renderExecutor = new RenderResponseExecutor();
064 }
065
066 public void execute(FacesContext facesContext) throws FacesException {
067 PhaseListenerManager phaseListenerMgr
068 = new PhaseListenerManager(this, facesContext, getPhaseListeners());
069
070 // At very first ensure the requestEncoding, this MUST done before
071 // accessing request parameters, wich can occur in custom phaseListeners.
072 RequestUtils.ensureEncoding(facesContext);
073
074 for (PhaseExecutor executor : lifecycleExecutors) {
075 if (executePhase(facesContext, executor, phaseListenerMgr)) {
076 return;
077 }
078 }
079 }
080
081 private boolean executePhase(FacesContext facesContext, PhaseExecutor executor,
082 PhaseListenerManager phaseListenerMgr)
083 throws FacesException {
084
085 boolean skipFurtherProcessing = false;
086 if (LOG.isTraceEnabled()) {
087 LOG.trace("entering " + executor.getPhase() + " in " + TobagoLifecycle.class.getName());
088 }
089
090 try {
091 phaseListenerMgr.informPhaseListenersBefore(executor.getPhase());
092
093 if (isResponseComplete(facesContext, executor.getPhase(), true)) {
094 // have to return right away
095 return true;
096 }
097 if (shouldRenderResponse(facesContext, executor.getPhase(), true)) {
098 skipFurtherProcessing = true;
099 }
100
101 if (executor.execute(facesContext)) {
102 return true;
103 }
104 } finally {
105 phaseListenerMgr.informPhaseListenersAfter(executor.getPhase());
106 }
107
108
109 if (isResponseComplete(facesContext, executor.getPhase(), false)
110 || shouldRenderResponse(facesContext, executor.getPhase(), false)) {
111 // since this phase is completed we don't need to return right away even if the response is completed
112 skipFurtherProcessing = true;
113 }
114
115 if (!skipFurtherProcessing && LOG.isTraceEnabled()) {
116 LOG.trace("exiting " + executor.getPhase() + " in " + TobagoLifecycle.class.getName());
117 }
118
119 return skipFurtherProcessing;
120 }
121
122 public void render(FacesContext facesContext) throws FacesException {
123 // if the response is complete we should not be invoking the phase listeners
124 if (isResponseComplete(facesContext, renderExecutor.getPhase(), true)) {
125 return;
126 }
127 if (LOG.isTraceEnabled()) {
128 LOG.trace("entering " + renderExecutor.getPhase() + " in " + TobagoLifecycle.class.getName());
129 }
130
131 PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, getPhaseListeners());
132
133 try {
134 phaseListenerMgr.informPhaseListenersBefore(renderExecutor.getPhase());
135 // also possible that one of the listeners completed the response
136 if (isResponseComplete(facesContext, renderExecutor.getPhase(), true)) {
137 return;
138 }
139
140 renderExecutor.execute(facesContext);
141 } finally {
142 phaseListenerMgr.informPhaseListenersAfter(renderExecutor.getPhase());
143 }
144
145 if (LOG.isTraceEnabled()) {
146 LOG.trace(ComponentUtil.toString(facesContext.getViewRoot(), 0));
147 }
148
149 if (LOG.isTraceEnabled()) {
150 LOG.trace("exiting " + renderExecutor.getPhase() + " in " + TobagoLifecycle.class.getName());
151 }
152 }
153
154 private boolean isResponseComplete(FacesContext facesContext, PhaseId phase, boolean before) {
155 boolean flag = false;
156 if (facesContext.getResponseComplete()) {
157 if (LOG.isDebugEnabled()) {
158 LOG.debug("exiting from lifecycle.execute in " + phase
159 + " because getResponseComplete is true from one of the "
160 + (before ? "before" : "after") + " listeners");
161 }
162 flag = true;
163 }
164 return flag;
165 }
166
167 private boolean shouldRenderResponse(FacesContext facesContext, PhaseId phase, boolean before) {
168 boolean flag = false;
169 if (facesContext.getRenderResponse()) {
170 if (LOG.isDebugEnabled()) {
171 LOG.debug("exiting from lifecycle.execute in " + phase
172 + " because getRenderResponse is true from one of the "
173 + (before ? "before" : "after") + " listeners");
174 }
175 flag = true;
176 }
177 return flag;
178 }
179
180 public void addPhaseListener(PhaseListener phaseListener) {
181 if (phaseListener == null) {
182 throw new NullPointerException("PhaseListener must not be null.");
183 }
184 synchronized (phaseListenerList) {
185 phaseListenerList.add(phaseListener);
186 phaseListenerArray = null; // reset lazy cache array
187 }
188 }
189
190 public void removePhaseListener(PhaseListener phaseListener) {
191 if (phaseListener == null) {
192 throw new NullPointerException("PhaseListener must not be null.");
193 }
194 synchronized (phaseListenerList) {
195 phaseListenerList.remove(phaseListener);
196 phaseListenerArray = null; // reset lazy cache array
197 }
198 }
199
200 public PhaseListener[] getPhaseListeners() {
201 synchronized (phaseListenerList) {
202 // (re)build lazy cache array if necessary
203 if (phaseListenerArray == null) {
204 phaseListenerArray = phaseListenerList.toArray(new PhaseListener[phaseListenerList.size()]);
205 }
206 return phaseListenerArray;
207 }
208 }
209 }