alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

package net.myvietnam.mvncore.configuration;
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowledgement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgement may appear in the software itself,
 *    if and wherever such third-party acknowledgements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Stack;

import org.apache.commons.digester.AbstractObjectCreationFactory;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.ObjectCreationFactory;
import org.apache.commons.digester.xmlrules.DigesterLoader;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
/**
 * Factory class to create a CompositeConfiguration from a .xml file using
 * Digester.  By default it can handle the Configurations from commons-
 * configuration.  If you need to add your own, then you can pass in your own
 * digester rules to use.  It is also namespace aware, by providing a
 * digesterRuleNamespaceURI.
 *
 * @author <a href="mailto:epugh@upstate.com">Eric Pugh
 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen
 * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger
 * @version $Id: ConfigurationFactory.java,v 1.1 2003/12/09 08:25:30 huumai Exp $
 */
public class ConfigurationFactory implements BasePathLoader
{
    /** Constant for the root element in the info file.*/
    private static final String SEC_ROOT = "configuration/";

    /** Constant for the override section.*/
    private static final String SEC_OVERRIDE = SEC_ROOT + "override/";

    /** Constant for the additional section.*/
    private static final String SEC_ADDITIONAL = SEC_ROOT + "additional/";

    /** Constant for the name of the load method.*/
    private static final String METH_LOAD = "load";

    /** Constant for the default base path (points to actual directory).*/
    private static final String DEF_BASE_PATH = ".";

    /** The XML file with the details about the configuration to load */
    private String configurationFileName;
    /** The URL to the XML file with the details about the configuration to
     * load.
     */
    private URL configurationURL;
    /**
     * The implicit base path for included files. This path is determined by
     * the configuration to load and used unless no other base path was
     * explicitely specified.
     */
    private String impliciteBasePath;
    /** The basePath to prefix file paths for file based property files. */
    private String basePath;
    /** static logger */
    private static Log log = LogFactory.getLog(ConfigurationFactory.class);
    /** URL for xml digester rules file */
    private URL digesterRules;
    /** The digester namespace to parse */
    private String digesterRuleNamespaceURI;
    /**
     * C'tor
     */
    public ConfigurationFactory()
    {
        setBasePath(DEF_BASE_PATH);
    }
    /**
     * C'tor with ConfigurationFile Name passed
     *
     * @param configurationFileName The path to the configuration file
     */
    public ConfigurationFactory(String configurationFileName)
    {
        this.configurationFileName = configurationFileName;
    }
    /**
     * Return the configuration provided by this factory. It
     * loads the configuration file which is a XML description of
     * the actual configurations to load. It can contain various
     * different types of configuration, currently Properties, XML and JNDI.
     *
     * @return A Configuration object
     * @throws Exception A generic exception that we had trouble during the
     * loading of the configuration data.
     */
    public Configuration getConfiguration() throws Exception
    {
        Digester digester;
        ConfigurationBuilder builder = new ConfigurationBuilder();
        URL url = getConfigurationURL();
        if(url == null)
        {
            url = ConfigurationUtils.getURL(impliciteBasePath,
            getConfigurationFileName());
        }  /* if */
        InputStream input = url.openStream();

        if (getDigesterRules() == null)
        {
            digester = new Digester();
            configureNamespace(digester);
            initDefaultDigesterRules(digester);
        }
        else
        {
            digester = DigesterLoader.createDigester(getDigesterRules());
            // This might already be too late. As far as I can see, the namespace
            // awareness must be configured before the digester rules are loaded.
            configureNamespace(digester);
        }
        // Put the composite builder object below all of the other objects.
        digester.push(builder);
        // Parse the input stream to configure our mappings
        try
        {
            digester.parse(input);
            input.close();
        }
        catch (SAXException e)
        {
            log.error("SAX Exception caught", e);
            throw e;
        }
        return builder.getConfiguration();
    }
    /**
     * Returns the configurationFile.
     *
     * @return The name of the configuration file. Can be null.
     */
    public String getConfigurationFileName()
    {
        return configurationFileName;
    }
    /**
     * Sets the configurationFile.
     * @param configurationFileName  The name of the configurationFile to use.
     */
    public void setConfigurationFileName(String configurationFileName)
    {
        File file = new File(configurationFileName).getAbsoluteFile();
        this.configurationFileName = file.getName();
        impliciteBasePath = file.getParent();
    }

    /**
     * Returns the URL of the configuration file to be loaded.
     * @return the URL of the configuration to load
     */
    public URL getConfigurationURL()
    {
        return configurationURL;
    }

    /**
     * Sets the URL of the configuration to load. This configuration can be
     * either specified by a file name or by a URL.
     * @param url the URL of the configuration to load
     */
    public void setConfigurationURL(URL url)
    {
        configurationURL = url;
        impliciteBasePath = url.toString();

        // The following is a hack caused by the need to keep backwards
        // compatibility: Per default the base path is set to the current
        // directory. For loading from a URL this makes no sense. So
        // unless no specific base path was set we clear it.
        if(DEF_BASE_PATH.equals(getBasePath()))
        {
            setBasePath(null);
        }  /* if */
    }

    /**
     * Returns the digesterRules.
     * @return URL
     */
    public URL getDigesterRules()
    {
        return digesterRules;
    }
    /**
     * Sets the digesterRules.
     * @param digesterRules The digesterRules to set
     */
    public void setDigesterRules(URL digesterRules)
    {
        this.digesterRules = digesterRules;
    }
    /**
     * Initializes the parsing rules for the default digester
     *
     * This allows the Configuration Factory to understand the
     * default types: Properties, XML and JNDI. Two special sections are
     * introduced: <code><override> and
     * <code><additional>.
     *
     * @param digester The digester to configure
     */
    protected void initDefaultDigesterRules(Digester digester)
    {
        initDigesterSectionRules(digester, SEC_ROOT, false);
        initDigesterSectionRules(digester, SEC_OVERRIDE, false);
        initDigesterSectionRules(digester, SEC_ADDITIONAL, true);
    }

    /**
     * Sets up digester rules for a specified section of the configuration
     * info file.
     * @param digester the current digester instance
     * @param matchString specifies the section
     * @param additional a flag if rules for the additional section are to be
     * added
     */
    protected void initDigesterSectionRules(Digester digester,
    String matchString, boolean additional)
    {
        setupDigesterInstance(
            digester,
            matchString + "properties",
            new BasePathConfigurationFactory(PropertiesConfiguration.class),
            METH_LOAD,
            additional);
        setupDigesterInstance(
            digester,
            matchString + "dom4j",
            new BasePathConfigurationFactory(DOM4JConfiguration.class),
            METH_LOAD,
            additional);
        setupDigesterInstance(
            digester,
            matchString + "hierarchicalDom4j",
            new BasePathConfigurationFactory(HierarchicalDOM4JConfiguration.class),
            METH_LOAD,
            additional);
        setupDigesterInstance(
            digester,
            matchString + "jndi",
            new JNDIConfigurationFactory(),
            null,
            additional);
    }

    /**
     * Sets up digester rules for a configuration to be loaded.
     * @param digester the current digester
     * @param matchString the pattern to match with this rule
     * @param factory an ObjectCreationFactory instance to use for creating new
     * objects
     * @param method the name of a method to be called or <b>null for none
     * @param additional a flag if rules for the additional section are to be
     * added
     */
    protected void setupDigesterInstance(
        Digester digester,
        String matchString,
        ObjectCreationFactory factory,
        String method,
        boolean additional)
    {
        if(additional)
        {
            setupUnionRules(digester, matchString);
        }  /* if */
        digester.addFactoryCreate(matchString, factory);
        digester.addSetProperties(matchString);
        if(method != null)
        {
            digester.addCallMethod(matchString, method);
        }  /* if */
        digester.addSetNext(
            matchString,
            "addConfiguration",
            Configuration.class.getName());
    }

    /**
     * Sets up rules for configurations in the additional section.
     * @param digester the current digester
     * @param matchString the pattern to match with this rule
     */
    protected void setupUnionRules(Digester digester, String matchString)
    {
        digester.addObjectCreate(matchString,
        AdditionalConfigurationData.class);
        digester.addSetProperties(matchString);
        digester.addSetNext(matchString, "addAdditionalConfig",
        AdditionalConfigurationData.class.getName());
    }
    /**
     * Returns the digesterRuleNamespaceURI.
     *
     * @return A String with the digesterRuleNamespaceURI.
     */
    public String getDigesterRuleNamespaceURI()
    {
        return digesterRuleNamespaceURI;
    }
    /**
     * Sets the digesterRuleNamespaceURI.
     *
     * @param digesterRuleNamespaceURI The new digesterRuleNamespaceURI to use
     */
    public void setDigesterRuleNamespaceURI(String digesterRuleNamespaceURI)
    {
        this.digesterRuleNamespaceURI = digesterRuleNamespaceURI;
    }
    /**
     * Configure the current digester to be namespace aware and to have
     * a Configuration object to which all of the other configurations
     * should be added
     *
     * @param digester The Digester to configure
     */
    private void configureNamespace(Digester digester)
    {
        if (getDigesterRuleNamespaceURI() != null)
        {
            digester.setNamespaceAware(true);
            digester.setRuleNamespaceURI(getDigesterRuleNamespaceURI());
        }
        else
        {
            digester.setNamespaceAware(false);
        }
        digester.setValidating(false);
    }
    /**
     * Returns the Base path from which this Configuration Factory operates.
     * This is never null. If you set the BasePath to null, then a base path
     * according to the configuration to load is returned.
     *
     * @return The base Path of this configuration factory.
     */
    public String getBasePath()
    {
        String path = StringUtils.isEmpty(basePath) ?
        impliciteBasePath : basePath;
        return StringUtils.isEmpty(path) ? "." : path;
    }
    /**
     * Sets the basePath for all file references from this Configuration Factory.
     * Normally a base path need not to be set because it is determined by
     * the location of the configuration file to load. All relative pathes in
     * this file are resolved relative to this file. Setting a base path makes
     * sense if such relative pathes should be otherwise resolved, e.g. if
     * the configuration file is loaded from the class path and all sub
     * configurations it refers to are stored in a special config directory.
     *
     * @param basePath The new basePath to set.
     */
    public void setBasePath(String basePath)
    {
        this.basePath = basePath;
    }

    /**
     * A base class for digester factory classes. This base class maintains
     * a default class for the objects to be created.
     * There will be sub classes for specific configuration implementations.
     */
    public class DigesterConfigurationFactory
        extends AbstractObjectCreationFactory
        implements ObjectCreationFactory
    {
        /** Actual class to use. */
        private Class clazz;

        /**
         * Creates a new instance of <code>DigesterConfigurationFactory.
         * @param clazz the class which we should instantiate
         */
        public DigesterConfigurationFactory(Class clazz)
        {
            this.clazz = clazz;
        }

        /**
         * Creates an instance of the specified class.
         * @param attribs the attributes (ignored)
         * @return the new object
         * @exception Exception if object creation fails
         */
        public Object createObject(Attributes attribs) throws Exception
        {
            return clazz.newInstance();
        }
    }

    /**
     * A tiny inner class that allows the Configuration Factory to
     * let the digester construct BasePathConfiguration objects
     * that already have the correct base Path set.
     *
     */
    public class BasePathConfigurationFactory
        extends DigesterConfigurationFactory
    {
        /**
         * C'tor
         *
         * @param clazz The class which we should instantiate.
         */
        public BasePathConfigurationFactory(Class clazz)
        {
            super(clazz);
        }

        /**
         * Gets called by the digester.
         *
         * @param attributes the actual attributes
         * @return the new object
         * @throws Exception Couldn't instantiate the requested object.
         */
        public Object createObject(Attributes attributes) throws Exception
        {
            BasePathLoader bpl =
                (BasePathLoader) super.createObject(attributes);
            bpl.setBasePath(getBasePath());
            return bpl;
        }
    }

    /**
     * A tiny inner class that allows the Configuration Factory to
     * let the digester construct JNDIPathConfiguration objects.
     *
     */
    public class JNDIConfigurationFactory
        extends DigesterConfigurationFactory
    {
        /**
         * C'tor
         */
        public JNDIConfigurationFactory()
        {
            super(JNDIConfiguration.class);
        }
    }

    /**
     * A simple data class that holds all information about a configuration
     * from the <code><additional> section.
     */
    public static class AdditionalConfigurationData
    {
        /** Stores the configuration object.*/
        private Configuration configuration;

        /** Stores the location of this configuration in the global tree.*/
        private String at;

        /**
         * Returns the value of the <code>at attribute.
         * @return the at attribute
         */
        public String getAt()
        {
            return at;
        }

        /**
         * Sets the value of the <code>at attribute.
         * @param string the attribute value
         */
        public void setAt(String string)
        {
            at = string;
        }

        /**
         * Returns the configuration object.
         * @return the configuration
         */
        public Configuration getConfiguration()
        {
            return configuration;
        }

        /**
         * Sets the configuration object. Note: Normally this method should be
         * named <code>setConfiguration(), but the name
         * <code>addConfiguration() is required by some of the digester
         * rules.
         * @param config the configuration to set
         */
        public void addConfiguration(Configuration config)
        {
            configuration = config;
        }
    }

    /**
     * An internally used helper class for constructing the composite
     * configuration object.
     */
    public static class ConfigurationBuilder
    {
        /** Stores the composite configuration.*/
        private CompositeConfiguration config;

        /** Stores a collection with the configs from the additional section.*/
        private Collection additionalConfigs;

        /**
         * Creates a new instance of <code>ConfigurationBuilder.
         */
        public ConfigurationBuilder()
        {
            config = new CompositeConfiguration();
            additionalConfigs = new LinkedList();
        }

        /**
         * Adds a new configuration to this object. This method is called by
         * Digester.
         * @param conf the configuration to be added
         */
        public void addConfiguration(Configuration conf)
        {
            config.addConfiguration(conf);
        }

        /**
         * Adds information about an additional configuration. This method is
         * called by Digester.
         * @param data the data about the additional configuration
         */
        public void addAdditionalConfig(AdditionalConfigurationData data)
        {
            additionalConfigs.add(data);
        }

        /**
         * Returns the final composite configuration.
         * @return the final configuration object
         */
        public CompositeConfiguration getConfiguration()
        {
            if(!additionalConfigs.isEmpty())
            {
                Configuration unionConfig =
                createAdditionalConfiguration(additionalConfigs);
                if(unionConfig != null)
                {
                    addConfiguration(unionConfig);
                }  /* if */
                additionalConfigs.clear();
            }  /* if */

            return config;
        }

        /**
         * Creates a configuration object with the union of all properties
         * defined in the <code><additional> section. This
         * implementation returns a <code>HierarchicalConfiguration
         * object.
         * @param configs a collection with
         * <code>AdditionalConfigurationData objects
         * @return the union configuration (can be <b>null)
         */
        protected Configuration createAdditionalConfiguration(
        Collection configs)
        {
            HierarchicalConfiguration result = new HierarchicalConfiguration();

            for(Iterator it = configs.iterator(); it.hasNext();)
            {
                AdditionalConfigurationData cdata =
                (AdditionalConfigurationData) it.next();
                result.addNodes(cdata.getAt(),
                createRootNode(cdata).getChildren().asVector());
            }  /* for */

            return (result.isEmpty()) ? null : result;
        }

        /**
         * Creates a configuration root node for the specified configuration.
         * @param cdata the configuration data object
         * @return a root node for this configuration
         */
        private HierarchicalConfiguration.Node createRootNode(
        AdditionalConfigurationData cdata)
        {
            if(cdata.getConfiguration() instanceof HierarchicalConfiguration)
            {
                // we can directly use this configuration's root node
                return ((HierarchicalConfiguration) cdata.getConfiguration())
                .getRoot();
            }  /* if */

            else
            {
                // transform configuration to a hierarchical root node
                HierarchicalConfigurationNodeConverter conv =
                new HierarchicalConfigurationNodeConverter();
                conv.process(cdata.getConfiguration());
                return conv.getRootNode();
            }  /* else */
        }
    }

    /**
     * A specialized <code>HierarchicalConfigurationConverter class
     * that creates a <code>HierarchicalConfiguration root node from
     * an arbitrary <code>Configuration object. This class is used to
     * add additional configuration objects to the hierarchical configuration
     * managed by the <code>ConfigurationBuilder.
     */
    static class HierarchicalConfigurationNodeConverter
    extends HierarchicalConfigurationConverter
    {
        /** A stack for constructing the hierarchy.*/
        private Stack nodes;

        /** Stores the root node.*/
        private HierarchicalConfiguration.Node root;

        /**
         * Default constructor.
         */
        public HierarchicalConfigurationNodeConverter()
        {
            nodes = new Stack();
            root = new HierarchicalConfiguration.Node();
            nodes.push(root);
        }

        /**
         * Callback for an element start event. Creates a new node and adds
         * it to the actual parent.
         * @param name the name of the new node
         * @param value the node's value
         */
        protected void elementStart(String name, Object value)
        {
            HierarchicalConfiguration.Node parent =
            (HierarchicalConfiguration.Node) nodes.peek();
            HierarchicalConfiguration.Node child =
            new HierarchicalConfiguration.Node(name);
            if(value != null)
            {
                child.setValue(value);
            }  /* if */
            parent.addChild(child);
            nodes.push(child);
        }

        /**
         * Callback for an element end event. Clears the stack.
         * @param name the name of the element
         */
        protected void elementEnd(String name)
        {
            nodes.pop();
        }

        /**
         * Returns the constructed root node.
         * @return the root node
         */
        public HierarchicalConfiguration.Node getRootNode()
        {
            return root;
        }
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.