diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index c6a5847f01..14f51439b1 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -35,6 +35,8 @@ package haveno.core.offer; import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.collect.ImmutableList; import com.google.inject.Inject; import haveno.common.ThreadUtils; import haveno.common.Timer; @@ -261,7 +263,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } private void cleanUpAddressEntries() { - Set openOffersIdSet = openOffers.getList().stream().map(OpenOffer::getId).collect(Collectors.toSet()); + Set openOffersIdSet; + synchronized (openOffers.getList()) { + openOffersIdSet = openOffers.getList().stream().map(OpenOffer::getId).collect(Collectors.toSet()); + } xmrWalletService.getAddressEntriesForOpenOffer().stream() .filter(e -> !openOffersIdSet.contains(e.getOfferId())) .forEach(e -> { @@ -292,7 +297,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe ThreadUtils.execute(() -> { // remove offers from offer book - synchronized (openOffers) { + synchronized (openOffers.getList()) { openOffers.forEach(openOffer -> { if (openOffer.getState() == OpenOffer.State.AVAILABLE) { offerBookService.removeOfferAtShutDown(openOffer.getOffer().getOfferPayload()); @@ -334,15 +339,17 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } private void removeOpenOffers(List openOffers, @Nullable Runnable completeHandler) { - int size = openOffers.size(); - // Copy list as we remove in the loop - List openOffersList = new ArrayList<>(openOffers); - openOffersList.forEach(openOffer -> cancelOpenOffer(openOffer, () -> { - }, errorMessage -> { - log.warn("Error removing open offer: " + errorMessage); - })); - if (completeHandler != null) - UserThread.runAfter(completeHandler, size * 200 + 500, TimeUnit.MILLISECONDS); + synchronized (openOffers) { + int size = openOffers.size(); + // Copy list as we remove in the loop + List openOffersList = new ArrayList<>(openOffers); + openOffersList.forEach(openOffer -> cancelOpenOffer(openOffer, () -> { + }, errorMessage -> { + log.warn("Error removing open offer: " + errorMessage); + })); + if (completeHandler != null) + UserThread.runAfter(completeHandler, size * 200 + 500, TimeUnit.MILLISECONDS); + } } @@ -450,13 +457,17 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe }); // poll spent status of open offer key images - for (OpenOffer openOffer : getOpenOffers()) { - xmrConnectionService.getKeyImagePoller().addKeyImages(openOffer.getOffer().getOfferPayload().getReserveTxKeyImages(), OPEN_OFFER_GROUP_KEY_IMAGE_ID); + synchronized (openOffers.getList()) { + for (OpenOffer openOffer : openOffers.getList()) { + xmrConnectionService.getKeyImagePoller().addKeyImages(openOffer.getOffer().getOfferPayload().getReserveTxKeyImages(), OPEN_OFFER_GROUP_KEY_IMAGE_ID); + } } // poll spent status of signed offer key images - for (SignedOffer signedOffer : signedOffers.getList()) { - xmrConnectionService.getKeyImagePoller().addKeyImages(signedOffer.getReserveTxKeyImages(), SIGNED_OFFER_KEY_IMAGE_GROUP_ID); + synchronized (signedOffers.getList()) { + for (SignedOffer signedOffer : signedOffers.getList()) { + xmrConnectionService.getKeyImagePoller().addKeyImages(signedOffer.getReserveTxKeyImages(), SIGNED_OFFER_KEY_IMAGE_GROUP_ID); + } } }, THREAD_ID); }); @@ -858,29 +869,25 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } public boolean hasAvailableOpenOffers() { - synchronized (openOffers) { - for (OpenOffer openOffer : getOpenOffers()) { - if (openOffer.getState() == OpenOffer.State.AVAILABLE) { - return true; - } + for (OpenOffer openOffer : getOpenOffers()) { + if (openOffer.getState() == OpenOffer.State.AVAILABLE) { + return true; } - return false; } + return false; } public List getOpenOffers() { - synchronized (openOffers) { - return new ArrayList<>(getObservableList()); + synchronized (openOffers.getList()) { + return ImmutableList.copyOf(getObservableList()); } } public List getOpenOfferGroup(String groupId) { if (groupId == null) throw new IllegalArgumentException("groupId cannot be null"); - synchronized (openOffers) { - return getOpenOffers().stream() - .filter(openOffer -> groupId.equals(openOffer.getGroupId())) - .collect(Collectors.toList()); - } + return getOpenOffers().stream() + .filter(openOffer -> groupId.equals(openOffer.getGroupId())) + .collect(Collectors.toList()); } public boolean hasClonedOffer(String offerId) { @@ -890,24 +897,22 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } public boolean hasClonedOffers() { - synchronized (openOffers) { - for (OpenOffer openOffer : getOpenOffers()) { - if (getOpenOfferGroup(openOffer.getGroupId()).size() > 1) { - return true; - } + for (OpenOffer openOffer : getOpenOffers()) { + if (getOpenOfferGroup(openOffer.getGroupId()).size() > 1) { + return true; } - return false; } + return false; } public List getSignedOffers() { - synchronized (signedOffers) { - return new ArrayList<>(signedOffers.getObservableList()); + synchronized (signedOffers.getList()) { + return ImmutableList.copyOf(signedOffers.getObservableList()); } } public ObservableList getObservableSignedOffersList() { - synchronized (signedOffers) { + synchronized (signedOffers.getList()) { return signedOffers.getObservableList(); } } @@ -917,9 +922,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } public Optional getOpenOffer(String offerId) { - synchronized (openOffers) { - return openOffers.stream().filter(e -> e.getId().equals(offerId)).findFirst(); - } + return getOpenOffers().stream().filter(e -> e.getId().equals(offerId)).findFirst(); } public boolean hasOpenOffer(String offerId) { @@ -927,14 +930,12 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } public Optional getSignedOfferById(String offerId) { - synchronized (signedOffers) { - return signedOffers.stream().filter(e -> e.getOfferId().equals(offerId)).findFirst(); - } + return getSignedOffers().stream().filter(e -> e.getOfferId().equals(offerId)).findFirst(); } private void addOpenOffer(OpenOffer openOffer) { log.info("Adding open offer {}", openOffer.getId()); - synchronized (openOffers) { + synchronized (openOffers.getList()) { openOffers.add(openOffer); } if (openOffer.getOffer().getOfferPayload().getReserveTxKeyImages() != null) { @@ -944,7 +945,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe private void removeOpenOffer(OpenOffer openOffer) { log.info("Removing open offer {}", openOffer.getId()); - synchronized (openOffers) { + synchronized (openOffers.getList()) { openOffers.remove(openOffer); } synchronized (placeOfferProtocols) { @@ -957,17 +958,19 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } private void cancelOpenOffersOnSpent(String keyImage) { - for (OpenOffer openOffer : getOpenOffers()) { - if (openOffer.getOffer().getOfferPayload().getReserveTxKeyImages() != null && openOffer.getOffer().getOfferPayload().getReserveTxKeyImages().contains(keyImage)) { - log.warn("Canceling open offer because reserved funds have been spent, offerId={}, state={}", openOffer.getId(), openOffer.getState()); - cancelOpenOffer(openOffer, null, null); + synchronized (openOffers.getList()) { + for (OpenOffer openOffer : openOffers.getList()) { + if (openOffer.getOffer().getOfferPayload().getReserveTxKeyImages() != null && openOffer.getOffer().getOfferPayload().getReserveTxKeyImages().contains(keyImage)) { + log.warn("Canceling open offer because reserved funds have been spent, offerId={}, state={}", openOffer.getId(), openOffer.getState()); + cancelOpenOffer(openOffer, null, null); + } } } } private void addSignedOffer(SignedOffer signedOffer) { log.info("Adding SignedOffer for offer {}", signedOffer.getOfferId()); - synchronized (signedOffers) { + synchronized (signedOffers.getList()) { // remove signed offers with common key images for (String keyImage : signedOffer.getReserveTxKeyImages()) { @@ -982,16 +985,18 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe private void removeSignedOffer(SignedOffer signedOffer) { log.info("Removing SignedOffer for offer {}", signedOffer.getOfferId()); - synchronized (signedOffers) { + synchronized (signedOffers.getList()) { signedOffers.remove(signedOffer); - xmrConnectionService.getKeyImagePoller().removeKeyImages(signedOffer.getReserveTxKeyImages(), SIGNED_OFFER_KEY_IMAGE_GROUP_ID); } + xmrConnectionService.getKeyImagePoller().removeKeyImages(signedOffer.getReserveTxKeyImages(), SIGNED_OFFER_KEY_IMAGE_GROUP_ID); } private void removeSignedOffers(String keyImage) { - for (SignedOffer signedOffer : new ArrayList(signedOffers.getList())) { - if (signedOffer.getReserveTxKeyImages().contains(keyImage)) { - removeSignedOffer(signedOffer); + synchronized (signedOffers.getList()) { + for (SignedOffer signedOffer : getSignedOffers()) { + if (signedOffer.getReserveTxKeyImages().contains(keyImage)) { + removeSignedOffer(signedOffer); + } } } } @@ -2070,7 +2075,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe OpenOffer openOffer = list.remove(0); boolean contained = false; - synchronized (openOffers) { + synchronized (openOffers.getList()) { contained = openOffers.contains(openOffer); } if (contained) { @@ -2171,7 +2176,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe if (periodicRefreshOffersTimer == null) periodicRefreshOffersTimer = UserThread.runPeriodically(() -> { if (!stopped) { - synchronized (openOffers) { + synchronized (openOffers.getList()) { int size = openOffers.size(); //we clone our list as openOffers might change during our delayed call final ArrayList openOffersList = new ArrayList<>(openOffers.getList()); @@ -2186,7 +2191,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe UserThread.runAfterRandomDelay(() -> { // we need to check if in the meantime the offer has been removed boolean contained = false; - synchronized (openOffers) { + synchronized (openOffers.getList()) { contained = openOffers.contains(openOffer); } if (contained) maybeRefreshOffer(openOffer, 0, 1); diff --git a/core/src/main/java/haveno/core/offer/SignedOfferList.java b/core/src/main/java/haveno/core/offer/SignedOfferList.java index a77d0402d9..4d75a14dd4 100644 --- a/core/src/main/java/haveno/core/offer/SignedOfferList.java +++ b/core/src/main/java/haveno/core/offer/SignedOfferList.java @@ -47,10 +47,12 @@ public final class SignedOfferList extends PersistableListAsObservable { @Override public Message toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder() - .setPaymentAccountList(protobuf.PaymentAccountList.newBuilder() - .addAllPaymentAccount(getList().stream().map(PaymentAccount::toProtoMessage).collect(Collectors.toList()))) - .build(); + synchronized (getList()) { + return protobuf.PersistableEnvelope.newBuilder() + .setPaymentAccountList(protobuf.PaymentAccountList.newBuilder() + .addAllPaymentAccount(getList().stream().map(PaymentAccount::toProtoMessage).collect(Collectors.toList()))) + .build(); + } } public static PaymentAccountList fromProto(protobuf.PaymentAccountList proto, CoreProtoResolver coreProtoResolver) { diff --git a/core/src/main/java/haveno/core/support/dispute/DisputeListService.java b/core/src/main/java/haveno/core/support/dispute/DisputeListService.java index 2c90565614..c2a1521a79 100644 --- a/core/src/main/java/haveno/core/support/dispute/DisputeListService.java +++ b/core/src/main/java/haveno/core/support/dispute/DisputeListService.java @@ -74,10 +74,12 @@ public abstract class DisputeListService> impleme @Override public void readPersisted(Runnable completeHandler) { persistenceManager.readPersisted(getFileName(), persisted -> { - disputeList.setAll(persisted.getList()); - completeHandler.run(); - }, - completeHandler); + synchronized (persisted.getList()) { + disputeList.setAll(persisted.getList()); + } + completeHandler.run(); + }, + completeHandler); } protected String getFileName() { @@ -145,26 +147,30 @@ public abstract class DisputeListService> impleme private void onDisputesChangeListener(List addedList, @Nullable List removedList) { if (removedList != null) { - removedList.forEach(dispute -> { - disputedTradeIds.remove(dispute.getTradeId()); + synchronized (removedList) { + removedList.forEach(dispute -> { + disputedTradeIds.remove(dispute.getTradeId()); + }); + } + } + synchronized (addedList) { + addedList.forEach(dispute -> { + // for each dispute added, keep track of its "BadgeCountProperty" + EasyBind.subscribe(dispute.getBadgeCountProperty(), + isAlerting -> { + // We get the event before the list gets updated, so we execute on next frame + UserThread.execute(() -> { + synchronized (disputeList.getObservableList()) { + int numAlerts = (int) disputeList.getList().stream() + .mapToLong(x -> x.getBadgeCountProperty().getValue()) + .sum(); + numOpenDisputes.set(numAlerts); + } + }); + }); + disputedTradeIds.add(dispute.getTradeId()); }); } - addedList.forEach(dispute -> { - // for each dispute added, keep track of its "BadgeCountProperty" - EasyBind.subscribe(dispute.getBadgeCountProperty(), - isAlerting -> { - // We get the event before the list gets updated, so we execute on next frame - UserThread.execute(() -> { - synchronized (disputeList.getObservableList()) { - int numAlerts = (int) disputeList.getList().stream() - .mapToLong(x -> x.getBadgeCountProperty().getValue()) - .sum(); - numOpenDisputes.set(numAlerts); - } - }); - }); - disputedTradeIds.add(dispute.getTradeId()); - }); } public void requestPersistence() { diff --git a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java index 047fe85456..ac0ede6e35 100644 --- a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java +++ b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java @@ -288,14 +288,16 @@ public abstract class DisputeManager> extends Sup cleanupDisputes(); List disputes = getDisputeList().getList(); - disputes.forEach(dispute -> { - try { - DisputeValidation.validateNodeAddresses(dispute, config); - } catch (DisputeValidation.ValidationException e) { - log.error(e.toString()); - validationExceptions.add(e); - } - }); + synchronized (disputes) { + disputes.forEach(dispute -> { + try { + DisputeValidation.validateNodeAddresses(dispute, config); + } catch (DisputeValidation.ValidationException e) { + log.error(e.toString()); + validationExceptions.add(e); + } + }); + } maybeClearSensitiveData(); } @@ -318,11 +320,13 @@ public abstract class DisputeManager> extends Sup public void maybeClearSensitiveData() { log.info("{} checking closed disputes eligibility for having sensitive data cleared", super.getClass().getSimpleName()); Instant safeDate = closedTradableManager.getSafeDateForSensitiveDataClearing(); - getDisputeList().getList().stream() - .filter(e -> e.isClosed()) - .filter(e -> e.getOpeningDate().toInstant().isBefore(safeDate)) - .forEach(Dispute::maybeClearSensitiveData); - requestPersistence(); + synchronized (getDisputeList().getList()) { + getDisputeList().getList().stream() + .filter(e -> e.isClosed()) + .filter(e -> e.getOpeningDate().toInstant().isBefore(safeDate)) + .forEach(Dispute::maybeClearSensitiveData); + requestPersistence(); + } } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java b/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java index 6ef9cd69ed..5ac7cd389a 100644 --- a/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java +++ b/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java @@ -176,18 +176,20 @@ public final class ArbitrationManager extends DisputeManager toRemoves = new HashSet<>(); List disputes = getDisputeList().getList(); - for (Dispute dispute : disputes) { + synchronized (disputes) { + for (Dispute dispute : disputes) { - // get dispute's trade - final Trade trade = tradeManager.getTrade(dispute.getTradeId()); - if (trade == null) { - log.warn("Dispute trade {} does not exist", dispute.getTradeId()); - return; - } - - // collect dispute if owned by arbitrator - if (dispute.getTraderPubKeyRing().equals(trade.getArbitrator().getPubKeyRing())) { - toRemoves.add(dispute); + // get dispute's trade + final Trade trade = tradeManager.getTrade(dispute.getTradeId()); + if (trade == null) { + log.warn("Dispute trade {} does not exist", dispute.getTradeId()); + return; + } + + // collect dispute if owned by arbitrator + if (dispute.getTraderPubKeyRing().equals(trade.getArbitrator().getPubKeyRing())) { + toRemoves.add(dispute); + } } } for (Dispute toRemove : toRemoves) { diff --git a/core/src/main/java/haveno/core/support/dispute/mediation/MediationDisputeList.java b/core/src/main/java/haveno/core/support/dispute/mediation/MediationDisputeList.java index 6387984b20..121c1c5a3b 100644 --- a/core/src/main/java/haveno/core/support/dispute/mediation/MediationDisputeList.java +++ b/core/src/main/java/haveno/core/support/dispute/mediation/MediationDisputeList.java @@ -55,8 +55,10 @@ public final class MediationDisputeList extends DisputeList { @Override public Message toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder().setMediationDisputeList(protobuf.MediationDisputeList.newBuilder() - .addAllDispute(ProtoUtil.collectionToProto(getList(), protobuf.Dispute.class))).build(); + synchronized (getList()) { + return protobuf.PersistableEnvelope.newBuilder().setMediationDisputeList(protobuf.MediationDisputeList.newBuilder() + .addAllDispute(ProtoUtil.collectionToProto(getList(), protobuf.Dispute.class))).build(); + } } public static MediationDisputeList fromProto(protobuf.MediationDisputeList proto, diff --git a/core/src/main/java/haveno/core/support/dispute/refund/RefundDisputeList.java b/core/src/main/java/haveno/core/support/dispute/refund/RefundDisputeList.java index 2ad31a0107..eb041c8fac 100644 --- a/core/src/main/java/haveno/core/support/dispute/refund/RefundDisputeList.java +++ b/core/src/main/java/haveno/core/support/dispute/refund/RefundDisputeList.java @@ -58,9 +58,10 @@ public final class RefundDisputeList extends DisputeList { @Override public Message toProtoMessage() { forEach(dispute -> checkArgument(dispute.getSupportType().equals(SupportType.REFUND), "Support type has to be REFUND")); - - return protobuf.PersistableEnvelope.newBuilder().setRefundDisputeList(protobuf.RefundDisputeList.newBuilder() - .addAllDispute(ProtoUtil.collectionToProto(getList(), protobuf.Dispute.class))).build(); + synchronized (getList()) { + return protobuf.PersistableEnvelope.newBuilder().setRefundDisputeList(protobuf.RefundDisputeList.newBuilder() + .addAllDispute(ProtoUtil.collectionToProto(getList(), protobuf.Dispute.class))).build(); + } } public static RefundDisputeList fromProto(protobuf.RefundDisputeList proto, diff --git a/core/src/main/java/haveno/core/trade/CleanupMailboxMessages.java b/core/src/main/java/haveno/core/trade/CleanupMailboxMessages.java index ee10d599ee..a201a985c2 100644 --- a/core/src/main/java/haveno/core/trade/CleanupMailboxMessages.java +++ b/core/src/main/java/haveno/core/trade/CleanupMailboxMessages.java @@ -55,21 +55,23 @@ public class CleanupMailboxMessages { } public void handleTrades(List trades) { - // We wrap in a try catch as in failed trades we cannot be sure if expected data is set, so we could get - // a NullPointer and do not want that this escalate to the user. - try { - if (p2PService.isBootstrapped()) { - cleanupMailboxMessages(trades); - } else { - p2PService.addP2PServiceListener(new BootstrapListener() { - @Override - public void onDataReceived() { - cleanupMailboxMessages(trades); - } - }); + synchronized (trades) { + // We wrap in a try catch as in failed trades we cannot be sure if expected data is set, so we could get + // a NullPointer and do not want that this escalate to the user. + try { + if (p2PService.isBootstrapped()) { + cleanupMailboxMessages(trades); + } else { + p2PService.addP2PServiceListener(new BootstrapListener() { + @Override + public void onDataReceived() { + cleanupMailboxMessages(trades); + } + }); + } + } catch (Throwable t) { + log.error("Cleanup mailbox messages failed. {}", t.toString()); } - } catch (Throwable t) { - log.error("Cleanup mailbox messages failed. {}", t.toString()); } } diff --git a/core/src/main/java/haveno/core/trade/ClosedTradableManager.java b/core/src/main/java/haveno/core/trade/ClosedTradableManager.java index cac8e9e261..02920c0e3d 100644 --- a/core/src/main/java/haveno/core/trade/ClosedTradableManager.java +++ b/core/src/main/java/haveno/core/trade/ClosedTradableManager.java @@ -81,13 +81,15 @@ public class ClosedTradableManager implements PersistedDataHost { @Override public void readPersisted(Runnable completeHandler) { persistenceManager.readPersisted(persisted -> { - closedTradables.setAll(persisted.getList()); - closedTradables.stream() - .filter(tradable -> tradable.getOffer() != null) - .forEach(tradable -> tradable.getOffer().setPriceFeedService(priceFeedService)); - completeHandler.run(); - }, - completeHandler); + synchronized (persisted.getList()) { + closedTradables.setAll(persisted.getList()); + closedTradables.stream() + .filter(tradable -> tradable.getOffer() != null) + .forEach(tradable -> tradable.getOffer().setPriceFeedService(priceFeedService)); + } + completeHandler.run(); + }, + completeHandler); } public void onAllServicesInitialized() { @@ -96,7 +98,7 @@ public class ClosedTradableManager implements PersistedDataHost { } public void add(Tradable tradable) { - synchronized (closedTradables) { + synchronized (closedTradables.getList()) { if (closedTradables.add(tradable)) { maybeClearSensitiveData(); requestPersistence(); @@ -105,7 +107,7 @@ public class ClosedTradableManager implements PersistedDataHost { } public void remove(Tradable tradable) { - synchronized (closedTradables) { + synchronized (closedTradables.getList()) { if (closedTradables.remove(tradable)) { requestPersistence(); } @@ -117,17 +119,17 @@ public class ClosedTradableManager implements PersistedDataHost { } public ObservableList getObservableList() { - synchronized (closedTradables) { - return closedTradables.getObservableList(); - } + return closedTradables.getObservableList(); } public List getTradableList() { - return ImmutableList.copyOf(new ArrayList<>(getObservableList())); + synchronized (closedTradables.getList()) { + return ImmutableList.copyOf(new ArrayList<>(getObservableList())); + } } public List getClosedTrades() { - synchronized (closedTradables) { + synchronized (closedTradables.getList()) { return ImmutableList.copyOf(getObservableList().stream() .filter(e -> e instanceof Trade) .map(e -> (Trade) e) @@ -136,7 +138,7 @@ public class ClosedTradableManager implements PersistedDataHost { } public List getCanceledOpenOffers() { - synchronized (closedTradables) { + synchronized (closedTradables.getList()) { return ImmutableList.copyOf(getObservableList().stream() .filter(e -> (e instanceof OpenOffer) && ((OpenOffer) e).getState().equals(CANCELED)) .map(e -> (OpenOffer) e) @@ -145,19 +147,19 @@ public class ClosedTradableManager implements PersistedDataHost { } public Optional getTradableById(String id) { - synchronized (closedTradables) { + synchronized (closedTradables.getList()) { return closedTradables.stream().filter(e -> e.getId().equals(id)).findFirst(); } } public Optional getTradeById(String id) { - synchronized (closedTradables) { + synchronized (closedTradables.getList()) { return getClosedTrades().stream().filter(e -> e.getId().equals(id)).findFirst(); } } public void maybeClearSensitiveData() { - synchronized (closedTradables) { + synchronized (closedTradables.getList()) { log.info("checking closed trades eligibility for having sensitive data cleared"); closedTradables.stream() .filter(e -> e instanceof Trade) @@ -170,11 +172,11 @@ public class ClosedTradableManager implements PersistedDataHost { public boolean canTradeHaveSensitiveDataCleared(String tradeId) { Instant safeDate = getSafeDateForSensitiveDataClearing(); - synchronized (closedTradables) { + synchronized (closedTradables.getList()) { return closedTradables.stream() - .filter(e -> e.getId().equals(tradeId)) - .filter(e -> e.getDate().toInstant().isBefore(safeDate)) - .count() > 0; + .filter(e -> e.getId().equals(tradeId)) + .filter(e -> e.getDate().toInstant().isBefore(safeDate)) + .count() > 0; } } @@ -205,9 +207,11 @@ public class ClosedTradableManager implements PersistedDataHost { } public BigInteger getTotalTradeFee(List tradableList) { - return BigInteger.valueOf(tradableList.stream() - .mapToLong(tradable -> getTradeFee(tradable).longValueExact()) - .sum()); + synchronized (tradableList) { + return BigInteger.valueOf(tradableList.stream() + .mapToLong(tradable -> getTradeFee(tradable).longValueExact()) + .sum()); + } } private BigInteger getTradeFee(Tradable tradable) { @@ -229,7 +233,7 @@ public class ClosedTradableManager implements PersistedDataHost { } public void removeTrade(Trade trade) { - synchronized (closedTradables) { + synchronized (closedTradables.getList()) { if (closedTradables.remove(trade)) { requestPersistence(); } diff --git a/core/src/main/java/haveno/core/trade/TradeManager.java b/core/src/main/java/haveno/core/trade/TradeManager.java index 975378ce11..1d75c8188e 100644 --- a/core/src/main/java/haveno/core/trade/TradeManager.java +++ b/core/src/main/java/haveno/core/trade/TradeManager.java @@ -269,13 +269,15 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi @Override public void readPersisted(Runnable completeHandler) { persistenceManager.readPersisted(persisted -> { - tradableList.setAll(persisted.getList()); - tradableList.stream() - .filter(trade -> trade.getOffer() != null) - .forEach(trade -> trade.getOffer().setPriceFeedService(priceFeedService)); - completeHandler.run(); - }, - completeHandler); + synchronized (persisted.getList()) { + tradableList.setAll(persisted.getList()); + tradableList.stream() + .filter(trade -> trade.getOffer() != null) + .forEach(trade -> trade.getOffer().setPriceFeedService(priceFeedService)); + } + completeHandler.run(); + }, + completeHandler); } @@ -992,7 +994,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi log.info("TradeManager.removeTrade() " + trade.getId()); // remove trade - synchronized (tradableList) { + synchronized (tradableList.getList()) { if (!tradableList.remove(trade)) return; } @@ -1036,18 +1038,20 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi private void updateTradePeriodState() { if (isShutDownStarted) return; - for (Trade trade : new ArrayList(tradableList.getList())) { - if (!trade.isPayoutPublished()) { - Date maxTradePeriodDate = trade.getMaxTradePeriodDate(); - Date halfTradePeriodDate = trade.getHalfTradePeriodDate(); - if (maxTradePeriodDate != null && halfTradePeriodDate != null) { - Date now = new Date(); - if (now.after(maxTradePeriodDate)) { - trade.setPeriodState(Trade.TradePeriodState.TRADE_PERIOD_OVER); - requestPersistence(); - } else if (now.after(halfTradePeriodDate)) { - trade.setPeriodState(Trade.TradePeriodState.SECOND_HALF); - requestPersistence(); + synchronized (tradableList.getList()) { + for (Trade trade : tradableList.getList()) { + if (!trade.isPayoutPublished()) { + Date maxTradePeriodDate = trade.getMaxTradePeriodDate(); + Date halfTradePeriodDate = trade.getHalfTradePeriodDate(); + if (maxTradePeriodDate != null && halfTradePeriodDate != null) { + Date now = new Date(); + if (now.after(maxTradePeriodDate)) { + trade.setPeriodState(Trade.TradePeriodState.TRADE_PERIOD_OVER); + requestPersistence(); + } else if (now.after(halfTradePeriodDate)) { + trade.setPeriodState(Trade.TradePeriodState.SECOND_HALF); + requestPersistence(); + } } } } @@ -1093,7 +1097,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } public Stream getTradesStreamWithFundsLockedIn() { - synchronized (tradableList) { + synchronized (tradableList.getList()) { return getObservableList().stream().filter(Trade::isFundsLockedIn); } } @@ -1108,7 +1112,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi public Set getSetOfFailedOrClosedTradeIdsFromLockedInFunds() throws TradeTxException { AtomicReference tradeTxException = new AtomicReference<>(); - synchronized (tradableList) { + synchronized (tradableList.getList()) { Set tradesIdSet = getTradesStreamWithFundsLockedIn() .filter(Trade::hasFailed) .map(Trade::getId) @@ -1170,7 +1174,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi initPersistedTrade(trade); UserThread.execute(() -> { - synchronized (tradableList) { + synchronized (tradableList.getList()) { if (!tradableList.contains(trade)) { tradableList.add(trade); } @@ -1241,7 +1245,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } public ObservableList getObservableList() { - synchronized (tradableList) { + synchronized (tradableList.getList()) { return tradableList.getObservableList(); } } @@ -1274,33 +1278,33 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } public Optional getOpenTrade(String tradeId) { - synchronized (tradableList) { + synchronized (tradableList.getList()) { return tradableList.stream().filter(e -> e.getId().equals(tradeId)).findFirst(); } } public boolean hasOpenTrade(Trade trade) { - synchronized (tradableList) { + synchronized (tradableList.getList()) { return tradableList.contains(trade); } } public boolean hasFailedScheduledTrade(String offerId) { - synchronized (failedTradesManager) { - return failedTradesManager.getTradeById(offerId).isPresent() && failedTradesManager.getTradeById(offerId).get().isProtocolErrorHandlingScheduled(); - } + return failedTradesManager.getTradeById(offerId).isPresent() && failedTradesManager.getTradeById(offerId).get().isProtocolErrorHandlingScheduled(); } public Optional getOpenTradeByUid(String tradeUid) { - synchronized (tradableList) { + synchronized (tradableList.getList()) { return tradableList.stream().filter(e -> e.getUid().equals(tradeUid)).findFirst(); } } public List getAllTrades() { - synchronized (tradableList) { + synchronized (tradableList.getList()) { List trades = new ArrayList(); - trades.addAll(tradableList.getList()); + synchronized (tradableList.getList()) { + trades.addAll(tradableList.getList()); + } trades.addAll(closedTradableManager.getClosedTrades()); trades.addAll(failedTradesManager.getObservableList()); return trades; @@ -1308,7 +1312,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } public List getOpenTrades() { - synchronized (tradableList) { + synchronized (tradableList.getList()) { return ImmutableList.copyOf(getObservableList().stream() .filter(e -> e instanceof Trade) .map(e -> e) @@ -1329,7 +1333,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } private void addTrade(Trade trade) { - synchronized (tradableList) { + synchronized (tradableList.getList()) { if (tradableList.add(trade)) { requestPersistence(); } diff --git a/core/src/main/java/haveno/core/trade/failed/FailedTradesManager.java b/core/src/main/java/haveno/core/trade/failed/FailedTradesManager.java index a99c28242e..a188e6638e 100644 --- a/core/src/main/java/haveno/core/trade/failed/FailedTradesManager.java +++ b/core/src/main/java/haveno/core/trade/failed/FailedTradesManager.java @@ -70,13 +70,15 @@ public class FailedTradesManager implements PersistedDataHost { @Override public void readPersisted(Runnable completeHandler) { persistenceManager.readPersisted(persisted -> { - failedTrades.setAll(persisted.getList()); - failedTrades.stream() - .filter(trade -> trade.getOffer() != null) - .forEach(trade -> trade.getOffer().setPriceFeedService(priceFeedService)); - completeHandler.run(); - }, - completeHandler); + synchronized (persisted.getList()) { + failedTrades.setAll(persisted.getList()); + failedTrades.stream() + .filter(trade -> trade.getOffer() != null) + .forEach(trade -> trade.getOffer().setPriceFeedService(priceFeedService)); + } + completeHandler.run(); + }, + completeHandler); } public void onAllServicesInitialized() { @@ -84,7 +86,7 @@ public class FailedTradesManager implements PersistedDataHost { } public void add(Trade trade) { - synchronized (failedTrades) { + synchronized (failedTrades.getList()) { if (failedTrades.add(trade)) { requestPersistence(); } @@ -92,7 +94,7 @@ public class FailedTradesManager implements PersistedDataHost { } public void removeTrade(Trade trade) { - synchronized (failedTrades) { + synchronized (failedTrades.getList()) { if (failedTrades.remove(trade)) { requestPersistence(); } @@ -104,26 +106,26 @@ public class FailedTradesManager implements PersistedDataHost { } public ObservableList getObservableList() { - synchronized (failedTrades) { + synchronized (failedTrades.getList()) { return failedTrades.getObservableList(); } } public Optional getTradeById(String id) { - synchronized (failedTrades) { + synchronized (failedTrades.getList()) { return failedTrades.stream().filter(e -> e.getId().equals(id)).findFirst(); } } public Stream getTradesStreamWithFundsLockedIn() { - synchronized (failedTrades) { + synchronized (failedTrades.getList()) { return failedTrades.stream() .filter(Trade::isFundsLockedIn); } } public void unFailTrade(Trade trade) { - synchronized (failedTrades) { + synchronized (failedTrades.getList()) { if (unFailTradeCallback == null) return; diff --git a/desktop/src/main/java/haveno/desktop/main/offer/signedoffer/SignedOffersDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/signedoffer/SignedOffersDataModel.java index 311cca3476..7c1cd7f539 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/signedoffer/SignedOffersDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/signedoffer/SignedOffersDataModel.java @@ -62,7 +62,9 @@ class SignedOffersDataModel extends ActivatableDataModel { private void applyList() { list.clear(); - list.addAll(openOfferManager.getObservableSignedOffersList().stream().map(SignedOfferListItem::new).collect(Collectors.toList())); + synchronized (openOfferManager.getObservableSignedOffersList()) { + list.addAll(openOfferManager.getObservableSignedOffersList().stream().map(SignedOfferListItem::new).collect(Collectors.toList())); + } // we sort by date, the earliest first list.sort((o1, o2) -> new Date(o2.getSignedOffer().getTimeStamp()).compareTo(new Date(o1.getSignedOffer().getTimeStamp()))); diff --git a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java index a5a0b86dfe..e825e29e6e 100644 --- a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java @@ -206,15 +206,17 @@ public class GUIUtil { persistenceManager.readPersisted(fileName, persisted -> { StringBuilder msg = new StringBuilder(); HashSet paymentAccounts = new HashSet<>(); - persisted.getList().forEach(paymentAccount -> { - String id = paymentAccount.getId(); - if (user.getPaymentAccount(id) == null) { - paymentAccounts.add(paymentAccount); - msg.append(Res.get("guiUtil.accountExport.tradingAccount", id)); - } else { - msg.append(Res.get("guiUtil.accountImport.noImport", id)); - } - }); + synchronized (persisted.getList()) { + persisted.getList().forEach(paymentAccount -> { + String id = paymentAccount.getId(); + if (user.getPaymentAccount(id) == null) { + paymentAccounts.add(paymentAccount); + msg.append(Res.get("guiUtil.accountExport.tradingAccount", id)); + } else { + msg.append(Res.get("guiUtil.accountImport.noImport", id)); + } + }); + } user.addImportedPaymentAccounts(paymentAccounts); new Popup().feedback(Res.get("guiUtil.accountImport.imported", path, msg)).show(); }, diff --git a/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageList.java b/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageList.java index 451d3e7e7f..e1e20225c4 100644 --- a/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageList.java +++ b/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageList.java @@ -48,12 +48,14 @@ public class MailboxMessageList extends PersistableList { @Override public Message toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder() - .setMailboxMessageList(protobuf.MailboxMessageList.newBuilder() - .addAllMailboxItem(getList().stream() - .map(MailboxItem::toProtoMessage) - .collect(Collectors.toList()))) - .build(); + synchronized (getList()) { + return protobuf.PersistableEnvelope.newBuilder() + .setMailboxMessageList(protobuf.MailboxMessageList.newBuilder() + .addAllMailboxItem(getList().stream() + .map(MailboxItem::toProtoMessage) + .collect(Collectors.toList()))) + .build(); + } } public static MailboxMessageList fromProto(protobuf.MailboxMessageList proto,