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 {
