Added: 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAConnectionWrapper.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAConnectionWrapper.java?rev=1734385&view=auto
==============================================================================
--- 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAConnectionWrapper.java
 (added)
+++ 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAConnectionWrapper.java
 Thu Mar 10 11:27:48 2016
@@ -0,0 +1,30 @@
+package org.apache.aries.tx.control.jdbc.xa.impl;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.sql.XAConnection;
+import javax.transaction.xa.XAResource;
+
+import org.apache.aries.tx.control.jdbc.common.impl.ConnectionWrapper;
+
+public class XAConnectionWrapper extends ConnectionWrapper {
+
+       private final Connection connection;
+       
+       private final XAResource xaResource;
+       
+       public XAConnectionWrapper(XAConnection xaConnection) throws 
SQLException {
+               this.connection = xaConnection.getConnection();
+               this.xaResource = xaConnection.getXAResource();
+       }
+
+       @Override
+       protected Connection getDelegate() {
+               return connection;
+       }
+
+       public XAResource getXaResource() {
+               return xaResource;
+       }
+}

Added: 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XADataSourceMapper.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XADataSourceMapper.java?rev=1734385&view=auto
==============================================================================
--- 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XADataSourceMapper.java
 (added)
+++ 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XADataSourceMapper.java
 Thu Mar 10 11:27:48 2016
@@ -0,0 +1,70 @@
+package org.apache.aries.tx.control.jdbc.xa.impl;
+
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.logging.Logger;
+
+import javax.sql.DataSource;
+import javax.sql.XADataSource;
+
+public class XADataSourceMapper implements DataSource {
+
+       private final XADataSource xaDataSource;
+       
+       public XADataSourceMapper(XADataSource xaDataSource) {
+               super();
+               this.xaDataSource = xaDataSource;
+       }
+
+       @Override
+       public PrintWriter getLogWriter() throws SQLException {
+               return xaDataSource.getLogWriter();
+       }
+
+       @Override
+       public void setLogWriter(PrintWriter out) throws SQLException {
+               xaDataSource.setLogWriter(out);
+       }
+
+       @Override
+       public void setLoginTimeout(int seconds) throws SQLException {
+               xaDataSource.setLoginTimeout(seconds);
+       }
+
+       @Override
+       public int getLoginTimeout() throws SQLException {
+               return xaDataSource.getLoginTimeout();
+       }
+
+       @Override
+       public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+               return xaDataSource.getParentLogger();
+       }
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public <T> T unwrap(Class<T> iface) throws SQLException {
+               if(isWrapperFor(iface)) {
+                       return (T) xaDataSource;
+               }
+               throw new SQLException("This datasource is not a wrapper for " 
+ iface);
+       }
+
+       @Override
+       public boolean isWrapperFor(Class<?> iface) throws SQLException {
+               return iface == XADataSource.class || 
iface.isInstance(xaDataSource);
+       }
+
+       @Override
+       public Connection getConnection() throws SQLException {
+               return new XAConnectionWrapper(xaDataSource.getXAConnection());
+       }
+
+       @Override
+       public Connection getConnection(String username, String password) 
throws SQLException {
+               return new 
XAConnectionWrapper(xaDataSource.getXAConnection(username, password));
+       }
+
+}

Added: 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnection.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnection.java?rev=1734385&view=auto
==============================================================================
--- 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnection.java
 (added)
+++ 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/main/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnection.java
 Thu Mar 10 11:27:48 2016
@@ -0,0 +1,127 @@
+package org.apache.aries.tx.control.jdbc.xa.impl;
+
+import static 
org.osgi.service.transaction.control.TransactionStatus.NO_TRANSACTION;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.UUID;
+
+import javax.sql.DataSource;
+import javax.transaction.xa.XAResource;
+
+import org.apache.aries.tx.control.jdbc.common.impl.ConnectionWrapper;
+import org.apache.aries.tx.control.jdbc.common.impl.ScopedConnectionWrapper;
+import org.apache.aries.tx.control.jdbc.common.impl.TxConnectionWrapper;
+import org.osgi.service.transaction.control.LocalResource;
+import org.osgi.service.transaction.control.TransactionContext;
+import org.osgi.service.transaction.control.TransactionControl;
+import org.osgi.service.transaction.control.TransactionException;
+
+public class XAEnabledTxContextBindingConnection extends ConnectionWrapper {
+
+       private final TransactionControl        txControl;
+       private final UUID                                      resourceId;
+       private final DataSource                        dataSource;
+       private final boolean                           xaEnabled;
+       private final boolean                           localEnabled;
+
+       public XAEnabledTxContextBindingConnection(TransactionControl txControl,
+                       DataSource dataSource, UUID resourceId, boolean 
xaEnabled, boolean localEnabled) {
+               this.txControl = txControl;
+               this.dataSource = dataSource;
+               this.resourceId = resourceId;
+               this.xaEnabled = xaEnabled;
+               this.localEnabled = localEnabled;
+       }
+
+       @Override
+       protected final Connection getDelegate() {
+
+               TransactionContext txContext = txControl.getCurrentContext();
+
+               if (txContext == null) {
+                       throw new TransactionException("The resource " + 
dataSource
+                                       + " cannot be accessed outside of an 
active Transaction Context");
+               }
+
+               Connection existing = (Connection) 
txContext.getScopedValue(resourceId);
+
+               if (existing != null) {
+                       return existing;
+               }
+
+               Connection toReturn;
+               Connection toClose;
+
+               try {
+                       if (txContext.getTransactionStatus() == NO_TRANSACTION) 
{
+                               toClose = dataSource.getConnection();
+                               toReturn = new ScopedConnectionWrapper(toClose);
+                       } else if (txContext.supportsXA() && xaEnabled) {
+                               toClose = dataSource.getConnection();
+                               toReturn = new TxConnectionWrapper(toClose);
+                               
txContext.registerXAResource(getXAResource(toClose));
+                       } else if (txContext.supportsLocal() && localEnabled) {
+                               toClose = dataSource.getConnection();
+                               toReturn = new TxConnectionWrapper(toClose);
+                               
txContext.registerLocalResource(getLocalResource(toClose));
+                       } else {
+                               throw new TransactionException(
+                                               "There is a transaction active, 
but it does not support local participants");
+                       }
+               } catch (Exception sqle) {
+                       throw new TransactionException(
+                                       "There was a problem getting hold of a 
database connection",
+                                       sqle);
+               }
+
+               
+               txContext.postCompletion(x -> {
+                               try {
+                                       toClose.close();
+                               } catch (SQLException sqle) {
+                                       // TODO log this
+                               }
+                       });
+               
+               txContext.putScopedValue(resourceId, toReturn);
+               
+               return toReturn;
+       }
+
+       
+       private XAResource getXAResource(Connection conn) throws SQLException {
+               if(conn instanceof XAConnectionWrapper) {
+                       return ((XAConnectionWrapper)conn).getXaResource();
+               } else if(conn.isWrapperFor(XAConnectionWrapper.class)){
+                       return 
conn.unwrap(XAConnectionWrapper.class).getXaResource();
+               } else {
+                       throw new IllegalArgumentException("The XAResource for 
the connection cannot be found");
+               }
+       }
+       
+       private LocalResource getLocalResource(Connection conn) {
+               return new LocalResource() {
+                       @Override
+                       public void commit() throws TransactionException {
+                               try {
+                                       conn.commit();
+                               } catch (SQLException e) {
+                                       throw new TransactionException(
+                                                       "An error occurred when 
committing the connection", e);
+                               }
+                       }
+
+                       @Override
+                       public void rollback() throws TransactionException {
+                               try {
+                                       conn.rollback();
+                               } catch (SQLException e) {
+                                       throw new TransactionException(
+                                                       "An error occurred when 
rolling back the connection", e);
+                               }
+                       }
+
+               };
+       }
+}

Added: 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java?rev=1734385&view=auto
==============================================================================
--- 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java
 (added)
+++ 
aries/trunk/tx-control/tx-control-provider-jdbc-xa/src/test/java/org/apache/aries/tx/control/jdbc/xa/impl/XAEnabledTxContextBindingConnectionTest.java
 Thu Mar 10 11:27:48 2016
@@ -0,0 +1,224 @@
+package org.apache.aries.tx.control.jdbc.xa.impl;
+
+import static org.mockito.Mockito.times;
+import static org.osgi.service.transaction.control.TransactionStatus.ACTIVE;
+import static 
org.osgi.service.transaction.control.TransactionStatus.NO_TRANSACTION;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.sql.DataSource;
+import javax.sql.XAConnection;
+import javax.sql.XADataSource;
+import javax.transaction.xa.XAResource;
+
+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.transaction.control.LocalResource;
+import org.osgi.service.transaction.control.TransactionContext;
+import org.osgi.service.transaction.control.TransactionControl;
+import org.osgi.service.transaction.control.TransactionException;
+
+@RunWith(MockitoJUnitRunner.class)
+public class XAEnabledTxContextBindingConnectionTest {
+
+       @Mock
+       TransactionControl control;
+       
+       @Mock
+       TransactionContext context;
+       
+       @Mock
+       DataSource dataSource;
+       
+       @Mock
+       XADataSource xaDataSource;
+
+       @Mock
+       XAConnection xaMock;
+       
+       @Mock
+       XAResource xaResource;
+       
+       @Mock
+       Connection rawConnection;
+       
+       Map<Object, Object> variables = new HashMap<>();
+       
+       UUID id = UUID.randomUUID();
+       
+       XAEnabledTxContextBindingConnection localConn;
+       XAEnabledTxContextBindingConnection xaConn;
+       
+       @Before
+       public void setUp() throws SQLException {
+               
Mockito.when(dataSource.getConnection()).thenReturn(rawConnection).thenReturn(null);
+               
+               Mockito.doAnswer(i -> variables.put(i.getArguments()[0], 
i.getArguments()[1]))
+                       .when(context).putScopedValue(Mockito.any(), 
Mockito.any());
+               Mockito.when(context.getScopedValue(Mockito.any()))
+                       .thenAnswer(i -> variables.get(i.getArguments()[0]));
+               
+               Mockito.when(xaDataSource.getXAConnection()).thenReturn(xaMock);
+               Mockito.when(xaMock.getConnection()).thenReturn(rawConnection);
+               Mockito.when(xaMock.getXAResource()).thenReturn(xaResource);
+               
+               localConn = new XAEnabledTxContextBindingConnection(control, 
dataSource, id, false, true);
+               xaConn = new XAEnabledTxContextBindingConnection(control, new 
XADataSourceMapper(xaDataSource), 
+                               id, true, false);
+       }
+       
+       private void setupNoTransaction() {
+               Mockito.when(control.getCurrentContext()).thenReturn(context);
+               
Mockito.when(context.getTransactionStatus()).thenReturn(NO_TRANSACTION);
+       }
+
+       private void setupLocalTransaction() {
+               Mockito.when(control.getCurrentContext()).thenReturn(context);
+               Mockito.when(context.supportsLocal()).thenReturn(true);
+               Mockito.when(context.getTransactionStatus()).thenReturn(ACTIVE);
+       }
+
+       private void setupXATransaction() {
+               Mockito.when(control.getCurrentContext()).thenReturn(context);
+               Mockito.when(context.supportsXA()).thenReturn(true);
+               Mockito.when(context.getTransactionStatus()).thenReturn(ACTIVE);
+       }
+       
+       
+       @Test(expected=TransactionException.class)
+       public void testUnscopedLocal() throws SQLException {
+               localConn.isValid(500);
+       }
+
+       @Test(expected=TransactionException.class)
+       public void testUnscopedXA() throws SQLException {
+               xaConn.isValid(500);
+       }
+
+       @Test
+       public void testNoTransaction() throws SQLException {
+               setupNoTransaction();
+               
+               localConn.isValid(500);
+               localConn.isValid(500);
+               
+               Mockito.verify(rawConnection, times(2)).isValid(500);
+               Mockito.verify(context, 
times(0)).registerLocalResource(Mockito.any());
+               
+               Mockito.verify(context).postCompletion(Mockito.any());
+       }
+
+       @Test
+       public void testNoTransactionXA() throws SQLException {
+               setupNoTransaction();
+               
+               xaConn.isValid(500);
+               xaConn.isValid(500);
+               
+               Mockito.verify(rawConnection, times(2)).isValid(500);
+               Mockito.verify(context, 
times(0)).registerLocalResource(Mockito.any());
+               
+               Mockito.verify(context).postCompletion(Mockito.any());
+       }
+
+       @Test
+       public void testLocalTransactionCommit() throws SQLException {
+               setupLocalTransaction();
+               
+               localConn.isValid(500);
+               localConn.isValid(500);
+               
+               ArgumentCaptor<LocalResource> captor = 
ArgumentCaptor.forClass(LocalResource.class);
+
+               Mockito.verify(rawConnection, times(2)).isValid(500);
+               Mockito.verify(context).registerLocalResource(captor.capture());
+               
+               Mockito.verify(context).postCompletion(Mockito.any());
+               
+               captor.getValue().commit();
+               
+               Mockito.verify(rawConnection).commit();
+       }
+
+       @Test
+       public void testLocalTransactionRollback() throws SQLException {
+               setupLocalTransaction();
+               
+               localConn.isValid(500);
+               localConn.isValid(500);
+               
+               ArgumentCaptor<LocalResource> captor = 
ArgumentCaptor.forClass(LocalResource.class);
+               
+               Mockito.verify(rawConnection, times(2)).isValid(500);
+               Mockito.verify(context).registerLocalResource(captor.capture());
+               
+               Mockito.verify(context).postCompletion(Mockito.any());
+               
+               captor.getValue().rollback();
+               
+               Mockito.verify(rawConnection).rollback();
+       }
+
+       @Test(expected=TransactionException.class)
+       public void testLocalTransactionNoLocal() throws SQLException {
+               setupLocalTransaction();
+               
+               Mockito.when(context.supportsLocal()).thenReturn(false);
+               localConn.isValid(500);
+       }
+       
+       @Test(expected=TransactionException.class)
+       public void testLocalConnWithXATransaction() throws SQLException {
+               setupXATransaction();
+               
+               localConn.isValid(500);
+       }
+
+       @Test
+       public void testXATransactionCommit() throws SQLException {
+               setupXATransaction();
+               
+               xaConn.isValid(500);
+               xaConn.isValid(500);
+               
+               
+               Mockito.verify(rawConnection, times(2)).isValid(500);
+               Mockito.verify(context).registerXAResource(xaResource);
+               
+               Mockito.verify(context).postCompletion(Mockito.any());
+               
+               Mockito.verify(rawConnection, times(0)).commit();
+       }
+       
+       @Test
+       public void testXATransactionRollback() throws SQLException {
+               setupXATransaction();
+               
+               xaConn.isValid(500);
+               xaConn.isValid(500);
+               
+               Mockito.verify(rawConnection, times(2)).isValid(500);
+               Mockito.verify(context).registerXAResource(xaResource);
+               
+               Mockito.verify(context).postCompletion(Mockito.any());
+               
+               Mockito.verify(rawConnection, times(0)).rollback();
+       }
+       
+       @Test(expected=TransactionException.class)
+       public void testXAConnTransactionWithLocal() throws SQLException {
+               setupLocalTransaction();
+               
+               xaConn.isValid(500);
+       }
+
+}

Modified: aries/trunk/tx-control/tx-control-service-xa/pom.xml
URL: 
http://svn.apache.org/viewvc/aries/trunk/tx-control/tx-control-service-xa/pom.xml?rev=1734385&r1=1734384&r2=1734385&view=diff
==============================================================================
--- aries/trunk/tx-control/tx-control-service-xa/pom.xml (original)
+++ aries/trunk/tx-control/tx-control-service-xa/pom.xml Thu Mar 10 11:27:48 
2016
@@ -48,9 +48,15 @@
                        org.apache.aries.tx.control.service.xa.*,
                        org.apache.geronimo.transaction.*
                </aries.osgi.private.pkg>
-               <!-- No transaction log at the moment -->
+               <!--
+                 No transaction log at the moment. 
+                 Also we must explicitly import javax.transaction.xa at zero 
so that we can pick it
+                 up from the JRE.
+               -->
                <aries.osgi.import.pkg>
+                       !javax.resource.*,
                        !org.objectweb.howl.*,
+                       javax.transaction.xa;version=0,
                        
org.osgi.service.transaction.control;version="[0.0.1,0.0.2)",
                        *
                </aries.osgi.import.pkg>


Reply via email to