You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/07/26 15:35:16 UTC

[isis] branch master updated: ISIS-2573: some housekeeping around ObjectAssociationContainer and EntityChangeTrackerDefault

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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 11bb58c  ISIS-2573: some housekeeping around ObjectAssociationContainer and EntityChangeTrackerDefault
11bb58c is described below

commit 11bb58cd2f8752bd56a597177b5d296b796d7c19
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Jul 26 17:35:04 2021 +0200

    ISIS-2573: some housekeeping around ObjectAssociationContainer and
    EntityChangeTrackerDefault
---
 .../spec/feature/ObjectAssociationContainer.java   | 22 ++++--
 .../runtime/events/TransactionEventEmitter.java    |  2 +-
 .../changetracking/EntityChangeTrackerDefault.java | 91 ++++++++++++----------
 .../publishing/PropertyPublishingTestAbstract.java |  2 +-
 .../publishing/jpa/JpaExecutionPublishingTest.java |  2 +
 5 files changed, 70 insertions(+), 49 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociationContainer.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociationContainer.java
index 33ff0f9..679aea5 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociationContainer.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociationContainer.java
@@ -38,22 +38,30 @@ public interface ObjectAssociationContainer {
      */
     Optional<ObjectAssociation> getAssociation(String id);
 
-    default ObjectAssociation getAssociationElseFail(String id) {
+    default ObjectAssociation getAssociationElseFail(final String id) {
         return getAssociation(id)
                 .orElseThrow(()->_Exceptions.noSuchElement("id=%s", id));
     }
 
-    default OneToOneAssociation getPropertyElseFail(String id) {
+    default Optional<OneToOneAssociation> getProperty(final String id) {
         return getAssociation(id)
                 .filter(ObjectAssociation.Predicates.PROPERTIES)
-                .map(OneToOneAssociation.class::cast)
+                .map(OneToOneAssociation.class::cast);
+    }
+
+    default OneToOneAssociation getPropertyElseFail(final String id) {
+        return getProperty(id)
                 .orElseThrow(()->_Exceptions.noSuchElement("id=%s", id));
     }
 
-    default OneToManyAssociation getCollectionElseFail(String id) {
+    default Optional<OneToManyAssociation> getCollection(final String id) {
         return getAssociation(id)
                 .filter(ObjectAssociation.Predicates.COLLECTIONS)
-                .map(OneToManyAssociation.class::cast)
+                .map(OneToManyAssociation.class::cast);
+    }
+
+    default OneToManyAssociation getCollectionElseFail(final String id) {
+        return getCollection(id)
                 .orElseThrow(()->_Exceptions.noSuchElement("id=%s", id));
     }
 
@@ -85,7 +93,7 @@ public interface ObjectAssociationContainer {
      * All {@link ObjectAssociation association}s that represent
      * {@link OneToOneAssociation properties}.
      */
-    default Stream<OneToOneAssociation> streamProperties(MixedIn contributed) {
+    default Stream<OneToOneAssociation> streamProperties(final MixedIn contributed) {
         return streamAssociations(contributed)
                 .filter(ObjectAssociation.Predicates.PROPERTIES)
                 .map(OneToOneAssociation.class::cast);
@@ -95,7 +103,7 @@ public interface ObjectAssociationContainer {
      * All {@link ObjectAssociation association}s that represents
      * {@link OneToManyAssociation collections}.
      */
-    default Stream<OneToManyAssociation> streamCollections(MixedIn contributed){
+    default Stream<OneToManyAssociation> streamCollections(final MixedIn contributed){
         return streamAssociations(contributed)
                 .filter(ObjectAssociation.Predicates.COLLECTIONS)
                 .map(OneToManyAssociation.class::cast);
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/events/TransactionEventEmitter.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/events/TransactionEventEmitter.java
index a4bd50c..89e246c 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/events/TransactionEventEmitter.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/events/TransactionEventEmitter.java
@@ -49,7 +49,7 @@ implements TransactionSynchronization, TransactionBoundaryAware {
     }
 
     @Override
-    public void afterCompletion(int status) {
+    public void afterCompletion(final int status) {
         val event = TransactionAfterCompletionEvent.forStatus(status);
         eventBusService.post(event);
         _Xray.txAfterCompletion(interactionLayerTracker, String.format("tx: afterCompletion (%s)", event.name()));
diff --git a/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTrackerDefault.java b/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTrackerDefault.java
index d94b3b8..db80a68 100644
--- a/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTrackerDefault.java
+++ b/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTrackerDefault.java
@@ -73,16 +73,14 @@ import org.apache.isis.core.metamodel.facets.object.publish.entitychange.EntityC
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.MixedIn;
-import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
-import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.core.transaction.changetracking.events.IsisTransactionPlaceholder;
 import org.apache.isis.core.transaction.events.TransactionBeforeCompletionEvent;
 
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.NonNull;
-import lombok.extern.log4j.Log4j2;
 import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
 /**
  * @since 2.0 {@index}
@@ -122,7 +120,7 @@ implements
     private final Provider<InteractionProvider> interactionProviderProvider;
 
     @Inject
-    public EntityChangeTrackerDefault(EntityPropertyChangePublisher entityPropertyChangePublisher, EntityChangesPublisher entityChangesPublisher, EventBusService eventBusService, Provider<InteractionProvider> interactionProviderProvider) {
+    public EntityChangeTrackerDefault(final EntityPropertyChangePublisher entityPropertyChangePublisher, final EntityChangesPublisher entityChangesPublisher, final EventBusService eventBusService, final Provider<InteractionProvider> interactionProviderProvider) {
         this.entityPropertyChangePublisher = entityPropertyChangePublisher;
         this.entityChangesPublisher = entityChangesPublisher;
         this.eventBusService = eventBusService;
@@ -154,35 +152,21 @@ implements
     }
 
     private void enlistUpdatingInternal(
-            final @NonNull ManagedObject adapter,
-            String propertyIdIfAny,
-            Object preValue) {
-        if(!isEntityEnabledForChangePublishing(adapter)) {
+            final @NonNull ManagedObject entity,
+            final String propertyIdIfAny,
+            final Object preValue) {
+        if(!isEntityEnabledForChangePublishing(entity)) {
             return;
         }
-        enlistForChangeKindPublishing(adapter, EntityChangeKind.UPDATE);
+        enlistForChangeKindPublishing(entity, EntityChangeKind.UPDATE);
 
         if(propertyIdIfAny != null) {
-            // if we've been provided with the preValue, then just save it
-            // in the appropriate PropertyChangeRecord
-
-            if(propertyChangeRecordsById.containsKey(propertyIdIfAny)) {
-                return;
-            }
-            adapter.getSpecification().getAssociation(propertyIdIfAny)
-                .filter(assoc -> !assoc.isMixedIn())
-                .filter(ObjectMember::isOneToOneAssociation)
-                .map(OneToOneAssociation.class::cast)
-                .filter(property->!property.isNotPersisted())
-                .map(property->_PropertyChangeRecord.of(adapter, property))
-                .ifPresent(record -> {
-                    record.setPreValue(preValue);
-                    propertyChangeRecordsById.put(propertyIdIfAny, record);
-                });
-
+            // if we've been provided with the property that changed and its pre-value,
+            // then just save it in the appropriate PropertyChangeRecord
+            enlistForPreAndPostValuePublishing(entity, propertyIdIfAny, preValue);
         } else {
-            // read from the pojo using the Isis MM.
-            enlistForPreAndPostValuePublishing(adapter, _PropertyChangeRecord::updatePreValue);
+            // read all persistent property pre-values from the pojo using the Isis MM.
+            enlistForPreAndPostValuePublishing(entity, _PropertyChangeRecord::updatePreValue);
         }
     }
 
@@ -224,7 +208,7 @@ implements
      * @apiNote intended to be called during before transaction completion by the framework internally
      */
     @EventListener(value = TransactionBeforeCompletionEvent.class) @Order(PriorityPrecedence.LATE)
-    public void onTransactionCompleting(TransactionBeforeCompletionEvent event) {
+    public void onTransactionCompleting(final TransactionBeforeCompletionEvent event) {
         try {
             doPublish();
         } finally {
@@ -324,9 +308,36 @@ implements
         return previousChangeKind == null;
     }
 
+
+    /**
+     * Variant of {@link #enlistForPreAndPostValuePublishing(ManagedObject, Consumer)},
+     * where we know the changed property and its pre-value in advance (JPA)
+     */
+    private void enlistForPreAndPostValuePublishing(
+            final ManagedObject entity,
+            final String propertyId,
+            final Object preValue) {
+
+        if(propertyChangeRecordsById.containsKey(propertyId)) {
+            return;
+        }
+
+        log.debug("enlist entity's single property change ({}) for publishing {}", propertyId, entity);
+
+        entity.getSpecification().getProperty(propertyId)
+        .filter(property->!property.isMixedIn())
+        .filter(property->!property.isNotPersisted())
+        .map(property->_PropertyChangeRecord.of(entity, property))
+        .ifPresent(record -> {
+            record.setPreValue(preValue);
+            propertyChangeRecordsById.put(record.getPropertyId(), record);
+        });
+
+    }
+
     private void enlistForPreAndPostValuePublishing(
             final ManagedObject entity,
-            final Consumer<_PropertyChangeRecord> fun) {
+            final Consumer<_PropertyChangeRecord> onNewChangeRecord) {
 
         log.debug("enlist entity's property changes for publishing {}", entity);
 
@@ -335,7 +346,7 @@ implements
         .map(property->_PropertyChangeRecord.of(entity, property))
         .filter(record->!propertyChangeRecordsById.containsKey(record.getPropertyId())) // already enlisted, so ignore
         .forEach(record->{
-            fun.accept(record);
+            onNewChangeRecord.accept(record);
             propertyChangeRecordsById.put(record.getPropertyId(), record);
         });
     }
@@ -378,7 +389,7 @@ implements
     // -- ENTITY CHANGE TRACKING
 
     @Override
-    public void enlistCreated(ManagedObject entity) {
+    public void enlistCreated(final ManagedObject entity) {
         _Xray.enlistCreated(entity, interactionProviderProvider);
         val hasAlreadyBeenEnlisted = isEnlisted(entity);
         enlistCreatedInternal(entity);
@@ -390,7 +401,7 @@ implements
     }
 
     @Override
-    public void enlistDeleting(ManagedObject entity) {
+    public void enlistDeleting(final ManagedObject entity) {
         _Xray.enlistDeleting(entity, interactionProviderProvider);
         enlistDeletingInternal(entity);
         CallbackFacet.callCallback(entity, RemovingCallbackFacet.class);
@@ -398,12 +409,12 @@ implements
     }
 
     @Override
-    public void enlistUpdating(ManagedObject entity) {
+    public void enlistUpdating(final ManagedObject entity) {
         enlistUpdating(entity, null, null);
     }
 
     @Override
-    public void enlistUpdating(ManagedObject entity, String propertyIdIfAny, Object preValue) {
+    public void enlistUpdating(final ManagedObject entity, final String propertyIdIfAny, final Object preValue) {
         _Xray.enlistUpdating(entity, interactionProviderProvider);
         val hasAlreadyBeenEnlisted = isEnlisted(entity);
         // we call this come what may;
@@ -418,7 +429,7 @@ implements
     }
 
     @Override
-    public void recognizeLoaded(ManagedObject entity) {
+    public void recognizeLoaded(final ManagedObject entity) {
         _Xray.recognizeLoaded(entity, interactionProviderProvider);
         CallbackFacet.callCallback(entity, LoadedCallbackFacet.class);
         postLifecycleEventIfRequired(entity, LoadedLifecycleEventFacet.class);
@@ -426,14 +437,14 @@ implements
     }
 
     @Override
-    public void recognizePersisting(ManagedObject entity) {
+    public void recognizePersisting(final ManagedObject entity) {
         _Xray.recognizePersisting(entity, interactionProviderProvider);
         CallbackFacet.callCallback(entity, PersistingCallbackFacet.class);
         postLifecycleEventIfRequired(entity, PersistingLifecycleEventFacet.class);
     }
 
     @Override
-    public void recognizeUpdating(ManagedObject entity) {
+    public void recognizeUpdating(final ManagedObject entity) {
         _Xray.recognizeUpdating(entity, interactionProviderProvider);
         CallbackFacet.callCallback(entity, UpdatedCallbackFacet.class);
         postLifecycleEventIfRequired(entity, UpdatedLifecycleEventFacet.class);
@@ -447,8 +458,8 @@ implements
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
     private void postLifecycleEventIfRequired(
-            ManagedObject adapter,
-            Class<? extends LifecycleEventFacet> lifecycleEventFacetClass) {
+            final ManagedObject adapter,
+            final Class<? extends LifecycleEventFacet> lifecycleEventFacetClass) {
 
         val lifecycleEventFacet = adapter.getSpecification().getFacet(lifecycleEventFacetClass);
         if(lifecycleEventFacet == null) {
diff --git a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/publishing/PropertyPublishingTestAbstract.java b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/publishing/PropertyPublishingTestAbstract.java
index 65d1189..0924cb2 100644
--- a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/publishing/PropertyPublishingTestAbstract.java
+++ b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/publishing/PropertyPublishingTestAbstract.java
@@ -49,7 +49,7 @@ implements HasPersistenceStandard {
             break;
         case POST_COMMIT:
             assertHasPropertyChangeEntries(Can.of(
-                    formatPersistenceStandardSpecificCapitalize("%s Product/name: 'Sample Book' -> 'Book #2'")));
+                    formatPersistenceStandardSpecificCapitalize("%s Book/name: 'Sample Book' -> 'Book #2'")));
             break;
         default:
             // if hitting this, the caller is requesting a verification stage, we are providing no case for
diff --git a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/publishing/jpa/JpaExecutionPublishingTest.java b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/publishing/jpa/JpaExecutionPublishingTest.java
index 0bacee6..92e2ddc 100644
--- a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/publishing/jpa/JpaExecutionPublishingTest.java
+++ b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/publishing/jpa/JpaExecutionPublishingTest.java
@@ -43,6 +43,8 @@ import org.apache.isis.testdomain.publishing.conf.Configuration_usingExecutionPu
         },
         properties = {
                 "logging.level.org.apache.isis.applib.services.publishing.log.ExecutionLogger=DEBUG",
+                "logging.level.org.apache.isis.persistence.jpa.applib.integration.IsisEntityListener=DEBUG",
+                "logging.level.org.apache.isis.core.transaction.changetracking.EntityChangeTrackerDefault=DEBUG",
                 "logging.level.org.apache.isis.core.runtimeservices.session.IsisInteractionFactoryDefault=DEBUG",
         })
 @TestPropertySource({