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;
+               });
+
+       }
+
+}


Reply via email to