Package org.apache.myfaces.lifecycle

Source Code of org.apache.myfaces.lifecycle.LifecycleImpl

/*
* 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<PhaseListener> _phaseListenerList = new CopyOnWriteArrayList<PhaseListener>(); // 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;
        }       
    }
}
TOP

Related Classes of org.apache.myfaces.lifecycle.LifecycleImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.