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 {