package org.togglz.core.repository.file; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.togglz.core.logging.Log; import org.togglz.core.logging.LogFactory; import org.togglz.core.repository.property.PropertySource; import org.togglz.core.util.IOUtils; /** * Please note that this class is NOT thread-safe. */ class ReloadablePropertiesFile implements PropertySource { private static final Log log = LogFactory.getLog(ReloadablePropertiesFile.class); private final File file; private final int minCheckInterval; private Properties values = new Properties(); private long lastRead = 0; private long lastCheck = 0; public ReloadablePropertiesFile(File file) { this(file, 1000); } public ReloadablePropertiesFile(File file, int minCheckInterval) { this.file = file; this.minCheckInterval = minCheckInterval; } public synchronized void reloadIfUpdated() { long now = System.currentTimeMillis(); if (now - lastCheck > minCheckInterval) { lastCheck = now; if (file.lastModified() > lastRead) { FileInputStream stream = null; try { // read new values stream = new FileInputStream(file); Properties newValues = new Properties(); newValues.load(stream); // update state values = newValues; lastRead = System.currentTimeMillis(); log.info("Reloaded file: " + file.getCanonicalPath()); } catch (FileNotFoundException e) { log.debug("File not found: " + file); } catch (IOException e) { log.error("Failed to read file", e); } finally { IOUtils.close(stream); } } } } public String getValue(String key, String defaultValue) { return values.getProperty(key, defaultValue); } public Set<String> getKeysStartingWith(String prefix) { Set<String> result = new HashSet<String>(); Enumeration<?> keys = values.propertyNames(); while (keys.hasMoreElements()) { String key = keys.nextElement().toString(); if (key.startsWith(prefix)) { result.add(key); } } return result; } public PropertySource.Editor getEditor() { return new PropertyFileEditor(values); } private void write(Properties newValues) { try { FileOutputStream fos = new FileOutputStream(file); newValues.store(fos, null); fos.flush(); fos.close(); } catch (IOException e) { throw new IllegalStateException("Failed to write new values", e); } lastRead = 0; lastCheck = 0; } private class PropertyFileEditor implements PropertySource.Editor { private Properties newValues; private PropertyFileEditor(Properties props) { newValues = new Properties(); newValues.putAll(props); } public void setValue(String key, String value) { if (value != null) { newValues.setProperty(key, value); } else { newValues.remove(key); } } public void removeKeysStartingWith(String prefix) { Iterator<Entry<Object, Object>> iterator = newValues.entrySet().iterator(); while (iterator.hasNext()) { Entry<Object, Object> entry = iterator.next(); if (entry.getKey().toString().startsWith(prefix)) { iterator.remove(); } } } public void commit() { write(newValues); } } }