package org.peerbox.app.config; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class offers functionality for reading and writing simple configuration * properties (key value pairs) which are stored in a text file. * It is based on Java Properties (java.util.Properties). * * @author albrecht * */ abstract class AbstractConfig { private static final Logger logger = LoggerFactory.getLogger(AbstractConfig.class); /** * Separator character for the serialization of lists of values */ protected static final String LIST_SEPARATOR = ","; private final Path propertyFile; private Properties properties; protected AbstractConfig(final Path file) { this.propertyFile = file; } /** * Returns the resource path to default properties. * * @return resource path */ protected abstract String getDefaultPropertiesResource(); /** * @return the path to the config file */ public Path getConfigFile() { return propertyFile; } /** * @return the property instance */ protected Properties getProperties() { return properties; } /** * Returns the value of a property * * @param key of the property * @return value associated with key. May return null if key not available. */ protected String getProperty(String key) { checkLoaded(); return properties.getProperty(key); } /** * Sets the value of a property * * @param key of the property * @param value associated with key */ protected void setProperty(String key, String value) { checkLoaded(); properties.setProperty(key, value); } /** * Removes a property * * @param key of property to clear */ protected void removeProperty(String key) { checkLoaded(); properties.remove(key); } /** * Loads the default and user properties from disk * * @throws IOException if loading of file fails */ public synchronized void load() throws IOException { if (propertyFile == null) { throw new IllegalStateException( "No filename for the user config file given (propertyFile = null)"); } // first read defaults Properties defaultProp = loadDefaultProperties(); // create parent dirs and empty file if not exists yet if (!Files.exists(propertyFile)) { if (!Files.exists(propertyFile.getParent())) { Files.createDirectories(propertyFile.getParent()); } Files.createFile(propertyFile); } properties = loadUserProperties(defaultProp); logger.info("Loaded property file: {}", propertyFile.toAbsolutePath()); } /** * Stores the current properties on disk * * @throws IOException if saving property file fails. */ protected synchronized void saveProperties() throws IOException { checkLoaded(); try (OutputStream out = new FileOutputStream(propertyFile.toFile())) { properties.store(out, null); } } /** * Loads the default properties * * @return default properties instance * @throws IOException if loading default properties fails. */ private Properties loadDefaultProperties() throws IOException { try (InputStream in = getClass().getResourceAsStream(getDefaultPropertiesResource())) { Properties defaultProps = new Properties(); defaultProps.load(in); return defaultProps; } } /** * Loads the user properties. The default and user properties are merged * * @param defaultProp default properties for merging config * @return user properties instance * @throws IOException if loading user properties fails. */ private Properties loadUserProperties(final Properties defaultProp) throws IOException { try (InputStream in = new FileInputStream(propertyFile.toFile())) { Properties p = new Properties(defaultProp); p.load(in); return p; } } private void checkLoaded() { if (properties == null) { throw new IllegalStateException( "Cannot access properties of config. Config file not loaded yet."); } } }