You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ti...@apache.org on 2016/03/09 15:38:16 UTC
svn commit: r1734243 [2/2] - in /aries/trunk/tx-control: ./
tx-control-itests/
tx-control-itests/src/test/java/org/apache/aries/tx/control/itests/
tx-control-service-common/ tx-control-service-common/src/
tx-control-service-common/src/main/ tx-control-...
Added: aries/trunk/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextImpl.java?rev=1734243&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextImpl.java (added)
+++ aries/trunk/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextImpl.java Wed Mar 9 14:38:15 2016
@@ -0,0 +1,436 @@
+package org.apache.aries.tx.control.service.xa.impl;
+
+import static java.util.Optional.ofNullable;
+import static javax.transaction.xa.XAException.XA_HEURMIX;
+import static javax.transaction.xa.XAException.XA_RBOTHER;
+import static javax.transaction.xa.XAException.XA_RBPROTO;
+import static org.osgi.service.transaction.control.TransactionStatus.ACTIVE;
+import static org.osgi.service.transaction.control.TransactionStatus.COMMITTED;
+import static org.osgi.service.transaction.control.TransactionStatus.COMMITTING;
+import static org.osgi.service.transaction.control.TransactionStatus.MARKED_ROLLBACK;
+import static org.osgi.service.transaction.control.TransactionStatus.PREPARED;
+import static org.osgi.service.transaction.control.TransactionStatus.PREPARING;
+import static org.osgi.service.transaction.control.TransactionStatus.ROLLED_BACK;
+import static org.osgi.service.transaction.control.TransactionStatus.ROLLING_BACK;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.apache.aries.tx.control.service.common.impl.AbstractTransactionContextImpl;
+import org.apache.geronimo.transaction.manager.GeronimoTransactionManager;
+import org.osgi.service.coordinator.Coordination;
+import org.osgi.service.transaction.control.LocalResource;
+import org.osgi.service.transaction.control.TransactionContext;
+import org.osgi.service.transaction.control.TransactionException;
+import org.osgi.service.transaction.control.TransactionStatus;
+
+public class TransactionContextImpl extends AbstractTransactionContextImpl implements TransactionContext {
+
+ final List<LocalResource> resources = new ArrayList<>();
+
+ private final Transaction oldTran;
+
+ private final Transaction currentTransaction;
+
+ private final AtomicReference<TransactionStatus> completionState = new AtomicReference<>();
+
+ private final GeronimoTransactionManager transactionManager;
+
+ private final Object key;
+
+ public TransactionContextImpl(GeronimoTransactionManager transactionManager, Coordination coordination) {
+ super(coordination);
+ this.transactionManager = transactionManager;
+ Transaction tmp = null;
+ try {
+ tmp = transactionManager.suspend();
+ transactionManager.begin();
+ } catch (Exception e) {
+ if(tmp != null) {
+ try {
+ transactionManager.resume(tmp);
+ } catch (Exception e1) {
+ e.addSuppressed(e1);
+ }
+ }
+ throw new TransactionException("There was a serious error creating a transaction");
+ }
+ oldTran = tmp;
+ currentTransaction = transactionManager.getTransaction();
+ key = transactionManager.getTransactionKey();
+ }
+
+ @Override
+ public Object getTransactionKey() {
+ return key;
+ }
+
+ @Override
+ public boolean getRollbackOnly() throws IllegalStateException {
+ switch (getTransactionStatus()) {
+ case MARKED_ROLLBACK:
+ case ROLLING_BACK:
+ case ROLLED_BACK:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void setRollbackOnly() throws IllegalStateException {
+ TransactionStatus status = getTransactionStatus();
+ switch (status) {
+ case ACTIVE:
+ case MARKED_ROLLBACK:
+ try {
+ currentTransaction.setRollbackOnly();
+ } catch (Exception e) {
+ throw new TransactionException("Unable to set rollback for the transaction", e);
+ }
+ break;
+ case COMMITTING:
+ // TODO something here? If it's the first resource then it might
+ // be ok to roll back?
+ throw new IllegalStateException("The transaction is already being committed");
+ case COMMITTED:
+ throw new IllegalStateException("The transaction is already committed");
+
+ case ROLLING_BACK:
+ case ROLLED_BACK:
+ // A no op
+ break;
+ default:
+ throw new IllegalStateException("The transaction is in an unkown state");
+ }
+ }
+
+ @Override
+ protected void safeSetRollbackOnly() {
+ TransactionStatus status = getTransactionStatus();
+ switch (status) {
+ case ACTIVE:
+ case MARKED_ROLLBACK:
+ try {
+ currentTransaction.setRollbackOnly();
+ } catch (Exception e) {
+ throw new TransactionException("Unable to set rollback for the transaction", e);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public TransactionStatus getTransactionStatus() {
+ return ofNullable(completionState.get())
+ .orElseGet(this::getStatusFromTransaction);
+ }
+
+ private TransactionStatus getStatusFromTransaction() {
+ int status;
+ try {
+ status = currentTransaction.getStatus();
+ } catch (SystemException e) {
+ throw new TransactionException("Unable to determine the state of the transaction.", e);
+ }
+
+ switch (status) {
+ case Status.STATUS_ACTIVE:
+ return ACTIVE;
+ case Status.STATUS_MARKED_ROLLBACK:
+ return MARKED_ROLLBACK;
+ case Status.STATUS_PREPARING:
+ return PREPARING;
+ case Status.STATUS_PREPARED:
+ return PREPARED;
+ case Status.STATUS_COMMITTING:
+ return COMMITTING;
+ case Status.STATUS_COMMITTED:
+ return COMMITTED;
+ case Status.STATUS_ROLLING_BACK:
+ return ROLLING_BACK;
+ case Status.STATUS_ROLLEDBACK:
+ return ROLLED_BACK;
+ default:
+ throw new TransactionException("Unable to determine the state of the transaction: " + status);
+ }
+ }
+
+ @Override
+ public void preCompletion(Runnable job) throws IllegalStateException {
+ TransactionStatus status = getTransactionStatus();
+ if (status.compareTo(MARKED_ROLLBACK) > 0) {
+ throw new IllegalStateException("The current transaction is in state " + status);
+ }
+
+ preCompletion.add(job);
+ }
+
+ @Override
+ public void postCompletion(Consumer<TransactionStatus> job) throws IllegalStateException {
+ TransactionStatus status = getTransactionStatus();
+ if (status == COMMITTED || status == ROLLED_BACK) {
+ throw new IllegalStateException("The current transaction is in state " + status);
+ }
+
+ postCompletion.add(job);
+ }
+
+ @Override
+ public void registerXAResource(XAResource resource) {
+ TransactionStatus status = getTransactionStatus();
+ if (status.compareTo(MARKED_ROLLBACK) > 0) {
+ throw new IllegalStateException("The current transaction is in state " + status);
+ }
+ try {
+ currentTransaction.enlistResource(resource);
+ } catch (Exception e) {
+ throw new TransactionException("The transaction was unable to enlist a resource", e);
+ }
+ }
+
+ @Override
+ public void registerLocalResource(LocalResource resource) {
+ TransactionStatus status = getTransactionStatus();
+ if (status.compareTo(MARKED_ROLLBACK) > 0) {
+ throw new IllegalStateException("The current transaction is in state " + status);
+ }
+ resources.add(resource);
+ }
+
+ @Override
+ public boolean supportsXA() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsLocal() {
+ return true;
+ }
+
+ @Override
+ protected boolean isAlive() {
+ TransactionStatus status = getTransactionStatus();
+ return status != COMMITTED && status != ROLLED_BACK;
+ }
+
+ @Override
+ public void finish() {
+
+ if(!resources.isEmpty()) {
+ XAResource localResource = new LocalXAResourceImpl();
+ try {
+ currentTransaction.enlistResource(localResource);
+ } catch (Exception e) {
+ safeSetRollbackOnly();
+ recordFailure(e);
+ try {
+ localResource.rollback(null);
+ } catch (XAException e1) {
+ recordFailure(e1);
+ }
+ }
+ }
+
+ TxListener listener;
+ boolean manualCallListener;
+ if(!preCompletion.isEmpty() || !postCompletion.isEmpty()) {
+ listener = new TxListener();
+ try {
+ transactionManager.registerInterposedSynchronization(listener);
+ manualCallListener = false;
+ } catch (Exception e) {
+ manualCallListener = true;
+ recordFailure(e);
+ safeSetRollbackOnly();
+ }
+ } else {
+ listener = null;
+ manualCallListener = false;
+ }
+
+
+ try {
+ int status;
+ try {
+ if (getRollbackOnly()) {
+ // GERONIMO-4449 says that we get no beforeCompletion
+ // callback for rollback :(
+ if(listener != null) {
+ listener.beforeCompletion();
+ }
+ transactionManager.rollback();
+ status = Status.STATUS_ROLLEDBACK;
+ completionState.set(ROLLED_BACK);
+ } else {
+ if(manualCallListener) {
+ listener.beforeCompletion();
+ }
+ transactionManager.commit();
+ status = Status.STATUS_COMMITTED;
+ completionState.set(COMMITTED);
+ }
+ } catch (Exception e) {
+ recordFailure(e);
+ status = Status.STATUS_ROLLEDBACK;
+ completionState.set(ROLLED_BACK);
+ }
+ if(manualCallListener) {
+ listener.afterCompletion(status);
+ }
+ } finally {
+ try {
+ transactionManager.resume(oldTran);
+ } catch (Exception e) {
+ recordFailure(e);
+ }
+ }
+ }
+
+ private class LocalXAResourceImpl implements XAResource {
+
+ private final AtomicBoolean finished = new AtomicBoolean();
+
+ @Override
+ public void commit(Xid xid, boolean onePhase) throws XAException {
+ if(!finished.compareAndSet(false, true)) {
+ return;
+ }
+ doCommit();
+ }
+
+ private void doCommit() throws XAException {
+ AtomicBoolean commit = new AtomicBoolean(true);
+
+ List<LocalResource> committed = new ArrayList<>(resources.size());
+ List<LocalResource> rolledback = new ArrayList<>(0);
+
+ resources.stream().forEach(lr -> {
+ try {
+ if (commit.get()) {
+ lr.commit();
+ committed.add(lr);
+ } else {
+ lr.rollback();
+ rolledback.add(lr);
+ }
+ } catch (Exception e) {
+ recordFailure(e);
+ if (committed.isEmpty()) {
+ commit.set(false);
+ // This is needed to override the status from the
+ // Transaction, which thinks that we're committing
+ // until we throw an XAException from this commit.
+ completionState.set(ROLLING_BACK);
+ }
+ rolledback.add(lr);
+ }
+ });
+
+ if(!rolledback.isEmpty()) {
+ if(committed.isEmpty()) {
+ throw (XAException) new XAException(XA_RBOTHER)
+ .initCause(firstUnexpectedException.get());
+ } else {
+ throw (XAException) new XAException(XA_HEURMIX)
+ .initCause(firstUnexpectedException.get());
+ }
+ }
+ }
+
+ @Override
+ public void end(Xid xid, int flags) throws XAException {
+ //Nothing to do here
+ }
+
+ @Override
+ public void forget(Xid xid) throws XAException {
+ //Nothing to do here
+ }
+
+ @Override
+ public int getTransactionTimeout() throws XAException {
+ return 3600;
+ }
+
+ @Override
+ public boolean isSameRM(XAResource xares) throws XAException {
+ return this == xares;
+ }
+
+ @Override
+ public int prepare(Xid xid) throws XAException {
+ if(!finished.compareAndSet(false, true)) {
+ switch(getTransactionStatus()) {
+ case COMMITTING:
+ return XA_OK;
+ case ROLLING_BACK:
+ throw new XAException(XA_RBOTHER);
+ default:
+ throw new XAException(XA_RBPROTO);
+ }
+ }
+ completionState.set(COMMITTING);
+ doCommit();
+ return XA_OK;
+ }
+
+ @Override
+ public Xid[] recover(int flag) throws XAException {
+ return new Xid[0];
+ }
+
+ @Override
+ public void rollback(Xid xid) throws XAException {
+ if(!finished.compareAndSet(false, true)) {
+ return;
+ }
+ resources.stream().forEach(lr -> {
+ try {
+ lr.rollback();
+ } catch (Exception e) {
+ // TODO log this
+ recordFailure(e);
+ }
+ });
+ }
+
+ @Override
+ public boolean setTransactionTimeout(int seconds) throws XAException {
+ return false;
+ }
+
+ @Override
+ public void start(Xid xid, int flags) throws XAException {
+ // Nothing to do here
+ }
+
+ }
+
+ private class TxListener implements Synchronization {
+
+ @Override
+ public void beforeCompletion() {
+ TransactionContextImpl.this.beforeCompletion(() -> safeSetRollbackOnly());
+ }
+
+ @Override
+ public void afterCompletion(int status) {
+ TransactionContextImpl.this.afterCompletion(status == Status.STATUS_COMMITTED ? COMMITTED : ROLLED_BACK);
+ }
+
+ }
+}
Added: aries/trunk/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java?rev=1734243&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java (added)
+++ aries/trunk/tx-control/tx-control-service-xa/src/main/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlImpl.java Wed Mar 9 14:38:15 2016
@@ -0,0 +1,23 @@
+package org.apache.aries.tx.control.service.xa.impl;
+
+import org.apache.aries.tx.control.service.common.impl.AbstractTransactionContextImpl;
+import org.apache.aries.tx.control.service.common.impl.AbstractTransactionControlImpl;
+import org.apache.geronimo.transaction.manager.GeronimoTransactionManager;
+import org.osgi.service.coordinator.Coordination;
+import org.osgi.service.coordinator.Coordinator;
+
+public class TransactionControlImpl extends AbstractTransactionControlImpl {
+
+ GeronimoTransactionManager transactionManager;
+
+ public TransactionControlImpl(GeronimoTransactionManager tm, Coordinator c) {
+ super(c);
+ this.transactionManager = tm;
+ }
+
+ @Override
+ protected AbstractTransactionContextImpl startTransaction(Coordination currentCoord) {
+ return new TransactionContextImpl(transactionManager, currentCoord);
+ }
+
+}
Copied: aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextTest.java (from r1733320, aries/trunk/tx-control/tx-control-service-local/src/test/java/org/apache/aries/tx/control/service/local/impl/TransactionContextTest.java)
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextTest.java?p2=aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextTest.java&p1=aries/trunk/tx-control/tx-control-service-local/src/test/java/org/apache/aries/tx/control/service/local/impl/TransactionContextTest.java&r1=1733320&r2=1734243&rev=1734243&view=diff
==============================================================================
--- aries/trunk/tx-control/tx-control-service-local/src/test/java/org/apache/aries/tx/control/service/local/impl/TransactionContextTest.java (original)
+++ aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionContextTest.java Wed Mar 9 14:38:15 2016
@@ -1,4 +1,4 @@
-package org.apache.aries.tx.control.service.local.impl;
+package org.apache.aries.tx.control.service.xa.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -17,12 +17,18 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
+import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import org.apache.aries.tx.control.service.common.impl.AbstractTransactionContextImpl;
+import org.apache.aries.tx.control.service.xa.impl.TransactionContextImpl;
+import org.apache.geronimo.transaction.manager.GeronimoTransactionManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
@@ -46,8 +52,8 @@ public class TransactionContextTest {
AbstractTransactionContextImpl ctx;
@Before
- public void setUp() {
- ctx = new TransactionContextImpl(coordination);
+ public void setUp() throws XAException {
+ ctx = new TransactionContextImpl(new GeronimoTransactionManager(), coordination);
variables = new HashMap<>();
Mockito.when(coordination.getVariables()).thenReturn(variables);
}
@@ -86,10 +92,10 @@ public class TransactionContextTest {
@Test
public void testXAResourceSupport() {
- assertFalse(ctx.supportsXA());
+ assertTrue(ctx.supportsXA());
}
- @Test(expected=IllegalStateException.class)
+ @Test
public void testXAResourceRegistration() {
ctx.registerXAResource(xaResource);
}
@@ -285,9 +291,9 @@ public class TransactionContextTest {
ctx.registerLocalResource(localResource);
Mockito.doAnswer(i -> {
- assertEquals(COMMITTING, ctx.getTransactionStatus());
+ assertEquals(ROLLING_BACK, ctx.getTransactionStatus());
return null;
- }).when(localResource).commit();
+ }).when(localResource).rollback();
getParticipant().ended(coordination);
@@ -386,4 +392,218 @@ public class TransactionContextTest {
Mockito.verify(localResource2).rollback();
}
+ @Test
+ public void testSingleXAResource() throws Exception {
+ ctx.registerXAResource(xaResource);
+
+ Mockito.doAnswer(i -> {
+ assertEquals(COMMITTING, ctx.getTransactionStatus());
+ return null;
+ }).when(xaResource).commit(Mockito.any(Xid.class), Mockito.eq(true));
+
+ ctx.finish();
+
+ ArgumentCaptor<Xid> captor = ArgumentCaptor.forClass(Xid.class);
+
+ InOrder inOrder = Mockito.inOrder(xaResource);
+
+ inOrder.verify(xaResource).start(captor.capture(), Mockito.eq(XAResource.TMNOFLAGS));
+ inOrder.verify(xaResource).setTransactionTimeout(Mockito.anyInt());
+ inOrder.verify(xaResource).end(Mockito.eq(captor.getValue()), Mockito.eq(XAResource.TMSUCCESS));
+ inOrder.verify(xaResource).commit(Mockito.eq(captor.getValue()), Mockito.eq(true));
+
+ Mockito.verifyNoMoreInteractions(xaResource);
+ }
+
+ @Test
+ public void testXAResourceEarlyEnd() throws Exception {
+ ctx.registerXAResource(xaResource);
+
+ Mockito.doAnswer(i -> {
+ assertEquals(ROLLING_BACK, ctx.getTransactionStatus());
+ return null;
+ }).when(xaResource).rollback(Mockito.any(Xid.class));
+
+ getParticipant().ended(coordination);
+
+ ctx.finish();
+
+ ArgumentCaptor<Xid> captor = ArgumentCaptor.forClass(Xid.class);
+
+ InOrder inOrder = Mockito.inOrder(xaResource);
+
+ inOrder.verify(xaResource).start(captor.capture(), Mockito.eq(XAResource.TMNOFLAGS));
+ inOrder.verify(xaResource).setTransactionTimeout(Mockito.anyInt());
+ inOrder.verify(xaResource).end(Mockito.eq(captor.getValue()), Mockito.eq(XAResource.TMFAIL));
+ inOrder.verify(xaResource).rollback(Mockito.eq(captor.getValue()));
+
+ Mockito.verifyNoMoreInteractions(xaResource);
+ }
+
+ @Test
+ public void testXAResourceRollbackOnly() throws Exception {
+ ctx.registerXAResource(xaResource);
+ ctx.setRollbackOnly();
+
+ Mockito.doAnswer(i -> {
+ assertEquals(ROLLING_BACK, ctx.getTransactionStatus());
+ return null;
+ }).when(xaResource).rollback(Mockito.any(Xid.class));
+
+ ctx.finish();
+
+ ArgumentCaptor<Xid> captor = ArgumentCaptor.forClass(Xid.class);
+
+ InOrder inOrder = Mockito.inOrder(xaResource);
+
+ inOrder.verify(xaResource).start(captor.capture(), Mockito.eq(XAResource.TMNOFLAGS));
+ inOrder.verify(xaResource).setTransactionTimeout(Mockito.anyInt());
+ inOrder.verify(xaResource).end(Mockito.eq(captor.getValue()), Mockito.eq(XAResource.TMFAIL));
+ inOrder.verify(xaResource).rollback(Mockito.eq(captor.getValue()));
+
+ Mockito.verifyNoMoreInteractions(xaResource);
+ }
+
+ @Test
+ public void testXAResourceFail() throws Exception {
+ ctx.registerXAResource(xaResource);
+
+ Mockito.doAnswer(i -> {
+ assertEquals(ROLLING_BACK, ctx.getTransactionStatus());
+ return null;
+ }).when(xaResource).rollback(Mockito.any(Xid.class));
+
+ getParticipant().failed(coordination);
+
+ ctx.finish();
+
+ ArgumentCaptor<Xid> captor = ArgumentCaptor.forClass(Xid.class);
+
+ InOrder inOrder = Mockito.inOrder(xaResource);
+
+ inOrder.verify(xaResource).start(captor.capture(), Mockito.eq(XAResource.TMNOFLAGS));
+ inOrder.verify(xaResource).setTransactionTimeout(Mockito.anyInt());
+ inOrder.verify(xaResource).end(Mockito.eq(captor.getValue()), Mockito.eq(XAResource.TMFAIL));
+ inOrder.verify(xaResource).rollback(Mockito.eq(captor.getValue()));
+
+ Mockito.verifyNoMoreInteractions(xaResource);
+ }
+
+ @Test
+ public void testXAResourcePreCommitException() throws Exception {
+ ctx.registerXAResource(xaResource);
+
+ Mockito.doAnswer(i -> {
+ assertEquals(ROLLING_BACK, ctx.getTransactionStatus());
+ return null;
+ }).when(xaResource).rollback(Mockito.any(Xid.class));
+
+ ctx.preCompletion(() -> { throw new IllegalArgumentException(); });
+
+ ctx.finish();
+
+ ArgumentCaptor<Xid> captor = ArgumentCaptor.forClass(Xid.class);
+
+ InOrder inOrder = Mockito.inOrder(xaResource);
+
+ inOrder.verify(xaResource).start(captor.capture(), Mockito.eq(XAResource.TMNOFLAGS));
+ inOrder.verify(xaResource).setTransactionTimeout(Mockito.anyInt());
+ inOrder.verify(xaResource).end(Mockito.eq(captor.getValue()), Mockito.eq(XAResource.TMFAIL));
+ inOrder.verify(xaResource).rollback(Mockito.eq(captor.getValue()));
+
+ Mockito.verifyNoMoreInteractions(xaResource);
+ }
+
+ @Test
+ public void testXAResourcePostCommitException() throws Exception {
+ ctx.registerXAResource(xaResource);
+
+ Mockito.doAnswer(i -> {
+ assertEquals(COMMITTING, ctx.getTransactionStatus());
+ return null;
+ }).when(xaResource).commit(Mockito.any(Xid.class), Mockito.eq(true));
+
+ ctx.postCompletion(i -> {
+ assertEquals(COMMITTED, ctx.getTransactionStatus());
+ throw new IllegalArgumentException();
+ });
+
+ ctx.finish();
+
+ ArgumentCaptor<Xid> captor = ArgumentCaptor.forClass(Xid.class);
+
+ InOrder inOrder = Mockito.inOrder(xaResource);
+
+ inOrder.verify(xaResource).start(captor.capture(), Mockito.eq(XAResource.TMNOFLAGS));
+ inOrder.verify(xaResource).setTransactionTimeout(Mockito.anyInt());
+ inOrder.verify(xaResource).end(Mockito.eq(captor.getValue()), Mockito.eq(XAResource.TMSUCCESS));
+ inOrder.verify(xaResource).commit(Mockito.eq(captor.getValue()), Mockito.eq(true));
+
+ Mockito.verifyNoMoreInteractions(xaResource);
+ }
+
+ @Test
+ public void testLastParticipantSuccessSoCommit() throws Exception {
+
+ ctx.registerLocalResource(localResource);
+ ctx.registerXAResource(xaResource);
+
+ Mockito.doAnswer(i -> {
+ assertEquals(COMMITTING, ctx.getTransactionStatus());
+ return null;
+ }).when(localResource).commit();
+
+ Mockito.doAnswer(i -> {
+ assertEquals(COMMITTING, ctx.getTransactionStatus());
+ return null;
+ }).when(xaResource).commit(Mockito.any(Xid.class), Mockito.eq(false));
+
+ ctx.finish();
+
+ ArgumentCaptor<Xid> captor = ArgumentCaptor.forClass(Xid.class);
+
+ InOrder inOrder = Mockito.inOrder(xaResource, localResource);
+
+ inOrder.verify(xaResource).start(captor.capture(), Mockito.eq(XAResource.TMNOFLAGS));
+ inOrder.verify(xaResource).setTransactionTimeout(Mockito.anyInt());
+ inOrder.verify(xaResource).end(Mockito.eq(captor.getValue()), Mockito.eq(XAResource.TMSUCCESS));
+ inOrder.verify(xaResource).prepare(captor.getValue());
+ inOrder.verify(localResource).commit();
+ inOrder.verify(xaResource).commit(Mockito.eq(captor.getValue()), Mockito.eq(false));
+
+ Mockito.verifyNoMoreInteractions(xaResource, localResource);
+ }
+
+ @Test
+ public void testLastParticipantFailsSoRollback() throws Exception {
+
+ ctx.registerLocalResource(localResource);
+ ctx.registerXAResource(xaResource);
+
+ Mockito.doAnswer(i -> {
+ assertEquals(COMMITTING, ctx.getTransactionStatus());
+ throw new TransactionException("Unable to commit");
+ }).when(localResource).commit();
+
+ Mockito.doAnswer(i -> {
+ assertEquals(ROLLING_BACK, ctx.getTransactionStatus());
+ return null;
+ }).when(xaResource).rollback(Mockito.any(Xid.class));
+
+ ctx.finish();
+
+ ArgumentCaptor<Xid> captor = ArgumentCaptor.forClass(Xid.class);
+
+ InOrder inOrder = Mockito.inOrder(xaResource, localResource);
+
+ inOrder.verify(xaResource).start(captor.capture(), Mockito.eq(XAResource.TMNOFLAGS));
+ inOrder.verify(xaResource).setTransactionTimeout(Mockito.anyInt());
+ inOrder.verify(xaResource).end(Mockito.eq(captor.getValue()), Mockito.eq(XAResource.TMSUCCESS));
+ inOrder.verify(xaResource).prepare(captor.getValue());
+ inOrder.verify(localResource).commit();
+ inOrder.verify(xaResource).rollback(Mockito.eq(captor.getValue()));
+
+ Mockito.verifyNoMoreInteractions(xaResource, localResource);
+ }
+
}
Added: aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlRunningTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlRunningTest.java?rev=1734243&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlRunningTest.java (added)
+++ aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlRunningTest.java Wed Mar 9 14:38:15 2016
@@ -0,0 +1,455 @@
+package org.apache.aries.tx.control.service.xa.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.osgi.service.transaction.control.TransactionStatus.COMMITTED;
+import static org.osgi.service.transaction.control.TransactionStatus.ROLLED_BACK;
+
+import java.net.BindException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.transaction.xa.XAException;
+
+import org.apache.aries.tx.control.service.xa.impl.TransactionControlImpl;
+import org.apache.geronimo.transaction.manager.GeronimoTransactionManager;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.service.coordinator.Coordination;
+import org.osgi.service.coordinator.Coordinator;
+import org.osgi.service.coordinator.Participant;
+import org.osgi.service.transaction.control.LocalResource;
+import org.osgi.service.transaction.control.ResourceProvider;
+import org.osgi.service.transaction.control.ScopedWorkException;
+import org.osgi.service.transaction.control.TransactionStatus;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TransactionControlRunningTest {
+
+ @Mock
+ Coordinator coordinator;
+ @Mock
+ Coordination coordination1;
+ @Mock
+ Coordination coordination2;
+
+ @Mock
+ ResourceProvider<Object> testProvider;
+ @Mock
+ LocalResource testResource;
+
+ TransactionControlImpl txControl;
+
+ Map<Class<?>, Object> variables1;
+ Map<Class<?>, Object> variables2;
+
+ @Before
+ public void setUp() throws XAException {
+ variables1 = new HashMap<>();
+ variables2 = new HashMap<>();
+
+ setupCoordinations();
+
+ txControl = new TransactionControlImpl(new GeronimoTransactionManager(), coordinator);
+ }
+
+ /**
+ * Allow up to two Coordinations to be happening
+ */
+ private void setupCoordinations() {
+ Mockito.when(coordinator.begin(Mockito.anyString(), Mockito.anyLong())).then(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(coordination1);
+ return coordination1;
+ }).then(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(coordination2);
+ return coordination2;
+ }).thenThrow(new IllegalStateException("Only two coordinations at a time in the test"));
+
+ Mockito.when(coordination1.getVariables()).thenReturn(variables1);
+ Mockito.when(coordination1.getId()).thenReturn(42L);
+ Mockito.doAnswer(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(null);
+ ArgumentCaptor<Participant> captor = ArgumentCaptor.forClass(Participant.class);
+ Mockito.verify(coordination1, Mockito.atLeast(1)).addParticipant(captor.capture());
+
+ for(Participant p : captor.getAllValues()) {
+ p.ended(coordination1);
+ }
+ return null;
+ }).when(coordination1).end();
+ Mockito.doAnswer(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(null);
+ ArgumentCaptor<Participant> captor = ArgumentCaptor.forClass(Participant.class);
+ Mockito.verify(coordination1, Mockito.atLeast(1)).addParticipant(captor.capture());
+
+ for(Participant p : captor.getAllValues()) {
+ p.failed(coordination1);
+ }
+ return null;
+ }).when(coordination1).fail(Mockito.any(Throwable.class));
+
+ Mockito.when(coordination2.getVariables()).thenReturn(variables2);
+ Mockito.when(coordination2.getId()).thenReturn(43L);
+ Mockito.doAnswer(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(coordination1);
+ ArgumentCaptor<Participant> captor = ArgumentCaptor.forClass(Participant.class);
+ Mockito.verify(coordination2, Mockito.atLeast(1)).addParticipant(captor.capture());
+
+ for(Participant p : captor.getAllValues()) {
+ p.ended(coordination2);
+ }
+ return null;
+ }).when(coordination2).end();
+ Mockito.doAnswer(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(coordination1);
+ ArgumentCaptor<Participant> captor = ArgumentCaptor.forClass(Participant.class);
+ Mockito.verify(coordination2, Mockito.atLeast(1)).addParticipant(captor.capture());
+
+ for(Participant p : captor.getAllValues()) {
+ p.failed(coordination2);
+ }
+ return null;
+ }).when(coordination2).fail(Mockito.any(Throwable.class));
+ }
+
+ @Test
+ public void testRequired() {
+
+ AtomicReference<TransactionStatus> finalStatus = new AtomicReference<>();
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ txControl.getCurrentContext().postCompletion(finalStatus::set);
+ return null;
+ });
+
+ assertEquals(COMMITTED, finalStatus.get());
+
+ }
+
+ @Test
+ public void testRequiredMarkedRollback() {
+
+ AtomicReference<TransactionStatus> finalStatus = new AtomicReference<>();
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ txControl.getCurrentContext().postCompletion(finalStatus::set);
+
+ txControl.setRollbackOnly();
+ return null;
+ });
+
+ assertEquals(ROLLED_BACK, finalStatus.get());
+ }
+
+ @Test
+ public void testRequiredUserException() {
+
+ AtomicReference<TransactionStatus> finalStatus = new AtomicReference<>();
+
+ Exception userEx = new Exception("Bang!");
+
+ try {
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ txControl.getCurrentContext().postCompletion(finalStatus::set);
+
+ throw userEx;
+ });
+ fail("Should not be reached");
+ } catch (ScopedWorkException swe) {
+ assertSame(userEx, swe.getCause());
+ }
+
+ assertEquals(ROLLED_BACK, finalStatus.get());
+ }
+
+ @Test
+ public void testRequiredNoRollbackException() {
+
+ AtomicReference<TransactionStatus> finalStatus = new AtomicReference<>();
+
+ Exception userEx = new BindException("Bang!");
+
+ try {
+ txControl.build()
+ .noRollbackFor(BindException.class)
+ .required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ txControl.getCurrentContext().postCompletion(finalStatus::set);
+
+ throw userEx;
+ });
+ fail("Should not be reached");
+ } catch (ScopedWorkException swe) {
+ assertSame(userEx, swe.getCause());
+ }
+
+ assertEquals(COMMITTED, finalStatus.get());
+ }
+
+ @Test
+ public void testTwoRequiredsNested() {
+
+ AtomicReference<TransactionStatus> finalStatusOuter = new AtomicReference<>();
+ AtomicReference<TransactionStatus> finalStatusInner = new AtomicReference<>();
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+
+ txControl.getCurrentContext().postCompletion(finalStatusOuter::set);
+
+ txControl.requiresNew(() -> {
+ assertFalse(key.equals(txControl.getCurrentContext().getTransactionKey()));
+
+ txControl.getCurrentContext().postCompletion(finalStatusInner::set);
+ return null;
+ });
+
+ return null;
+ });
+
+ assertEquals(COMMITTED, finalStatusOuter.get());
+ assertEquals(COMMITTED, finalStatusInner.get());
+
+ }
+
+ @Test
+ public void testTwoRequiredsNestedOuterMarkedRollback() {
+
+ AtomicReference<TransactionStatus> finalStatusOuter = new AtomicReference<>();
+ AtomicReference<TransactionStatus> finalStatusInner = new AtomicReference<>();
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+
+ txControl.getCurrentContext().postCompletion(finalStatusOuter::set);
+
+ txControl.setRollbackOnly();
+
+ txControl.requiresNew(() -> {
+ assertFalse(key.equals(txControl.getCurrentContext().getTransactionKey()));
+
+ txControl.getCurrentContext().postCompletion(finalStatusInner::set);
+ return null;
+ });
+
+ return null;
+ });
+
+ assertEquals(ROLLED_BACK, finalStatusOuter.get());
+ assertEquals(COMMITTED, finalStatusInner.get());
+
+ }
+
+ @Test
+ public void testTwoRequiredsNestedInnerMarkedRollback() {
+
+ AtomicReference<TransactionStatus> finalStatusOuter = new AtomicReference<>();
+ AtomicReference<TransactionStatus> finalStatusInner = new AtomicReference<>();
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+
+ txControl.getCurrentContext().postCompletion(finalStatusOuter::set);
+
+ txControl.requiresNew(() -> {
+ assertFalse(key.equals(txControl.getCurrentContext().getTransactionKey()));
+
+ txControl.getCurrentContext().postCompletion(finalStatusInner::set);
+
+ txControl.setRollbackOnly();
+
+ return null;
+ });
+
+ return null;
+ });
+
+ assertEquals(COMMITTED, finalStatusOuter.get());
+ assertEquals(ROLLED_BACK, finalStatusInner.get());
+
+ }
+
+ @Test
+ public void testTwoRequiredsNestedBothMarkedRollback() {
+
+ AtomicReference<TransactionStatus> finalStatusOuter = new AtomicReference<>();
+ AtomicReference<TransactionStatus> finalStatusInner = new AtomicReference<>();
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+
+ txControl.getCurrentContext().postCompletion(finalStatusOuter::set);
+
+ txControl.setRollbackOnly();
+
+ txControl.requiresNew(() -> {
+ assertFalse(key.equals(txControl.getCurrentContext().getTransactionKey()));
+
+ txControl.getCurrentContext().postCompletion(finalStatusInner::set);
+
+ txControl.setRollbackOnly();
+
+ return null;
+ });
+
+ return null;
+ });
+
+ assertEquals(ROLLED_BACK, finalStatusOuter.get());
+ assertEquals(ROLLED_BACK, finalStatusInner.get());
+
+ }
+
+ @Test
+ public void testTwoRequiredsNestedOuterThrowsException() {
+
+ AtomicReference<TransactionStatus> finalStatusOuter = new AtomicReference<>();
+ AtomicReference<TransactionStatus> finalStatusInner = new AtomicReference<>();
+
+ Exception userEx = new Exception("Bang!");
+
+ try {
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+
+ txControl.getCurrentContext().postCompletion(finalStatusOuter::set);
+
+ txControl.setRollbackOnly();
+
+ txControl.requiresNew(() -> {
+ assertFalse(key.equals(txControl.getCurrentContext().getTransactionKey()));
+
+ txControl.getCurrentContext().postCompletion(finalStatusInner::set);
+ return null;
+ });
+
+ throw userEx;
+ });
+ fail("Should not be reached");
+ } catch (ScopedWorkException swe) {
+ assertSame(userEx, swe.getCause());
+ }
+
+ assertEquals(ROLLED_BACK, finalStatusOuter.get());
+ assertEquals(COMMITTED, finalStatusInner.get());
+
+ }
+
+ @Test
+ public void testTwoRequiredsNestedInnerThrowsException() {
+
+ AtomicReference<TransactionStatus> finalStatusOuter = new AtomicReference<>();
+ AtomicReference<TransactionStatus> finalStatusInner = new AtomicReference<>();
+
+ Exception userEx = new Exception("Bang!");
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+
+ txControl.getCurrentContext().postCompletion(finalStatusOuter::set);
+
+ try {
+ txControl.requiresNew(() -> {
+ assertFalse(key.equals(txControl.getCurrentContext().getTransactionKey()));
+
+ txControl.getCurrentContext().postCompletion(finalStatusInner::set);
+
+ txControl.setRollbackOnly();
+
+ throw userEx;
+ });
+ fail("Should not be reached!");
+ } catch (ScopedWorkException swe) {
+ assertSame(userEx, swe.getCause());
+ }
+ return null;
+ });
+
+ assertEquals(COMMITTED, finalStatusOuter.get());
+ assertEquals(ROLLED_BACK, finalStatusInner.get());
+
+ }
+
+ @Test
+ public void testTwoRequiredsNestedNoRollbackForInnerException() {
+
+ AtomicReference<TransactionStatus> finalStatusOuter = new AtomicReference<>();
+ AtomicReference<TransactionStatus> finalStatusInner = new AtomicReference<>();
+
+ Exception userEx = new BindException("Bang!");
+
+ try {
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+
+ txControl.getCurrentContext().postCompletion(finalStatusOuter::set);
+
+ try {
+ txControl.build()
+ .noRollbackFor(BindException.class)
+ .requiresNew(() -> {
+ assertFalse(key.equals(txControl.getCurrentContext().getTransactionKey()));
+
+ txControl.getCurrentContext().postCompletion(finalStatusInner::set);
+
+ throw userEx;
+ });
+ fail("Should not be reached!");
+ } catch (ScopedWorkException swe) {
+ throw swe.as(BindException.class);
+ }
+
+ return null;
+ });
+ fail("Should not be reached!");
+ } catch (ScopedWorkException swe) {
+ assertSame(userEx, swe.getCause());
+ }
+
+ assertEquals(ROLLED_BACK, finalStatusOuter.get());
+ assertEquals(COMMITTED, finalStatusInner.get());
+
+ }
+
+}
Added: aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlStatusTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlStatusTest.java?rev=1734243&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlStatusTest.java (added)
+++ aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionControlStatusTest.java Wed Mar 9 14:38:15 2016
@@ -0,0 +1,324 @@
+package org.apache.aries.tx.control.service.xa.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.osgi.service.transaction.control.TransactionStatus.ACTIVE;
+import static org.osgi.service.transaction.control.TransactionStatus.MARKED_ROLLBACK;
+import static org.osgi.service.transaction.control.TransactionStatus.NO_TRANSACTION;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.transaction.xa.XAException;
+
+import org.apache.aries.tx.control.service.xa.impl.TransactionControlImpl;
+import org.apache.geronimo.transaction.manager.GeronimoTransactionManager;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.service.coordinator.Coordination;
+import org.osgi.service.coordinator.Coordinator;
+import org.osgi.service.coordinator.Participant;
+import org.osgi.service.transaction.control.LocalResource;
+import org.osgi.service.transaction.control.ResourceProvider;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TransactionControlStatusTest {
+
+ @Mock
+ Coordinator coordinator;
+ @Mock
+ Coordination coordination1;
+ @Mock
+ Coordination coordination2;
+
+ @Mock
+ ResourceProvider<Object> testProvider;
+ @Mock
+ LocalResource testResource;
+
+ TransactionControlImpl txControl;
+
+ Object resource = new Object();
+ Map<Class<?>, Object> variables1;
+ Map<Class<?>, Object> variables2;
+
+ @Before
+ public void setUp() throws XAException {
+
+ resource = new Object();
+ variables1 = new HashMap<>();
+ variables2 = new HashMap<>();
+
+ Mockito.when(coordination1.getVariables()).thenReturn(variables1);
+ Mockito.when(coordination2.getVariables()).thenReturn(variables2);
+
+ txControl = new TransactionControlImpl(new GeronimoTransactionManager(), coordinator);
+ }
+
+ @Test
+ public void testGetRollbackOnlyUnscopedNoCoord() {
+ try {
+ txControl.getRollbackOnly();
+ fail("Should not be able to get rollback only");
+ } catch (IllegalStateException e) {
+
+ }
+ }
+
+ @Test
+ public void testSetRollbackOnlyUnscopedNoCoord() {
+ try {
+ txControl.setRollbackOnly();
+ fail("Should not be able to set rollback only");
+ } catch (IllegalStateException e) {
+
+ }
+ }
+
+ @Test
+ public void testTranChecksUnscopedNoCoord() {
+ assertFalse(txControl.activeTransaction());
+ assertFalse(txControl.activeScope());
+ assertNull(txControl.getCurrentContext());
+ }
+
+ private void setupExistingCoordination() {
+ Mockito.when(coordinator.peek()).thenReturn(coordination1);
+ Mockito.when(coordination1.getVariables()).thenReturn(variables1);
+ }
+
+ @Test
+ public void testGetRollbackOnlyUnscopedWithCoordination() {
+ setupExistingCoordination();
+
+ try {
+ txControl.getRollbackOnly();
+ fail("Should not be able to get rollback only");
+ } catch (IllegalStateException e) {
+
+ }
+ }
+
+ @Test
+ public void testSetRollbackOnlyUnscopedWithCoordination() {
+ setupExistingCoordination();
+
+
+ try {
+ txControl.setRollbackOnly();
+ fail("Should not be able to set rollback only");
+ } catch (IllegalStateException e) {
+
+ }
+ }
+
+ @Test
+ public void testTranChecksUnscopedWithCoordination() {
+
+ setupExistingCoordination();
+
+ assertFalse(txControl.activeTransaction());
+ assertFalse(txControl.activeScope());
+ assertNull(txControl.getCurrentContext());
+ }
+
+ private void setupCoordinatorForSingleTransaction() {
+ setupCoordinatorForSingleTransaction(null);
+ }
+
+ private void setupCoordinatorForSingleTransaction(Coordination existing) {
+
+ Mockito.when(coordinator.peek()).thenReturn(existing);
+
+ Mockito.when(coordinator.begin(Mockito.anyString(), Mockito.anyLong()))
+ .then(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(coordination1);
+ return coordination1;
+ });
+
+
+ Mockito.doAnswer(i -> Mockito.when(coordinator.peek()).thenReturn(existing))
+ .when(coordination1).end();
+ Mockito.doAnswer(i -> Mockito.when(coordinator.peek()).thenReturn(existing) != null)
+ .when(coordination1).fail(Mockito.any(Throwable.class));
+
+ Mockito.when(coordination1.getVariables()).thenReturn(variables1);
+ }
+
+ @Test
+ public void testGetRollbackOnlyScoped() {
+ setupCoordinatorForSingleTransaction();
+ txControl.notSupported(() -> {
+ Mockito.verify(coordination1).addParticipant(Mockito.any(Participant.class));
+ try {
+ txControl.getRollbackOnly();
+ fail("Should not be able to get or set rollback when there is no transaction");
+ } catch (IllegalStateException ise) {
+ }
+ return null;
+ });
+ }
+
+ @Test
+ public void testSetRollbackOnlyScoped() {
+ setupCoordinatorForSingleTransaction();
+
+ txControl.notSupported(() -> {
+ Mockito.verify(coordination1).addParticipant(Mockito.any(Participant.class));
+ try {
+ txControl.setRollbackOnly();
+ fail("Should not be able to get or set rollback when there is no transaction");
+ } catch (IllegalStateException ise) {
+ }
+ return null;
+ });
+ }
+
+ @Test
+ public void testTranChecksScoped() {
+
+ setupCoordinatorForSingleTransaction();
+ txControl.notSupported(() -> {
+ assertFalse(txControl.activeTransaction());
+ assertTrue(txControl.activeScope());
+ assertNotNull(txControl.getCurrentContext());
+ assertEquals(NO_TRANSACTION, txControl.getCurrentContext().getTransactionStatus());
+
+ return null;
+ });
+ }
+
+ @Test
+ public void testGetRollbackOnlyScopedExistingCoordination() {
+ setupCoordinatorForSingleTransaction(coordination2);
+ txControl.notSupported(() -> {
+ Mockito.verify(coordination1).addParticipant(Mockito.any(Participant.class));
+ try {
+ txControl.getRollbackOnly();
+ fail("Should not be able to get or set rollback when there is no transaction");
+ } catch (IllegalStateException ise) {
+ }
+ return null;
+ });
+ }
+
+ @Test
+ public void testSetRollbackOnlyScopedExistingCoordination() {
+ setupCoordinatorForSingleTransaction(coordination2);
+
+ txControl.notSupported(() -> {
+ Mockito.verify(coordination1).addParticipant(Mockito.any(Participant.class));
+ try {
+ txControl.setRollbackOnly();
+ fail("Should not be able to get or set rollback when there is no transaction");
+ } catch (IllegalStateException ise) {
+ }
+ return null;
+ });
+ }
+
+ @Test
+ public void testTranChecksScopedExistingCoordination() {
+
+ setupCoordinatorForSingleTransaction(coordination2);
+ txControl.notSupported(() -> {
+ assertFalse(txControl.activeTransaction());
+ assertTrue(txControl.activeScope());
+ assertNotNull(txControl.getCurrentContext());
+ assertEquals(NO_TRANSACTION, txControl.getCurrentContext().getTransactionStatus());
+
+ return null;
+ });
+ }
+
+ @Test
+ public void testGetRollbackOnlyActive() {
+ setupCoordinatorForSingleTransaction();
+ txControl.required(() -> {
+ Mockito.verify(coordination1).addParticipant(Mockito.any(Participant.class));
+ assertFalse(txControl.getRollbackOnly());
+ return null;
+ });
+ }
+
+ @Test
+ public void testSetRollbackOnlyActive() {
+ setupCoordinatorForSingleTransaction();
+
+ txControl.required(() -> {
+ Mockito.verify(coordination1).addParticipant(Mockito.any(Participant.class));
+ assertFalse(txControl.getRollbackOnly());
+ txControl.setRollbackOnly();
+ assertTrue(txControl.getRollbackOnly());
+
+ return null;
+ });
+ }
+
+ @Test
+ public void testTranChecksActive() {
+
+ setupCoordinatorForSingleTransaction();
+ txControl.required(() -> {
+ assertTrue(txControl.activeTransaction());
+ assertTrue(txControl.activeScope());
+ assertNotNull(txControl.getCurrentContext());
+ assertEquals(ACTIVE, txControl.getCurrentContext().getTransactionStatus());
+
+ txControl.setRollbackOnly();
+ assertEquals(MARKED_ROLLBACK, txControl.getCurrentContext().getTransactionStatus());
+
+ return null;
+ });
+ }
+
+ @Test
+ public void testGetRollbackOnlyActiveExistingCoordination() {
+ setupCoordinatorForSingleTransaction(coordination2);
+ txControl.required(() -> {
+ Mockito.verify(coordination1).addParticipant(Mockito.any(Participant.class));
+ assertFalse(txControl.getRollbackOnly());
+ return null;
+ });
+ }
+
+ @Test
+ public void testSetRollbackOnlyActiveExistingCoordination() {
+ setupCoordinatorForSingleTransaction(coordination2);
+
+ txControl.required(() -> {
+ Mockito.verify(coordination1).addParticipant(Mockito.any(Participant.class));
+ assertFalse(txControl.getRollbackOnly());
+ txControl.setRollbackOnly();
+ assertTrue(txControl.getRollbackOnly());
+
+ return null;
+ });
+ }
+
+ @Test
+ public void testTranChecksActiveExistingCoordination() {
+
+ setupCoordinatorForSingleTransaction(coordination2);
+ txControl.required(() -> {
+ assertTrue(txControl.activeTransaction());
+ assertTrue(txControl.activeScope());
+ assertNotNull(txControl.getCurrentContext());
+ assertEquals(ACTIVE, txControl.getCurrentContext().getTransactionStatus());
+
+ txControl.setRollbackOnly();
+ assertEquals(MARKED_ROLLBACK, txControl.getCurrentContext().getTransactionStatus());
+
+ return null;
+ });
+ }
+
+}
Added: aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionLifecycleTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionLifecycleTest.java?rev=1734243&view=auto
==============================================================================
--- aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionLifecycleTest.java (added)
+++ aries/trunk/tx-control/tx-control-service-xa/src/test/java/org/apache/aries/tx/control/service/xa/impl/TransactionLifecycleTest.java Wed Mar 9 14:38:15 2016
@@ -0,0 +1,307 @@
+package org.apache.aries.tx.control.service.xa.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.transaction.xa.XAException;
+
+import org.apache.aries.tx.control.service.xa.impl.TransactionControlImpl;
+import org.apache.geronimo.transaction.manager.GeronimoTransactionManager;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.service.coordinator.Coordination;
+import org.osgi.service.coordinator.Coordinator;
+import org.osgi.service.transaction.control.LocalResource;
+import org.osgi.service.transaction.control.ResourceProvider;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TransactionLifecycleTest {
+
+ @Mock
+ Coordinator coordinator;
+ @Mock
+ Coordination coordination1;
+ @Mock
+ Coordination coordination2;
+
+ @Mock
+ ResourceProvider<Object> testProvider;
+ @Mock
+ LocalResource testResource;
+
+ TransactionControlImpl txControl;
+
+ Map<Class<?>, Object> variables1;
+ Map<Class<?>, Object> variables2;
+
+ @Before
+ public void setUp() throws XAException {
+ variables1 = new HashMap<>();
+ variables2 = new HashMap<>();
+
+ setupCoordinations();
+
+ txControl = new TransactionControlImpl(new GeronimoTransactionManager(), coordinator);
+ }
+
+ /**
+ * Allow up to two Coordinations to be happening
+ */
+ private void setupCoordinations() {
+ Mockito.when(coordinator.begin(Mockito.anyString(), Mockito.anyLong())).then(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(coordination1);
+ return coordination1;
+ }).then(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(coordination2);
+ return coordination2;
+ }).thenThrow(new IllegalStateException("Only two coordinations at a time in the test"));
+
+ Mockito.when(coordination1.getVariables()).thenReturn(variables1);
+ Mockito.when(coordination1.getId()).thenReturn(42L);
+ Mockito.doAnswer(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(null);
+ return null;
+ }).when(coordination1).end();
+ Mockito.doAnswer(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(null);
+ return null;
+ }).when(coordination1).fail(Mockito.any(Throwable.class));
+
+ Mockito.when(coordination2.getVariables()).thenReturn(variables2);
+ Mockito.when(coordination2.getId()).thenReturn(43L);
+ Mockito.doAnswer(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(coordination1);
+ return null;
+ }).when(coordination2).end();
+ Mockito.doAnswer(i -> {
+ Mockito.when(coordinator.peek()).thenReturn(coordination1);
+ return null;
+ }).when(coordination2).fail(Mockito.any(Throwable.class));
+ }
+
+ @Test
+ public void testRequired() {
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ return null;
+ });
+
+ }
+
+ @Test
+ public void testNestedRequired() {
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+ txControl.getCurrentContext().putScopedValue("visible", Boolean.TRUE);
+
+ txControl.required(() -> {
+ assertEquals(key, txControl.getCurrentContext().getTransactionKey());
+ assertEquals(Boolean.TRUE, txControl.getCurrentContext().getScopedValue("visible"));
+ txControl.getCurrentContext().putScopedValue("visible", Boolean.FALSE);
+ return null;
+ });
+
+ assertEquals(key, txControl.getCurrentContext().getTransactionKey());
+ assertEquals(Boolean.FALSE, txControl.getCurrentContext().getScopedValue("visible"));
+
+ return null;
+ });
+
+ }
+
+ @Test
+ public void testNestedRequiredFromNoTran() {
+
+ txControl.supports(() -> {
+
+ assertFalse(txControl.activeTransaction());
+
+ txControl.getCurrentContext().putScopedValue("invisible", Boolean.TRUE);
+
+ txControl.required(() -> {
+ assertTrue(txControl.activeTransaction());
+ assertNull(txControl.getCurrentContext().getScopedValue("invisible"));
+ txControl.getCurrentContext().putScopedValue("invisible", Boolean.FALSE);
+ return null;
+ });
+
+ assertEquals(Boolean.TRUE, txControl.getCurrentContext().getScopedValue("invisible"));
+
+ return null;
+ });
+
+ }
+
+ @Test
+ public void testRequiresNew() {
+
+ txControl.requiresNew(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ return null;
+ });
+
+ }
+
+ @Test
+ public void testNestedRequiresNew() {
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+ txControl.getCurrentContext().putScopedValue("invisible", Boolean.TRUE);
+
+ txControl.requiresNew(() -> {
+ assertFalse("Parent key " + key + " Child Key " + txControl.getCurrentContext().getTransactionKey(),
+ key.equals(txControl.getCurrentContext().getTransactionKey()));
+ assertNull(txControl.getCurrentContext().getScopedValue("invisible"));
+ txControl.getCurrentContext().putScopedValue("invisible", Boolean.FALSE);
+ return null;
+ });
+
+ assertEquals(key, txControl.getCurrentContext().getTransactionKey());
+ assertEquals(Boolean.TRUE, txControl.getCurrentContext().getScopedValue("invisible"));
+
+ return null;
+ });
+
+ }
+
+ @Test
+ public void testSupports() {
+
+ txControl.supports(() -> {
+
+ assertFalse(txControl.activeTransaction());
+
+ return null;
+ });
+
+ }
+
+ @Test
+ public void testNestedSupports() {
+
+ txControl.supports(() -> {
+
+ assertFalse(txControl.activeTransaction());
+
+ txControl.getCurrentContext().putScopedValue("visible", Boolean.TRUE);
+
+ txControl.supports(() -> {
+ assertEquals(Boolean.TRUE, txControl.getCurrentContext().getScopedValue("visible"));
+ txControl.getCurrentContext().putScopedValue("visible", Boolean.FALSE);
+ return null;
+ });
+
+ assertEquals(Boolean.FALSE, txControl.getCurrentContext().getScopedValue("visible"));
+
+ return null;
+ });
+
+ }
+
+ @Test
+ public void testNestedSupportsInActiveTran() {
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+ txControl.getCurrentContext().putScopedValue("visible", Boolean.TRUE);
+
+ txControl.supports(() -> {
+ assertEquals(key, txControl.getCurrentContext().getTransactionKey());
+ assertEquals(Boolean.TRUE, txControl.getCurrentContext().getScopedValue("visible"));
+ txControl.getCurrentContext().putScopedValue("visible", Boolean.FALSE);
+ return null;
+ });
+
+ assertEquals(key, txControl.getCurrentContext().getTransactionKey());
+ assertEquals(Boolean.FALSE, txControl.getCurrentContext().getScopedValue("visible"));
+
+ return null;
+ });
+
+ }
+
+ @Test
+ public void testNotSupported() {
+
+ txControl.notSupported(() -> {
+
+ assertFalse(txControl.activeTransaction());
+
+ return null;
+ });
+
+ }
+
+ @Test
+ public void testNestedNotSupported() {
+
+ txControl.notSupported(() -> {
+
+ assertFalse(txControl.activeTransaction());
+
+ txControl.getCurrentContext().putScopedValue("visible", Boolean.TRUE);
+
+ txControl.notSupported(() -> {
+ assertEquals(Boolean.TRUE, txControl.getCurrentContext().getScopedValue("visible"));
+ return null;
+ });
+
+ assertEquals(Boolean.TRUE, txControl.getCurrentContext().getScopedValue("visible"));
+
+ return null;
+ });
+
+ }
+
+ @Test
+ public void testNestedNotSupportedInActiveTran() {
+
+ txControl.required(() -> {
+
+ assertTrue(txControl.activeTransaction());
+
+ Object key = txControl.getCurrentContext().getTransactionKey();
+ txControl.getCurrentContext().putScopedValue("invisible", Boolean.TRUE);
+
+ txControl.notSupported(() -> {
+ assertFalse(txControl.activeTransaction());
+ assertNull(txControl.getCurrentContext().getScopedValue("invisible"));
+ txControl.getCurrentContext().putScopedValue("invisible", Boolean.FALSE);
+
+ return null;
+ });
+
+ assertEquals(key, txControl.getCurrentContext().getTransactionKey());
+ assertEquals(Boolean.TRUE, txControl.getCurrentContext().getScopedValue("invisible"));
+
+ return null;
+ });
+
+ }
+
+}