Handle offer removal on disconnect (WIP)

This commit is contained in:
Manfred Karrer 2016-02-16 13:18:25 +01:00
parent 92ad387d70
commit db363fac48
12 changed files with 199 additions and 55 deletions

View file

@ -25,6 +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.data.RequiresLiveOwner;
import io.bitsquare.p2p.storage.data.StorageMessage;
import io.bitsquare.payment.PaymentMethod;
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
@ -46,18 +47,22 @@ 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 {
public final class Offer implements StorageMessage, RequiresLiveOwner {
// 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;
@JsonExclude
private static final Logger log = LoggerFactory.getLogger(Offer.class);
public static final long TTL = TimeUnit.MINUTES.toMillis(10);
//public static final long TTL = TimeUnit.MINUTES.toMillis(10);
//TODO
public static final long TTL = TimeUnit.SECONDS.toMillis(10);
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.";
public enum Direction {BUY, SELL}
public enum State {
@ -154,6 +159,11 @@ public final class Offer implements StorageMessage {
}
}
@Override
public NodeAddress getOwnerNodeAddress() {
return offererNodeAddress;
}
public void validate() {
checkNotNull(getAmount(), "Amount is null");
checkNotNull(getArbitratorNodeAddresses(), "Arbitrator is null");

View file

@ -21,10 +21,13 @@ 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.data.ProtectedData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
@ -35,8 +38,14 @@ import java.util.stream.Collectors;
public class OfferBookService {
private static final Logger log = LoggerFactory.getLogger(OfferBookService.class);
private final P2PService p2PService;
public interface OfferBookChangedListener {
void onAdded(Offer offer);
void onRemoved(Offer offer);
}
private final P2PService p2PService;
private final List<OfferBookChangedListener> offerBookChangedListeners = new LinkedList<>();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -45,12 +54,37 @@ public class OfferBookService {
@Inject
public OfferBookService(P2PService p2PService) {
this.p2PService = p2PService;
p2PService.addHashSetChangedListener(new HashMapChangedListener() {
@Override
public void onAdded(ProtectedData entry) {
log.debug("OfferBookService.onAdded " + entry);
offerBookChangedListeners.stream().forEach(listener -> {
if (entry.expirableMessage instanceof Offer)
listener.onAdded((Offer) entry.expirableMessage);
});
}
@Override
public void onRemoved(ProtectedData entry) {
offerBookChangedListeners.stream().forEach(listener -> {
log.debug("OfferBookService.onRemoved " + entry);
if (entry.expirableMessage instanceof Offer)
listener.onRemoved((Offer) entry.expirableMessage);
});
}
});
}
public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) {
p2PService.addHashSetChangedListener(hashMapChangedListener);
public void addOfferBookChangedListener(OfferBookChangedListener offerBookChangedListener) {
offerBookChangedListeners.add(offerBookChangedListener);
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void addOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
doAddOffer(offer, resultHandler, errorMessageHandler, false);
}
@ -74,25 +108,27 @@ public class OfferBookService {
}
}
public void removeOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
public void removeOffer(Offer offer, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {
if (p2PService.removeData(offer)) {
log.trace("Remove offer from network was successful. Offer = " + offer);
if (resultHandler != null) resultHandler.handleResult();
if (resultHandler != null)
resultHandler.handleResult();
} else {
if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage("Remove offer failed");
if (errorMessageHandler != null)
errorMessageHandler.handleErrorMessage("Remove offer failed");
}
}
public List<Offer> getOffers() {
final List<Offer> offers = p2PService.getDataMap().values().stream()
return p2PService.getDataMap().values().stream()
.filter(e -> e.expirableMessage instanceof Offer)
.map(e -> (Offer) e.expirableMessage)
.collect(Collectors.toList());
return offers;
}
public void removeOfferAtShutDown(Offer offer) {
log.debug("removeOfferAtShutDown " + offer);
removeOffer(offer, null, null);
}
}

View file

@ -71,6 +71,7 @@ public class OpenOfferManager {
private BootstrapListener bootstrapListener;
private final Timer timer = new Timer();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@ -153,6 +154,18 @@ public class OpenOfferManager {
}
};
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);
}
});
}
private void rePublishOffers() {
@ -161,7 +174,6 @@ public class OpenOfferManager {
offerBookService.republishOffer(openOffer.getOffer(),
() -> log.debug("Successful added offer to P2P network"),
errorMessage -> log.error("Add offer to P2P network failed. " + errorMessage));
//setupDepositPublishedListener(openOffer);
openOffer.setStorage(openOffersStorage);
}
}
@ -175,14 +187,14 @@ public class OpenOfferManager {
timer.cancel();
if (!shutDownRequested) {
log.debug("shutDown");
log.info("remove all open offers at shutDown");
shutDownRequested = true;
int numOffers = openOffers.size();
// we remove own offers from offerbook when we go offline
openOffers.forEach(openOffer -> offerBookService.removeOfferAtShutDown(openOffer.getOffer()));
//TODO
// openOffers.forEach(openOffer -> offerBookService.removeOfferAtShutDown(openOffer.getOffer()));
if (completeHandler != null)
UserThread.runAfter(completeHandler::run, numOffers * 200 + 300, TimeUnit.MILLISECONDS);
UserThread.runAfter(completeHandler::run, openOffers.size() * 200 + 300, TimeUnit.MILLISECONDS);
}
}