/** * JTA {@code afterCompletion} callback: invoked after commit/rollback. * <p>Needs to invoke the Spring synchronization's {@code beforeCompletion} * at this late stage in case of a rollback, since there is no corresponding * callback with JTA. * @see org.springframework.transaction.support.TransactionSynchronization#beforeCompletion * @see org.springframework.transaction.support.TransactionSynchronization#afterCompletion */ @Override public void afterCompletion(int status) { if (!this.beforeCompletionCalled) { // beforeCompletion not called before (probably because of JTA rollback). // Perform the cleanup here. this.springSynchronization.beforeCompletion(); } // Call afterCompletion with the appropriate status indication. switch (status) { case Status.STATUS_COMMITTED: this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_COMMITTED); break; case Status.STATUS_ROLLEDBACK: this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK); break; default: this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_UNKNOWN); } }
/** * JTA {@code beforeCompletion} callback: just invoked before commit. * <p>In case of an exception, the JTA transaction will be marked as rollback-only. * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit */ @Override public void beforeCompletion() { try { boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); this.springSynchronization.beforeCommit(readOnly); } catch (RuntimeException | Error ex) { setRollbackOnlyIfPossible(); throw ex; } finally { // Process Spring's beforeCompletion early, in order to avoid issues // with strict JTA implementations that issue warnings when doing JDBC // operations after transaction completion (e.g. Connection.getWarnings). this.beforeCompletionCalled = true; this.springSynchronization.beforeCompletion(); } }
/** * Actually invoke the {@code afterCommit} methods of the * given Spring TransactionSynchronization objects. * @param synchronizations a List of TransactionSynchronization objects * @see TransactionSynchronization#afterCommit() */ public static void invokeAfterCommit(@Nullable List<TransactionSynchronization> synchronizations) { if (synchronizations != null) { for (TransactionSynchronization synchronization : synchronizations) { synchronization.afterCommit(); } } }
@Test public void jtaTransactionManagerWithCommitAndSynchronizationOnActual() throws Exception { UserTransaction ut = mock(UserTransaction.class); given(ut.getStatus()).willReturn(Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE, Status.STATUS_ACTIVE); final TransactionSynchronization synch = mock(TransactionSynchronization.class); JtaTransactionManager ptm = newJtaTransactionManager(ut); TransactionTemplate tt = new TransactionTemplate(ptm); ptm.setTransactionSynchronization(JtaTransactionManager.SYNCHRONIZATION_ON_ACTUAL_TRANSACTION); assertFalse(TransactionSynchronizationManager.isSynchronizationActive()); tt.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { // something transactional assertTrue(TransactionSynchronizationManager.isSynchronizationActive()); TransactionSynchronizationManager.registerSynchronization(synch); } }); assertFalse(TransactionSynchronizationManager.isSynchronizationActive()); verify(ut).begin(); verify(ut).commit(); verify(synch).beforeCommit(false); verify(synch).beforeCompletion(); verify(synch).afterCommit(); verify(synch).afterCompletion(TransactionSynchronization.STATUS_COMMITTED); }
@Test public void jtaTransactionManagerWithExistingTransactionAndCommitException() throws Exception { UserTransaction ut = mock(UserTransaction.class); given(ut.getStatus()).willReturn(Status.STATUS_ACTIVE); final TransactionSynchronization synch = mock(TransactionSynchronization.class); willThrow(new OptimisticLockingFailureException("")).given(synch).beforeCommit(false); JtaTransactionManager ptm = newJtaTransactionManager(ut); TransactionTemplate tt = new TransactionTemplate(ptm); assertFalse(TransactionSynchronizationManager.isSynchronizationActive()); try { tt.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { assertTrue(TransactionSynchronizationManager.isSynchronizationActive()); TransactionSynchronizationManager.registerSynchronization(synch); } }); fail("Should have thrown OptimisticLockingFailureException"); } catch (OptimisticLockingFailureException ex) { // expected } assertFalse(TransactionSynchronizationManager.isSynchronizationActive()); verify(ut).setRollbackOnly(); verify(synch).beforeCompletion(); verify(synch).afterCompletion(TransactionSynchronization.STATUS_UNKNOWN); }
/** * Trigger {@code beforeCommit} callbacks on all currently registered synchronizations. * @param readOnly whether the transaction is defined as read-only transaction * @throws RuntimeException if thrown by a {@code beforeCommit} callback * @see TransactionSynchronization#beforeCommit(boolean) */ public static void triggerBeforeCommit(boolean readOnly) { for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { synchronization.beforeCommit(readOnly); } }
/** * Actually invoke the {@code afterCompletion} methods of the * given Spring TransactionSynchronization objects. * @param synchronizations a List of TransactionSynchronization objects * @param completionStatus the completion status according to the * constants in the TransactionSynchronization interface * @see TransactionSynchronization#afterCompletion(int) * @see TransactionSynchronization#STATUS_COMMITTED * @see TransactionSynchronization#STATUS_ROLLED_BACK * @see TransactionSynchronization#STATUS_UNKNOWN */ public static void invokeAfterCompletion(@Nullable List<TransactionSynchronization> synchronizations, int completionStatus) { if (synchronizations != null) { for (TransactionSynchronization synchronization : synchronizations) { try { synchronization.afterCompletion(completionStatus); } catch (Throwable tsex) { logger.error("TransactionSynchronization.afterCompletion threw exception", tsex); } } } }
/** * Reactivate transaction synchronization for the current thread * and resume all given synchronizations. * @param suspendedSynchronizations a List of TransactionSynchronization objects */ private void doResumeSynchronization(List<TransactionSynchronization> suspendedSynchronizations) { TransactionSynchronizationManager.initSynchronization(); for (TransactionSynchronization synchronization : suspendedSynchronizations) { synchronization.resume(); TransactionSynchronizationManager.registerSynchronization(synchronization); } }
/** * Suspend all current synchronizations and deactivate transaction * synchronization for the current thread. * @return the List of suspended TransactionSynchronization objects */ private List<TransactionSynchronization> doSuspendSynchronization() { List<TransactionSynchronization> suspendedSynchronizations = TransactionSynchronizationManager.getSynchronizations(); for (TransactionSynchronization synchronization : suspendedSynchronizations) { synchronization.suspend(); } TransactionSynchronizationManager.clearSynchronization(); return suspendedSynchronizations; }
/** * Trigger {@code beforeCompletion} callbacks on all currently registered synchronizations. * @see TransactionSynchronization#beforeCompletion() */ public static void triggerBeforeCompletion() { for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { try { synchronization.beforeCompletion(); } catch (Throwable tsex) { logger.error("TransactionSynchronization.beforeCompletion threw exception", tsex); } } }
/** * Trigger {@code flush} callbacks on all currently registered synchronizations. * @throws RuntimeException if thrown by a {@code flush} callback * @see TransactionSynchronization#flush() */ public static void triggerFlush() { for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { synchronization.flush(); } }
assertEquals(1, synchs.size()); TransactionSynchronization synch = synchs.get(0); synch.beforeCommit(false); synch.beforeCompletion(); synch.afterCommit(); synch.afterCompletion(TransactionSynchronization.STATUS_UNKNOWN);
/** * Trigger {@code beforeCommit} callbacks on all currently registered synchronizations. * @param readOnly whether the transaction is defined as read-only transaction * @throws RuntimeException if thrown by a {@code beforeCommit} callback * @see TransactionSynchronization#beforeCommit(boolean) */ public static void triggerBeforeCommit(boolean readOnly) { for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { synchronization.beforeCommit(readOnly); } }
public void executeAfterCompletion(int status) { for (TransactionSynchronization synchronization : synchronizations) { synchronization.afterCompletion(status); } } }
/** * Reactivate transaction synchronization for the current thread * and resume all given synchronizations. * @param suspendedSynchronizations a List of TransactionSynchronization objects */ private void doResumeSynchronization(List<TransactionSynchronization> suspendedSynchronizations) { TransactionSynchronizationManager.initSynchronization(); for (TransactionSynchronization synchronization : suspendedSynchronizations) { synchronization.resume(); TransactionSynchronizationManager.registerSynchronization(synchronization); } }
/** * Suspend all current synchronizations and deactivate transaction * synchronization for the current thread. * @return the List of suspended TransactionSynchronization objects */ private List<TransactionSynchronization> doSuspendSynchronization() { List<TransactionSynchronization> suspendedSynchronizations = TransactionSynchronizationManager.getSynchronizations(); for (TransactionSynchronization synchronization : suspendedSynchronizations) { synchronization.suspend(); } TransactionSynchronizationManager.clearSynchronization(); return suspendedSynchronizations; }
/** * Trigger {@code beforeCompletion} callbacks on all currently registered synchronizations. * @see TransactionSynchronization#beforeCompletion() */ public static void triggerBeforeCompletion() { for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { try { synchronization.beforeCompletion(); } catch (Throwable tsex) { logger.error("TransactionSynchronization.beforeCompletion threw exception", tsex); } } }
/** * Trigger {@code flush} callbacks on all currently registered synchronizations. * @throws RuntimeException if thrown by a {@code flush} callback * @see TransactionSynchronization#flush() */ public static void triggerFlush() { for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { synchronization.flush(); } }
/** * JTA {@code afterCompletion} callback: invoked after commit/rollback. * <p>Needs to invoke the Spring synchronization's {@code beforeCompletion} * at this late stage in case of a rollback, since there is no corresponding * callback with JTA. * @see org.springframework.transaction.support.TransactionSynchronization#beforeCompletion * @see org.springframework.transaction.support.TransactionSynchronization#afterCompletion */ @Override public void afterCompletion(int status) { if (!this.beforeCompletionCalled) { // beforeCompletion not called before (probably because of JTA rollback). // Perform the cleanup here. this.springSynchronization.beforeCompletion(); } // Call afterCompletion with the appropriate status indication. switch (status) { case Status.STATUS_COMMITTED: this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_COMMITTED); break; case Status.STATUS_ROLLEDBACK: this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK); break; default: this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_UNKNOWN); } }
verify(synch).beforeCommit(false); verify(synch).beforeCompletion(); verify(synch).afterCommit(); verify(synch).afterCompletion(TransactionSynchronization.STATUS_COMMITTED);