[tx-control] Add support for read-only transactions
git-svn-id: https://svn.apache.org/repos/asf/aries/trunk/tx-control@1740209 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/tx-control-api/src/main/java/org/osgi/service/transaction/control/TransactionBuilder.java b/tx-control-api/src/main/java/org/osgi/service/transaction/control/TransactionBuilder.java
index 24d2fb6..df35e70 100644
--- a/tx-control-api/src/main/java/org/osgi/service/transaction/control/TransactionBuilder.java
+++ b/tx-control-api/src/main/java/org/osgi/service/transaction/control/TransactionBuilder.java
@@ -131,4 +131,23 @@
noRollbackFor.addAll(Arrays.asList(throwables));
return this;
}
+
+ /**
+ * Indicate to the Transaction Control service that this transaction
+ * will be read-only. This hint may be used by the Transaction Control
+ * service and associated resources to optimise the transaction.
+ *
+ * <p>
+ * Note that this method is for optimisation purposes only. The TransactionControl
+ * service is free to ignore the call if it does not offer read-only optimisation.
+ *
+ * <p>
+ * If a transaction is marked read-only and then the scoped work performs a write
+ * operation on a resource then this is a programming error. The resource is
+ * free to raise an exception when the write is attempted, or to permit the write
+ * operation. As a result the transaction may commit successfully, or may rollback.
+ *
+ * @return this builder
+ */
+ public abstract TransactionBuilder readOnly();
}
diff --git a/tx-control-api/src/main/java/org/osgi/service/transaction/control/TransactionContext.java b/tx-control-api/src/main/java/org/osgi/service/transaction/control/TransactionContext.java
index e511fb5..6a96ec4 100644
--- a/tx-control-api/src/main/java/org/osgi/service/transaction/control/TransactionContext.java
+++ b/tx-control-api/src/main/java/org/osgi/service/transaction/control/TransactionContext.java
@@ -99,6 +99,14 @@
boolean supportsLocal();
/**
+ * @return true if the TransactionContext supports read-only optimisations
+ * <em>and</em> the transaction was marked read only. In particular it is
+ * legal for this method to return false even if the transaction was marked
+ * read only by the initiating client.
+ */
+ boolean isReadOnly();
+
+ /**
* Register an XA resource with the current transaction
*
* @param resource
diff --git a/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/AbstractTransactionControlImpl.java b/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/AbstractTransactionControlImpl.java
index 7454c8a..7a1d2b4 100644
--- a/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/AbstractTransactionControlImpl.java
+++ b/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/AbstractTransactionControlImpl.java
@@ -24,6 +24,14 @@
private final class TransactionBuilderImpl extends TransactionBuilder {
+ private boolean readOnly = false;
+
+ @Override
+ public TransactionBuilder readOnly() {
+ readOnly = true;
+ return this;
+ }
+
private void checkExceptions() {
List<Class<? extends Throwable>> duplicates = rollbackFor.stream()
.filter(noRollbackFor::contains)
@@ -56,10 +64,12 @@
currentCoord = coordinator.begin(
"Resource-Local-Transaction.REQUIRED", 30000);
endCoordination = true;
- currentTran = startTransaction(currentCoord);
+ currentTran = startTransaction(currentCoord, readOnly);
endTransaction = true;
currentCoord.getVariables().put(TransactionContextKey.class,
currentTran);
+ } else if (currentTran.isReadOnly() && !readOnly){
+ throw new TransactionException("A read only transaction is currently active, and cannot be upgraded to a writeable transaction");
}
} catch (RuntimeException re) {
if(endTransaction) {
@@ -85,7 +95,7 @@
currentCoord = coordinator.begin(
"Resource-Local-Transaction.REQUIRES_NEW", 30000);
- currentTran = startTransaction(currentCoord);
+ currentTran = startTransaction(currentCoord, readOnly);
currentCoord.getVariables().put(TransactionContextKey.class,
currentTran);
} catch (RuntimeException re) {
@@ -271,7 +281,7 @@
coordinator = c;
}
- protected abstract AbstractTransactionContextImpl startTransaction(Coordination currentCoord);
+ protected abstract AbstractTransactionContextImpl startTransaction(Coordination currentCoord, boolean readOnly);
@Override
public TransactionBuilder build() {
diff --git a/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/NoTransactionContextImpl.java b/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/NoTransactionContextImpl.java
index 8ac0cfb..73c0a83 100644
--- a/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/NoTransactionContextImpl.java
+++ b/tx-control-service-common/src/main/java/org/apache/aries/tx/control/service/common/impl/NoTransactionContextImpl.java
@@ -83,6 +83,11 @@
}
@Override
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ @Override
protected boolean isAlive() {
return !finished.get();
}
diff --git a/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/TransactionContextImpl.java b/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/TransactionContextImpl.java
index baab431..4cf8ee1 100644
--- a/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/TransactionContextImpl.java
+++ b/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/TransactionContextImpl.java
@@ -24,10 +24,14 @@
final List<LocalResource> resources = new ArrayList<>();
+ private final boolean readOnly;
+
private AtomicReference<TransactionStatus> tranStatus = new AtomicReference<>(ACTIVE);
- public TransactionContextImpl(Coordination coordination) {
+
+ public TransactionContextImpl(Coordination coordination, boolean readOnly) {
super(coordination);
+ this.readOnly = readOnly;
}
@Override
@@ -134,6 +138,11 @@
}
@Override
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ @Override
protected boolean isAlive() {
TransactionStatus status = tranStatus.get();
return status != COMMITTED && status != ROLLED_BACK;
diff --git a/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/TransactionControlImpl.java b/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/TransactionControlImpl.java
index 7632abf..1654899 100644
--- a/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/TransactionControlImpl.java
+++ b/tx-control-service-local/src/main/java/org/apache/aries/tx/control/service/local/impl/TransactionControlImpl.java
@@ -12,8 +12,8 @@
}
@Override
- protected AbstractTransactionContextImpl startTransaction(Coordination currentCoord) {
- return new TransactionContextImpl(currentCoord);
+ protected AbstractTransactionContextImpl startTransaction(Coordination currentCoord, boolean readOnly) {
+ return new TransactionContextImpl(currentCoord, readOnly);
}
}
diff --git a/tx-control-service-local/src/test/java/org/apache/aries/tx/control/service/local/impl/TransactionContextTest.java b/tx-control-service-local/src/test/java/org/apache/aries/tx/control/service/local/impl/TransactionContextTest.java
index b436f5a..c66cd72 100644
--- a/tx-control-service-local/src/test/java/org/apache/aries/tx/control/service/local/impl/TransactionContextTest.java
+++ b/tx-control-service-local/src/test/java/org/apache/aries/tx/control/service/local/impl/TransactionContextTest.java
@@ -48,7 +48,7 @@
@Before
public void setUp() {
- ctx = new TransactionContextImpl(coordination);
+ ctx = new TransactionContextImpl(coordination, false);
variables = new HashMap<>();
Mockito.when(coordination.getVariables()).thenReturn(variables);
}
@@ -65,6 +65,17 @@
}
@Test
+ public void testisReadOnlyFalse() {
+ assertFalse(ctx.isReadOnly());
+ }
+
+ @Test
+ public void testisReadOnlyTrue() {
+ ctx = new TransactionContextImpl(coordination, true);
+ assertTrue(ctx.isReadOnly());
+ }
+
+ @Test
public void testTransactionKey() {
Mockito.when(coordination.getId()).thenReturn(42L);
diff --git a/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextImpl.java b/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextImpl.java
index 827d462..990eb2c 100644
--- a/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextImpl.java
+++ b/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextImpl.java
@@ -49,9 +49,13 @@
private final Object key;
- public TransactionContextImpl(GeronimoTransactionManager transactionManager, Coordination coordination) {
+ private final boolean readOnly;
+
+ public TransactionContextImpl(GeronimoTransactionManager transactionManager, Coordination coordination,
+ boolean readOnly) {
super(coordination);
this.transactionManager = transactionManager;
+ this.readOnly = readOnly;
Transaction tmp = null;
try {
tmp = transactionManager.suspend();
@@ -222,6 +226,11 @@
}
@Override
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ @Override
protected boolean isAlive() {
TransactionStatus status = getTransactionStatus();
return status != COMMITTED && status != ROLLED_BACK;
diff --git a/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java b/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java
index 4db6950..6785e4d 100644
--- a/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java
+++ b/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java
@@ -16,8 +16,8 @@
}
@Override
- protected AbstractTransactionContextImpl startTransaction(Coordination currentCoord) {
- return new TransactionContextImpl(transactionManager, currentCoord);
+ protected AbstractTransactionContextImpl startTransaction(Coordination currentCoord, boolean readOnly) {
+ return new TransactionContextImpl(transactionManager, currentCoord, readOnly);
}
}
diff --git a/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextTest.java b/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextTest.java
index 6e3a4c2..75a041f 100644
--- a/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextTest.java
+++ b/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextTest.java
@@ -53,7 +53,7 @@
@Before
public void setUp() throws XAException {
- ctx = new TransactionContextImpl(new GeronimoTransactionManager(), coordination);
+ ctx = new TransactionContextImpl(new GeronimoTransactionManager(), coordination, false);
variables = new HashMap<>();
Mockito.when(coordination.getVariables()).thenReturn(variables);
}
@@ -68,6 +68,18 @@
ctx.setRollbackOnly();
assertTrue(ctx.getRollbackOnly());
}
+
+ @Test
+ public void testisReadOnlyFalse() {
+ assertFalse(ctx.isReadOnly());
+ }
+
+ @Test
+ public void testisReadOnlyTrue() throws XAException {
+ ctx = new TransactionContextImpl(new GeronimoTransactionManager(), coordination, true);
+ assertTrue(ctx.isReadOnly());
+ }
+
@Test
public void testTransactionKey() {