AfterCompletion { public synchronized void doOp(TXState txState, CancelCriterion cancelCriterion) { try { waitForExecuteOrCancel(cancelCriterion); } catch (RuntimeException | Error ignore) { action = Action.CANCEL; } started = true; logger.debug("executing afterCompletion notification"); try { switch (action) { case CANCEL: txState.doCleanup(); break; case COMMIT: txState.doAfterCompletionCommit(); break; case ROLLBACK: txState.doAfterCompletionRollback(); break; } } catch (RuntimeException exception) { this.exception = exception; } finally { logger.debug("afterCompletion notification completed"); finished = true; notifyAll(); } }  synchronized void doOp(TXState txState, CancelCriterion cancelCriterion); void executeCommit(); void executeRollback(); synchronized void cancel(); synchronized boolean isStarted();  }
@Test public void doOpInvokesDoCleanupIfCancelCriteriaThrows() { doThrow(new RuntimeException()).when(cancelCriterion).checkCancelInProgress(null); afterCompletion.doOp(txState, cancelCriterion); verify(txState).doCleanup(); }