You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2017/12/21 17:38:30 UTC

[isis] 02/03: ISIS-1799: adds TransactionService3#nextTransaction(Policy) overload, and uses from integration tests (HeadlessWithBootstrappingAbstract)

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

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

commit 44002c890324dfe98c505a56aef07c76a40aa459
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Dec 21 15:32:00 2017 +0000

    ISIS-1799: adds TransactionService3#nextTransaction(Policy) overload, and uses from integration tests (HeadlessWithBootstrappingAbstract)
---
 ...c_application-layer-api_TransactionService.adoc | 59 +++++++++++------
 .../isis/applib/services/xactn/Transaction.java    |  9 ++-
 ...{TransactionService2.java => Transaction2.java} |  2 +-
 .../applib/services/xactn/TransactionService.java  |  2 +-
 .../applib/services/xactn/TransactionService2.java |  4 ++
 .../applib/services/xactn/TransactionService3.java | 74 ++++++++++++++++++++++
 .../PersistenceSessionServiceInternal.java         |  7 +-
 .../PersistenceSessionServiceInternalNoop.java     |  9 ++-
 .../services/xactn/TransactionServiceDefault.java  | 39 ++++++++++--
 .../core/runtime/headless/HeadlessAbstract.java    |  6 +-
 .../HeadlessWithBootstrappingAbstract.java         |  3 +-
 .../PersistenceSessionServiceInternalDefault.java  | 11 +++-
 .../system/transaction/IsisTransaction.java        | 25 ++++++--
 .../IsisComponentProvider.java                     |  8 ++-
 14 files changed, 212 insertions(+), 46 deletions(-)

diff --git a/adocs/documentation/src/main/asciidoc/guides/rgsvc/_rgsvc_application-layer-api_TransactionService.adoc b/adocs/documentation/src/main/asciidoc/guides/rgsvc/_rgsvc_application-layer-api_TransactionService.adoc
index 1eaddc8..d633c56 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgsvc/_rgsvc_application-layer-api_TransactionService.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgsvc/_rgsvc_application-layer-api_TransactionService.adoc
@@ -1,11 +1,11 @@
 [[_rgsvc_application-layer-api_TransactionService]]
-= `TransactionService2`
+= `TransactionService3` (`1.16.0-SNAPSHOT`)
 :Notice: 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 ag [...]
 :_basedir: ../../
 :_imagesdir: images/
 
 
-The `TransactionService2` provides a small number of methods to allow domain objects to influence user transactions.
+The `TransactionService3` (`1.16.0-SNAPSHOT`) provides a small number of methods to allow domain objects to influence user transactions.
 
 [NOTE]
 ====
@@ -15,33 +15,36 @@ The methods in this service replace similar methods (now deprecated) in xref:../
 
 == API
 
-The API of `TransactionService2` is:
+The API of `TransactionService3` is:
 
 [source,java]
 ----
-public interface TransactionService {
-    Transaction currentTransaction();       // <1>
+public interface TransactionService3 {
+    Transaction2 currentTransaction();      // <1>
     void nextTransaction();                 // <2>
-    void flushTransaction();                // <3>
-    TransactionState getTransactionState(); // <4>
+    void nextTransaction(Policy policy);    // <3>
+    void flushTransaction();                // <4>
+    TransactionState getTransactionState(); // <5>
 }
 ----
 <1> to obtain a handle on the current `Transaction`, discussed further below
-<2> The framework automatically start a transaction before each user interaction (action invocation or property edit), and will commit that transaction after the interaction has completed.  Under certain circumstances (eg actions used to perform data migration, say, or for large fixture scripts), it can be helpful to programmatically complete one transaction and start another one.
-<3> If the user interaction creates/persists an object or deletes an object (eg using the
+<2> The framework automatically start a transaction before each user interaction (action invocation or property edit), and will commit that transaction after the interaction has completed.
+Under certain circumstances (eg actions used to perform data migration, say, or for large fixture scripts), it can be helpful to programmatically complete one transaction and start another one.
+<3> overload of nextTransaction() that provides more control on the action to be performed if the current transaction has been marked for abort only
+<4> If the user interaction creates/persists an object or deletes an object (eg using the
 xref:../rgsvc/rgsvc.adoc#_rgsvc_persistence-layer-api_RepositoryService[`RepositoryService`]'s `persist()` or `delete()` methods), then the framework actually queues up the work and only performs the persistence command either at the end of the transaction or immediately prior to the next query.
 Performing a flush will cause any pending calls to be performed immediately.
-<4> the state of the current or most recently completed transaction.
+<5> the state of the current or most recently completed transaction.
+
 
 Here `TransactionState` is an enum defined as:
 
 [source,java]
 ----
 public enum TransactionState {
-    NONE,       // <1>
+    NONE,           // <1>
     IN_PROGRESS,    // <2>
     MUST_ABORT,     // <3>
-    /**
     COMMITTED,      // <4>
     ABORTED;        // <5>
 }
@@ -56,24 +59,40 @@ May not flush or abort or commit.
 <5> Completed, having aborted.
 Again, may not flush or abort or commit.
 
-The `nextTransaction()` is also used by the xref:../ugvw/ugvw.adoc#[Wicket viewer]'s support for bulk actions; each action is invoked in its own transaction.
 
-The `Transaction` object - as obtained by `currentTransaction()` method, above - is a minimal wrapper around the underlying database transaction.  Its API is:
+As noted above, `nextTransaction()` can be useful for actions used to perform data migration, say, or for large fixture scripts.
+It is also used by the xref:../ugvw/ugvw.adoc#[Wicket viewer]'s support for bulk actions; each action is invoked in its own transaction.
+An overload of this method takes a `Policy` enum, defined as:
+
+[source,java]
+----
+public enum Policy {
+    UNLESS_MARKED_FOR_ABORT,
+    ALWAYS
+}
+----
+
+If the current transaction has been marked for abort, then the `Policy.UNLESS_MARKED_FOR_ABORT` will escalate to a runtime exception, that is, will fail fast.
+Specifying `Policy.ALWAYS` is provided for use by integration tests so that they can continue on with the test teardown even if the test caused an issue.
+
+The `Transaction2` object - as obtained by `currentTransaction()` method, above - is a minimal wrapper around the underlying database transaction.  Its API is:
 
 [source,java]
 ----
-public interface Transaction {
-    UUID getTransactionId();            // <1>
-    int getSequence();                  // <2>
-    void flush();                       // <3>
-    void clearAbortCause();             // <4>
+public interface Transaction2 {
+    UUID getTransactionId();                    // <1>
+    int getSequence();                          // <2>
+    void flush();                               // <3>
+    TransactionState getTransactionState();     // <4>
+    void clearAbortCause();                     // <5>
 }
 ----
 <1> is a unique identifier for the interaction/request, as defined by the
 xref:../rgcms/rgcms.adoc#_rgcms_classes_mixins_HasTransactionId[`HasTransactionId`] mixin.
 <2> there can actually be multiple transactions within such a request/interaction; the sequence is a (0-based) is used to distinguish such.
 <3> as per `TransactionService#flushTransaction()` described above.
-<4> If the cause has been rendered higher up in the stack, then clear the cause so that it won't be picked up and rendered elsewhere.
+<4> The state of this transaction (same as `TransactionService#getTransactionState()`).
+<5> (For framework use only) If the cause has been rendered higher up in the stack, then clear the cause so that it won't be picked up and rendered elsewhere.
 
 [TIP]
 ====
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/xactn/Transaction.java b/core/applib/src/main/java/org/apache/isis/applib/services/xactn/Transaction.java
index d95a044..d3dc95a 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/xactn/Transaction.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/xactn/Transaction.java
@@ -22,8 +22,6 @@ package org.apache.isis.applib.services.xactn;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.HasTransactionId;
 
-import java.util.UUID;
-
 /**
  * Representation of the current transaction, which conceptually wraps the underlying objectstore transaction.
  */
@@ -56,7 +54,14 @@ public interface Transaction extends HasTransactionId {
     /**
      * If the cause has been rendered higher up in the stack, then clear the cause so that
      * it won't be picked up and rendered elsewhere.
+     *
+     * <p>
+     *     DO NOT CALL - for framework use only.
+     * </p>
+     *
+     * @deprecated - for framework use only
      */
+    @Deprecated
     @Programmatic
     void clearAbortCause();
 
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService2.java b/core/applib/src/main/java/org/apache/isis/applib/services/xactn/Transaction2.java
similarity index 93%
copy from core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService2.java
copy to core/applib/src/main/java/org/apache/isis/applib/services/xactn/Transaction2.java
index 0f44090..8e54117 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService2.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/xactn/Transaction2.java
@@ -21,7 +21,7 @@ package org.apache.isis.applib.services.xactn;
 
 import org.apache.isis.applib.annotation.Programmatic;
 
-public interface TransactionService2 extends TransactionService {
+public interface Transaction2 extends Transaction {
 
     @Programmatic
     TransactionState getTransactionState();
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java b/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
index fa4a9e0..42380a6 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
@@ -39,7 +39,7 @@ public interface TransactionService {
     void flushTransaction();
 
     /**
-     * Intended only for use by fixture scripts and integration tests; commits this transaction and starts a new one.
+     * See also {@link TransactionService3#nextTransaction(TransactionService3.Policy)} with a {@link TransactionService3.Policy} of {@link TransactionService3.Policy#UNLESS_MARKED_FOR_ABORT}.
      */
     @Programmatic
     void nextTransaction();
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService2.java b/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService2.java
index 0f44090..4e93a61 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService2.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService2.java
@@ -23,6 +23,10 @@ import org.apache.isis.applib.annotation.Programmatic;
 
 public interface TransactionService2 extends TransactionService {
 
+    /**
+     * Generally this is equivalent to using {@link #currentTransaction()} and {@link Transaction2#getTransactionState()}.
+     * However, if there is no current transaction, then this will return {@link TransactionState#NONE}.
+     */
     @Programmatic
     TransactionState getTransactionState();
 
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService3.java b/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService3.java
new file mode 100644
index 0000000..c8683ba
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService3.java
@@ -0,0 +1,74 @@
+/*
+ *  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.isis.applib.services.xactn;
+
+import org.apache.isis.applib.annotation.Programmatic;
+
+public interface TransactionService3 extends TransactionService2 {
+
+    /**
+     * Returns a representation of the current transaction.
+     */
+    @Programmatic
+    Transaction2 currentTransaction();
+
+    /**
+     * Intended only for use by fixture scripts and integration tests.
+     *
+     * <p>
+     *     The behaviour depends on the current state of the transaction, and the specified policy.
+     *     <ul>
+     *         <li>
+     *              If the current transaction is in that it is still in progress, then commits and starts a new one.
+     *         </li>
+     *         <li>
+     *              If the current transaction is complete, in that it is already committed or was rolled back, then simply starts a new one.
+     *         </li>
+     *         <li>
+     *              If the current transaction is marked for abort, then depends on the provided policy:
+     *              <ul>
+     *                  <li>
+     *                      If set to {@link Policy#ALWAYS always}, then rolls back and starts a new transaction
+     *                  </li>
+     *                  <li>
+     *                      But if set to {@link Policy#UNLESS_MARKED_FOR_ABORT marked for abort}, then fails fast by throwing a runtime exception.
+     *                  </li>
+     *              </ul>
+     *         </li>
+     *
+     *     </ul>
+     *     If the current transaction has been marked for abort only, then depends on the provided rolls it back, and (again) starts a new one.
+     * </p>
+     *
+     * <p>
+     *     This is a refinement of the {@link TransactionService#nextTransaction()}, introduced in
+     *     order to improve the error handling of that method in the case of an already must-abort transaction, and
+     *     also to allow the caller to have more control on how to continue.
+     * </p>
+     */
+    @Programmatic
+    void nextTransaction(Policy policy);
+
+    public enum Policy {
+        UNLESS_MARKED_FOR_ABORT,
+        ALWAYS
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/persistsession/PersistenceSessionServiceInternal.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/persistsession/PersistenceSessionServiceInternal.java
index ff8f23a..4d9908b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/persistsession/PersistenceSessionServiceInternal.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/persistsession/PersistenceSessionServiceInternal.java
@@ -22,7 +22,7 @@ import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.query.Query;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.BookmarkService2;
-import org.apache.isis.applib.services.xactn.Transaction;
+import org.apache.isis.applib.services.xactn.Transaction2;
 import org.apache.isis.applib.services.xactn.TransactionState;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
@@ -112,7 +112,10 @@ public interface PersistenceSessionServiceInternal extends AdapterManager {
     void commit();
 
     @Programmatic
-    Transaction currentTransaction();
+    void abortTransaction();
+
+    @Programmatic
+    Transaction2 currentTransaction();
 
 
     @Programmatic
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/persistsession/PersistenceSessionServiceInternalNoop.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/persistsession/PersistenceSessionServiceInternalNoop.java
index e46b6e2..77ac9dd 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/persistsession/PersistenceSessionServiceInternalNoop.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/persistsession/PersistenceSessionServiceInternalNoop.java
@@ -25,7 +25,7 @@ import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.query.Query;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.BookmarkService2;
-import org.apache.isis.applib.services.xactn.Transaction;
+import org.apache.isis.applib.services.xactn.Transaction2;
 import org.apache.isis.applib.services.xactn.TransactionState;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.oid.Oid;
@@ -126,12 +126,17 @@ public class PersistenceSessionServiceInternalNoop implements PersistenceSession
     }
 
     @Override
+    public void abortTransaction() {
+        throw new UnsupportedOperationException("Not supported by this implementation of PersistenceSessionServiceInternal");
+    }
+
+    @Override
     public void executeWithinTransaction(TransactionalClosure transactionalClosure) {
         transactionalClosure.execute();
     }
 
     @Override
-    public Transaction currentTransaction() {
+    public Transaction2 currentTransaction() {
         throw new UnsupportedOperationException("Not supported by this implementation of PersistenceSessionServiceInternal");
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/xactn/TransactionServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/xactn/TransactionServiceDefault.java
index 4b066d0..68b1fb4 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/xactn/TransactionServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/xactn/TransactionServiceDefault.java
@@ -21,16 +21,17 @@ package org.apache.isis.core.metamodel.services.xactn;
 
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
-import org.apache.isis.applib.services.xactn.Transaction;
-import org.apache.isis.applib.services.xactn.TransactionService2;
+import org.apache.isis.applib.services.xactn.Transaction2;
+import org.apache.isis.applib.services.xactn.TransactionService3;
 import org.apache.isis.applib.services.xactn.TransactionState;
+import org.apache.isis.core.commons.exceptions.IsisException;
 import org.apache.isis.core.metamodel.services.persistsession.PersistenceSessionServiceInternal;
 
 @DomainService(
         nature = NatureOfService.DOMAIN,
         menuOrder = "" + Integer.MAX_VALUE
 )
-public class TransactionServiceDefault implements TransactionService2 {
+public class TransactionServiceDefault implements TransactionService3 {
 
 
     @Override
@@ -40,12 +41,39 @@ public class TransactionServiceDefault implements TransactionService2 {
 
     @Override
     public void nextTransaction() {
-        persistenceSessionServiceInternal.commit();
+        nextTransaction(TransactionService3.Policy.UNLESS_MARKED_FOR_ABORT);
+    }
+
+    @Override
+    public void nextTransaction(TransactionService3.Policy policy) {
+        final TransactionState transactionState = getTransactionState();
+        switch (transactionState) {
+        case NONE:
+            break;
+        case IN_PROGRESS:
+            persistenceSessionServiceInternal.commit();
+            break;
+        case MUST_ABORT:
+            switch (policy) {
+            case UNLESS_MARKED_FOR_ABORT:
+                throw new IsisException("Transaction is marked to abort");
+            case ALWAYS:
+                persistenceSessionServiceInternal.abortTransaction();
+                currentTransaction().clearAbortCause();
+                break;
+            }
+            break;
+        case COMMITTED:
+            break;
+        case ABORTED:
+            break;
+        }
+
         persistenceSessionServiceInternal.beginTran();
     }
 
     @Override
-    public Transaction currentTransaction() {
+    public Transaction2 currentTransaction() {
         return persistenceSessionServiceInternal.currentTransaction();
     }
 
@@ -57,4 +85,5 @@ public class TransactionServiceDefault implements TransactionService2 {
     @javax.inject.Inject
     PersistenceSessionServiceInternal persistenceSessionServiceInternal;
 
+
 }
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/headless/HeadlessAbstract.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/headless/HeadlessAbstract.java
index adb1427..9a66503 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/headless/HeadlessAbstract.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/headless/HeadlessAbstract.java
@@ -23,8 +23,8 @@ import javax.inject.Inject;
 import org.joda.time.LocalDate;
 
 import org.apache.isis.applib.clock.Clock;
-import org.apache.isis.applib.fixtures.TickingFixtureClock;
 import org.apache.isis.applib.fixtures.FixtureClock;
+import org.apache.isis.applib.fixtures.TickingFixtureClock;
 import org.apache.isis.applib.fixturescripts.BuilderScriptAbstract;
 import org.apache.isis.applib.fixturescripts.FixtureScript;
 import org.apache.isis.applib.fixturescripts.FixtureScripts;
@@ -37,7 +37,7 @@ import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.services.sessmgmt.SessionManagementService;
 import org.apache.isis.applib.services.user.UserService;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
-import org.apache.isis.applib.services.xactn.TransactionService;
+import org.apache.isis.applib.services.xactn.TransactionService3;
 
 /**
  * Reworked base class for headless access.
@@ -134,7 +134,7 @@ public abstract class HeadlessAbstract {
     @Inject
     protected WrapperFactory wrapperFactory;
     @Inject
-    protected TransactionService transactionService;
+    protected TransactionService3 transactionService;
     @Inject
     protected SessionManagementService sessionManagementService;
 
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/headless/HeadlessWithBootstrappingAbstract.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/headless/HeadlessWithBootstrappingAbstract.java
index 6aeb73a..a4027b4 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/headless/HeadlessWithBootstrappingAbstract.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/headless/HeadlessWithBootstrappingAbstract.java
@@ -30,6 +30,7 @@ import org.slf4j.event.Level;
 
 import org.apache.isis.applib.Module;
 import org.apache.isis.applib.clock.Clock;
+import org.apache.isis.applib.services.xactn.TransactionService3;
 import org.apache.isis.core.commons.factory.InstanceUtil;
 import org.apache.isis.core.runtime.headless.logging.LogConfig;
 import org.apache.isis.core.runtime.headless.logging.LogStream;
@@ -127,7 +128,7 @@ public abstract class HeadlessWithBootstrappingAbstract extends HeadlessAbstract
             return;
         }
 
-        transactionService.nextTransaction();
+        transactionService.nextTransaction(TransactionService3.Policy.ALWAYS);
 
         isisSystemBootstrapper.tearDownAllModules();
 
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/persistsession/PersistenceSessionServiceInternalDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/persistsession/PersistenceSessionServiceInternalDefault.java
index 954b492..d29662c 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/persistsession/PersistenceSessionServiceInternalDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/persistsession/PersistenceSessionServiceInternalDefault.java
@@ -26,7 +26,7 @@ import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.query.Query;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.BookmarkService2;
-import org.apache.isis.applib.services.xactn.Transaction;
+import org.apache.isis.applib.services.xactn.Transaction2;
 import org.apache.isis.applib.services.xactn.TransactionState;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.oid.Oid;
@@ -162,7 +162,12 @@ public class PersistenceSessionServiceInternalDefault implements PersistenceSess
     }
 
     @Override
-    public Transaction currentTransaction() {
+    public void abortTransaction() {
+        getTransactionManager().abortTransaction();
+    }
+
+    @Override
+    public Transaction2 currentTransaction() {
         return getTransactionManager().getCurrentTransaction();
     }
 
@@ -188,7 +193,7 @@ public class PersistenceSessionServiceInternalDefault implements PersistenceSess
             return TransactionState.NONE;
         }
         IsisTransaction.State state = transaction.getState();
-        return state.getRuntimeContextState();
+        return state.getTransactionState();
     }
 
     protected PersistenceSession getPersistenceSession() {
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
index 6a4b474..f26f368 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
@@ -30,7 +30,8 @@ import org.slf4j.LoggerFactory;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.HasTransactionId;
 import org.apache.isis.applib.services.WithTransactionScope;
-import org.apache.isis.applib.services.xactn.Transaction;
+import org.apache.isis.applib.services.xactn.Transaction2;
+import org.apache.isis.applib.services.xactn.TransactionState;
 import org.apache.isis.core.commons.authentication.AuthenticationSession;
 import org.apache.isis.core.commons.authentication.MessageBroker;
 import org.apache.isis.core.commons.components.TransactionScopedComponent;
@@ -39,7 +40,6 @@ import org.apache.isis.core.commons.util.ToString;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.services.publishing.PublishingServiceInternal;
-import org.apache.isis.applib.services.xactn.TransactionState;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.CreateObjectCommand;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.DestroyObjectCommand;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.PersistenceCommand;
@@ -58,8 +58,7 @@ import org.apache.isis.core.runtime.services.persistsession.PersistenceSessionSe
  * the {@link IsisTransactionManager} to ensure that the underlying persistence
  * mechanism (for example, the <tt>ObjectStore</tt>) is also committed.
  */
-public class IsisTransaction implements TransactionScopedComponent, Transaction {
-
+public class IsisTransaction implements TransactionScopedComponent, Transaction2 {
 
     public static class Placeholder {
         public static Placeholder NEW = new Placeholder("[NEW]");
@@ -153,7 +152,7 @@ public class IsisTransaction implements TransactionScopedComponent, Transaction
             return this == MUST_ABORT;
         }
 
-        public TransactionState getRuntimeContextState() {
+        public TransactionState getTransactionState() {
             return transactionState;
         }
     }
@@ -239,6 +238,22 @@ public class IsisTransaction implements TransactionScopedComponent, Transaction
         this.state = state;
     }
 
+    @Override
+    public TransactionState getTransactionState() {
+
+        if (getState() == null) {
+            return TransactionState.NONE;
+        }
+
+        final TransactionState transactionState = getState().getTransactionState();
+        if (transactionState == null) {
+            return TransactionState.NONE;
+        }
+
+        return transactionState;
+    }
+
+
     //endregion
 
     //region > commands
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/systemusinginstallers/IsisComponentProvider.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/systemusinginstallers/IsisComponentProvider.java
index 0618233..9773fa9 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/systemusinginstallers/IsisComponentProvider.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/systemusinginstallers/IsisComponentProvider.java
@@ -226,7 +226,13 @@ public abstract class IsisComponentProvider {
     }
 
     private static String join(final String csv1, final String csv2) {
-        return csv1 != null ? Joiner.on(",").join(csv1, csv2) : null;
+        if (csv1 == null) {
+            return csv2;
+        }
+        if (csv2 == null) {
+            return csv1;
+        }
+        return Joiner.on(",").join(csv1, csv2);
     }
 
     private Iterable<String> modulePackageNamesFrom(final AppManifest appManifest) {

-- 
To stop receiving notification emails like this one, please contact
"commits@isis.apache.org" <co...@isis.apache.org>.