Handle offer removal on disconnects

This commit is contained in:
Manfred Karrer 2016-02-18 00:42:22 +01:00
parent cb236f65fb
commit 58bb1868a3
29 changed files with 532 additions and 385 deletions

View file

@ -20,7 +20,7 @@ package io.bitsquare.alert;
import com.google.inject.Inject;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.ProtectedData;
import io.bitsquare.p2p.storage.data.ProtectedData;
import io.bitsquare.user.User;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;

View file

@ -30,7 +30,7 @@ import io.bitsquare.p2p.BootstrapListener;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.ProtectedData;
import io.bitsquare.p2p.storage.data.ProtectedData;
import io.bitsquare.user.User;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;

View file

@ -25,7 +25,7 @@ import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.common.util.JsonExclude;
import io.bitsquare.locale.Country;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.storage.messages.RequiresLiveOwnerData;
import io.bitsquare.p2p.storage.messages.RequiresOwnerIsOnlineMessage;
import io.bitsquare.p2p.storage.messages.StorageMessage;
import io.bitsquare.payment.PaymentMethod;
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
@ -47,7 +47,7 @@ import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public final class Offer implements StorageMessage, RequiresLiveOwnerData {
public final class Offer implements StorageMessage, RequiresOwnerIsOnlineMessage {
// That object is sent over the wire, so we need to take care of version compatibility.
@JsonExclude
private static final long serialVersionUID = Version.P2P_NETWORK_VERSION;
@ -55,7 +55,7 @@ public final class Offer implements StorageMessage, RequiresLiveOwnerData {
private static final Logger log = LoggerFactory.getLogger(Offer.class);
public static final long TTL = TimeUnit.SECONDS.toMillis(60);
public final static String TAC_OFFERER = "When placing that offer I accept that anyone who fulfills my conditions can " +
"take that offer.";
public static final String TAC_TAKER = "With taking the offer I commit to the trade conditions as defined.";

View file

@ -21,7 +21,7 @@ import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.ProtectedData;
import io.bitsquare.p2p.storage.data.ProtectedData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -84,21 +84,27 @@ public class OfferBookService {
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void republishOffers(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
doAddOffer(offer, resultHandler, errorMessageHandler, true);
}
public void addOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
doAddOffer(offer, resultHandler, errorMessageHandler, false);
}
public void republishOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
doAddOffer(offer, resultHandler, errorMessageHandler, true);
public void doAddOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler, boolean forceBroadcast) {
boolean result = p2PService.addData(offer, forceBroadcast);
if (result) {
log.trace("Add offer to network was successful. Offer ID = " + offer.getId());
resultHandler.handleResult();
} else {
errorMessageHandler.handleErrorMessage("Add offer failed");
}
}
private void doAddOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler, boolean rePublish) {
boolean result;
if (rePublish)
result = p2PService.republishData(offer);
else
result = p2PService.addData(offer);
public void refreshOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
boolean result = p2PService.refreshTTL(offer);
if (result) {
log.trace("Add offer to network was successful. Offer ID = " + offer.getId());
resultHandler.handleResult();

View file

@ -30,6 +30,10 @@ 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;
import io.bitsquare.trade.closed.ClosedTradableManager;
@ -70,6 +74,10 @@ public class OpenOfferManager {
private boolean shutDownRequested;
private BootstrapListener bootstrapListener;
private final Timer timer = new Timer();
private Timer republishOffersTime;
private boolean firstTimeConnection;
private boolean allowRefreshOffers;
private boolean lostAllConnections;
///////////////////////////////////////////////////////////////////////////////////////////
@ -112,6 +120,44 @@ public class OpenOfferManager {
if (message instanceof OfferAvailabilityRequest)
handleOfferAvailabilityRequest((OfferAvailabilityRequest) message, peersNodeAddress);
});
NetworkNode networkNode = p2PService.getNetworkNode();
networkNode.addConnectionListener(new ConnectionListener() {
@Override
public void onConnection(Connection connection) {
log.error("ConnectionListener onConnection size " + networkNode.getAllConnections().size());
log.error("ConnectionListener onConnection lostAllConnections " + lostAllConnections);
log.error("ConnectionListener onConnection allowRefreshOffers " + allowRefreshOffers);
log.error("ConnectionListener onConnection republishOffersTime " + republishOffersTime);
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) {
log.error("ConnectionListener onDisconnect size " + networkNode.getAllConnections().size());
lostAllConnections = networkNode.getAllConnections().isEmpty();
if (lostAllConnections)
allowRefreshOffers = false;
}
@Override
public void onError(Throwable throwable) {
}
});
}
@ -132,53 +178,75 @@ public class OpenOfferManager {
bootstrapListener = new BootstrapListener() {
@Override
public void onBootstrapComplete() {
startRePublishThread();
onBootstrapped();
}
};
p2PService.addP2PServiceListener(bootstrapListener);
} else {
startRePublishThread();
onBootstrapped();
}
}
private void startRePublishThread() {
private void onBootstrapped() {
if (bootstrapListener != null)
p2PService.removeP2PServiceListener(bootstrapListener);
// republish sufficiently before offer would expire
republishOffers();
startRefreshOffersThread();
//TODO should not be needed
// startRepublishOffersThread();
}
private void startRefreshOffersThread() {
allowRefreshOffers = true;
// refresh sufficiently before offer would expire
long period = (long) (Offer.TTL * 0.7);
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
UserThread.execute(OpenOfferManager.this::rePublishOffers);
UserThread.execute(OpenOfferManager.this::refreshOffers);
}
};
timer.scheduleAtFixedRate(timerTask, 500, period);
p2PService.getNumConnectedPeers().addListener((observable, oldValue, newValue) -> {
if ((int) oldValue == 0 && (int) newValue > 0) {
rePublishOffers();
// We repeat a rePublishOffers call after 10 seconds if we have more than 3 peers
UserThread.runAfter(() -> {
if (p2PService.getNumConnectedPeers().get() > 3)
rePublishOffers();
}, 10);
}
});
timer.scheduleAtFixedRate(timerTask, period, period);
}
private void rePublishOffers() {
private void startRepublishOffersThread() {
long period = Offer.TTL * 10;
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
UserThread.execute(OpenOfferManager.this::republishOffers);
}
};
timer.scheduleAtFixedRate(timerTask, period, period);
}
private void republishOffers() {
log.error("republishOffers ");
Log.traceCall("Number of offer for republish: " + openOffers.size());
for (OpenOffer openOffer : openOffers) {
offerBookService.republishOffer(openOffer.getOffer(),
offerBookService.republishOffers(openOffer.getOffer(),
() -> log.debug("Successful added offer to P2P network"),
errorMessage -> log.error("Add offer to P2P network failed. " + errorMessage));
openOffer.setStorage(openOffersStorage);
}
}
private void refreshOffers() {
if (allowRefreshOffers) {
Log.traceCall("Number of offer for refresh: " + openOffers.size());
for (OpenOffer openOffer : openOffers) {
offerBookService.refreshOffer(openOffer.getOffer(),
() -> log.debug("Successful refreshed TTL for offer"),
errorMessage -> log.error("Refresh TTL for offer failed. " + errorMessage));
openOffer.setStorage(openOffersStorage);
}
}
}
@SuppressWarnings("WeakerAccess")
public void shutDown() {
shutDown(null);