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

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

commit 7c0fb3c1fe6788c5190b915c809663ac8cfa3499
Author: danhaywood <d...@haywood-associates.co.uk>
AuthorDate: Mon Dec 11 18:29:01 2023 +0000

    CAUSEWAY-3654: fixes compile issues in tests
    
    CAUSEWAY-3654: adds a more complete dummy PlatformTransactionManager to 
AppManifestBase
    
    for integ tests
    
    CAUSEWAY-3654: introduces NoopTransactionSynchronizationService to ensure...
    
    ... that there is always at least one transaction-scoped service.
    Also moves StackedTransactionScope et al from core-interaction to 
core-transaction module
    
    CAUSEWAY-3654: fixes some further integ tests
    
    CAUSEWAY-3654: fixes some further integ tests
    
    CAUSEWAY-3654: comments out some tests; sigh
    
    CAUSEWAY-3654: fixes order of wrapping; reenable commented out tests to see 
what gives
    
    CAUSEWAY-3654: fixes JPA integ test
    
    CAUSEWAY-3654: cleans up javadoc is all
    
    CAUSEWAY-3654: fixes (?) integ test for stable good domain
---
 .../interaction/CausewayModuleCoreInteraction.java |   2 -
 .../transaction/TransactionServiceSpring.java      |  57 ++++++----
 core/transaction/src/main/java/module-info.java    |   1 +
 .../transaction/CausewayModuleCoreTransaction.java |   5 +
 .../NoopTransactionSynchronizationService.java     |  30 +++++
 .../scope/StackedTransactionScope.java             |   4 +-
 .../TransactionScopeBeanFactoryPostProcessor.java  |   2 +-
 .../metamodel/PdfjsViewer_Abstract_IntegTest.java  |  19 +++-
 ...elTest_usingBadDomain_noAnnotationEnforced.java |   7 +-
 .../DomainModelTest_usingGoodDomain.java           |   4 +-
 .../integtest/Layout_Counter_IntegTest.java        |  20 +++-
 .../jdo/JdoExceptionTranslationTest.java           |  10 +-
 .../testdomain/persistence/jdo/JdoJaxbTest.java    |   2 +
 .../jdo/JdoTransactionScopeListenerTest.java       | 121 ---------------------
 .../jpa/JpaExceptionTranslationTest.java           |  18 ++-
 .../transactions/jpa/CommitListener.java           |  78 +++++++++++++
 ...actionRollbackTest_usingInteractionService.java |  60 +++-------
 ...actionRollbackTest_usingTransactionService.java |  65 +++--------
 .../testdomain/RegressionTestAbstract.java         |  15 +--
 .../testdomain/conf/Configuration_headless.java    |  55 +++-------
 .../publishing/PublishingTestFactoryAbstract.java  |  31 ++++--
 .../util/interaction/InteractionBoundaryProbe.java |  59 ++++++----
 22 files changed, 320 insertions(+), 345 deletions(-)

diff --git 
a/core/interaction/src/main/java/org/apache/causeway/core/interaction/CausewayModuleCoreInteraction.java
 
b/core/interaction/src/main/java/org/apache/causeway/core/interaction/CausewayModuleCoreInteraction.java
index 13ff6e5897..09871d7ce2 100644
--- 
a/core/interaction/src/main/java/org/apache/causeway/core/interaction/CausewayModuleCoreInteraction.java
+++ 
b/core/interaction/src/main/java/org/apache/causeway/core/interaction/CausewayModuleCoreInteraction.java
@@ -21,14 +21,12 @@ package org.apache.causeway.core.interaction;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
-import 
org.apache.causeway.core.interaction.scope.TransactionScopeBeanFactoryPostProcessor;
 import 
org.apache.causeway.core.interaction.scope.InteractionScopeBeanFactoryPostProcessor;
 
 @Configuration
 @Import({
 
     InteractionScopeBeanFactoryPostProcessor.class,
-    TransactionScopeBeanFactoryPostProcessor.class,
 
 })
 public class CausewayModuleCoreInteraction {
diff --git 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/transaction/TransactionServiceSpring.java
 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/transaction/TransactionServiceSpring.java
index af7b7149ac..e4fd820bad 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/transaction/TransactionServiceSpring.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/transaction/TransactionServiceSpring.java
@@ -28,6 +28,8 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Provider;
 
+import org.apache.causeway.applib.services.iactnlayer.InteractionContext;
+
 import org.springframework.aop.support.AopUtils;
 import org.springframework.beans.factory.annotation.Qualifier;
 import 
org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -86,7 +88,6 @@ implements
     private final Provider<InteractionLayerTracker> 
interactionLayerTrackerProvider;
     private final Can<PersistenceExceptionTranslator> 
persistenceExceptionTranslators;
     private final ConfigurableListableBeanFactory 
configurableListableBeanFactory;
-//    private final Can<TransactionBoundaryAware> 
transactionBoundaryAwareBeans;
 
     @Inject
     public TransactionServiceSpring(
@@ -94,7 +95,6 @@ implements
             final List<PersistenceExceptionTranslator> 
persistenceExceptionTranslators,
             final Provider<InteractionLayerTracker> 
interactionLayerTrackerProvider,
             final ConfigurableListableBeanFactory 
configurableListableBeanFactory
-//            , final List<TransactionBoundaryAware> 
transactionBoundaryAwareBeans
     ) {
 
         this.platformTransactionManagers = 
Can.ofCollection(platformTransactionManagers);
@@ -106,12 +106,9 @@ implements
         log.info("PersistenceExceptionTranslators: {}", 
persistenceExceptionTranslators);
 
         this.interactionLayerTrackerProvider = interactionLayerTrackerProvider;
-
-//        this.transactionBoundaryAwareBeans = 
Can.ofCollection(transactionBoundaryAwareBeans);
-//        log.info("TransactionBoundaryAwareBeans: {}", 
transactionBoundaryAwareBeans);
     }
 
-    // -- SPRING INTEGRATION
+    // -- API
 
     @Override
     public <T> Try<T> callTransactional(final TransactionDefinition def, final 
Callable<T> callable) {
@@ -123,26 +120,22 @@ implements
         try {
 
             TransactionStatus txStatus = 
platformTransactionManager.getTransaction(def);
-//            if(tx.isNewTransaction()) {
-//                transactionBoundaryAwareBeans.forEach(tba -> 
tba.afterEnteringTransactionalBoundary(platformTransactionManager));
-//            }
             registerTransactionSynchronizations(txStatus);
 
 
-            result = Try.call(callable)
+            result = Try.call(() -> {
+                        final T callResult = callable.call();
+                        // we flush here to ensure that the result captures 
any exception, eg from a declarative constraint violation
+                        txStatus.flush();
+                        return callResult;
+                    })
                     .mapFailure(ex->translateExceptionIfPossible(ex, 
platformTransactionManager));
 
-//            if(tx.isNewTransaction()) {
-//                transactionBoundaryAwareBeans.forEach(tba -> 
tba.beforeLeavingTransactionalBoundary(platformTransactionManager));
-//            }
             if(result.isFailure()) {
                 platformTransactionManager.rollback(txStatus);
             } else {
                 platformTransactionManager.commit(txStatus);
             }
-//            if(tx.isNewTransaction()) {
-//                transactionBoundaryAwareBeans.forEach(tba -> 
tba.afterLeavingTransactionalBoundary(platformTransactionManager));
-//            }
         } catch (Exception ex) {
 
             return result!=null
@@ -152,7 +145,7 @@ implements
                     // (so we don't shadow the original failure)
                     ? result
 
-                    // return the failure we just catched
+                    // return the failure we just caught
                     : Try.failure(translateExceptionIfPossible(ex, 
platformTransactionManager));
 
         }
@@ -277,6 +270,7 @@ implements
         .orElse(TransactionState.NONE);
     }
 
+
     // -- TRANSACTION SEQUENCE TRACKING
 
     // TODO: this ThreadLocal (as with all thread-locals) should perhaps 
somehow be managed using
@@ -285,7 +279,8 @@ implements
     private ThreadLocal<LongAdder> txCounter = 
ThreadLocal.withInitial(LongAdder::new);
 
 
-    // -- HELPER
+    // -- SPRING INTEGRATION
+
 
     private PlatformTransactionManager transactionManagerForElseFail(final 
TransactionDefinition def) {
         if(def instanceof TransactionTemplate) {
@@ -356,6 +351,12 @@ implements
     }
 
 
+    /**
+     * For use only by {@link 
org.apache.causeway.core.runtimeservices.session.InteractionServiceDefault}, 
sets up
+     * the initial transaction automatically against all available {@link 
PlatformTransactionManager}s.
+     *
+     * @param interaction The {@link CausewayInteraction} object representing 
the current interaction.
+     */
     public void onOpen(final @NonNull CausewayInteraction interaction) {
 
         txCounter.get().reset();
@@ -391,7 +392,6 @@ implements
                             txStatus,
                             txManager.getClass().getName(), // info to be used 
for display in case of errors
                             () -> {
-//                                transactionBoundaryAwareBeans.forEach(tbab 
-> tbab.beforeLeavingTransactionalBoundary(txManager));
                                 
_Xray.txBeforeCompletion(interactionLayerTrackerProvider.get(), "tx: 
beforeCompletion");
                                 final TransactionCompletionStatus event;
                                 if (txStatus.isRollbackOnly()) {
@@ -403,7 +403,6 @@ implements
                                 }
                                 
_Xray.txAfterCompletion(interactionLayerTrackerProvider.get(), 
String.format("tx: afterCompletion (%s)", event.name()));
 
-//                                transactionBoundaryAwareBeans.forEach(tbab 
-> tbab.afterLeavingTransactionalBoundary(txManager));
                                 txCounter.get().increment();
                             }
                         )
@@ -411,15 +410,29 @@ implements
 
             });
         }
-
     }
 
 
+    /**
+     * For use only by {@link 
org.apache.causeway.core.runtimeservices.session.InteractionServiceDefault}, if
+     * {@link 
org.apache.causeway.applib.services.iactnlayer.InteractionService#run(InteractionContext,
 ThrowingRunnable)}
+     * or {@link 
org.apache.causeway.applib.services.iactnlayer.InteractionService#call(InteractionContext,
 Callable)}
+     * (or their various overloads) result in an exception.
+     *
+     * @param interaction The {@link CausewayInteraction} object representing 
the current interaction.
+     */
     public void requestRollback(final @NonNull CausewayInteraction 
interaction) {
         Optional.ofNullable(interaction.getAttribute(OnCloseHandle.class))
                 .ifPresent(OnCloseHandle::requestRollback);
     }
 
+    /**
+     * For use only by {@link 
org.apache.causeway.core.runtimeservices.session.InteractionServiceDefault}, to 
close the
+     * transaction initially set up in {@link #onOpen(CausewayInteraction)} 
against all configured
+     * {@link PlatformTransactionManager}s.
+     *
+     * @param interaction The {@link CausewayInteraction} object representing 
the current interaction.
+     */
     public void onClose(final @NonNull CausewayInteraction interaction) {
 
         if (log.isDebugEnabled()) {
@@ -462,9 +475,7 @@ implements
                             onCloseTask.getOnErrorInfo(),
                             ex);
                 }
-
             });
         }
     }
-
 }
diff --git a/core/transaction/src/main/java/module-info.java 
b/core/transaction/src/main/java/module-info.java
index d64c8c9e33..bec887b514 100644
--- a/core/transaction/src/main/java/module-info.java
+++ b/core/transaction/src/main/java/module-info.java
@@ -21,6 +21,7 @@ module org.apache.causeway.core.transaction {
     exports org.apache.causeway.core.transaction.changetracking;
     exports org.apache.causeway.core.transaction.changetracking.events;
     exports org.apache.causeway.core.transaction.events;
+    exports org.apache.causeway.core.transaction.scope;
 
     requires java.annotation;
     requires java.sql;
diff --git 
a/core/transaction/src/main/java/org/apache/causeway/core/transaction/CausewayModuleCoreTransaction.java
 
b/core/transaction/src/main/java/org/apache/causeway/core/transaction/CausewayModuleCoreTransaction.java
index 21a841b29a..80667e7cf9 100644
--- 
a/core/transaction/src/main/java/org/apache/causeway/core/transaction/CausewayModuleCoreTransaction.java
+++ 
b/core/transaction/src/main/java/org/apache/causeway/core/transaction/CausewayModuleCoreTransaction.java
@@ -18,6 +18,9 @@
  */
 package org.apache.causeway.core.transaction;
 
+import 
org.apache.causeway.core.transaction.scope.NoopTransactionSynchronizationService;
+import 
org.apache.causeway.core.transaction.scope.TransactionScopeBeanFactoryPostProcessor;
+
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
@@ -27,6 +30,8 @@ import 
org.apache.causeway.core.transaction.changetracking.events.TimestampServi
 @Import({
         // @Service's
         TimestampService.class,
+        TransactionScopeBeanFactoryPostProcessor.class,
+        NoopTransactionSynchronizationService.class,
 
 })
 public class CausewayModuleCoreTransaction {
diff --git 
a/core/transaction/src/main/java/org/apache/causeway/core/transaction/scope/NoopTransactionSynchronizationService.java
 
b/core/transaction/src/main/java/org/apache/causeway/core/transaction/scope/NoopTransactionSynchronizationService.java
new file mode 100644
index 0000000000..afe9036eef
--- /dev/null
+++ 
b/core/transaction/src/main/java/org/apache/causeway/core/transaction/scope/NoopTransactionSynchronizationService.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2002-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.causeway.core.transaction.scope;
+
+import org.apache.causeway.applib.annotation.TransactionScope;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.support.TransactionSynchronization;
+
+/**
+ * This service, which does nothing in and of itself, exists in order to 
ensure that the {@link StackedTransactionScope}
+ * is always initialized, findinag at least one {@link TransactionScope 
transaction-scope}d service.
+ */
+@Service
+@TransactionScope
+public class NoopTransactionSynchronizationService implements 
TransactionSynchronization {
+}
diff --git 
a/core/interaction/src/main/java/org/apache/causeway/core/interaction/scope/StackedTransactionScope.java
 
b/core/transaction/src/main/java/org/apache/causeway/core/transaction/scope/StackedTransactionScope.java
similarity index 99%
rename from 
core/interaction/src/main/java/org/apache/causeway/core/interaction/scope/StackedTransactionScope.java
rename to 
core/transaction/src/main/java/org/apache/causeway/core/transaction/scope/StackedTransactionScope.java
index 80ca608509..d1a431fb9e 100644
--- 
a/core/interaction/src/main/java/org/apache/causeway/core/interaction/scope/StackedTransactionScope.java
+++ 
b/core/transaction/src/main/java/org/apache/causeway/core/transaction/scope/StackedTransactionScope.java
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2002-2020 the original author or authors.
  *
@@ -14,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.causeway.core.interaction.scope;
+package org.apache.causeway.core.transaction.scope;
 
 import java.util.HashMap;
 import java.util.LinkedHashMap;
diff --git 
a/core/interaction/src/main/java/org/apache/causeway/core/interaction/scope/TransactionScopeBeanFactoryPostProcessor.java
 
b/core/transaction/src/main/java/org/apache/causeway/core/transaction/scope/TransactionScopeBeanFactoryPostProcessor.java
similarity index 96%
rename from 
core/interaction/src/main/java/org/apache/causeway/core/interaction/scope/TransactionScopeBeanFactoryPostProcessor.java
rename to 
core/transaction/src/main/java/org/apache/causeway/core/transaction/scope/TransactionScopeBeanFactoryPostProcessor.java
index 51deb40f56..5e84f9c9c5 100644
--- 
a/core/interaction/src/main/java/org/apache/causeway/core/interaction/scope/TransactionScopeBeanFactoryPostProcessor.java
+++ 
b/core/transaction/src/main/java/org/apache/causeway/core/transaction/scope/TransactionScopeBeanFactoryPostProcessor.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.causeway.core.interaction.scope;
+package org.apache.causeway.core.transaction.scope;
 
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
diff --git 
a/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_Abstract_IntegTest.java
 
b/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_Abstract_IntegTest.java
index 8b29374849..4186610697 100644
--- 
a/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_Abstract_IntegTest.java
+++ 
b/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_Abstract_IntegTest.java
@@ -42,6 +42,8 @@ import 
org.springframework.transaction.PlatformTransactionManager;
 import org.springframework.transaction.TransactionDefinition;
 import org.springframework.transaction.TransactionException;
 import org.springframework.transaction.TransactionStatus;
+import 
org.springframework.transaction.support.AbstractPlatformTransactionManager;
+import org.springframework.transaction.support.DefaultTransactionStatus;
 
 public abstract class PdfjsViewer_Abstract_IntegTest extends 
CausewayIntegrationTestAbstract {
 
@@ -60,18 +62,25 @@ public abstract class PdfjsViewer_Abstract_IntegTest 
extends CausewayIntegration
         @Bean
         @Singleton
         public PlatformTransactionManager platformTransactionManager() {
-            return new PlatformTransactionManager() {
+            return new AbstractPlatformTransactionManager() {
                 @Override
-                public void rollback(final TransactionStatus status) throws 
TransactionException {
+                protected Object doGetTransaction() throws 
TransactionException {
+                    return new Object();
                 }
 
                 @Override
-                public TransactionStatus getTransaction(final 
TransactionDefinition definition) throws TransactionException {
-                    return null;
+                protected void doBegin(Object transaction, 
TransactionDefinition definition) throws TransactionException {
+
+                }
+
+                @Override
+                protected void doCommit(DefaultTransactionStatus status) 
throws TransactionException {
+
                 }
 
                 @Override
-                public void commit(final TransactionStatus status) throws 
TransactionException {
+                protected void doRollback(DefaultTransactionStatus status) 
throws TransactionException {
+
                 }
             };
         }
diff --git 
a/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingBadDomain_noAnnotationEnforced.java
 
b/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingBadDomain_noAnnotationEnforced.java
index 114a4be1cd..96bfa272c5 100644
--- 
a/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingBadDomain_noAnnotationEnforced.java
+++ 
b/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingBadDomain_noAnnotationEnforced.java
@@ -20,7 +20,12 @@ package org.apache.causeway.testdomain.domainmodel;
 
 import javax.inject.Inject;
 
+import 
org.apache.causeway.testing.integtestsupport.applib.CausewayIntegrationTestAbstract;
+import 
org.apache.causeway.testing.integtestsupport.applib.CausewayInteractionHandler;
+
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.TestPropertySource;
 
@@ -54,7 +59,7 @@ import lombok.val;
     CausewayPresets.SilenceMetaModel,
     CausewayPresets.SilenceProgrammingModel
 })
-class DomainModelTest_usingBadDomain_noAnnotationEnforced {
+class DomainModelTest_usingBadDomain_noAnnotationEnforced extends 
CausewayIntegrationTestAbstract {
 
     @Inject private CausewayConfiguration configuration;
     @Inject private CausewaySystemEnvironment causewaySystemEnvironment;
diff --git 
a/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
 
b/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
index fe2326bd9e..7912d46f55 100644
--- 
a/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
+++ 
b/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
@@ -24,6 +24,8 @@ import java.util.stream.Stream;
 
 import javax.inject.Inject;
 
+import 
org.apache.causeway.testing.integtestsupport.applib.CausewayIntegrationTestAbstract;
+
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
@@ -130,7 +132,7 @@ import lombok.val;
     CausewayPresets.SilenceMetaModel,
     CausewayPresets.SilenceProgrammingModel
 })
-class DomainModelTest_usingGoodDomain {
+class DomainModelTest_usingGoodDomain extends CausewayIntegrationTestAbstract {
 
     @Inject private MetaModelService metaModelService;
     @Inject private JaxbService jaxbService;
diff --git 
a/regressiontests/stable-layouts/src/test/java/org/apache/causeway/regressiontests/layouts/integtest/Layout_Counter_IntegTest.java
 
b/regressiontests/stable-layouts/src/test/java/org/apache/causeway/regressiontests/layouts/integtest/Layout_Counter_IntegTest.java
index 27b2a698a0..37fc232b26 100644
--- 
a/regressiontests/stable-layouts/src/test/java/org/apache/causeway/regressiontests/layouts/integtest/Layout_Counter_IntegTest.java
+++ 
b/regressiontests/stable-layouts/src/test/java/org/apache/causeway/regressiontests/layouts/integtest/Layout_Counter_IntegTest.java
@@ -70,6 +70,9 @@ import 
org.apache.causeway.security.bypass.CausewayModuleSecurityBypass;
 import 
org.apache.causeway.testing.integtestsupport.applib.CausewayIntegrationTestAbstract;
 import 
org.apache.causeway.viewer.wicket.applib.CausewayModuleViewerWicketApplibMixins;
 
+import 
org.springframework.transaction.support.AbstractPlatformTransactionManager;
+import org.springframework.transaction.support.DefaultTransactionStatus;
+
 import lombok.val;
 
 @SpringBootTest(
@@ -95,19 +98,26 @@ public class Layout_Counter_IntegTest extends 
CausewayIntegrationTestAbstract {
         @Bean
         @Singleton
         public PlatformTransactionManager platformTransactionManager() {
-            return new PlatformTransactionManager() {
+            return new AbstractPlatformTransactionManager() {
 
                 @Override
-                public void rollback(final TransactionStatus status) throws 
TransactionException {
+                protected Object doGetTransaction() throws 
TransactionException {
+                    return null;
                 }
 
                 @Override
-                public TransactionStatus getTransaction(final 
TransactionDefinition definition) throws TransactionException {
-                    return null;
+                protected void doBegin(Object transaction, 
TransactionDefinition definition) throws TransactionException {
+
                 }
 
                 @Override
-                public void commit(final TransactionStatus status) throws 
TransactionException {
+                protected void doCommit(DefaultTransactionStatus status) 
throws TransactionException {
+
+                }
+
+                @Override
+                protected void doRollback(DefaultTransactionStatus status) 
throws TransactionException {
+
                 }
             };
         }
diff --git 
a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/persistence/jdo/JdoExceptionTranslationTest.java
 
b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/persistence/jdo/JdoExceptionTranslationTest.java
index 59ac3c3c2a..2f31e0a0db 100644
--- 
a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/persistence/jdo/JdoExceptionTranslationTest.java
+++ 
b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/persistence/jdo/JdoExceptionTranslationTest.java
@@ -22,15 +22,16 @@ import java.sql.SQLException;
 
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.dao.DataAccessException;
-import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.TestPropertySources;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.dao.DataAccessException;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.TestPropertySources;
+
 import org.apache.causeway.core.config.presets.CausewayPresets;
 import org.apache.causeway.testdomain.conf.Configuration_usingJdo;
 import org.apache.causeway.testdomain.jdo.RegressionTestWithJdoFixtures;
@@ -49,7 +50,6 @@ import lombok.val;
 @TestPropertySources({
     @TestPropertySource(CausewayPresets.UseLog4j2Test)
 })
-//@Transactional ... we manage transaction ourselves
 class JdoExceptionTranslationTest extends RegressionTestWithJdoFixtures {
 
     @BeforeAll
diff --git 
a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/persistence/jdo/JdoJaxbTest.java
 
b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/persistence/jdo/JdoJaxbTest.java
index cf57499752..2ebefdc81a 100644
--- 
a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/persistence/jdo/JdoJaxbTest.java
+++ 
b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/persistence/jdo/JdoJaxbTest.java
@@ -20,6 +20,7 @@ package org.apache.causeway.testdomain.persistence.jdo;
 
 import javax.inject.Inject;
 
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.TestPropertySource;
@@ -45,6 +46,7 @@ class JdoJaxbTest extends RegressionTestWithJdoFixtures {
 
     @Inject private JaxbService jaxbService;
 
+    // @Disabled // CAUSEWAY-3654
     @Test
     void inventoryJaxbVm_shouldRoundtripProperly() {
 
diff --git 
a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/transactions/jdo/JdoTransactionScopeListenerTest.java
 
b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/transactions/jdo/JdoTransactionScopeListenerTest.java
deleted file mode 100644
index e0f6eb3b9d..0000000000
--- 
a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/causeway/testdomain/transactions/jdo/JdoTransactionScopeListenerTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.testdomain.transactions.jdo;
-
-import javax.inject.Inject;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.TestPropertySource;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import org.apache.causeway.applib.services.iactnlayer.InteractionService;
-import org.apache.causeway.applib.services.repository.RepositoryService;
-import org.apache.causeway.applib.services.xactn.TransactionService;
-import org.apache.causeway.core.config.presets.CausewayPresets;
-import org.apache.causeway.testdomain.conf.Configuration_usingJdo;
-import org.apache.causeway.testdomain.fixtures.EntityTestFixtures;
-import org.apache.causeway.testdomain.jdo.JdoTestFixtures;
-import org.apache.causeway.testdomain.jdo.entities.JdoBook;
-import 
org.apache.causeway.testdomain.util.interaction.InteractionBoundaryProbe;
-import org.apache.causeway.testdomain.util.kv.KVStoreForTesting;
-
-@SpringBootTest(
-        classes = {
-                Configuration_usingJdo.class,
-                InteractionBoundaryProbe.class
-        },
-        properties = {
-                
"spring.datasource.url=jdbc:h2:mem:JdoTransactionScopeListenerTest"
-        })
-//@Transactional
-@TestPropertySource(CausewayPresets.UseLog4j2Test)
-/**
- * With this test we manage CausewayInteractions ourselves. (not sub-classing 
CausewayIntegrationTestAbstract)
- */
-class JdoTransactionScopeListenerTest {
-
-    @Inject private JdoTestFixtures jdoTestFixtures;
-    @Inject private TransactionService transactionService;
-    @Inject private RepositoryService repository;
-    @Inject private InteractionService interactionService;
-    @Inject private KVStoreForTesting kvStoreForTesting;
-    private EntityTestFixtures.Lock lock;
-
-    /* Expectations:
-     * 1. for each InteractionScope there should be a new 
InteractionBoundaryProbe instance
-     * 2. for each Transaction the current InteractionBoundaryProbe should get 
notified
-     *
-     * first we have 1 InteractionScope with 1 expected Transaction during 
'setUp'
-     * then we have 1 InteractionScope with 3 expected Transactions within the 
test method
-     *
-     */
-
-    @BeforeEach
-    void setUp() {
-        // clear repository
-        lock = jdoTestFixtures.aquireLockAndClear();
-    }
-
-    @AfterEach
-    void cleanUp() {
-        // clear repository
-        lock.release();
-    }
-
-    @Test
-    void sessionScopedProbe_shouldBeReused_andBeAwareofTransactionBoundaries() 
{
-
-        assertEquals(0, 
InteractionBoundaryProbe.totalInteractionsStarted(kvStoreForTesting));
-        assertEquals(0, 
InteractionBoundaryProbe.totalInteractionsEnded(kvStoreForTesting));
-        assertEquals(0, 
InteractionBoundaryProbe.totalTransactionsEnding(kvStoreForTesting));
-        assertEquals(0, 
InteractionBoundaryProbe.totalTransactionsCommitted(kvStoreForTesting));
-
-        // new InteractionScope with a new transaction (#1)
-        interactionService.runAnonymous(()->{
-
-            // expected pre condition
-            // reuse transaction (#1)
-            assertEquals(0, repository.allInstances(JdoBook.class).size());
-
-            // reuse transaction (#1)
-            transactionService.runWithinCurrentTransactionElseCreateNew(()->{
-                // + 1 interaction + 1 transaction
-                jdoTestFixtures.add3Books();
-            })
-            .ifFailureFail();
-
-            // expected post condition
-            // reuse transaction (#1)
-            assertEquals(3, repository.allInstances(JdoBook.class).size());
-
-        });
-
-        assertEquals(1, 
InteractionBoundaryProbe.totalInteractionsStarted(kvStoreForTesting));
-        assertEquals(1, 
InteractionBoundaryProbe.totalInteractionsEnded(kvStoreForTesting));
-        assertEquals(1, 
InteractionBoundaryProbe.totalTransactionsEnding(kvStoreForTesting));
-        assertEquals(1, 
InteractionBoundaryProbe.totalTransactionsCommitted(kvStoreForTesting));
-
-    }
-
-
-}
diff --git 
a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/persistence/jpa/JpaExceptionTranslationTest.java
 
b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/persistence/jpa/JpaExceptionTranslationTest.java
index ed487ed662..f8afe0c294 100644
--- 
a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/persistence/jpa/JpaExceptionTranslationTest.java
+++ 
b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/persistence/jpa/JpaExceptionTranslationTest.java
@@ -22,15 +22,17 @@ import java.sql.SQLException;
 
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.dao.DataAccessException;
-import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.TestPropertySources;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.TestPropertySources;
+
 import org.apache.causeway.core.config.presets.CausewayPresets;
 import org.apache.causeway.testdomain.conf.Configuration_usingJpa;
 import org.apache.causeway.testdomain.jpa.RegressionTestWithJpaFixtures;
@@ -44,12 +46,11 @@ import lombok.val;
         },
         properties = {
                 
"spring.datasource.url=jdbc:h2:mem:JpaExceptionTranslationTest",
-        })
+        }
+)
 @TestPropertySources({
     @TestPropertySource(CausewayPresets.UseLog4j2Test)
 })
-//@Transactional ... we manage transaction ourselves
-//@DirtiesContext
 class JpaExceptionTranslationTest extends RegressionTestWithJpaFixtures {
 
     @BeforeAll
@@ -61,7 +62,6 @@ class JpaExceptionTranslationTest extends 
RegressionTestWithJpaFixtures {
     @Test
     void booksUniqueByIsbn_whenViolated_shouldThrowTranslatedException() {
 
-
         // when adding a book for which one with same ISBN already exists in 
the database,
         // we expect to see a Spring recognized DataAccessException been thrown
 
@@ -102,7 +102,5 @@ class JpaExceptionTranslationTest extends 
RegressionTestWithJpaFixtures {
             testFixtures.assertInventoryHasBooks(inventory.getProducts(), 1, 
2, 3);
         });
 
-
     }
-
 }
diff --git 
a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/CommitListener.java
 
b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/CommitListener.java
new file mode 100644
index 0000000000..804b372519
--- /dev/null
+++ 
b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/CommitListener.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.testdomain.transactions.jpa;
+
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+import org.apache.causeway.applib.annotation.TransactionScope;
+import org.apache.causeway.applib.annotation.Value;
+import org.apache.causeway.core.transaction.events.TransactionCompletionStatus;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.support.TransactionSynchronization;
+
+@Service
+@TransactionScope
+public class CommitListener implements TransactionSynchronization {
+
+
+    @Value
+    @RequiredArgsConstructor
+    public static class TransactionAfterCompletionEvent {
+        final TransactionCompletionStatus transactionCompletionStatus;
+    }
+
+//        /** transaction end boundary (pre) */
+//        @EventListener(TransactionBeforeCompletionEvent.class)
+//        public void onPreCompletion(final TransactionBeforeCompletionEvent 
event) {
+//            //_Probe.errOut("=== TRANSACTION before completion");
+//        }
+
+
+//        /** transaction end boundary (post) */
+//        @EventListener(TransactionAfterCompletionEvent.class)
+//        public void onPostCompletion(final TransactionAfterCompletionEvent 
event) {...}
+
+    @Override
+    public void afterCompletion(int status) {
+        TransactionCompletionStatus transactionCompletionStatus = 
TransactionCompletionStatus.forStatus(status);
+        TransactionAfterCompletionEvent event = new 
TransactionAfterCompletionEvent(transactionCompletionStatus);
+        //_Probe.errOut("=== TRANSACTION after completion (%s)", event.name());
+        Optional.ofNullable(listener)
+                .ifPresent(li -> {
+                    li.accept(event);
+                    unbind();
+                });
+    }
+
+    private Consumer<TransactionAfterCompletionEvent> listener;
+
+    void bind(final @NonNull Consumer<TransactionAfterCompletionEvent> 
listener) {
+        this.listener = listener;
+    }
+
+    void unbind() {
+        this.listener = null;
+    }
+
+}
diff --git 
a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/JpaTransactionRollbackTest_usingInteractionService.java
 
b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/JpaTransactionRollbackTest_usingInteractionService.java
index fb80bc4358..e597e07e72 100644
--- 
a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/JpaTransactionRollbackTest_usingInteractionService.java
+++ 
b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/JpaTransactionRollbackTest_usingInteractionService.java
@@ -23,6 +23,10 @@ import java.util.function.Consumer;
 
 import javax.inject.Inject;
 
+import org.apache.causeway.applib.annotation.TransactionScope;
+
+import org.apache.causeway.core.transaction.events.TransactionCompletionStatus;
+
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -49,13 +53,15 @@ import org.apache.causeway.testdomain.jpa.entities.JpaBook;
 import 
org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScripts;
 import 
org.apache.causeway.testing.integtestsupport.applib.CausewayInteractionHandler;
 
+import org.springframework.transaction.support.TransactionSynchronization;
+
 import lombok.NonNull;
 import lombok.val;
 
 @SpringBootTest(
         classes = {
                 Configuration_usingJpa.class,
-                
JpaTransactionRollbackTest_usingInteractionService.CommitListener.class
+                CommitListener.class
         },
         properties = {
                   
"spring.datasource.url=jdbc:h2:mem:JpaTransactionRollbackTest_usingInteractionService",
@@ -76,7 +82,7 @@ class JpaTransactionRollbackTest_usingInteractionService
     @Inject private RepositoryService repository;
     @Inject private CommitListener commitListener;
 
-    private ObjectReference<TransactionAfterCompletionEvent> 
transactionAfterCompletionEvent;
+    private ObjectReference<CommitListener.TransactionAfterCompletionEvent> 
transactionAfterCompletionEvent;
 
     @BeforeEach
     void setUp() {
@@ -84,8 +90,7 @@ class JpaTransactionRollbackTest_usingInteractionService
         // cleanup
         fixtureScripts.runPersona(JpaTestDomainPersona.InventoryPurgeAll);
 
-        transactionAfterCompletionEvent =
-                _Refs.<TransactionAfterCompletionEvent>objectRef(null);
+        transactionAfterCompletionEvent = _Refs.objectRef(null);
     }
 
     @AfterEach
@@ -109,8 +114,8 @@ class JpaTransactionRollbackTest_usingInteractionService
         });
 
         assertEquals(
-                TransactionAfterCompletionEvent.COMMITTED,
-                transactionAfterCompletionEvent.getValueElseDefault(null));
+                TransactionCompletionStatus.COMMITTED,
+                transactionAfterCompletionEvent.getValue().map(x -> 
x.transactionCompletionStatus).orElse(null));
 
         transactionService.runWithinCurrentTransactionElseCreateNew(()->{
 
@@ -144,8 +149,8 @@ class JpaTransactionRollbackTest_usingInteractionService
 
         assertTrue(result.isFailure());
         assertEquals(
-                TransactionAfterCompletionEvent.ROLLED_BACK,
-                transactionAfterCompletionEvent.getValueElseDefault(null));
+                TransactionCompletionStatus.ROLLED_BACK,
+                transactionAfterCompletionEvent.getValue().map(x -> 
x.transactionCompletionStatus).orElse(null));
 
         transactionService.runWithinCurrentTransactionElseCreateNew(()->{
 
@@ -190,9 +195,9 @@ class JpaTransactionRollbackTest_usingInteractionService
         // interactionService detects whether a rollback was requested and 
does not throw in such a case
         assertTrue(result.isSuccess());
 
-        val actualEvent = 
transactionAfterCompletionEvent.getValueElseDefault(null);
-        assertTrue(
-                actualEvent == TransactionAfterCompletionEvent.ROLLED_BACK);
+        assertEquals(
+                TransactionCompletionStatus.ROLLED_BACK,
+                transactionAfterCompletionEvent.getValue().map(x -> 
x.transactionCompletionStatus).orElse(null));
 
         transactionService.runWithinCurrentTransactionElseCreateNew(()->{
 
@@ -204,37 +209,4 @@ class JpaTransactionRollbackTest_usingInteractionService
 
     // -- HELPER
 
-    @Service
-    public static class CommitListener {
-
-        /** transaction end boundary (pre) */
-        @EventListener(TransactionBeforeCompletionEvent.class)
-        public void onPreCompletion(final TransactionBeforeCompletionEvent 
event) {
-            //_Probe.errOut("=== TRANSACTION before completion");
-        }
-
-        /** transaction end boundary (post) */
-        @EventListener(TransactionAfterCompletionEvent.class)
-        public void onPostCompletion(final TransactionAfterCompletionEvent 
event) {
-            //_Probe.errOut("=== TRANSACTION after completion (%s)", 
event.name());
-            Optional.ofNullable(listener)
-            .ifPresent(li->{
-                li.accept(event);
-                unbind();
-            });
-        }
-
-        private Consumer<TransactionAfterCompletionEvent> listener;
-
-        void bind(final @NonNull Consumer<TransactionAfterCompletionEvent> 
listener) {
-            this.listener = listener;
-        }
-
-        void unbind() {
-            this.listener = null;
-        }
-
-    }
-
-
 }
diff --git 
a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/JpaTransactionRollbackTest_usingTransactionService.java
 
b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/JpaTransactionRollbackTest_usingTransactionService.java
index 6d295dd39e..1c3af590a9 100644
--- 
a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/JpaTransactionRollbackTest_usingTransactionService.java
+++ 
b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/causeway/testdomain/transactions/jpa/JpaTransactionRollbackTest_usingTransactionService.java
@@ -18,19 +18,18 @@
  */
 package org.apache.causeway.testdomain.transactions.jpa;
 
-import java.util.Optional;
-import java.util.function.Consumer;
-
 import javax.inject.Inject;
 
+import org.apache.causeway.commons.internal.base._Refs;
+
+import org.apache.causeway.core.transaction.events.TransactionCompletionStatus;
+
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.event.EventListener;
-import org.springframework.stereotype.Service;
 import org.springframework.test.annotation.DirtiesContext;
 import org.springframework.test.context.TestPropertySource;
 
@@ -39,8 +38,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.apache.causeway.applib.services.repository.RepositoryService;
 import org.apache.causeway.applib.services.xactn.TransactionService;
-import org.apache.causeway.commons.internal.base._Refs;
-import org.apache.causeway.commons.internal.base._Refs.ObjectReference;
 import org.apache.causeway.core.config.presets.CausewayPresets;
 import org.apache.causeway.testdomain.conf.Configuration_usingJpa;
 import org.apache.causeway.testdomain.jpa.JpaTestDomainPersona;
@@ -48,13 +45,12 @@ import org.apache.causeway.testdomain.jpa.entities.JpaBook;
 import 
org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScripts;
 import 
org.apache.causeway.testing.integtestsupport.applib.CausewayInteractionHandler;
 
-import lombok.NonNull;
 import lombok.val;
 
 @SpringBootTest(
         classes = {
                 Configuration_usingJpa.class,
-                
JpaTransactionRollbackTest_usingTransactionService.CommitListener.class
+                CommitListener.class
         },
         properties = {
                 
"spring.datasource.url=jdbc:h2:mem:JpaTransactionRollbackTest_usingTransactionService",
@@ -74,7 +70,7 @@ class JpaTransactionRollbackTest_usingTransactionService
     @Inject private RepositoryService repository;
     @Inject private CommitListener commitListener;
 
-    private ObjectReference<TransactionAfterCompletionEvent> 
transactionAfterCompletionEvent;
+    private 
_Refs.ObjectReference<CommitListener.TransactionAfterCompletionEvent> 
transactionAfterCompletionEvent;
 
     @BeforeEach
     void setUp() {
@@ -82,8 +78,7 @@ class JpaTransactionRollbackTest_usingTransactionService
         // cleanup
         fixtureScripts.runPersona(JpaTestDomainPersona.InventoryPurgeAll);
 
-        transactionAfterCompletionEvent =
-                _Refs.<TransactionAfterCompletionEvent>objectRef(null);
+        transactionAfterCompletionEvent = _Refs.objectRef(null);
     }
 
     @AfterEach
@@ -107,8 +102,8 @@ class JpaTransactionRollbackTest_usingTransactionService
         });
 
         assertEquals(
-                TransactionAfterCompletionEvent.COMMITTED,
-                transactionAfterCompletionEvent.getValueElseDefault(null));
+                TransactionCompletionStatus.COMMITTED,
+                transactionAfterCompletionEvent.getValue().map(x -> 
x.transactionCompletionStatus).orElse(null));
 
         transactionService.runWithinCurrentTransactionElseCreateNew(()->{
 
@@ -142,8 +137,8 @@ class JpaTransactionRollbackTest_usingTransactionService
 
         assertTrue(result.isFailure());
         assertEquals(
-                TransactionAfterCompletionEvent.ROLLED_BACK,
-                transactionAfterCompletionEvent.getValueElseDefault(null));
+                TransactionCompletionStatus.ROLLED_BACK,
+                transactionAfterCompletionEvent.getValue().map(x -> 
x.transactionCompletionStatus).orElse(null));
 
         transactionService.runWithinCurrentTransactionElseCreateNew(()->{
 
@@ -187,10 +182,10 @@ class JpaTransactionRollbackTest_usingTransactionService
 
         assertTrue(result.isFailure());
 
-        val actualEvent = 
transactionAfterCompletionEvent.getValueElseDefault(null);
+        val actualEvent = transactionAfterCompletionEvent.getValue().map(x -> 
x.transactionCompletionStatus).orElse(null);
         assertTrue(
-                actualEvent == TransactionAfterCompletionEvent.ROLLED_BACK
-                    || actualEvent == TransactionAfterCompletionEvent.UNKNOWN);
+                actualEvent == TransactionCompletionStatus.ROLLED_BACK
+                    || actualEvent == TransactionCompletionStatus.UNKNOWN);
 
         transactionService.runWithinCurrentTransactionElseCreateNew(()->{
 
@@ -202,37 +197,5 @@ class JpaTransactionRollbackTest_usingTransactionService
 
     // -- HELPER
 
-    @Service
-    public static class CommitListener {
-
-        /** transaction end boundary (pre) */
-        @EventListener(TransactionBeforeCompletionEvent.class)
-        public void onPreCompletion(final TransactionBeforeCompletionEvent 
event) {
-            //_Probe.errOut("=== TRANSACTION before completion");
-        }
-
-        /** transaction end boundary (post) */
-        @EventListener(TransactionAfterCompletionEvent.class)
-        public void onPostCompletion(final TransactionAfterCompletionEvent 
event) {
-            //_Probe.errOut("=== TRANSACTION after completion (%s)", 
event.name());
-            Optional.ofNullable(listener)
-            .ifPresent(li->{
-                li.accept(event);
-                unbind();
-            });
-        }
-
-        private Consumer<TransactionAfterCompletionEvent> listener;
-
-        void bind(final @NonNull Consumer<TransactionAfterCompletionEvent> 
listener) {
-            this.listener = listener;
-        }
-
-        void unbind() {
-            this.listener = null;
-        }
-
-    }
-
 
 }
diff --git 
a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/RegressionTestAbstract.java
 
b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/RegressionTestAbstract.java
index 43f0fa1d31..1c6a3dd564 100644
--- 
a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/RegressionTestAbstract.java
+++ 
b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/RegressionTestAbstract.java
@@ -22,6 +22,8 @@ import java.util.concurrent.Callable;
 
 import javax.inject.Inject;
 
+import 
org.apache.causeway.testing.integtestsupport.applib.CausewayIntegrationTestAbstract;
+
 import org.springframework.transaction.annotation.Propagation;
 
 import org.apache.causeway.applib.services.bookmark.BookmarkService;
@@ -40,16 +42,15 @@ import 
org.apache.causeway.core.metamodel.objectmanager.ObjectManager;
 public abstract class RegressionTestAbstract {
 
     protected void run(final ThrowingRunnable runnable) {
-        transactionService.runTransactional(Propagation.REQUIRES_NEW, ()->
-            interactionService.runAnonymous(runnable))
-        .ifFailureFail();
+        interactionService.runAnonymous(() ->
+            transactionService.runTransactional(Propagation.REQUIRES_NEW, 
runnable).ifFailureFail()
+        );
     }
 
     protected <T> T call(final Callable<T> callable) {
-        return transactionService.callTransactional(Propagation.REQUIRES_NEW, 
()->
-            interactionService.callAnonymous(callable))
-        // assuming return value of callable is not nullable
-        .valueAsNonNullElseFail();
+        return interactionService.callAnonymous(() ->
+                transactionService.callTransactional(Propagation.REQUIRES_NEW, 
callable))
+            .valueAsNonNullElseFail();
     }
 
     // -- ASSERTIONS
diff --git 
a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/conf/Configuration_headless.java
 
b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/conf/Configuration_headless.java
index 29b93681f6..2378fd7e87 100644
--- 
a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/conf/Configuration_headless.java
+++ 
b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/conf/Configuration_headless.java
@@ -21,6 +21,8 @@ package org.apache.causeway.testdomain.conf;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import org.apache.causeway.applib.annotation.TransactionScope;
+
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
@@ -41,13 +43,16 @@ import 
org.apache.causeway.security.bypass.CausewayModuleSecurityBypass;
 import 
org.apache.causeway.testdomain.util.interaction.DomainObjectTesterFactory;
 import org.apache.causeway.testdomain.util.kv.KVStoreForTesting;
 
+import 
org.springframework.transaction.support.AbstractPlatformTransactionManager;
+import org.springframework.transaction.support.DefaultTransactionStatus;
+import org.springframework.transaction.support.TransactionSynchronization;
+
 import lombok.RequiredArgsConstructor;
 
 @Configuration
 @Import({
     CausewayModuleCoreRuntimeServices.class,
     CausewayModuleSecurityBypass.class,
-    Configuration_headless.HeadlessCommandSupport.class,
     KVStoreForTesting.class, // Helper for JUnit Tests
     DomainObjectTesterFactory.class // Helper for JUnit Tests
 })
@@ -56,53 +61,29 @@ import lombok.RequiredArgsConstructor;
 })
 public class Configuration_headless {
 
-    @Service
-    @javax.annotation.Priority(PriorityPrecedence.MIDPOINT)
-    @RequiredArgsConstructor(onConstructor_ = {@Inject})
-    public static class HeadlessCommandSupport
-    implements TransactionBoundaryAware {
-
-        @Override
-        public void beforeEnteringTransactionalBoundary(final Interaction 
interaction) {
-//            _Probe.errOut("Interaction HAS_STARTED conversationId=%s", 
interaction.getInteractionId());
-            setupCommandCreateIfMissing();
-        }
-
-        @Override
-        public void afterLeavingTransactionalBoundary(final Interaction 
interaction) {
-//            _Probe.errOut("Interaction IS_ENDING conversationId=%s", 
interaction.getInteractionId());
-        }
-
-        public void setupCommandCreateIfMissing() {
-
-//            val interactionProvider = interactionProviderProvider.get();
-//            @SuppressWarnings("unused")
-//            final Interaction interaction = 
Optional.ofNullable(interactionContext.getInteraction())
-//                    .orElseGet(()->{
-//                        val newCommand = new Command();
-//                        val newInteraction = new Interaction(newCommand);
-//                        interactionProvider.setInteraction(newInteraction);
-//                        return newInteraction;
-//                    });
-        }
-
-    }
 
     @Bean @Singleton
     public PlatformTransactionManager platformTransactionManager() {
-        return new PlatformTransactionManager() {
+        return new AbstractPlatformTransactionManager() {
 
             @Override
-            public void rollback(final TransactionStatus status) throws 
TransactionException {
+            protected Object doGetTransaction() throws TransactionException {
+                return null;
             }
 
             @Override
-            public TransactionStatus getTransaction(final 
TransactionDefinition definition) throws TransactionException {
-                return null;
+            protected void doBegin(Object transaction, TransactionDefinition 
definition) throws TransactionException {
+
+            }
+
+            @Override
+            protected void doCommit(DefaultTransactionStatus status) throws 
TransactionException {
+
             }
 
             @Override
-            public void commit(final TransactionStatus status) throws 
TransactionException {
+            protected void doRollback(DefaultTransactionStatus status) throws 
TransactionException {
+
             }
         };
     }
diff --git 
a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/publishing/PublishingTestFactoryAbstract.java
 
b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/publishing/PublishingTestFactoryAbstract.java
index 46e344f5f2..3a21164369 100644
--- 
a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/publishing/PublishingTestFactoryAbstract.java
+++ 
b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/publishing/PublishingTestFactoryAbstract.java
@@ -24,6 +24,10 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 import java.util.function.BiConsumer;
 
+import org.apache.causeway.applib.annotation.TransactionScope;
+
+import org.apache.causeway.core.transaction.events.TransactionCompletionStatus;
+
 import org.junit.jupiter.api.DynamicTest;
 import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Service;
@@ -47,6 +51,8 @@ import 
org.apache.causeway.commons.internal.debug.xray.XrayModel.Stickiness;
 import org.apache.causeway.commons.internal.debug.xray.XrayModel.ThreadMemento;
 import org.apache.causeway.commons.internal.debug.xray.XrayUi;
 
+import org.springframework.transaction.support.TransactionSynchronization;
+
 import lombok.Getter;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
@@ -158,13 +164,17 @@ public abstract class PublishingTestFactoryAbstract {
      * to, until the end of the test's exclusive transaction.
      */
     @Service
-    public static class CommitListener {
+    @TransactionScope
+    public static class CommitListener implements TransactionSynchronization {
 
         private PublishingTestContext testContext;
 
-        /** transaction end boundary (pre) */
-        @EventListener(TransactionBeforeCompletionEvent.class)
-        public void onPreCompletion(final TransactionBeforeCompletionEvent 
event) {
+//        /** transaction end boundary (pre) */
+//      @EventListener(TransactionBeforeCompletionEvent.class)
+//      public void onPreCompletion(final TransactionBeforeCompletionEvent 
event) {
+
+        @Override
+        public void beforeCompletion() {
             _Probe.errOut("=== TRANSACTION before completion");
             if(testContext!=null) {
                 testContext.getTraceLog().log("3.1 pre-commit event is 
occurring");
@@ -172,13 +182,18 @@ public abstract class PublishingTestFactoryAbstract {
             }
         }
 
-        /** transaction end boundary (post) */
-        @EventListener(TransactionAfterCompletionEvent.class)
-        public void onPostCompletion(final TransactionAfterCompletionEvent 
event) {
+//        /** transaction end boundary (post) */
+//        @EventListener(TransactionAfterCompletionEvent.class)
+//        public void onPostCompletion(final TransactionAfterCompletionEvent 
event) {
+
+        @Override
+        public void afterCompletion(int status) {
+
+            TransactionCompletionStatus transactionCompletionStatus = 
TransactionCompletionStatus.forStatus(status);
             _Probe.errOut("=== TRANSACTION after completion");
             if(testContext!=null) {
                 try {
-                    if(event.isCommitted()) {
+                    if(transactionCompletionStatus.isCommitted()) {
                         testContext.getTraceLog().log("3.2 post-commit event 
is occurring");
                         testContext.runVerify(VerificationStage.POST_COMMIT);
                     } else {
diff --git 
a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/util/interaction/InteractionBoundaryProbe.java
 
b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/util/interaction/InteractionBoundaryProbe.java
index 33c97b25e1..b097e181e1 100644
--- 
a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/util/interaction/InteractionBoundaryProbe.java
+++ 
b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/util/interaction/InteractionBoundaryProbe.java
@@ -22,6 +22,10 @@ import java.util.function.Supplier;
 
 import javax.inject.Inject;
 
+import org.apache.causeway.applib.annotation.TransactionScope;
+
+import org.apache.causeway.core.transaction.events.TransactionCompletionStatus;
+
 import org.junit.jupiter.api.Assertions;
 import org.springframework.context.event.EventListener;
 import org.springframework.core.annotation.Order;
@@ -31,42 +35,56 @@ import 
org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.services.iactn.Interaction;
 import org.apache.causeway.testdomain.util.kv.KVStoreForTesting;
 
+import org.springframework.transaction.support.TransactionSynchronization;
+
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
 @Service
+@TransactionScope
 @Log4j2
-public class InteractionBoundaryProbe implements TransactionBoundaryAware {
+public class InteractionBoundaryProbe implements TransactionSynchronization {
 
     @Inject private KVStoreForTesting kvStoreForTesting;
 
-    /** INTERACTION BEGIN BOUNDARY */
-    @Override
-    public void beforeEnteringTransactionalBoundary(Interaction interaction) {
-        log.debug("iaStarted");
-        kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, 
"iaStarted");
-    }
+//    /** INTERACTION BEGIN BOUNDARY */
+//    @Override
+//    public void beforeEnteringTransactionalBoundary(Interaction interaction) 
{
+//        log.debug("iaStarted");
+//        kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, 
"iaStarted");
+//    }
+
+//    /** INTERACTION END BOUNDARY */
+//    @Override
+//    public void afterLeavingTransactionalBoundary(Interaction interaction) {
+//        log.debug("iaEnded");
+//        kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, 
"iaEnded");
+//    }
+
+
+//    /** TRANSACTION BEGIN BOUNDARY */
+//    @EventListener(TransactionBeforeCompletionEvent.class) 
@Order(PriorityPrecedence.FIRST + 100)
+//    public void onTransactionEnding(TransactionBeforeCompletionEvent event) {
 
-    /** INTERACTION END BOUNDARY */
     @Override
-    public void afterLeavingTransactionalBoundary(Interaction interaction) {
-        log.debug("iaEnded");
-        kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, 
"iaEnded");
-    }
+    public void beforeCompletion() {
+        TransactionSynchronization.super.beforeCompletion();
 
-    /** TRANSACTION BEGIN BOUNDARY */
-    @EventListener(TransactionBeforeCompletionEvent.class) 
@Order(PriorityPrecedence.FIRST + 100)
-    public void onTransactionEnding(TransactionBeforeCompletionEvent event) {
         log.debug("txStarted");
         kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, 
"txEnding");
     }
 
-    /** TRANSACTION END BOUNDARY */
-    @EventListener(TransactionAfterCompletionEvent.class) 
@Order(PriorityPrecedence.LAST - 100)
-    public void onTransactionEnded(TransactionAfterCompletionEvent event) {
-        if(event.isRolledBack()) {
+//    /** TRANSACTION END BOUNDARY */
+//    @EventListener(TransactionAfterCompletionEvent.class) 
@Order(PriorityPrecedence.LAST - 100)
+//    public void onTransactionEnded(TransactionAfterCompletionEvent event) {
+
+    @Override
+    public void afterCompletion(int status) {
+        TransactionCompletionStatus transactionCompletionStatus = 
TransactionCompletionStatus.forStatus(status);
+
+        if(transactionCompletionStatus.isRolledBack()) {
             kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, 
"txRolledBack");
-        } else if(event.isCommitted()) {
+        } else if(transactionCompletionStatus.isCommitted()) {
             kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, 
"txCommitted");
         }
     }
@@ -130,5 +148,4 @@ public class InteractionBoundaryProbe implements 
TransactionBoundaryAware {
         return result;
     }
 
-
 }

Reply via email to