fix concurrent modification exception in disputes list

This commit is contained in:
woodser 2024-08-05 12:09:17 -04:00
parent d9a3feba8d
commit 3dfaa2fc52
7 changed files with 80 additions and 58 deletions

View file

@ -275,11 +275,13 @@ public class CoreDisputesService {
disputeResult.summaryNotesProperty().get()
);
synchronized (dispute.getChatMessages()) {
if (reason == DisputeResult.Reason.OPTION_TRADE &&
dispute.getChatMessages().size() > 1 &&
dispute.getChatMessages().get(1).isSystemMessage()) {
textToSign += "\n" + dispute.getChatMessages().get(1).getMessage() + "\n";
}
}
String summaryText = DisputeSummaryVerification.signAndApply(disputeManager, disputeResult, textToSign);

View file

@ -114,6 +114,7 @@ public class DisputeMsgEvents {
// We check at every new message if it might be a message sent after the dispute had been closed. If that is the
// case we revert the isClosed flag so that the UI can reopen the dispute and indicate that a new dispute
// message arrived.
synchronized (dispute.getChatMessages()) {
ObservableList<ChatMessage> chatMessages = dispute.getChatMessages();
// If last message is not a result message we re-open as we might have received a new message from the
// trader/mediator/arbitrator who has reopened the case
@ -127,3 +128,4 @@ public class DisputeMsgEvents {
}
}
}
}

View file

@ -192,6 +192,7 @@ public abstract class SupportManager {
if (ackMessage.getSourceMsgClassName().equals(ChatMessage.class.getSimpleName())) {
Trade trade = tradeManager.getTrade(ackMessage.getSourceId());
for (Dispute dispute : trade.getDisputes()) {
synchronized (dispute.getChatMessages()) {
for (ChatMessage chatMessage : dispute.getChatMessages()) {
if (chatMessage.getUid().equals(ackMessage.getSourceUid())) {
if (trade.getDisputeState() == Trade.DisputeState.DISPUTE_REQUESTED) {
@ -204,6 +205,7 @@ public abstract class SupportManager {
}
}
}
}
} else {
log.warn("Received AckMessage with error state for {} with tradeId={}, sender={}, errorMessage={}",
ackMessage.getSourceMsgClassName(), ackMessage.getSourceId(), ackMessage.getSenderNodeAddress(), ackMessage.getErrorMessage());
@ -212,6 +214,7 @@ public abstract class SupportManager {
if (ackMessage.getSourceMsgClassName().equals(ChatMessage.class.getSimpleName())) {
Trade trade = tradeManager.getTrade(ackMessage.getSourceId());
for (Dispute dispute : trade.getDisputes()) {
synchronized (dispute.getChatMessages()) {
for (ChatMessage chatMessage : dispute.getChatMessages()) {
if (chatMessage.getUid().equals(ackMessage.getSourceUid())) {
if (trade.getDisputeState().isCloseRequested()) {
@ -224,6 +227,7 @@ public abstract class SupportManager {
}
}
}
}
getAllChatMessages(ackMessage.getSourceId()).stream()
.filter(msg -> msg.getUid().equals(ackMessage.getSourceUid()))

View file

@ -353,18 +353,21 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
///////////////////////////////////////////////////////////////////////////////////////////
public void addAndPersistChatMessage(ChatMessage chatMessage) {
synchronized (chatMessages) {
if (!chatMessages.contains(chatMessage)) {
chatMessages.add(chatMessage);
} else {
log.error("disputeDirectMessage already exists");
}
}
}
public boolean isMediationDispute() {
return !chatMessages.isEmpty() && chatMessages.get(0).getSupportType() == SupportType.MEDIATION;
}
public boolean removeAllChatMessages() {
synchronized (chatMessages) {
if (chatMessages.size() > 1) {
// removes all chat except the initial guidelines message.
String firstMessageUid = chatMessages.get(0).getUid();
@ -373,6 +376,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
}
return false;
}
}
public void maybeClearSensitiveData() {
String change = "";

View file

@ -90,6 +90,7 @@ public abstract class DisputeListService<T extends DisputeList<Dispute>> impleme
///////////////////////////////////////////////////////////////////////////////////////////
public void cleanupDisputes(@Nullable Consumer<String> closedDisputeHandler) {
synchronized (disputeList.getObservableList()) {
disputeList.stream().forEach(dispute -> {
String tradeId = dispute.getTradeId();
if (dispute.isClosed() && closedDisputeHandler != null) {
@ -97,6 +98,7 @@ public abstract class DisputeListService<T extends DisputeList<Dispute>> impleme
}
});
}
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -130,8 +132,10 @@ public abstract class DisputeListService<T extends DisputeList<Dispute>> impleme
}
ObservableList<Dispute> getObservableList() {
synchronized (disputeList.getObservableList()) {
return disputeList.getObservableList();
}
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -151,10 +155,12 @@ public abstract class DisputeListService<T extends DisputeList<Dispute>> impleme
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());

View file

@ -187,11 +187,13 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
@Override
public List<ChatMessage> getAllChatMessages(String tradeId) {
synchronized (getDisputeList().getObservableList()) {
return getDisputeList().stream()
.filter(dispute -> dispute.getTradeId().equals(tradeId))
.flatMap(dispute -> dispute.getChatMessages().stream())
.collect(Collectors.toList());
}
}
@Override
public boolean channelOpen(ChatMessage message) {
@ -239,7 +241,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
}
public ObservableList<Dispute> getDisputesAsObservableList() {
synchronized(disputeListService.getDisputeList()) {
synchronized(disputeListService.getDisputeList().getObservableList()) {
return disputeListService.getObservableList();
}
}
@ -249,7 +251,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
}
protected T getDisputeList() {
synchronized(disputeListService.getDisputeList()) {
synchronized(disputeListService.getDisputeList().getObservableList()) {
return disputeListService.getDisputeList();
}
}
@ -354,7 +356,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
return;
}
synchronized (disputeList) {
synchronized (disputeList.getObservableList()) {
if (disputeList.contains(dispute)) {
String msg = "We got a dispute msg that we have already stored. TradeId = " + dispute.getTradeId() + ", DisputeId = " + dispute.getId();
log.warn(msg);

View file

@ -287,11 +287,13 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
// set dispute state
cleanupRetryMap(uid);
synchronized (dispute.getChatMessages()) {
if (!dispute.getChatMessages().contains(chatMessage)) {
dispute.addAndPersistChatMessage(chatMessage);
} else {
log.warn("We got a dispute mail msg that we have already stored. TradeId = " + chatMessage.getTradeId());
}
}
dispute.setIsClosed();
if (dispute.disputeResultProperty().get() != null) {
log.info("We already got a dispute result, indicating the message was resent after updating multisig info. TradeId = " + tradeId);