/**
   * Check that the given exception thrown by the target can produce the desired behavior with the
   * appropriate transaction attribute.
   *
   * @param ex exception to be thrown by the target
   * @param shouldRollback whether this should cause a transaction rollback
   */
  protected void doTestRollbackOnException(
      final Exception ex, final boolean shouldRollback, boolean rollbackException)
      throws Exception {

    TransactionAttribute txatt =
        new DefaultTransactionAttribute() {
          public boolean rollbackOn(Throwable t) {
            assertTrue(t == ex);
            return shouldRollback;
          }
        };

    Method m = exceptionalMethod;
    MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
    tas.register(m, txatt);

    MockControl statusControl = MockControl.createControl(TransactionStatus.class);
    TransactionStatus status = (TransactionStatus) statusControl.getMock();
    MockControl ptmControl = MockControl.createControl(PlatformTransactionManager.class);
    PlatformTransactionManager ptm = (PlatformTransactionManager) ptmControl.getMock();
    // Gets additional call(s) from TransactionControl

    ptm.getTransaction(txatt);
    ptmControl.setReturnValue(status, 1);

    if (shouldRollback) {
      ptm.rollback(status);
    } else {
      ptm.commit(status);
    }
    TransactionSystemException tex = new TransactionSystemException("system exception");
    if (rollbackException) {
      ptmControl.setThrowable(tex, 1);
    } else {
      ptmControl.setVoidCallable(1);
    }
    ptmControl.replay();

    TestBean tb = new TestBean();
    ITestBean itb = (ITestBean) advised(tb, ptm, tas);

    try {
      itb.exceptional(ex);
      fail("Should have thrown exception");
    } catch (Throwable t) {
      if (rollbackException) {
        assertEquals("Caught wrong exception", tex, t);
      } else {
        assertEquals("Caught wrong exception", ex, t);
      }
    }

    ptmControl.verify();
  }
  public void testEnclosingTransactionWithNonTransactionMethodOnAdvisedInside() throws Throwable {
    TransactionAttribute txatt = new DefaultTransactionAttribute();

    MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
    tas.register(exceptionalMethod, txatt);

    TransactionStatus status = transactionStatusForNewTransaction();
    MockControl ptmControl = MockControl.createControl(PlatformTransactionManager.class);
    PlatformTransactionManager ptm = (PlatformTransactionManager) ptmControl.getMock();
    // Expect a transaction
    ptm.getTransaction(txatt);
    ptmControl.setReturnValue(status, 1);
    ptm.commit(status);
    ptmControl.setVoidCallable(1);
    ptmControl.replay();

    final String spouseName = "innerName";

    TestBean outer =
        new TestBean() {
          public void exceptional(Throwable t) throws Throwable {
            TransactionInfo ti = TransactionAspectSupport.currentTransactionInfo();
            assertTrue(ti.hasTransaction());
            assertEquals(spouseName, getSpouse().getName());
          }
        };
    TestBean inner =
        new TestBean() {
          public String getName() {
            // Assert that we're in the inner proxy
            TransactionInfo ti = TransactionAspectSupport.currentTransactionInfo();
            assertFalse(ti.hasTransaction());
            return spouseName;
          }
        };

    ITestBean outerProxy = (ITestBean) advised(outer, ptm, tas);
    ITestBean innerProxy = (ITestBean) advised(inner, ptm, tas);
    outer.setSpouse(innerProxy);

    checkTransactionStatus(false);

    // Will invoke inner.getName, which is non-transactional
    outerProxy.exceptional(null);

    checkTransactionStatus(false);

    ptmControl.verify();
  }
  public void testTransactionExceptionPropagatedWithCallbackPreference() throws Throwable {
    TransactionAttribute txatt = new DefaultTransactionAttribute();

    MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
    tas.register(exceptionalMethod, txatt);

    MockCallbackPreferringTransactionManager ptm = new MockCallbackPreferringTransactionManager();

    TestBean tb = new TestBean();
    ITestBean itb = (ITestBean) advised(tb, ptm, tas);

    checkTransactionStatus(false);
    try {
      itb.exceptional(new OptimisticLockingFailureException(""));
      fail("Should have thrown OptimisticLockingFailureException");
    } catch (OptimisticLockingFailureException ex) {
      // expected
    }
    checkTransactionStatus(false);

    assertSame(txatt, ptm.getDefinition());
    assertFalse(ptm.getStatus().isRollbackOnly());
  }