All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.daisy.common.properties.Properties Maven / Gradle / Ivy

package org.daisy.common.properties;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class Properties {

	private Properties() {
	}

	// System.getProperties() returns an object that can change afterwards, so make a copy to get
	// the initial system properties
	private final static Map systemProperties = new HashMap<>(propertiesAsMap(System.getProperties()));
	private final static Map systemEnv = System.getenv();
	private final static String PROPERTIES_FILE_PROPERTY = "org.daisy.pipeline.properties";
	private final static String propertiesFile = systemProperties.get(PROPERTIES_FILE_PROPERTY);
	private static Map propertiesFromFile = null;

	private static Logger logger;
	private static Logger logger() {
		if (logger == null) logger = LoggerFactory.getLogger(Properties.class);
		return logger;
	}

	/**
	 * Returns a set of all the keys in this property list.
	 */
	public static Set propertyNames() {
		Set keys = new HashSet();
		synchronized (settableProperties) {
			for (String p : settableProperties.keySet())
				keys.add(p);
		}
		if (propertiesFromFile == null)
			propertiesFromFile = readPropertiesFromFile();
		if (propertiesFromFile != null)
			for (String p : propertiesFromFile.keySet())
				if (p.startsWith("org.daisy.pipeline."))
					keys.add(p);
		for (String p : systemProperties.keySet())
			keys.add(p);
		for (String envKey : systemEnv.keySet())
			if (envKey.startsWith("PIPELINE2_")) {
				String p = "org.daisy.pipeline." + envKey.substring(10).replace('_','.').toLowerCase();
				keys.add(p); }
		return keys;
	}

	/**
	 * Returns the momentary set of properties as a {@link Map} object.
	 */
	public static Map getSnapshot() {
		synchronized (settableProperties) {
			java.util.Properties snapshot = new java.util.Properties();
			for (String prop : propertyNames()) {
				String val = getProperty(prop);
				if (val != null) // could be null for a settable property that has not been set
					snapshot.setProperty(prop, val);
			}
			return propertiesAsMap(snapshot);
		}
	}

	/**
	 * Gets the system property or environment variable indicated by the specified key.
	 *
	 * If the key starts with "org.daisy.pipeline", the function first looks for an environment
	 * variable, then for a system property, and then reads the pipeline.properties file. Otherwise
	 * only system properties are considered. The name of the environment variable is derived from
	 * the name of the system property.
	 *
	 * For example, if the key is "org.daisy.pipeline.ws.host", an environment variable
	 * "PIPELINE2_WS_HOST" will have precedence over the system property.
	 *
	 * Variable interpolation is supported, meaning that the value of a property may dereference
	 * other properties, using the "${...}" syntax.
	 *
	 * @param key The name of the system property.
	 * @return The string value of the system property or environment variable, or null if
	 *         there is no property or variable with that key.
	 */
	public static String getProperty(String key) {
		return getProperty(key, null);
	}

	/**
	 * @param key The name of the property.
	 * @param defaultValue A default value.
	 * @return The string value of the system property or environment variable, or the
	 *         default value if there is no property or variable with that key. Modifications to
	 *         settable properties are taken into account.
	 */
	public static String getProperty(String key, String defaultValue) {
		if (key == null)
			throw new IllegalArgumentException();
		// modified properties have highest priority
		synchronized (settableProperties) {
			SettableProperty p = settableProperties.get(key);
			if (p != null)
				// defaultValue ignored (only the defaultValue that was defined first is remembered)
				return p.getValue();
		}
		// then come environment variables
		if (key.startsWith("org.daisy.pipeline.")) {
			String envKey = "PIPELINE2_" + key.substring(19).replace('.', '_').toUpperCase();
			if (systemEnv.containsKey(envKey))
				return expand(systemEnv.get(envKey));
		}
		// then come system properties
		String v = systemProperties.get(key);
		if (v != null)
			return expand(v);
		// and finally properties defined in the pipeline.properties file
		if (key.startsWith("org.daisy.pipeline.")) {
			if (propertiesFromFile == null)
				propertiesFromFile = readPropertiesFromFile();
			if (propertiesFromFile != null) {
				v = propertiesFromFile.get(key);
				if (v != null)
					return expand(v);
			}
		}
		// return default value
		return defaultValue;
	}

	private static final Map settableProperties = new HashMap<>();

	/**
	 * @param settable Allow a property to be modified and get a {@link SettableProperty} object for
	 *                 reading future values.
	 */
	public static Property getProperty(String key, boolean settable, String description, boolean sensitive) {
		return getProperty(key, settable, description, sensitive, null);
	}

	public static Property getProperty(String key, boolean settable, String description, boolean sensitive, String defaultValue) {
		if (settable)
			synchronized (settableProperties) {
				if (settableProperties.containsKey(key)) {
					// defaultValue ignored (only the defaultValue that was defined first is remembered)
					return settableProperties.get(key);
				}
				SettableProperty prop = new SettableProperty(key, description, sensitive, defaultValue, getProperty(key));
				settableProperties.put(key, prop);
				return prop;
			}
		else
			return new Property(key, getProperty(key, defaultValue));
	}

	/**
	 * Get the current set of properties that can be modified.
	 */
	public static Set getSettableProperties() {
		synchronized (settableProperties) {
			return new HashSet<>(settableProperties.values());
		}
	}

	private static Map readPropertiesFromFile() {
		if (propertiesFile != null) {
			java.util.Properties props = new java.util.Properties();
			try {
				props.load(new FileReader(propertiesFile));
				return propertiesAsMap(props);
			} catch (IOException e) {
				// should normally not happen
				return null;
			}
		} else
			// should normally not happen
			return null;
	}

	private static Pattern variableReference = Pattern.compile("\\$\\{(?[^\\}]+)\\}");

	private static String expand(String value) {
		Matcher m = variableReference.matcher(value);
		if (m.find()) {
			StringBuilder s = new StringBuilder();
			int i = 0;
			do {
				s.append(value.substring(i, m.start()));
				s.append(getProperty(m.group("var"), m.group()));
				i = m.end();
			} while (m.find());
			s.append(value.substring(i, value.length()));
			return s.toString();
		} else
			return value;
	}

	public static class Property {

		protected final String key;
		private final String value;

		private Property(String key, String value) {
			if (key == null)
				throw new IllegalArgumentException();
			this.key = key;
			this.value = value;
		}

		/**
		 * Get the property name
		 */
		public String getName() {
			return key;
		}

		/**
		 * Get the value
		 */
		public String getValue() {
			return value;
		}

		public String getValue(Map snapshot) {
			return getValue();
		}
	}

	public static class SettableProperty extends Property {

		private final String description;
		private final boolean sensitive;
		private final String defaultValue;
		private String value;

		private SettableProperty(String key, String description, boolean sensitive, String defaultValue, String value) {
			super(key, defaultValue);
			this.description = description;
			this.sensitive = sensitive;
			this.value = value;
			this.defaultValue = defaultValue;
		}

		/**
		 * Set the value
		 */
		public synchronized void setValue(String value) {
			synchronized (settableProperties) {
				this.value = value;
			}
		}

		/**
		 * Get the current value, or the default value if the property has not been set yet and did
		 * not have an initial value.
		 */
		@Override
		public synchronized String getValue() {
			return value != null ? value : defaultValue;
		}

		/**
		 * Get the value that was in effect at the moment the snapshot was made, or the default
		 * value if the value was {@code null}.
		 */
		@Override
		public String getValue(Map snapshot) {
			if (snapshot == null)
				throw new IllegalArgumentException();
			String v = snapshot.get(key);
			if (v != null)
				return v; // already expanded
			return defaultValue;
		}

		public String getDescription() {
			return description;
		}

		public boolean isSensitive() {
			return sensitive;
		}
	}

	private static Map propertiesAsMap(java.util.Properties properties) {
		return (Map)(Map)properties;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy