/* (c) 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.platform;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.util.logging.Logging;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.PlaceholderConfigurerSupport;
import org.springframework.core.Constants;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
/**
* Utility class uses to process GeoServer configuration workflow through external environment variables.
*
* This class must be used everytime we need to resolve a configuration placeholder at runtime.
* <p>
* An instance of this class needs to be registered in spring context as follows.
*
* <pre>
* <code>
* <bean id="geoserverEnvironment" class="org.geoserver.GeoServerEnvironment" depends-on="extensions"/>
* </code>
* </pre>
*
* It must be a singleton, and must not be loaded lazily. Furthermore, this bean must be loaded before any beans that use it.
*
* @author Alessio Fabiani, GeoSolutions
*
*/
public class GeoServerEnvironment {
/**
* logger
*/
protected static final Logger LOGGER = Logging.getLogger("org.geoserver.platform");
private static final Constants constants = new Constants(PlaceholderConfigurerSupport.class);
/**
* Constant set via System Environment in order to instruct GeoServer to make use or not of the config placeholders translation.
*
* Default to FALSE
*/
public final static boolean ALLOW_ENV_PARAMETRIZATION = Boolean
.valueOf(System.getProperty("ALLOW_ENV_PARAMETRIZATION", "false"));
private static final String PROPERTYFILENAME = "geoserver-environment.properties";
private static final String nullValue = "null";
private final PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper(
constants.asString("DEFAULT_PLACEHOLDER_PREFIX"),
constants.asString("DEFAULT_PLACEHOLDER_SUFFIX"),
constants.asString("DEFAULT_VALUE_SEPARATOR"), true);
private final PlaceholderResolver resolver = new PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
return GeoServerEnvironment.this.resolvePlaceholder(placeholderName);
}
};
private FileWatcher<Properties> configFile;
private Properties props;
/**
* Internal "props" getter method.
*
* @return the props
*/
public Properties getProps() {
return props;
}
public GeoServerEnvironment() {
try {
GeoServerResourceLoader loader = GeoServerExtensions
.bean(GeoServerResourceLoader.class);
configFile = new FileWatcher<Properties>(loader.get(PROPERTYFILENAME)) {
@Override
protected Properties parseFileContents(InputStream in) throws IOException {
Properties p = new Properties();
p.load(in);
return p;
}
};
props = configFile.read();
} catch (Exception e) {
LOGGER.log(Level.WARNING,
"Could not find any '" + PROPERTYFILENAME + "' property file.", e);
props = new Properties();
}
}
protected String resolvePlaceholder(String placeholder) {
String propVal = null;
propVal = resolveSystemProperty(placeholder);
if (configFile != null && configFile.isModified()) {
try {
props = configFile.read();
} catch (IOException e) {
LOGGER.log(Level.WARNING,
"Could not find any '" + PROPERTYFILENAME + "' property file.", e);
props = new Properties();
}
}
if (props != null && propVal == null) {
propVal = props.getProperty(placeholder);
}
return propVal;
}
protected String resolveSystemProperty(String key) {
try {
String value = System.getProperty(key);
if (value == null) {
value = System.getenv(key);
}
return value;
} catch (Throwable ex) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Could not access system property '" + key + "': " + ex);
}
return null;
}
}
protected String resolveStringValue(String strVal) throws BeansException {
String resolved = this.helper.replacePlaceholders(strVal, this.resolver);
return (resolved.equals(nullValue) ? null : resolved);
}
/**
* Translates placeholders in the form of Spring Property placemark ${...} into their real values.
*
* The method first looks for System variables which take precedence on local ones, then into internal props loaded from configuration file
* 'geoserver-environment.properties'.
*
* @param value
* @return
*/
public Object resolveValue(Object value) {
if (value != null) {
if (value instanceof String) {
return resolveStringValue((String) value);
}
}
return value;
}
/**
* Returns 'false' whenever the configuration file 'geoserver-environment.properties' has changed.
*
* @param value
* @return
*/
public boolean isStale() {
return this.configFile != null && this.configFile.isModified();
}
}