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 }