Author: rmannibucau
Date: Mon Jul 23 20:49:19 2012
New Revision: 1364794

URL: http://svn.apache.org/viewvc?rev=1364794&view=rev
Log:
fixing multiple usage of ds and multiple tx context for managed local ds

Modified:
    
openejb/branches/openejb-pool/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/ManagedConnection.java
    
openejb/branches/openejb-pool/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/ManagedDataSourceTest.java
    
openejb/branches/openejb-pool/container/openejb-jee/src/main/java/org/apache/openejb/jee/EjbJar.java
    
openejb/branches/openejb-pool/container/openejb-jee/src/main/java/org/apache/openejb/jee/SessionBean.java

Modified: 
openejb/branches/openejb-pool/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/ManagedConnection.java
URL: 
http://svn.apache.org/viewvc/openejb/branches/openejb-pool/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/ManagedConnection.java?rev=1364794&r1=1364793&r2=1364794&view=diff
==============================================================================
--- 
openejb/branches/openejb-pool/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/ManagedConnection.java
 (original)
+++ 
openejb/branches/openejb-pool/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/ManagedConnection.java
 Mon Jul 23 20:49:19 2012
@@ -37,6 +37,19 @@ public class ManagedConnection implement
 
     @Override
     public Object invoke(final Object proxy, final Method method, final 
Object[] args) throws Throwable {
+        // first some Object method management
+        final String mtdName = method.getName();
+        if ("toString".equals(mtdName)) {
+            return "ManagedConnection{" + delegate.toString() + "}";
+        }
+        if ("hashCode".equals(mtdName)) {
+            return delegate.hashCode();
+        }
+        if ("equals".equals(mtdName)) {
+            return delegate.equals(args[0]);
+        }
+
+        // here the real logic starts
         try {
             final TransactionManager transactionManager = 
OpenEJB.getTransactionManager();
             final Transaction transaction = 
transactionManager.getTransaction();
@@ -45,6 +58,7 @@ public class ManagedConnection implement
                 return method.invoke(delegate, args);
             }
 
+            // if we have a tx check it is the same this connection is linked 
to
             if (currentTransaction != null) {
                 if (isUnderTransaction(currentTransaction.getStatus())) {
                     if (currentTransaction != transaction) {
@@ -52,44 +66,46 @@ public class ManagedConnection implement
                     }
                     return invokeUnderTransaction(delegate, method, args);
                 } else {
-                    System.out.println("no tx close");
                     close(delegate);
                 }
             }
 
+            // get the already bound connection to the current transaction
+            // or enlist this one in the tx
             int status = transaction.getStatus();
             if (isUnderTransaction(status)) {
                 final Connection connection = 
CONNECTION_BY_TX.get(transaction);
-                if (connection != null) { // shared one
-                    System.out.println("conn != null close");
-                    delegate.close(); // return to pool
-                    delegate = connection;
-                } else {
-                    CONNECTION_BY_TX.put(transaction, delegate);
-                    currentTransaction = transaction;
-                    try {
-                        transaction.enlistResource(xaResource);
-                    } catch (RollbackException ignored) {
-                        // no-op
-                    } catch (SystemException e) {
-                        throw new SQLException("Unable to enlist connection 
the transaction", e);
+                if (connection != delegate) {
+                    if (connection != null) { // use already existing one
+                        delegate.close(); // return to pool
+                        delegate = connection;
+                    } else {
+                        CONNECTION_BY_TX.put(transaction, delegate);
+                        currentTransaction = transaction;
+                        try {
+                            transaction.enlistResource(xaResource);
+                        } catch (RollbackException ignored) {
+                            // no-op
+                        } catch (SystemException e) {
+                            throw new SQLException("Unable to enlist 
connection the transaction", e);
+                        }
+
+                        transaction.registerSynchronization(new 
ClosingSynchronization(delegate));
+
+                        delegate.setAutoCommit(false);
                     }
                 }
-                transaction.registerSynchronization(new 
ClosingSynchronization(delegate));
-
-                delegate.setAutoCommit(false); // TODO: previous value?
 
                 return invokeUnderTransaction(delegate, method, args);
             }
 
-
             return method.invoke(delegate, args);
         } catch (InvocationTargetException ite) {
             throw ite.getTargetException();
         }
     }
 
-    private static Object invokeUnderTransaction(final Connection delegate, 
final Method method, final Object[] args) throws Exception, 
IllegalAccessException {
+    private static Object invokeUnderTransaction(final Connection delegate, 
final Method method, final Object[] args) throws Exception {
         final String mtdName = method.getName();
         if ("setAutoCommit".equals(mtdName)
                 || "commit".equals(mtdName)
@@ -100,12 +116,17 @@ public class ManagedConnection implement
         }
         if ("close".equals(mtdName)) {
             // will be done later
-            // we need to differ it in case of rollback
-            return null;
+            // we need to delay it in case of rollback
+            return noop();
         }
         return method.invoke(delegate, args);
     }
 
+    // just to give a better semantiv to a trivial operation
+    private static Object noop() {
+        return null;
+    }
+
     private static boolean isUnderTransaction(int status) {
         return status == Status.STATUS_ACTIVE || status == 
Status.STATUS_MARKED_ROLLBACK;
     }

Modified: 
openejb/branches/openejb-pool/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/ManagedDataSourceTest.java
URL: 
http://svn.apache.org/viewvc/openejb/branches/openejb-pool/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/ManagedDataSourceTest.java?rev=1364794&r1=1364793&r2=1364794&view=diff
==============================================================================
--- 
openejb/branches/openejb-pool/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/ManagedDataSourceTest.java
 (original)
+++ 
openejb/branches/openejb-pool/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/ManagedDataSourceTest.java
 Mon Jul 23 20:49:19 2012
@@ -1,6 +1,6 @@
 package org.apache.openejb.resource.jdbc;
 
-import org.apache.openejb.jee.Empty;
+import org.apache.openejb.jee.EjbJar;
 import org.apache.openejb.jee.SingletonBean;
 import org.apache.openejb.junit.ApplicationComposer;
 import org.apache.openejb.junit.Configuration;
@@ -14,8 +14,11 @@ import org.junit.runner.RunWith;
 
 import javax.annotation.Resource;
 import javax.ejb.EJB;
+import javax.ejb.EJBContext;
 import javax.ejb.LocalBean;
 import javax.ejb.Singleton;
+import javax.ejb.TransactionAttribute;
+import javax.ejb.TransactionAttributeType;
 import javax.sql.DataSource;
 import java.lang.reflect.Field;
 import java.sql.Connection;
@@ -32,13 +35,13 @@ import static org.junit.Assert.assertTru
 
 @RunWith(ApplicationComposer.class)
 public class ManagedDataSourceTest {
-    private static final String URL = "jdbc:hsqldb:mem:managed";
+    private static final String URL = 
"jdbc:hsqldb:mem:managed;hsqldb.tx=MVCC"; // mvcc otherwise multiple 
transaction tests will fail
     private static final String USER = "sa";
     private static final String PASSWORD = "";
     private static final String TABLE = "PUBLIC.MANAGED_DATASOURCE_TEST";
 
     @EJB
-    private PersistManager persistManager;
+    private Persister persistManager;
 
     @BeforeClass
     public static void createTable() throws SQLException, 
ClassNotFoundException {
@@ -67,37 +70,72 @@ public class ManagedDataSourceTest {
     }
 
     @Module
-    public SingletonBean app() throws Exception {
-        final SingletonBean bean = new SingletonBean(PersistManager.class);
-        bean.setLocalBean(new Empty());
-        return bean;
+    public EjbJar app() throws Exception {
+        return new EjbJar()
+                .enterpriseBean(new SingletonBean(Persister.class).localBean())
+                .enterpriseBean(new 
SingletonBean(OtherPersister.class).localBean());
     }
 
     @LocalBean
     @Singleton
-    public static class PersistManager {
+    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+    public static class OtherPersister {
         @Resource(name = "managed")
         private DataSource ds;
 
+        @Resource
+        private EJBContext context;
+
         public void save() throws SQLException {
-            save(1);
+            ManagedDataSourceTest.save(ds, 10);
         }
 
         public void saveAndRollback() throws SQLException {
-            save(2);
-            throw new RuntimeException();
+            ManagedDataSourceTest.save(ds, 11);
+            context.setRollbackOnly();
         }
+    }
 
-        private void save(int id) throws SQLException {
-            execute("INSERT INTO " + TABLE + "(ID) VALUES(" + id + ")");
+    @LocalBean
+    @Singleton
+    public static class Persister {
+        @Resource(name = "managed")
+        private DataSource ds;
+
+        @Resource
+        private EJBContext context;
+
+        @EJB
+        private OtherPersister other;
+
+        public void save() throws SQLException {
+            ManagedDataSourceTest.save(ds, 1);
         }
 
-        private void execute(final String sql) throws SQLException {
-            final Connection connection = ds.getConnection();
-            final Statement statement = connection.createStatement();
-            statement.executeUpdate(sql);
-            statement.close();
-            connection.close();
+        public void saveAndRollback() throws SQLException {
+            ManagedDataSourceTest.save(ds, 2);
+            context.setRollbackOnly();
+        }
+
+        public void saveTwice() throws SQLException {
+            ManagedDataSourceTest.save(ds, 3);
+            ManagedDataSourceTest.save(ds, 4);
+        }
+
+        public void rollbackMultipleSave() throws SQLException {
+            ManagedDataSourceTest.save(ds, 5);
+            ManagedDataSourceTest.save(ds, 6);
+            context.setRollbackOnly();
+        }
+
+        public void saveInThisTxAndAnotherOne() throws SQLException {
+            ManagedDataSourceTest.save(ds, 7);
+            other.save();
+        }
+
+        public void saveInThisTxAndRollbackInAnotherOne() throws SQLException {
+            ManagedDataSourceTest.save(ds, 8);
+            other.saveAndRollback();
         }
     }
 
@@ -109,16 +147,40 @@ public class ManagedDataSourceTest {
 
     @Test
     public void rollback() throws SQLException {
-        try {
-            persistManager.saveAndRollback();
-        } catch (Exception ignored) {
-            System.out.println(ignored);
-        }
+        persistManager.saveAndRollback();
         assertFalse(exists(2));
     }
 
+    @Test
+    public void commit2() throws SQLException {
+        persistManager.saveTwice();
+        assertTrue(exists(3));
+        assertTrue(exists(4));
+    }
+
+    @Test
+    public void rollback2() throws SQLException {
+        persistManager.rollbackMultipleSave();
+        assertFalse(exists(5));
+        assertFalse(exists(6));
+    }
+
+    @Test
+    public void saveDifferentTx() throws SQLException {
+        persistManager.saveInThisTxAndAnotherOne();
+        assertTrue(exists(7));
+        assertTrue(exists(10));
+    }
+
+    @Test
+    public void saveRollbackDifferentTx() throws SQLException {
+        persistManager.saveInThisTxAndRollbackInAnotherOne();
+        assertTrue(exists(8));
+        assertFalse(exists(12));
+    }
+
     @After
-    public void checkTxMapIsEmpty() throws Exception {
+    public void checkTxMapIsEmpty() throws Exception { // avoid memory leak
         final Field map = 
ManagedConnection.class.getDeclaredField("CONNECTION_BY_TX");
         map.setAccessible(true);
         final Map<?, ?> instance = (Map<?, ?>) map.get(null);
@@ -137,4 +199,16 @@ public class ManagedDataSourceTest {
             connection.close();
         }
     }
+
+    private static void save(final DataSource ds, int id) throws SQLException {
+        execute(ds, "INSERT INTO " + TABLE + "(ID) VALUES(" + id + ")");
+    }
+
+    private static void execute(final DataSource ds, final String sql) throws 
SQLException {
+        final Connection connection = ds.getConnection();
+        final Statement statement = connection.createStatement();
+        statement.executeUpdate(sql);
+        statement.close();
+        connection.close();
+    }
 }

Modified: 
openejb/branches/openejb-pool/container/openejb-jee/src/main/java/org/apache/openejb/jee/EjbJar.java
URL: 
http://svn.apache.org/viewvc/openejb/branches/openejb-pool/container/openejb-jee/src/main/java/org/apache/openejb/jee/EjbJar.java?rev=1364794&r1=1364793&r2=1364794&view=diff
==============================================================================
--- 
openejb/branches/openejb-pool/container/openejb-jee/src/main/java/org/apache/openejb/jee/EjbJar.java
 (original)
+++ 
openejb/branches/openejb-pool/container/openejb-jee/src/main/java/org/apache/openejb/jee/EjbJar.java
 Mon Jul 23 20:49:19 2012
@@ -194,6 +194,11 @@ public class EjbJar implements NamedModu
         return bean;
     }
 
+    public <T extends EnterpriseBean> EjbJar enterpriseBean(T bean) {
+        addEnterpriseBean(bean);
+        return this;
+    }
+
     public EnterpriseBean removeEnterpriseBean(EnterpriseBean bean){
         return removeEnterpriseBean(bean.getEjbName());
     }

Modified: 
openejb/branches/openejb-pool/container/openejb-jee/src/main/java/org/apache/openejb/jee/SessionBean.java
URL: 
http://svn.apache.org/viewvc/openejb/branches/openejb-pool/container/openejb-jee/src/main/java/org/apache/openejb/jee/SessionBean.java?rev=1364794&r1=1364793&r2=1364794&view=diff
==============================================================================
--- 
openejb/branches/openejb-pool/container/openejb-jee/src/main/java/org/apache/openejb/jee/SessionBean.java
 (original)
+++ 
openejb/branches/openejb-pool/container/openejb-jee/src/main/java/org/apache/openejb/jee/SessionBean.java
 Mon Jul 23 20:49:19 2012
@@ -913,4 +913,9 @@ public class SessionBean implements Remo
     public Collection<String> getParents() {
         return parents;
     }
+
+    public EnterpriseBean localBean() {
+        setLocalBean(new Empty());
+        return this;
+    }
 }


Reply via email to