com.redhat.satellite.search.config.Configuration.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.satellite.search.config.Configuration.java

Source

/**
 * Copyright (c) 2008--2013 Red Hat, Inc.
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 *
 * Red Hat trademarks are not licensed under GPLv2. No permission is
 * granted to use or replicate Red Hat trademarks that are incorporated
 * in this software or its documentation.
 */

package com.redhat.satellite.search.config;

import com.redhat.satellite.search.config.translator.TranslatorRegistry;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeSet;

/**
 * The Config class acts as an abstraction layer between our configuration
 * system, and the actual implementation. The basic idea is that there is a
 * global config, but you can instantiate one of your own if you want. This
 * layer insulates us from changing the underlying implementation.
 * <p>
 * Config files are properties, with /usr/share/rhn/config-defaults/*.conf
 * setting defaults that can be overridden by /etc/rhn/rhn.conf.
 *
 * @version $Rev$
 */
public class Configuration {

    private static Logger logger = Logger.getLogger(Configuration.class);

    //
    // Location of config files
    //

    /**
     * The default directory in which to look for config files
     */
    public static final String DEFAULT_CONF_DIR = "/etc/rhn";

    /**
     * The directory in which to look for default config values
     */
    public static final String DEFAULT_DEFAULT_CONF_DIR = "/usr/share/rhn/config-defaults";

    /**
     * The system property containing the Spacewalk configuration directory.
     * If the property is not set, config files are read
     * from {@link #DEFAULT_CONF_DIR}
     */
    private static final String RHN_CONF_DIR_PROPERTY = "rhn.config.dir";

    /**
     * The system property containing the SearchServer  configuration directory.
     * If the property is not set, config files are read
     * from /usr/share/rhn/config-defaults
     */
    private static final String SEARCH_CONF_DIR_PROPERTY = "search.config.dir";

    /**
     * List of values that are considered true, ignoring case.
     */
    private static final String[] TRUE_VALUES = { "1", "y", "true", "yes", "on" };

    /**
     * array of prefix in the order they should be search
     * if the given lookup string is without a namespace.
     */
    private String[] prefixOrder = new String[] { "web", "server" };

    /** hash of configuration properties */
    private Properties configValues = new Properties();

    private List<KeyTranslator> translators;

    /** set of configuration file names */
    private TreeSet<File> fileList = new TreeSet<File>(new Comparator<File>() {

        /** {inheritDoc} */
        public int compare(File f1, File f2) {
            // Need to make sure we read the child namespace before the base
            // namespace.  To do that, we sort the list in reverse order based
            // on the length of the file name.  If two filenames have the same
            // length, then we need to do a lexigraphical comparison to make
            // sure that the filenames themselves are different.

            int lenDif = f2.getAbsolutePath().length() - f1.getAbsolutePath().length();

            if (lenDif != 0) {
                return lenDif;
            }
            return f2.compareTo(f1);
        }
    });

    /**
     * public constructor. Rereads config entries every time it is called.
     *
     * @throws ConfigException error from the Configuration layers. the jakarta
     * commons conf system just throws Exception, which makes it hard to react.
     * sometimes it is an IOExceptions, sometimes a SAXParserException,
     * sometimes a VindictiveException. so we just turn them into our own
     * exception type and toss them up. as we discover ones we might
     * meaningfully want to react to, we can specialize ConfigException and catch
     * those
     */
    public Configuration() throws ConfigException {
        translators = TranslatorRegistry.getTranslators();
        addPath(getSearchConfigDir());
        addPath(getDefaultConfigFilePath());
        parseFiles();
    }

    /**
     * Construct from a Reader.
     * @param rdr Configuration data in Reader format.
     * @throws ConfigException thrown if a problem reading the configuration
     * occurs.
     */
    public Configuration(Reader rdr) throws ConfigException {
        translators = TranslatorRegistry.getTranslators();
        ReaderWrapper rdrwrap = new ReaderWrapper(rdr);
        Properties props = new Properties();
        try {
            parseStream(props, "search", rdrwrap);
        } catch (IOException ioe) {
            throw new ConfigException(ioe.getMessage(), ioe);
        }
    }

    /**
     * Add a path to the config object for parsing
     * @param path The path to add
     */
    public void addPath(String path) {
        addFiles(path);
    }

    /**
     * static method to get the singleton Config option
     *
     * @return the config option
     */
    //    public static synchronized Config get() {
    //        if (singletonConfig == null) {
    //            singletonConfig = new Config();
    //        }
    //        return singletonConfig;
    //    }

    private static String getRHNConfigDir() {
        String confDir = System.getProperty(RHN_CONF_DIR_PROPERTY);

        if (StringUtils.isBlank(confDir)) {
            confDir = DEFAULT_CONF_DIR;
        }
        return confDir;
    }

    private static String getSearchConfigDir() {
        String confDir = System.getProperty(SEARCH_CONF_DIR_PROPERTY);

        if (StringUtils.isBlank(confDir)) {
            confDir = DEFAULT_DEFAULT_CONF_DIR;
        }
        return confDir;
    }

    /**
     * Get the path to the rhn.conf file we use.
     *
     * @return String path.
     */
    public static String getDefaultConfigFilePath() {
        return getRHNConfigDir() + "/rhn.conf";
    }

    /**
     * Get the configuration entry for the given string name.  If the value
     * is null, then return the given defValue.  defValue can be null as well.
     * @param name name of property
     * @param defValue default value for property if it is null.
     * @return the value of the property with the given name, or defValue.
     */
    public String getString(String name, String defValue) {
        String ret = getString(name);
        if (ret == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("getString() - returning default value");
            }
            ret = defValue;
        }
        return ret;
    }

    /**
     * Returns a map of the values with the given name. The value must be
     * in the format of key:value otherwise, an empty map is returned.
     * @param name name of property
     * @return map of the values of the property matching k:v.
     */
    public Map<String, String> getMap(String name) {
        String value = getString(name);
        Map<String, String> retval = null;
        if (value != null) {
            retval = new HashMap<String, String>();
            String[] pairs = value.split(",");
            for (int x = 0; x < pairs.length; x++) {
                String[] nv = pairs[x].split(":");
                if (nv.length != 2) {
                    continue;
                }
                retval.put(nv[0].trim(), nv[1].trim());
            }
        }
        return retval;
    }

    /**
     * get the config entry for string s
     *
     * @param value string to get the value of
     * @return the value
     */
    public String getString(String value) {
        if (logger.isDebugEnabled()) {
            logger.debug("getString() -     getString() called with: " + value);
        }
        if (value == null) {
            return null;
        }

        int lastDot = value.lastIndexOf(".");
        String ns = "";
        String property = value;
        if (lastDot > 0) {
            property = value.substring(lastDot + 1);
            ns = value.substring(0, lastDot);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("getString() -     getString() -> Getting property: " + property);
        }
        String result = configValues.getProperty(property);
        if (logger.isDebugEnabled()) {
            logger.debug("getString() -     getString() -> result: " + result);
        }
        if (result == null) {
            if (!"".equals(ns)) {
                result = configValues.getProperty(ns + "." + property);
            } else {
                for (int i = 0; i < prefixOrder.length; i++) {
                    result = configValues.getProperty(prefixOrder[i] + "." + property);
                    if (result != null) {
                        break;
                    }
                }
            }
        }
        if (logger.isDebugEnabled()) {
            logger.debug("getString() -     getString() -> returning: " + result);
        }

        if (result == null || result.equals("")) {
            return null;
        }

        return StringUtils.trim(result);
    }

    /**
     * get the config entry for string s
     *
     * @param s string to get the value of
     * @return the value
     */
    public int getInt(String s) {
        return getInt(s, 0);
    }

    /**
     * get the config entry for string s, if no value is found
     * return the defaultValue specified.
     *
     * @param s string to get the value of
     * @param defaultValue Default value if entry is not found.
     * @return the value
     */
    public int getInt(String s, int defaultValue) {
        Integer val = getInteger(s);
        if (val == null) {
            return defaultValue;
        }
        return val.intValue();
    }

    /**
     * get the config entry for string s
     *
     * @param s string to get the value of
     * @return the value
     */
    public double getDouble(String s) {
        return getDouble(s, 0.0);
    }

    /**
     * get the config entry for string s, if no value is found
     * return the defaultValue specified.
     *
     * @param s string to get the value of
     * @param defaultValue Default value if entry is not found.
     * @return the value
     */
    public double getDouble(String s, double defaultValue) {
        String val = getString(s);
        if (val == null) {
            return defaultValue;
        }
        return new Double(val).doubleValue();
    }

    /**
     * get the config entry for string s
     *
     * @param s string to get the value of
     * @return the value
     */
    public Integer getInteger(String s) {
        String val = getString(s);
        if (val == null) {
            return null;
        }
        return new Integer(val);
    }

    /**
     * Parses a comma-delimited list of values as a java.util.List
     * @param name config entry name
     * @return instance of java.util.List populated with config values
     */
    public List<String> getList(String name) {
        List<String> retval = new LinkedList<String>();
        String[] vals = getStringArray(name);
        if (vals != null) {
            retval.addAll(Arrays.asList(vals));
        }
        return retval;
    }

    /**
     * get the config entry for string s
     *
     * @param s string to get the value of
     * @return the value
     */
    public String[] getStringArray(String s) {
        if (s == null) {
            return null;
        }
        String value = getString(s);

        if (value == null) {
            return null;
        }

        return value.split(",");
    }

    /**
     * get the config entry for string name
     *
     * @param name string to set the value of
     * @param value new value
     * @return the previous value of the property
     */
    public String setString(String name, String value) {
        return (String) configValues.setProperty(name, value);
    }

    /**
     * get the config entry for string s
     *
     * @param s string to get the value of
     * @return the value
     */
    public boolean getBoolean(String s) {
        String value = getString(s);
        if (logger.isDebugEnabled()) {
            logger.debug("getBoolean() - " + s + " is : " + value);
        }
        if (value == null) {
            return false;
        }

        //need to check the possible true values
        // tried to use BooleanUtils, but that didn't
        // get the job done for an integer as a String.

        for (int i = 0; i < TRUE_VALUES.length; i++) {
            if (TRUE_VALUES[i].equalsIgnoreCase(value)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("getBoolean() - Returning true: " + value);
                }
                return true;
            }
        }

        return false;
    }

    /**
     * set the config entry for string name
     * @param s string to set the value of
     * @param b new value
     */
    public void setBoolean(String s, String b) {
        // need to check the possible true values
        // tried to use BooleanUtils, but that didn't
        // get the job done for an integer as a String.
        for (int i = 0; i < TRUE_VALUES.length; i++) {
            if (TRUE_VALUES[i].equalsIgnoreCase(b)) {
                configValues.setProperty(s, "1");

                // get out we're done here
                return;
            }
        }
        configValues.setProperty(s, "0");
    }

    private void addFiles(String path) {
        File f = new File(path);

        if (f.isDirectory()) {
            // bugzilla: 154517; only add items that end in .conf
            File[] files = f.listFiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].getName().endsWith((".conf"))) {
                    fileList.add(files[i]);
                }
            }
        } else {
            fileList.add(f);
        }
    }

    private String makeNamespace(File f) {
        String ns = f.getName();

        // This is really hokey, but it works. Basically, rhn.conf doesn't
        // match the standard rhn_foo.conf convention. So, to create the
        // namespace, we first special case rhn.*
        if (ns.startsWith("rhn.")) {
            return "";
        }

        ns = ns.replaceFirst("rhn_", "");
        ns = ns.substring(0, ns.lastIndexOf('.'));
        ns = ns.replaceAll("_", ".");
        return ns;
    }

    private void parseStream(Properties props, String namespace, InputStream is) throws IOException {

        props.load(is);

        // loop through all of the config values in the properties file
        // making sure the prefix is there.
        Properties newProps = new Properties();
        for (Iterator j = props.keySet().iterator(); j.hasNext();) {
            String key = (String) j.next();
            String newKey = key;
            if (!key.startsWith(namespace)) {
                newKey = namespace + "." + key;
            }
            logger.debug("Adding: " + newKey + ": " + props.getProperty(key));
            // translate the original key
            newKey = translateKey(key);
            newProps.put(newKey, props.getProperty(key));
        }
        configValues.putAll(newProps);
    }

    /**
     * Parse all of the added files.
     */
    public void parseFiles() {
        for (Iterator i = fileList.iterator(); i.hasNext();) {
            File curr = (File) i.next();
            parseFile(curr);
        }
    }

    private void parseFile(File file) {
        Properties props = new Properties();
        String ns = makeNamespace(file);
        logger.debug("Adding namespace: " + ns + " for file: " + file.getAbsolutePath());

        try {
            parseStream(props, ns, new FileInputStream(file));
        } catch (IOException e) {
            logger.error("Could not parse file" + file, e);
        }
    }

    /**
     * Returns a subset of the properties for the given namespace. This is
     * not a particularly fast method and should be used only at startup or
     * some other discreet time.  Repeated calls to this method are guaranteed
     * to be slow.
     * @param namespace Namespace of properties to be returned.
     * @return subset of the properties that begin with the given namespace.
     */
    public Properties getNamespaceProperties(String namespace) {
        Properties prop = new Properties();
        for (Iterator i = configValues.keySet().iterator(); i.hasNext();) {
            String key = (String) i.next();
            if (key.startsWith(namespace)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Looking for key: [" + key + "]");
                }
                prop.put(key, configValues.getProperty(key));
            }
        }
        return prop;
    }

    private String translateKey(String key) {
        for (Iterator itr = translators.iterator(); itr.hasNext();) {
            KeyTranslator trans = (KeyTranslator) itr.next();
            if (trans.shouldTranslate(key)) {
                key = trans.translateKey(key);
            }
        }

        return key;
    }

    private static class ReaderWrapper extends InputStream {
        private Reader rdr;

        public ReaderWrapper(Reader reader) {
            rdr = reader;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int read() throws IOException {
            return rdr.read();
        }

        @Override
        public void close() throws IOException {
            super.close();
            rdr.close();
        }
    }
}