com.sun.faces.config.ConfigureListener.java Source code

Java tutorial

Introduction

Here is the source code for com.sun.faces.config.ConfigureListener.java

Source

/*
 * $Id: ConfigureListener.java,v 1.25.10.1 2006/04/12 19:32:04 ofung Exp $
 */
/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License). You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the License at
 * https://javaserverfaces.dev.java.net/CDDL.html or
 * legal/CDDLv1.0.txt. 
 * See the License for the specific language governing
 * permission and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at legal/CDDLv1.0.txt.    
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * [Name of File] [ver.__] [Date]
 * 
 * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
 */

package com.sun.faces.config;

import com.sun.faces.RIConstants;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.application.ConfigNavigationCase;
import com.sun.faces.config.beans.ApplicationBean;
import com.sun.faces.config.beans.ComponentBean;
import com.sun.faces.config.beans.ConverterBean;
import com.sun.faces.config.beans.FacesConfigBean;
import com.sun.faces.config.beans.FactoryBean;
import com.sun.faces.config.beans.LifecycleBean;
import com.sun.faces.config.beans.LocaleConfigBean;
import com.sun.faces.config.beans.ManagedBeanBean;
import com.sun.faces.config.beans.NavigationCaseBean;
import com.sun.faces.config.beans.NavigationRuleBean;
import com.sun.faces.config.beans.RenderKitBean;
import com.sun.faces.config.beans.RendererBean;
import com.sun.faces.config.beans.ValidatorBean;
import com.sun.faces.config.rules.FacesConfigRuleSet;
import com.sun.faces.util.Util;
import org.apache.commons.digester.Digester;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.InputSource;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.application.NavigationHandler;
import javax.faces.application.StateManager;
import javax.faces.application.ViewHandler;
import javax.faces.context.ExternalContext;
import javax.faces.el.PropertyResolver;
import javax.faces.el.VariableResolver;
import javax.faces.event.ActionListener;
import javax.faces.event.PhaseListener;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.Renderer;
import javax.faces.webapp.FacesServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

/**
 * <p>Parse all relevant JavaServer Faces configuration resources, and
 * configure the Reference Implementation runtime environment.</p>
 * <p/>
 */
public class ConfigureListener implements ServletContextListener {

    // -------------------------------------------------------- Static Variables

    /**
     * <p><code>ServletContext</code> attribute key.</p>
     */
    protected static String FACES_CONFIG_BEAN_KEY = RIConstants.FACES_PREFIX + "FACES_CONFIG_BEAN";

    /**
     * <p>The path to the RI main configuration file.</p>
     */
    protected static final String JSF_RI_CONFIG = "com/sun/faces/jsf-ri-runtime.xml";

    /**
     * <p>The resource path for faces-config files included in the
     * <code>META-INF</code> directory of JAR files.</p>
     */
    protected static final String META_INF_RESOURCES = "META-INF/faces-config.xml";

    /**
     * <p>The resource path for the faces configuration in the
     * <code>WEB-INF</code> directory of an application.</p>
     */
    protected static final String WEB_INF_RESOURCE = "/WEB-INF/faces-config.xml";

    /**
     * <p>The context initialization parameter that determines whether
     * or not the config files will be validated against their respective
     * DTD.</p>
     */
    protected static final String VALIDATE_XML = RIConstants.FACES_PREFIX + "validateXml";

    /**
     * <p>The context initialization parameter that determines whether
     * or not the RI will attempt to validate that all defined objects
     * can be created.</p>
     */
    protected static final String VERIFY_OBJECTS = RIConstants.FACES_PREFIX + "verifyObjects";

    /**
     * <p>The context initialization parameter that determines whether
     * or not the RI will enable HTML TLV processing.</p>
     */
    protected static final String ENABLE_HTML_TLV = RIConstants.FACES_PREFIX + "enableHtmlTagLibValidator";

    /*
     * The first element is the path, the second is the public ID.
     */
    private static String[][] DTD_INFO = {
            { "/com/sun/faces/web-facesconfig_1_0.dtd",
                    "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" },
            { "/com/sun/faces/web-facesconfig_1_1.dtd",
                    "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" } };

    /**
     * <p>All known factory names.</p>
     */
    private static final String[] FACTORY_NAMES = { FactoryFinder.APPLICATION_FACTORY,
            FactoryFinder.FACES_CONTEXT_FACTORY, FactoryFinder.LIFECYCLE_FACTORY,
            FactoryFinder.RENDER_KIT_FACTORY };

    /**
     * <p>Array of known primitive types.</p>
     */
    private static final Class PRIM_CLASSES_TO_CONVERT[] = { java.lang.Boolean.TYPE, java.lang.Byte.TYPE,
            java.lang.Character.TYPE, java.lang.Double.TYPE, java.lang.Float.TYPE, java.lang.Integer.TYPE,
            java.lang.Long.TYPE, java.lang.Short.TYPE };

    /**
     * <p>Array of known converters for primitive types.</p>
     */
    private static final String CONVERTERS_FOR_PRIMS[] = { "javax.faces.convert.BooleanConverter",
            "javax.faces.convert.ByteConverter", "javax.faces.convert.CharacterConverter",
            "javax.faces.convert.DoubleConverter", "javax.faces.convert.FloatConverter",
            "javax.faces.convert.IntegerConverter", "javax.faces.convert.LongConverter",
            "javax.faces.convert.ShortConverter" };

    /**
     * <p>The set of <code>ClassLoader</code> instances that have
     * already been configured by this <code>ConfigureListener</code>.</p>
     */
    private static Set loaders = new HashSet();

    /**
     * <p>The <code>Log</code> instance for this class.</p>
     */
    private static Log log = LogFactory.getLog(ConfigureListener.class);

    // ------------------------------------------ ServletContextListener Methods

    /**
     * <p>This ivar is used to convey the ServletContext instance to the
     * ApplicationAssociate ctor, which is executed when the Application
     * is instantiated.  Note that this data bridge is only used by the
     * Sun RI ApplicationImpl.  If the user replaces ApplicationFactory,
     * and chooses to decorate the existing Application instance, this
     * data bridge is used.  However, if the user replaces
     * ApplicationFactory and entirely replaces the ApplicationInstance,
     * this data-bridge is not used. </p>
     *
     */

    private static ThreadLocal tlsExternalContext = new ThreadLocal() {
        protected Object initialValue() {
            return (null);
        }
    };

    static ThreadLocal getThreadLocalExternalContext() {
        if (RIConstants.IS_UNIT_TEST_MODE) {
            return tlsExternalContext;
        }
        return null;
    }

    /**
     * <p>During the execution of {@link #contextInitialized}, this
     * method will return the ServletContext instance.</p>
     */

    public static ExternalContext getExternalContextDuringInitialize() {
        return (ExternalContext) tlsExternalContext.get();
    }

    public void contextInitialized(ServletContextEvent sce) {
        // Prepare local variables we will need
        Digester digester = null;
        FacesConfigBean fcb = new FacesConfigBean();
        ServletContext context = sce.getServletContext();

        // store the servletContext instance in Thread local Storage.
        // This enables our Application's ApplicationAssociate to locate
        // it so it can store the ApplicationAssociate in the
        // ServletContext.
        tlsExternalContext.set(new ServletContextAdapter(context));

        // see if we're operating in the unit test environment
        try {
            if (RIConstants.IS_UNIT_TEST_MODE) {
                // if so, put the fcb in the servletContext
                context.setAttribute(FACES_CONFIG_BEAN_KEY, fcb);
            }
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("Can't query for test environment");
            }
        }

        // see if we need to disable our TLValidator
        RIConstants.HTML_TLV_ACTIVE = isFeatureEnabled(context, ENABLE_HTML_TLV);

        URL url = null;
        if (log.isDebugEnabled()) {
            log.debug("contextInitialized(" + context.getServletContextName() + ")");
        }

        // Ensure that we initialize a particular application ony once
        if (initialized()) {
            return;
        }

        // Step 1, configure a Digester instance we can use
        digester = digester(isFeatureEnabled(context, VALIDATE_XML));

        // Step 2, parse the RI configuration resource
        url = Util.getCurrentLoader(this).getResource(JSF_RI_CONFIG);
        parse(digester, url, fcb);

        // Step 3, parse any "/META-INF/faces-config.xml" resources
        Iterator resources;
        try {
            List list = new LinkedList();
            Enumeration items = Util.getCurrentLoader(this).getResources(META_INF_RESOURCES);
            while (items.hasMoreElements()) {
                list.add(0, items.nextElement());
            }
            resources = list.iterator();
        } catch (IOException e) {
            String message = null;
            try {
                message = Util.getExceptionMessageString(Util.CANT_PARSE_FILE_ERROR_MESSAGE_ID,
                        new Object[] { META_INF_RESOURCES });
            } catch (Exception ee) {
                message = "Can't parse configuration file:" + META_INF_RESOURCES;
            }
            log.warn(message, e);
            throw new FacesException(message, e);
        }
        while (resources.hasNext()) {
            url = (URL) resources.next();
            parse(digester, url, fcb);
        }

        // Step 4, parse any context-relative resources specified in
        // the web application deployment descriptor
        String paths = context.getInitParameter(FacesServlet.CONFIG_FILES_ATTR);
        if (paths != null) {
            for (StringTokenizer t = new StringTokenizer(paths.trim(), ","); t.hasMoreTokens();) {

                url = getContextURLForPath(context, t.nextToken().trim());
                if (url != null) {
                    parse(digester, url, fcb);
                }

            }
        }

        // Step 5, parse "/WEB-INF/faces-config.xml" if it exists
        url = getContextURLForPath(context, WEB_INF_RESOURCE);
        if (url != null) {
            parse(digester, url, fcb);
        }

        // Step 6, use the accumulated configuration beans to configure the RI
        try {
            configure(context, fcb);
        } catch (FacesException e) {
            e.printStackTrace();
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            throw new FacesException(e);
        }

        // Step 7, verify that all the configured factories are available
        // and optionall that configured objects can be created
        verifyFactories();
        if (isFeatureEnabled(context, VERIFY_OBJECTS)) {
            verifyObjects(context, fcb);
        }

        tlsExternalContext.set(null);
    }

    public void contextDestroyed(ServletContextEvent sce) {

        ServletContext context = sce.getServletContext();
        if (log.isDebugEnabled()) {
            log.debug("contextDestroyed(" + context.getServletContextName() + ')');
        }

        // Release any allocated application resources
        FactoryFinder.releaseFactories();
        tlsExternalContext.set(new ServletContextAdapter(context));
        ApplicationAssociate.clearInstance((ExternalContext) tlsExternalContext.get());
        tlsExternalContext.set(null);

        // Release the initialization mark on this web application
        release();

    }

    // --------------------------------------------------------- Private Methods

    /**
     * <p>Return the implementation-specific <code>Application</code>
     * instance for this application.  You must <strong>NOT</strong>
     * call this method prior to configuring the appropriate
     * <code>ApplicationFactory</code> class.</p>
     */
    private Application application() {

        ApplicationFactory afactory = (ApplicationFactory) FactoryFinder
                .getFactory(FactoryFinder.APPLICATION_FACTORY);
        return afactory.getApplication();

    }

    /**
     * <p>Configure the JavaServer Faces reference implementation based on
     * the accumulated configuration beans.</p>
     *
     * @param context <code>ServletContext</code> for this web application
     * @param config  <code>FacesConfigBean</code> that is the root of the
     *                tree of configuration information
     */
    protected void configure(ServletContext context, FacesConfigBean config) throws Exception {
        configure(config.getFactory());
        configure(config.getLifecycle());

        configure(config.getApplication());
        configure(config.getComponents());
        configure(config.getConvertersByClass());
        configure(config.getConvertersById());
        configure(config.getManagedBeans());
        configure(config.getNavigationRules());
        configure(config.getRenderKits());
        configure(config.getValidators());

    }

    /**
     * <p>Configure the application objects for this application.</p>
     *
     * @param config   <code>ApplicationBean</code> that contains our
     *                 configuration information
     */
    private void configure(ApplicationBean config) throws Exception {

        if (config == null) {
            return;
        }
        Application application = application();
        Object instance;
        String value;
        String values[];

        // Configure scalar properties

        configure(config.getLocaleConfig());

        value = config.getMessageBundle();
        if (value != null) {
            if (log.isTraceEnabled()) {
                log.trace("setMessageBundle(" + value + ')');
            }
            application.setMessageBundle(value);
        }

        value = config.getDefaultRenderKitId();
        if (value != null) {
            if (log.isTraceEnabled()) {
                log.trace("setDefaultRenderKitId(" + value + ')');
            }
            application.setDefaultRenderKitId(value);
        }

        // Configure chains of handlers

        values = config.getActionListeners();
        if ((values != null) && (values.length > 0)) {
            for (int i = 0; i < values.length; i++) {
                if (log.isTraceEnabled()) {
                    log.trace("setActionListener(" + values[i] + ')');
                }
                instance = Util.createInstance(values[i], ActionListener.class, application.getActionListener());
                if (instance != null) {
                    application.setActionListener((ActionListener) instance);
                }
            }
        }

        values = config.getNavigationHandlers();
        if ((values != null) && (values.length > 0)) {
            for (int i = 0; i < values.length; i++) {
                if (log.isTraceEnabled()) {
                    log.trace("setNavigationHandler(" + values[i] + ')');
                }
                instance = Util.createInstance(values[i], NavigationHandler.class,
                        application.getNavigationHandler());
                if (instance != null) {
                    application.setNavigationHandler((NavigationHandler) instance);
                }
            }
        }

        values = config.getPropertyResolvers();
        if ((values != null) && (values.length > 0)) {
            for (int i = 0; i < values.length; i++) {
                if (log.isTraceEnabled()) {
                    log.trace("setPropertyResolver(" + values[i] + ')');
                }
                instance = Util.createInstance(values[i], PropertyResolver.class,
                        application.getPropertyResolver());
                if (instance != null) {
                    application.setPropertyResolver((PropertyResolver) instance);
                }
            }
        }

        values = config.getStateManagers();
        if ((values != null) && (values.length > 0)) {
            for (int i = 0; i < values.length; i++) {
                if (log.isTraceEnabled()) {
                    log.trace("setStateManager(" + values[i] + ')');
                }
                instance = Util.createInstance(values[i], StateManager.class, application.getStateManager());
                if (instance != null) {
                    application.setStateManager((StateManager) instance);
                }
            }
        }

        values = config.getVariableResolvers();
        if ((values != null) && (values.length > 0)) {
            for (int i = 0; i < values.length; i++) {
                if (log.isTraceEnabled()) {
                    log.trace("setVariableResolver(" + values[i] + ')');
                }
                instance = Util.createInstance(values[i], VariableResolver.class,
                        application.getVariableResolver());
                if (instance != null) {
                    application.setVariableResolver((VariableResolver) instance);
                }
            }
        }

        values = config.getViewHandlers();
        if ((values != null) && (values.length > 0)) {
            for (int i = 0; i < values.length; i++) {
                if (log.isTraceEnabled()) {
                    log.trace("setViewHandler(" + values[i] + ')');
                }
                instance = Util.createInstance(values[i], ViewHandler.class, application.getViewHandler());
                if (instance != null) {
                    application.setViewHandler((ViewHandler) instance);
                }
            }
        }

    }

    /**
     * <p>Configure all registered components.</p>
     *
     * @param config Array of <code>ComponentBean</code> that contains
     *               our configuration information
     */
    private void configure(ComponentBean config[]) throws Exception {

        if (config == null) {
            return;
        }
        Application application = application();

        for (int i = 0; i < config.length; i++) {
            if (log.isTraceEnabled()) {
                log.trace(
                        "addComponent(" + config[i].getComponentType() + ',' + config[i].getComponentClass() + ')');
            }
            application.addComponent(config[i].getComponentType(), config[i].getComponentClass());
        }

    }

    /**
     * <p>Configure all registered converters.</p>
     *
     * @param config Array of <code>ConverterBean</code> that contains
     *               our configuration information
     */
    private void configure(ConverterBean config[]) throws Exception {
        int i = 0, len = 0;
        Application application = application();

        // at a minimum, configure the primitive converters
        for (i = 0, len = PRIM_CLASSES_TO_CONVERT.length; i < len; i++) {
            if (log.isTraceEnabled()) {
                log.trace(
                        "addConverterByClass(" + PRIM_CLASSES_TO_CONVERT[i] + ',' + CONVERTERS_FOR_PRIMS[i] + ')');
            }
            application.addConverter(PRIM_CLASSES_TO_CONVERT[i], CONVERTERS_FOR_PRIMS[i]);
        }

        if (config == null) {
            return;
        }

        for (i = 0, len = config.length; i < len; i++) {
            if (config[i].getConverterId() != null) {
                if (log.isTraceEnabled()) {
                    log.trace("addConverterById(" + config[i].getConverterId() + ',' + config[i].getConverterClass()
                            + ')');
                }
                application.addConverter(config[i].getConverterId(), config[i].getConverterClass());
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("addConverterByClass(" + config[i].getConverterForClass() + ','
                            + config[i].getConverterClass() + ')');
                }
                Class clazz = Util.getCurrentLoader(this).loadClass(config[i].getConverterForClass());
                application.addConverter(clazz, config[i].getConverterClass());
            }
        }

    }

    /**
     * <p>Configure the object factories for this application.</p>
     *
     * @param config <code>FactoryBean</code> that contains our
     *               configuration information
     */
    private void configure(FactoryBean config) throws Exception {

        if (config == null) {
            return;
        }
        Iterator iter = null;
        String value;

        iter = config.getApplicationFactories().iterator();
        while (iter.hasNext()) {
            value = (String) iter.next();
            if (value != null) {
                if (log.isTraceEnabled()) {
                    log.trace("setApplicationFactory(" + value + ')');
                }
                FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY, value);
            }
        }

        iter = config.getFacesContextFactories().iterator();
        while (iter.hasNext()) {
            value = (String) iter.next();
            if (value != null) {
                if (log.isTraceEnabled()) {
                    log.trace("setFacesContextFactory(" + value + ')');
                }
                FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY, value);
            }
        }

        iter = config.getLifecycleFactories().iterator();
        while (iter.hasNext()) {
            value = (String) iter.next();
            if (value != null) {
                if (log.isTraceEnabled()) {
                    log.trace("setLifecycleFactory(" + value + ')');
                }
                FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY, value);
            }
        }

        iter = config.getRenderKitFactories().iterator();
        while (iter.hasNext()) {
            value = (String) iter.next();
            if (value != null) {
                if (log.isTraceEnabled()) {
                    log.trace("setRenderKitFactory(" + value + ')');
                }
                FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY, value);
            }
        }

    }

    /**
     * <p>Configure the lifecycle listeners for this application.</p>
     *
     * @param config <code>LifecycleBean</code> that contains our
     *               configuration information
     */
    private void configure(LifecycleBean config) throws Exception {

        if (config == null) {
            return;
        }
        String listeners[] = config.getPhaseListeners();
        LifecycleFactory factory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
        Lifecycle lifecycle = factory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
        for (int i = 0; i < listeners.length; i++) {
            if (log.isTraceEnabled()) {
                log.trace("addPhaseListener(" + listeners[i] + ')');
            }
            Class clazz = Util.loadClass(listeners[i], this);
            lifecycle.addPhaseListener((PhaseListener) clazz.newInstance());
        }

    }

    /**
     * <p>Configure the locale support for this application.</p>
     *
     * @param config <code>LocaleConfigBean</code> that contains our
     *               configuration information
     */
    private void configure(LocaleConfigBean config) throws Exception {

        if (config == null) {
            return;
        }
        Application application = application();
        String value;
        String values[];

        value = config.getDefaultLocale();
        if (value != null) {
            if (log.isTraceEnabled()) {
                log.trace("setDefaultLocale(" + value + ')');
            }
            application.setDefaultLocale(Util.getLocaleFromString(value));
        }

        values = config.getSupportedLocales();
        if ((values != null) && (values.length > 0)) {
            List locales = new ArrayList();
            for (int i = 0; i < values.length; i++) {
                if (log.isTraceEnabled()) {
                    log.trace("addSupportedLocale(" + values[i] + ')');
                }
                locales.add(Util.getLocaleFromString(values[i]));
            }
            application.setSupportedLocales(locales);
        }

    }

    /**
     * <p>Configure all registered managed beans.</p>
     *
     * @param config Array of <code>ManagedBeanBean</code> that contains
     *               our configuration information
     */
    // PENDING - the code below is a start at converting new-style config beans
    // back to old style ones so we don't have to modify the functional code.
    // It is not clear that this is the lower-effort choice, however.
    private void configure(ManagedBeanBean config[]) throws Exception {
        if (config == null) {
            return;
        }
        ApplicationAssociate associate = ApplicationAssociate.getInstance(getExternalContextDuringInitialize());

        if (null == associate) {
            return;
        }

        for (int i = 0; i < config.length; i++) {
            if (log.isTraceEnabled()) {
                log.trace("addManagedBean(" + config[i].getManagedBeanName() + ',' + config[i].getManagedBeanClass()
                        + ')');
            }
            ManagedBeanFactory mbf = new ManagedBeanFactory(config[i]);
            associate.addManagedBeanFactory(config[i].getManagedBeanName(), mbf);
        }
    }

    /**
     * <p>Configure all registered navigation rules.</p>
     *
     * @param config Array of <code>NavigationRuleBean</code> that contains
     *               our configuration information
     */
    private void configure(NavigationRuleBean config[]) {

        if (config == null) {
            return;
        }
        ApplicationAssociate associate = ApplicationAssociate.getInstance(getExternalContextDuringInitialize());

        if (null == associate) {
            return;
        }

        for (int i = 0; i < config.length; i++) {
            if (log.isTraceEnabled()) {
                log.trace("addNavigationRule(" + config[i].getFromViewId() + ')');
            }
            NavigationCaseBean ncb[] = config[i].getNavigationCases();
            for (int j = 0; j < ncb.length; j++) {
                if (log.isTraceEnabled()) {
                    log.trace("addNavigationCase(" + ncb[j].getFromAction() + ',' + ncb[j].getFromOutcome() + ','
                            + ncb[j].isRedirect() + ',' + ncb[j].getToViewId() + ')');
                }
                ConfigNavigationCase cnc = new ConfigNavigationCase();
                if (config[i].getFromViewId() == null) {
                    cnc.setFromViewId("*");
                } else {
                    cnc.setFromViewId(config[i].getFromViewId());
                }
                cnc.setFromAction(ncb[j].getFromAction());
                String fromAction = ncb[j].getFromAction();
                if (fromAction == null) {
                    fromAction = "-";
                }
                cnc.setFromOutcome(ncb[j].getFromOutcome());
                String fromOutcome = ncb[j].getFromOutcome();
                if (fromOutcome == null) {
                    fromOutcome = "-";
                }
                cnc.setToViewId(ncb[j].getToViewId());
                String toViewId = ncb[j].getToViewId();
                if (toViewId == null) {
                    toViewId = "-";
                }
                cnc.setKey(cnc.getFromViewId() + fromAction + fromOutcome);
                if (ncb[j].isRedirect()) {
                    cnc.setRedirect("true");
                } else {
                    cnc.setRedirect(null);
                }
                associate.addNavigationCase(cnc);
            }
        }

    }

    /**
     * <p>Configure all registered renderers for this renderkit.</p>
     *
     * @param config Array of <code>RendererBean</code> that contains
     *               our configuration information
     * @param rk     RenderKit to be configured
     */
    private void configure(RendererBean config[], RenderKit rk) throws Exception {

        if (config == null) {
            return;
        }

        for (int i = 0; i < config.length; i++) {
            if (log.isTraceEnabled()) {
                log.trace("addRenderer(" + config[i].getComponentFamily() + ',' + config[i].getRendererType() + ','
                        + config[i].getRendererClass() + ')');
            }
            Renderer r = (Renderer) Util.loadClass(config[i].getRendererClass(), this).newInstance();
            rk.addRenderer(config[i].getComponentFamily(), config[i].getRendererType(), r);
        }

    }

    /**
     * <p>Configure all registered renderKits.</p>
     *
     * @param config Array of <code>RenderKitBean</code> that contains
     *               our configuration information
     */
    private void configure(RenderKitBean config[]) throws Exception {

        if (config == null) {
            return;
        }
        RenderKitFactory rkFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);

        for (int i = 0; i < config.length; i++) {
            RenderKit rk = rkFactory.getRenderKit(null, config[i].getRenderKitId());
            if (rk == null) {
                if (log.isTraceEnabled()) {
                    log.trace("createRenderKit(" + config[i].getRenderKitId() + ',' + config[i].getRenderKitClass()
                            + ')');
                }
                if (config[i].getRenderKitClass() == null) {
                    throw new IllegalArgumentException// PENDING - i18n
                    ("No renderKitClass for renderKit " + config[i].getRenderKitId());
                }
                rk = (RenderKit) Util.loadClass(config[i].getRenderKitClass(), this).newInstance();
                rkFactory.addRenderKit(config[i].getRenderKitId(), rk);
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("getRenderKit(" + config[i].getRenderKitId() + ')');
                }
            }
            configure(config[i].getRenderers(), rk);
        }

    }

    /**
     * <p>Configure all registered validators.</p>
     *
     * @param config Array of <code>ValidatorBean</code> that contains
     *               our configuration information
     */
    private void configure(ValidatorBean config[]) throws Exception {

        if (config == null) {
            return;
        }
        Application application = application();

        for (int i = 0; i < config.length; i++) {
            if (log.isTraceEnabled()) {
                log.trace("addValidator(" + config[i].getValidatorId() + ',' + config[i].getValidatorClass() + ')');
            }
            application.addValidator(config[i].getValidatorId(), config[i].getValidatorClass());
        }

    }

    /**
     * <p>Configure and return a <code>Digester</code> instance suitable for
     * parsing the runtime configuration information we need.</p>
     *
     * @param validateXml if true, validation is turned on during parsing.
     */
    protected Digester digester(boolean validateXml) {
        Digester digester = new Digester();

        // Configure basic properties
        digester.setNamespaceAware(false);
        digester.setUseContextClassLoader(true);
        digester.setValidating(validateXml);

        // Configure parsing rules
        // PENDING - Read from file?
        digester.addRuleSet(new FacesConfigRuleSet(false, false, true));

        // Register known entities
        for (int i = 0; i < DTD_INFO.length; i++) {
            URL url = this.getClass().getResource(DTD_INFO[i][0]);
            if (url != null) {
                digester.register(DTD_INFO[i][1], url.toString());
            } else {
                throw new FacesException(Util.getExceptionMessageString(Util.NO_DTD_FOUND_ERROR_ID,
                        new Object[] { DTD_INFO[i][1], DTD_INFO[i][0] }));
            }
        }

        // Push an initial FacesConfigBean onto the stack
        digester.push(new FacesConfigBean());

        return (digester);

    }

    /**
     * <p>Verify that all of the required factory objects are available.</p>
     *
     * @throws FacesException if a factory cannot be created
     */
    private void verifyFactories() throws FacesException {

        for (int i = 0, len = FACTORY_NAMES.length; i < len; i++) {
            try {
                FactoryFinder.getFactory(FACTORY_NAMES[i]);
            } catch (Exception e) {
                throw new FacesException(e);
            }
        }

    }

    /**
     * <p>Return <code>true</code> if this web application has already
     * been initialized.  If it has not been initialized, also record
     * the initialization of this application until removed by a call to
     * <code>release()</code>.</p>
     */
    private boolean initialized() {

        // Initialize at most once per web application class loader
        ClassLoader cl = Util.getCurrentLoader(this);
        synchronized (loaders) {
            if (!loaders.contains(cl)) {
                loaders.add(cl);
                if (log.isTraceEnabled()) {
                    log.trace("Initializing this webapp");
                }
                return false;
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Listener already completed for this webapp");
                }
                return true;
            }
        }

    }

    /**
     * <p>Verify that all of the application-defined objects that have been
     * configured can be successfully instantiated.</p>
     *
     * @param context <code>ServletContext</code> instance for this application
     * @param fcb     <code>FacesConfigBean</code> containing the
     *                configuration information
     * @throws FacesException if an application-defined object cannot
     *                        be instantiated
     */
    private void verifyObjects(ServletContext context, FacesConfigBean fcb) throws FacesException {

        if (log.isDebugEnabled()) {
            log.debug("Verifying application objects");
        }

        ApplicationFactory af = (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
        Application app = af.getApplication();
        RenderKitFactory rkf = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
        boolean success = true;

        // Check components
        ComponentBean comp[] = fcb.getComponents();
        for (int i = 0, len = comp.length; i < len; i++) {
            try {
                app.createComponent(comp[i].getComponentType());
            } catch (Exception e) {
                context.log(comp[i].getComponentClass(), e);
                success = false;
            }
        }

        // Check converters
        ConverterBean conv1[] = fcb.getConvertersByClass();
        Class clazz;
        for (int i = 0, len = conv1.length; i < len; i++) {
            try {
                clazz = Util.loadClass(conv1[i].getConverterForClass(), this);
                app.createConverter(clazz);
            } catch (Exception e) {
                context.log(conv1[i].getConverterForClass(), e);
                clazz = null;
                success = false;
            }
            try {
                app.createConverter(clazz);
            } catch (Exception e) {
                context.log(conv1[i].getConverterClass(), e);
                success = false;
            }
        }
        ConverterBean conv2[] = fcb.getConvertersById();
        for (int i = 0, len = conv2.length; i < len; i++) {
            try {
                app.createConverter(conv2[i].getConverterId());
            } catch (Exception e) {
                context.log(conv2[i].getConverterClass());
                success = false;
            }
        }

        // Check renderers
        RenderKitBean rkb[] = fcb.getRenderKits();
        RenderKit rk;
        for (int i = 0, len = rkb.length; i < len; i++) {
            try {
                rk = rkf.getRenderKit(null, rkb[i].getRenderKitId());
                RendererBean rb[] = rkb[i].getRenderers();
                for (int j = 0, len2 = rb.length; j < len2; j++) {
                    try {
                        rk.getRenderer(rb[j].getComponentFamily(), rb[j].getRendererType());
                    } catch (Exception e) {
                        context.log(rb[j].getRendererClass(), e);
                        success = false;
                    }
                }
            } catch (Exception e) {
                context.log(rkb[i].getRenderKitId());
                success = false;
            }
        }

        // Check validators
        ValidatorBean val[] = fcb.getValidators();
        for (int i = 0, len = val.length; i < len; i++) {
            try {
                app.createValidator(val[i].getValidatorId());
            } catch (Exception e) {
                context.log(val[i].getValidatorClass(), e);
                success = false;
            }
        }

        // Throw an exception on any failures
        if (!success) {
            String message;
            try {
                message = Util.getExceptionMessageString(Util.OBJECT_CREATION_ERROR_ID, new Object[] {});
            } catch (Exception ee) {
                message = "One or more configured application objects "
                        + "cannot be created.  See your web application logs " + "for details.";
            }
            if (log.isErrorEnabled()) {
                log.error(message);
            }
            throw new FacesException(message);
        } else {
            if (log.isInfoEnabled()) {
                log.info("Application object verification completed successfully");
            }
        }

    }

    /**
     * <p>Parse the configuration resource at the specified URL, using
     * the specified <code>Digester</code> instance.</p>
     *
     * @param digester Digester to use for parsing
     * @param url      URL of the configuration resource to be parsed
     * @param fcb      FacesConfigBean to accumulate results
     */
    protected void parse(Digester digester, URL url, FacesConfigBean fcb) {

        if (log.isDebugEnabled()) {
            log.debug("parse(" + url.toExternalForm() + ')');
        }

        URLConnection conn = null;
        InputStream stream = null;
        InputSource source = null;
        try {
            conn = url.openConnection();
            conn.setUseCaches(false);
            stream = conn.getInputStream();
            source = new InputSource(url.toExternalForm());
            source.setByteStream(stream);
            digester.clear();
            digester.push(fcb);
            digester.parse(source);
            stream.close();
            stream = null;
        } catch (Exception e) {
            String message = null;
            try {
                message = Util.getExceptionMessageString(Util.CANT_PARSE_FILE_ERROR_MESSAGE_ID,
                        new Object[] { url.toExternalForm() });
            } catch (Exception ee) {
                message = "Can't parse configuration file:" + url.toExternalForm();
            }
            if (log.isErrorEnabled()) {
                log.error(message, e);
            }
            throw new FacesException(message, e);
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (Exception e) {
                    ;
                }
            }
            stream = null;
        }

    }

    /**
     * <p>Determines if a particular feature, configured via the web
     * deployment descriptor as a <code>true/false</code> value, is
     * enabled or not.</p>
     * @param context the <code>ServletContext</code> of the application
     * @param paramName the name of the context init paramName to check
     *
     * @return <code>true</code> if the feature in question is enabled, otherwise
     *  <code>false</code>
     */
    protected boolean isFeatureEnabled(ServletContext context, String paramName) {
        String paramValue = context.getInitParameter(paramName);
        if (paramValue != null) {
            paramValue = paramValue.trim();
            if (!(paramValue.equals("true")) && !(paramValue.equals("false"))) {

                if (log.isWarnEnabled()) {
                    log.warn(Util.getExceptionMessageString(Util.INVALID_INIT_PARAM_ERROR_MESSAGE_ID,
                            new Object[] { paramValue, "validateXml" }));
                }
            }
        }

        return Boolean.valueOf(paramValue).booleanValue();
    }

    /**
     * <p>Return the URL for the given path.</p>
     * @param context the <code>ServletContext</code> of the current application
     * @param path the resource path
     * @return the URL for the given resource or <code>null</code>
     */
    private URL getContextURLForPath(ServletContext context, String path) {
        try {
            return context.getResource(path);
        } catch (MalformedURLException mue) {
            throw new FacesException(mue);
        }
    }

    /**
     * <p>Release the mark that this web application has been initialized.</p>
     */
    private void release() {

        ClassLoader cl = Util.getCurrentLoader(this);
        synchronized (loaders) {
            loaders.remove(cl);
        }

    }

    public class ServletContextAdapter extends ExternalContext {

        private ServletContext servletContext = null;
        private ApplicationMap applicationMap = null;

        public ServletContextAdapter(ServletContext sc) {
            this.servletContext = sc;
        }

        public void dispatch(String path) throws IOException {
        }

        public String encodeActionURL(String url) {
            return null;
        }

        public String encodeNamespace(String name) {
            return null;
        }

        public String encodeResourceURL(String url) {
            return null;
        }

        public Map getApplicationMap() {
            if (applicationMap == null) {
                applicationMap = new ApplicationMap(servletContext);
            }
            return applicationMap;
        }

        public String getAuthType() {
            return null;
        }

        public Object getContext() {
            return servletContext;
        }

        public String getInitParameter(String name) {
            return null;
        }

        public Map getInitParameterMap() {
            return null;
        }

        public String getRemoteUser() {
            return null;
        }

        public Object getRequest() {
            return null;
        }

        public String getRequestContextPath() {
            return null;
        }

        public Map getRequestCookieMap() {
            return null;
        }

        public Map getRequestHeaderMap() {
            return null;
        }

        public Map getRequestHeaderValuesMap() {
            return null;
        }

        public Locale getRequestLocale() {
            return null;
        }

        public Iterator getRequestLocales() {
            return null;
        }

        public Map getRequestMap() {
            return null;
        }

        public Map getRequestParameterMap() {
            return null;
        }

        public Iterator getRequestParameterNames() {
            return null;
        }

        public Map getRequestParameterValuesMap() {
            return null;
        }

        public String getRequestPathInfo() {
            return null;
        }

        public String getRequestServletPath() {
            return null;
        }

        public URL getResource(String path) throws MalformedURLException {
            return null;
        }

        public InputStream getResourceAsStream(String path) {
            return null;
        }

        public Set getResourcePaths(String path) {
            return null;
        }

        public Object getResponse() {
            return null;
        }

        public Object getSession(boolean create) {
            return null;
        }

        public Map getSessionMap() {
            return null;
        }

        public java.security.Principal getUserPrincipal() {
            return null;
        }

        public boolean isUserInRole(String role) {
            return false;
        }

        public void log(String message) {
        }

        public void log(String message, Throwable exception) {
        }

        public void redirect(String url) throws IOException {
        }

    }

    class ApplicationMap extends java.util.AbstractMap {

        private ServletContext servletContext = null;

        ApplicationMap(ServletContext servletContext) {
            this.servletContext = servletContext;
        }

        public Object get(Object key) {
            if (key == null) {
                throw new NullPointerException();
            }
            return servletContext.getAttribute(key.toString());
        }

        public Object put(Object key, Object value) {
            if (key == null) {
                throw new NullPointerException();
            }
            String keyString = key.toString();
            Object result = servletContext.getAttribute(keyString);
            servletContext.setAttribute(keyString, value);
            return (result);
        }

        public Object remove(Object key) {
            if (key == null) {
                return null;
            }
            String keyString = key.toString();
            Object result = servletContext.getAttribute(keyString);
            servletContext.removeAttribute(keyString);
            return (result);
        }

        public Set entrySet() {
            throw new UnsupportedOperationException();
        }

        public boolean equals(Object obj) {
            if (obj == null || !(obj instanceof ApplicationMap))
                return false;
            return super.equals(obj);
        }

        public void clear() {
            throw new UnsupportedOperationException();
        }

        public void putAll(Map t) {
            throw new UnsupportedOperationException();
        }

    } // END ApplicationMap

}