/** Check that a transaction is created and committed. */
  public void testTransactionShouldSucceed() throws Exception {
    TransactionAttribute txatt = new DefaultTransactionAttribute();

    MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
    tas.register(getNameMethod, 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();

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

    checkTransactionStatus(false);
    itb.getName();
    checkTransactionStatus(false);

    ptmControl.verify();
  }
  /** Simulate a transaction infrastructure failure. Shouldn't invoke target method. */
  public void testCannotCreateTransaction() throws Exception {
    TransactionAttribute txatt = new DefaultTransactionAttribute();

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

    MockControl ptmControl = MockControl.createControl(PlatformTransactionManager.class);
    PlatformTransactionManager ptm = (PlatformTransactionManager) ptmControl.getMock();
    // Expect a transaction
    ptm.getTransaction(txatt);
    CannotCreateTransactionException ex = new CannotCreateTransactionException("foobar", null);
    ptmControl.setThrowable(ex);
    ptmControl.replay();

    TestBean tb =
        new TestBean() {
          public String getName() {
            throw new UnsupportedOperationException(
                "Shouldn't have invoked target method when couldn't create transaction for transactional method");
          }
        };
    ITestBean itb = (ITestBean) advised(tb, ptm, tas);

    try {
      itb.getName();
      fail("Shouldn't have invoked method");
    } catch (CannotCreateTransactionException thrown) {
      assertTrue(thrown == ex);
    }
    ptmControl.verify();
  }
  /**
   * Simulate failure of the underlying transaction infrastructure to commit. Check that the target
   * method was invoked, but that the transaction infrastructure exception was thrown to the client
   */
  public void testCannotCommitTransaction() throws Exception {
    TransactionAttribute txatt = new DefaultTransactionAttribute();

    Method m = setNameMethod;
    MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
    tas.register(m, txatt);
    Method m2 = getNameMethod;
    // No attributes for m2

    MockControl ptmControl = MockControl.createControl(PlatformTransactionManager.class);
    PlatformTransactionManager ptm = (PlatformTransactionManager) ptmControl.getMock();

    TransactionStatus status = transactionStatusForNewTransaction();
    ptm.getTransaction(txatt);
    ptmControl.setReturnValue(status);
    UnexpectedRollbackException ex = new UnexpectedRollbackException("foobar", null);
    ptm.commit(status);
    ptmControl.setThrowable(ex);
    ptmControl.replay();

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

    String name = "new name";
    try {
      itb.setName(name);
      fail("Shouldn't have succeeded");
    } catch (UnexpectedRollbackException thrown) {
      assertTrue(thrown == ex);
    }

    // Should have invoked target and changed name
    assertTrue(itb.getName() == name);
    ptmControl.verify();
  }
  /** Test that TransactionStatus.setRollbackOnly works. */
  public void testProgrammaticRollback() throws Exception {
    TransactionAttribute txatt = new DefaultTransactionAttribute();

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

    TransactionStatus status = transactionStatusForNewTransaction();
    MockControl ptmControl = MockControl.createControl(PlatformTransactionManager.class);
    PlatformTransactionManager ptm = (PlatformTransactionManager) ptmControl.getMock();

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

    final String name = "jenny";
    TestBean tb =
        new TestBean() {
          public String getName() {
            TransactionStatus txStatus = TransactionInterceptor.currentTransactionStatus();
            txStatus.setRollbackOnly();
            return name;
          }
        };

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

    // verification!?
    assertTrue(name.equals(itb.getName()));

    ptmControl.verify();
  }
  /**
   * 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();
  }
  /**
   * Check that a transaction is created and committed using
   * CallbackPreferringPlatformTransactionManager.
   */
  public void testTransactionShouldSucceedWithCallbackPreference() throws Exception {
    TransactionAttribute txatt = new DefaultTransactionAttribute();

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

    MockCallbackPreferringTransactionManager ptm = new MockCallbackPreferringTransactionManager();

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

    checkTransactionStatus(false);
    itb.getName();
    checkTransactionStatus(false);

    assertSame(txatt, ptm.getDefinition());
    assertFalse(ptm.getStatus().isRollbackOnly());
  }
  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());
  }