delete and restore account restarts application automatically

added standard input to keepalive loop for issuing commands to daemon

Co-authored-by: duriancrepe <duriancrepe@protonmail.com>
This commit is contained in:
woodser 2023-01-19 19:19:56 -05:00
parent 350d2d5398
commit 9877ba87a4
14 changed files with 181 additions and 73 deletions

View file

@ -1,64 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.daemon.app;
import java.util.concurrent.*;
/**
* A cancellable console input reader.
* Derived from https://www.javaspecialists.eu/archive/Issue153-Timeout-on-Console-Input.html
*/
public class ConsoleInput {
private final int tries;
private final int timeout;
private final TimeUnit unit;
private Future<String> future;
public ConsoleInput(int tries, int timeout, TimeUnit unit) {
this.tries = tries;
this.timeout = timeout;
this.unit = unit;
}
public void cancel() {
if (future != null)
future.cancel(true);
}
public String readLine() throws InterruptedException {
ExecutorService ex = Executors.newSingleThreadExecutor();
String input = null;
try {
for (int i = 0; i < tries; i++) {
future = ex.submit(new ConsoleInputReadTask());
try {
input = future.get(timeout, unit);
break;
} catch (ExecutionException e) {
e.getCause().printStackTrace();
} catch (TimeoutException e) {
future.cancel(true);
} finally {
future = null;
}
}
} finally {
ex.shutdownNow();
}
return input;
}
}

View file

@ -1,45 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.daemon.app;
import java.io.*;
import java.util.concurrent.Callable;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ConsoleInputReadTask implements Callable<String> {
public String call() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
log.debug("ConsoleInputReadTask run() called.");
String input;
do {
try {
// wait until we have data to complete a readLine()
while (!br.ready()) {
Thread.sleep(100);
}
// readline will always block until an input exists.
input = br.readLine();
} catch (InterruptedException e) {
log.debug("ConsoleInputReadTask() cancelled");
return null;
}
} while ("".equals(input));
return input;
}
}

View file

@ -17,6 +17,7 @@
package bisq.daemon.app;
import bisq.core.app.ConsoleInput;
import bisq.core.app.HavenoHeadlessAppMain;
import bisq.core.app.HavenoSetup;
import bisq.core.api.AccountServiceListener;
@ -26,6 +27,7 @@ import bisq.common.UserThread;
import bisq.common.app.AppModule;
import bisq.common.crypto.IncorrectPasswordException;
import bisq.common.handlers.ResultHandler;
import bisq.common.persistence.PersistenceManager;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
@ -34,6 +36,7 @@ import java.util.concurrent.CancellationException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import lombok.extern.slf4j.Slf4j;
@ -45,7 +48,28 @@ public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSet
private GrpcServer grpcServer;
public static void main(String[] args) {
new HavenoDaemonMain().execute(args);
var keepRunning = true;
while (keepRunning) {
keepRunning = false;
var daemon = new HavenoDaemonMain();
var ret = daemon.execute(args);
if (ret == EXIT_SUCCESS) {
UserThread.execute(() -> daemon.gracefulShutDown(() -> {}));
} else if (ret == EXIT_RESTART) {
AtomicBoolean shuttingDown = new AtomicBoolean(true);
UserThread.execute(() -> daemon.gracefulShutDown(() -> shuttingDown.set(false), false));
keepRunning = true;
// wait for graceful shutdown
try {
while (shuttingDown.get()) {
Thread.sleep(1000);
}
PersistenceManager.reset();
} catch (InterruptedException e) {
System.out.println("interrupted!");
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////////
@ -106,8 +130,8 @@ public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSet
}
@Override
public void gracefulShutDown(ResultHandler resultHandler) {
super.gracefulShutDown(resultHandler);
public void gracefulShutDown(ResultHandler resultHandler, boolean exit) {
super.gracefulShutDown(resultHandler, exit);
if (grpcServer != null) grpcServer.shutdown(); // could be null if application attempted to shutdown early
}
@ -120,7 +144,6 @@ public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSet
// Start rpc server in case login is coming in from rpc
grpcServer = injector.getInstance(GrpcServer.class);
grpcServer.start();
if (!opened) {
// Nonblocking, we need to stop if the login occurred through rpc.
@ -129,7 +152,6 @@ public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSet
Thread t = new Thread(() -> {
interactiveLogin(reader);
});
t.start();
// Handle asynchronous account opens.
// Will need to also close and reopen account.
@ -143,10 +165,14 @@ public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSet
};
accountService.addListener(accountListener);
// start server after the listener is registered
grpcServer.start();
try {
// Wait until interactive login or rpc. Check one more time if account is open to close race condition.
if (!accountService.isAccountOpen()) {
log.info("Interactive login required");
t.start();
t.join();
}
} catch (InterruptedException e) {
@ -155,6 +181,8 @@ public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSet
accountService.removeListener(accountListener);
opened = accountService.isAccountOpen();
} else {
grpcServer.start();
}
return opened;