View Javadoc

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  
20  package org.apache.myfaces.tobago.internal.lifecycle;
21  
22  import org.apache.myfaces.tobago.util.DebugUtils;
23  import org.apache.myfaces.tobago.util.RequestUtils;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  import javax.faces.FacesException;
28  import javax.faces.context.FacesContext;
29  import javax.faces.event.PhaseId;
30  import javax.faces.event.PhaseListener;
31  import javax.faces.lifecycle.Lifecycle;
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  /**
36   * Implements the lifecycle as described in Spec. 1.0 PFD Chapter 2
37   */
38  public class TobagoLifecycle extends Lifecycle {
39  
40    private static final Logger LOG = LoggerFactory.getLogger(TobagoLifecycle.class);
41  
42    public static final String VIEW_ROOT_KEY = TobagoLifecycle.class.getName() + ".VIEW_ROOT_KEY";
43    public static final String FACES_MESSAGES_KEY = TobagoLifecycle.class.getName() + ".FACES_MESSAGES_KEY";
44  
45    private PhaseExecutor[] lifecycleExecutors;
46    private PhaseExecutor renderExecutor;
47  
48    private final List<PhaseListener> phaseListenerList = new ArrayList<PhaseListener>();
49  
50    /**
51     * Lazy cache for returning phaseListenerList as an Array.
52     */
53    private PhaseListener[] phaseListenerArray = null;
54  
55    public TobagoLifecycle() {
56      // hide from public access
57      lifecycleExecutors = new PhaseExecutor[]{
58          new RestoreViewExecutor(),
59          new ApplyRequestValuesExecutor(),
60          new ProcessValidationsExecutor(),
61          new UpdateModelValuesExecutor(),
62          new InvokeApplicationExecutor()
63      };
64  
65      renderExecutor = new RenderResponseExecutor();
66    }
67  
68    public void execute(FacesContext context) throws FacesException {
69  
70      PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, context, getPhaseListeners());
71  
72      // At very first ensure the requestEncoding, this MUST done before
73      // accessing request parameters, which can occur in custom phaseListeners.
74      RequestUtils.ensureEncoding(context);
75  
76      for (PhaseExecutor executor : lifecycleExecutors) {
77        if (executePhase(context, executor, phaseListenerMgr)) {
78          return;
79        }
80      }
81    }
82  
83    private boolean executePhase(FacesContext facesContext, PhaseExecutor executor, PhaseListenerManager phaseListenerMgr)
84        throws FacesException {
85  
86      boolean skipFurtherProcessing = false;
87      if (LOG.isTraceEnabled()) {
88        LOG.trace("entering " + executor.getPhase() + " in " + TobagoLifecycle.class.getName());
89      }
90  
91      try {
92        phaseListenerMgr.informPhaseListenersBefore(executor.getPhase());
93  
94        if (isResponseComplete(facesContext, executor.getPhase(), true)) {
95          // have to return right away
96          return true;
97        }
98        if (shouldRenderResponse(facesContext, executor.getPhase(), true)) {
99          skipFurtherProcessing = true;
100       }
101 
102       if (executor.execute(facesContext)) {
103         return true;
104       }
105     } finally {
106       phaseListenerMgr.informPhaseListenersAfter(executor.getPhase());
107     }
108 
109 
110     if (isResponseComplete(facesContext, executor.getPhase(), false)
111         || shouldRenderResponse(facesContext, executor.getPhase(), false)) {
112       // since this phase is completed we don't need to return right away even if the response is completed
113       skipFurtherProcessing = true;
114     }
115 
116     if (!skipFurtherProcessing && LOG.isTraceEnabled()) {
117       LOG.trace("exiting " + executor.getPhase() + " in " + TobagoLifecycle.class.getName());
118     }
119 
120     return skipFurtherProcessing;
121   }
122 
123   public void render(FacesContext facesContext) throws FacesException {
124     // if the response is complete we should not be invoking the phase listeners
125     if (isResponseComplete(facesContext, renderExecutor.getPhase(), true)) {
126       return;
127     }
128     if (LOG.isTraceEnabled()) {
129       LOG.trace("entering " + renderExecutor.getPhase() + " in " + TobagoLifecycle.class.getName());
130     }
131 
132     PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, getPhaseListeners());
133 
134     try {
135       phaseListenerMgr.informPhaseListenersBefore(renderExecutor.getPhase());
136       // also possible that one of the listeners completed the response
137       if (isResponseComplete(facesContext, renderExecutor.getPhase(), true)) {
138         return;
139       }
140       renderExecutor.execute(facesContext);
141     } finally {
142       phaseListenerMgr.informPhaseListenersAfter(renderExecutor.getPhase());
143     }
144 
145     if (LOG.isTraceEnabled()) {
146       LOG.trace(DebugUtils.toString(facesContext.getViewRoot(), 0));
147       LOG.trace("exiting " + renderExecutor.getPhase() + " in " + TobagoLifecycle.class.getName());
148     }
149 
150   }
151 
152   private boolean isResponseComplete(FacesContext facesContext, PhaseId phase, boolean before) {
153     boolean flag = false;
154     if (facesContext.getResponseComplete()) {
155       if (LOG.isDebugEnabled()) {
156         LOG.debug("exiting from lifecycle.execute in " + phase
157             + " because getResponseComplete is true from one of the "
158             + (before ? "before" : "after") + " listeners");
159       }
160       flag = true;
161     }
162     return flag;
163   }
164 
165   private boolean shouldRenderResponse(FacesContext facesContext, PhaseId phase, boolean before) {
166     boolean flag = false;
167     if (facesContext.getRenderResponse()) {
168       if (LOG.isDebugEnabled()) {
169         LOG.debug("exiting from lifecycle.execute in " + phase
170             + " because getRenderResponse is true from one of the "
171             + (before ? "before" : "after") + " listeners");
172       }
173       flag = true;
174     }
175     return flag;
176   }
177 
178   public void addPhaseListener(PhaseListener phaseListener) {
179     if (phaseListener == null) {
180       throw new NullPointerException("PhaseListener must not be null.");
181     }
182     synchronized (phaseListenerList) {
183       phaseListenerList.add(phaseListener);
184       phaseListenerArray = null; // reset lazy cache array
185     }
186   }
187 
188   public void removePhaseListener(PhaseListener phaseListener) {
189     if (phaseListener == null) {
190       throw new NullPointerException("PhaseListener must not be null.");
191     }
192     synchronized (phaseListenerList) {
193       phaseListenerList.remove(phaseListener);
194       phaseListenerArray = null; // reset lazy cache array
195     }
196   }
197 
198   public PhaseListener[] getPhaseListeners() {
199     synchronized (phaseListenerList) {
200       // (re)build lazy cache array if necessary
201       if (phaseListenerArray == null) {
202         phaseListenerArray = phaseListenerList.toArray(new PhaseListener[phaseListenerList.size()]);
203       }
204       return phaseListenerArray;
205     }
206   }
207 }