/* * 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 <http://www.gnu.org/licenses/>. */ package io.bitsquare.app; import io.bitsquare.BitsquareException; import io.bitsquare.btc.BitcoinNetwork; import io.bitsquare.btc.BtcOptionKeys; import io.bitsquare.btc.RegTestHost; import io.bitsquare.common.CommonOptionKeys; import io.bitsquare.network.NetworkOptionKeys; import io.bitsquare.p2p.P2PService; import io.bitsquare.util.joptsimple.EnumValueConverter; import joptsimple.OptionException; import joptsimple.OptionParser; import joptsimple.OptionSet; import org.springframework.util.StringUtils; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import static io.bitsquare.app.BitsquareEnvironment.*; import static java.lang.String.format; import static java.lang.String.join; public abstract class BitsquareExecutable { private static final int EXIT_SUCCESS = 0; public static final int EXIT_FAILURE = 1; private 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 void customizeOptionParsing(OptionParser parser) { parser.accepts(CommonOptionKeys.LOG_LEVEL_KEY, description("Log level [OFF, ALL, ERROR, WARN, INFO, DEBUG, TRACE]", LOG_LEVEL_DEFAULT)) .withRequiredArg(); parser.accepts(NetworkOptionKeys.SEED_NODES_KEY, description("Override hard coded seed nodes as comma separated list: E.g. rxdkppp3vicnbgqt.onion:8002, mfla72c4igh5ta2t.onion:8002", "")) .withRequiredArg(); parser.accepts(NetworkOptionKeys.MY_ADDRESS, description("My own onion address (used for botstrap nodes to exclude itself)", "")) .withRequiredArg(); parser.accepts(NetworkOptionKeys.BAN_LIST, description("Nodes to exclude from network connections.", "")) .withRequiredArg(); // use a fixed port as arbitrator use that for his ID parser.accepts(NetworkOptionKeys.PORT_KEY, description("Port to listen on", 9999)) .withRequiredArg() .ofType(int.class); parser.accepts(NetworkOptionKeys.USE_LOCALHOST, description("Use localhost network for development", false)) .withRequiredArg() .ofType(boolean.class); parser.accepts(NetworkOptionKeys.MAX_CONNECTIONS, description("Max. connections a peer will try to keep", P2PService.MAX_CONNECTIONS_DEFAULT)) .withRequiredArg() .ofType(int.class); parser.accepts(NetworkOptionKeys.SOCKS_5_PROXY_BTC_ADDRESS, description("A proxy address to be used for Bitcoin network. [host:port]", "")) .withRequiredArg(); parser.accepts(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS, description("A proxy address to be used for Http requests (should be non-Tor). [host:port]", "")) .withRequiredArg(); parser.accepts(NetworkOptionKeys.SOCKS5_DISCOVER_MODE, description("Specify discovery mode for Bitcoin nodes. One or more of: [ADDR, DNS, ONION, ALL]" + " (comma separated, they get OR'd together). Default value is ALL", "ALL")) .withRequiredArg(); parser.accepts(AppOptionKeys.USER_DATA_DIR_KEY, description("User data directory", DEFAULT_USER_DATA_DIR)) .withRequiredArg(); parser.accepts(AppOptionKeys.APP_NAME_KEY, description("Application name", DEFAULT_APP_NAME)) .withRequiredArg(); parser.accepts(AppOptionKeys.MAX_MEMORY, description("Max. permitted memory (used only at headless versions)", 600)) .withRequiredArg(); parser.accepts(AppOptionKeys.APP_DATA_DIR_KEY, description("Application data directory", DEFAULT_APP_DATA_DIR)) .withRequiredArg(); parser.accepts(AppOptionKeys.IGNORE_DEV_MSG_KEY, description("If set to true all signed messages from Bitsquare developers are ignored " + "(Global alert, Version update alert, Filters for offers, nodes or trading account data)", false)) .withRequiredArg() .ofType(boolean.class); parser.accepts(AppOptionKeys.DUMP_STATISTICS, description("If set to true the trade statistics are stored as json file in the data dir.", false)) .withRequiredArg() .ofType(boolean.class); parser.accepts(AppOptionKeys.PRICE_FEED_PROVIDERS, description("Custom price feed providers (comma separated)", false)) .withRequiredArg(); parser.accepts(BtcOptionKeys.BTC_NETWORK, description("Bitcoin network", BitcoinNetwork.DEFAULT)) .withRequiredArg() .ofType(BitcoinNetwork.class) .withValuesConvertedBy(new EnumValueConverter(BitcoinNetwork.class)); parser.accepts(BtcOptionKeys.REG_TEST_HOST, description("", RegTestHost.DEFAULT)) .withRequiredArg() .ofType(RegTestHost.class) .withValuesConvertedBy(new EnumValueConverter(RegTestHost.class)); parser.accepts(AppOptionKeys.BTC_NODES, description("Custom nodes used for BitcoinJ as comma separated IP addresses.", "")) .withRequiredArg(); parser.accepts(AppOptionKeys.USE_TOR_FOR_BTC, description("If set to true BitcoinJ is routed over tor (socks 5 proxy).", "")) .withRequiredArg(); } protected static String description(String descText, Object defaultValue) { String description = ""; if (StringUtils.hasText(descText)) description = description.concat(descText); if (defaultValue != null) description = join(" ", description, format("(default: %s)", defaultValue)); return description; } protected abstract void doExecute(OptionSet options); public static void initAppDir(String appDir) { Path dir = Paths.get(appDir); if (Files.exists(dir)) { if (!Files.isWritable(dir)) throw new BitsquareException("Application data directory '%s' is not writeable", dir); else return; } try { Files.createDirectories(dir); } catch (IOException ex) { throw new BitsquareException(ex, "Application data directory '%s' could not be created", dir); } } }