diff --git a/common/src/main/java/io/bitsquare/common/Clock.java b/common/src/main/java/io/bitsquare/common/Clock.java index ab3b05874e..b9e60fa430 100644 --- a/common/src/main/java/io/bitsquare/common/Clock.java +++ b/common/src/main/java/io/bitsquare/common/Clock.java @@ -13,5 +13,7 @@ public interface Clock { void onSecondTick(); void onMinuteTick(); + + void onMissedSecondTick(long missed); } } diff --git a/common/src/main/java/io/bitsquare/common/DefaultJavaTimer.java b/common/src/main/java/io/bitsquare/common/DefaultJavaTimer.java new file mode 100644 index 0000000000..c07545430e --- /dev/null +++ b/common/src/main/java/io/bitsquare/common/DefaultJavaTimer.java @@ -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; + } +} diff --git a/common/src/main/java/io/bitsquare/common/Timer.java b/common/src/main/java/io/bitsquare/common/Timer.java new file mode 100644 index 0000000000..cab1c499e3 --- /dev/null +++ b/common/src/main/java/io/bitsquare/common/Timer.java @@ -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(); +} diff --git a/common/src/main/java/io/bitsquare/common/UserThread.java b/common/src/main/java/io/bitsquare/common/UserThread.java index 4e8ed4fbdb..c10a17c75d 100644 --- a/common/src/main/java/io/bitsquare/common/UserThread.java +++ b/common/src/main/java/io/bitsquare/common/UserThread.java @@ -21,14 +21,15 @@ import com.google.common.util.concurrent.MoreExecutors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.InvocationTargetException; +import java.time.Duration; import java.util.Random; -import java.util.Timer; -import java.util.TimerTask; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; public class UserThread { private static final Logger log = LoggerFactory.getLogger(UserThread.class); + private static Class timerClass; public static Executor getExecutor() { return executor; @@ -38,9 +39,14 @@ public class UserThread { UserThread.executor = executor; } + public static void setTimerClass(Class timerClass) { + UserThread.timerClass = timerClass; + } + static { // If not defined we use same thread as caller thread executor = MoreExecutors.directExecutor(); + timerClass = DefaultJavaTimer.class; } private static Executor executor; @@ -65,19 +71,25 @@ public class UserThread { } public static Timer runAfter(Runnable runnable, long delay, TimeUnit timeUnit) { - Timer timer = new 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()); - } - } - }, timeUnit.toMillis(delay)); - return timer; + return getTimer().runLater(Duration.ofMillis(timeUnit.toMillis(delay)), runnable); + } + + public static Timer runPeriodically(Runnable runnable, long interval) { + 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 { + return timerClass.getDeclaredConstructor().newInstance(); + } catch (InstantiationException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + String message = "Could not instantiate timer bsTimerClass=" + timerClass; + log.error(message); + e.printStackTrace(); + throw new RuntimeException(message); + } } } diff --git a/core/src/main/java/io/bitsquare/btc/WalletService.java b/core/src/main/java/io/bitsquare/btc/WalletService.java index 2c42686729..96dabb6b8c 100644 --- a/core/src/main/java/io/bitsquare/btc/WalletService.java +++ b/core/src/main/java/io/bitsquare/btc/WalletService.java @@ -25,6 +25,7 @@ import com.google.common.util.concurrent.Service; import io.bitsquare.btc.listeners.AddressConfidenceListener; import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.TxConfidenceListener; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ExceptionHandler; @@ -187,7 +188,7 @@ public class WalletService { // set after wallet is ready 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 UserThread.runAfter(resultHandler::handleResult, 100, TimeUnit.MILLISECONDS); @@ -242,7 +243,7 @@ public class WalletService { public void failed(@NotNull Service.State from, @NotNull Throwable failure) { walletAppKit = null; log.error("walletAppKit failed"); - timeoutTimer.cancel(); + timeoutTimer.stop(); UserThread.execute(() -> exceptionHandler.handleException(failure)); } }, Threading.USER_THREAD); diff --git a/core/src/main/java/io/bitsquare/btc/blockchain/GetFeeRequest.java b/core/src/main/java/io/bitsquare/btc/blockchain/GetFeeRequest.java index 2e7c5f68d8..cc702ff304 100644 --- a/core/src/main/java/io/bitsquare/btc/blockchain/GetFeeRequest.java +++ b/core/src/main/java/io/bitsquare/btc/blockchain/GetFeeRequest.java @@ -2,6 +2,7 @@ package io.bitsquare.btc.blockchain; import com.google.common.util.concurrent.*; import io.bitsquare.btc.blockchain.providers.FeeProvider; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.common.util.Utilities; import io.bitsquare.http.HttpException; @@ -11,7 +12,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.util.Timer; class GetFeeRequest { private static final Logger log = LoggerFactory.getLogger(GetFeeRequest.class); @@ -72,7 +72,7 @@ class GetFeeRequest { } private void stopTimer() { - timer.cancel(); + timer.stop(); timer = null; } } diff --git a/core/src/main/java/io/bitsquare/trade/offer/OpenOffer.java b/core/src/main/java/io/bitsquare/trade/offer/OpenOffer.java index 4d83029645..a79e536070 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/OpenOffer.java +++ b/core/src/main/java/io/bitsquare/trade/offer/OpenOffer.java @@ -18,6 +18,7 @@ package io.bitsquare.trade.offer; import io.bitsquare.app.Version; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.storage.Storage; 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. private static final long TIMEOUT_SEC = 30; - transient private java.util.Timer timeoutTimer; + transient private Timer timeoutTimer; public enum State { AVAILABLE, @@ -118,7 +119,7 @@ public final class OpenOffer implements Tradable { private void stopTimeout() { if (timeoutTimer != null) { - timeoutTimer.cancel(); + timeoutTimer.stop(); timeoutTimer = null; } } diff --git a/core/src/main/java/io/bitsquare/trade/offer/OpenOfferManager.java b/core/src/main/java/io/bitsquare/trade/offer/OpenOfferManager.java index 6954e165c7..81e9250313 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/OpenOfferManager.java +++ b/core/src/main/java/io/bitsquare/trade/offer/OpenOfferManager.java @@ -21,6 +21,8 @@ import com.google.inject.Inject; import io.bitsquare.app.Log; import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; +import io.bitsquare.common.Clock; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.handlers.ErrorMessageHandler; @@ -30,6 +32,9 @@ import io.bitsquare.p2p.Message; import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.P2PService; 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.storage.Storage; import io.bitsquare.trade.TradableList; @@ -48,8 +53,6 @@ import javax.annotation.Nullable; import javax.inject.Named; import java.io.File; import java.util.Optional; -import java.util.Timer; -import java.util.TimerTask; import java.util.concurrent.TimeUnit; import static com.google.inject.internal.util.$Preconditions.checkNotNull; @@ -67,17 +70,19 @@ public class OpenOfferManager { private final TradeWalletService tradeWalletService; private final OfferBookService offerBookService; private final ClosedTradableManager closedTradableManager; + private Clock clock; private final TradableList openOffers; private final Storage> openOffersStorage; private boolean shutDownRequested; private BootstrapListener bootstrapListener; - private final Timer timer = new Timer(); - private Timer republishOffersTime; - private boolean firstTimeConnection; + //private final Timer republishOffersTimer = new Timer(); + private Timer refreshOffersTimer; + private Timer republishOffersTimer; private boolean allowRefreshOffers; private boolean lostAllConnections; private long refreshOffersPeriod; + private Clock.Listener listener; /////////////////////////////////////////////////////////////////////////////////////////// @@ -92,6 +97,7 @@ public class OpenOfferManager { TradeWalletService tradeWalletService, OfferBookService offerBookService, ClosedTradableManager closedTradableManager, + Clock clock, @Named("storage.dir") File storageDir) { this.keyRing = keyRing; this.user = user; @@ -100,6 +106,7 @@ public class OpenOfferManager { this.tradeWalletService = tradeWalletService; this.offerBookService = offerBookService; this.closedTradableManager = closedTradableManager; + this.clock = clock; openOffersStorage = new Storage<>(storageDir); this.openOffers = new TradableList<>(openOffersStorage, "OpenOffers"); @@ -120,42 +127,6 @@ public class OpenOfferManager { if (message instanceof OfferAvailabilityRequest) 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 //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() { long period = Offer.TTL * 10; TimerTask timerTask = new TimerTask() { @@ -205,18 +230,27 @@ public class OpenOfferManager { UserThread.execute(OpenOfferManager.this::republishOffers); } }; - timer.scheduleAtFixedRate(timerTask, period, period); - } + republishOffersTimer.scheduleAtFixedRate(timerTask, period, period); + }*/ private void republishOffers() { Log.traceCall("Number of offer for republish: " + openOffers.size()); + allowRefreshOffers = false; + if (republishOffersTimer != null) { + republishOffersTimer.stop(); + republishOffersTimer = null; + } + for (OpenOffer openOffer : openOffers) { offerBookService.republishOffers(openOffer.getOffer(), () -> { log.debug("Successful added offer to P2P network"); 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); } } @@ -224,13 +258,7 @@ public class OpenOfferManager { private void startRefreshOffersThread() { // refresh sufficiently before offer would expire refreshOffersPeriod = (long) (Offer.TTL * 0.7); - TimerTask timerTask = new TimerTask() { - @Override - public void run() { - UserThread.execute(OpenOfferManager.this::refreshOffers); - } - }; - timer.scheduleAtFixedRate(timerTask, refreshOffersPeriod, refreshOffersPeriod); + refreshOffersTimer = UserThread.runPeriodically(OpenOfferManager.this::refreshOffers, refreshOffersPeriod, TimeUnit.MILLISECONDS); } private void refreshOffers() { @@ -251,8 +279,14 @@ public class OpenOfferManager { } public void shutDown(@Nullable Runnable completeHandler) { - if (timer != null) - timer.cancel(); + if (republishOffersTimer != null) + republishOffersTimer.stop(); + + if (refreshOffersTimer != null) + refreshOffersTimer.stop(); + + if (listener != null) + clock.removeListener(listener); if (!shutDownRequested) { log.info("remove all open offers at shutDown"); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/availability/OfferAvailabilityProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/availability/OfferAvailabilityProtocol.java index 0874b5dde8..8b6711435c 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/availability/OfferAvailabilityProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/availability/OfferAvailabilityProtocol.java @@ -17,6 +17,7 @@ package io.bitsquare.trade.protocol.availability; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ResultHandler; @@ -44,7 +45,7 @@ public class OfferAvailabilityProtocol { private final DecryptedDirectMessageListener decryptedDirectMessageListener; private TaskRunner taskRunner; - private java.util.Timer timeoutTimer; + private Timer timeoutTimer; /////////////////////////////////////////////////////////////////////////////////////////// @@ -145,7 +146,7 @@ public class OfferAvailabilityProtocol { private void stopTimeout() { if (timeoutTimer != null) { - timeoutTimer.cancel(); + timeoutTimer.stop(); timeoutTimer = null; } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java index 5ffd097676..c8d8798067 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java @@ -17,6 +17,7 @@ package io.bitsquare.trade.protocol.trade; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.common.crypto.PubKeyRing; import io.bitsquare.p2p.Message; @@ -42,7 +43,7 @@ public abstract class TradeProtocol { protected final ProcessModel processModel; private final DecryptedDirectMessageListener decryptedDirectMessageListener; protected Trade trade; - private java.util.Timer timeoutTimer; + private Timer timeoutTimer; public TradeProtocol(Trade trade) { this.trade = trade; @@ -116,7 +117,7 @@ public abstract class TradeProtocol { protected void stopTimeout() { if (timeoutTimer != null) { - timeoutTimer.cancel(); + timeoutTimer.stop(); timeoutTimer = null; } } diff --git a/gui/src/main/java/io/bitsquare/app/BitsquareApp.java b/gui/src/main/java/io/bitsquare/app/BitsquareApp.java index ab43ea52f8..51cbcbf5c3 100644 --- a/gui/src/main/java/io/bitsquare/app/BitsquareApp.java +++ b/gui/src/main/java/io/bitsquare/app/BitsquareApp.java @@ -27,6 +27,7 @@ import io.bitsquare.common.UserThread; import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.util.Utilities; import io.bitsquare.gui.SystemTray; +import io.bitsquare.gui.common.UITimer; import io.bitsquare.gui.common.view.CachingViewLoader; import io.bitsquare.gui.common.view.View; import io.bitsquare.gui.common.view.ViewLoader; @@ -105,6 +106,7 @@ public class BitsquareApp extends Application { Version.printVersion(); UserThread.setExecutor(Platform::runLater); + UserThread.setTimerClass(UITimer.class); // setup UncaughtExceptionHandler Thread.UncaughtExceptionHandler handler = (thread, throwable) -> { diff --git a/gui/src/main/java/io/bitsquare/gui/common/UIClock.java b/gui/src/main/java/io/bitsquare/gui/common/UIClock.java index ff51dcf3c5..ec98837208 100644 --- a/gui/src/main/java/io/bitsquare/gui/common/UIClock.java +++ b/gui/src/main/java/io/bitsquare/gui/common/UIClock.java @@ -1,14 +1,14 @@ package io.bitsquare.gui.common; import io.bitsquare.common.Clock; -import org.reactfx.util.FxTimer; -import org.reactfx.util.Timer; +import io.bitsquare.common.Timer; +import io.bitsquare.common.UserThread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.time.Duration; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.TimeUnit; public class UIClock implements Clock { private static final Logger log = LoggerFactory.getLogger(UIClock.class); @@ -16,21 +16,48 @@ public class UIClock implements Clock { private final List listeners = new LinkedList<>(); private long counter = 0; + private long lastSecondTick; public UIClock() { } @Override public void start() { - if (timer == null) - timer = FxTimer.runPeriodically(Duration.ofSeconds(1), () -> { + if (timer == null) { + lastSecondTick = System.currentTimeMillis(); + timer = UserThread.runPeriodically(() -> { 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; + }, 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 diff --git a/gui/src/main/java/io/bitsquare/gui/common/UITimer.java b/gui/src/main/java/io/bitsquare/gui/common/UITimer.java new file mode 100644 index 0000000000..332c240ed3 --- /dev/null +++ b/gui/src/main/java/io/bitsquare/gui/common/UITimer.java @@ -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; + } +} diff --git a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java index 66ca28f3df..4839638a95 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java @@ -33,6 +33,7 @@ import io.bitsquare.btc.WalletService; import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.pricefeed.MarketPriceFeed; import io.bitsquare.common.Clock; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.gui.Navigation; 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.Subscription; import org.fxmisc.easybind.monadic.MonadicBinding; -import org.reactfx.util.FxTimer; -import org.reactfx.util.Timer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; -import java.time.Duration; import java.util.*; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; @@ -135,8 +133,8 @@ public class MainViewModel implements ViewModel { private final User user; private int numBTCPeers = 0; private ChangeListener numConnectedPeersListener, btcNumPeersListener; - private java.util.Timer numberOfBtcPeersTimer; - private java.util.Timer numberOfP2PNetworkPeersTimer; + private Timer numberOfBtcPeersTimer; + private Timer numberOfP2PNetworkPeersTimer; private Timer startupTimeout; private final Map disputeIsClosedSubscriptionsMap = new HashMap<>(); private Subscription downloadPercentageSubscription; @@ -199,7 +197,7 @@ public class MainViewModel implements ViewModel { onAllServicesInitialized(); }); - startupTimeout = FxTimer.runLater(Duration.ofMinutes(3), () -> { + startupTimeout = UserThread.runAfter(() -> { log.warn("startupTimeout called"); MainView.blur(); new Popup().warning("The application could not startup after 3 minutes.\n" + @@ -208,7 +206,18 @@ public class MainViewModel implements ViewModel { .closeButtonText("Shut down") .onClose(BitsquareApp.shutDownHandler::run) .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() { @@ -329,7 +338,7 @@ public class MainViewModel implements ViewModel { if ((int) oldValue > 0 && (int) newValue == 0) { // give a bit of tolerance if (numberOfBtcPeersTimer != null) - numberOfBtcPeersTimer.cancel(); + numberOfBtcPeersTimer.stop(); numberOfBtcPeersTimer = UserThread.runAfter(() -> { if (walletService.numPeersProperty().get() == 0) { 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) { // give a bit of tolerance if (numberOfP2PNetworkPeersTimer != null) - numberOfP2PNetworkPeersTimer.cancel(); + numberOfP2PNetworkPeersTimer.stop(); numberOfP2PNetworkPeersTimer = UserThread.runAfter(() -> { if (p2PService.getNumConnectedPeers().get() == 0) { p2PNetworkWarnMsg.set("You lost the connection to all P2P network peers.\n" + diff --git a/gui/src/main/java/io/bitsquare/gui/main/popups/Popup.java b/gui/src/main/java/io/bitsquare/gui/main/popups/Popup.java index 5e8f090583..bd8e5b2330 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/popups/Popup.java +++ b/gui/src/main/java/io/bitsquare/gui/main/popups/Popup.java @@ -17,6 +17,7 @@ package io.bitsquare.gui.main.popups; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.common.util.Utilities; import io.bitsquare.gui.main.MainView; @@ -42,7 +43,6 @@ import org.slf4j.LoggerFactory; import java.time.Duration; import java.util.Optional; -import java.util.Timer; import static io.bitsquare.gui.util.FormBuilder.addCheckBox; @@ -109,7 +109,7 @@ public class Popup { window.widthProperty().removeListener(positionListener); if (centerTime != null) - centerTime.cancel(); + centerTime.stop(); removeEffectFromBackground(); @@ -268,7 +268,7 @@ public class Popup { if (stage != null) { layout(); if (centerTime != null) - centerTime.cancel(); + centerTime.stop(); centerTime = UserThread.runAfter(this::layout, 3); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/TradeStepView.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/TradeStepView.java index 414c3caf72..79dd80f41e 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/TradeStepView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/TradeStepView.java @@ -120,6 +120,10 @@ public abstract class TradeStepView extends AnchorPane { public void onMinuteTick() { updateTimeLeft(); } + + @Override + public void onMissedSecondTick(long missed) { + } }; model.clock.addListener(clockListener); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkStatisticListItem.java b/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkStatisticListItem.java index e067a552c8..fca02611cd 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkStatisticListItem.java +++ b/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkStatisticListItem.java @@ -63,7 +63,10 @@ public class NetworkStatisticListItem { @Override public void onMinuteTick() { + } + @Override + public void onMissedSecondTick(long missed) { } }; clock.addListener(listener); diff --git a/network/src/main/java/io/bitsquare/p2p/P2PService.java b/network/src/main/java/io/bitsquare/p2p/P2PService.java index 15c7b18417..0ba7157973 100644 --- a/network/src/main/java/io/bitsquare/p2p/P2PService.java +++ b/network/src/main/java/io/bitsquare/p2p/P2PService.java @@ -8,6 +8,7 @@ import com.google.inject.Inject; import com.google.inject.name.Named; import io.bitsquare.app.Log; import io.bitsquare.app.ProgramArguments; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.common.crypto.CryptoException; import io.bitsquare.common.crypto.KeyRing; @@ -550,7 +551,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis if (message instanceof AddDataMessage && ((AddDataMessage) message).data.equals(protectedMailboxData)) { sendMailboxMessageListener.onStoredInMailbox(); - sendMailboxMessageTimeoutTimer.cancel(); + sendMailboxMessageTimeoutTimer.stop(); } }; broadcaster.addListener(listener); @@ -572,7 +573,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis boolean result = p2PDataStorage.add(protectedMailboxData, networkNode.getNodeAddress()); if (!result) { - sendMailboxMessageTimeoutTimer.cancel(); + sendMailboxMessageTimeoutTimer.stop(); broadcaster.removeListener(listener); broadcaster.getNumOfBroadcastsProperty().removeListener(numOfBroadcastsChangeListener); sendMailboxMessageListener.onFault("Data already exists in our local database"); diff --git a/network/src/main/java/io/bitsquare/p2p/network/TorNetworkNode.java b/network/src/main/java/io/bitsquare/p2p/network/TorNetworkNode.java index 21d5ff9d10..9c3b2ee3df 100644 --- a/network/src/main/java/io/bitsquare/p2p/network/TorNetworkNode.java +++ b/network/src/main/java/io/bitsquare/p2p/network/TorNetworkNode.java @@ -7,6 +7,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext; import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager; import io.bitsquare.app.Log; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.common.util.Utilities; import io.bitsquare.p2p.NodeAddress; @@ -26,7 +27,6 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.net.Socket; -import java.util.Timer; import java.util.concurrent.TimeUnit; 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.subscribe((observable, oldValue, newValue) -> { if (newValue) { - shutDownTimeoutTimer.cancel(); + shutDownTimeoutTimer.stop(); long ts = System.currentTimeMillis(); log.debug("Shutdown executorService"); try { diff --git a/network/src/main/java/io/bitsquare/p2p/peers/PeerManager.java b/network/src/main/java/io/bitsquare/p2p/peers/PeerManager.java index 16ede724a1..137b07db1b 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/PeerManager.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/PeerManager.java @@ -1,6 +1,7 @@ package io.bitsquare.p2p.peers; import io.bitsquare.app.Log; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.NodeAddress; @@ -507,7 +508,7 @@ public class PeerManager implements ConnectionListener, MessageListener { private void stopCheckMaxConnectionsTimer() { if (checkMaxConnectionsTimer != null) { - checkMaxConnectionsTimer.cancel(); + checkMaxConnectionsTimer.stop(); checkMaxConnectionsTimer = null; } } diff --git a/network/src/main/java/io/bitsquare/p2p/peers/getdata/RequestDataHandshake.java b/network/src/main/java/io/bitsquare/p2p/peers/getdata/RequestDataHandshake.java index 289002eb01..226907c652 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/getdata/RequestDataHandshake.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/getdata/RequestDataHandshake.java @@ -4,6 +4,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.SettableFuture; import io.bitsquare.app.Log; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.NodeAddress; @@ -24,7 +25,6 @@ import org.slf4j.LoggerFactory; import java.util.HashSet; import java.util.Random; -import java.util.Timer; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkArgument; @@ -200,7 +200,7 @@ public class RequestDataHandshake implements MessageListener { private void stopTimeoutTimer() { if (timeoutTimer != null) { - timeoutTimer.cancel(); + timeoutTimer.stop(); timeoutTimer = null; } } diff --git a/network/src/main/java/io/bitsquare/p2p/peers/getdata/RequestDataManager.java b/network/src/main/java/io/bitsquare/p2p/peers/getdata/RequestDataManager.java index ca025a614e..ae05c83dbe 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/getdata/RequestDataManager.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/getdata/RequestDataManager.java @@ -1,6 +1,7 @@ package io.bitsquare.p2p.peers.getdata; import io.bitsquare.app.Log; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.NodeAddress; @@ -250,7 +251,7 @@ public class RequestDataManager implements MessageListener { private void stopRequestDataTimer() { if (requestDataTimer != null) { - requestDataTimer.cancel(); + requestDataTimer.stop(); requestDataTimer = null; } } diff --git a/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/GetPeersRequestHandler.java b/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/GetPeersRequestHandler.java index 4c69801974..91ba0cd33f 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/GetPeersRequestHandler.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/GetPeersRequestHandler.java @@ -4,6 +4,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.SettableFuture; import io.bitsquare.app.Log; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.p2p.network.CloseConnectionReason; import io.bitsquare.p2p.network.Connection; @@ -15,7 +16,6 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Timer; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkArgument; @@ -116,7 +116,7 @@ class GetPeersRequestHandler { private void cleanup() { if (timeoutTimer != null) { - timeoutTimer.cancel(); + timeoutTimer.stop(); timeoutTimer = null; } } diff --git a/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/PeerExchangeHandler.java b/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/PeerExchangeHandler.java index 7e1bf0d4f8..d3333dac17 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/PeerExchangeHandler.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/PeerExchangeHandler.java @@ -4,6 +4,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.SettableFuture; import io.bitsquare.app.Log; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.NodeAddress; @@ -20,7 +21,6 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.util.Random; -import java.util.Timer; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkArgument; @@ -70,7 +70,7 @@ class PeerExchangeHandler implements MessageListener { connection.removeMessageListener(this); if (timeoutTimer != null) { - timeoutTimer.cancel(); + timeoutTimer.stop(); timeoutTimer = null; } } diff --git a/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/PeerExchangeManager.java b/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/PeerExchangeManager.java index e551707dfd..cf5ac8465c 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/PeerExchangeManager.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/peerexchange/PeerExchangeManager.java @@ -2,6 +2,7 @@ package io.bitsquare.p2p.peers.peerexchange; import com.google.common.util.concurrent.MoreExecutors; import io.bitsquare.app.Log; +import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.common.util.Utilities; import io.bitsquare.p2p.Message; @@ -101,7 +102,7 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener if (lostAllConnections || connectToMorePeersTimer == null) { long delaySec = lostAllConnections ? RETRY_DELAY_AFTER_ALL_CON_LOST_SEC : RETRY_DELAY_SEC; if (lostAllConnections && connectToMorePeersTimer != null) - connectToMorePeersTimer.cancel(); + connectToMorePeersTimer.stop(); connectToMorePeersTimer = UserThread.runAfter(() -> { log.trace("ConnectToMorePeersTimer called from onDisconnect code path"); @@ -292,7 +293,7 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener private void stopConnectToMorePeersTimer() { if (connectToMorePeersTimer != null) { - connectToMorePeersTimer.cancel(); + connectToMorePeersTimer.stop(); connectToMorePeersTimer = null; } }