Coverage Report - org.apache.myfaces.lifecycle.LifecycleImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
LifecycleImpl
0%
0/102
0%
0/54
3.692
 
 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  
 package org.apache.myfaces.lifecycle;
 20  
 
 21  
 import java.util.List;
 22  
 import java.util.concurrent.CopyOnWriteArrayList;
 23  
 import java.util.logging.Level;
 24  
 import java.util.logging.Logger;
 25  
 
 26  
 import javax.faces.FacesException;
 27  
 import javax.faces.FactoryFinder;
 28  
 import javax.faces.application.ProjectStage;
 29  
 import javax.faces.context.FacesContext;
 30  
 import javax.faces.context.Flash;
 31  
 import javax.faces.event.ExceptionQueuedEvent;
 32  
 import javax.faces.event.ExceptionQueuedEventContext;
 33  
 import javax.faces.event.PhaseId;
 34  
 import javax.faces.event.PhaseListener;
 35  
 import javax.faces.lifecycle.ClientWindow;
 36  
 import javax.faces.lifecycle.ClientWindowFactory;
 37  
 import javax.faces.lifecycle.Lifecycle;
 38  
 
 39  
 import org.apache.myfaces.config.FacesConfigurator;
 40  
 import org.apache.myfaces.shared_impl.webapp.webxml.WebXml;
 41  
 import org.apache.myfaces.util.DebugUtils;
 42  
 
 43  
 /**
 44  
  * Implements the lifecycle as described in Spec. 1.0 PFD Chapter 2
 45  
  * 
 46  
  * @author Manfred Geiler (latest modification by $Author$)
 47  
  * @author Nikolay Petrov
 48  
  * @version $Revision$ $Date$
 49  
  */
 50  
 public class LifecycleImpl extends Lifecycle
 51  
 {
 52  
     //private static final Log log = LogFactory.getLog(LifecycleImpl.class);
 53  0
     private static final Logger log = Logger.getLogger(LifecycleImpl.class.getName());
 54  
     
 55  
     /**
 56  
      * Boolean.TRUE is stored under this key in the application map if
 57  
      * the first request has been processed.
 58  
      */
 59  
     public static final String FIRST_REQUEST_PROCESSED_PARAM = "org.apache.myfaces.lifecycle.first.request.processed";
 60  
     
 61  
     private final PhaseExecutor[] lifecycleExecutors;
 62  
     private final PhaseExecutor renderExecutor;
 63  
 
 64  
     /**
 65  
      * Initially, for ensure thread safety we used synchronization blocks and a cached 
 66  
      * _phaseListenerArray and that works. The intention is ensure atomicity between
 67  
      * _phaseListenerList and _phaseListenerArray, but thinking more about it use
 68  
      * CopyOnWriteArrayList and do not use _phaseListenerArray is a lot better. 
 69  
      * 
 70  
      * Most times, we have few instances of PhaseListener registered, so the advantage of 
 71  
      * use _phaseListenerArray is overcome by do not have a synchronization block on getPhaseListeners().
 72  
      * Additionally, it is more often to perform traversals than insertions/removals and 
 73  
      * we can expect only 2 calls for getPhaseListeners() per request (so only two copy 
 74  
      * operations of a very small list).
 75  
      */
 76  0
     private final List<PhaseListener> _phaseListenerList
 77  
             = new CopyOnWriteArrayList<PhaseListener>(); // new ArrayList();
 78  
 
 79  
     /**
 80  
      * This variable should be marked as volatile to ensure all threads can see it
 81  
      * after the first request is processed. Note that LifecycleImpl instance could be
 82  
      * shared by multiple requests at the same time, so this is relevant to prevent
 83  
      * multiple updates to FIRST_REQUEST_PROCESSED_PARAM. Really since the value
 84  
      * only changes from false to true, have a racy single check here does not harm, but
 85  
      * note in this case the semantic of the variable must be preserved.
 86  
      */
 87  0
     private volatile boolean _firstRequestProcessed = false;
 88  
     /**
 89  
      * Lazy cache for returning _phaseListenerList as an Array.
 90  
      * 
 91  
      * Replaced by _phaseListenerList CopyOnWriteArrayList
 92  
      */
 93  
     //private PhaseListener[] _phaseListenerArray = null;
 94  
     
 95  
     private ClientWindowFactory clientWindowFactory;
 96  
     
 97  
     public LifecycleImpl()
 98  0
     {
 99  
         // hide from public access
 100  0
         lifecycleExecutors = new PhaseExecutor[] { new RestoreViewExecutor(), new ApplyRequestValuesExecutor(),
 101  
                 new ProcessValidationsExecutor(), new UpdateModelValuesExecutor(), new InvokeApplicationExecutor() };
 102  
 
 103  0
         renderExecutor = new RenderResponseExecutor();
 104  0
         clientWindowFactory = (ClientWindowFactory) FactoryFinder.getFactory(FactoryFinder.CLIENT_WINDOW_FACTORY);
 105  0
     }
 106  
     
 107  
     @Override
 108  
     public void attachWindow(FacesContext facesContext)
 109  
     {
 110  0
         ClientWindow clientWindow = facesContext.getExternalContext().getClientWindow();
 111  0
         if (clientWindow == null)
 112  
         {
 113  0
             clientWindow = getClientWindowFactory().getClientWindow(facesContext);
 114  
         }
 115  0
         if (clientWindow != null)
 116  
         {
 117  0
             clientWindow.decode(facesContext);
 118  0
             facesContext.getExternalContext().setClientWindow(clientWindow);
 119  
         }
 120  0
     }
 121  
     
 122  
     protected ClientWindowFactory getClientWindowFactory()
 123  
     {
 124  0
         return clientWindowFactory;
 125  
     }
 126  
 
 127  
     @Override
 128  
     public void execute(FacesContext facesContext) throws FacesException
 129  
     {
 130  
         //try
 131  
         //{
 132  
             // check for updates of web.xml and faces-config descriptors 
 133  
             // only if project state is not production
 134  0
             if(!facesContext.isProjectStage(ProjectStage.Production))
 135  
             {
 136  0
                 WebXml.update(facesContext.getExternalContext());
 137  0
                 new FacesConfigurator(facesContext.getExternalContext()).update();
 138  
             }
 139  
             
 140  0
             PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, getPhaseListeners());
 141  0
             for (PhaseExecutor executor : lifecycleExecutors)
 142  
             {
 143  0
                 if (executePhase(facesContext, executor, phaseListenerMgr))
 144  
                 {
 145  0
                     return;
 146  
                 }
 147  
             }
 148  
         //}
 149  
         //catch (Throwable ex)
 150  
         //{
 151  
             // handle the Throwable accordingly. Maybe generate an error page.
 152  
             //ErrorPageWriter.handleThrowable(facesContext, ex);
 153  
         //}
 154  0
     }
 155  
 
 156  
     private boolean executePhase(FacesContext context, PhaseExecutor executor, PhaseListenerManager phaseListenerMgr)
 157  
         throws FacesException
 158  
     {
 159  0
         boolean skipFurtherProcessing = false;
 160  
 
 161  0
         if (log.isLoggable(Level.FINEST))
 162  
         {
 163  0
             log.finest("entering " + executor.getPhase() + " in " + LifecycleImpl.class.getName());
 164  
         }
 165  
 
 166  0
         PhaseId currentPhaseId = executor.getPhase();
 167  0
         Flash flash = context.getExternalContext().getFlash();
 168  
 
 169  
         try
 170  
         {
 171  
             /* 
 172  
              * Specification, section 2.2
 173  
              * The default request lifecycle processing implementation must ensure that the currentPhaseId property 
 174  
              * of the FacesContext instance for this request is set with the proper PhaseId constant for the current 
 175  
              * phase as the first instruction at the beginning of each phase
 176  
              */
 177  0
             context.setCurrentPhaseId(currentPhaseId);
 178  
             
 179  0
             flash.doPrePhaseActions(context);
 180  
             
 181  
             // let the PhaseExecutor do some pre-phase actions
 182  0
             executor.doPrePhaseActions(context);
 183  
 
 184  0
             phaseListenerMgr.informPhaseListenersBefore(currentPhaseId);
 185  
 
 186  0
             if (isResponseComplete(context, currentPhaseId, true))
 187  
             {
 188  
                 // have to return right away
 189  0
                 return true;
 190  
             }
 191  0
             if (shouldRenderResponse(context, currentPhaseId, true))
 192  
             {
 193  0
                 skipFurtherProcessing = true;
 194  
             }
 195  
 
 196  0
             if (executor.execute(context))
 197  
             {
 198  0
                 return true;
 199  
             }
 200  
         }
 201  
         
 202  0
         catch (Throwable e)
 203  
         {
 204  
             // JSF 2.0: publish the executor's exception (if any).
 205  
             
 206  0
             publishException (e, currentPhaseId, context);
 207  
         }
 208  
         
 209  
         finally
 210  
         {
 211  0
             phaseListenerMgr.informPhaseListenersAfter(currentPhaseId);
 212  
             
 213  0
             flash.doPostPhaseActions(context);
 214  
             
 215  0
         }
 216  
         
 217  0
         context.getExceptionHandler().handle();
 218  
         
 219  0
         if (isResponseComplete(context, currentPhaseId, false) || shouldRenderResponse(context, currentPhaseId, false))
 220  
         {
 221  
             // since this phase is completed we don't need to return right away even if the response is completed
 222  0
             skipFurtherProcessing = true;
 223  
         }
 224  
 
 225  0
         if (!skipFurtherProcessing && log.isLoggable(Level.FINEST))
 226  
         {
 227  0
             log.finest("exiting " + executor.getPhase() + " in " + LifecycleImpl.class.getName());
 228  
         }
 229  
 
 230  0
         return skipFurtherProcessing;
 231  
     }
 232  
 
 233  
     @Override
 234  
     public void render(FacesContext facesContext) throws FacesException
 235  
     {
 236  
         //try
 237  
         //{
 238  
             // if the response is complete we should not be invoking the phase listeners
 239  0
             if (isResponseComplete(facesContext, renderExecutor.getPhase(), true))
 240  
             {
 241  0
                 return;
 242  
             }
 243  0
             if (log.isLoggable(Level.FINEST))
 244  
             {
 245  0
                 log.finest("entering " + renderExecutor.getPhase() + " in " + LifecycleImpl.class.getName());
 246  
             }
 247  
     
 248  0
             PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, getPhaseListeners());
 249  0
             Flash flash = facesContext.getExternalContext().getFlash();
 250  
             
 251  
             try
 252  
             {
 253  0
                 facesContext.setCurrentPhaseId(renderExecutor.getPhase());
 254  
                 
 255  0
                 flash.doPrePhaseActions(facesContext);
 256  
                 
 257  
                 // let the PhaseExecutor do some pre-phase actions
 258  0
                 renderExecutor.doPrePhaseActions(facesContext);
 259  
                 
 260  0
                 phaseListenerMgr.informPhaseListenersBefore(renderExecutor.getPhase());
 261  
                 // also possible that one of the listeners completed the response
 262  0
                 if (isResponseComplete(facesContext, renderExecutor.getPhase(), true))
 263  
                 {
 264  
                     return;
 265  
                 }
 266  
                 
 267  0
                 renderExecutor.execute(facesContext);
 268  
             }
 269  
             
 270  0
             catch (Throwable e)
 271  
             {
 272  
                 // JSF 2.0: publish the executor's exception (if any).
 273  
                 
 274  0
                 publishException (e, renderExecutor.getPhase(), facesContext);
 275  
             }
 276  
             
 277  
             finally
 278  
             {
 279  0
                 phaseListenerMgr.informPhaseListenersAfter(renderExecutor.getPhase());
 280  0
                 flash.doPostPhaseActions(facesContext);
 281  
                 
 282  
                 // publish a field in the application map to indicate
 283  
                 // that the first request has been processed
 284  0
                 requestProcessed(facesContext);
 285  0
             }
 286  
             
 287  0
             facesContext.getExceptionHandler().handle();
 288  
             
 289  0
             if (log.isLoggable(Level.FINEST))
 290  
             {
 291  
                 // Note: DebugUtils Logger must also be in trace level
 292  0
                 DebugUtils.traceView("View after rendering");
 293  
             }
 294  
     
 295  0
             if (log.isLoggable(Level.FINEST))
 296  
             {
 297  0
                 log.finest("exiting " + renderExecutor.getPhase() + " in " + LifecycleImpl.class.getName());
 298  
             }
 299  
         //}
 300  
         //catch (Throwable ex)
 301  
         //{
 302  
             // handle the Throwable accordingly. Maybe generate an error page.
 303  
             //ErrorPageWriter.handleThrowable(facesContext, ex);
 304  
         //}
 305  0
     }
 306  
 
 307  
     private boolean isResponseComplete(FacesContext facesContext, PhaseId phase, boolean before)
 308  
     {
 309  0
         boolean flag = false;
 310  0
         if (facesContext.getResponseComplete())
 311  
         {
 312  0
             if (log.isLoggable(Level.FINE))
 313  
             {
 314  0
                 log.fine("exiting from lifecycle.execute in " + phase
 315  
                         + " because getResponseComplete is true from one of the " + (before ? "before" : "after")
 316  
                         + " listeners");
 317  
             }
 318  0
             flag = true;
 319  
         }
 320  0
         return flag;
 321  
     }
 322  
 
 323  
     private boolean shouldRenderResponse(FacesContext facesContext, PhaseId phase, boolean before)
 324  
     {
 325  0
         boolean flag = false;
 326  0
         if (facesContext.getRenderResponse())
 327  
         {
 328  0
             if (log.isLoggable(Level.FINE))
 329  
             {
 330  0
                 log.fine("exiting from lifecycle.execute in " + phase
 331  
                         + " because getRenderResponse is true from one of the " + (before ? "before" : "after")
 332  
                         + " listeners");
 333  
             }
 334  0
             flag = true;
 335  
         }
 336  0
         return flag;
 337  
     }
 338  
 
 339  
     @Override
 340  
     public void addPhaseListener(PhaseListener phaseListener)
 341  
     {
 342  0
         if (phaseListener == null)
 343  
         {
 344  0
             throw new NullPointerException("PhaseListener must not be null.");
 345  
         }
 346  
         //synchronized (_phaseListenerList)
 347  
         //{
 348  0
             _phaseListenerList.add(phaseListener);
 349  
             //_phaseListenerArray = null; // reset lazy cache array
 350  
         //}
 351  0
     }
 352  
 
 353  
     @Override
 354  
     public void removePhaseListener(PhaseListener phaseListener)
 355  
     {
 356  0
         if (phaseListener == null)
 357  
         {
 358  0
             throw new NullPointerException("PhaseListener must not be null.");
 359  
         }
 360  
         //synchronized (_phaseListenerList)
 361  
         //{
 362  0
             _phaseListenerList.remove(phaseListener);
 363  
             //_phaseListenerArray = null; // reset lazy cache array
 364  
         //}
 365  0
     }
 366  
 
 367  
     @Override
 368  
     public PhaseListener[] getPhaseListeners()
 369  
     {
 370  
         //synchronized (_phaseListenerList)
 371  
         //{
 372  
             // (re)build lazy cache array if necessary
 373  
             //if (_phaseListenerArray == null)
 374  
             //{
 375  
             //    _phaseListenerArray = _phaseListenerList.toArray(new PhaseListener[_phaseListenerList.size()]);
 376  
             //}
 377  
             //return _phaseListenerArray;
 378  
         //}
 379  0
         return _phaseListenerList.toArray(new PhaseListener[_phaseListenerList.size()]);
 380  
     }
 381  
     
 382  
     private void publishException (Throwable e, PhaseId phaseId, FacesContext facesContext)
 383  
     {
 384  0
         ExceptionQueuedEventContext context = new ExceptionQueuedEventContext (facesContext, e, null, phaseId);
 385  
         
 386  0
         facesContext.getApplication().publishEvent (facesContext, ExceptionQueuedEvent.class, context);
 387  0
     }
 388  
     
 389  
     /**
 390  
      * This method places an attribute on the application map to 
 391  
      * indicate that the first request has been processed. This
 392  
      * attribute is used by several methods in ApplicationImpl 
 393  
      * to determine whether or not to throw an IllegalStateException
 394  
      * @param facesContext
 395  
      */
 396  
     private void requestProcessed(FacesContext facesContext)
 397  
     {
 398  0
         if(!_firstRequestProcessed)
 399  
         {
 400  
             // The order here is important. First it is necessary to put
 401  
             // the value on application map before change the value here.
 402  
             // If multiple threads reach this point concurrently, the
 403  
             // variable will be written on the application map at the same
 404  
             // time but always with the same value.
 405  0
             facesContext.getExternalContext().getApplicationMap()
 406  
                 .put(FIRST_REQUEST_PROCESSED_PARAM, Boolean.TRUE);
 407  
             
 408  0
             _firstRequestProcessed = true;
 409  
         }        
 410  0
     }
 411  
 }