From acf44128b2e8274db9dc41318fec854341668ff9 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Mon, 10 Nov 2014 15:48:27 +0100 Subject: [PATCH] Overhaul property management and main class infrastructure Use of the Spring Environment ----------------------------- This change replaces the use of the argparse4j library and basic Properties objects with the Spring Framework's Environment abstraction. The Environment allows for managing any number of 'property sources' in a hierarchical fashion, such that a call to `environment.getProperty("someKey")` iterates through an ordered set of property sources, returning the first value associated with the given key. BitsquareEnvironment, introduced in this commit, eliminates the functionality previously present in ConfigLoader, modeling the bitsquare.conf and bitsquare.properties files as Spring Resource objects, and in turn creating ResourcePropertySources out of them. These custom property sources are combined with standard property sources based on system environment variables and Java system properties as well as a property source based on the command-line arguments passed to a Bitsquare application to form a unified, one-stop configuration hierarchy. For example, let's say a Bitsquare user wishes to customize the port that his Bitsquare application listens on. The simplest approach (assuming the user is comfortable with the command line), would be the following: java -jar bitsquare.jar --port=1234 where '1234' is the custom port of choice. This is convenient enough for one-off experimentation, but if the user wishes to make this a permanent arrangement, he may want to add a `port=1234` entry to his {bitsquare_app_dir}/bitsquare.conf file. Alternatively, the user may wish to specify the port value as an environment variable, e.g.: PORT=1234 java -jar bitsquare.jar or with a JVM system property, e.g.: java -jar -DPORT=1234 bitsquare.jar With BitsquareEnvironment, and its customized set of PropertySources in place, the value of the port property may be specified in any of the ways described above, and it is all handled in a unified way. Restructuring of *Main classes ------------------------------ This commit also introduces significant changes to the structure of executable Bitsquare applications. For example, prior to this change, the io.bitsquare.app.gui.Main class was responsible for being both a JavaFX Application and a standard Java main class. Now, however, these concerns have been renamed and separated. BitsquareApp is the JavaFX Application, and BitsquareAppMain is the Java main class. Likewise, BootstrapNode has been broken out into BootstrapNode and BootstrapNodeMain. A common base class for the *Main classes has been extracted, named BitsquareExecutable, which creates a template for option parsing, environment creation, and ultimately application execution that applies both to the BootstrapNode and BitsquareApp cases. Improved help text ------------------ With the removal of argparse4j and the introduction of JOpt for argument parsing, the application's help text has been improved. Use --help to display this text, where you'll see information about default values, etc. To do this easily from the Gradle build, run any of the following commands: # Display help text ./gradlew run -Pargs="--help" # Qualify the application name as "Bitsquare-Alice" ./gradlew run -Pargs="--appName=Alice" # Customize the port ./gradlew run -Pargs="--port=7377" Renaming of FatalException -------------------------- Finally, the exception formerly known as io.bitsquare.gui.FatalException has been moved up a package and generalized to io.bitsquare.BitsquareException, as it is now used more widely. --- build.gradle | 5 +- ...Exception.java => BitsquareException.java} | 12 ++- .../java/io/bitsquare/BitsquareModule.java | 9 +- .../java/io/bitsquare/app/ArgumentParser.java | 75 -------------- .../bitsquare/app/BitsquareEnvironment.java | 98 +++++++++++++++++++ .../io/bitsquare/app/BitsquareExecutable.java | 57 +++++++++++ .../io/bitsquare/app/cli/BootstrapNode.java | 26 ++--- .../bitsquare/app/cli/BootstrapNodeMain.java | 42 ++++++++ .../app/gui/{Main.java => BitsquareApp.java} | 67 ++++--------- .../bitsquare/app/gui/BitsquareAppMain.java | 57 +++++++++++ .../BitsquareAppModule.java} | 42 ++++---- .../java/io/bitsquare/btc/BitcoinModule.java | 8 +- .../io/bitsquare/crypto/CryptoModule.java | 6 +- src/main/java/io/bitsquare/gui/GuiModule.java | 8 +- .../java/io/bitsquare/gui/ViewLoader.java | 5 +- .../java/io/bitsquare/msg/MessageModule.java | 6 +- .../msg/tomp2p/TomP2PMessageModule.java | 16 +-- .../java/io/bitsquare/offer/OfferModule.java | 6 +- .../offer/tomp2p/TomP2POfferModule.java | 6 +- .../java/io/bitsquare/trade/TradeModule.java | 6 +- .../java/io/bitsquare/util/ConfigLoader.java | 77 --------------- .../bitsquare/BitsquareEnvironmentTests.java} | 36 +++---- .../io/bitsquare/app/gui/ViewLoaderTests.java | 15 ++- 23 files changed, 380 insertions(+), 305 deletions(-) rename src/main/java/io/bitsquare/{gui/FatalException.java => BitsquareException.java} (73%) delete mode 100644 src/main/java/io/bitsquare/app/ArgumentParser.java create mode 100644 src/main/java/io/bitsquare/app/BitsquareEnvironment.java create mode 100644 src/main/java/io/bitsquare/app/BitsquareExecutable.java create mode 100644 src/main/java/io/bitsquare/app/cli/BootstrapNodeMain.java rename src/main/java/io/bitsquare/app/gui/{Main.java => BitsquareApp.java} (61%) create mode 100644 src/main/java/io/bitsquare/app/gui/BitsquareAppMain.java rename src/main/java/io/bitsquare/app/{AppModule.java => gui/BitsquareAppModule.java} (71%) delete mode 100644 src/main/java/io/bitsquare/util/ConfigLoader.java rename src/{main/java/io/bitsquare/app/gui/MainModule.java => test/java/io/bitsquare/BitsquareEnvironmentTests.java} (50%) diff --git a/build.gradle b/build.gradle index c221fd02d7..57e31ed917 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ sourceCompatibility = 1.8 sourceSets.main.resources.srcDirs += 'src/main/java' -mainClassName = "io.bitsquare.app.gui.Main" +mainClassName = "io.bitsquare.app.gui.BitsquareAppMain" run { if (project.hasProperty('args')) { @@ -33,6 +33,8 @@ repositories { dependencies { compile 'org.bitcoinj:bitcoinj-core:0.12' compile 'net.tomp2p:tomp2p-all:5.0-Alpha.32cd9f9-SNAPSHOT' + compile 'org.springframework:spring-core:4.1.1.RELEASE' + compile 'net.sf.jopt-simple:jopt-simple:4.8' compile 'org.slf4j:slf4j-api:1.7.7' compile 'ch.qos.logback:logback-core:1.1.2' compile 'ch.qos.logback:logback-classic:1.1.2' @@ -45,7 +47,6 @@ dependencies { compile 'com.google.code.findbugs:jsr305:2.0.3' compile 'net.jcip:jcip-annotations:1.0' compile 'org.jetbrains:annotations:13.0' - compile 'net.sourceforge.argparse4j:argparse4j:0.4.4' compile 'eu.hansolo.enzo:Enzo:0.1.5' testCompile 'junit:junit:4.11' } diff --git a/src/main/java/io/bitsquare/gui/FatalException.java b/src/main/java/io/bitsquare/BitsquareException.java similarity index 73% rename from src/main/java/io/bitsquare/gui/FatalException.java rename to src/main/java/io/bitsquare/BitsquareException.java index 1ceb228864..ced9490e4f 100644 --- a/src/main/java/io/bitsquare/gui/FatalException.java +++ b/src/main/java/io/bitsquare/BitsquareException.java @@ -15,16 +15,20 @@ * along with Bitsquare. If not, see . */ -package io.bitsquare.gui; +package io.bitsquare; @SuppressWarnings("serializable") -public class FatalException extends RuntimeException { +public class BitsquareException extends RuntimeException { - public FatalException(String format, Object... args) { + public BitsquareException(Throwable cause) { + super(cause); + } + + public BitsquareException(String format, Object... args) { super(String.format(format, args)); } - public FatalException(Throwable cause, String format, Object... args) { + public BitsquareException(Throwable cause, String format, Object... args) { super(String.format(format, args), cause); } } diff --git a/src/main/java/io/bitsquare/BitsquareModule.java b/src/main/java/io/bitsquare/BitsquareModule.java index 9293650999..1e244cc936 100644 --- a/src/main/java/io/bitsquare/BitsquareModule.java +++ b/src/main/java/io/bitsquare/BitsquareModule.java @@ -22,17 +22,18 @@ import com.google.common.collect.Sets; import com.google.inject.AbstractModule; import com.google.inject.Injector; -import java.util.Properties; import java.util.Set; +import org.springframework.core.env.Environment; + public abstract class BitsquareModule extends AbstractModule { - protected final Properties properties; + protected final Environment env; private final Set modules = Sets.newHashSet(); - protected BitsquareModule(Properties properties) { - this.properties = properties; + protected BitsquareModule(Environment env) { + this.env = env; } protected void install(BitsquareModule module) { diff --git a/src/main/java/io/bitsquare/app/ArgumentParser.java b/src/main/java/io/bitsquare/app/ArgumentParser.java deleted file mode 100644 index e3a847d77e..0000000000 --- a/src/main/java/io/bitsquare/app/ArgumentParser.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of Bitsquare. - * - * Bitsquare 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. - * - * Bitsquare 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 Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bitsquare. If not, see . - */ - -package io.bitsquare.app; - -import io.bitsquare.btc.BitcoinModule; -import io.bitsquare.network.Node; - -import net.sourceforge.argparse4j.ArgumentParsers; -import net.sourceforge.argparse4j.inf.ArgumentParserException; -import net.sourceforge.argparse4j.inf.Namespace; - -import static io.bitsquare.app.AppModule.APP_NAME_KEY; -import static io.bitsquare.msg.tomp2p.TomP2PMessageModule.*; - -public class ArgumentParser { - - private final net.sourceforge.argparse4j.inf.ArgumentParser parser; - - public ArgumentParser() { - parser = ArgumentParsers.newArgumentParser("Bitsquare") - .defaultHelp(true) - .description("Bitsquare - The decentralized bitcoin exchange"); - - // Args for local node config - parser.addArgument("--" + Node.NAME_KEY) - .help("Local node name"); - parser.addArgument("--" + Node.PORT_KEY) - .help("Local node port"); - - // Args for bootstrap node config - parser.addArgument("--" + BOOTSTRAP_NODE_NAME_KEY) - .help("Bootstrap node name"); - parser.addArgument("--" + BOOTSTRAP_NODE_IP_KEY) - .help("Bootstrap node IP address"); - parser.addArgument("--" + BOOTSTRAP_NODE_PORT_KEY) - .help("Bootstrap node port"); - - // A custom network interface (needed at the moment for windows, but might be useful also later) - parser.addArgument("--" + NETWORK_INTERFACE_KEY) - .help("Network interface"); - - parser.addArgument("--" + BitcoinModule.BITCOIN_NETWORK_KEY) - .setDefault(BitcoinModule.DEFAULT_BITCOIN_NETWORK.toString()) - .help("Bitcoin network to use"); - - // Args for app config - parser.addArgument("-n", "--" + APP_NAME_KEY) - .help("Name to append to default application name"); - } - - public Namespace parseArgs(String... args) { - try { - return parser.parseArgs(args); - } catch (ArgumentParserException e) { - parser.handleError(e); - System.exit(1); - return null; - } - } -} diff --git a/src/main/java/io/bitsquare/app/BitsquareEnvironment.java b/src/main/java/io/bitsquare/app/BitsquareEnvironment.java new file mode 100644 index 0000000000..2155ba3bf8 --- /dev/null +++ b/src/main/java/io/bitsquare/app/BitsquareEnvironment.java @@ -0,0 +1,98 @@ +/* + * This file is part of Bitsquare. + * + * Bitsquare 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. + * + * Bitsquare 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 Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bitsquare. If not, see . + */ + +package io.bitsquare.app; + +import io.bitsquare.BitsquareException; + +import com.google.common.base.Preconditions; + +import java.io.IOException; + +import java.util.Properties; + +import joptsimple.OptionSet; +import lighthouse.files.AppDirectory; +import org.springframework.core.env.JOptCommandLinePropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.ResourcePropertySource; + +public class BitsquareEnvironment extends StandardEnvironment { + + public static final String APP_NAME_KEY = "appName"; + public static final String DEFAULT_APP_NAME = "Bitsquare"; + + private static final String BITSQUARE_APP_PROPERTY_SOURCE_NAME = "bitsquareAppProperties"; + 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"; + + private final ResourceLoader resourceLoader = new DefaultResourceLoader(); + + 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) ? + DEFAULT_APP_NAME + "-" + commandLineProperties.getProperty(APP_NAME_KEY) : + DEFAULT_APP_NAME; + + MutablePropertySources propertySources = this.getPropertySources(); + propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, appProperties(appName)); + propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, classpathProperties()); + propertySources.addBefore(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, filesystemProperties(appName)); + propertySources.addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, commandLineProperties); + } + + private PropertySource appProperties(String appName) { + return new PropertiesPropertySource(BITSQUARE_APP_PROPERTY_SOURCE_NAME, new Properties() {{ + setProperty(APP_NAME_KEY, appName); + }}); + } + + 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); + } + } + + private PropertySource filesystemProperties(String appName) { + String location = String.format("file:%s/bitsquare.conf", AppDirectory.dir(appName)); + Resource resource = resourceLoader.getResource(location); + + 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); + } + } +} diff --git a/src/main/java/io/bitsquare/app/BitsquareExecutable.java b/src/main/java/io/bitsquare/app/BitsquareExecutable.java new file mode 100644 index 0000000000..fd5b3f35f4 --- /dev/null +++ b/src/main/java/io/bitsquare/app/BitsquareExecutable.java @@ -0,0 +1,57 @@ +/* + * This file is part of Bitsquare. + * + * Bitsquare 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. + * + * Bitsquare 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 Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bitsquare. If not, see . + */ + +package io.bitsquare.app; + +import joptsimple.OptionException; +import joptsimple.OptionParser; +import joptsimple.OptionSet; + +public abstract class BitsquareExecutable { + public static final int EXIT_SUCCESS = 0; + public static final int EXIT_FAILURE = 1; + public static final String HELP_KEY = "help"; + + public void execute(String[] args) throws Exception { + OptionParser parser = new OptionParser(); + parser.accepts(HELP_KEY, "This help text").forHelp(); + + this.customizeOptionParsing(parser); + + OptionSet options; + try { + options = parser.parse(args); + if (options.has(HELP_KEY)) { + parser.printHelpOn(System.out); + System.exit(EXIT_SUCCESS); + return; + } + } catch (OptionException ex) { + System.out.println("error: " + ex.getMessage()); + System.out.println(); + parser.printHelpOn(System.out); + System.exit(EXIT_FAILURE); + return; + } + + this.doExecute(options); + } + + protected abstract void customizeOptionParsing(OptionParser parser); + + protected abstract void doExecute(OptionSet options); +} diff --git a/src/main/java/io/bitsquare/app/cli/BootstrapNode.java b/src/main/java/io/bitsquare/app/cli/BootstrapNode.java index b5d076a4a6..296d7e55b0 100644 --- a/src/main/java/io/bitsquare/app/cli/BootstrapNode.java +++ b/src/main/java/io/bitsquare/app/cli/BootstrapNode.java @@ -17,7 +17,6 @@ package io.bitsquare.app.cli; -import io.bitsquare.app.ArgumentParser; import io.bitsquare.network.Node; import net.tomp2p.dht.PeerBuilderDHT; @@ -33,7 +32,7 @@ import net.tomp2p.rpc.ObjectDataReply; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.sourceforge.argparse4j.inf.Namespace; +import org.springframework.core.env.Environment; public class BootstrapNode { private static final Logger log = LoggerFactory.getLogger(BootstrapNode.class); @@ -41,16 +40,15 @@ public class BootstrapNode { private static Peer peer = null; private static boolean running = true; - public static void main(String[] args) throws Exception { - ArgumentParser parser = new ArgumentParser(); - Namespace namespace = parser.parseArgs(args); + private final Environment env; - String name = namespace.getString(Node.NAME_KEY); - if (name == null) - throw new IllegalArgumentException(String.format("--%s option is required", Node.NAME_KEY)); + public BootstrapNode(Environment env) { + this.env = env; + } - String portValue = namespace.getString(Node.PORT_KEY); - int port = portValue != null ? Integer.valueOf(portValue) : Node.DEFAULT_PORT; + public void start() { + String name = env.getRequiredProperty(Node.NAME_KEY); + int port = env.getProperty(Node.PORT_KEY, Integer.class, Node.DEFAULT_PORT); try { Number160 peerId = Number160.createHash(name); @@ -90,12 +88,4 @@ public class BootstrapNode { peer.shutdown().awaitUninterruptibly(); } } - - public static void stop() { - running = false; - if (peer != null) { - peer.shutdown().awaitUninterruptibly(); - } - peer = null; - } } diff --git a/src/main/java/io/bitsquare/app/cli/BootstrapNodeMain.java b/src/main/java/io/bitsquare/app/cli/BootstrapNodeMain.java new file mode 100644 index 0000000000..b45849e4f6 --- /dev/null +++ b/src/main/java/io/bitsquare/app/cli/BootstrapNodeMain.java @@ -0,0 +1,42 @@ +/* + * This file is part of Bitsquare. + * + * Bitsquare 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. + * + * Bitsquare 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 Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bitsquare. If not, see . + */ + +package io.bitsquare.app.cli; + +import io.bitsquare.app.BitsquareEnvironment; +import io.bitsquare.app.BitsquareExecutable; +import io.bitsquare.network.Node; + +import joptsimple.OptionParser; +import joptsimple.OptionSet; + +public class BootstrapNodeMain extends BitsquareExecutable { + + public static void main(String[] args) throws Exception { + new BootstrapNodeMain().execute(args); + } + + protected void customizeOptionParsing(OptionParser parser) { + parser.accepts(Node.NAME_KEY, "Name of this node").withRequiredArg().isRequired(); + parser.accepts(Node.PORT_KEY, "Port to listen on").withRequiredArg() + .defaultsTo(String.valueOf(Node.DEFAULT_PORT)); + } + + protected void doExecute(OptionSet options) { + new BootstrapNode(new BitsquareEnvironment(options)).start(); + } +} diff --git a/src/main/java/io/bitsquare/app/gui/Main.java b/src/main/java/io/bitsquare/app/gui/BitsquareApp.java similarity index 61% rename from src/main/java/io/bitsquare/app/gui/Main.java rename to src/main/java/io/bitsquare/app/gui/BitsquareApp.java index 899d7aba7c..2fe756c9de 100644 --- a/src/main/java/io/bitsquare/app/gui/Main.java +++ b/src/main/java/io/bitsquare/app/gui/BitsquareApp.java @@ -17,7 +17,7 @@ package io.bitsquare.app.gui; -import io.bitsquare.app.ArgumentParser; +import io.bitsquare.app.BitsquareEnvironment; import io.bitsquare.gui.Navigation; import io.bitsquare.gui.SystemTray; import io.bitsquare.gui.ViewLoader; @@ -26,8 +26,8 @@ import io.bitsquare.gui.util.ImageUtil; import io.bitsquare.persistence.Persistence; import io.bitsquare.settings.Settings; import io.bitsquare.user.User; -import io.bitsquare.util.ConfigLoader; +import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.inject.Guice; @@ -35,8 +35,6 @@ import com.google.inject.Injector; import java.io.IOException; -import java.util.Properties; - import javafx.application.Application; import javafx.scene.*; import javafx.scene.image.*; @@ -47,59 +45,28 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import lighthouse.files.AppDirectory; -import net.sourceforge.argparse4j.inf.Namespace; +import org.springframework.core.env.Environment; -import static io.bitsquare.app.AppModule.APP_NAME_KEY; -import static io.bitsquare.btc.BitcoinModule.BITCOIN_NETWORK_KEY; -import static io.bitsquare.msg.tomp2p.TomP2PMessageModule.*; -import static io.bitsquare.network.Node.*; +public class BitsquareApp extends Application { + private static final Logger log = LoggerFactory.getLogger(BitsquareApp.class); -public class Main extends Application { - private static final Logger log = LoggerFactory.getLogger(Main.class); - private static String appName = "Bitsquare"; - private static Properties properties; + private static Environment env; - private MainModule mainModule; + private BitsquareAppModule bitsquareAppModule; private Injector injector; - public static void main(String[] args) { - Namespace argumentsNamespace = new ArgumentParser().parseArgs(args); - - if (argumentsNamespace.getString(APP_NAME_KEY) != null) - appName = appName + "-" + argumentsNamespace.getString(APP_NAME_KEY); - - properties = ConfigLoader.loadConfig(appName); - - properties.setProperty(APP_NAME_KEY, appName); - - if (argumentsNamespace.getString(NAME_KEY) != null) - properties.setProperty(NAME_KEY, argumentsNamespace.getString(NAME_KEY)); - - if (argumentsNamespace.getString(PORT_KEY) != null) - properties.setProperty(PORT_KEY, argumentsNamespace.getString(PORT_KEY)); - - if (argumentsNamespace.getString(BOOTSTRAP_NODE_NAME_KEY) != null) - properties.setProperty(BOOTSTRAP_NODE_NAME_KEY, argumentsNamespace.getString(BOOTSTRAP_NODE_NAME_KEY)); - - if (argumentsNamespace.getString(BOOTSTRAP_NODE_IP_KEY) != null) - properties.setProperty(BOOTSTRAP_NODE_IP_KEY, argumentsNamespace.getString(BOOTSTRAP_NODE_IP_KEY)); - - if (argumentsNamespace.getString(BOOTSTRAP_NODE_PORT_KEY) != null) - properties.setProperty(BOOTSTRAP_NODE_PORT_KEY, argumentsNamespace.getString(BOOTSTRAP_NODE_PORT_KEY)); - - if (argumentsNamespace.getString(NETWORK_INTERFACE_KEY) != null) - properties.setProperty(NETWORK_INTERFACE_KEY, argumentsNamespace.getString(NETWORK_INTERFACE_KEY)); - - if (argumentsNamespace.getString(BITCOIN_NETWORK_KEY) != null) - properties.setProperty(BITCOIN_NETWORK_KEY, argumentsNamespace.getString(BITCOIN_NETWORK_KEY)); - - Application.launch(Main.class, args); + public static void setEnvironment(Environment env) { + BitsquareApp.env = env; } @Override - public void start(Stage primaryStage) { - mainModule = new MainModule(properties, primaryStage); - injector = Guice.createInjector(mainModule); + public void start(Stage primaryStage) throws IOException { + Preconditions.checkArgument(env != null, "Environment must not be null"); + + String appName = env.getRequiredProperty(BitsquareEnvironment.APP_NAME_KEY); + + bitsquareAppModule = new BitsquareAppModule(env, primaryStage); + injector = Guice.createInjector(bitsquareAppModule); // route uncaught exceptions to a user-facing dialog @@ -173,7 +140,7 @@ public class Main extends Application { @Override public void stop() { - mainModule.close(injector); + bitsquareAppModule.close(injector); System.exit(0); } } diff --git a/src/main/java/io/bitsquare/app/gui/BitsquareAppMain.java b/src/main/java/io/bitsquare/app/gui/BitsquareAppMain.java new file mode 100644 index 0000000000..1bc8fbe0b5 --- /dev/null +++ b/src/main/java/io/bitsquare/app/gui/BitsquareAppMain.java @@ -0,0 +1,57 @@ +/* + * This file is part of Bitsquare. + * + * Bitsquare 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. + * + * Bitsquare 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 Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bitsquare. If not, see . + */ + +package io.bitsquare.app.gui; + +import io.bitsquare.app.BitsquareEnvironment; +import io.bitsquare.app.BitsquareExecutable; +import io.bitsquare.btc.BitcoinModule; +import io.bitsquare.network.BootstrapNodes; +import io.bitsquare.network.Node; + +import joptsimple.OptionParser; +import joptsimple.OptionSet; + +import static io.bitsquare.app.BitsquareEnvironment.APP_NAME_KEY; +import static io.bitsquare.btc.BitcoinModule.BITCOIN_NETWORK_KEY; +import static io.bitsquare.msg.tomp2p.TomP2PMessageModule.*; +import static io.bitsquare.network.Node.*; + +public class BitsquareAppMain extends BitsquareExecutable { + + public static void main(String[] args) throws Exception { + new BitsquareAppMain().execute(args); + } + + @Override + protected void customizeOptionParsing(OptionParser parser) { + parser.accepts(APP_NAME_KEY, "Qualified application name").withRequiredArg(); + parser.accepts(NAME_KEY, "Name of this node").withRequiredArg(); + parser.accepts(PORT_KEY, "Port to listen on").withRequiredArg().defaultsTo(String.valueOf(Node.DEFAULT_PORT)); + parser.accepts(BITCOIN_NETWORK_KEY).withRequiredArg().defaultsTo(BitcoinModule.DEFAULT_BITCOIN_NETWORK); + parser.accepts(BOOTSTRAP_NODE_NAME_KEY).withRequiredArg().defaultsTo(BootstrapNodes.DEFAULT.getName()); + parser.accepts(BOOTSTRAP_NODE_IP_KEY).withRequiredArg().defaultsTo(BootstrapNodes.DEFAULT.getIp()); + parser.accepts(BOOTSTRAP_NODE_PORT_KEY).withRequiredArg().defaultsTo(BootstrapNodes.DEFAULT.getPortAsString()); + parser.accepts(NETWORK_INTERFACE_KEY, "Network interface").withRequiredArg(); + } + + @Override + protected void doExecute(OptionSet options) { + BitsquareApp.setEnvironment(new BitsquareEnvironment(options)); + javafx.application.Application.launch(BitsquareApp.class); + } +} diff --git a/src/main/java/io/bitsquare/app/AppModule.java b/src/main/java/io/bitsquare/app/gui/BitsquareAppModule.java similarity index 71% rename from src/main/java/io/bitsquare/app/AppModule.java rename to src/main/java/io/bitsquare/app/gui/BitsquareAppModule.java index 8b7a134a0e..8e2343f059 100644 --- a/src/main/java/io/bitsquare/app/AppModule.java +++ b/src/main/java/io/bitsquare/app/gui/BitsquareAppModule.java @@ -15,11 +15,13 @@ * along with Bitsquare. If not, see . */ -package io.bitsquare.app; +package io.bitsquare.app.gui; import io.bitsquare.BitsquareModule; +import io.bitsquare.app.BitsquareEnvironment; import io.bitsquare.btc.BitcoinModule; import io.bitsquare.crypto.CryptoModule; +import io.bitsquare.gui.GuiModule; import io.bitsquare.msg.MessageModule; import io.bitsquare.msg.tomp2p.TomP2PMessageModule; import io.bitsquare.offer.OfferModule; @@ -29,21 +31,20 @@ import io.bitsquare.settings.Settings; import io.bitsquare.trade.TradeModule; import io.bitsquare.user.User; -import com.google.common.base.Preconditions; - import com.google.inject.Injector; import com.google.inject.name.Names; -import java.util.Properties; +import javafx.stage.Stage; -/** - * Configures all non-UI modules necessary to run a Bitsquare application. - */ -public class AppModule extends BitsquareModule { - public static final String APP_NAME_KEY = "appName"; +import org.springframework.core.env.Environment; - public AppModule(Properties properties) { - super(properties); +class BitsquareAppModule extends BitsquareModule { + + private final Stage primaryStage; + + public BitsquareAppModule(Environment env, Stage primaryStage) { + super(env); + this.primaryStage = primaryStage; } @Override @@ -57,35 +58,38 @@ public class AppModule extends BitsquareModule { install(cryptoModule()); install(tradeModule()); install(offerModule()); + install(guiModule()); - String appName = properties.getProperty(APP_NAME_KEY); - Preconditions.checkArgument(appName != null, "App name must be non-null"); + String appName = env.getRequiredProperty(BitsquareEnvironment.APP_NAME_KEY); bindConstant().annotatedWith(Names.named("appName")).to(appName); } protected MessageModule messageModule() { - return new TomP2PMessageModule(properties); + return new TomP2PMessageModule(env); } protected BitcoinModule bitcoinModule() { - return new BitcoinModule(properties); + return new BitcoinModule(env); } protected CryptoModule cryptoModule() { - return new CryptoModule(properties); + return new CryptoModule(env); } protected TradeModule tradeModule() { - return new TradeModule(properties); + return new TradeModule(env); } protected OfferModule offerModule() { - return new TomP2POfferModule(properties); + return new TomP2POfferModule(env); + } + + protected GuiModule guiModule() { + return new GuiModule(env, primaryStage); } @Override protected void doClose(Injector injector) { } } - diff --git a/src/main/java/io/bitsquare/btc/BitcoinModule.java b/src/main/java/io/bitsquare/btc/BitcoinModule.java index c43edba080..1d77a557ff 100644 --- a/src/main/java/io/bitsquare/btc/BitcoinModule.java +++ b/src/main/java/io/bitsquare/btc/BitcoinModule.java @@ -26,15 +26,15 @@ import org.bitcoinj.params.TestNet3Params; import com.google.inject.Injector; -import java.util.Properties; +import org.springframework.core.env.Environment; public class BitcoinModule extends BitsquareModule { public static final String BITCOIN_NETWORK_KEY = "bitcoin.network"; public static final String DEFAULT_BITCOIN_NETWORK = BitcoinNetwork.TESTNET.toString(); - public BitcoinModule(Properties properties) { - super(properties); + public BitcoinModule(Environment env) { + super(env); } @Override @@ -52,7 +52,7 @@ public class BitcoinModule extends BitsquareModule { private NetworkParameters network() { BitcoinNetwork network = BitcoinNetwork.valueOf( - properties.getProperty(BITCOIN_NETWORK_KEY, DEFAULT_BITCOIN_NETWORK).toUpperCase()); + env.getProperty(BITCOIN_NETWORK_KEY, DEFAULT_BITCOIN_NETWORK).toUpperCase()); switch (network) { case MAINNET: diff --git a/src/main/java/io/bitsquare/crypto/CryptoModule.java b/src/main/java/io/bitsquare/crypto/CryptoModule.java index fc3472c98b..77189b2d3f 100644 --- a/src/main/java/io/bitsquare/crypto/CryptoModule.java +++ b/src/main/java/io/bitsquare/crypto/CryptoModule.java @@ -19,12 +19,12 @@ package io.bitsquare.crypto; import io.bitsquare.BitsquareModule; -import java.util.Properties; +import org.springframework.core.env.Environment; public class CryptoModule extends BitsquareModule { - public CryptoModule(Properties properties) { - super(properties); + public CryptoModule(Environment env) { + super(env); } @Override diff --git a/src/main/java/io/bitsquare/gui/GuiModule.java b/src/main/java/io/bitsquare/gui/GuiModule.java index 8491ce60bb..48f06c1975 100644 --- a/src/main/java/io/bitsquare/gui/GuiModule.java +++ b/src/main/java/io/bitsquare/gui/GuiModule.java @@ -28,16 +28,16 @@ import io.bitsquare.gui.util.validation.FiatValidator; import io.bitsquare.gui.util.validation.InputValidator; import io.bitsquare.gui.util.validation.PasswordValidator; -import java.util.Properties; - import javafx.stage.Stage; +import org.springframework.core.env.Environment; + public class GuiModule extends BitsquareModule { private final Stage primaryStage; - public GuiModule(Properties properties, Stage primaryStage) { - super(properties); + public GuiModule(Environment env, Stage primaryStage) { + super(env); this.primaryStage = primaryStage; } diff --git a/src/main/java/io/bitsquare/gui/ViewLoader.java b/src/main/java/io/bitsquare/gui/ViewLoader.java index 1de55a7a2e..fc3d7b3b9c 100644 --- a/src/main/java/io/bitsquare/gui/ViewLoader.java +++ b/src/main/java/io/bitsquare/gui/ViewLoader.java @@ -17,6 +17,7 @@ package io.bitsquare.gui; +import io.bitsquare.BitsquareException; import io.bitsquare.locale.BSResources; import com.google.inject.Injector; @@ -56,7 +57,7 @@ public class ViewLoader { public ViewLoader(Navigation.FxmlResource navItem, boolean useCaching) { this.url = ViewLoader.class.getResource(navItem.getFxmlUrl()); if (this.url == null) { - throw new FatalException("'%s' could not be loaded as a resource", navItem.getFxmlUrl()); + throw new BitsquareException("'%s' could not be loaded as a resource", navItem.getFxmlUrl()); } isCached = useCaching && cachedGUIItems.containsKey(url); @@ -87,7 +88,7 @@ public class ViewLoader { cachedGUIItems.put(url, item); return result; } catch (IOException e) { - throw new FatalException(e, "Failed to load view at %s", url); + throw new BitsquareException(e, "Failed to load view at %s", url); } } diff --git a/src/main/java/io/bitsquare/msg/MessageModule.java b/src/main/java/io/bitsquare/msg/MessageModule.java index e7092fa4da..14c358adbe 100644 --- a/src/main/java/io/bitsquare/msg/MessageModule.java +++ b/src/main/java/io/bitsquare/msg/MessageModule.java @@ -21,12 +21,12 @@ import io.bitsquare.BitsquareModule; import com.google.inject.Injector; -import java.util.Properties; +import org.springframework.core.env.Environment; public abstract class MessageModule extends BitsquareModule { - protected MessageModule(Properties properties) { - super(properties); + protected MessageModule(Environment env) { + super(env); } @Override diff --git a/src/main/java/io/bitsquare/msg/tomp2p/TomP2PMessageModule.java b/src/main/java/io/bitsquare/msg/tomp2p/TomP2PMessageModule.java index d1c1e7ca45..e4d49908c6 100644 --- a/src/main/java/io/bitsquare/msg/tomp2p/TomP2PMessageModule.java +++ b/src/main/java/io/bitsquare/msg/tomp2p/TomP2PMessageModule.java @@ -24,7 +24,7 @@ import io.bitsquare.network.Node; import com.google.inject.name.Names; -import java.util.Properties; +import org.springframework.core.env.Environment; import static io.bitsquare.msg.tomp2p.BootstrappedPeerFactory.*; @@ -35,25 +35,25 @@ public class TomP2PMessageModule extends MessageModule { public static final String BOOTSTRAP_NODE_PORT_KEY = "bootstrap.node.port"; public static final String NETWORK_INTERFACE_KEY = BootstrappedPeerFactory.NETWORK_INTERFACE_KEY; - public TomP2PMessageModule(Properties properties) { - super(properties); + public TomP2PMessageModule(Environment env) { + super(env); } @Override protected void doConfigure() { bind(int.class).annotatedWith(Names.named(Node.PORT_KEY)).toInstance( - Integer.valueOf(properties.getProperty(Node.PORT_KEY, String.valueOf(Node.DEFAULT_PORT)))); + env.getProperty(Node.PORT_KEY, Integer.class, Node.DEFAULT_PORT)); bind(TomP2PNode.class).asEagerSingleton(); bind(Node.class).annotatedWith(Names.named(BOOTSTRAP_NODE_KEY)).toInstance( Node.at( - properties.getProperty(BOOTSTRAP_NODE_NAME_KEY, BootstrapNodes.DEFAULT.getName()), - properties.getProperty(BOOTSTRAP_NODE_IP_KEY, BootstrapNodes.DEFAULT.getIp()), - properties.getProperty(BOOTSTRAP_NODE_PORT_KEY, BootstrapNodes.DEFAULT.getPortAsString()) + env.getProperty(BOOTSTRAP_NODE_NAME_KEY, BootstrapNodes.DEFAULT.getName()), + env.getProperty(BOOTSTRAP_NODE_IP_KEY, BootstrapNodes.DEFAULT.getIp()), + env.getProperty(BOOTSTRAP_NODE_PORT_KEY, BootstrapNodes.DEFAULT.getPortAsString()) ) ); bindConstant().annotatedWith(Names.named(NETWORK_INTERFACE_KEY)).to( - properties.getProperty(NETWORK_INTERFACE_KEY, NETWORK_INTERFACE_UNSPECIFIED)); + env.getProperty(NETWORK_INTERFACE_KEY, NETWORK_INTERFACE_UNSPECIFIED)); bind(BootstrappedPeerFactory.class).asEagerSingleton(); } diff --git a/src/main/java/io/bitsquare/offer/OfferModule.java b/src/main/java/io/bitsquare/offer/OfferModule.java index a83627f92e..227c30656b 100644 --- a/src/main/java/io/bitsquare/offer/OfferModule.java +++ b/src/main/java/io/bitsquare/offer/OfferModule.java @@ -19,12 +19,12 @@ package io.bitsquare.offer; import io.bitsquare.BitsquareModule; -import java.util.Properties; +import org.springframework.core.env.Environment; public abstract class OfferModule extends BitsquareModule { - protected OfferModule(Properties properties) { - super(properties); + protected OfferModule(Environment env) { + super(env); } @Override diff --git a/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferModule.java b/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferModule.java index fb7e3729ad..af2262573d 100644 --- a/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferModule.java +++ b/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferModule.java @@ -20,12 +20,12 @@ package io.bitsquare.offer.tomp2p; import io.bitsquare.offer.OfferModule; import io.bitsquare.offer.OfferRepository; -import java.util.Properties; +import org.springframework.core.env.Environment; public class TomP2POfferModule extends OfferModule { - public TomP2POfferModule(Properties properties) { - super(properties); + public TomP2POfferModule(Environment env) { + super(env); } @Override diff --git a/src/main/java/io/bitsquare/trade/TradeModule.java b/src/main/java/io/bitsquare/trade/TradeModule.java index 626e01e6ce..30242578f7 100644 --- a/src/main/java/io/bitsquare/trade/TradeModule.java +++ b/src/main/java/io/bitsquare/trade/TradeModule.java @@ -19,12 +19,12 @@ package io.bitsquare.trade; import io.bitsquare.BitsquareModule; -import java.util.Properties; +import org.springframework.core.env.Environment; public class TradeModule extends BitsquareModule { - public TradeModule(Properties properties) { - super(properties); + public TradeModule(Environment env) { + super(env); } @Override diff --git a/src/main/java/io/bitsquare/util/ConfigLoader.java b/src/main/java/io/bitsquare/util/ConfigLoader.java deleted file mode 100644 index 484cd7823c..0000000000 --- a/src/main/java/io/bitsquare/util/ConfigLoader.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of Bitsquare. - * - * Bitsquare 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. - * - * Bitsquare 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 Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bitsquare. If not, see . - */ - -package io.bitsquare.util; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -import java.util.Properties; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import lighthouse.files.AppDirectory; - -public class ConfigLoader { - private static final Logger log = LoggerFactory.getLogger(ConfigLoader.class); - private static String configFilePath; - - public static Properties loadConfig(String appName) { - configFilePath = AppDirectory.dir(appName) + "/bitsquare.conf"; - InputStream inputStream = null; - - // load default properties from class path - Properties defaultProperties = new Properties(); - try { - InputStream is = ConfigLoader.class.getResourceAsStream("/bitsquare.properties"); - defaultProperties.load(is); - } catch (IOException ioe) { - ioe.printStackTrace(); - } finally { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - } - - // load properties file from config file path - Properties properties = new Properties(defaultProperties); - if (new File(configFilePath).exists()) { - try { - inputStream = new FileInputStream(configFilePath); - properties.load(inputStream); - } catch (IOException ioe) { - ioe.printStackTrace(); - } finally { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - } - } - return properties; - } -} diff --git a/src/main/java/io/bitsquare/app/gui/MainModule.java b/src/test/java/io/bitsquare/BitsquareEnvironmentTests.java similarity index 50% rename from src/main/java/io/bitsquare/app/gui/MainModule.java rename to src/test/java/io/bitsquare/BitsquareEnvironmentTests.java index 7fd38c8c52..c962effb94 100644 --- a/src/main/java/io/bitsquare/app/gui/MainModule.java +++ b/src/test/java/io/bitsquare/BitsquareEnvironmentTests.java @@ -15,27 +15,27 @@ * along with Bitsquare. If not, see . */ -package io.bitsquare.app.gui; +package io.bitsquare; -import io.bitsquare.BitsquareModule; -import io.bitsquare.app.AppModule; -import io.bitsquare.gui.GuiModule; +import io.bitsquare.app.BitsquareEnvironment; -import java.util.Properties; +import org.junit.Test; -import javafx.stage.Stage; +import joptsimple.OptionParser; -class MainModule extends BitsquareModule { - private final Stage primaryStage; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.*; - public MainModule(Properties properties, Stage primaryStage) { - super(properties); - this.primaryStage = primaryStage; +public class BitsquareEnvironmentTests { + + @Test + public void test() { + String[] args = new String[]{ "--arg1=val1", "--arg2=val2" }; + OptionParser parser = new OptionParser(); + parser.accepts("arg1").withRequiredArg(); + parser.accepts("arg2").withRequiredArg(); + BitsquareEnvironment env = new BitsquareEnvironment(parser.parse(args)); + assertThat(env.getProperty("arg1"), equalTo("val1")); + assertThat(env.getProperty("arg2"), equalTo("val2")); } - - @Override - protected void configure() { - install(new AppModule(properties)); - install(new GuiModule(properties, primaryStage)); - } -} +} \ No newline at end of file diff --git a/src/test/java/io/bitsquare/app/gui/ViewLoaderTests.java b/src/test/java/io/bitsquare/app/gui/ViewLoaderTests.java index b766d5aab0..d0d79f7aea 100644 --- a/src/test/java/io/bitsquare/app/gui/ViewLoaderTests.java +++ b/src/test/java/io/bitsquare/app/gui/ViewLoaderTests.java @@ -17,7 +17,8 @@ package io.bitsquare.app.gui; -import io.bitsquare.gui.FatalException; +import io.bitsquare.BitsquareException; +import io.bitsquare.app.BitsquareEnvironment; import io.bitsquare.gui.Navigation; import io.bitsquare.gui.ViewLoader; @@ -34,7 +35,8 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import static io.bitsquare.app.AppModule.APP_NAME_KEY; +import joptsimple.OptionParser; +import org.springframework.core.env.PropertiesPropertySource; public class ViewLoaderTests { @@ -64,8 +66,11 @@ public class ViewLoaderTests { @Before public void setUp() { Properties properties = new Properties(); - properties.setProperty(APP_NAME_KEY, "testApp"); - Injector injector = Guice.createInjector(new MainModule(properties, TestApp.primaryStage)); + properties.setProperty(BitsquareEnvironment.APP_NAME_KEY, "testApp"); + OptionParser parser = new OptionParser(); + BitsquareEnvironment env = new BitsquareEnvironment(parser.parse(new String[] {})); + env.getPropertySources().addLast(new PropertiesPropertySource("testProperties", properties)); + Injector injector = Guice.createInjector(new BitsquareAppModule(env, TestApp.primaryStage)); ViewLoader.setInjector(injector); } @@ -74,7 +79,7 @@ public class ViewLoaderTests { ViewLoader.setInjector(null); } - @Test(expected = FatalException.class) + @Test(expected = BitsquareException.class) public void loadingBogusFxmlResourceShouldThrow() { new ViewLoader(() -> "a bogus fxml resource", false).load(); }