All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.lifecycle.LifecycleImpl Maven / Gradle / Ivy

Go to download

The private implementation classes of the Apache MyFaces Core JSF-2.0 Implementation

There is a newer version: 4.1.0-RC2
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.myfaces.lifecycle;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.FacesException;
import javax.faces.application.ProjectStage;
import javax.faces.context.FacesContext;
import javax.faces.context.Flash;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.lifecycle.Lifecycle;

import org.apache.myfaces.config.FacesConfigurator;
import org.apache.myfaces.renderkit.ErrorPageWriter;
import org.apache.myfaces.shared.util.ClassUtils;
import org.apache.myfaces.shared_impl.webapp.webxml.WebXml;
import org.apache.myfaces.util.DebugUtils;

/**
 * Implements the lifecycle as described in Spec. 1.0 PFD Chapter 2
 * 
 * @author Manfred Geiler (latest modification by $Author: lu4242 $)
 * @author Nikolay Petrov
 * @version $Revision: 1151677 $ $Date: 2011-07-27 19:03:59 -0500 (Wed, 27 Jul 2011) $
 */
public class LifecycleImpl extends Lifecycle
{
    //private static final Log log = LogFactory.getLog(LifecycleImpl.class);
    private static final Logger log = Logger.getLogger(LifecycleImpl.class.getName());
    
    /**
     * Boolean.TRUE is stored under this key in the application map if
     * the first request has been processed.
     */
    public static final String FIRST_REQUEST_PROCESSED_PARAM = "org.apache.myfaces.lifecycle.first.request.processed";
    
    private final PhaseExecutor[] lifecycleExecutors;
    private final PhaseExecutor renderExecutor;

    /**
     * Initially, for ensure thread safety we used synchronization blocks and a cached 
     * _phaseListenerArray and that works. The intention is ensure atomicity between
     * _phaseListenerList and _phaseListenerArray, but thinking more about it use
     * CopyOnWriteArrayList and do not use _phaseListenerArray is a lot better. 
     * 
     * Most times, we have few instances of PhaseListener registered, so the advantage of 
     * use _phaseListenerArray is overcome by do not have a synchronization block on getPhaseListeners().
     * Additionally, it is more often to perform traversals than insertions/removals and 
     * we can expect only 2 calls for getPhaseListeners() per request (so only two copy 
     * operations of a very small list).
     */
    private final List _phaseListenerList = new CopyOnWriteArrayList(); // new ArrayList();

    /**
     * This variable should be marked as volatile to ensure all threads can see it
     * after the first request is processed. Note that LifecycleImpl instance could be
     * shared by multiple requests at the same time, so this is relevant to prevent
     * multiple updates to FIRST_REQUEST_PROCESSED_PARAM. Really since the value
     * only changes from false to true, have a racy single check here does not harm, but
     * note in this case the semantic of the variable must be preserved.
     */
    private volatile boolean _firstRequestProcessed = false;
    /**
     * Lazy cache for returning _phaseListenerList as an Array.
     * 
     * Replaced by _phaseListenerList CopyOnWriteArrayList
     */
    //private PhaseListener[] _phaseListenerArray = null;

    public LifecycleImpl()
    {
        // hide from public access
        lifecycleExecutors = new PhaseExecutor[] { new RestoreViewExecutor(), new ApplyRequestValuesExecutor(),
                new ProcessValidationsExecutor(), new UpdateModelValuesExecutor(), new InvokeApplicationExecutor() };

        renderExecutor = new RenderResponseExecutor();
    }

    @Override
    public void execute(FacesContext facesContext) throws FacesException
    {
        //try
        //{
            // check for updates of web.xml and faces-config descriptors 
            // only if project state is not production
            if(!facesContext.isProjectStage(ProjectStage.Production))
            {
                WebXml.update(facesContext.getExternalContext());
                new FacesConfigurator(facesContext.getExternalContext()).update();
            }
            
            PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, getPhaseListeners());
            for (PhaseExecutor executor : lifecycleExecutors)
            {
                if (executePhase(facesContext, executor, phaseListenerMgr))
                {
                    return;
                }
            }
        //}
        //catch (Throwable ex)
        //{
            // handle the Throwable accordingly. Maybe generate an error page.
            //ErrorPageWriter.handleThrowable(facesContext, ex);
        //}
    }

    private boolean executePhase(FacesContext context, PhaseExecutor executor, PhaseListenerManager phaseListenerMgr)
        throws FacesException
    {
        boolean skipFurtherProcessing = false;

        if (log.isLoggable(Level.FINEST))
        {
            log.finest("entering " + executor.getPhase() + " in " + LifecycleImpl.class.getName());
        }

        PhaseId currentPhaseId = executor.getPhase();
        Flash flash = context.getExternalContext().getFlash();

        try
        {
            /* 
             * Specification, section 2.2
             * The default request lifecycle processing implementation must ensure that the currentPhaseId property 
             * of the FacesContext instance for this request is set with the proper PhaseId constant for the current 
             * phase as the first instruction at the beginning of each phase
             */
            context.setCurrentPhaseId(currentPhaseId);
            
            flash.doPrePhaseActions(context);
            
            // let the PhaseExecutor do some pre-phase actions
            executor.doPrePhaseActions(context);

            phaseListenerMgr.informPhaseListenersBefore(currentPhaseId);

            if (isResponseComplete(context, currentPhaseId, true))
            {
                // have to return right away
                return true;
            }
            if (shouldRenderResponse(context, currentPhaseId, true))
            {
                skipFurtherProcessing = true;
            }

            if (executor.execute(context))
            {
                return true;
            }
        }
        
        catch (Throwable e) {
            // JSF 2.0: publish the executor's exception (if any).
            
            publishException (e, currentPhaseId, context);
        }
        
        finally
        {
            phaseListenerMgr.informPhaseListenersAfter(currentPhaseId);
            
            flash.doPostPhaseActions(context);
            
        }
        
        context.getExceptionHandler().handle();
        
        if (isResponseComplete(context, currentPhaseId, false) || shouldRenderResponse(context, currentPhaseId, false))
        {
            // since this phase is completed we don't need to return right away even if the response is completed
            skipFurtherProcessing = true;
        }

        if (!skipFurtherProcessing && log.isLoggable(Level.FINEST))
        {
            log.finest("exiting " + executor.getPhase() + " in " + LifecycleImpl.class.getName());
        }

        return skipFurtherProcessing;
    }

    @Override
    public void render(FacesContext facesContext) throws FacesException
    {
        //try
        //{
            // if the response is complete we should not be invoking the phase listeners
            if (isResponseComplete(facesContext, renderExecutor.getPhase(), true))
            {
                return;
            }
            if (log.isLoggable(Level.FINEST))
                log.finest("entering " + renderExecutor.getPhase() + " in " + LifecycleImpl.class.getName());
    
            PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this, facesContext, getPhaseListeners());
            Flash flash = facesContext.getExternalContext().getFlash();
            
            try
            {
                facesContext.setCurrentPhaseId(renderExecutor.getPhase());
                
                flash.doPrePhaseActions(facesContext);
                
                // let the PhaseExecutor do some pre-phase actions
                renderExecutor.doPrePhaseActions(facesContext);
                
                phaseListenerMgr.informPhaseListenersBefore(renderExecutor.getPhase());
                // also possible that one of the listeners completed the response
                if (isResponseComplete(facesContext, renderExecutor.getPhase(), true))
                {
                    return;
                }
                
                renderExecutor.execute(facesContext);
            }
            
            catch (Throwable e) {
                // JSF 2.0: publish the executor's exception (if any).
                
                publishException (e, renderExecutor.getPhase(), facesContext);
            }
            
            finally
            {
                phaseListenerMgr.informPhaseListenersAfter(renderExecutor.getPhase());
                flash.doPostPhaseActions(facesContext);
                
                // publish a field in the application map to indicate
                // that the first request has been processed
                requestProcessed(facesContext);
            }
            
            facesContext.getExceptionHandler().handle();
            
            if (log.isLoggable(Level.FINEST))
            {
                // Note: DebugUtils Logger must also be in trace level
                DebugUtils.traceView("View after rendering");
            }
    
            if (log.isLoggable(Level.FINEST))
            {
                log.finest("exiting " + renderExecutor.getPhase() + " in " + LifecycleImpl.class.getName());
            }
        //}
        //catch (Throwable ex)
        //{
            // handle the Throwable accordingly. Maybe generate an error page.
            //ErrorPageWriter.handleThrowable(facesContext, ex);
        //}
    }

    private boolean isResponseComplete(FacesContext facesContext, PhaseId phase, boolean before)
    {
        boolean flag = false;
        if (facesContext.getResponseComplete())
        {
            if (log.isLoggable(Level.FINE))
            {
                log.fine("exiting from lifecycle.execute in " + phase
                        + " because getResponseComplete is true from one of the " + (before ? "before" : "after")
                        + " listeners");
            }
            flag = true;
        }
        return flag;
    }

    private boolean shouldRenderResponse(FacesContext facesContext, PhaseId phase, boolean before)
    {
        boolean flag = false;
        if (facesContext.getRenderResponse())
        {
            if (log.isLoggable(Level.FINE))
            {
                log.fine("exiting from lifecycle.execute in " + phase
                        + " because getRenderResponse is true from one of the " + (before ? "before" : "after")
                        + " listeners");
            }
            flag = true;
        }
        return flag;
    }

    @Override
    public void addPhaseListener(PhaseListener phaseListener)
    {
        if (phaseListener == null)
        {
            throw new NullPointerException("PhaseListener must not be null.");
        }
        //synchronized (_phaseListenerList)
        //{
            _phaseListenerList.add(phaseListener);
            //_phaseListenerArray = null; // reset lazy cache array
        //}
    }

    @Override
    public void removePhaseListener(PhaseListener phaseListener)
    {
        if (phaseListener == null)
        {
            throw new NullPointerException("PhaseListener must not be null.");
        }
        //synchronized (_phaseListenerList)
        //{
            _phaseListenerList.remove(phaseListener);
            //_phaseListenerArray = null; // reset lazy cache array
        //}
    }

    @Override
    public PhaseListener[] getPhaseListeners()
    {
        //synchronized (_phaseListenerList)
        //{
            // (re)build lazy cache array if necessary
            //if (_phaseListenerArray == null)
            //{
            //    _phaseListenerArray = _phaseListenerList.toArray(new PhaseListener[_phaseListenerList.size()]);
            //}
            //return _phaseListenerArray;
        //}
        return _phaseListenerList.toArray(new PhaseListener[_phaseListenerList.size()]);
    }
    
    private void publishException (Throwable e, PhaseId phaseId, FacesContext facesContext)
    {
        ExceptionQueuedEventContext context = new ExceptionQueuedEventContext (facesContext, e, null, phaseId);
        
        facesContext.getApplication().publishEvent (facesContext, ExceptionQueuedEvent.class, context);
    }
    
    /**
     * This method places an attribute on the application map to 
     * indicate that the first request has been processed. This
     * attribute is used by several methods in ApplicationImpl 
     * to determine whether or not to throw an IllegalStateException
     * @param facesContext
     */
    private void requestProcessed(FacesContext facesContext)
    {
        if(!_firstRequestProcessed)
        {
            // The order here is important. First it is necessary to put
            // the value on application map before change the value here.
            // If multiple threads reach this point concurrently, the
            // variable will be written on the application map at the same
            // time but always with the same value.
            facesContext.getExternalContext().getApplicationMap()
                .put(FIRST_REQUEST_PROCESSED_PARAM, Boolean.TRUE);
            
            _firstRequestProcessed = true;
        }        
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy