com.ocpsoft.pretty.faces.el.resolver.FacesConfigBeanNameResolver.java Source code

Java tutorial

Introduction

Here is the source code for com.ocpsoft.pretty.faces.el.resolver.FacesConfigBeanNameResolver.java

Source

/*
 * Copyright 2010 Lincoln Baxter, III
 * 
 * Licensed 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 com.ocpsoft.pretty.faces.el.resolver;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.faces.webapp.FacesServlet;
import javax.servlet.ServletContext;

import org.apache.commons.digester.Digester;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;

import com.ocpsoft.pretty.faces.spi.ELBeanNameResolver;
import com.ocpsoft.pretty.faces.util.EmptyEntityResolver;

/**
 * 
 * <p>
 * Implementation of {@link ELBeanNameResolver} handling beans configured via the
 * standard XML configuration mechanism.
 * </p>
 * 
 * <p>
 * This resolver handles beans specified in faces-config.xml files. The resolver
 * will pick up the default configuration file <code>/WEB-INF/faces-config.xml</code>, 
 * all <code>/META-INF/faces-config.xml</code> found by the supplied {@link ClassLoader} 
 * and all files specified via the <code>javax.faces.CONFIG_FILES</code> init parameter.
 * </p>
 * 
 * @author Christian Kaltepoth
 * 
 */
public class FacesConfigBeanNameResolver implements ELBeanNameResolver {

    private final static Log log = LogFactory.getLog(FacesConfigBeanNameResolver.class);

    /**
     * Name of default <code>faces-config.xml</code> loaded via
     * {@link ServletContext#getResource(String)}
     */
    private final static String WEB_INF_FACES_CONFIG_XML = "/WEB-INF/faces-config.xml";

    /**
     * Name of <code>faces-config.xml</code> loaded via
     * {@link ClassLoader#getResource(String)}
     */
    private final static String META_INF_FACES_CONFIG_XML = "META-INF/faces-config.xml";

    /**
     * Lookup map: FQCN -> bean name
     */
    private Map<String, String> beanNameMap = new HashMap<String, String>();

    /**
     * Initialization procedure. Reads all faces-config.xml files and registers
     * the beans found.
     */
    public boolean init(ServletContext servletContext, ClassLoader classLoader) {

        // list of beans found
        List<FacesConfigEntry> facesConfigEntries = new ArrayList<FacesConfigEntry>();

        // get the list of faces configuration files to process
        Set<URL> facesConfigs = getFacesConfigFiles(servletContext, classLoader);

        // process all configuration files
        for (URL url : facesConfigs) {
            processFacesConfig(url, facesConfigEntries);
        }

        // Create bean name lookup map from all entries found
        for (FacesConfigEntry entry : facesConfigEntries) {
            beanNameMap.put(entry.getBeanClass(), entry.getName());
        }

        // debug statement containing number of classes found
        if (log.isDebugEnabled()) {
            log.debug("Found " + beanNameMap.size() + " bean names in faces configuration.");
        }

        // we will always enable this resolver
        return true;
    }

    /**
     * Returns a set of faces-config.xml files. This set includes the default
     * configuration file, all additional files mentioned via the
     * <code>javax.faces.CONFIG_FILES</code> init parameter and all files found
     * in META-INF folders.
     * 
     * @param servletContext
     *           The ServletContext
     * @param classLoader
     *           The classloader used to find files in META-INF directories
     * @return A set of URLs
     */
    private Set<URL> getFacesConfigFiles(ServletContext servletContext, ClassLoader classLoader) {

        // set of URLs to process
        Set<URL> result = new HashSet<URL>();

        try {

            // get default faces-config.xml
            URL defaultFacesConfig = servletContext.getResource(WEB_INF_FACES_CONFIG_XML);
            if (defaultFacesConfig != null) {
                result.add(defaultFacesConfig);
            }

            // get additional configuration files from init parameter
            result.addAll(getConfigFilesFromInitParameter(servletContext));

            // Find configuration files META-INF directories
            try {
                Enumeration<URL> resources = classLoader.getResources(META_INF_FACES_CONFIG_XML);
                while (resources.hasMoreElements()) {
                    result.add(resources.nextElement());
                }
            } catch (IOException e) {
                log.error("Failed to load faces-config.xml files from META-INF directories", e);
            }

        } catch (MalformedURLException e) {
            // should not happen, because URLs are hard-coded
            throw new IllegalArgumentException(e);
        }

        return result;
    }

    /**
     * Returns a collection of faces configuration files mentioned via the
     * default <code>javax.faces.CONFIG_FILES</code> init parameter
     * 
     * @param servletContext
     *           The ServletContext
     * @return A collection of URLs (never null)
     */
    private Collection<URL> getConfigFilesFromInitParameter(ServletContext servletContext) {

        // read init parameter
        String initParam = servletContext.getInitParameter(FacesServlet.CONFIG_FILES_ATTR);

        // empty? return empty set
        if (initParam == null || initParam.trim().length() == 0) {
            return Collections.emptySet();
        }

        // split string at each comma
        String[] files = initParam.split(",");

        // the result
        Set<URL> result = new HashSet<URL>();

        // process each single file
        for (String file : files) {

            // ignore empty entries
            if (file.trim().length() == 0) {
                continue;
            }

            try {
                // try get URL for this file
                URL url = servletContext.getResource(file.trim());

                // add it to the result, if it exists
                if (url != null) {
                    result.add(url);
                }
            } catch (MalformedURLException e) {
                // log on debug level, because the JSF implementation should
                // handle such a case
                log.debug("Invalid entry in javax.faces.CONFIG_FILES init parameter: " + file);
            }

        }

        return result;
    }

    /**
     * Process a single <code>faces-config.xml</code> file and add all beans
     * found to the supplied list of {@link FacesConfigEntry} objects.
     * 
     * @param url
     *           The faces-config.xml file
     * @param facesConfigEntries
     *           list of entries to add the beans to
     */
    private void processFacesConfig(URL url, List<FacesConfigEntry> facesConfigEntries) {

        // log name of current file
        if (log.isTraceEnabled()) {
            log.trace("Loading bean names from: " + url.toString());
        }

        // setup digester
        Digester digester = new Digester();

        /*
         * We use the context class loader to resolve classes.
         * This fixes ClassNotFoundExceptions on Geronimo.
         */
        digester.setUseContextClassLoader(true);

        // prevent downloading of DTDs
        digester.setEntityResolver(new EmptyEntityResolver());

        digester.setValidating(false);
        digester.push(facesConfigEntries);
        digester.addObjectCreate("faces-config/managed-bean", FacesConfigEntry.class);
        digester.addCallMethod("faces-config/managed-bean/managed-bean-name", "setName", 0);
        digester.addCallMethod("faces-config/managed-bean/managed-bean-class", "setBeanClass", 0);
        digester.addSetNext("faces-config/managed-bean", "add");

        // stream used to read faces-config.xml file
        InputStream stream = null;

        try {
            // open the file and let digester pares it
            stream = url.openStream();
            digester.parse(stream);

        } catch (IOException e) {
            // may be thrown when reading the file
            log.error("Failed to parse: " + url.toString(), e);
        } catch (SAXException e) {
            // parsing errors
            log.error("Failed to parse: " + url.toString(), e);
        } finally {
            // close stream
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    // ignore IOExceptions on close
                }

            }
        }

    }

    /*
     * Implementation if interface method
     */
    public String getBeanName(Class<?> clazz) {
        // first check bean name map containing faces-config.xml entries
        String name = beanNameMap.get(clazz.getName());
        if (name != null) {
            return name;
        }

        return null;
    }

    /**
     * Private class to hold values of single "managed-bean" entries
     */
    public static class FacesConfigEntry {

        private String name;

        private String beanClass;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getBeanClass() {
            return beanClass;
        }

        public void setBeanClass(String beanClass) {
            this.beanClass = beanClass;
        }

    }

}