You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by rm...@apache.org on 2015/04/21 11:12:25 UTC

tomee git commit: default rollback for runtime exception for @Tx

Repository: tomee
Updated Branches:
  refs/heads/master d5ef3cbb5 -> 0e123386c


default rollback for runtime exception for @Tx


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/0e123386
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/0e123386
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/0e123386

Branch: refs/heads/master
Commit: 0e123386cbc917870c23acdea1e2b4e4e78e4f73
Parents: d5ef3cb
Author: Romain Manni-Bucau <rm...@apache.org>
Authored: Tue Apr 21 11:12:20 2015 +0200
Committer: Romain Manni-Bucau <rm...@apache.org>
Committed: Tue Apr 21 11:12:20 2015 +0200

----------------------------------------------------------------------
 .../cdi/transactional/InterceptorBase.java      |  55 +++--
 .../cdi/transactional/TransactionalTest.java    | 232 ++++++++++++++++---
 2 files changed, 243 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/0e123386/container/openejb-core/src/main/java/org/apache/openejb/cdi/transactional/InterceptorBase.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/cdi/transactional/InterceptorBase.java b/container/openejb-core/src/main/java/org/apache/openejb/cdi/transactional/InterceptorBase.java
index bbfcce1..116660a 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/cdi/transactional/InterceptorBase.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/cdi/transactional/InterceptorBase.java
@@ -21,17 +21,27 @@ import org.apache.openejb.OpenEJB;
 import org.apache.openejb.SystemException;
 import org.apache.openejb.core.CoreUserTransaction;
 import org.apache.openejb.core.transaction.TransactionPolicy;
+import org.apache.webbeans.config.WebBeansContext;
 
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.CDI;
 import javax.interceptor.InvocationContext;
 import javax.transaction.TransactionManager;
 import javax.transaction.Transactional;
 import javax.transaction.TransactionalException;
 import java.io.Serializable;
 import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static java.util.Arrays.asList;
 
 public abstract class InterceptorBase implements Serializable {
     private static final IllegalStateException ILLEGAL_STATE_EXCEPTION = new IllegalStateException("Can't use UserTransaction from @Transaction call");
 
+    private transient Map<Class<?>, Boolean> rollback = new ConcurrentHashMap<>();
+
     protected Object intercept(final InvocationContext ic) throws Exception {
         Exception error = null;
         TransactionPolicy policy = null;
@@ -63,16 +73,33 @@ public abstract class InterceptorBase implements Serializable {
 
             if (policy != null) {
                 if (error != null) {
-                    // computed lazily but we could cache it later for sure if that's really a normal case
-                    final Method method = ic.getMethod();
-                    Transactional tx = method.getAnnotation(Transactional.class);
-                    if (tx == null) {
-                        tx = method.getDeclaringClass().getAnnotation(Transactional.class);
-                    }
-                    if (tx != null) { // TODO: find Bean instead of reflection
-                        if (new ExceptionPriotiryRules(tx.rollbackOn(), tx.dontRollbackOn()).accept(error)) {
+                    final Class<? extends Exception> errorClass = error.getClass();
+                    Boolean doRollback = rollback.get(errorClass);
+                    if (doRollback != null) {
+                        if (doRollback) {
                             policy.setRollbackOnly();
                         }
+                    } else {
+                        // computed lazily but we could cache it later for sure if that's really a normal case
+                        final Method method = ic.getMethod();
+                        final AnnotatedType<?> annotatedType = CDI.current().getBeanManager().createAnnotatedType(method.getDeclaringClass());
+                        Transactional tx = null;
+                        for (final AnnotatedMethod<?> m : annotatedType.getMethods()) {
+                            if (method.equals(m.getJavaMember())) {
+                                tx = m.getAnnotation(Transactional.class);
+                                break;
+                            }
+                        }
+                        if (tx == null) {
+                            tx = annotatedType.getAnnotation(Transactional.class);
+                        }
+                        if (tx != null) {
+                            doRollback = new ExceptionPriotiryRules(tx.rollbackOn(), tx.dontRollbackOn()).accept(error, method.getExceptionTypes());
+                            rollback.putIfAbsent(errorClass, doRollback);
+                            if (doRollback) {
+                                policy.setRollbackOnly();
+                            }
+                        }
                     }
                 }
                 policy.commit();
@@ -99,7 +126,7 @@ public abstract class InterceptorBase implements Serializable {
             this.excludes = excludes;
         }
 
-        public boolean accept(final Exception e) {
+        public boolean accept(final Exception e, final Class<?>[] exceptionTypes) {
             if (e == null) {
                 return false;
             }
@@ -108,10 +135,9 @@ public abstract class InterceptorBase implements Serializable {
             final int excludeScore = contains(excludes, e);
 
             if (excludeScore < 0) {
-                return includeScore >= 0;
+                return includeScore >= 0 || isNotChecked(e, exceptionTypes);
             }
-
-            return includeScore >= 0 && includeScore - excludeScore <= 0;
+            return includeScore - excludeScore >= 0;
         }
 
         private static int contains(final Class<?>[] list, final Exception e) {
@@ -138,6 +164,9 @@ public abstract class InterceptorBase implements Serializable {
             }
             return score;
         }
-    }
 
+        private static boolean isNotChecked(final Exception e, final Class<?>[] exceptionTypes) {
+            return RuntimeException.class.isInstance(e) && (exceptionTypes.length == 0 || !asList(exceptionTypes).contains(e.getClass()));
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/tomee/blob/0e123386/container/openejb-core/src/test/java/org/apache/openejb/cdi/transactional/TransactionalTest.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/test/java/org/apache/openejb/cdi/transactional/TransactionalTest.java b/container/openejb-core/src/test/java/org/apache/openejb/cdi/transactional/TransactionalTest.java
index a030d12..9bce175 100644
--- a/container/openejb-core/src/test/java/org/apache/openejb/cdi/transactional/TransactionalTest.java
+++ b/container/openejb-core/src/test/java/org/apache/openejb/cdi/transactional/TransactionalTest.java
@@ -56,24 +56,32 @@ public class TransactionalTest {
 
     @Test(expected = TransactionalException.class)
     public void mandatoryKO() {
-        bean.withoutATxIllThrowAnException();
+        for (int i = 0; i < 2; i++) {
+            bean.withoutATxIllThrowAnException();
+        }
     }
 
     @Test
     public void mandatoryOk() throws Exception {
-        OpenEJB.getTransactionManager().begin();
-        bean.withoutATxIllThrowAnException();
-        OpenEJB.getTransactionManager().rollback();
+        for (int i = 0; i < 2; i++) {
+            OpenEJB.getTransactionManager().begin();
+            bean.withoutATxIllThrowAnException();
+            OpenEJB.getTransactionManager().rollback();
+        }
     }
 
     @Test
     public void requiredStartsTx() throws Exception {
-        bean.required(); // asserts in the method
+        for (int i = 0; i < 2; i++) {
+            bean.required(); // asserts in the method
+        }
     }
 
     @Test
     public void utAllowedWhenThereIsNoTx() throws Exception {
-        bean.notSupportedUtOk();
+        for (int i = 0; i < 2; i++) {
+            bean.notSupportedUtOk();
+        }
     }
 
     @Test(expected = IllegalStateException.class)
@@ -83,42 +91,179 @@ public class TransactionalTest {
 
     @Test
     public void rollbackException() throws Exception {
-        final AtomicInteger status = new AtomicInteger();
-        final TransactionManager transactionManager = OpenEJB.getTransactionManager();
-        transactionManager.begin();
-        transactionManager.getTransaction().registerSynchronization(new Synchronization() {
-            @Override
-            public void beforeCompletion() {
+        for (int i = 0; i < 2; i++) {
+            final AtomicInteger status = new AtomicInteger();
+            final TransactionManager transactionManager = OpenEJB.getTransactionManager();
+            transactionManager.begin();
+            transactionManager.getTransaction().registerSynchronization(new Synchronization() {
+                @Override
+                public void beforeCompletion() {
+                    // no-op
+                }
+
+                @Override
+                public void afterCompletion(int state) {
+                    status.set(state);
+                }
+            });
+            try {
+                bean.anException();
+                fail();
+            } catch (final TransactionalException e) {
                 // no-op
             }
+            OpenEJB.getTransactionManager().rollback();
+            assertEquals(Status.STATUS_ROLLEDBACK, status.get());
+        }
+    }
 
-            @Override
-            public void afterCompletion(int state) {
-                status.set(state);
+    @Test
+    public void runtimeException() throws Exception {
+        for (int i = 0; i < 2; i++) {
+            final AtomicInteger status = new AtomicInteger();
+            try {
+                bean.runtimeEx(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            OpenEJB.getTransactionManager().getTransaction().registerSynchronization(new Synchronization() {
+                                @Override
+                                public void beforeCompletion() {
+                                    // no-op
+                                }
+
+                                @Override
+                                public void afterCompletion(int state) {
+                                    status.set(state);
+                                }
+                            });
+                        } catch (final RollbackException | SystemException e) {
+                            fail();
+                        }
+                    }
+                });
+                fail();
+            } catch (final TransactionalException e) {
+                // no-op
             }
-        });
-        try {
-            bean.anException();
-            fail();
-        } catch (final TransactionalException e) {
-            // no-op
+            assertEquals(Status.STATUS_ROLLEDBACK, status.get());
+        }
+    }
+
+    @Test
+    public void checked() throws Exception {
+        for (int i = 0; i < 2; i++) {
+            final AtomicInteger status = new AtomicInteger();
+            try {
+                bean.checked(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            OpenEJB.getTransactionManager().getTransaction().registerSynchronization(new Synchronization() {
+                                @Override
+                                public void beforeCompletion() {
+                                    // no-op
+                                }
+
+                                @Override
+                                public void afterCompletion(int state) {
+                                    status.set(state);
+                                }
+                            });
+                        } catch (final RollbackException | SystemException e) {
+                            fail();
+                        }
+                    }
+                });
+                fail();
+            } catch (final TransactionalException e) {
+                // no-op
+            }
+            assertEquals(Status.STATUS_COMMITTED, status.get());
+        }
+    }
+
+    @Test
+    public void runtimeChecked() throws Exception {
+        for (int i = 0; i < 2; i++) {
+            final AtomicInteger status = new AtomicInteger();
+            try {
+                bean.runtimeChecked(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            OpenEJB.getTransactionManager().getTransaction().registerSynchronization(new Synchronization() {
+                                @Override
+                                public void beforeCompletion() {
+                                    // no-op
+                                }
+
+                                @Override
+                                public void afterCompletion(int state) {
+                                    status.set(state);
+                                }
+                            });
+                        } catch (final RollbackException | SystemException e) {
+                            fail();
+                        }
+                    }
+                });
+                fail();
+            } catch (final TransactionalException e) {
+                // no-op
+            }
+            assertEquals(Status.STATUS_COMMITTED, status.get());
+        }
+    }
+
+    @Test
+    public void classLevel() throws Exception {
+        for (int i = 0; i < 2; i++) {
+            final AtomicInteger status = new AtomicInteger();
+            try {
+                bean.classLevel(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            OpenEJB.getTransactionManager().getTransaction().registerSynchronization(new Synchronization() {
+                                @Override
+                                public void beforeCompletion() {
+                                    // no-op
+                                }
+
+                                @Override
+                                public void afterCompletion(int state) {
+                                    status.set(state);
+                                }
+                            });
+                        } catch (final RollbackException | SystemException e) {
+                            fail();
+                        }
+                    }
+                });
+                fail();
+            } catch (final TransactionalException e) {
+                // no-op
+            }
+            assertEquals(Status.STATUS_COMMITTED, status.get());
         }
-        OpenEJB.getTransactionManager().rollback();
-        assertEquals(Status.STATUS_ROLLEDBACK, status.get());
     }
 
     @Test
     public void dontRollbackException() throws Exception {
-        final AtomicInteger status = new AtomicInteger();
-        try {
-            bean.anotherException(status);
-            fail();
-        } catch (final TransactionalException e) {
-            // no-op
+        for (int i = 0; i < 2; i++) {
+            final AtomicInteger status = new AtomicInteger();
+            try {
+                bean.anotherException(status);
+                fail();
+            } catch (final TransactionalException e) {
+                // no-op
+            }
+            assertEquals(Status.STATUS_COMMITTED, status.get());
         }
-        assertEquals(Status.STATUS_COMMITTED, status.get());
     }
 
+    @Transactional(value = REQUIRED, rollbackOn = AnCheckedException.class)
     public static class TxBean {
         @Resource
         private UserTransaction ut;
@@ -167,6 +312,30 @@ public class TransactionalTest {
             throw new AnException();
         }
 
+        @Transactional(REQUIRED)
+        public void runtimeEx(Runnable runnable) {
+            runnable.run();
+            throw new AnException();
+        }
+
+        @Transactional(REQUIRED)
+        public void classLevel(Runnable runnable) throws AnCheckedException {
+            runnable.run();
+            throw new AnCheckedException();
+        }
+
+        @Transactional(REQUIRED)
+        public void checked(Runnable runnable) throws AnCheckedException {
+            runnable.run();
+            throw new AnCheckedException();
+        }
+
+        @Transactional(REQUIRED)
+        public void runtimeChecked(Runnable runnable) throws AnException {
+            runnable.run();
+            throw new AnException();
+        }
+
         @Transactional(value = REQUIRED, dontRollbackOn = AnotherException.class)
         public void anotherException(final AtomicInteger status) {
             try {
@@ -188,8 +357,9 @@ public class TransactionalTest {
         }
     }
 
+    public static class AnCheckedException extends Exception {
+    }
     public static class AnException extends RuntimeException {
-
     }
     public static class AnotherException extends RuntimeException {