This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch CAUSEWAY-3799
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit ad59abc1582ee0c0327175fb0f627decf28c094b
Author: Dan Haywood <[email protected]>
AuthorDate: Thu Jun 27 15:03:25 2024 +0100

    CAUSEWAY-3799: makes EntityPropertyChange implement Comparable and hashcode 
etc
    
    so that duplicates can be eliminated
---
 .../publishing/spi/EntityPropertyChange.java       | 82 ++++++++++++++++------
 .../EntityPropertyChangePublisherDefault.java      | 26 ++++---
 2 files changed, 79 insertions(+), 29 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/spi/EntityPropertyChange.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/spi/EntityPropertyChange.java
index 280a27c8fb..4c9486b00b 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/spi/EntityPropertyChange.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/spi/EntityPropertyChange.java
@@ -19,40 +19,82 @@
 package org.apache.causeway.applib.services.publishing.spi;
 
 import java.sql.Timestamp;
+import java.util.Comparator;
 import java.util.UUID;
 
 import org.apache.causeway.applib.services.bookmark.Bookmark;
 
-import lombok.Value;
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
 
 /**
  * Immutable data record for {@link EntityPropertyChangeSubscriber}s.
  *
  * @since 2.0 {@index}
  */
-@Value(staticConstructor = "of")
-public class EntityPropertyChange {
-
-    private final UUID interactionId;
-    private final int sequence;
-    private final Bookmark target;
-    private final String logicalMemberIdentifier;
-    private final String propertyId;
-    private final String preValue;
-    private final String postValue;
-    private final String username;
-    private final Timestamp timestamp;
+
+@EqualsAndHashCode(of = {"interactionId", "sequence", "targetStr", 
"propertyId"})
+public final class EntityPropertyChange implements 
Comparable<EntityPropertyChange> {
+
+    @Getter private final UUID interactionId;
+    @Getter private final int sequence;
+    @Getter private final Bookmark target;
+    @Getter private final String logicalMemberIdentifier;
+    @Getter private final String propertyId;
+    @Getter private final String preValue;
+    @Getter private final String postValue;
+    @Getter private final String username;
+    @Getter private final Timestamp timestamp;
+
+    @Getter(AccessLevel.PRIVATE) private final String targetStr;
+
+    public static EntityPropertyChange of(UUID interactionId, int sequence, 
Bookmark target, String logicalMemberIdentifier, String propertyId, String 
preValue, String postValue, String username, Timestamp timestamp) {
+        return new EntityPropertyChange(interactionId, sequence, target, 
logicalMemberIdentifier, propertyId, preValue, postValue, username, timestamp);
+    }
+
+    private EntityPropertyChange(
+            final UUID interactionId,
+            final int sequence,
+            final Bookmark target,
+            final String logicalMemberIdentifier,
+            final String propertyId,
+            final String preValue,
+            final String postValue,
+            final String username,
+            final Timestamp timestamp) {
+        this.interactionId = interactionId;
+        this.sequence = sequence;
+        this.target = target;
+        this.logicalMemberIdentifier = logicalMemberIdentifier;
+        this.propertyId = propertyId;
+        this.preValue = preValue;
+        this.postValue = postValue;
+        this.username = username;
+        this.timestamp = timestamp;
+
+        this.targetStr = target.toString();
+    }
 
     @Override
     public String toString() {
         return String.format("%s,%d: %s by %s, %s: %s -> %s",
-        getInteractionId(),
-        getSequence(),
-        getTarget().toString(),
-        getUsername(),
-        getPropertyId(),
-        getPreValue(),
-        getPostValue());
+                getInteractionId(),
+                getSequence(),
+                getTargetStr(),
+                getUsername(),
+                getPropertyId(),
+                getPreValue(),
+                getPostValue());
     }
 
+    @Override
+    public int compareTo(EntityPropertyChange o) {
+        return Comparator
+                .comparing(EntityPropertyChange::getInteractionId)
+                .thenComparing(EntityPropertyChange::getSequence)
+                .thenComparing(EntityPropertyChange::getTargetStr)
+                .thenComparing(EntityPropertyChange::getPropertyId)
+                .compare(this, o);
+    }
 }
diff --git 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
index a6a6d565ec..d9b1e9152b 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
@@ -18,7 +18,9 @@
  */
 package org.apache.causeway.core.runtimeservices.publish;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 import javax.annotation.PostConstruct;
@@ -49,6 +51,7 @@ import 
org.apache.causeway.core.transaction.changetracking.EntityPropertyChangeP
 
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
 import lombok.val;
 
 /**
@@ -60,7 +63,7 @@ import lombok.val;
 @Priority(PriorityPrecedence.EARLY)
 @Qualifier("Default")
 @RequiredArgsConstructor(onConstructor_ = {@Inject})
-//@Log4j2
+@Log4j2
 public class EntityPropertyChangePublisherDefault implements 
EntityPropertyChangePublisher {
 
     private final List<EntityPropertyChangeSubscriber> subscribers;
@@ -96,32 +99,37 @@ public class EntityPropertyChangePublisherDefault 
implements EntityPropertyChang
         val currentUser = userService.currentUserNameElseNobody();
         val currentTransactionId = 
transactionService.currentTransactionId().orElse(TransactionId.empty());
 
-        val propertyChanges = 
hasEnlistedEntityPropertyChanges().getPropertyChanges(
+        val enlistedPropertyChanges = 
hasEnlistedEntityPropertyChanges().getPropertyChanges(
                 currentTime,
                 currentUser,
-                currentTransactionId)
-                .toSet() // ensure uniqueness
+                currentTransactionId);
+        val duplicates = new ArrayList<EntityPropertyChange>();
+        val uniquePropertyChanges = enlistedPropertyChanges
+                .toSet(duplicates::add) // ensure uniqueness
                 .stream()
                 .collect(Can.toCan());
+        if(log.isWarnEnabled() && ! duplicates.isEmpty()) {
+            log.warn("Duplicate enlisted property changes discovered\n{}", 
duplicates);
+        }
 
         XrayUtil.SequenceHandle xrayHandle = null;
         try {
             xrayHandle = _Xray.enterEntityPropertyChangePublishing(
                     iaTracker,
-                    propertyChanges,
+                    uniquePropertyChanges,
                     enabledSubscribers,
-                    () -> getCannotPublishReason(propertyChanges)
+                    () -> getCannotPublishReason(uniquePropertyChanges)
             );
 
-            if (propertyChanges.size() <= 
causewayConfiguration.getCore().getRuntimeServices().getEntityPropertyChangePublisher().getBulk().getThreshold())
 {
-                propertyChanges.forEach(propertyChange -> {
+            if (uniquePropertyChanges.size() <= 
causewayConfiguration.getCore().getRuntimeServices().getEntityPropertyChangePublisher().getBulk().getThreshold())
 {
+                uniquePropertyChanges.forEach(propertyChange -> {
                     for (val subscriber : enabledSubscribers) {
                         subscriber.onChanging(propertyChange);
                     }
                 });
             } else {
                 for (val subscriber : enabledSubscribers) {
-                    subscriber.onChanging(propertyChanges);
+                    subscriber.onChanging(uniquePropertyChanges);
                 }
             }
         } finally {

Reply via email to