mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Tighten up error handling
This commit factors out a run() method in CliMain that either returns (void) or throws an exception. This eliminates the need to call System.exit in so many places as were previously. Indeed, now there is only one place where System.exit is called. It also removes the duplication of printing "Error: ..." to stderr when something goes wrong by doing this once in the global catch clause in the main method.
This commit is contained in:
parent
b0e5da8e2e
commit
d48f9eb6f0
@ -29,7 +29,6 @@ import bisq.proto.grpc.WalletGrpc;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import joptsimple.OptionException;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
|
||||
@ -45,6 +44,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.err;
|
||||
import static java.lang.System.exit;
|
||||
import static java.lang.System.out;
|
||||
@ -55,9 +55,6 @@ import static java.lang.System.out;
|
||||
@Slf4j
|
||||
public class CliMain {
|
||||
|
||||
private static final int EXIT_SUCCESS = 0;
|
||||
private static final int EXIT_FAILURE = 1;
|
||||
|
||||
private enum Method {
|
||||
getversion,
|
||||
getbalance,
|
||||
@ -68,6 +65,15 @@ public class CliMain {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
run(args);
|
||||
} catch (Throwable t) {
|
||||
err.println("Error: " + t.getMessage());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static void run(String[] args) {
|
||||
var parser = new OptionParser();
|
||||
|
||||
var helpOpt = parser.accepts("help", "Print this help text")
|
||||
@ -85,43 +91,33 @@ public class CliMain {
|
||||
var passwordOpt = parser.accepts("password", "rpc server password")
|
||||
.withRequiredArg();
|
||||
|
||||
OptionSet options = null;
|
||||
try {
|
||||
options = parser.parse(args);
|
||||
} catch (OptionException ex) {
|
||||
err.println("Error: " + ex.getMessage());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
OptionSet options = parser.parse(args);
|
||||
|
||||
if (options.has(helpOpt)) {
|
||||
printHelp(parser, out);
|
||||
exit(EXIT_SUCCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
var nonOptionArgs = (List<String>) options.nonOptionArguments();
|
||||
if (nonOptionArgs.isEmpty()) {
|
||||
printHelp(parser, err);
|
||||
err.println("Error: no method specified");
|
||||
exit(EXIT_FAILURE);
|
||||
throw new IllegalArgumentException("no method specified");
|
||||
}
|
||||
|
||||
var methodName = nonOptionArgs.get(0);
|
||||
Method method = null;
|
||||
final Method method;
|
||||
try {
|
||||
method = Method.valueOf(methodName);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
err.printf("Error: '%s' is not a supported method\n", methodName);
|
||||
exit(EXIT_FAILURE);
|
||||
throw new IllegalArgumentException(format("'%s' is not a supported method", methodName));
|
||||
}
|
||||
|
||||
var host = options.valueOf(hostOpt);
|
||||
var port = options.valueOf(portOpt);
|
||||
var password = options.valueOf(passwordOpt);
|
||||
if (password == null) {
|
||||
err.println("Error: missing required 'password' option");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (password == null)
|
||||
throw new IllegalArgumentException("missing required 'password' option");
|
||||
|
||||
var credentials = new PasswordCallCredentials(password);
|
||||
|
||||
@ -130,8 +126,7 @@ public class CliMain {
|
||||
try {
|
||||
channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace(err);
|
||||
exit(EXIT_FAILURE);
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}));
|
||||
|
||||
@ -144,76 +139,74 @@ public class CliMain {
|
||||
var request = GetVersionRequest.newBuilder().build();
|
||||
var version = versionService.getVersion(request).getVersion();
|
||||
out.println(version);
|
||||
exit(EXIT_SUCCESS);
|
||||
return;
|
||||
}
|
||||
case getbalance: {
|
||||
var request = GetBalanceRequest.newBuilder().build();
|
||||
var reply = walletService.getBalance(request);
|
||||
out.println(formatBalance(reply.getBalance()));
|
||||
exit(EXIT_SUCCESS);
|
||||
var satoshiBalance = reply.getBalance();
|
||||
var satoshiDivisor = new BigDecimal(100000000);
|
||||
var btcFormat = new DecimalFormat("###,##0.00000000");
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
var btcBalance = btcFormat.format(BigDecimal.valueOf(satoshiBalance).divide(satoshiDivisor));
|
||||
out.println(btcBalance);
|
||||
return;
|
||||
}
|
||||
case lockwallet: {
|
||||
var request = LockWalletRequest.newBuilder().build();
|
||||
walletService.lockWallet(request);
|
||||
out.println("wallet locked");
|
||||
exit(EXIT_SUCCESS);
|
||||
return;
|
||||
}
|
||||
case unlockwallet: {
|
||||
if (nonOptionArgs.size() < 2) {
|
||||
err.println("Error: no \"password\" specified");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (nonOptionArgs.size() < 3) {
|
||||
err.println("Error: no unlock timeout specified");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (nonOptionArgs.size() < 2)
|
||||
throw new IllegalArgumentException("no password specified");
|
||||
|
||||
if (nonOptionArgs.size() < 3)
|
||||
throw new IllegalArgumentException("no unlock timeout specified");
|
||||
|
||||
long timeout = 0;
|
||||
try {
|
||||
timeout = Long.parseLong(nonOptionArgs.get(2));
|
||||
} catch (NumberFormatException e) {
|
||||
err.println(nonOptionArgs.get(2) + " is not a number");
|
||||
exit(EXIT_FAILURE);
|
||||
throw new IllegalArgumentException(format("'%s' is not a number", nonOptionArgs.get(2)));
|
||||
}
|
||||
var request = UnlockWalletRequest.newBuilder()
|
||||
.setPassword(nonOptionArgs.get(1))
|
||||
.setTimeout(timeout).build();
|
||||
walletService.unlockWallet(request);
|
||||
out.println("wallet unlocked");
|
||||
exit(EXIT_SUCCESS);
|
||||
return;
|
||||
}
|
||||
case removewalletpassword: {
|
||||
if (nonOptionArgs.size() < 2) {
|
||||
err.println("Error: no \"password\" specified");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (nonOptionArgs.size() < 2)
|
||||
throw new IllegalArgumentException("no password specified");
|
||||
|
||||
var request = RemoveWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1)).build();
|
||||
walletService.removeWalletPassword(request);
|
||||
out.println("wallet decrypted");
|
||||
exit(EXIT_SUCCESS);
|
||||
return;
|
||||
}
|
||||
case setwalletpassword: {
|
||||
if (nonOptionArgs.size() < 2) {
|
||||
err.println("Error: no \"password\" specified");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (nonOptionArgs.size() < 2)
|
||||
throw new IllegalArgumentException("no password specified");
|
||||
|
||||
var requestBuilder = SetWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1));
|
||||
var hasNewPassword = nonOptionArgs.size() == 3;
|
||||
if (hasNewPassword)
|
||||
requestBuilder.setNewPassword(nonOptionArgs.get(2));
|
||||
walletService.setWalletPassword(requestBuilder.build());
|
||||
out.println("wallet encrypted" + (hasNewPassword ? " with new password" : ""));
|
||||
exit(EXIT_SUCCESS);
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
err.printf("Error: unhandled method '%s'\n", method);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
throw new RuntimeException(format("unhandled method '%s'", method));
|
||||
}
|
||||
}
|
||||
} catch (StatusRuntimeException ex) {
|
||||
// Remove the leading gRPC status code (e.g. "UNKNOWN: ") from the message
|
||||
String message = ex.getMessage().replaceFirst("^[A-Z_]+: ", "");
|
||||
err.println("Error: " + message);
|
||||
exit(EXIT_FAILURE);
|
||||
throw new RuntimeException(message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,19 +223,13 @@ public class CliMain {
|
||||
stream.format("%-19s%-30s%s%n", "getversion", "", "Get server version");
|
||||
stream.format("%-19s%-30s%s%n", "getbalance", "", "Get server wallet balance");
|
||||
stream.format("%-19s%-30s%s%n", "lockwallet", "", "Remove wallet password from memory, locking the wallet");
|
||||
stream.format("%-19s%-30s%s%n", "unlockwallet", "\"password\" timeout", "Store wallet password in memory for 'timeout' seconds");
|
||||
stream.format("%-19s%-30s%s%n", "setwalletpassword", "\"password\" [,\"newpassword\"]",
|
||||
stream.format("%-19s%-30s%s%n", "unlockwallet", "password timeout",
|
||||
"Store wallet password in memory for timeout seconds");
|
||||
stream.format("%-19s%-30s%s%n", "setwalletpassword", "password [newpassword]",
|
||||
"Encrypt wallet with password, or set new password on encrypted wallet");
|
||||
stream.println();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace(stream);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
private static String formatBalance(long satoshis) {
|
||||
var btcFormat = new DecimalFormat("###,##0.00000000");
|
||||
var satoshiDivisor = new BigDecimal(100000000);
|
||||
return btcFormat.format(BigDecimal.valueOf(satoshis).divide(satoshiDivisor));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user