Polish and test BitsquareEnvironment

- Introduce a test-time dependency on spring-test module for access to
   MockPropertySource and friends.

 - Add BitsquareEnvironmentTests and test that property source precedence
   works as expected, i.e. that properties supplied on the command line
   have highest precedence, overriding those picked up via environment
   variables, system properties, the bitsquare.properties file or any of
   the other available property sources.
This commit is contained in:
Chris Beams 2014-11-11 19:24:18 +01:00
parent c8770f96a9
commit 0136ea2884
No known key found for this signature in database
GPG key ID: 3D214F8F5BC5ED73
4 changed files with 70 additions and 44 deletions

View file

@ -49,6 +49,7 @@ dependencies {
compile 'org.jetbrains:annotations:13.0'
compile 'eu.hansolo.enzo:Enzo:0.1.5'
testCompile 'junit:junit:4.11'
testCompile 'org.springframework:spring-test:4.1.1.RELEASE'
}
task packageNative(type: Exec, dependsOn: shadowJar) {

View file

@ -23,10 +23,6 @@ import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.ViewCB;
import io.bitsquare.persistence.Persistence;
import com.google.common.base.Preconditions;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Properties;
@ -42,51 +38,59 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePropertySource;
public class BitsquareEnvironment extends StandardEnvironment {
import static com.google.common.base.Preconditions.checkNotNull;
public static final String APP_NAME_KEY = "app.name";
public static final String DEFAULT_APP_NAME = "Bitsquare";
public class BitsquareEnvironment extends StandardEnvironment {
public static final String USER_DATA_DIR_KEY = "user.data.dir";
public static final String DEFAULT_USER_DATA_DIR = defaultUserDataDir();
public static final String APP_NAME_KEY = "app.name";
public static final String DEFAULT_APP_NAME = "Bitsquare";
public static final String APP_DATA_DIR_KEY = "app.data.dir";
public static final String DEFAULT_APP_DATA_DIR = appDataDir(DEFAULT_USER_DATA_DIR, DEFAULT_APP_NAME);
private static final String BITSQUARE_DEFAULT_PROPERTY_SOURCE_NAME = "bitsquareDefaultProperties";
private static final String BITSQUARE_CLASSPATH_PROPERTY_SOURCE_NAME = "bitsquareClasspathProperties";
private static final String BITSQUARE_FILESYSTEM_PROPERTY_SOURCE_NAME = "bitsquareFilesystemProperties";
private static final String BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME = "bitsquareCommandLineProperties";
static final String BITSQUARE_DEFAULT_PROPERTY_SOURCE_NAME = "bitsquareDefaultProperties";
static final String BITSQUARE_CLASSPATH_PROPERTY_SOURCE_NAME = "bitsquareClasspathProperties";
static final String BITSQUARE_FILESYSTEM_PROPERTY_SOURCE_NAME = "bitsquareFilesystemProperties";
static final String BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME = "bitsquareCommandLineProperties";
private final ResourceLoader resourceLoader = new DefaultResourceLoader();
private final String appName;
private final String appDataDir;
public BitsquareEnvironment(OptionSet options) {
Preconditions.checkArgument(options != null, "Options must not be null");
PropertySource commandLineProperties =
new JOptCommandLinePropertySource(BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME, options);
String appName = commandLineProperties.containsProperty(APP_NAME_KEY) ?
(String) commandLineProperties.getProperty(APP_NAME_KEY) :
DEFAULT_APP_NAME;
this(new JOptCommandLinePropertySource(BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME, checkNotNull(options)));
}
BitsquareEnvironment(PropertySource commandLineProperties) {
String userDataDir = commandLineProperties.containsProperty(USER_DATA_DIR_KEY) ?
(String) commandLineProperties.getProperty(USER_DATA_DIR_KEY) :
DEFAULT_USER_DATA_DIR;
String appDataDir = commandLineProperties.containsProperty(APP_DATA_DIR_KEY) ?
this.appName = commandLineProperties.containsProperty(APP_NAME_KEY) ?
(String) commandLineProperties.getProperty(APP_NAME_KEY) :
DEFAULT_APP_NAME;
this.appDataDir = commandLineProperties.containsProperty(APP_DATA_DIR_KEY) ?
(String) commandLineProperties.getProperty(APP_DATA_DIR_KEY) :
appDataDir(userDataDir, appName);
MutablePropertySources propertySources = this.getPropertySources();
propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, defaultProperties(appDataDir, appName));
propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, classpathProperties());
propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, filesystemProperties(appDataDir));
propertySources.addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, commandLineProperties);
propertySources.addFirst(commandLineProperties);
try {
propertySources.addLast(filesystemProperties());
propertySources.addLast(classpathProperties());
propertySources.addLast(defaultProperties());
} catch (Exception ex) {
throw new BitsquareException(ex);
}
}
private PropertySource<?> defaultProperties(String appDataDir, String appName) {
PropertySource<?> defaultProperties() throws Exception {
return new PropertiesPropertySource(BITSQUARE_DEFAULT_PROPERTY_SOURCE_NAME, new Properties() {{
setProperty(APP_DATA_DIR_KEY, appDataDir);
setProperty(APP_NAME_KEY, appName);
@ -104,28 +108,19 @@ public class BitsquareEnvironment extends StandardEnvironment {
}});
}
private PropertySource<?> classpathProperties() {
try {
Resource resource = resourceLoader.getResource("classpath:bitsquare.properties");
return new ResourcePropertySource(BITSQUARE_CLASSPATH_PROPERTY_SOURCE_NAME, resource);
} catch (IOException ex) {
throw new BitsquareException(ex);
}
PropertySource<?> classpathProperties() throws Exception {
Resource resource = resourceLoader.getResource("classpath:bitsquare.properties");
return new ResourcePropertySource(BITSQUARE_CLASSPATH_PROPERTY_SOURCE_NAME, resource);
}
private PropertySource<?> filesystemProperties(String appDir) {
String location = String.format("file:%s/bitsquare.conf", appDir);
PropertySource<?> filesystemProperties() throws Exception {
String location = String.format("file:%s/bitsquare.conf", appDataDir);
Resource resource = resourceLoader.getResource(location);
if (!resource.exists()) {
if (!resource.exists())
return new PropertySource.StubPropertySource(BITSQUARE_FILESYSTEM_PROPERTY_SOURCE_NAME);
}
try {
return new ResourcePropertySource(BITSQUARE_FILESYSTEM_PROPERTY_SOURCE_NAME, resource);
} catch (IOException ex) {
throw new BitsquareException(ex);
}
return new ResourcePropertySource(BITSQUARE_FILESYSTEM_PROPERTY_SOURCE_NAME, resource);
}

View file

@ -39,8 +39,8 @@ public class BitsquareAppMain extends BitsquareExecutable {
@Override
protected void customizeOptionParsing(OptionParser parser) {
parser.accepts(APP_NAME_KEY, "Application name").withRequiredArg().defaultsTo(DEFAULT_APP_NAME);
parser.accepts(USER_DATA_DIR_KEY, "User data directory").withRequiredArg().defaultsTo(DEFAULT_USER_DATA_DIR);
parser.accepts(APP_NAME_KEY, "Application name").withRequiredArg().defaultsTo(DEFAULT_APP_NAME);
parser.accepts(APP_DATA_DIR_KEY, "Application data directory").withRequiredArg().defaultsTo(DEFAULT_APP_DATA_DIR);
parser.accepts(NAME_KEY, "Network name").withRequiredArg();
parser.accepts(PORT_KEY, "Port to listen on").withRequiredArg().defaultsTo(String.valueOf(Node.DEFAULT_PORT));

View file

@ -19,13 +19,43 @@ package io.bitsquare.app;
import org.junit.Test;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockPropertySource;
import static io.bitsquare.app.BitsquareEnvironment.*;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.*;
import static org.springframework.core.env.PropertySource.named;
import static org.springframework.core.env.StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
import static org.springframework.core.env.StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME;
public class BitsquareEnvironmentTests {
@Test
public void test() {
assertThat(1, equalTo(1));
public void testPropertySourcePrecedence() {
PropertySource commandlineProps = new MockPropertySource(BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME)
.withProperty("key.x", "x.commandline");
PropertySource filesystemProps = new MockPropertySource(BITSQUARE_FILESYSTEM_PROPERTY_SOURCE_NAME)
.withProperty("key.x", "x.env")
.withProperty("key.y", "y.env");
ConfigurableEnvironment env = new BitsquareEnvironment(commandlineProps) {
@Override PropertySource<?> filesystemProperties() { return filesystemProps; }
};
MutablePropertySources propertySources = env.getPropertySources();
assertThat(propertySources.precedenceOf(named(BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME)), equalTo(0));
assertThat(propertySources.precedenceOf(named(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)), equalTo(1));
assertThat(propertySources.precedenceOf(named(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)), equalTo(2));
assertThat(propertySources.precedenceOf(named(BITSQUARE_FILESYSTEM_PROPERTY_SOURCE_NAME)), equalTo(3));
assertThat(propertySources.precedenceOf(named(BITSQUARE_CLASSPATH_PROPERTY_SOURCE_NAME)), equalTo(4));
assertThat(propertySources.precedenceOf(named(BITSQUARE_DEFAULT_PROPERTY_SOURCE_NAME)), equalTo(5));
assertThat(propertySources.size(), equalTo(6));
assertThat(env.getProperty("key.x"), equalTo("x.commandline")); // commandline value wins due to precedence
assertThat(env.getProperty("key.y"), equalTo("y.env")); // env value wins because it's the only one available
}
}