package ch.ge.ve.commons.properties; /*- * #%L * Common properties * %% * Copyright (C) 2015 - 2016 République et Canton de Genève * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import com.google.common.base.Splitter; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.log4j.Logger; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Map; import java.util.Properties; import java.util.ServiceLoader; /** * Service to get the module or application configuration values from a properties file. */ public class PropertyConfigurationService { private static final Logger LOG = Logger.getLogger(PropertyConfigurationService.class); private Properties properties = new Properties(); /** * Creates the configuration service, sourcing itself from the declared configuration providers * (using the Java ServiceLoader). */ public PropertyConfigurationService() { loadConfigurationProviders(); } /** * Creates a new configuration service that sources itself from a properties file, that * is retrieved from the classpath, and from the declared configuration providers * (using the Java ServiceLoader). * * @param propertiesFilePath path to the properties file to use as a source, in the format needed by the classloader. * @throws PropertyConfigurationRuntimeException if fails to load config */ public PropertyConfigurationService(String propertiesFilePath) { final InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(propertiesFilePath); if (resourceAsStream == null) { throw new PropertyConfigurationRuntimeException("Properties file cannot be found: " + propertiesFilePath); } loadConfig(resourceAsStream); } /** * Creates a new configuration service that sources itself from from the declared configuration * providers (using the Java ServiceLoader), and using its default values from a properties object * * @param properties properties object to be used as defaults */ public PropertyConfigurationService(final Properties properties) { this.properties = new Properties(properties); loadConfigurationProviders(); } private void loadConfig(InputStream inputStream) { try { properties.load(inputStream); } catch (IOException e) { throw new PropertyConfigurationRuntimeException("Unable to load the base configuration", e); } loadConfigurationProviders(); } private void loadConfigurationProviders() { // load complementary properties from the service loader ServiceLoader<PropertyConfigurationProvider> providers = ServiceLoader.load(PropertyConfigurationProvider.class); for (PropertyConfigurationProvider provider : providers) { final Properties providerProperties = provider.getProperties(); for (Map.Entry<Object, Object> entry : providerProperties.entrySet()) { // check if property is not already defined if (properties.get(entry.getKey()) != null) { throw new PropertyConfigurationRuntimeException( String.format("Property [%s] is already defined with value [%s]", entry.getKey(), entry.getValue() )); } else { properties.put(entry.getKey(), entry.getValue()); } } } } /** * Gets a property value as an Integer. * * @param key the property key * @return the property value * @throws PropertyConfigurationException if fails to find or convert the property */ public int getConfigValueAsInt(String key) throws PropertyConfigurationException { int valeur; try { valeur = Integer.parseInt(getConfigValue(key)); } catch (NumberFormatException nfe) { throw new PropertyConfigurationException(String.format("The value [%s] for key [%s] is not an integer", getConfigValue(key), key)); } return valeur; } /** * Gets a property value as a Boolean. * * @param key the property key * @return the property value * @throws PropertyConfigurationException if fails to find or convert the property */ public boolean getConfigValueAsBoolean(String key) throws PropertyConfigurationException { return Boolean.parseBoolean(getConfigValue(key)); } /** * Gets a property value as a Long. * * @param key the property key * @return the property value * @throws PropertyConfigurationException if fails to find or convert the property */ public long getConfigValueAsLong(String key) throws PropertyConfigurationException { long valeur; try { valeur = Long.parseLong(getConfigValue(key)); } catch (NumberFormatException nfe) { throw new PropertyConfigurationException(String.format("The value [%s] for key [%s] is not a long", getConfigValue(key), key)); } return valeur; } /** * Gets a property value as a String. * * @param key the property key * @return the property value * @throws PropertyConfigurationException if fails to find or convert the property */ public String getConfigValue(String key) throws PropertyConfigurationException { String retour = properties.getProperty(key); if (retour == null) { if (LOG.isDebugEnabled()) { LOG.debug(String.format("Value %s does not exist", key)); } throw new PropertyConfigurationException(String.format("The property [%s] does not exist", key)); } return retour; } /** * Gets a property value as an array of String. * The property values have to be formatted with each value separated by a comma, * e.g "value1,value2" * * @param key the property key * @return the property value * @throws PropertyConfigurationException if fails to find or convert the property */ public String[] getConfigValueAsArray(String key) throws PropertyConfigurationException { String configValue = properties.getProperty(key); if (configValue == null) { throw new PropertyConfigurationException("The property [" + key + "] does not exist"); } ArrayList<String> elementList = Lists.newArrayList(); Iterable<String> elements = Splitter.on(",").split(configValue); for (String element : elements) { elementList.add(element.trim()); } return Iterables.toArray(elementList, String.class); } /** * Gets a property value as an array of Long. * The property values have to be formatted with each value separated by a comma, * e.g "value1,value2" * * @param key the property key * @return the property value * @throws PropertyConfigurationException if fails to find or convert the property */ public long[] getConfigValueAsArrayLong(String key) throws PropertyConfigurationException { final String[] strings = getConfigValueAsArray(key); long[] values = new long[strings.length]; for (int i = 0; i < strings.length; i++) { final String string = strings[i]; try { values[i] = Long.parseLong(string); } catch (NumberFormatException nfe) { throw new PropertyConfigurationException(String.format("The value [%s] in [%s] for key [%s] is not a long", string, getConfigValue(key), key)); } } return values; } /** * Gets a property value as a String, using a prefix and a key to define the actual key in the properties. * * @param subConfigPrefix prefix of the configuration key * @param key other part of the configuration key * @return the property value * @throws PropertyConfigurationException PropertyConfigurationException if fails to find or convert the property */ public String getSubConfigValue(String subConfigPrefix, String key) throws PropertyConfigurationException { return getConfigValue(buildKey(subConfigPrefix, key)); } private static String buildKey(String subConfigPrefix, String key) { return subConfigPrefix != null ? subConfigPrefix + "." + key : key; } /** * @return all the properties */ public Properties getProperties() { return properties; } /** * Tells whether the key is defined in the properties. * * @param key the property key * @return <code>true</code> if the property id defined, <code>false</code> otherwise */ public boolean isDefined(String key) { return properties.getProperty(key) != null; } /** * Tells whether the key is defined in the properties. * * @param subConfigPrefix prefix of the configuration key * @param key other part of the configuration key * @return <code>true</code> if the property id defined, <code>false</code> otherwise */ public boolean isDefined(String subConfigPrefix, String key) { return isDefined(buildKey(subConfigPrefix, key)); } /** * Add a key/value pair to the configuration properties * * @param key property key * @param value property value */ public void addConfigValue(String key, String value) { assert properties != null; properties.put(key, value); } }