Use FXTimer instead of Java Timer, add idle detection for republish offers

This commit is contained in:
Manfred Karrer 2016-02-19 19:56:17 +01:00
parent 79de2bcb11
commit bd3b55cf47
25 changed files with 331 additions and 112 deletions

View file

@ -13,5 +13,7 @@ public interface Clock {
void onSecondTick(); void onSecondTick();
void onMinuteTick(); void onMinuteTick();
void onMissedSecondTick(long missed);
} }
} }

View file

@ -0,0 +1,65 @@
package io.bitsquare.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.util.Random;
import java.util.TimerTask;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class DefaultJavaTimer implements Timer {
private final Logger log = LoggerFactory.getLogger(DefaultJavaTimer.class);
private java.util.Timer timer;
public DefaultJavaTimer() {
}
@Override
public Timer runLater(Duration delay, Runnable runnable) {
checkArgument(timer == null, "runLater or runPeriodically already called on that timer");
timer = new java.util.Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Thread.currentThread().setName("TimerTask-" + new Random().nextInt(10000));
try {
UserThread.execute(runnable::run);
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing timerTask failed. " + t.getMessage());
}
}
}, delay.toMillis());
return this;
}
@Override
public Timer runPeriodically(java.time.Duration interval, Runnable runnable) {
checkArgument(timer == null, "runLater or runPeriodically already called on that timer");
timer = new java.util.Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Thread.currentThread().setName("TimerTask-" + new Random().nextInt(10000));
try {
UserThread.execute(runnable::run);
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing timerTask failed. " + t.getMessage());
}
}
}, interval.toMillis(), interval.toMillis());
return this;
}
@Override
public void stop() {
checkNotNull(timer, "Timer must not be null");
timer.cancel();
timer = null;
}
}

View file

@ -0,0 +1,11 @@
package io.bitsquare.common;
import java.time.Duration;
public interface Timer {
Timer runLater(java.time.Duration delay, Runnable action);
Timer runPeriodically(Duration interval, Runnable runnable);
void stop();
}

View file

@ -21,14 +21,15 @@ import com.google.common.util.concurrent.MoreExecutors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationTargetException;
import java.time.Duration;
import java.util.Random; import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class UserThread { public class UserThread {
private static final Logger log = LoggerFactory.getLogger(UserThread.class); private static final Logger log = LoggerFactory.getLogger(UserThread.class);
private static Class<? extends Timer> timerClass;
public static Executor getExecutor() { public static Executor getExecutor() {
return executor; return executor;
@ -38,9 +39,14 @@ public class UserThread {
UserThread.executor = executor; UserThread.executor = executor;
} }
public static void setTimerClass(Class<? extends Timer> timerClass) {
UserThread.timerClass = timerClass;
}
static { static {
// If not defined we use same thread as caller thread // If not defined we use same thread as caller thread
executor = MoreExecutors.directExecutor(); executor = MoreExecutors.directExecutor();
timerClass = DefaultJavaTimer.class;
} }
private static Executor executor; private static Executor executor;
@ -65,19 +71,25 @@ public class UserThread {
} }
public static Timer runAfter(Runnable runnable, long delay, TimeUnit timeUnit) { public static Timer runAfter(Runnable runnable, long delay, TimeUnit timeUnit) {
Timer timer = new Timer(); return getTimer().runLater(Duration.ofMillis(timeUnit.toMillis(delay)), runnable);
timer.schedule(new TimerTask() { }
@Override
public void run() { public static Timer runPeriodically(Runnable runnable, long interval) {
Thread.currentThread().setName("TimerTask-" + new Random().nextInt(10000)); return UserThread.runPeriodically(runnable, interval, TimeUnit.SECONDS);
}
public static Timer runPeriodically(Runnable runnable, long interval, TimeUnit timeUnit) {
return getTimer().runPeriodically(Duration.ofMillis(timeUnit.toMillis(interval)), runnable);
}
private static Timer getTimer() {
try { try {
UserThread.execute(runnable::run); return timerClass.getDeclaredConstructor().newInstance();
} catch (Throwable t) { } catch (InstantiationException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
t.printStackTrace(); String message = "Could not instantiate timer bsTimerClass=" + timerClass;
log.error("Executing timerTask failed. " + t.getMessage()); log.error(message);
e.printStackTrace();
throw new RuntimeException(message);
} }
} }
}, timeUnit.toMillis(delay));
return timer;
}
} }

View file

@ -25,6 +25,7 @@ import com.google.common.util.concurrent.Service;
import io.bitsquare.btc.listeners.AddressConfidenceListener; import io.bitsquare.btc.listeners.AddressConfidenceListener;
import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.listeners.TxConfidenceListener; import io.bitsquare.btc.listeners.TxConfidenceListener;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ExceptionHandler; import io.bitsquare.common.handlers.ExceptionHandler;
@ -187,7 +188,7 @@ public class WalletService {
// set after wallet is ready // set after wallet is ready
tradeWalletService.setWalletAppKit(walletAppKit); tradeWalletService.setWalletAppKit(walletAppKit);
timeoutTimer.cancel(); timeoutTimer.stop();
// onSetupCompleted in walletAppKit is not the called on the last invocations, so we add a bit of delay // onSetupCompleted in walletAppKit is not the called on the last invocations, so we add a bit of delay
UserThread.runAfter(resultHandler::handleResult, 100, TimeUnit.MILLISECONDS); UserThread.runAfter(resultHandler::handleResult, 100, TimeUnit.MILLISECONDS);
@ -242,7 +243,7 @@ public class WalletService {
public void failed(@NotNull Service.State from, @NotNull Throwable failure) { public void failed(@NotNull Service.State from, @NotNull Throwable failure) {
walletAppKit = null; walletAppKit = null;
log.error("walletAppKit failed"); log.error("walletAppKit failed");
timeoutTimer.cancel(); timeoutTimer.stop();
UserThread.execute(() -> exceptionHandler.handleException(failure)); UserThread.execute(() -> exceptionHandler.handleException(failure));
} }
}, Threading.USER_THREAD); }, Threading.USER_THREAD);

View file

@ -2,6 +2,7 @@ package io.bitsquare.btc.blockchain;
import com.google.common.util.concurrent.*; import com.google.common.util.concurrent.*;
import io.bitsquare.btc.blockchain.providers.FeeProvider; import io.bitsquare.btc.blockchain.providers.FeeProvider;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
import io.bitsquare.http.HttpException; import io.bitsquare.http.HttpException;
@ -11,7 +12,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.Timer;
class GetFeeRequest { class GetFeeRequest {
private static final Logger log = LoggerFactory.getLogger(GetFeeRequest.class); private static final Logger log = LoggerFactory.getLogger(GetFeeRequest.class);
@ -72,7 +72,7 @@ class GetFeeRequest {
} }
private void stopTimer() { private void stopTimer() {
timer.cancel(); timer.stop();
timer = null; timer = null;
} }
} }

View file

@ -18,6 +18,7 @@
package io.bitsquare.trade.offer; package io.bitsquare.trade.offer;
import io.bitsquare.app.Version; import io.bitsquare.app.Version;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Tradable; import io.bitsquare.trade.Tradable;
@ -36,7 +37,7 @@ public final class OpenOffer implements Tradable {
// Timeout for offer reservation during takeoffer process. If deposit tx is not completed in that time we reset the offer to AVAILABLE state. // Timeout for offer reservation during takeoffer process. If deposit tx is not completed in that time we reset the offer to AVAILABLE state.
private static final long TIMEOUT_SEC = 30; private static final long TIMEOUT_SEC = 30;
transient private java.util.Timer timeoutTimer; transient private Timer timeoutTimer;
public enum State { public enum State {
AVAILABLE, AVAILABLE,
@ -118,7 +119,7 @@ public final class OpenOffer implements Tradable {
private void stopTimeout() { private void stopTimeout() {
if (timeoutTimer != null) { if (timeoutTimer != null) {
timeoutTimer.cancel(); timeoutTimer.stop();
timeoutTimer = null; timeoutTimer = null;
} }
} }

View file

@ -21,6 +21,8 @@ import com.google.inject.Inject;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.common.Clock;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
@ -30,6 +32,9 @@ import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.messaging.SendDirectMessageListener; import io.bitsquare.p2p.messaging.SendDirectMessageListener;
import io.bitsquare.p2p.network.CloseConnectionReason;
import io.bitsquare.p2p.network.Connection;
import io.bitsquare.p2p.network.ConnectionListener;
import io.bitsquare.p2p.network.NetworkNode; import io.bitsquare.p2p.network.NetworkNode;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.TradableList; import io.bitsquare.trade.TradableList;
@ -48,8 +53,6 @@ import javax.annotation.Nullable;
import javax.inject.Named; import javax.inject.Named;
import java.io.File; import java.io.File;
import java.util.Optional; import java.util.Optional;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static com.google.inject.internal.util.$Preconditions.checkNotNull; import static com.google.inject.internal.util.$Preconditions.checkNotNull;
@ -67,17 +70,19 @@ public class OpenOfferManager {
private final TradeWalletService tradeWalletService; private final TradeWalletService tradeWalletService;
private final OfferBookService offerBookService; private final OfferBookService offerBookService;
private final ClosedTradableManager closedTradableManager; private final ClosedTradableManager closedTradableManager;
private Clock clock;
private final TradableList<OpenOffer> openOffers; private final TradableList<OpenOffer> openOffers;
private final Storage<TradableList<OpenOffer>> openOffersStorage; private final Storage<TradableList<OpenOffer>> openOffersStorage;
private boolean shutDownRequested; private boolean shutDownRequested;
private BootstrapListener bootstrapListener; private BootstrapListener bootstrapListener;
private final Timer timer = new Timer(); //private final Timer republishOffersTimer = new Timer();
private Timer republishOffersTime; private Timer refreshOffersTimer;
private boolean firstTimeConnection; private Timer republishOffersTimer;
private boolean allowRefreshOffers; private boolean allowRefreshOffers;
private boolean lostAllConnections; private boolean lostAllConnections;
private long refreshOffersPeriod; private long refreshOffersPeriod;
private Clock.Listener listener;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -92,6 +97,7 @@ public class OpenOfferManager {
TradeWalletService tradeWalletService, TradeWalletService tradeWalletService,
OfferBookService offerBookService, OfferBookService offerBookService,
ClosedTradableManager closedTradableManager, ClosedTradableManager closedTradableManager,
Clock clock,
@Named("storage.dir") File storageDir) { @Named("storage.dir") File storageDir) {
this.keyRing = keyRing; this.keyRing = keyRing;
this.user = user; this.user = user;
@ -100,6 +106,7 @@ public class OpenOfferManager {
this.tradeWalletService = tradeWalletService; this.tradeWalletService = tradeWalletService;
this.offerBookService = offerBookService; this.offerBookService = offerBookService;
this.closedTradableManager = closedTradableManager; this.closedTradableManager = closedTradableManager;
this.clock = clock;
openOffersStorage = new Storage<>(storageDir); openOffersStorage = new Storage<>(storageDir);
this.openOffers = new TradableList<>(openOffersStorage, "OpenOffers"); this.openOffers = new TradableList<>(openOffersStorage, "OpenOffers");
@ -120,42 +127,6 @@ public class OpenOfferManager {
if (message instanceof OfferAvailabilityRequest) if (message instanceof OfferAvailabilityRequest)
handleOfferAvailabilityRequest((OfferAvailabilityRequest) message, peersNodeAddress); handleOfferAvailabilityRequest((OfferAvailabilityRequest) message, peersNodeAddress);
}); });
NetworkNode networkNode = p2PService.getNetworkNode();
// TODO: Use check for detecting inactivity instead. run timer and check if elapsed time is in expected range,
// if not we have been in standby and need a republish
/* networkNode.addConnectionListener(new ConnectionListener() {
@Override
public void onConnection(Connection connection) {
if (lostAllConnections) {
lostAllConnections = false;
allowRefreshOffers = false;
// We repeat a rePublishOffers call after 10 seconds if we have more than 3 peers
if (republishOffersTime == null) {
republishOffersTime = UserThread.runAfter(() -> {
if (networkNode.getAllConnections().size() > 3)
republishOffers();
allowRefreshOffers = true;
republishOffersTime = null;
}, 5);
}
}
}
@Override
public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) {
lostAllConnections = networkNode.getAllConnections().isEmpty();
if (lostAllConnections)
allowRefreshOffers = false;
}
@Override
public void onError(Throwable throwable) {
}
});*/
} }
@ -195,8 +166,62 @@ public class OpenOfferManager {
//TODO should not be needed //TODO should not be needed
//startRepublishOffersThread(); //startRepublishOffersThread();
// we check if app was idle for more then 5 sec.
listener = new Clock.Listener() {
@Override
public void onSecondTick() {
} }
@Override
public void onMinuteTick() {
}
@Override
public void onMissedSecondTick(long missed) {
if (missed > 5000) {
log.error("We have been idle for {} sec", missed / 1000);
// We have been idle for at least 5 sec.
//republishOffers();
// run again after 5 sec as it might be that the app needs a bit for getting all re-animated again
if (republishOffersTimer == null)
republishOffersTimer = UserThread.runAfter(OpenOfferManager.this::republishOffers, 5);
}
}
};
clock.addListener(listener);
// We also check if we got completely disconnected
NetworkNode networkNode = p2PService.getNetworkNode();
networkNode.addConnectionListener(new ConnectionListener() {
@Override
public void onConnection(Connection connection) {
if (lostAllConnections) {
lostAllConnections = false;
if (republishOffersTimer != null)
republishOffersTimer.stop();
//republishOffers();
// run again after 5 sec as it might be that the app needs a bit for getting all re-animated again
republishOffersTimer = UserThread.runAfter(OpenOfferManager.this::republishOffers, 5);
}
}
@Override
public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) {
lostAllConnections = networkNode.getAllConnections().isEmpty();
if (lostAllConnections)
allowRefreshOffers = false;
}
@Override
public void onError(Throwable throwable) {
}
});
}
/*
private void startRepublishOffersThread() { private void startRepublishOffersThread() {
long period = Offer.TTL * 10; long period = Offer.TTL * 10;
TimerTask timerTask = new TimerTask() { TimerTask timerTask = new TimerTask() {
@ -205,18 +230,27 @@ public class OpenOfferManager {
UserThread.execute(OpenOfferManager.this::republishOffers); UserThread.execute(OpenOfferManager.this::republishOffers);
} }
}; };
timer.scheduleAtFixedRate(timerTask, period, period); republishOffersTimer.scheduleAtFixedRate(timerTask, period, period);
} }*/
private void republishOffers() { private void republishOffers() {
Log.traceCall("Number of offer for republish: " + openOffers.size()); Log.traceCall("Number of offer for republish: " + openOffers.size());
allowRefreshOffers = false;
if (republishOffersTimer != null) {
republishOffersTimer.stop();
republishOffersTimer = null;
}
for (OpenOffer openOffer : openOffers) { for (OpenOffer openOffer : openOffers) {
offerBookService.republishOffers(openOffer.getOffer(), offerBookService.republishOffers(openOffer.getOffer(),
() -> { () -> {
log.debug("Successful added offer to P2P network"); log.debug("Successful added offer to P2P network");
allowRefreshOffers = true; allowRefreshOffers = true;
}, },
errorMessage -> log.error("Add offer to P2P network failed. " + errorMessage)); errorMessage -> {
//TODO handle with retry
log.error("Add offer to P2P network failed. " + errorMessage);
});
openOffer.setStorage(openOffersStorage); openOffer.setStorage(openOffersStorage);
} }
} }
@ -224,13 +258,7 @@ public class OpenOfferManager {
private void startRefreshOffersThread() { private void startRefreshOffersThread() {
// refresh sufficiently before offer would expire // refresh sufficiently before offer would expire
refreshOffersPeriod = (long) (Offer.TTL * 0.7); refreshOffersPeriod = (long) (Offer.TTL * 0.7);
TimerTask timerTask = new TimerTask() { refreshOffersTimer = UserThread.runPeriodically(OpenOfferManager.this::refreshOffers, refreshOffersPeriod, TimeUnit.MILLISECONDS);
@Override
public void run() {
UserThread.execute(OpenOfferManager.this::refreshOffers);
}
};
timer.scheduleAtFixedRate(timerTask, refreshOffersPeriod, refreshOffersPeriod);
} }
private void refreshOffers() { private void refreshOffers() {
@ -251,8 +279,14 @@ public class OpenOfferManager {
} }
public void shutDown(@Nullable Runnable completeHandler) { public void shutDown(@Nullable Runnable completeHandler) {
if (timer != null) if (republishOffersTimer != null)
timer.cancel(); republishOffersTimer.stop();
if (refreshOffersTimer != null)
refreshOffersTimer.stop();
if (listener != null)
clock.removeListener(listener);
if (!shutDownRequested) { if (!shutDownRequested) {
log.info("remove all open offers at shutDown"); log.info("remove all open offers at shutDown");

View file

@ -17,6 +17,7 @@
package io.bitsquare.trade.protocol.availability; package io.bitsquare.trade.protocol.availability;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
@ -44,7 +45,7 @@ public class OfferAvailabilityProtocol {
private final DecryptedDirectMessageListener decryptedDirectMessageListener; private final DecryptedDirectMessageListener decryptedDirectMessageListener;
private TaskRunner<OfferAvailabilityModel> taskRunner; private TaskRunner<OfferAvailabilityModel> taskRunner;
private java.util.Timer timeoutTimer; private Timer timeoutTimer;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -145,7 +146,7 @@ public class OfferAvailabilityProtocol {
private void stopTimeout() { private void stopTimeout() {
if (timeoutTimer != null) { if (timeoutTimer != null) {
timeoutTimer.cancel(); timeoutTimer.stop();
timeoutTimer = null; timeoutTimer = null;
} }
} }

View file

@ -17,6 +17,7 @@
package io.bitsquare.trade.protocol.trade; package io.bitsquare.trade.protocol.trade;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.PubKeyRing; import io.bitsquare.common.crypto.PubKeyRing;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
@ -42,7 +43,7 @@ public abstract class TradeProtocol {
protected final ProcessModel processModel; protected final ProcessModel processModel;
private final DecryptedDirectMessageListener decryptedDirectMessageListener; private final DecryptedDirectMessageListener decryptedDirectMessageListener;
protected Trade trade; protected Trade trade;
private java.util.Timer timeoutTimer; private Timer timeoutTimer;
public TradeProtocol(Trade trade) { public TradeProtocol(Trade trade) {
this.trade = trade; this.trade = trade;
@ -116,7 +117,7 @@ public abstract class TradeProtocol {
protected void stopTimeout() { protected void stopTimeout() {
if (timeoutTimer != null) { if (timeoutTimer != null) {
timeoutTimer.cancel(); timeoutTimer.stop();
timeoutTimer = null; timeoutTimer = null;
} }
} }

View file

@ -27,6 +27,7 @@ import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
import io.bitsquare.gui.SystemTray; import io.bitsquare.gui.SystemTray;
import io.bitsquare.gui.common.UITimer;
import io.bitsquare.gui.common.view.CachingViewLoader; import io.bitsquare.gui.common.view.CachingViewLoader;
import io.bitsquare.gui.common.view.View; import io.bitsquare.gui.common.view.View;
import io.bitsquare.gui.common.view.ViewLoader; import io.bitsquare.gui.common.view.ViewLoader;
@ -105,6 +106,7 @@ public class BitsquareApp extends Application {
Version.printVersion(); Version.printVersion();
UserThread.setExecutor(Platform::runLater); UserThread.setExecutor(Platform::runLater);
UserThread.setTimerClass(UITimer.class);
// setup UncaughtExceptionHandler // setup UncaughtExceptionHandler
Thread.UncaughtExceptionHandler handler = (thread, throwable) -> { Thread.UncaughtExceptionHandler handler = (thread, throwable) -> {

View file

@ -1,14 +1,14 @@
package io.bitsquare.gui.common; package io.bitsquare.gui.common;
import io.bitsquare.common.Clock; import io.bitsquare.common.Clock;
import org.reactfx.util.FxTimer; import io.bitsquare.common.Timer;
import org.reactfx.util.Timer; import io.bitsquare.common.UserThread;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
public class UIClock implements Clock { public class UIClock implements Clock {
private static final Logger log = LoggerFactory.getLogger(UIClock.class); private static final Logger log = LoggerFactory.getLogger(UIClock.class);
@ -16,21 +16,48 @@ public class UIClock implements Clock {
private final List<Listener> listeners = new LinkedList<>(); private final List<Listener> listeners = new LinkedList<>();
private long counter = 0; private long counter = 0;
private long lastSecondTick;
public UIClock() { public UIClock() {
} }
@Override @Override
public void start() { public void start() {
if (timer == null) if (timer == null) {
timer = FxTimer.runPeriodically(Duration.ofSeconds(1), () -> { lastSecondTick = System.currentTimeMillis();
timer = UserThread.runPeriodically(() -> {
listeners.stream().forEach(Listener::onSecondTick); listeners.stream().forEach(Listener::onSecondTick);
counter++; counter++;
if (counter >= 60) { if (counter >= 60) {
counter = 0; counter = 0;
listeners.stream().forEach(Listener::onMinuteTick); listeners.stream().forEach(Listener::onMinuteTick);
} }
});
long currentTimeMillis = System.currentTimeMillis();
long diff = currentTimeMillis - lastSecondTick;
if (diff > 1000)
listeners.stream().forEach(listener -> listener.onMissedSecondTick(diff - 1000));
lastSecondTick = currentTimeMillis;
}, 1, TimeUnit.SECONDS);
/* timer = FxTimer.runPeriodically(Duration.ofSeconds(1), () -> {
listeners.stream().forEach(Listener::onSecondTick);
counter++;
if (counter >= 60) {
counter = 0;
listeners.stream().forEach(Listener::onMinuteTick);
}
long currentTimeMillis = System.currentTimeMillis();
long diff = currentTimeMillis - lastSecondTick;
if (diff > 1000)
listeners.stream().forEach(listener -> listener.onMissedSecondTick(diff - 1000));
lastSecondTick = currentTimeMillis;
});*/
}
} }
@Override @Override

View file

@ -0,0 +1,42 @@
package io.bitsquare.gui.common;
import io.bitsquare.common.Timer;
import org.reactfx.util.FxTimer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class UITimer implements Timer {
private final Logger log = LoggerFactory.getLogger(UITimer.class);
private org.reactfx.util.Timer timer;
public UITimer() {
}
@Override
public Timer runLater(Duration delay, Runnable runnable) {
checkArgument(timer == null, "runLater or runPeriodically already called on that timer");
timer = FxTimer.create(delay, runnable);
timer.restart();
return this;
}
@Override
public Timer runPeriodically(Duration interval, Runnable runnable) {
checkArgument(timer == null, "runLater or runPeriodically already called on that timer");
timer = FxTimer.createPeriodic(interval, runnable);
timer.restart();
return this;
}
@Override
public void stop() {
checkNotNull(timer, "Timer must not be null");
timer.stop();
timer = null;
}
}

View file

@ -33,6 +33,7 @@ import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.pricefeed.MarketPriceFeed; import io.bitsquare.btc.pricefeed.MarketPriceFeed;
import io.bitsquare.common.Clock; import io.bitsquare.common.Clock;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.gui.Navigation; import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.model.ViewModel; import io.bitsquare.gui.common.model.ViewModel;
@ -67,13 +68,10 @@ import org.bitcoinj.store.BlockStoreException;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
import org.fxmisc.easybind.monadic.MonadicBinding; import org.fxmisc.easybind.monadic.MonadicBinding;
import org.reactfx.util.FxTimer;
import org.reactfx.util.Timer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.time.Duration;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -135,8 +133,8 @@ public class MainViewModel implements ViewModel {
private final User user; private final User user;
private int numBTCPeers = 0; private int numBTCPeers = 0;
private ChangeListener<Number> numConnectedPeersListener, btcNumPeersListener; private ChangeListener<Number> numConnectedPeersListener, btcNumPeersListener;
private java.util.Timer numberOfBtcPeersTimer; private Timer numberOfBtcPeersTimer;
private java.util.Timer numberOfP2PNetworkPeersTimer; private Timer numberOfP2PNetworkPeersTimer;
private Timer startupTimeout; private Timer startupTimeout;
private final Map<String, Subscription> disputeIsClosedSubscriptionsMap = new HashMap<>(); private final Map<String, Subscription> disputeIsClosedSubscriptionsMap = new HashMap<>();
private Subscription downloadPercentageSubscription; private Subscription downloadPercentageSubscription;
@ -199,7 +197,7 @@ public class MainViewModel implements ViewModel {
onAllServicesInitialized(); onAllServicesInitialized();
}); });
startupTimeout = FxTimer.runLater(Duration.ofMinutes(3), () -> { startupTimeout = UserThread.runAfter(() -> {
log.warn("startupTimeout called"); log.warn("startupTimeout called");
MainView.blur(); MainView.blur();
new Popup().warning("The application could not startup after 3 minutes.\n" + new Popup().warning("The application could not startup after 3 minutes.\n" +
@ -208,7 +206,18 @@ public class MainViewModel implements ViewModel {
.closeButtonText("Shut down") .closeButtonText("Shut down")
.onClose(BitsquareApp.shutDownHandler::run) .onClose(BitsquareApp.shutDownHandler::run)
.show(); .show();
}); }, 3);
/*startupTimeout = FxTimer.runLater(Duration.ofMinutes(3), () -> {
log.warn("startupTimeout called");
MainView.blur();
new Popup().warning("The application could not startup after 3 minutes.\n" +
"There might be some network connection problems or a unstable Tor path.\n\n" +
"Please restart and try again.")
.closeButtonText("Shut down")
.onClose(BitsquareApp.shutDownHandler::run)
.show();
});*/
} }
public void shutDown() { public void shutDown() {
@ -329,7 +338,7 @@ public class MainViewModel implements ViewModel {
if ((int) oldValue > 0 && (int) newValue == 0) { if ((int) oldValue > 0 && (int) newValue == 0) {
// give a bit of tolerance // give a bit of tolerance
if (numberOfBtcPeersTimer != null) if (numberOfBtcPeersTimer != null)
numberOfBtcPeersTimer.cancel(); numberOfBtcPeersTimer.stop();
numberOfBtcPeersTimer = UserThread.runAfter(() -> { numberOfBtcPeersTimer = UserThread.runAfter(() -> {
if (walletService.numPeersProperty().get() == 0) { if (walletService.numPeersProperty().get() == 0) {
walletServiceErrorMsg.set("You lost the connection to all bitcoin network peers.\n" + walletServiceErrorMsg.set("You lost the connection to all bitcoin network peers.\n" +
@ -520,7 +529,7 @@ public class MainViewModel implements ViewModel {
if ((int) oldValue > 0 && (int) newValue == 0) { if ((int) oldValue > 0 && (int) newValue == 0) {
// give a bit of tolerance // give a bit of tolerance
if (numberOfP2PNetworkPeersTimer != null) if (numberOfP2PNetworkPeersTimer != null)
numberOfP2PNetworkPeersTimer.cancel(); numberOfP2PNetworkPeersTimer.stop();
numberOfP2PNetworkPeersTimer = UserThread.runAfter(() -> { numberOfP2PNetworkPeersTimer = UserThread.runAfter(() -> {
if (p2PService.getNumConnectedPeers().get() == 0) { if (p2PService.getNumConnectedPeers().get() == 0) {
p2PNetworkWarnMsg.set("You lost the connection to all P2P network peers.\n" + p2PNetworkWarnMsg.set("You lost the connection to all P2P network peers.\n" +

View file

@ -17,6 +17,7 @@
package io.bitsquare.gui.main.popups; package io.bitsquare.gui.main.popups;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
import io.bitsquare.gui.main.MainView; import io.bitsquare.gui.main.MainView;
@ -42,7 +43,6 @@ import org.slf4j.LoggerFactory;
import java.time.Duration; import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.Timer;
import static io.bitsquare.gui.util.FormBuilder.addCheckBox; import static io.bitsquare.gui.util.FormBuilder.addCheckBox;
@ -109,7 +109,7 @@ public class Popup {
window.widthProperty().removeListener(positionListener); window.widthProperty().removeListener(positionListener);
if (centerTime != null) if (centerTime != null)
centerTime.cancel(); centerTime.stop();
removeEffectFromBackground(); removeEffectFromBackground();
@ -268,7 +268,7 @@ public class Popup {
if (stage != null) { if (stage != null) {
layout(); layout();
if (centerTime != null) if (centerTime != null)
centerTime.cancel(); centerTime.stop();
centerTime = UserThread.runAfter(this::layout, 3); centerTime = UserThread.runAfter(this::layout, 3);
} }

View file

@ -120,6 +120,10 @@ public abstract class TradeStepView extends AnchorPane {
public void onMinuteTick() { public void onMinuteTick() {
updateTimeLeft(); updateTimeLeft();
} }
@Override
public void onMissedSecondTick(long missed) {
}
}; };
model.clock.addListener(clockListener); model.clock.addListener(clockListener);
} }

View file

@ -63,7 +63,10 @@ public class NetworkStatisticListItem {
@Override @Override
public void onMinuteTick() { public void onMinuteTick() {
}
@Override
public void onMissedSecondTick(long missed) {
} }
}; };
clock.addListener(listener); clock.addListener(listener);

View file

@ -8,6 +8,7 @@ import com.google.inject.Inject;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.app.ProgramArguments; import io.bitsquare.app.ProgramArguments;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.CryptoException; import io.bitsquare.common.crypto.CryptoException;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
@ -550,7 +551,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
if (message instanceof AddDataMessage && if (message instanceof AddDataMessage &&
((AddDataMessage) message).data.equals(protectedMailboxData)) { ((AddDataMessage) message).data.equals(protectedMailboxData)) {
sendMailboxMessageListener.onStoredInMailbox(); sendMailboxMessageListener.onStoredInMailbox();
sendMailboxMessageTimeoutTimer.cancel(); sendMailboxMessageTimeoutTimer.stop();
} }
}; };
broadcaster.addListener(listener); broadcaster.addListener(listener);
@ -572,7 +573,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
boolean result = p2PDataStorage.add(protectedMailboxData, networkNode.getNodeAddress()); boolean result = p2PDataStorage.add(protectedMailboxData, networkNode.getNodeAddress());
if (!result) { if (!result) {
sendMailboxMessageTimeoutTimer.cancel(); sendMailboxMessageTimeoutTimer.stop();
broadcaster.removeListener(listener); broadcaster.removeListener(listener);
broadcaster.getNumOfBroadcastsProperty().removeListener(numOfBroadcastsChangeListener); broadcaster.getNumOfBroadcastsProperty().removeListener(numOfBroadcastsChangeListener);
sendMailboxMessageListener.onFault("Data already exists in our local database"); sendMailboxMessageListener.onFault("Data already exists in our local database");

View file

@ -7,6 +7,7 @@ import com.google.common.util.concurrent.MoreExecutors;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext; import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager; import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
@ -26,7 +27,6 @@ import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import java.util.Timer;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -116,7 +116,7 @@ public class TorNetworkNode extends NetworkNode {
allShutDown = EasyBind.combine(torNetworkNodeShutDown, networkNodeShutDown, shutDownTimerTriggered, (a, b, c) -> (a && b) || c); allShutDown = EasyBind.combine(torNetworkNodeShutDown, networkNodeShutDown, shutDownTimerTriggered, (a, b, c) -> (a && b) || c);
allShutDown.subscribe((observable, oldValue, newValue) -> { allShutDown.subscribe((observable, oldValue, newValue) -> {
if (newValue) { if (newValue) {
shutDownTimeoutTimer.cancel(); shutDownTimeoutTimer.stop();
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
log.debug("Shutdown executorService"); log.debug("Shutdown executorService");
try { try {

View file

@ -1,6 +1,7 @@
package io.bitsquare.p2p.peers; package io.bitsquare.p2p.peers;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
@ -507,7 +508,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
private void stopCheckMaxConnectionsTimer() { private void stopCheckMaxConnectionsTimer() {
if (checkMaxConnectionsTimer != null) { if (checkMaxConnectionsTimer != null) {
checkMaxConnectionsTimer.cancel(); checkMaxConnectionsTimer.stop();
checkMaxConnectionsTimer = null; checkMaxConnectionsTimer = null;
} }
} }

View file

@ -4,6 +4,7 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
@ -24,7 +25,6 @@ import org.slf4j.LoggerFactory;
import java.util.HashSet; import java.util.HashSet;
import java.util.Random; import java.util.Random;
import java.util.Timer;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
@ -200,7 +200,7 @@ public class RequestDataHandshake implements MessageListener {
private void stopTimeoutTimer() { private void stopTimeoutTimer() {
if (timeoutTimer != null) { if (timeoutTimer != null) {
timeoutTimer.cancel(); timeoutTimer.stop();
timeoutTimer = null; timeoutTimer = null;
} }
} }

View file

@ -1,6 +1,7 @@
package io.bitsquare.p2p.peers.getdata; package io.bitsquare.p2p.peers.getdata;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
@ -250,7 +251,7 @@ public class RequestDataManager implements MessageListener {
private void stopRequestDataTimer() { private void stopRequestDataTimer() {
if (requestDataTimer != null) { if (requestDataTimer != null) {
requestDataTimer.cancel(); requestDataTimer.stop();
requestDataTimer = null; requestDataTimer = null;
} }
} }

View file

@ -4,6 +4,7 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.network.CloseConnectionReason; import io.bitsquare.p2p.network.CloseConnectionReason;
import io.bitsquare.p2p.network.Connection; import io.bitsquare.p2p.network.Connection;
@ -15,7 +16,6 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Timer;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
@ -116,7 +116,7 @@ class GetPeersRequestHandler {
private void cleanup() { private void cleanup() {
if (timeoutTimer != null) { if (timeoutTimer != null) {
timeoutTimer.cancel(); timeoutTimer.stop();
timeoutTimer = null; timeoutTimer = null;
} }
} }

View file

@ -4,6 +4,7 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
@ -20,7 +21,6 @@ import org.slf4j.LoggerFactory;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Random; import java.util.Random;
import java.util.Timer;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
@ -70,7 +70,7 @@ class PeerExchangeHandler implements MessageListener {
connection.removeMessageListener(this); connection.removeMessageListener(this);
if (timeoutTimer != null) { if (timeoutTimer != null) {
timeoutTimer.cancel(); timeoutTimer.stop();
timeoutTimer = null; timeoutTimer = null;
} }
} }

View file

@ -2,6 +2,7 @@ package io.bitsquare.p2p.peers.peerexchange;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
@ -101,7 +102,7 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener
if (lostAllConnections || connectToMorePeersTimer == null) { if (lostAllConnections || connectToMorePeersTimer == null) {
long delaySec = lostAllConnections ? RETRY_DELAY_AFTER_ALL_CON_LOST_SEC : RETRY_DELAY_SEC; long delaySec = lostAllConnections ? RETRY_DELAY_AFTER_ALL_CON_LOST_SEC : RETRY_DELAY_SEC;
if (lostAllConnections && connectToMorePeersTimer != null) if (lostAllConnections && connectToMorePeersTimer != null)
connectToMorePeersTimer.cancel(); connectToMorePeersTimer.stop();
connectToMorePeersTimer = UserThread.runAfter(() -> { connectToMorePeersTimer = UserThread.runAfter(() -> {
log.trace("ConnectToMorePeersTimer called from onDisconnect code path"); log.trace("ConnectToMorePeersTimer called from onDisconnect code path");
@ -292,7 +293,7 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener
private void stopConnectToMorePeersTimer() { private void stopConnectToMorePeersTimer() {
if (connectToMorePeersTimer != null) { if (connectToMorePeersTimer != null) {
connectToMorePeersTimer.cancel(); connectToMorePeersTimer.stop();
connectToMorePeersTimer = null; connectToMorePeersTimer = null;
} }
} }