diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java index 07e17055bd..9f6dad1deb 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java @@ -306,6 +306,8 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init } else { + final ThrowableHolder throwableHolder = new ThrowableHolder(); + // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> { @@ -325,7 +327,8 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init } else { // A normal return value: will lead to a commit. - return new ThrowableHolder(ex); + throwableHolder.throwable = ex; + return null; } } finally { @@ -333,17 +336,28 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init } }); - // Check result: It might indicate a Throwable to rethrow. - if (result instanceof ThrowableHolder) { - throw ((ThrowableHolder) result).getThrowable(); - } - else { - return result; + // Check result state: It might indicate a Throwable to rethrow. + if (throwableHolder.throwable != null) { + throw throwableHolder.throwable; } + return result; } catch (ThrowableHolderException ex) { throw ex.getCause(); } + catch (TransactionSystemException ex2) { + if (throwableHolder.throwable != null) { + logger.error("Application exception overridden by commit exception", throwableHolder.throwable); + ex2.initApplicationException(throwableHolder.throwable); + } + throw ex2; + } + catch (Throwable ex2) { + if (throwableHolder.throwable != null) { + logger.error("Application exception overridden by commit exception", throwableHolder.throwable); + } + throw ex2; + } } } @@ -540,14 +554,10 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init ex2.initApplicationException(ex); throw ex2; } - catch (RuntimeException ex2) { + catch (RuntimeException | Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } - catch (Error err) { - logger.error("Application exception overridden by rollback error", ex); - throw err; - } } else { // We don't roll back on this exception. @@ -560,14 +570,10 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init ex2.initApplicationException(ex); throw ex2; } - catch (RuntimeException ex2) { + catch (RuntimeException | Error ex2) { logger.error("Application exception overridden by commit exception", ex); throw ex2; } - catch (Error err) { - logger.error("Application exception overridden by commit error", ex); - throw err; - } } } } @@ -679,20 +685,12 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init /** - * Internal holder class for a Throwable, used as a return value - * from a TransactionCallback (to be subsequently unwrapped again). + * Internal holder class for a Throwable in a callback transaction model. */ private static class ThrowableHolder { - private final Throwable throwable; - - public ThrowableHolder(Throwable throwable) { - this.throwable = throwable; - } - - public final Throwable getThrowable() { - return this.throwable; - } + @Nullable + public Throwable throwable; } diff --git a/spring-tx/src/main/java/org/springframework/transaction/jta/WebSphereUowTransactionManager.java b/spring-tx/src/main/java/org/springframework/transaction/jta/WebSphereUowTransactionManager.java index 039d15297e..3b82c3fd0b 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/jta/WebSphereUowTransactionManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/jta/WebSphereUowTransactionManager.java @@ -261,7 +261,8 @@ public class WebSphereUowTransactionManager extends JtaTransactionManager "Transaction propagation 'nested' not supported for WebSphere UOW transactions"); } if (pb == TransactionDefinition.PROPAGATION_SUPPORTS || - pb == TransactionDefinition.PROPAGATION_REQUIRED || pb == TransactionDefinition.PROPAGATION_MANDATORY) { + pb == TransactionDefinition.PROPAGATION_REQUIRED || + pb == TransactionDefinition.PROPAGATION_MANDATORY) { joinTx = true; newSynch = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); } @@ -279,7 +280,8 @@ public class WebSphereUowTransactionManager extends JtaTransactionManager "Transaction propagation 'mandatory' but no existing transaction found"); } if (pb == TransactionDefinition.PROPAGATION_SUPPORTS || - pb == TransactionDefinition.PROPAGATION_NOT_SUPPORTED || pb == TransactionDefinition.PROPAGATION_NEVER) { + pb == TransactionDefinition.PROPAGATION_NOT_SUPPORTED || + pb == TransactionDefinition.PROPAGATION_NEVER) { uowType = UOWSynchronizationRegistry.UOW_TYPE_LOCAL_TRANSACTION; newSynch = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); } @@ -293,6 +295,7 @@ public class WebSphereUowTransactionManager extends JtaTransactionManager logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); } SuspendedResourcesHolder suspendedResources = (!joinTx ? suspend(null) : null); + UOWActionAdapter action = null; try { if (definition.getTimeout() > TransactionDefinition.TIMEOUT_DEFAULT) { uowManager.setUOWTimeout(uowType, definition.getTimeout()); @@ -300,7 +303,7 @@ public class WebSphereUowTransactionManager extends JtaTransactionManager if (debug) { logger.debug("Invoking WebSphere UOW action: type=" + uowType + ", join=" + joinTx); } - UOWActionAdapter action = new UOWActionAdapter<>( + action = new UOWActionAdapter<>( definition, callback, (uowType == UOWManager.UOW_TYPE_GLOBAL_TRANSACTION), !joinTx, newSynch, debug); uowManager.runUnderUOW(uowType, joinTx, action); if (debug) { @@ -308,11 +311,15 @@ public class WebSphereUowTransactionManager extends JtaTransactionManager } return action.getResult(); } - catch (UOWException ex) { - throw new TransactionSystemException("UOWManager transaction processing failed", ex); - } - catch (UOWActionException ex) { - throw new TransactionSystemException("UOWManager threw unexpected UOWActionException", ex); + catch (UOWException | UOWActionException ex) { + TransactionSystemException tse = + new TransactionSystemException("UOWManager transaction processing failed", ex); + Throwable appEx = action.getException(); + if (appEx != null) { + logger.error("Application exception overridden by rollback exception", appEx); + tse.initApplicationException(appEx); + } + throw tse; } finally { if (suspendedResources != null) { @@ -368,12 +375,15 @@ public class WebSphereUowTransactionManager extends JtaTransactionManager } catch (Throwable ex) { this.exception = ex; + if (status.isDebug()) { + logger.debug("Rolling back on application exception from transaction callback", ex); + } uowManager.setRollbackOnly(); } finally { if (status.isLocalRollbackOnly()) { if (status.isDebug()) { - logger.debug("Transactional code has requested rollback"); + logger.debug("Transaction callback has explicitly requested rollback"); } uowManager.setRollbackOnly(); } @@ -396,6 +406,11 @@ public class WebSphereUowTransactionManager extends JtaTransactionManager return this.result; } + @Nullable + public Throwable getException() { + return this.exception; + } + @Override public boolean isRollbackOnly() { return obtainUOWManager().getRollbackOnly(); diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java b/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java index f4465f7f55..a3745102cd 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java @@ -172,14 +172,10 @@ public class TransactionTemplate extends DefaultTransactionDefinition ex2.initApplicationException(ex); throw ex2; } - catch (RuntimeException ex2) { + catch (RuntimeException | Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } - catch (Error err) { - logger.error("Application exception overridden by rollback error", ex); - throw err; - } } }