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

danhaywood pushed a commit to branch ISIS-3110
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/ISIS-3110 by this push:
     new d3ce224d00 ISIS-3110: moves the subscriber for 
EntityTrackerChangeDefault...
d3ce224d00 is described below

commit d3ce224d0096e2a9a6f009c01a460c8e8d48daba
Author: Dan Haywood <[email protected]>
AuthorDate: Thu Aug 4 09:30:13 2022 +0100

    ISIS-3110: moves the subscriber for EntityTrackerChangeDefault...
    
    ... into a singleton that can then actively check that there is an 
interaction in scope
---
 ...ctionInvocationFacetForDomainEventAbstract.java |  8 +--
 .../autocomplete/AutoCompleteFacetAbstract.java    |  4 +-
 .../services/publishing/ExecutionPublisher.java    |  4 +-
 .../specimpl/OneToManyAssociationMixedIn.java      |  4 +-
 .../specimpl/OneToOneAssociationMixedIn.java       |  4 +-
 .../executor/MemberExecutorServiceDefault.java     | 22 ++++--
 .../publish/EntityChangesPublisherDefault.java     |  2 +-
 .../EntityPropertyChangePublisherDefault.java      |  4 +-
 .../publish/ExecutionPublisherDefault.java         | 11 ++-
 .../publish/ObjectLifecyclePublisherDefault.java   | 16 ++---
 .../changetracking/EntityChangeTracker.java        | 15 +++-
 .../changetracking/EntityChangesPublisher.java     |  2 +-
 .../HasInteractionId_commandLogEntry.java          |  5 +-
 .../ApplicationPermissionRepositoryAbstract.java   |  4 +-
 .../dom/ApplicationRoleRepositoryAbstract.java     |  3 +-
 .../dom/ApplicationTenancyRepositoryAbstract.java  |  2 +-
 .../dom/ApplicationUserRepositoryAbstract.java     |  5 +-
 .../secman/applib/user/menu/MeService.java         |  3 +-
 .../integration/authorizor/AuthorizorSecman.java   | 10 +--
 .../facets/TenantedAuthorizationFacetDefault.java  |  4 +-
 .../facets/TenantedAuthorizationPostProcessor.java |  1 +
 .../commons/IsisModulePersistenceCommons.java      |  1 +
 .../changetracking/EntityChangeTrackerDefault.java | 79 +++++++++++++++++++---
 .../IsisModulePersistenceJdoDatanucleus.java       | 10 +--
 .../changetracking/JdoLifecycleListener.java       |  9 ++-
 .../persistence/jpa/JpaBootstrappingTest.java      |  2 +-
 ...rgetRespondListenerToResetQueryResultCache.java |  3 +-
 27 files changed, 165 insertions(+), 72 deletions(-)

diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java
index 36efa2a4ed..2be7bf26d8 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java
@@ -152,7 +152,7 @@ implements ImperativeFacet {
         final ActionSemanticsFacet semanticsFacet = 
getFacetHolder().getFacet(ActionSemanticsFacet.class);
         final boolean cacheable = semanticsFacet != null && 
semanticsFacet.value().isSafeAndRequestCacheable();
         if(cacheable) {
-            final QueryResultsCache queryResultsCache = getQueryResultsCache();
+            final QueryResultsCache queryResultsCache = queryResultsCache();
             final Object[] targetPojoPlusExecutionParameters = 
_Arrays.combine(executionParameters, targetPojo);
             return queryResultsCache.execute(
                     ()->CanonicalInvoker.invoke(method, targetPojo, 
executionParameters),
@@ -163,11 +163,11 @@ implements ImperativeFacet {
         }
     }
 
-    private QueryResultsCache getQueryResultsCache() {
+    private QueryResultsCache queryResultsCache() {
         return serviceRegistry.lookupServiceElseFail(QueryResultsCache.class);
     }
 
-    private InteractionDtoFactory getInteractionDtoServiceInternal() {
+    private InteractionDtoFactory interactionDtoFactory() {
         return 
serviceRegistry.lookupServiceElseFail(InteractionDtoFactory.class);
     }
 
@@ -184,7 +184,7 @@ implements ImperativeFacet {
         public Object execute(final ActionInvocation currentExecution) {
 
             // update the current execution with the DTO (memento)
-            val invocationDto = getInteractionDtoServiceInternal()
+            val invocationDto = interactionDtoFactory()
             .asActionInvocationDto(owningAction, head, initialArgs);
 
             currentExecution.setDto(invocationDto);
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/autocomplete/AutoCompleteFacetAbstract.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/autocomplete/AutoCompleteFacetAbstract.java
index e5a31b73bb..5cca5267fe 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/autocomplete/AutoCompleteFacetAbstract.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/autocomplete/AutoCompleteFacetAbstract.java
@@ -73,7 +73,7 @@ implements AutoCompleteFacet {
             final String search,
             final InteractionInitiatedBy interactionInitiatedBy) {
 
-        val resultAdapter = getPublisherDispatchService()
+        val resultAdapter = executionPublisher()
         .withPublishingSuppressed(()->{
                 final Object list = _Reflect.invokeMethodOn(repositoryMethod, 
getRepository(), search)
                         .ifFailure(e->log.warn("failure while executing 
auto-complete", e))
@@ -90,7 +90,7 @@ implements AutoCompleteFacet {
         return 
getServiceRegistry().lookupService(repositoryClass).orElse(null);
     }
 
-    private ExecutionPublisher getPublisherDispatchService() {
+    private ExecutionPublisher executionPublisher() {
         return 
getServiceRegistry().lookupServiceElseFail(ExecutionPublisher.class);
     }
 
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/publishing/ExecutionPublisher.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/publishing/ExecutionPublisher.java
index 197e043ca9..4007aace35 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/publishing/ExecutionPublisher.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/publishing/ExecutionPublisher.java
@@ -20,6 +20,8 @@ package org.apache.isis.core.metamodel.services.publishing;
 
 import java.util.function.Supplier;
 
+import org.springframework.beans.factory.DisposableBean;
+
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.services.iactn.Execution;
@@ -33,7 +35,7 @@ import 
org.apache.isis.applib.services.publishing.spi.ExecutionSubscriber;
  *
  * @see ExecutionSubscriber
  */
-public interface ExecutionPublisher {
+public interface ExecutionPublisher extends DisposableBean {
 
     /**
      * Notifies {@link ExecutionSubscriber}s of an action invocation through
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToManyAssociationMixedIn.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToManyAssociationMixedIn.java
index a216d1e76e..911fabc6a6 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToManyAssociationMixedIn.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToManyAssociationMixedIn.java
@@ -145,7 +145,7 @@ implements MixedInMember {
             final ManagedObject ownerAdapter,
             final InteractionInitiatedBy interactionInitiatedBy) {
 
-        return getPublishingServiceInternal().withPublishingSuppressed(
+        return executionPublisher().withPublishingSuppressed(
                 () -> mixinAction.executeInternal(
                         headFor(ownerAdapter), Can.empty(), 
interactionInitiatedBy));
     }
@@ -176,7 +176,7 @@ implements MixedInMember {
                 || _Annotations.synthesize(javaMethod, 
Domain.Include.class).isPresent();
     }
 
-    private ExecutionPublisher getPublishingServiceInternal() {
+    private ExecutionPublisher executionPublisher() {
         return 
getServiceRegistry().lookupServiceElseFail(ExecutionPublisher.class);
     }
 
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationMixedIn.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationMixedIn.java
index 8618c0de79..d90bfc5cf3 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationMixedIn.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationMixedIn.java
@@ -128,7 +128,7 @@ implements MixedInMember {
 
         val head = headFor(mixedInAdapter);
 
-        return getPublisherDispatchService().withPublishingSuppressed(
+        return executionPublisher().withPublishingSuppressed(
                 () -> mixinAction.executeInternal(head, Can.empty(), 
interactionInitiatedBy)
         );
     }
@@ -159,7 +159,7 @@ implements MixedInMember {
                 || _Annotations.synthesize(javaMethod, 
Domain.Include.class).isPresent();
     }
 
-    private ExecutionPublisher getPublisherDispatchService() {
+    private ExecutionPublisher executionPublisher() {
         return 
getServiceRegistry().lookupServiceElseFail(ExecutionPublisher.class);
     }
 
diff --git 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java
index 4978fd27d2..2768bb108b 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java
@@ -51,7 +51,6 @@ import 
org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.execution.InteractionInternal;
 import org.apache.isis.core.metamodel.execution.MemberExecutorService;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import 
org.apache.isis.core.metamodel.facets.actions.action.invocation.IdentifierUtil;
 import 
org.apache.isis.core.metamodel.facets.members.publish.command.CommandPublishingFacet;
 import 
org.apache.isis.core.metamodel.facets.members.publish.execution.ExecutionPublishingFacet;
 import 
org.apache.isis.core.metamodel.facets.properties.property.modify.PropertySetterOrClearFacetForDomainEventAbstract.EditingVariant;
@@ -91,12 +90,20 @@ implements MemberExecutorService {
     private final @Getter ObjectManager objectManager;
     private final @Getter ClockService clockService;
     private final @Getter ServiceInjector serviceInjector;
-    private final @Getter Provider<MetricsService> metricsService;
+    private final @Getter Provider<MetricsService> metricsServiceProvider;
     private final @Getter InteractionDtoFactory interactionDtoFactory;
-    private final @Getter Provider<ExecutionPublisher> executionPublisher;
+    private final @Getter Provider<ExecutionPublisher> 
executionPublisherProvider;
     private final @Getter MetamodelEventService metamodelEventService;
     private final @Getter TransactionService transactionService;
 
+    private MetricsService metricsService() {
+        return metricsServiceProvider.get();
+    }
+
+    private ExecutionPublisher executionPublisher() {
+        return executionPublisherProvider.get();
+    }
+
     @Override
     public Optional<InteractionInternal> getInteraction() {
         return interactionLayerTracker.currentInteraction()
@@ -150,7 +157,7 @@ implements MemberExecutorService {
         val memberExecutor = 
actionExecutorFactory.createExecutor(owningAction, head, argumentAdapters);
 
         // sets up startedAt and completedAt on the execution, also manages 
the execution call graph
-        interaction.execute(memberExecutor, actionInvocation, clockService, 
metricsService.get(), command);
+        interaction.execute(memberExecutor, actionInvocation, clockService, 
metricsService(), command);
 
         // handle any exceptions
         final Execution<ActionInvocationDto, ?> priorExecution =
@@ -179,13 +186,14 @@ implements MemberExecutorService {
 
         // publish (if not a contributed association, query-only mixin)
         if (ExecutionPublishingFacet.isPublishingEnabled(facetHolder)) {
-            executionPublisher.get().publishActionInvocation(priorExecution);
+            executionPublisher().publishActionInvocation(priorExecution);
         }
 
         val result = resultFilteredHonoringVisibility(method, returnedAdapter, 
interactionInitiatedBy);
         _Xray.exitInvocation(xrayHandle);
         return result;
     }
+
     @Override
     public ManagedObject setOrClearProperty(
             final @NonNull OneToOneAssociation owningProperty,
@@ -218,7 +226,7 @@ implements MemberExecutorService {
                         interactionInitiatedBy, editingVariant);
 
         // sets up startedAt and completedAt on the execution, also manages 
the execution call graph
-        val targetPojo = interaction.execute(executor, propertyEdit, 
clockService, metricsService.get(), command);
+        val targetPojo = interaction.execute(executor, propertyEdit, 
clockService, metricsService(), command);
 
         // handle any exceptions
         final Execution<?, ?> priorExecution = interaction.getPriorExecution();
@@ -235,7 +243,7 @@ implements MemberExecutorService {
         // publish (if not a contributed association, query-only mixin)
         val publishedPropertyFacet = 
facetHolder.getFacet(ExecutionPublishingFacet.class);
         if (publishedPropertyFacet != null) {
-            executionPublisher.get().publishPropertyEdit(priorExecution);
+            executionPublisher().publishPropertyEdit(priorExecution);
         }
 
         val result = getObjectManager().adapt(targetPojo);
diff --git 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/EntityChangesPublisherDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/EntityChangesPublisherDefault.java
index 69b0b251e1..883f5cb60c 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/EntityChangesPublisherDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/EntityChangesPublisherDefault.java
@@ -85,7 +85,7 @@ public class EntityChangesPublisherDefault implements 
EntityChangesPublisher {
 
     // -- HELPER
 
-    private Optional<EntityChanges> getPayload(HasEnlistedEntityChanges 
hasEnlistedEntityChanges) {
+    private Optional<EntityChanges> getPayload(final @NonNull 
HasEnlistedEntityChanges hasEnlistedEntityChanges) {
         return enabledSubscribers.isEmpty()
                 ? Optional.empty()
                 : hasEnlistedEntityChanges.getEntityChanges(
diff --git 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
index 83e4cda4e2..c83f1406e7 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/EntityPropertyChangePublisherDefault.java
@@ -72,7 +72,7 @@ public class EntityPropertyChangePublisherDefault implements 
EntityPropertyChang
                 .filter(HasEnabling::isEnabled);
     }
 
-    private HasEnlistedEntityPropertyChanges 
getHasEnlistedEntityPropertyChanges() {
+    private HasEnlistedEntityPropertyChanges 
hasEnlistedEntityPropertyChanges() {
         return hasEnlistedEntityPropertyChangesProvider.get();
     }
 
@@ -89,7 +89,7 @@ public class EntityPropertyChangePublisherDefault implements 
EntityPropertyChang
         val currentUser = userService.currentUserNameElseNobody();
         val currentTransactionId = 
transactionService.currentTransactionId().orElse(TransactionId.empty());
 
-        val propertyChanges = 
getHasEnlistedEntityPropertyChanges().getPropertyChanges(
+        val propertyChanges = 
hasEnlistedEntityPropertyChanges().getPropertyChanges(
                 currentTime,
                 currentUser,
                 currentTransactionId);
diff --git 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ExecutionPublisherDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ExecutionPublisherDefault.java
index 62fe2a68f4..c8e641a8b5 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ExecutionPublisherDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ExecutionPublisherDefault.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.concurrent.atomic.LongAdder;
 import java.util.function.Supplier;
 
+import org.springframework.beans.factory.DisposableBean;
 import org.springframework.lang.Nullable;
 import javax.annotation.PostConstruct;
 import javax.annotation.Priority;
@@ -57,6 +58,10 @@ implements ExecutionPublisher {
     private final InteractionLayerTracker iaTracker;
 
     private Can<ExecutionSubscriber> enabledSubscribers = Can.empty();
+    /**
+     * this is the reason that this service is @InteractionScope'd
+     */
+    private final LongAdder suppressionRequestCounter = new LongAdder();
 
     @PostConstruct
     public void init() {
@@ -64,6 +69,11 @@ implements ExecutionPublisher {
                 .filter(HasEnabling::isEnabled);
     }
 
+    @Override
+    public void destroy() throws Exception {
+        suppressionRequestCounter.reset();
+    }
+
     @Override
     public void publishActionInvocation(final Execution<?,?> execution) {
         notifySubscribers(execution);
@@ -104,7 +114,6 @@ implements ExecutionPublisher {
 
     }
 
-    private final LongAdder suppressionRequestCounter = new LongAdder();
 
     private boolean canPublish() {
         return enabledSubscribers.isNotEmpty()
diff --git 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ObjectLifecyclePublisherDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ObjectLifecyclePublisherDefault.java
index 26feea1bfc..71489accdb 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ObjectLifecyclePublisherDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ObjectLifecyclePublisherDefault.java
@@ -28,6 +28,7 @@ import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.PriorityPrecedence;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.facets.object.callbacks.CallbackFacet;
 import 
org.apache.isis.core.metamodel.facets.object.callbacks.LoadedCallbackFacet;
@@ -46,6 +47,8 @@ import 
org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
 import org.apache.isis.core.transaction.changetracking.EntityChangeTracker;
 import org.apache.isis.core.transaction.changetracking.events.PostStoreEvent;
 
+import lombok.RequiredArgsConstructor;
+
 /**
  * @see ObjectLifecyclePublisher
  * @since 2.0 {@index}
@@ -54,23 +57,18 @@ import 
org.apache.isis.core.transaction.changetracking.events.PostStoreEvent;
 @Named(IsisModuleCoreRuntimeServices.NAMESPACE + 
".ObjectLifecyclePublisherDefault")
 @Priority(PriorityPrecedence.EARLY)
 @Qualifier("Default")
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
 //@Log4j2
 public class ObjectLifecyclePublisherDefault implements 
ObjectLifecyclePublisher {
 
     private final Provider<EntityChangeTracker> entityChangeTrackerProvider;
     private final Provider<LifecycleCallbackNotifier> 
lifecycleCallbackNotifierProvider;
-
-    @Inject
-    public ObjectLifecyclePublisherDefault(
-            final Provider<EntityChangeTracker> entityChangeTrackerProvider,
-            final Provider<LifecycleCallbackNotifier> 
lifecycleCallbackNotifierProvider) {
-        this.entityChangeTrackerProvider = entityChangeTrackerProvider;
-        this.lifecycleCallbackNotifierProvider = 
lifecycleCallbackNotifierProvider;
-    }
+    private final InteractionService interactionService;
 
     EntityChangeTracker entityChangeTracker() {
-        return entityChangeTrackerProvider.get();
+        return interactionService.isInInteraction() ? 
entityChangeTrackerProvider.get() : EntityChangeTracker.NOOP;
     }
+
     LifecycleCallbackNotifier lifecycleCallbackNotifier() {
         return lifecycleCallbackNotifierProvider.get();
     }
diff --git 
a/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTracker.java
 
b/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTracker.java
index eaf09281b4..a75a7ead10 100644
--- 
a/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTracker.java
+++ 
b/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTracker.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.core.transaction.changetracking;
 
+import org.springframework.beans.factory.DisposableBean;
 import org.springframework.lang.Nullable;
 
 import org.apache.isis.commons.collections.Can;
@@ -30,7 +31,19 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
  *
  * @since 1.x but renamed/refactored for v2 {@index}
  */
-public interface EntityChangeTracker {
+public interface EntityChangeTracker extends DisposableBean {
+
+    /**
+     * Provided primarily for testing, but also used in cases where an attempt 
is made to resolve a bean but
+     * there is no active interaction.
+     */
+    EntityChangeTracker NOOP = new EntityChangeTracker() {
+        @Override public void destroy() throws Exception {}
+        @Override public void enlistCreated(ManagedObject entity) {}
+        @Override public void enlistUpdating(ManagedObject entity, 
Can<PropertyChangeRecord> propertyChangeRecords) {}
+        @Override public void enlistDeleting(ManagedObject entity) {}
+        @Override public void incrementLoaded(ManagedObject entity) {}
+    };
 
     /**
      * Publishing support: for object stores to enlist an object that has just 
been created,
diff --git 
a/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangesPublisher.java
 
b/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangesPublisher.java
index d3c6efec0e..a5583acd70 100644
--- 
a/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangesPublisher.java
+++ 
b/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangesPublisher.java
@@ -33,6 +33,6 @@ public interface EntityChangesPublisher {
      * an {@link org.apache.isis.applib.services.iactn.Interaction}, calling
      * the {@link EntityChangesSubscriber#onChanging(EntityChanges)} callback.
      */
-    void publishChangingEntities(HasEnlistedEntityChanges 
hasEnlistedEntityChanges);
+    void publishChangingEntities(final HasEnlistedEntityChanges 
hasEnlistedEntityChanges);
 
 }
diff --git 
a/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/contributions/HasInteractionId_commandLogEntry.java
 
b/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/contributions/HasInteractionId_commandLogEntry.java
index 2245fb3420..9961cb3ac7 100644
--- 
a/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/contributions/HasInteractionId_commandLogEntry.java
+++ 
b/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/contributions/HasInteractionId_commandLogEntry.java
@@ -21,6 +21,7 @@
 package org.apache.isis.extensions.commandlog.applib.contributions;
 
 import javax.inject.Inject;
+import javax.inject.Provider;
 
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.mixins.system.HasInteractionId;
@@ -48,7 +49,7 @@ public class HasInteractionId_commandLogEntry {
 
 
     public CommandLogEntry prop() {
-        return queryResultsCache.execute(this::doProp, getClass(), "prop");
+        return queryResultsCacheProvider.get().execute(this::doProp, 
getClass(), "prop");
     }
 
     private CommandLogEntry doProp() {
@@ -64,6 +65,6 @@ public class HasInteractionId_commandLogEntry {
     }
 
     @Inject CommandLogEntryRepository<? extends CommandLogEntry> 
commandLogEntryRepository;
-    @Inject QueryResultsCache queryResultsCache;
+    @Inject Provider<QueryResultsCache> queryResultsCacheProvider;
 
 }
diff --git 
a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/ApplicationPermissionRepositoryAbstract.java
 
b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/ApplicationPermissionRepositoryAbstract.java
index 4035605680..0f94b8ab4a 100644
--- 
a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/ApplicationPermissionRepositoryAbstract.java
+++ 
b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/ApplicationPermissionRepositoryAbstract.java
@@ -24,6 +24,7 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 
 import javax.inject.Inject;
+import javax.inject.Provider;
 
 import org.apache.isis.applib.query.Query;
 import org.apache.isis.applib.services.appfeat.ApplicationFeature;
@@ -54,11 +55,10 @@ implements ApplicationPermissionRepository {
 
     @Inject private RepositoryService repository;
     @Inject private ApplicationFeatureRepository featureRepository;
-    @Inject private ApplicationRoleRepository roleRepository;
     @Inject private FactoryService factory;
     @Inject private MessageService messages;
 
-    @Inject private javax.inject.Provider<QueryResultsCache> 
queryResultsCacheProvider;
+    @Inject private Provider<QueryResultsCache> queryResultsCacheProvider;
 
     private final Class<P> applicationPermissionClass;
 
diff --git 
a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/ApplicationRoleRepositoryAbstract.java
 
b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/ApplicationRoleRepositoryAbstract.java
index b23ab86855..59d7d2f902 100644
--- 
a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/ApplicationRoleRepositoryAbstract.java
+++ 
b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/ApplicationRoleRepositoryAbstract.java
@@ -51,8 +51,7 @@ implements ApplicationRoleRepository {
     @Inject private FactoryService factoryService;
     @Inject private RepositoryService repository;
     @Inject private IsisConfiguration config;
-    @Inject RegexReplacer regexReplacer;
-
+    @Inject private RegexReplacer regexReplacer;
     @Inject private Provider<QueryResultsCache> queryResultsCacheProvider;
 
     private final Class<R> applicationRoleClass;
diff --git 
a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/ApplicationTenancyRepositoryAbstract.java
 
b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/ApplicationTenancyRepositoryAbstract.java
index bc441a40a4..502e189012 100644
--- 
a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/ApplicationTenancyRepositoryAbstract.java
+++ 
b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/ApplicationTenancyRepositoryAbstract.java
@@ -40,8 +40,8 @@ implements ApplicationTenancyRepository {
 
     @Inject private FactoryService factory;
     @Inject private RepositoryService repository;
+    @Inject private RegexReplacer regexReplacer;
     @Inject private Provider<QueryResultsCache> queryResultsCacheProvider;
-    @Inject RegexReplacer regexReplacer;
 
 
     private final Class<T> applicationTenancyClass;
diff --git 
a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java
 
b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java
index a0f6c56381..e046776c3e 100644
--- 
a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java
+++ 
b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java
@@ -25,6 +25,7 @@ import java.util.Optional;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
+import javax.inject.Provider;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -57,12 +58,12 @@ implements ApplicationUserRepository {
     @Inject private RepositoryService repository;
        @Inject protected IsisConfiguration config;
     @Inject private EventBusService eventBusService;
-    @Inject RegexReplacer regexReplacer;
+    @Inject private RegexReplacer regexReplacer;
+    @Inject private Provider<QueryResultsCache> queryResultsCacheProvider;
 
     // empty if no candidate is available
     @Autowired(required = false) @Qualifier("secman") PasswordEncoder 
passwordEncoder;
 
-    @Inject private javax.inject.Provider<QueryResultsCache> 
queryResultsCacheProvider;
 
     private final Class<U> applicationUserClass;
 
diff --git 
a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/MeService.java
 
b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/MeService.java
index 3611c45dff..18a0bcdfe5 100644
--- 
a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/MeService.java
+++ 
b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/MeService.java
@@ -22,6 +22,7 @@ import java.util.concurrent.Callable;
 
 import javax.inject.Inject;
 import javax.inject.Named;
+import javax.inject.Provider;
 
 import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Component;
@@ -68,7 +69,7 @@ public class MeService {
 
     final ApplicationUserRepository applicationUserRepository;
     final UserService userService;
-    final javax.inject.Provider<QueryResultsCache> queryResultsCacheProvider;
+    final Provider<QueryResultsCache> queryResultsCacheProvider;
 
 
     @ObjectSupport public String iconName() {
diff --git 
a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authorizor/AuthorizorSecman.java
 
b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authorizor/AuthorizorSecman.java
index 8b4bfa41b4..95e1fd4c8e 100644
--- 
a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authorizor/AuthorizorSecman.java
+++ 
b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authorizor/AuthorizorSecman.java
@@ -95,21 +95,17 @@ public class AuthorizorSecman implements Authorizor {
     @InteractionScope
     static class PermissionCache implements DisposableBean {
 
-        private Map<String, Optional<ApplicationPermissionValueSet>> 
permissionsByUsername;
+        private final Map<String, Optional<ApplicationPermissionValueSet>> 
permissionsByUsername = _Maps.newHashMap();
 
         @Override
-        public void destroy() throws Exception {
-            permissionsByUsername = null;
+        public void destroy() {
+            permissionsByUsername.clear();
         }
 
         Optional<ApplicationPermissionValueSet> computeIfAbsent(
                 final @NonNull String userName,
                 final Supplier<Optional<ApplicationPermissionValueSet>> 
lookup) {
 
-            if(permissionsByUsername==null) {
-                permissionsByUsername = _Maps.newHashMap();
-            }
-
             return permissionsByUsername.computeIfAbsent(userName, 
__->lookup.get());
         }
 
diff --git 
a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacetDefault.java
 
b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacetDefault.java
index a86191527d..2b2a1dd19e 100644
--- 
a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacetDefault.java
+++ 
b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacetDefault.java
@@ -37,14 +37,14 @@ public class TenantedAuthorizationFacetDefault
 extends FacetAbstract
 implements TenantedAuthorizationFacet {
 
-    private static final Class<? extends Facet> type() {
+    private static Class<? extends Facet> type() {
         return TenantedAuthorizationFacet.class;
     }
 
     private final List<ApplicationTenancyEvaluator> evaluators;
     private final ApplicationUserRepository applicationUserRepository;
-    private final Provider<QueryResultsCache> queryResultsCacheProvider;
     private final UserService userService;
+    private final Provider<QueryResultsCache> queryResultsCacheProvider;
 
     public TenantedAuthorizationFacetDefault(
             final List<ApplicationTenancyEvaluator> evaluators,
diff --git 
a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationPostProcessor.java
 
b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationPostProcessor.java
index 9a8bdaa8ea..38747bf5e5 100644
--- 
a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationPostProcessor.java
+++ 
b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationPostProcessor.java
@@ -66,6 +66,7 @@ extends ObjectSpecificationPostProcessorAbstract {
     @Inject UserService userService;
     @Inject @Lazy ApplicationUserRepository userRepository;
     @Inject Provider<QueryResultsCache> queryResultsCacheProvider;
+
     @Autowired(required=false) List<ApplicationTenancyEvaluator> 
applicationTenancyEvaluators;
 
     @Inject
diff --git 
a/persistence/commons/src/main/java/org/apache/isis/persistence/commons/IsisModulePersistenceCommons.java
 
b/persistence/commons/src/main/java/org/apache/isis/persistence/commons/IsisModulePersistenceCommons.java
index c2613ac727..8d791d2131 100644
--- 
a/persistence/commons/src/main/java/org/apache/isis/persistence/commons/IsisModulePersistenceCommons.java
+++ 
b/persistence/commons/src/main/java/org/apache/isis/persistence/commons/IsisModulePersistenceCommons.java
@@ -31,6 +31,7 @@ import 
org.apache.isis.persistence.jpa.integration.changetracking.EntityChangeTr
 
         // @Service's
         EntityChangeTrackerDefault.class,
+        EntityChangeTrackerDefault.TransactionSubscriber.class,
 
 })
 public class IsisModulePersistenceCommons {
diff --git 
a/persistence/commons/src/main/java/org/apache/isis/persistence/jpa/integration/changetracking/EntityChangeTrackerDefault.java
 
b/persistence/commons/src/main/java/org/apache/isis/persistence/jpa/integration/changetracking/EntityChangeTrackerDefault.java
index 620f5c96ec..f2b40b61f8 100644
--- 
a/persistence/commons/src/main/java/org/apache/isis/persistence/jpa/integration/changetracking/EntityChangeTrackerDefault.java
+++ 
b/persistence/commons/src/main/java/org/apache/isis/persistence/jpa/integration/changetracking/EntityChangeTrackerDefault.java
@@ -31,10 +31,12 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Provider;
 
+import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.event.EventListener;
 import org.springframework.core.annotation.Order;
 import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.DomainObject;
@@ -44,6 +46,7 @@ import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.iactn.Interaction;
 import org.apache.isis.applib.services.iactn.InteractionProvider;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
 import org.apache.isis.applib.services.metrics.MetricsService;
 import org.apache.isis.applib.services.publishing.spi.EntityChanges;
 import org.apache.isis.applib.services.publishing.spi.EntityPropertyChange;
@@ -85,6 +88,7 @@ import lombok.extern.log4j.Log4j2;
  * service <i>is</i> interaction-scoped, a new instance of the service is 
created for each interaction, and so the
  * data held in this service is private to each user's interaction.
  * </p>
+ *
  * @since 2.0 {@index}
  */
 @Service
@@ -102,6 +106,10 @@ implements
     HasEnlistedEntityChanges {
 
 
+    private final EntityPropertyChangePublisher entityPropertyChangePublisher;
+    private final EntityChangesPublisher entityChangesPublisher;
+    private final Provider<InteractionProvider> interactionProviderProvider;
+
     /**
      * Contains a record for every objectId/propertyId that was changed.
      */
@@ -122,9 +130,17 @@ implements
     private final LongAdder entityChangeEventCount = new LongAdder();
     private final AtomicBoolean persistentChangesEncountered = new 
AtomicBoolean();
 
-    private final EntityPropertyChangePublisher entityPropertyChangePublisher;
-    private final EntityChangesPublisher entityChangesPublisher;
-    private final Provider<InteractionProvider> interactionProviderProvider;
+
+    @Override
+    public void destroy() throws Exception {
+        enlistedPropertyChangeRecordsById.clear();
+        entityPropertyChangeRecordsForPublishing.clear();
+        changeKindByEnlistedAdapter.clear();
+
+        numberEntitiesLoaded.reset();
+        entityChangeEventCount.reset();
+        persistentChangesEncountered.set(false);
+    }
 
     Set<PropertyChangeRecord> snapshotPropertyChangeRecords() {
         // this code path has side-effects, it locks the result for this 
transaction,
@@ -172,11 +188,58 @@ implements
     }
 
     /**
-     * TRANSACTION END BOUNDARY
-     * @apiNote intended to be called during before transaction completion by 
the framework internally
+     * Subscribes to transactions and forwards onto the current interaction's 
EntityChangeTracker, if available.
+     *
+     * <p>
+     *     Note that this service has singleton-scope, unlike {@link 
EntityChangeTrackerDefault} which has
+     *     {@link InteractionScope interaction scope}. The problem with using 
{@link EntityChangeTrackerDefault} as
+     *     the direct subscriber is that if there's no {@link Interaction}, 
then Spring will fail to activate an instance resulting in an
+     *     {@link 
org.springframework.beans.factory.support.ScopeNotActiveException}.  Now, 
admittedly that exception
+     *     gets swallowed in the call stack somewhere, but it's still not 
pretty.
+     * </p>
+     *
+     * <p>
+     *     This design, instead, at least lets us check if there's an 
interaction in scope, and effectively ignore
+     *     the call if not.
+     * </p>
      */
-    @EventListener(value = TransactionBeforeCompletionEvent.class) 
@Order(PriorityPrecedence.LATE)
-    public void onTransactionCompleting(final TransactionBeforeCompletionEvent 
event) {
+    @Component
+    
@Named("isis.persistence.commons.EntityChangeTrackerDefault.TransactionSubscriber")
+    @Priority(PriorityPrecedence.EARLY)
+    @Qualifier("default")
+    @RequiredArgsConstructor(onConstructor_ = {@Inject})
+    public static class TransactionSubscriber {
+
+        private final InteractionService interactionService;
+        private final Provider<EntityChangeTrackerDefault> 
entityChangeTrackerProvider;
+
+        /**
+         * TRANSACTION END BOUNDARY
+         * @apiNote intended to be called during before transaction completion 
by the framework internally
+         */
+        @EventListener(value = TransactionBeforeCompletionEvent.class)
+        @Order(PriorityPrecedence.LATE)
+        public void onTransactionCompleting(final 
TransactionBeforeCompletionEvent event) {
+
+            if(!interactionService.isInInteraction()) {
+                // discard request is there is no interaction in scope.
+                // this shouldn't ever really occur, but some low-level (could 
be improved?) integration tests do
+                // hit this case.
+                return;
+            }
+            entityChangeTracker().onTransactionCompleting(event);
+        }
+
+        private EntityChangeTrackerDefault entityChangeTracker() {
+            return entityChangeTrackerProvider.get();
+        }
+    }
+
+    /**
+     * As called by {@link TransactionSubscriber}, so long as there is an 
{@link Interaction} in
+     * {@link InteractionScope scope}.
+     */
+    void onTransactionCompleting(final TransactionBeforeCompletionEvent event) 
{
         try {
             doPublish();
         } finally {
@@ -392,6 +455,4 @@ implements
     }
 
 
-
-
 }
diff --git 
a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/IsisModulePersistenceJdoDatanucleus.java
 
b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/IsisModulePersistenceJdoDatanucleus.java
index e127e956a1..d9ad45994c 100644
--- 
a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/IsisModulePersistenceJdoDatanucleus.java
+++ 
b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/IsisModulePersistenceJdoDatanucleus.java
@@ -42,6 +42,7 @@ import 
org.springframework.dao.support.PersistenceExceptionTranslator;
 import org.springframework.transaction.interceptor.TransactionInterceptor;
 
 import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
 import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.core.config.IsisConfiguration;
@@ -157,7 +158,7 @@ public class IsisModulePersistenceJdoDatanucleus {
             final IsisConfiguration isisConfiguration,
             final DataSource dataSource,
             final MetaModelContext metaModelContext,
-            final EventBusService eventBusService,
+            final InteractionService interactionService,
             final ObjectLifecyclePublisher objectLifecyclePublisher,
             final Provider<EntityChangeTracker> entityChangeTrackerProvider,
             final IsisBeanTypeRegistry beanTypeRegistry,
@@ -175,14 +176,14 @@ public class IsisModulePersistenceJdoDatanucleus {
                 val pu = createDefaultPersistenceUnit(beanTypeRegistry);
                 val pmf = new JDOPersistenceManagerFactory(pu, props);
                 pmf.setConnectionFactory(dataSource);
-                integrateWithApplicationLayer(metaModelContext, 
entityChangeTrackerProvider, objectLifecyclePublisher, pmf);
+                integrateWithApplicationLayer(metaModelContext, 
entityChangeTrackerProvider, objectLifecyclePublisher, interactionService, pmf);
                 return pmf;
             }
             @Override
             protected PersistenceManagerFactory 
newPersistenceManagerFactory(final String name) {
                 val pmf = super.newPersistenceManagerFactory(name);
                 pmf.setConnectionFactory(dataSource); //might be too late, 
anyway, not sure if this is ever called
-                integrateWithApplicationLayer(metaModelContext, 
entityChangeTrackerProvider, objectLifecyclePublisher, pmf);
+                integrateWithApplicationLayer(metaModelContext, 
entityChangeTrackerProvider, objectLifecyclePublisher, interactionService, pmf);
                 return pmf;
             }
         };
@@ -334,12 +335,13 @@ public class IsisModulePersistenceJdoDatanucleus {
             final MetaModelContext metaModelContext,
             final Provider<EntityChangeTracker> entityChangeTrackerProvider,
             final ObjectLifecyclePublisher objectLifecyclePublisher,
+            final InteractionService interactionService,
             final PersistenceManagerFactory pmf) {
 
         // install JDO specific entity change listeners ...
 
         val jdoLifecycleListener =
-                new JdoLifecycleListener(metaModelContext, 
entityChangeTrackerProvider, objectLifecyclePublisher);
+                new JdoLifecycleListener(metaModelContext, 
entityChangeTrackerProvider, objectLifecyclePublisher, interactionService);
         pmf.addInstanceLifecycleListener(jdoLifecycleListener, (Class[]) null);
 
     }
diff --git 
a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java
 
b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java
index 79f182388d..5eafecd564 100644
--- 
a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java
+++ 
b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java
@@ -32,15 +32,13 @@ import javax.jdo.listener.StoreLifecycleListener;
 
 import org.datanucleus.enhancement.Persistable;
 
-import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import 
org.apache.isis.core.metamodel.facets.object.publish.entitychange.EntityChangePublishingFacet;
 import 
org.apache.isis.core.metamodel.objectmanager.ObjectManager.EntityAdaptingMode;
 import 
org.apache.isis.core.metamodel.services.objectlifecycle.ObjectLifecyclePublisher;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.transaction.changetracking.EntityChangeTracker;
-import org.apache.isis.core.transaction.changetracking.events.PostStoreEvent;
-import org.apache.isis.core.transaction.changetracking.events.PreStoreEvent;
 
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
@@ -69,6 +67,7 @@ DetachLifecycleListener, DirtyLifecycleListener, 
LoadLifecycleListener, StoreLif
     private final @NonNull MetaModelContext metaModelContext;
     private final @NonNull Provider<EntityChangeTracker> 
entityChangeTrackerProvider;
     private final @NonNull ObjectLifecyclePublisher objectLifecyclePublisher;
+    private final @NonNull InteractionService interactionService;
 
     // -- CALLBACKS
 
@@ -210,8 +209,8 @@ DetachLifecycleListener, DirtyLifecycleListener, 
LoadLifecycleListener, StoreLif
 
     // -- DEPENDENCIES
 
-    private EntityChangeTracker getEntityChangeTracker() {
-        return entityChangeTrackerProvider.get();
+    private EntityChangeTracker entityChangeTracker() {
+        return interactionService.isInInteraction() ? 
entityChangeTrackerProvider.get() : EntityChangeTracker.NOOP;
     }
 
 }
diff --git 
a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java
 
b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java
index aea9fc23cc..1f409f9306 100644
--- 
a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java
+++ 
b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java
@@ -60,7 +60,7 @@ import lombok.val;
         })
 @TestPropertySource(IsisPresets.UseLog4j2Test)
 @Transactional @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
- @DirtiesContext // doesn't seem to tidy up correctly ... I see 
InteractionService still injected into entities in the _next_ tests run 
(JpaExceptionTranslationTest_usingTransactional)
+// @DirtiesContext // doesn't seem to tidy up correctly ... I see 
InteractionService still injected into entities in the _next_ tests run 
(JpaExceptionTranslationTest_usingTransactional)
 class JpaBootstrappingTest extends IsisIntegrationTestAbstract {
 
     @Inject private Optional<PlatformTransactionManager> 
platformTransactionManager;
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/TargetRespondListenerToResetQueryResultCache.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/TargetRespondListenerToResetQueryResultCache.java
index 4cc1f1dd8a..3bb2cd8588 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/TargetRespondListenerToResetQueryResultCache.java
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/TargetRespondListenerToResetQueryResultCache.java
@@ -19,6 +19,7 @@
 package org.apache.isis.viewer.wicket.viewer.wicketapp;
 
 import javax.inject.Inject;
+import javax.inject.Provider;
 
 import org.apache.wicket.ajax.AjaxRequestTarget;
 
@@ -29,7 +30,7 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 class TargetRespondListenerToResetQueryResultCache implements 
AjaxRequestTarget.ITargetRespondListener {
 
-    @Inject private javax.inject.Provider<QueryResultsCache> 
queryResultsCacheProvider;
+    @Inject private Provider<QueryResultsCache> queryResultsCacheProvider;
 
     @Override
     public void onTargetRespond(final AjaxRequestTarget target) {

Reply via email to