/** * This file Copyright (c) 2011-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.init; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Abstract implementation, providing the basic behavior expected from a {@link MagnoliaConfigurationProperties} implementation. * * TODO: cache results. * * @author gjoseph * @version $Revision: $ ($Author: $) */ public abstract class AbstractMagnoliaConfigurationProperties implements MagnoliaConfigurationProperties { protected static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractMagnoliaConfigurationProperties.class); protected List<PropertySource> sources; protected AbstractMagnoliaConfigurationProperties(List<PropertySource> propertySources) { this.sources = propertySources; } @Override public void init() throws Exception { } @Override public Set<String> getKeys() { final Set<String> allKeys = new HashSet<String>(); for (PropertySource source : sources) { allKeys.addAll(source.getKeys()); } return allKeys; } @Override public PropertySource getPropertySource(String key) { for (PropertySource source : sources) { if (source.hasProperty(key)) { return source; } } return null; } @Override public String getProperty(String key) { final PropertySource propertySource = getPropertySource(key); if (propertySource != null) { final String value = propertySource.getProperty(key); return parseStringValue(value, new HashSet<String>()); } return null; } @Override public boolean getBooleanProperty(String property) { return Boolean.parseBoolean(getProperty(property)); } @Override public boolean hasProperty(String key) { return getPropertySource(key) != null; } @Override public String describe() { final StringBuilder s = new StringBuilder() .append("[") .append(getClass().getSimpleName()) .append(" with sources: "); for (PropertySource source : sources) { s.append(source.describe()); } s.append("]"); return s.toString(); } @Override public String toString() { return describe() + " with properties: " + sources; } /** * 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) */ protected String parseStringValue(String strVal, Set<String> visitedPlaceholders) { final StringBuffer buf = new StringBuffer(strVal.trim()); 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... // Can't call getProperty() directly, as it would blow the call stack final PropertySource propertySource = getPropertySource(placeholder); String propVal = propertySource != null ? propertySource.getProperty(placeholder) : null; 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(); } protected static final String PLACEHOLDER_PREFIX = "${"; protected static final String PLACEHOLDER_SUFFIX = "}"; }