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

com.norconex.commons.lang.config.ConfigurationUtil Maven / Gradle / Ivy

/* Copyright 2010-2016 Norconex Inc.
 *
 * 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.norconex.commons.lang.config;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.List;

import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.lang3.CharEncoding;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.norconex.commons.lang.file.FileUtil;

/**
 * Utility methods when dealing with configuration files.
 * @author Pascal Essiembre
 */
public final class ConfigurationUtil {
    private static final Logger LOG = 
            LogManager.getLogger(ConfigurationUtil.class);

    private ConfigurationUtil() {
        super();
    }
   

    /**
     * Disables delimiter parsing for both attributes and elements.
     * @param xml XML configuration
     */
    public static void disableDelimiterParsing(XMLConfiguration xml) {
        xml.setListDelimiter('\0');
        xml.setDelimiterParsingDisabled(true);
        xml.setAttributeSplittingDisabled(true);
    }
    
    
    /**
     * This load method will return an Apache XML Configuration from
     * from a {@link HierarchicalConfiguration}, with delimiter parsing 
     * disabled. 
     * @param c hierarchical configuration
     * @return XMLConfiguration
     * @since 1.5.0
     */
    public static XMLConfiguration newXMLConfiguration(
            HierarchicalConfiguration c) {
        XMLConfiguration xml = new XMLConfiguration(c);
        disableDelimiterParsing(xml);
        return xml;
    }
    /**
     * 

This load method will return an Apache XML Configuration from * from a reader, with delimiter parsing disabled.

*

Note: Leading and trailing white spaces are not preserved by * default. * To preserve them, add xml:space="preserve" * to your tag, like this: *

*
     *   <mytag xml:space="preserve"> </mytag>
     * 
*

The above example will preserve the white space in the tag's body. * @param in input stream * @return XMLConfiguration * @since 1.5.0 */ public static XMLConfiguration newXMLConfiguration(Reader in) { XMLConfiguration xml = new XMLConfiguration(); disableDelimiterParsing(xml); try { xml.load(in); } catch (org.apache.commons.configuration.ConfigurationException e) { throw new ConfigurationException("Cannot load XMLConfiguration", e); } return xml; } /** * Creates a new instance of the class represented by the "class" attribute * on the given node. The class must have an empty constructor. * If the class is an instance of {@link IXMLConfigurable}, the object * created will be automatically populated by invoking the * {@link IXMLConfigurable#loadFromXML(Reader)} method, * passing it the node XML automatically populated. * @param node the node representing the class to instantiate. * @param the type of the return value * @return a new object. * @throws ConfigurationException if instance cannot be created/populated */ public static T newInstance( HierarchicalConfiguration node) { return newInstance(node, null, true); } /** * Creates a new instance of the class represented by the "class" attribute * on the given node. The class must have an empty constructor. * If the class is an instance of {@link IXMLConfigurable} and * supportXMLConfigurable is true, the object created * will be automatically populated by invoking the * {@link IXMLConfigurable#loadFromXML(Reader)} method, * passing it the node XML automatically populated. * @param node the node representing the class to instantiate. * @param supportXMLConfigurable automatically populates the object from XML * if it is implementing {@link IXMLConfigurable}. * @param the type of the return value * @return a new object. * @throws ConfigurationException if instance cannot be created/populated */ public static T newInstance( HierarchicalConfiguration node, boolean supportXMLConfigurable) { return newInstance(node, null, supportXMLConfigurable); } /** * Creates a new instance of the class represented by the "class" attribute * on the given node. The class must have an empty constructor. * If the class is an instance of {@link IXMLConfigurable}, the object * created will be automatically populated by invoking the * {@link IXMLConfigurable#loadFromXML(Reader)} method, * passing it the node XML automatically populated. * @param node the node representing the class to instantiate. * @param defaultObject if returned object is null or undefined, * returns this default object. * @param the type of the return value * @return a new object. * @throws ConfigurationException if instance cannot be created/populated */ public static T newInstance( HierarchicalConfiguration node, T defaultObject) { return newInstance(node, defaultObject, true); } /** * Creates a new instance of the class represented by the "class" attribute * on the given node. The class must have an empty constructor. * If the class is an instance of {@link IXMLConfigurable} and * supportXMLConfigurable is true, the object created * will be automatically populated by invoking the * {@link IXMLConfigurable#loadFromXML(Reader)} method, * passing it the node XML automatically populated. * @param node the node representing the class to instantiate. * @param defaultObject if returned object is null or undefined, * returns this default object. * @param supportXMLConfigurable automatically populates the object from XML * if it is implementing {@link IXMLConfigurable}. * @param the type of the return value * @return a new object. * @throws ConfigurationException if instance cannot be created/populated */ @SuppressWarnings("unchecked") public static T newInstance( HierarchicalConfiguration node, T defaultObject, boolean supportXMLConfigurable) { T obj; String clazz; if (node == null) { return defaultObject; } clazz = node.getString("[@class]", null); if (clazz != null) { try { obj = (T) Class.forName(clazz).newInstance(); } catch (Exception e) { throw new ConfigurationException( "This class could not be instantiated: \"" + clazz + "\".", e); } } else { LOG.debug("A configuration entry was found without class " + "reference where one could have been provided; " + "using default value:" + defaultObject); obj = defaultObject; } if (obj == null) { return defaultObject; } if (obj instanceof IXMLConfigurable && supportXMLConfigurable) { try { ((IXMLConfigurable) obj).loadFromXML(newReader(node)); } catch (IOException e) { throw new ConfigurationException( "Could not load new instance from XML \"" + clazz + "\".", e); } } return obj; } /** *

Creates a new instance of the class represented by the "class" * attribute * on the sub-node of the node argument, matching the key provided. * The class must have an empty constructor. * If the class is an instance of {@link IXMLConfigurable}, the object * created will be automatically populated by invoking the * {@link IXMLConfigurable#loadFromXML(Reader)} method, * passing it the node XML automatically populated.

* *

Since 1.6.0, this method should throw a * {@link ConfigurationException} upon error. Use a method * with a default value argument to avoid throwing exceptions.

* * @param node the node representing the class to instantiate. * @param key sub-node name/hierarchical path * @param the type of the return value * @return a new object. * @throws ConfigurationException if instance cannot be created/populated */ public static T newInstance( HierarchicalConfiguration node, String key) { return newInstance(node, key, null, true, true); } /** *

Creates a new instance of the class represented by the "class" * attribute on the sub-node of the node argument, matching the key * provided. * The class must have an empty constructor. * If the class is an instance of {@link IXMLConfigurable} and * supportXMLConfigurable is true, the object created * will be automatically populated by invoking the * {@link IXMLConfigurable#loadFromXML(Reader)} method, * passing it the node XML automatically populated.

* *

Since 1.6.0, this method should throw a * {@link ConfigurationException} upon error. Use a method * with a default value argument to avoid throwing exceptions.

* * @param node the node representing the class to instantiate. * @param key sub-node name/hierarchical path * @param supportXMLConfigurable automatically populates the object from XML * if it is implementing {@link IXMLConfigurable}. * @param the type of the return value * @return a new object. * @throws ConfigurationException if instance cannot be created/populated */ public static T newInstance( HierarchicalConfiguration node, String key, boolean supportXMLConfigurable) { return newInstance(node, key, null, supportXMLConfigurable, true); } /** *

Creates a new instance of the class represented by the "class" * attribute * on the sub-node of the node argument, matching the key provided. * The class must have an empty constructor. * If the class is an instance of {@link IXMLConfigurable}, the object * created will be automatically populated by invoking the * {@link IXMLConfigurable#loadFromXML(Reader)} method, * passing it the node XML automatically populated.

* *

This method should not throw exception upon errors, but will return * the default value instead (even if null). Use a method without * a default value argument to get exception on errors.

* * @param node the node representing the class to instantiate. * @param defaultObject if returned object is null or undefined, * returns this default object. * @param key sub-node name/hierarchical path * @param the type of the return value * @return a new object. */ public static T newInstance( HierarchicalConfiguration node, String key, T defaultObject) { return newInstance(node, key, defaultObject, true, false); } /** *

Creates a new instance of the class represented by the "class" * attribute * on the sub-node of the node argument, matching the key provided. * The class must have an empty constructor. * If the class is an instance of {@link IXMLConfigurable} and * supportXMLConfigurable is true, the object created * will be automatically populated by invoking the * {@link IXMLConfigurable#loadFromXML(Reader)} method, * passing it the node XML automatically populated.

* *

This method should not throw exception upon errors, but will return * the default value instead (even if null). Use a method without * a default value argument to get exception on errors.

* * @param node the node representing the class to instantiate. * @param defaultObject if returned object is null or undefined, * returns this default object. * @param key sub-node name/hierarchical path * @param supportXMLConfigurable automatically populates the object from XML * if it is implementing {@link IXMLConfigurable}. * @param the type of the return value * @return a new object. */ public static T newInstance( HierarchicalConfiguration node, String key, T defaultObject, boolean supportXMLConfigurable) { return newInstance(node, key, defaultObject, supportXMLConfigurable, false); } private static T newInstance( HierarchicalConfiguration node, String key, T defaultObject, boolean supportXMLConfigurable, boolean canThrowException) { if (node == null) { return defaultObject; } try { if (key == null && defaultObject == null) { return ConfigurationUtil.newInstance( node, (T) null, supportXMLConfigurable); } HierarchicalConfiguration subconfig = safeConfigurationAt(node, key); return ConfigurationUtil.newInstance( subconfig, defaultObject, supportXMLConfigurable); } catch (Exception e) { if (canThrowException) { if (e instanceof ConfigurationException) { throw (ConfigurationException) e; } else { throw new ConfigurationException( "Could not instantiate object from configuration " + "for \"" + node.getRoot().getName() + " -> " + key + "\".", e); } } else { if (e instanceof ConfigurationException && e.getCause() != null && e.getCause() instanceof ClassNotFoundException) { LOG.error("You declared a class that does not exists " + "for \"" + node.getRoot().getName() + " -> " + key + "\". " + "Check for typos in your XML and make sure that " + "class is part of your Java classpath.", e); } else{ LOG.debug("Could not instantiate object from configuration " + "for \"" + node.getRoot().getName() + " -> " + key + "\".", e); } } return defaultObject; } } /** * Creates a new {@link Reader} from a {@link XMLConfiguration}. * Do not forget to close the reader instance when you are done with it. * @param node the xml configuration to convert to a reader instance. * @return reader * @throws ConfigurationException cannot read configuration * @throws IOException cannot read configuration */ public static Reader newReader(HierarchicalConfiguration node) throws IOException { XMLConfiguration xml; if (node instanceof XMLConfiguration) { xml = (XMLConfiguration) node; } else { xml = new XMLConfiguration(node); disableDelimiterParsing(xml); } StringWriter w = new StringWriter(); try { xml.save(w); } catch (org.apache.commons.configuration.ConfigurationException e) { throw new ConfigurationException( "Could transform XML node to reader.", e); } StringReader r = new StringReader(w.toString()); w.close(); return r; } /** * This method is the same as * {@link HierarchicalConfiguration#configurationAt(String)}, except that * it first checks if the key exists before attempting to retrieve it, * and returns null on missing keys instead of an * IllegalArgumentException * @param node the tree to extract a sub tree from * @param key the key that selects the sub tree * @return a XML configuration that contains this sub tree */ public static XMLConfiguration getXmlAt( HierarchicalConfiguration node, String key) { if (node == null) { return null; } HierarchicalConfiguration sub = safeConfigurationAt(node, key); if (sub == null) { return null; } XMLConfiguration xml = new XMLConfiguration(sub); disableDelimiterParsing(xml); return xml; } /** * Convenience class for testing that a {@link IXMLConfigurable} instance * can be written, and read into an new instance that is equal as per * {@link #equals(Object)}. * @param xmlConfiurable the instance to test if it writes/read properly * @throws IOException Cannot read/write * @throws ConfigurationException Cannot load configuration */ public static void assertWriteRead(IXMLConfigurable xmlConfiurable) throws IOException { File tempFile = File.createTempFile("XMLConfigurableTester", ".xml"); // Write Writer out = new OutputStreamWriter( new FileOutputStream(tempFile), CharEncoding.UTF_8); try { xmlConfiurable.saveToXML(out); } finally { out.close(); } // Read XMLConfiguration xml = new ConfigurationLoader().loadXML(tempFile); IXMLConfigurable readConfigurable = (IXMLConfigurable) ConfigurationUtil.newInstance(xml); FileUtil.delete(tempFile); if (!xmlConfiurable.equals(readConfigurable)) { LOG.error("BEFORE: " + xmlConfiurable); LOG.error(" AFTER: " + readConfigurable); throw new ConfigurationException( "Saved and loaded XML are not the same."); } } // This method is because the regular configuration at MUST have 1 // entry or will fail, and the containsKey(String) method is not reliable // since it expects a value (body text) or returns false. private static HierarchicalConfiguration safeConfigurationAt( HierarchicalConfiguration node, String key) { List subs = node.configurationsAt(key); if (subs != null && !subs.isEmpty()) { return subs.get(0); } return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy