diff --git a/core/src/main/java/org/dcsa/conformance/core/party/ActionPromptsQueue.java b/core/src/main/java/org/dcsa/conformance/core/party/ActionPromptsQueue.java index 8c80a6c3..5aca0d6b 100644 --- a/core/src/main/java/org/dcsa/conformance/core/party/ActionPromptsQueue.java +++ b/core/src/main/java/org/dcsa/conformance/core/party/ActionPromptsQueue.java @@ -5,22 +5,22 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.HashSet; import java.util.LinkedList; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.StreamSupport; import org.dcsa.conformance.core.state.StatefulEntity; public class ActionPromptsQueue implements StatefulEntity { - private final Set allActionIds = new HashSet<>(); + private final Set allActionIds = ConcurrentHashMap.newKeySet(); private final LinkedList pendingActions = new LinkedList<>(); - synchronized void clear() { + void clear() { allActionIds.clear(); pendingActions.clear(); } - synchronized void addLast(JsonNode actionPrompt) { + void addLast(JsonNode actionPrompt) { String actionId = actionPrompt.get("actionId").asText(); if (!allActionIds.contains(actionId)) { allActionIds.add(actionId); @@ -28,11 +28,11 @@ synchronized void addLast(JsonNode actionPrompt) { } } - synchronized boolean isEmpty() { + boolean isEmpty() { return pendingActions.isEmpty(); } - synchronized JsonNode removeFirst() { + JsonNode removeFirst() { if (pendingActions.isEmpty()) { return null; } @@ -42,7 +42,7 @@ synchronized JsonNode removeFirst() { } @Override - public synchronized JsonNode exportJsonState() { + public JsonNode exportJsonState() { ObjectNode stateNode = OBJECT_MAPPER.createObjectNode(); ArrayNode allActionIdsNode = stateNode.putArray("allActionIds"); @@ -56,7 +56,7 @@ public synchronized JsonNode exportJsonState() { } @Override - public synchronized void importJsonState(JsonNode jsonState) { + public void importJsonState(JsonNode jsonState) { StreamSupport.stream(jsonState.get("allActionIds").spliterator(), false) .map(JsonNode::asText) .forEach(allActionIds::add); diff --git a/core/src/main/java/org/dcsa/conformance/core/state/MemorySortedPartitionsLockingMap.java b/core/src/main/java/org/dcsa/conformance/core/state/MemorySortedPartitionsLockingMap.java index 3c177d50..cca6c9e4 100644 --- a/core/src/main/java/org/dcsa/conformance/core/state/MemorySortedPartitionsLockingMap.java +++ b/core/src/main/java/org/dcsa/conformance/core/state/MemorySortedPartitionsLockingMap.java @@ -1,11 +1,11 @@ package org.dcsa.conformance.core.state; import com.fasterxml.jackson.databind.JsonNode; -import lombok.extern.slf4j.Slf4j; - -import java.util.HashMap; +import java.util.Map; import java.util.Objects; -import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; +import lombok.extern.slf4j.Slf4j; @Slf4j public class MemorySortedPartitionsLockingMap extends SortedPartitionsLockingMap { @@ -16,22 +16,21 @@ private static class MemoryMapItem { JsonNode value; } - private final HashMap> memoryMap = new HashMap<>(); + private final Map> memoryMap = new ConcurrentHashMap<>(); public MemorySortedPartitionsLockingMap() { super(60L * 1000L, 100L, 60L * 1000L); } - private MemoryMapItem _getOrCreateItem(String partitionKey, String sortKey) { + private synchronized MemoryMapItem getOrCreateItem(String partitionKey, String sortKey) { return memoryMap - .computeIfAbsent(partitionKey, ignoredKey -> new TreeMap<>()) + .computeIfAbsent(partitionKey, ignoredKey -> new ConcurrentSkipListMap<>()) .computeIfAbsent(sortKey, ignoredKey -> new MemoryMapItem()); } @Override protected void _saveItem(String lockedBy, String partitionKey, String sortKey, JsonNode value) { - synchronized (memoryMap) { - MemoryMapItem item = _getOrCreateItem(partitionKey, sortKey); + MemoryMapItem item = getOrCreateItem(partitionKey, sortKey); if (Objects.equals(lockedBy, item.lockedBy)) { if (item.lockedUntil > System.currentTimeMillis()) { item.value = value; @@ -42,39 +41,34 @@ protected void _saveItem(String lockedBy, String partitionKey, String sortKey, J } else { throw new RuntimeException("%s cannot save: item is locked by %s".formatted(lockedBy, item.lockedBy)); } - } } @Override protected JsonNode _loadItem(String lockedBy, String partitionKey, String sortKey) throws TemporaryLockingMapException { - synchronized (memoryMap) { - MemoryMapItem item = _getOrCreateItem(partitionKey, sortKey); + MemoryMapItem item = getOrCreateItem(partitionKey, sortKey); long currentTime = System.currentTimeMillis(); if (item.lockedBy != null && item.lockedUntil > currentTime) { - log.debug("%s cannot load: must wait for %s to save".formatted(lockedBy, item.lockedBy)); + log.debug("{} cannot load: must wait for {} to save", lockedBy, item.lockedBy); throw new TemporaryLockingMapException(null); } else { item.lockedBy = lockedBy; item.lockedUntil = currentTime + lockDurationMillis; } return item.value; - } } @Override protected void _unlockItem(String lockedBy, String partitionKey, String sortKey) { - synchronized (memoryMap) { - MemoryMapItem item = _getOrCreateItem(partitionKey, sortKey); + MemoryMapItem item = getOrCreateItem(partitionKey, sortKey); if (Objects.equals(lockedBy, item.lockedBy)) { if (item.lockedUntil > System.currentTimeMillis()) { item.lockedBy = null; } else { - log.debug("%s does not need to unlock: lock has expired".formatted(lockedBy)); + log.debug("{} does not need to unlock: lock has expired", lockedBy); } } else { throw new RuntimeException("%s cannot unlock: item is locked by %s".formatted(lockedBy, item.lockedBy)); } } - } } diff --git a/core/src/main/java/org/dcsa/conformance/core/state/MemorySortedPartitionsNonLockingMap.java b/core/src/main/java/org/dcsa/conformance/core/state/MemorySortedPartitionsNonLockingMap.java index 589fa585..736a7caf 100644 --- a/core/src/main/java/org/dcsa/conformance/core/state/MemorySortedPartitionsNonLockingMap.java +++ b/core/src/main/java/org/dcsa/conformance/core/state/MemorySortedPartitionsNonLockingMap.java @@ -1,51 +1,53 @@ package org.dcsa.conformance.core.state; -import com.fasterxml.jackson.databind.JsonNode; -import lombok.SneakyThrows; +import static org.dcsa.conformance.core.toolkit.JsonToolkit.OBJECT_MAPPER; -import java.util.HashMap; +import com.fasterxml.jackson.databind.JsonNode; import java.util.List; -import java.util.TreeMap; - -import static org.dcsa.conformance.core.toolkit.JsonToolkit.OBJECT_MAPPER; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; +import lombok.SneakyThrows; public class MemorySortedPartitionsNonLockingMap implements SortedPartitionsNonLockingMap { - private final HashMap> memoryMap = new HashMap<>(); - + private static final String VALUE = "value"; + private final ConcurrentHashMap> memoryMap = new ConcurrentHashMap<>(); @SneakyThrows @Override public synchronized void setItemValue(String partitionKey, String sortKey, JsonNode value) { JsonNode valueCopy = OBJECT_MAPPER.readTree(value.toString()); memoryMap - .computeIfAbsent(partitionKey, ignoredKey -> new TreeMap<>()) - .put(sortKey, OBJECT_MAPPER.createObjectNode().set("value", valueCopy)); + .computeIfAbsent(partitionKey, ignoredKey -> new ConcurrentSkipListMap<>()) + .put(sortKey, OBJECT_MAPPER.createObjectNode().set(VALUE, valueCopy)); } @Override - public synchronized JsonNode getItemValue(String partitionKey, String sortKey) { - return memoryMap - .getOrDefault(partitionKey, new TreeMap<>()) + public JsonNode getItemValue(String partitionKey, String sortKey) { + return getPartitionOrDefault(partitionKey) .getOrDefault(sortKey, OBJECT_MAPPER.createObjectNode()) - .get("value"); + .get(VALUE); + } + + private synchronized ConcurrentSkipListMap getPartitionOrDefault(String partitionKey) { + return memoryMap.getOrDefault(partitionKey, new ConcurrentSkipListMap<>()); } @Override - public synchronized JsonNode getFirstItemValue(String partitionKey) { - TreeMap partition = memoryMap.getOrDefault(partitionKey, new TreeMap<>()); - return partition.isEmpty() ? null : partition.get(partition.firstKey()).get("value"); + public JsonNode getFirstItemValue(String partitionKey) { + ConcurrentSkipListMap partition = getPartitionOrDefault(partitionKey); + return partition.isEmpty() ? null : partition.get(partition.firstKey()).get(VALUE); } @Override - public synchronized JsonNode getLastItemValue(String partitionKey) { - TreeMap partition = memoryMap.getOrDefault(partitionKey, new TreeMap<>()); - return partition.isEmpty() ? null : partition.get(partition.lastKey()).get("value"); + public JsonNode getLastItemValue(String partitionKey) { + ConcurrentSkipListMap partition = getPartitionOrDefault(partitionKey); + return partition.isEmpty() ? null : partition.get(partition.lastKey()).get(VALUE); } @Override - public synchronized List getPartitionValues(String partitionKey) { - return memoryMap.getOrDefault(partitionKey, new TreeMap<>()).values().stream() - .map(item -> item.get("value")) + public List getPartitionValues(String partitionKey) { + return getPartitionOrDefault(partitionKey).values().stream() + .map(item -> item.get(VALUE)) .toList(); } } diff --git a/ebl-surrender/src/main/java/org/dcsa/conformance/standards/eblsurrender/action/SurrenderRequestAction.java b/ebl-surrender/src/main/java/org/dcsa/conformance/standards/eblsurrender/action/SurrenderRequestAction.java index f3a3e1f6..cc95e783 100644 --- a/ebl-surrender/src/main/java/org/dcsa/conformance/standards/eblsurrender/action/SurrenderRequestAction.java +++ b/ebl-surrender/src/main/java/org/dcsa/conformance/standards/eblsurrender/action/SurrenderRequestAction.java @@ -71,7 +71,7 @@ public String getHumanReadablePrompt() { } @Override - public synchronized Supplier getSrrSupplier() { + public Supplier getSrrSupplier() { return srrSupplier; } diff --git a/ebl-surrender/src/main/java/org/dcsa/conformance/standards/eblsurrender/action/SurrenderResponseAction.java b/ebl-surrender/src/main/java/org/dcsa/conformance/standards/eblsurrender/action/SurrenderResponseAction.java index d03de71d..e9aaa643 100644 --- a/ebl-surrender/src/main/java/org/dcsa/conformance/standards/eblsurrender/action/SurrenderResponseAction.java +++ b/ebl-surrender/src/main/java/org/dcsa/conformance/standards/eblsurrender/action/SurrenderResponseAction.java @@ -1,9 +1,7 @@ package org.dcsa.conformance.standards.eblsurrender.action; import com.fasterxml.jackson.core.JsonPointer; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; - import java.util.Objects; import java.util.function.Supplier; import java.util.stream.Stream; @@ -36,16 +34,6 @@ public SurrenderResponseAction( this.requestSchemaValidator = requestSchemaValidator; } - @Override - public ObjectNode exportJsonState() { - return super.exportJsonState(); - } - - @Override - public void importJsonState(JsonNode jsonState) { - super.importJsonState(jsonState); - } - @Override public String getHumanReadablePrompt() { return ("%s the surrender request with surrender request reference '%s'") @@ -53,7 +41,7 @@ public String getHumanReadablePrompt() { } @Override - public synchronized Supplier getSrrSupplier() { + public Supplier getSrrSupplier() { if (srrSupplier != null) return srrSupplier; for (ConformanceAction action = this.previousAction; action != null;