org.hyperic.util.PropertyUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.util.PropertyUtil.java

Source

/*
 * NOTE: This copyright does *not* cover user programs that use HQ
 * program services by normal system calls through the application
 * program interfaces provided as part of the Hyperic Plug-in Development
 * Kit or the Hyperic Client Development Kit - this is merely considered
 * normal use of the program, and does *not* fall under the heading of
 * "derived work".
 * 
 * Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
 * This file is part of HQ.
 * 
 * HQ is free software; you can redistribute it and/or modify
 * it under the terms version 2 of the GNU General Public License as
 * published by the Free Software Foundation. This program is distributed
 * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 */

package org.hyperic.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.util.security.SecurityUtil;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PropertyUtil {

    private static final Log LOG = LogFactory.getLog(PropertyUtil.class);

    /**
     * A regular expression pattern that is used for splitting properties lines into three parts: key, ':' or '=', and
     * value. This pattern is used for rewriting properties file (when values need to be encrypted.
     */
    private static final Pattern PROPERTY_LINE_PATTERN = Pattern.compile("([^=:]*)([=|:])(.*)");

    /**
     * Expand variable references in property values.
     *
     * I.e. if you have a props file:
     *
     * a=foo
     * b=bar
     * c=${a} ${b}
     *
     * The value for 'c' will be 'foo bar'
     *
     * @param props  Properties to replace
     *
     */
    public static void expandVariables(Map props) {
        List<String> vars = new ArrayList<String>();

        for (Object o : props.entrySet()) {
            Map.Entry ent = (Map.Entry) o;
            String value;
            int idx;

            value = (String) ent.getValue();
            idx = value.indexOf("${");

            if (idx == -1)
                continue;

            vars.clear();
            while (idx != -1) {
                int endIdx = value.indexOf("}", idx);

                if (endIdx == -1)
                    break;

                endIdx++;
                vars.add(value.substring(idx, endIdx));
                idx = value.indexOf("${", endIdx);
            }

            for (String replace : vars) {
                String replaceVar, lookupVal;

                replaceVar = replace.substring(2, replace.length() - 1);
                lookupVal = (String) props.get(replaceVar);
                if (lookupVal == null)
                    continue;

                value = StringUtil.replace(value, replace, lookupVal);
            }
            props.put(ent.getKey(), value);
        }
    }

    /**
     * Load properties from a file.
     */
    public static Properties loadProperties(String file) throws PropertyUtilException {
        FileInputStream fi = null;
        Properties props = new Properties();
        try {
            fi = new FileInputStream(file);
            props.load(fi);
        } catch (Exception exc) {
            throw new PropertyUtilException(exc);
        } finally {
            if (fi != null)
                try {
                    fi.close();
                } catch (IOException ignore) {
                    LOG.trace(ignore);
                }
        }
        return props;
    }

    /**
     * encrypt the input entries and append them at the end of the property file.
     * Any entries with identical keys would be erased.
     *
     * @param file the name of the properties file
     * @param propEncKey the properties encryption key
     * @param entriesToStore a map of properties to store
     * @throws PropertyUtilException
     */
    public static void storeProperties(String file, String propEncKey, Map<String, String> entriesToStore)
            throws PropertyUtilException {

        Map<String, String> encryptedEntriesToStore = new HashMap<String, String>();
        for (Map.Entry<String, String> entryToStore : entriesToStore.entrySet()) {
            String encryptedVal = SecurityUtil.encrypt(SecurityUtil.DEFAULT_ENCRYPTION_ALGORITHM, propEncKey,
                    entryToStore.getValue());
            encryptedEntriesToStore.put(entryToStore.getKey(), encryptedVal);
        }

        _storeProperties(file, encryptedEntriesToStore);
    }

    /**
     * Saves the provided map of keys and values into the properties file specified by <code>propFilePath</code>.
     * Values of properties that already exist int the file are overwritten. New values are placed near the end of the
     * file.
     *
     * @param propFilePath the path (absolute/relative) to the properties file to edit.
     * @param props the properties to add/update.
     */
    public static void storeProperties(String propFilePath, Map<String, String> props)
            throws PropertyUtilException {
        Map<String, String> duplicatedEntriesToStore = new HashMap<String, String>();
        for (Map.Entry<String, String> entryToStore : props.entrySet()) {
            duplicatedEntriesToStore.put(entryToStore.getKey(), entryToStore.getValue());
        }

        _storeProperties(propFilePath, duplicatedEntriesToStore);
    }

    /**
     * Saves the provided map of keys and values into the properties file specified by <code>propFilePath</code>.
     * Values of properties that already exist int the file are overwritten. New values are placed near the end of the
     * file.
     *
     * @param propFilePath the path (absolute/relative) to the properties file to edit.
     * @param props the properties to add/update.
     */
    private static void _storeProperties(String propFilePath, Map<String, String> props)
            throws PropertyUtilException {
        // If the provided properties map is null or empty then exit the method.
        if (props == null || props.size() < 1) {
            return;
        }

        // Used for writing the properties file back to the disk.
        PrintWriter writer = null;
        // Used for reading the properties file from the disk.
        FileReader reader = null;

        try {
            // Create new reader
            reader = new FileReader(propFilePath);
            // Wrap the reader with a buffer so we can walk through the file line by line.
            BufferedReader bufferedReader = new BufferedReader(reader);

            // The list of lines that will be written to disk.
            List<String> newLines = new ArrayList<String>();

            // Read the lines from the file and replace values.
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                newLines.add(processLine(line, props));
            } // EO while.

            // Iterate values that are left in the provided map and add them to the file.
            for (String key : props.keySet()) {
                newLines.add(key + " = " + props.get(key));
            }

            // TODO: change to UTF-8 and add support on property loader side. This might help:
            // http://stackoverflow.com/questions/863838/problem-with-java-properties-utf8-encoding-in-eclipse
            writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(propFilePath), "ISO-8859-1"));
            for (String aLineData : newLines) {
                writer.println(aLineData);
            }
            writer.flush();
        } catch (IOException exc) {
            String message = "Failed to store properties into the file: " + propFilePath;
            LOG.error(message, exc);
            throw new PropertyUtilException(exc);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException ignore) {
                    /* ignore */ }
            }
            if (writer != null) {
                writer.close();
            }
        } // EO try-catch
    } // EOM

    /**
     * Splits the provided properties line into key, ':' or '=', and value and checks the props map for a new value for
     * the extracted key. If there is a new value then the property line is reassembled using the new value; Otherwise
     * the line is returned as is.
     *
     * @param line the property line to process.
     * @param props the properties map in which a new value for the property might reside.
     * @return the final (modified or same) property line to write in the properties file.
     */
    private static String processLine(String line, Map<String, String> props) {
        // The result line.
        String result;

        // User the regex pattern to split the line.
        Matcher matcher = PROPERTY_LINE_PATTERN.matcher(line);
        // If a match is found then replace it value if there's a matching key in the provided map.
        if (matcher.find()) {
            // Extract the key (strip the escape characters).
            String key = matcher.group(1).trim().replaceAll("\\\\", "");
            // Try removing the entry from the map.
            String value = props.remove(key);
            if (value == null) {
                // No matching found. Add the line as is.
                result = line;
            } else {
                // Found a match -- replace the value.
                result = matcher.group(1) + matcher.group(2) + value;
            }
        } else {
            // No match found. Add the line as is.
            result = line;
        }

        return result;
    }// EOM
}