info.magnolia.cms.beans.config.PropertiesInitializer.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.cms.beans.config.PropertiesInitializer.java

Source

/**
 * This file Copyright (c) 2008-2012 Magnolia International
 * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
 *
 *
 * This file is dual-licensed under both the Magnolia
 * Network Agreement and the GNU General Public License.
 * You may elect to use one or the other of these licenses.
 *
 * This file is distributed in the hope that it will be
 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
 * Redistribution, except as permitted by whichever of the GPL
 * or MNA you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or
 * modify this file under the terms of the GNU General
 * Public License, Version 3, as published by the Free Software
 * Foundation.  You should have received a copy of the GNU
 * General Public License, Version 3 along with this program;
 * if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 2. For the Magnolia Network Agreement (MNA), this file
 * and the accompanying materials are made available under the
 * terms of the MNA which accompanies this distribution, and
 * is available at http://www.magnolia-cms.com/mna.html
 *
 * Any modifications to this file must keep this entire header
 * intact.
 *
 */
package info.magnolia.cms.beans.config;

import info.magnolia.cms.core.Path;
import info.magnolia.cms.core.SystemProperty;
import info.magnolia.module.ModuleRegistry;
import info.magnolia.module.model.ModuleDefinition;
import info.magnolia.module.model.PropertyDefinition;
import info.magnolia.objectfactory.Components;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.inject.Inject;
import javax.servlet.ServletContext;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class is responsible for loading the various "magnolia.properties" files, merging them,
 * and substituting variables in their values.
 * @author pbracher
 * @author fgiust
 *
 * @deprecated since 4.5 - replaced by classes in the {@link info.magnolia.init} package.
 */
public class PropertiesInitializer {
    private static final Logger log = LoggerFactory.getLogger(PropertiesInitializer.class);

    /**
     * The properties file containing the bean default implementations.
     */
    private static final String MGNL_BEANS_PROPERTIES = "/mgnl-beans.properties";

    /**
     * Placeholder prefix: "${".
     */
    public static final String PLACEHOLDER_PREFIX = "${";

    /**
     * Placeholder suffix: "}".
     */
    public static final String PLACEHOLDER_SUFFIX = "}";

    /**
     * Context attribute prefix, to obtain a property definition like ${contextAttribute/property}, that can refer to
     * any context attribute.
     */
    public static final String CONTEXT_ATTRIBUTE_PLACEHOLDER_PREFIX = "contextAttribute/"; //$NON-NLS-1$

    /**
     * Context parameter prefix, to obtain a property definition like ${contextParam/property}, that can refer to any
     * context parameter.
     */
    public static final String CONTEXT_PARAM_PLACEHOLDER_PREFIX = "contextParam/"; //$NON-NLS-1$

    /**
     * System property prefix, to obtain a property definition like ${systemProperty/property}, that can refer to any
     * System property.
     */
    public static final String SYSTEM_PROPERTY_PLACEHOLDER_PREFIX = "systemProperty/"; //$NON-NLS-1$

    /**
     * System property prefix, to obtain a property definition like ${systemProperty/property}, that can refer to any
     * System property.
     */
    public static final String ENV_PROPERTY_PLACEHOLDER_PREFIX = "env/"; //$NON-NLS-1$

    /**
     * @deprecated since 4.5, use IoC
     */
    public static PropertiesInitializer getInstance() {
        return Components.getSingleton(PropertiesInitializer.class);
    }

    /**
     * Default value for the MAGNOLIA_INITIALIZATION_FILE parameter.
     */
    public static final String DEFAULT_INITIALIZATION_PARAMETER = //
            "WEB-INF/config/${servername}/${webapp}/magnolia.properties," //$NON-NLS-1$
                    + "WEB-INF/config/${servername}/magnolia.properties," //$NON-NLS-1$
                    + "WEB-INF/config/${webapp}/magnolia.properties," //$NON-NLS-1$
                    + "WEB-INF/config/default/magnolia.properties," //$NON-NLS-1$
                    + "WEB-INF/config/magnolia.properties"; //$NON-NLS-1$

    private final ModuleRegistry moduleRegistry;

    @Inject
    public PropertiesInitializer(ModuleRegistry moduleRegistry) {
        this.moduleRegistry = moduleRegistry;
    }

    public void loadAllProperties(String propertiesFilesString, String rootPath) {
        // load mgnl-beans.properties first
        loadBeanProperties();

        loadAllModuleProperties();

        // complete or override with WEB-INF properties files
        loadPropertiesFiles(propertiesFilesString, rootPath);

        // complete or override with JVM system properties
        overloadWithSystemProperties();

        // resolve nested properties
        resolveNestedProperties();
    }

    private void resolveNestedProperties() {

        Properties sysProps = SystemProperty.getProperties();

        for (Iterator<Object> it = sysProps.keySet().iterator(); it.hasNext();) {
            String key = (String) it.next();
            String oldValue = (String) sysProps.get(key);
            String value = parseStringValue(oldValue, new HashSet<String>());
            SystemProperty.getProperties().put(key, value.trim());
        }

    }

    public void loadAllModuleProperties() {
        // complete or override with modules' properties
        final List<ModuleDefinition> moduleDefinitions = moduleRegistry.getModuleDefinitions();
        loadModuleProperties(moduleDefinitions);
    }

    /**
     * Load the properties defined in the module descriptors. They can get overridden later in the properties files in
     * WEB-INF
     */
    protected void loadModuleProperties(List<ModuleDefinition> moduleDefinitions) {
        for (ModuleDefinition module : moduleDefinitions) {
            for (PropertyDefinition property : module.getProperties()) {
                SystemProperty.setProperty(property.getName(), property.getValue());
            }
        }
    }

    public void loadPropertiesFiles(String propertiesLocationString, String rootPath) {

        String[] propertiesLocation = StringUtils.split(propertiesLocationString, ',');

        boolean found = false;
        // attempt to load each properties file at the given locations in reverse order: first files in the list
        // override the later ones
        for (int j = propertiesLocation.length - 1; j >= 0; j--) {
            String location = StringUtils.trim(propertiesLocation[j]);

            if (loadPropertiesFile(rootPath, location)) {
                found = true;
            }
        }

        if (!found) {
            final String msg = MessageFormat.format(
                    "No configuration found using location list {0}. Base path is [{1}]", //$NON-NLS-1$
                    ArrayUtils.toString(propertiesLocation), rootPath);
            log.error(msg);
            throw new ConfigurationException(msg);
        }
    }

    /**
     * @deprecated since 4.5, replaced by a new ClasspathPropertySource("/mgnl-beans.properties").
     * @see info.magnolia.init.DefaultMagnoliaConfigurationProperties
     */
    public void loadBeanProperties() {
        InputStream mgnlbeansStream = getClass().getResourceAsStream(MGNL_BEANS_PROPERTIES);

        if (mgnlbeansStream != null) {
            Properties mgnlbeans = new Properties();
            try {
                mgnlbeans.load(mgnlbeansStream);
            } catch (IOException e) {
                log.error("Unable to load {} due to an IOException: {}", MGNL_BEANS_PROPERTIES, e.getMessage());
            } finally {
                IOUtils.closeQuietly(mgnlbeansStream);
            }

            for (Iterator<Object> iter = mgnlbeans.keySet().iterator(); iter.hasNext();) {
                String key = (String) iter.next();
                SystemProperty.setProperty(key, mgnlbeans.getProperty(key));
            }

        } else {
            log.warn(
                    "{} not found in the classpath. Check that all the needed implementation classes are defined in your custom magnolia.properties file.",
                    MGNL_BEANS_PROPERTIES);
        }
    }

    /**
     * Try to load a magnolia.properties file.
     * @param rootPath
     * @param location
     * @return
     */
    public boolean loadPropertiesFile(String rootPath, String location) {
        final File initFile;
        if (Path.isAbsolute(location)) {
            initFile = new File(location);
        } else {
            initFile = new File(rootPath, location);
        }

        if (!initFile.exists() || initFile.isDirectory()) {
            log.debug("Configuration file not found with path [{}]", initFile.getAbsolutePath()); //$NON-NLS-1$
            return false;
        }

        InputStream fileStream = null;
        try {
            fileStream = new FileInputStream(initFile);
        } catch (FileNotFoundException e1) {
            log.debug("Configuration file not found with path [{}]", initFile.getAbsolutePath());
            return false;
        }

        try {
            SystemProperty.getProperties().load(fileStream);
            log.info("Loading configuration at {}", initFile.getAbsolutePath());//$NON-NLS-1$
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        } finally {
            IOUtils.closeQuietly(fileStream);
        }
        return true;
    }

    /**
     * Overload the properties with set system properties.
     */
    public void overloadWithSystemProperties() {
        Iterator<Object> it = SystemProperty.getProperties().keySet().iterator();
        while (it.hasNext()) {
            String key = (String) it.next();
            if (System.getProperties().containsKey(key)) {
                log.info("system property found: {}", key);
                String value = System.getProperty(key);
                SystemProperty.setProperty(key, value);
            }
        }
    }

    /**
     * Returns the property files configuration string passed, replacing all the needed values: ${servername} will be
     * reaplced with the name of the server, ${webapp} will be replaced with webapp name, ${systemProperty/*} will be
     * replaced with the corresponding system property, ${env/*} will be replaced with the corresponding environment property,
     * and ${contextAttribute/*} and ${contextParam/*} will be replaced with the corresponding attributes or parameters
     * taken from servlet context.
     * This can be very useful for all those application servers that has multiple instances on the same server, like
     * WebSphere. Typical usage in this case:
     * <code>WEB-INF/config/${servername}/${contextAttribute/com.ibm.websphere.servlet.application.host}/magnolia.properties</code>
     *
     * @param context Servlet context
     * @param servername Server name
     * @param webapp Webapp name
     * @param propertiesFilesString a comma separated list of paths.
     * @return Property file configuration string with everything replaced.
     *
     * @deprecated since 4.5, this is done by {@link info.magnolia.init.DefaultMagnoliaPropertiesResolver#DefaultMagnoliaPropertiesResolver}.
     * Note: when remove this class and method, this code will need to be cleaned up and moved to info.magnolia.init.DefaultMagnoliaPropertiesResolver
     */
    public static String processPropertyFilesString(ServletContext context, String servername, String webapp,
            String propertiesFilesString, String contextPath) {
        // Replacing basic properties.
        propertiesFilesString = StringUtils.replace(propertiesFilesString, "${servername}", servername); //$NON-NLS-1$
        propertiesFilesString = StringUtils.replace(propertiesFilesString, "${webapp}", webapp); //$NON-NLS-1$
        propertiesFilesString = StringUtils.replace(propertiesFilesString, "${contextPath}", contextPath);

        // Replacing servlet context attributes (${contextAttribute/something})
        String[] contextAttributeNames = getNamesBetweenPlaceholders(propertiesFilesString,
                CONTEXT_ATTRIBUTE_PLACEHOLDER_PREFIX);
        if (contextAttributeNames != null) {
            for (String ctxAttrName : contextAttributeNames) {
                if (ctxAttrName != null) {
                    // Some implementation may not accept a null as attribute key, but all should accept an empty
                    // string.
                    final String originalPlaceHolder = PLACEHOLDER_PREFIX + CONTEXT_ATTRIBUTE_PLACEHOLDER_PREFIX
                            + ctxAttrName + PLACEHOLDER_SUFFIX;
                    final Object attrValue = context.getAttribute(ctxAttrName);
                    if (attrValue != null) {
                        propertiesFilesString = propertiesFilesString.replace(originalPlaceHolder,
                                attrValue.toString());
                    }
                }
            }
        }

        // Replacing servlet context parameters (${contextParam/something})
        String[] contextParamNames = getNamesBetweenPlaceholders(propertiesFilesString,
                CONTEXT_PARAM_PLACEHOLDER_PREFIX);
        if (contextParamNames != null) {
            for (String ctxParamName : contextParamNames) {
                if (ctxParamName != null) {
                    // Some implementation may not accept a null as param key, but an empty string? TODO Check.
                    final String originalPlaceHolder = PLACEHOLDER_PREFIX + CONTEXT_PARAM_PLACEHOLDER_PREFIX
                            + ctxParamName + PLACEHOLDER_SUFFIX;
                    final String paramValue = context.getInitParameter(ctxParamName);
                    if (paramValue != null) {
                        propertiesFilesString = propertiesFilesString.replace(originalPlaceHolder, paramValue);
                    }
                }
            }
        }

        // Replacing system property (${systemProperty/something})
        String[] systemPropertiesNames = getNamesBetweenPlaceholders(propertiesFilesString,
                SYSTEM_PROPERTY_PLACEHOLDER_PREFIX);
        if (systemPropertiesNames != null) {
            for (String sysPropName : systemPropertiesNames) {
                if (StringUtils.isNotBlank(sysPropName)) {
                    final String originalPlaceHolder = PLACEHOLDER_PREFIX + SYSTEM_PROPERTY_PLACEHOLDER_PREFIX
                            + sysPropName + PLACEHOLDER_SUFFIX;
                    final String paramValue = System.getProperty(sysPropName);
                    if (paramValue != null) {
                        propertiesFilesString = propertiesFilesString.replace(originalPlaceHolder, paramValue);
                    }
                }
            }
        }

        // Replacing environment property (${env/something})
        String[] envPropertiesNames = getNamesBetweenPlaceholders(propertiesFilesString,
                ENV_PROPERTY_PLACEHOLDER_PREFIX);
        if (envPropertiesNames != null) {
            for (String envPropName : envPropertiesNames) {
                if (StringUtils.isNotBlank(envPropName)) {
                    final String originalPlaceHolder = PLACEHOLDER_PREFIX + ENV_PROPERTY_PLACEHOLDER_PREFIX
                            + envPropName + PLACEHOLDER_SUFFIX;
                    final String paramValue = System.getenv(envPropName);
                    if (paramValue != null) {
                        propertiesFilesString = propertiesFilesString.replace(originalPlaceHolder, paramValue);
                    }
                }
            }
        }

        return propertiesFilesString;
    }

    private static String[] getNamesBetweenPlaceholders(String propertiesFilesString,
            String contextNamePlaceHolder) {
        final String[] names = StringUtils.substringsBetween(propertiesFilesString,
                PLACEHOLDER_PREFIX + contextNamePlaceHolder, PLACEHOLDER_SUFFIX);
        return StringUtils.stripAll(names);
    }

    /**
     * Parse the given String value recursively, to be able to resolve nested placeholders. Partly borrowed from
     * org.springframework.beans.factory.config.PropertyPlaceholderConfigurer (original author: Juergen Hoeller)
     *
     * @deprecated since 4.5 this is now done by {@link info.magnolia.init.AbstractMagnoliaConfigurationProperties#parseStringValue}.
     */
    protected String parseStringValue(String strVal, Set<String> visitedPlaceholders) {

        StringBuffer buf = new StringBuffer(strVal);

        int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);
        while (startIndex != -1) {
            int endIndex = -1;

            int index = startIndex + PLACEHOLDER_PREFIX.length();
            int withinNestedPlaceholder = 0;
            while (index < buf.length()) {
                if (PLACEHOLDER_SUFFIX.equals(buf.subSequence(index, index + PLACEHOLDER_SUFFIX.length()))) {
                    if (withinNestedPlaceholder > 0) {
                        withinNestedPlaceholder--;
                        index = index + PLACEHOLDER_SUFFIX.length();
                    } else {
                        endIndex = index;
                        break;
                    }
                } else if (PLACEHOLDER_PREFIX.equals(buf.subSequence(index, index + PLACEHOLDER_PREFIX.length()))) {
                    withinNestedPlaceholder++;
                    index = index + PLACEHOLDER_PREFIX.length();
                } else {
                    index++;
                }
            }

            if (endIndex != -1) {
                String placeholder = buf.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
                if (!visitedPlaceholders.add(placeholder)) {

                    log.warn("Circular reference detected in properties, \"{}\" is not resolvable", strVal);
                    return strVal;
                }
                // Recursive invocation, parsing placeholders contained in the placeholder key.
                placeholder = parseStringValue(placeholder, visitedPlaceholders);
                // Now obtain the value for the fully resolved key...
                String propVal = SystemProperty.getProperty(placeholder);
                if (propVal != null) {
                    // Recursive invocation, parsing placeholders contained in the
                    // previously resolved placeholder value.
                    propVal = parseStringValue(propVal, visitedPlaceholders);
                    buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal);
                    startIndex = buf.indexOf(PLACEHOLDER_PREFIX, startIndex + propVal.length());
                } else {
                    // Proceed with unprocessed value.
                    startIndex = buf.indexOf(PLACEHOLDER_PREFIX, endIndex + PLACEHOLDER_SUFFIX.length());
                }
                visitedPlaceholders.remove(placeholder);
            } else {
                startIndex = -1;
            }
        }

        return buf.toString();
    }

}