From 94ee49c5afdc0262dc888e58b4fcf572975307cd Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 26 Mar 2020 20:17:18 -0300 Subject: [PATCH 1/2] Remove read stdin loop Replaced the Scanner input read loop with upgraded joptsimple dependency. Cli now takes a single, non-option program argument, runs it and exits. Also removed the "stop client" command because there is no input loop, but shutdown() is called for orderly channel shudown before the jvm terminates. Also changed cmd syntax from camel case to lowercase, mimicking bitcoin-cli. Configured logback to supress all debug & info level netty output, and bypassed logback to print results to System.out. --- build.gradle | 3 +- .../main/java/bisq/cli/app/BisqCliMain.java | 106 +++++++++--------- .../main/java/bisq/cli/app/CommandParser.java | 27 +++++ cli/src/main/resources/logback.xml | 4 +- gradle/witness/gradle-witness.gradle | 2 +- 5 files changed, 86 insertions(+), 56 deletions(-) create mode 100644 cli/src/main/java/bisq/cli/app/CommandParser.java diff --git a/build.gradle b/build.gradle index 19c2d56679b..f8c8682b805 100644 --- a/build.gradle +++ b/build.gradle @@ -51,7 +51,7 @@ configure(subprojects) { jcsvVersion = '1.4.0' jetbrainsAnnotationsVersion = '13.0' jfoenixVersion = '9.0.6' - joptVersion = '5.0.3' + joptVersion = '5.0.4' jsonsimpleVersion = '1.1.1' junitVersion = '4.12' jupiterVersion = '5.3.2' @@ -337,6 +337,7 @@ configure(project(':cli')) { dependencies { compile project(':proto') + implementation "net.sf.jopt-simple:jopt-simple:$joptVersion" implementation "com.google.guava:guava:$guavaVersion" implementation "com.google.protobuf:protobuf-java:$protobufVersion" implementation("io.grpc:grpc-core:$grpcVersion") { diff --git a/cli/src/main/java/bisq/cli/app/BisqCliMain.java b/cli/src/main/java/bisq/cli/app/BisqCliMain.java index 5f1dec323e3..b0ac0c34cc9 100644 --- a/cli/src/main/java/bisq/cli/app/BisqCliMain.java +++ b/cli/src/main/java/bisq/cli/app/BisqCliMain.java @@ -20,18 +20,22 @@ import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; -import java.util.ArrayList; -import java.util.Arrays; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import joptsimple.OptionSpec; + import java.util.List; -import java.util.Scanner; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; +import static bisq.cli.app.CommandParser.GETBALANCE; +import static bisq.cli.app.CommandParser.GETVERSION; +import static bisq.cli.app.CommandParser.STOPSERVER; import static java.lang.String.format; import static java.lang.System.currentTimeMillis; import static java.lang.System.exit; -import static java.lang.System.in; +import static java.lang.System.out; /** * gRPC client. @@ -41,60 +45,28 @@ public class BisqCliMain { private final ManagedChannel channel; private final CliCommand cmd; + private final OptionParser cmdParser; public static void main(String[] args) { - new BisqCliMain("localhost", 8888); + new BisqCliMain("localhost", 8888, args); } - private BisqCliMain(String host, int port) { + private BisqCliMain(String host, int port, String[] params) { // Channels are secure by default (via SSL/TLS); for the example disable TLS to avoid needing certificates. this(ManagedChannelBuilder.forAddress(host, port).usePlaintext().build()); - // Simple input scanner - // TODO use some more sophisticated input processing with validation.... - try (Scanner scanner = new Scanner(in)) { - while (true) { - long startTs = currentTimeMillis(); - - String[] tokens = scanner.nextLine().split(" "); - if (tokens.length == 0) { - return; - } - String command = tokens[0]; - if (tokens.length > 1) { - List params = new ArrayList<>(Arrays.asList(tokens)); - params.remove(0); - } - String result; - - switch (command) { - case "getBalance": - long satoshis = cmd.getBalance(); - // TODO mimic bitcoin-cli? Depends on an error code: Loading block index... Verifying blocks... - result = satoshis == -1 ? "Server initializing..." : cmd.prettyBalance.apply(satoshis); - break; - case "getVersion": - result = cmd.getVersion(); - break; - case "stop": - result = "Shut down client"; - try { - shutdown(); - } catch (InterruptedException e) { - log.error(e.toString(), e); - } - break; - case "stopServer": - cmd.stopServer(); - result = "Server stopped"; - break; - default: - result = format("Unknown command '%s'", command); - } - - // First response is rather slow (300 ms) but following responses are fast (3-5 ms). - log.info("{}\t{}", result, cmd.responseTime.apply(startTs)); - } + long startTs = currentTimeMillis(); + + String command = parseCommand(params); + String result = runCommand(command); + + // First response is rather slow (300 ms) but following responses are fast (3-5 ms). + // log.info("{}\t{}", result, cmd.responseTime.apply(startTs)); + out.println(result + "\t" + cmd.responseTime.apply(startTs)); + + try { + shutdown(); // Orderly channel shutdown + } catch (InterruptedException ignored) { } } @@ -104,6 +76,38 @@ private BisqCliMain(String host, int port) { private BisqCliMain(ManagedChannel channel) { this.channel = channel; this.cmd = new CliCommand(channel); + this.cmdParser = new CommandParser().configure(); + } + + private String runCommand(String command) { + final String result; + switch (command) { + case GETBALANCE: + long satoshis = cmd.getBalance(); + result = satoshis == -1 ? "Server initializing..." : cmd.prettyBalance.apply(satoshis); + break; + case GETVERSION: + result = cmd.getVersion(); + break; + case STOPSERVER: + cmd.stopServer(); + result = "Server stopped"; + break; + default: + result = format("Unknown command '%s'", command); + } + return result; + } + + private String parseCommand(String[] params) { + OptionSpec nonOptions = cmdParser.nonOptions().ofType(String.class); + OptionSet options = cmdParser.parse(params); + List detectedOptions = nonOptions.values(options); + if (detectedOptions.isEmpty()) { + CommandParser.printUsage(); + exit(0); + } + return detectedOptions.get(0); } private void shutdown() throws InterruptedException { diff --git a/cli/src/main/java/bisq/cli/app/CommandParser.java b/cli/src/main/java/bisq/cli/app/CommandParser.java new file mode 100644 index 00000000000..1c5c8b1157d --- /dev/null +++ b/cli/src/main/java/bisq/cli/app/CommandParser.java @@ -0,0 +1,27 @@ +package bisq.cli.app; + +import joptsimple.OptionParser; + +import static java.lang.System.err; + +final class CommandParser { + + // Option name constants + static final String HELP = "help"; + static final String GETBALANCE = "getbalance"; + static final String GETVERSION = "getversion"; + static final String STOPSERVER = "stopserver"; + + OptionParser configure() { + OptionParser parser = new OptionParser(); + parser.allowsUnrecognizedOptions(); + parser.nonOptions(GETBALANCE).ofType(String.class).describedAs("get btc balance"); + parser.nonOptions(GETVERSION).ofType(String.class).describedAs("get bisq version"); + return parser; + } + + static void printUsage() { + err.println("Usage: bisq-cli getbalance | getversion"); + } + +} diff --git a/cli/src/main/resources/logback.xml b/cli/src/main/resources/logback.xml index c5b1fcf9aa3..bc8edf02211 100644 --- a/cli/src/main/resources/logback.xml +++ b/cli/src/main/resources/logback.xml @@ -10,8 +10,6 @@ - - - + diff --git a/gradle/witness/gradle-witness.gradle b/gradle/witness/gradle-witness.gradle index bce96457fc8..82d9b023060 100644 --- a/gradle/witness/gradle-witness.gradle +++ b/gradle/witness/gradle-witness.gradle @@ -68,7 +68,7 @@ dependencyVerification { 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'net.glxn:qrgen:c85d9d8512d91e8ad11fe56259a7825bd50ce0245447e236cf168d1b17591882', 'net.jcip:jcip-annotations:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0', - 'net.sf.jopt-simple:jopt-simple:6f45c00908265947c39221035250024f2caec9a15c1c8cf553ebeecee289f342', + 'net.sf.jopt-simple:jopt-simple:df26cc58f235f477db07f753ba5a3ab243ebe5789d9f89ecf68dd62ea9a66c28', 'network.bisq.btcd-cli4j:btcd-cli4j-core:203156fc63dc1202774de9818e4f21149549f79b25d356b08bb0c784be40c0e8', 'network.bisq.btcd-cli4j:btcd-cli4j-daemon:0a2783a851add6e3d8ae899ade48c041b250bfac64b6a4c5f6380ebcdbbe6848', 'org.apache.commons:commons-compress:5f2df1e467825e4cac5996d44890c4201c000b43c0b23cffc0782d28a0beb9b0', From aba595b8e8d9db2b80843a3f802095186b6d83c8 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Mon, 30 Mar 2020 11:20:19 -0300 Subject: [PATCH 2/2] Implement requested changes in PR #4097 Change member name OptionParser cmdParser -> parser. Change server listening port to 9998, client port to 9998. Change constructor argument from String[] param -> args. Print the result only, w/out exec time. Handle help command & print that to stdout; print help triggered by user error to stderr. Use explicit system SUCCESS/FAIL codes in System.exit(0 || 1). --- .../main/java/bisq/cli/app/BisqCliMain.java | 37 +++++++++---------- .../main/java/bisq/cli/app/CliCommand.java | 4 -- .../main/java/bisq/cli/app/CommandParser.java | 6 +-- .../java/bisq/core/grpc/BisqGrpcServer.java | 2 +- 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/cli/src/main/java/bisq/cli/app/BisqCliMain.java b/cli/src/main/java/bisq/cli/app/BisqCliMain.java index b0ac0c34cc9..015051bdc32 100644 --- a/cli/src/main/java/bisq/cli/app/BisqCliMain.java +++ b/cli/src/main/java/bisq/cli/app/BisqCliMain.java @@ -31,9 +31,9 @@ import static bisq.cli.app.CommandParser.GETBALANCE; import static bisq.cli.app.CommandParser.GETVERSION; +import static bisq.cli.app.CommandParser.HELP; import static bisq.cli.app.CommandParser.STOPSERVER; import static java.lang.String.format; -import static java.lang.System.currentTimeMillis; import static java.lang.System.exit; import static java.lang.System.out; @@ -43,27 +43,23 @@ @Slf4j public class BisqCliMain { + private static final int EXIT_SUCCESS = 0; + private static final int EXIT_FAILURE = 1; + private final ManagedChannel channel; private final CliCommand cmd; - private final OptionParser cmdParser; + private final OptionParser parser; public static void main(String[] args) { - new BisqCliMain("localhost", 8888, args); + new BisqCliMain("localhost", 9998, args); } - private BisqCliMain(String host, int port, String[] params) { + private BisqCliMain(String host, int port, String[] args) { // Channels are secure by default (via SSL/TLS); for the example disable TLS to avoid needing certificates. this(ManagedChannelBuilder.forAddress(host, port).usePlaintext().build()); - - long startTs = currentTimeMillis(); - - String command = parseCommand(params); + String command = parseCommand(args); String result = runCommand(command); - - // First response is rather slow (300 ms) but following responses are fast (3-5 ms). - // log.info("{}\t{}", result, cmd.responseTime.apply(startTs)); - out.println(result + "\t" + cmd.responseTime.apply(startTs)); - + out.println(result); try { shutdown(); // Orderly channel shutdown } catch (InterruptedException ignored) { @@ -76,12 +72,15 @@ private BisqCliMain(String host, int port, String[] params) { private BisqCliMain(ManagedChannel channel) { this.channel = channel; this.cmd = new CliCommand(channel); - this.cmdParser = new CommandParser().configure(); + this.parser = new CommandParser().configure(); } private String runCommand(String command) { final String result; switch (command) { + case HELP: + CommandParser.printHelp(); + exit(EXIT_SUCCESS); case GETBALANCE: long satoshis = cmd.getBalance(); result = satoshis == -1 ? "Server initializing..." : cmd.prettyBalance.apply(satoshis); @@ -100,18 +99,18 @@ private String runCommand(String command) { } private String parseCommand(String[] params) { - OptionSpec nonOptions = cmdParser.nonOptions().ofType(String.class); - OptionSet options = cmdParser.parse(params); + OptionSpec nonOptions = parser.nonOptions().ofType(String.class); + OptionSet options = parser.parse(params); List detectedOptions = nonOptions.values(options); if (detectedOptions.isEmpty()) { - CommandParser.printUsage(); - exit(0); + CommandParser.printHelp(); + exit(EXIT_FAILURE); } return detectedOptions.get(0); } private void shutdown() throws InterruptedException { channel.shutdown().awaitTermination(1, TimeUnit.SECONDS); - exit(0); + exit(EXIT_SUCCESS); } } diff --git a/cli/src/main/java/bisq/cli/app/CliCommand.java b/cli/src/main/java/bisq/cli/app/CliCommand.java index f40a39c673c..e3b0bc813fe 100644 --- a/cli/src/main/java/bisq/cli/app/CliCommand.java +++ b/cli/src/main/java/bisq/cli/app/CliCommand.java @@ -18,8 +18,6 @@ import lombok.extern.slf4j.Slf4j; -import static java.lang.System.currentTimeMillis; - @Slf4j final class CliCommand { @@ -32,8 +30,6 @@ final class CliCommand { @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") final Function prettyBalance = (sats) -> btcFormat.format(BigDecimal.valueOf(sats).divide(satoshiDivisor)); - final Function responseTime = (t0) -> "(response time: " + (currentTimeMillis() - t0) + " ms)"; - CliCommand(ManagedChannel channel) { getBalanceStub = GetBalanceGrpc.newBlockingStub(channel); getVersionStub = GetVersionGrpc.newBlockingStub(channel); diff --git a/cli/src/main/java/bisq/cli/app/CommandParser.java b/cli/src/main/java/bisq/cli/app/CommandParser.java index 1c5c8b1157d..b1615d81165 100644 --- a/cli/src/main/java/bisq/cli/app/CommandParser.java +++ b/cli/src/main/java/bisq/cli/app/CommandParser.java @@ -2,7 +2,7 @@ import joptsimple.OptionParser; -import static java.lang.System.err; +import static java.lang.System.out; final class CommandParser { @@ -20,8 +20,8 @@ OptionParser configure() { return parser; } - static void printUsage() { - err.println("Usage: bisq-cli getbalance | getversion"); + static void printHelp() { + out.println("Usage: bisq-cli getbalance | getversion"); } } diff --git a/core/src/main/java/bisq/core/grpc/BisqGrpcServer.java b/core/src/main/java/bisq/core/grpc/BisqGrpcServer.java index fc6a09726a7..ef6631462f9 100644 --- a/core/src/main/java/bisq/core/grpc/BisqGrpcServer.java +++ b/core/src/main/java/bisq/core/grpc/BisqGrpcServer.java @@ -200,7 +200,7 @@ public void stop() { private void start() throws IOException { // TODO add to options - int port = 8888; + int port = 9998; // Config services server = ServerBuilder.forPort(port)