[tor-commits] [collector/master] All non-scheduling properties can now be reconfigured at runtime.
karsten at torproject.org
karsten at torproject.org
Thu Aug 11 08:44:43 UTC 2016
commit b17f3d2d9c26cad3cbc26680a39e964e23a534a8
Author: iwakeh <iwakeh at torproject.org>
Date: Tue Aug 2 21:36:07 2016 +0200
All non-scheduling properties can now be reconfigured at runtime.
Implements #19720:
Adapted testcoverage and test, restructured Configuration, adapted logging.
---
build.xml | 12 +--
src/main/java/org/torproject/collector/Main.java | 28 ++---
.../torproject/collector/conf/Configuration.java | 118 +++++++++++++++++++--
.../torproject/collector/cron/CollecTorMain.java | 34 +++++-
.../org/torproject/collector/cron/Scheduler.java | 2 +-
src/main/resources/collector.properties | 5 +
src/main/resources/logback.xml | 8 ++
.../java/org/torproject/collector/MainTest.java | 46 ++++++--
.../collector/conf/ConfigurationTest.java | 95 +++++++++++++----
9 files changed, 280 insertions(+), 68 deletions(-)
diff --git a/build.xml b/build.xml
index e06bb99..75e00f8 100644
--- a/build.xml
+++ b/build.xml
@@ -239,12 +239,12 @@
</fileset>
</cobertura-report>
<cobertura-check totallinerate="5" totalbranchrate="1" >
- <regex pattern="org.torproject.collector.conf.*" branchrate="100" linerate="100"/>
- <regex pattern="org.torproject.collector.cron.CollecTorMain"
- branchrate="50" linerate="63" />
- <regex pattern="org.torproject.collector.cron.Scheduler"
- branchrate="75" linerate="82" />
- <regex pattern="org.torproject.collector.Main" branchrate="66" linerate="94" />
+ <regex pattern="org.torproject.collector.conf"
+ branchrate="87" linerate="100"/>
+ <regex pattern="org.torproject.collector.cron"
+ branchrate="50" linerate="71" />
+ <regex pattern="org.torproject.collector.Main"
+ branchrate="100" linerate="100" />
</cobertura-check>
</target>
<target name="test" depends="compile,compile-tests">
diff --git a/src/main/java/org/torproject/collector/Main.java b/src/main/java/org/torproject/collector/Main.java
index 97c7a0c..b48c3e6 100644
--- a/src/main/java/org/torproject/collector/Main.java
+++ b/src/main/java/org/torproject/collector/Main.java
@@ -21,6 +21,8 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map;
@@ -57,20 +59,20 @@ public class Main {
* See class description {@link Main}.
*/
public static void main(String[] args) throws Exception {
- File confFile = null;
+ Path confPath = null;
if (args == null || args.length == 0) {
- confFile = new File(CONF_FILE);
+ confPath = Paths.get(CONF_FILE);
} else if (args.length == 1) {
- confFile = new File(args[0]);
+ confPath = Paths.get(args[0]);
} else {
printUsage("CollecTor takes at most one argument.");
return;
}
- if (!confFile.exists() || confFile.length() < 1L) {
- writeDefaultConfig(confFile);
+ if (!confPath.toFile().exists() || confPath.toFile().length() < 1L) {
+ writeDefaultConfig(confPath);
return;
} else {
- readConfigurationFrom(confFile);
+ conf.setWatchableSourceAndLoad(confPath);
}
Scheduler.getInstance().scheduleModuleRuns(collecTorMains, conf);
}
@@ -81,10 +83,10 @@ public class Main {
System.out.println(msg + "\n" + usage);
}
- private static void writeDefaultConfig(File confFile) {
+ private static void writeDefaultConfig(Path confPath) {
try {
Files.copy(Main.class.getClassLoader().getResource(CONF_FILE).openStream(),
- confFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ confPath, StandardCopyOption.REPLACE_EXISTING);
printUsage("Could not find config file. In the default "
+ "configuration, we are not configured to read data from any "
+ "data source or write data to any data sink. You need to "
@@ -93,15 +95,7 @@ public class Main {
+ "Refer to the manual for more information.");
} catch (IOException e) {
log.error("Cannot write default configuration. Reason: " + e, e);
- }
- }
-
- private static void readConfigurationFrom(File confFile) throws Exception {
- try (FileInputStream fis = new FileInputStream(confFile)) {
- conf.load(fis);
- } catch (Exception e) { // catch all possible problems
- log.error("Cannot read configuration. Reason: " + e, e);
- throw e;
+ throw new RuntimeException(e);
}
}
diff --git a/src/main/java/org/torproject/collector/conf/Configuration.java b/src/main/java/org/torproject/collector/conf/Configuration.java
index 9295811..205a977 100644
--- a/src/main/java/org/torproject/collector/conf/Configuration.java
+++ b/src/main/java/org/torproject/collector/conf/Configuration.java
@@ -3,21 +3,125 @@
package org.torproject.collector.conf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.attribute.FileTime;
+import java.util.List;
+import java.util.Observable;
import java.util.Properties;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
/**
* Initialize configuration with defaults from collector.properties,
* unless a configuration properties file is available.
*/
-public class Configuration extends Properties {
+public class Configuration extends Observable implements Cloneable {
+
+ private static final Logger log = LoggerFactory.getLogger(Configuration.class);
+
+ private final ScheduledExecutorService scheduler =
+ Executors.newScheduledThreadPool(1);
public static final String FIELDSEP = ",";
public static final String ARRAYSEP = ";";
+ private final Properties props = new Properties();
+ private Path configurationFile;
+ private FileTime ft;
+
+ /**
+ * Load the configuration from the given path and start monitoring changes.
+ * If the file was changed, re-read and inform all observers.
+ */
+ public void setWatchableSourceAndLoad(final Path confPath) throws
+ ConfigurationException {
+ this.configurationFile = confPath;
+ try {
+ ft = Files.getLastModifiedTime(confPath);
+ reload();
+ } catch (IOException e) {
+ throw new ConfigurationException("Cannot watch configuration file. "
+ + "Reason: " + e.getMessage(), e);
+ }
+ this.scheduler.scheduleAtFixedRate(new Runnable() {
+ public void run() {
+ log.trace("Check configuration file.");
+ try {
+ FileTime ftNow = Files.getLastModifiedTime(confPath);
+ if (ft.compareTo(ftNow) < 0) {
+ log.info("Configuration file was changed.");
+ reload();
+ setChanged();
+ notifyObservers(null);
+ }
+ ft = ftNow;
+ } catch (IOException | RuntimeException re) {
+ log.error("Cannot reload configuration file.", re);
+ }
+ }
+ }, 1, 1, TimeUnit.MINUTES);
+ }
+
+ private final void reload() throws IOException {
+ props.clear();
+ try (FileInputStream fis
+ = new FileInputStream(configurationFile.toFile())) {
+ props.load(fis);
+ }
+ }
+
+ /** Return a copy of all properties. */
+ public Properties getPropertiesCopy() {
+ return (Properties) props.clone();
+ }
+
+ /**
+ * Loads properties from the given stream.
+ */
+ public void load(InputStream fis) throws IOException {
+ props.load(fis);
+ }
+
+ /** Retrieves the value for key. */
+ public String getProperty(String key) {
+ return props.getProperty(key);
+ }
+
+ /** Sets the value for key. */
+ public void setProperty(String key, String value) {
+ props.setProperty(key, value);
+ }
+
+ /** clears all properties. */
+ public void clear() {
+ props.clear();
+ }
+
+ /** Add all given properties. */
+ public void putAll(Properties allProps) {
+ props.putAll(allProps);
+ }
+
+ /** Count of properties. */
+ public int size() {
+ return props.size();
+ }
+
/**
* Returns {@code String[][]} from a property. Commas seperate array elements
* and semicolons separate arrays, e.g.,
@@ -26,7 +130,7 @@ public class Configuration extends Properties {
public String[][] getStringArrayArray(Key key) throws ConfigurationException {
try {
checkClass(key, String[][].class);
- String[] interim = getProperty(key.name()).split(ARRAYSEP);
+ String[] interim = props.getProperty(key.name()).split(ARRAYSEP);
String[][] res = new String[interim.length][];
for (int i = 0; i < interim.length; i++) {
res[i] = interim[i].trim().split(FIELDSEP);
@@ -49,7 +153,7 @@ public class Configuration extends Properties {
public String[] getStringArray(Key key) throws ConfigurationException {
try {
checkClass(key, String[].class);
- String[] res = getProperty(key.name()).split(FIELDSEP);
+ String[] res = props.getProperty(key.name()).split(FIELDSEP);
for (int i = 0; i < res.length; i++) {
res[i] = res[i].trim();
}
@@ -74,7 +178,7 @@ public class Configuration extends Properties {
public boolean getBool(Key key) throws ConfigurationException {
try {
checkClass(key, Boolean.class);
- return Boolean.parseBoolean(getProperty(key.name()));
+ return Boolean.parseBoolean(props.getProperty(key.name()));
} catch (RuntimeException re) {
throw new ConfigurationException("Corrupt property: " + key
+ " reason: " + re.getMessage(), re);
@@ -89,7 +193,7 @@ public class Configuration extends Properties {
public int getInt(Key key) throws ConfigurationException {
try {
checkClass(key, Integer.class);
- String prop = getProperty(key.name());
+ String prop = props.getProperty(key.name());
if ("inf".equals(prop)) {
return Integer.MAX_VALUE;
} else {
@@ -108,7 +212,7 @@ public class Configuration extends Properties {
public Path getPath(Key key) throws ConfigurationException {
try {
checkClass(key, Path.class);
- return Paths.get(getProperty(key.name()));
+ return Paths.get(props.getProperty(key.name()));
} catch (RuntimeException re) {
throw new ConfigurationException("Corrupt property: " + key
+ " reason: " + re.getMessage(), re);
@@ -122,7 +226,7 @@ public class Configuration extends Properties {
public URL getUrl(Key key) throws ConfigurationException {
try {
checkClass(key, URL.class);
- return new URL(getProperty(key.name()));
+ return new URL(props.getProperty(key.name()));
} catch (MalformedURLException mue) {
throw new ConfigurationException("Corrupt property: " + key
+ " reason: " + mue.getMessage(), mue);
diff --git a/src/main/java/org/torproject/collector/cron/CollecTorMain.java b/src/main/java/org/torproject/collector/cron/CollecTorMain.java
index 83d1ec3..34d711a 100644
--- a/src/main/java/org/torproject/collector/cron/CollecTorMain.java
+++ b/src/main/java/org/torproject/collector/cron/CollecTorMain.java
@@ -16,27 +16,45 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Calendar;
import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
-public abstract class CollecTorMain implements Runnable {
+public abstract class CollecTorMain implements Observer, Runnable {
private static Logger log = LoggerFactory.getLogger(CollecTorMain.class);
private static final long LIMIT_MB = 200;
- protected Configuration config;
+ private final AtomicBoolean newConfigAvailable = new AtomicBoolean(false);
+
+ protected Configuration config = new Configuration();
+
+ private Configuration newConfig;
public CollecTorMain(Configuration conf) {
- this.config = conf;
+ this.config.putAll(conf.getPropertiesCopy());
+ conf.addObserver(this);
}
/**
- * Log errors preventing successful completion of the module.
+ * Log all errors preventing successful completion of the module.
*/
@Override
public final void run() {
+ synchronized (this) {
+ if (newConfigAvailable.get()) {
+ log.info("Module {} received new configuration.", module());
+ synchronized (newConfig) {
+ config.clear();
+ config.putAll(newConfig.getPropertiesCopy());
+ newConfigAvailable.set(false);
+ }
+ }
+ }
log.info("Starting {} module of CollecTor.", module());
try {
startProcessing();
@@ -46,6 +64,14 @@ public abstract class CollecTorMain implements Runnable {
log.info("Terminating {} module of CollecTor.", module());
}
+ @Override
+ public synchronized void update(Observable obs, Object obj) {
+ newConfigAvailable.set(true);
+ if (obs instanceof Configuration) {
+ newConfig = (Configuration) obs;
+ }
+ }
+
/**
* Module specific code goes here.
*/
diff --git a/src/main/java/org/torproject/collector/cron/Scheduler.java b/src/main/java/org/torproject/collector/cron/Scheduler.java
index 66fd7be..85cde5d 100644
--- a/src/main/java/org/torproject/collector/cron/Scheduler.java
+++ b/src/main/java/org/torproject/collector/cron/Scheduler.java
@@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit;
/**
* Scheduler that starts the modules configured in collector.properties.
*/
-public class Scheduler implements ThreadFactory {
+public final class Scheduler implements ThreadFactory {
public static final String ACTIVATED = "Activated";
public static final String PERIODMIN = "PeriodMinutes";
diff --git a/src/main/resources/collector.properties b/src/main/resources/collector.properties
index 9ba4c3e..0999fcb 100644
--- a/src/main/resources/collector.properties
+++ b/src/main/resources/collector.properties
@@ -1,6 +1,9 @@
######## Collector Properties
#
######## Run Configuration ########
+## This part of the configuration cannot be updated at runtime!
+## Changes require a restart in order to become effective.
+##
# If RunOnce=true, the activated modules below will only be
# run one time and without any delay.
# Make sure only to run non-interfering modules together.
@@ -35,6 +38,8 @@ UpdateindexActivated = false
UpdateindexPeriodMinutes = 2
# offset in minutes since the epoch and
UpdateindexOffsetMinutes = 0
+##########################################
+## All below can be changed at runtime.
######## General Properties ########
InstanceBaseUrl = "https://collector.torproject.org"
LockFilePath = lock
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index f31cf74..2d8a1ce 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -126,6 +126,14 @@
<appender-ref ref="FILEUPDATEINDEX" />
</logger>
+ <logger name="org.torproject.collector.conf" >
+ <appender-ref ref="FILEBRIDGEDESCS" />
+ <appender-ref ref="FILEEXITLISTS" />
+ <appender-ref ref="FILERELAYDESCS" />
+ <appender-ref ref="FILETORPERF" />
+ <appender-ref ref="FILEUPDATEINDEX" />
+ </logger>
+
<logger name="org.torproject.collector.cron" >
<appender-ref ref="FILEBRIDGEDESCS" />
<appender-ref ref="FILEEXITLISTS" />
diff --git a/src/test/java/org/torproject/collector/MainTest.java b/src/test/java/org/torproject/collector/MainTest.java
index 6b90978..10a6958 100644
--- a/src/test/java/org/torproject/collector/MainTest.java
+++ b/src/test/java/org/torproject/collector/MainTest.java
@@ -9,6 +9,8 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.torproject.collector.conf.Key;
+import org.torproject.collector.conf.Configuration;
+import org.torproject.collector.conf.ConfigurationException;
import org.torproject.collector.cron.Scheduler;
import org.junit.Rule;
@@ -19,39 +21,50 @@ import java.io.BufferedWriter;
import java.io.IOException;
import java.io.File;
import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.List;
import java.util.Properties;
-import java.util.Random;
public class MainTest {
- private Random randomSource = new Random();
-
@Rule
public TemporaryFolder tmpf = new TemporaryFolder();
- @Test(expected = IOException.class)
+ @Test(expected = ConfigurationException.class)
public void testInitializationConfigException() throws Exception {
File conf = new File(Main.CONF_FILE);
- assertFalse("Please remove " + Main.CONF_FILE + " before running tests!", conf.exists());
+ checkCleanEnv(conf);
Main.main(new String[] {"/tmp/"});
assertTrue(conf.exists());
assertTrue(conf.delete());
}
+ private void checkCleanEnv(File conf) {
+ assertFalse("Please remove " + Main.CONF_FILE + " before running tests!",
+ conf.exists());
+ }
+
@Test()
public void testInitializationNullArgs() throws Exception {
File conf = new File(Main.CONF_FILE);
- assertFalse("Please remove " + Main.CONF_FILE + " before running tests!", conf.exists());
+ checkCleanEnv(conf);
Main.main(null);
assertTrue(conf.exists());
assertTrue(conf.delete());
}
+ @Test(expected = RuntimeException.class)
+ public void testInitializationUnwritable() throws Exception {
+ File conf = tmpf.newFolder("folder");
+
+ Main.main(new String[] {
+ Paths.get(conf.toString(), "x", "y", "z").toString()});
+ }
+
@Test()
public void testInitializationEmptyArgs() throws Exception {
File conf = new File(Main.CONF_FILE);
- assertFalse("Please remove " + Main.CONF_FILE + " before running tests!", conf.exists());
+ checkCleanEnv(conf);
Main.main(new String[] { });
assertTrue(conf.exists());
assertTrue(conf.delete());
@@ -60,7 +73,7 @@ public class MainTest {
@Test()
public void testInitializationTooManyArgs() throws Exception {
File conf = new File(Main.CONF_FILE);
- assertFalse("Please remove " + Main.CONF_FILE + " before running tests!", conf.exists());
+ checkCleanEnv(conf);
Main.main(new String[] { "x", "y" });
assertFalse(conf.exists());
}
@@ -72,12 +85,23 @@ public class MainTest {
assertEquals(0L, conf.length());
Main.main(new String[]{conf.toString()});
assertTrue(4_000L <= conf.length());
- changeFilePathsAndSetActivation(conf, lockPath, "TorperfActivated");
+ changeFilePathsAndSetActivation(conf, lockPath,
+ Key.TorperfActivated.name());
Main.main(new String[]{conf.toString()});
- for(int t = 0; t < 1_000_000; t++) { }
+ waitSec(2);
+ }
+
+ public static void waitSec(int sec) {
+ long now = System.currentTimeMillis();
+ while (System.currentTimeMillis() - now < 1_000L * sec) {
+ try {
+ Thread.sleep(sec * 1_000L);
+ } catch (Exception e) {/* ignored */}
+ }
}
- private void changeFilePathsAndSetActivation(File f, File l, String a) throws Exception {
+ private void changeFilePathsAndSetActivation(File f, File l, String a)
+ throws Exception {
List<String> lines = Files.readAllLines(f.toPath());
BufferedWriter bw = Files.newBufferedWriter(f.toPath());
File in = tmpf.newFolder();
diff --git a/src/test/java/org/torproject/collector/conf/ConfigurationTest.java b/src/test/java/org/torproject/collector/conf/ConfigurationTest.java
index 2055ae1..14033dc 100644
--- a/src/test/java/org/torproject/collector/conf/ConfigurationTest.java
+++ b/src/test/java/org/torproject/collector/conf/ConfigurationTest.java
@@ -7,17 +7,29 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import org.torproject.collector.MainTest;
+
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
import java.io.ByteArrayInputStream;
import java.io.File;
+import java.nio.file.Paths;
import java.util.Arrays;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Properties;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
public class ConfigurationTest {
private Random randomSource = new Random();
+ @Rule
+ public TemporaryFolder tmpf = new TemporaryFolder();
+
@Test()
public void testKeyCount() throws Exception {
assertEquals("The number of properties keys in enum Key changed."
@@ -28,9 +40,14 @@ public class ConfigurationTest {
@Test()
public void testConfiguration() throws Exception {
Configuration conf = new Configuration();
- conf.load(new ByteArrayInputStream("TorperfOutputDirectory = xyz".getBytes()));
+ String val = "xyz";
+ conf.setProperty(Key.TorperfOutputDirectory.name(), val);
assertEquals(1, conf.size());
- assertEquals("xyz", conf.getProperty("TorperfOutputDirectory"));
+ assertEquals(val, conf.getProperty(Key.TorperfOutputDirectory.name()));
+ }
+
+ private String propLine(Key key, String val) {
+ return key.name() + " = " + val + "\n";
}
@Test()
@@ -41,14 +58,16 @@ public class ConfigurationTest {
}
String[] arrays = new String[] {
Arrays.toString(array).replace("[", "").replace("]", ""),
- Arrays.toString(array).replace("[", "").replace("]", "").replaceAll(" ", "")
+ Arrays.toString(array).replace("[", "").replace("]", "")
+ .replaceAll(" ", "")
};
Configuration conf = new Configuration();
for(String input : arrays) {
conf.clear();
- conf.load(new ByteArrayInputStream(("CachedRelayDescriptorsDirectories = " + input).getBytes()));
+ conf.setProperty(Key.CachedRelayDescriptorsDirectories.name(), input);
assertArrayEquals("expected " + Arrays.toString(array) + "\nreceived: "
- + Arrays.toString(conf.getStringArray(Key.CachedRelayDescriptorsDirectories)),
+ + Arrays.toString(conf
+ .getStringArray(Key.CachedRelayDescriptorsDirectories)),
array, conf.getStringArray(Key.CachedRelayDescriptorsDirectories));
}
}
@@ -56,9 +75,9 @@ public class ConfigurationTest {
@Test()
public void testBoolValues() throws Exception {
Configuration conf = new Configuration();
- conf.load(new ByteArrayInputStream(("CompressRelayDescriptorDownloads=false"
- + "\nImportDirectoryArchives = trUe"
- + "\nReplaceIpAddressesWithHashes= false").getBytes()));
+ conf.setProperty(Key.CompressRelayDescriptorDownloads.name(), "false");
+ conf.setProperty(Key.ImportDirectoryArchives.name(), "trUe");
+ conf.setProperty(Key.ReplaceIpAddressesWithHashes.name(), "false");
assertFalse(conf.getBool(Key.CompressRelayDescriptorDownloads));
assertTrue(conf.getBool(Key.ImportDirectoryArchives));
assertFalse(conf.getBool(Key.ReplaceIpAddressesWithHashes));
@@ -67,12 +86,13 @@ public class ConfigurationTest {
@Test()
public void testIntValues() throws Exception {
Configuration conf = new Configuration();
- conf.load(new ByteArrayInputStream("BridgeDescriptorMappingsLimit = inf".getBytes()));
+ conf.setProperty(Key.BridgeDescriptorMappingsLimit.name(), "inf");
assertEquals(Integer.MAX_VALUE,
conf.getInt(Key.BridgeDescriptorMappingsLimit));
int r = randomSource.nextInt(Integer.MAX_VALUE);
conf.clear();
- conf.load(new ByteArrayInputStream(("BridgeDescriptorMappingsLimit =" + r).getBytes()));
+ conf.load(new ByteArrayInputStream(
+ propLine(Key.BridgeDescriptorMappingsLimit, "" + r).getBytes()));
assertEquals(r,
conf.getInt(Key.BridgeDescriptorMappingsLimit));
}
@@ -83,8 +103,9 @@ public class ConfigurationTest {
Configuration conf = new Configuration();
for(String file : files) {
conf.clear();
- conf.load(new ByteArrayInputStream(("DirectoryArchivesOutputDirectory = " + file).getBytes()));
- assertEquals(new File(file), conf.getPath(Key.DirectoryArchivesOutputDirectory).toFile());
+ conf.setProperty(Key.DirectoryArchivesOutputDirectory.name(), file);
+ assertEquals(new File(file),
+ conf.getPath(Key.DirectoryArchivesOutputDirectory).toFile());
}
}
@@ -94,52 +115,82 @@ public class ConfigurationTest {
new String[]{"localsource", "http://127.0.0.1:12345"},
new String[]{"somesource", "https://some.host.org:12345"}};
Configuration conf = new Configuration();
- conf.load(new ByteArrayInputStream(("TorperfSources = "
- + Arrays.deepToString(sourceStrings)).replace("[[", "").replace("]]", "")
- .replace("], [", Configuration.ARRAYSEP).getBytes()));
- assertArrayEquals(sourceStrings, conf.getStringArrayArray(Key.TorperfSources));
+ conf.setProperty(Key.TorperfSources.name(),
+ Arrays.deepToString(sourceStrings).replace("[[", "").replace("]]", "")
+ .replace("], [", Configuration.ARRAYSEP));
+ assertArrayEquals(sourceStrings,
+ conf.getStringArrayArray(Key.TorperfSources));
}
@Test(expected = ConfigurationException.class)
public void testArrayArrayValueException() throws Exception {
Configuration conf = new Configuration();
- conf.load(new ByteArrayInputStream("CachedRelayDescriptorsDirectories".getBytes()));
+ conf.setProperty(Key.CachedRelayDescriptorsDirectories.name(), "");
conf.getStringArrayArray(Key.TorperfOutputDirectory);
}
@Test(expected = ConfigurationException.class)
public void testArrayValueException() throws Exception {
Configuration conf = new Configuration();
- conf.load(new ByteArrayInputStream("CachedRelayDescriptorsDirectories".getBytes()));
+ conf.setProperty(Key.CachedRelayDescriptorsDirectories.name(), "");
conf.getStringArray(Key.TorperfSources);
}
@Test(expected = ConfigurationException.class)
public void testBoolValueException() throws Exception {
Configuration conf = new Configuration();
- conf.load(new ByteArrayInputStream("TorperfSource = http://x.y.z".getBytes()));
+ conf.setProperty(Key.TorperfSources.name(), "http://x.y.z");
conf.getBool(Key.CachedRelayDescriptorsDirectories);
}
@Test(expected = ConfigurationException.class)
public void testPathValueException() throws Exception {
Configuration conf = new Configuration();
- conf.load(new ByteArrayInputStream("DirectoryArchivesDirectory = \\u0000:".getBytes()));
+ conf.setProperty(Key.DirectoryArchivesDirectory.name(), "\\\u0000:");
conf.getPath(Key.DirectoryArchivesDirectory);
}
@Test(expected = ConfigurationException.class)
public void testUrlValueException() throws Exception {
Configuration conf = new Configuration();
- conf.load(new ByteArrayInputStream("ExitlistUrl = xxx://y.y.y".getBytes()));
+ conf.setProperty(Key.ExitlistUrl.name(), "xxx://y.y.y");
conf.getUrl(Key.ExitlistUrl);
}
@Test(expected = ConfigurationException.class)
public void testIntValueException() throws Exception {
Configuration conf = new Configuration();
- conf.load(new ByteArrayInputStream("BridgeDescriptorMappingsLimit = y7".getBytes()));
+ conf.setProperty(Key.BridgeDescriptorMappingsLimit.name(), "y7");
conf.getInt(Key.BridgeDescriptorMappingsLimit);
}
+ @Test(expected = ConfigurationException.class)
+ public void testSetWatchableSourceAndLoad() throws Exception {
+ Configuration conf = new Configuration();
+ conf.setWatchableSourceAndLoad(Paths.get("/tmp/phantom.path"));
+ }
+
+ @Test()
+ public void testConfigChange() throws Exception {
+ Configuration conf = new Configuration();
+ final AtomicBoolean called = new AtomicBoolean(false);
+ conf.addObserver(new Observer() {
+ public void update(Observable obs, Object obj) {
+ called.set(true);
+ }
+ });
+ File confFile = tmpf.newFile("empty");
+ conf.setWatchableSourceAndLoad(confFile.toPath());
+ confFile.setLastModified(System.currentTimeMillis());
+ MainTest.waitSec(90);
+ assertTrue("Update was not called.", called.get());
+ called.set(false);
+ MainTest.waitSec(60);
+ assertFalse("Update was called.", called.get());
+ confFile.delete();
+ tmpf.newFolder("empty");
+ MainTest.waitSec(60);
+ assertEquals(0, conf.size());
+ }
+
}
More information about the tor-commits
mailing list