Page 1 of 4 123 ... LastLast
Results 1 to 10 of 33

Thread: UnexpectedRollbackException in a rollback JUnit test

  1. #1
    Join Date
    Aug 2004
    Location
    Tampa, FL
    Posts
    39

    Default UnexpectedRollbackException in a rollback JUnit test

    I have a JUnit test that uses AbstractTransactionalDataSourceSpringContextTests. I recently upgraded from Spring 1.1.5 to 1.2.2 and the test fails (succeeded in 1.1.5) with the following exception:

    Code:
    org.springframework.transaction.UnexpectedRollbackException: Transaction has been rolled back because it has been marked as rollback-only
    	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:417)
    	at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(TransactionAspectSupport.java:258)
    	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:67)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:144)
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:174)
    	at $Proxy0.loginCheck(Unknown Source)
    	at com.mycompany.ws.common.LoginCheckInterceptor.before(LoginCheckInterceptor.java:25)
    	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:144)
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:174)
    	at $Proxy44.getShelfInfoList(Unknown Source)
    	at component.service.EquippingServiceImplTests.testMissingInput(EquippingServiceImplTests.java:91)
    It appears to be failing when a MethodBeforeAdvice is executed (LoginCheckInterceptor above which calls a method loginCheck in a class called LoginServiceImpl). loginCheck executes in a transaction, as does the class that is being advised (both using TransactionProxyFactoryBean). In both cases the transaction attributes are PROPAGATION_REQUIRED for all methods.

    Strangely, I haven't seen this occur with other tests that do similar things. The only thing unusual about this test is that it has multiple calls to the same advised method and in each case an exception is thrown and caught (it expects an exception).

    I just tried putting one of these method calls in its own unit test, and it does not exhibit this problem. It only appears when I have more than one calls.
    Nilesh Kapadia
    http://www.nileshk.com

  2. #2
    Join Date
    Aug 2004
    Location
    Linz, Austria
    Posts
    391

    Default

    Outside of JTA, that can only happen if an inner transaction has marked the global resource holder as rollback-only - probably as result of its rollback rules -, with the outer transaction not noticing this and still calling commit. To make the caller of the outer transaction aware that what actually happened wasn't a commit, the commit call fails with an UnexpectedRollbackException.

    So I would recommend to check out what's going on in detail. Within the scope of the transaction that fails here, is there an inner transaction (probably PROPAGATION_REQUIRED) that might fail and mark the entire transaction as rollback-only? Why doesn't the exception get propagated from there and cause the outer transaction to cause a rollback as well?

    Maybe the answer is in the aspects that you're weaving in, which don't necessarily see an exception that's passed up the call stack. Anyway, you need to figure out where the transaction gets marked as rollback-only, and why the outer transaction doesn't notice this and still calls commit - with the result being the UnexpectedRollbackException.

    Juergen

  3. #3
    Join Date
    Aug 2004
    Location
    Linz, Austria
    Posts
    391

    Default

    BTW, this strict UnexpectedRollbackException behavior has been introduced in Spring 1.2.1, to avoid the situation where a commit call results in a rollback without the caller noticing it. This change was made in response to a request from a user who suffered from such unnoticed rollbacks. An example for a bug fix that introduced stronger enforcement of a rule, I guess.

    Juergen

  4. #4
    Join Date
    Aug 2004
    Posts
    229

    Default

    I would think the newer behavior better as it could help uncover potential bugs, no?

    - Andy

  5. #5
    Join Date
    Aug 2004
    Location
    Linz, Austria
    Posts
    391

    Default

    I agree - that's why the new behavior has been introduced in the first place :-) A commit call should not execute without notice that the actual thing that happened was a rollback.

    Juergen

  6. #6
    Join Date
    Aug 2004
    Location
    Tampa, FL
    Posts
    39

    Default

    Thanks for the response. I think I understand what is going on now, and my unit test was actually performing behavior that should result in the error.

    In case anyone is interested in the details:

    My understanding of AbstractTransactionalDataSourceSpringContextTests and its superclass AbstractTransactionalSpringContextTests (which roll back the transaction at the end of each test) is that it starts a transaction and rolls back the transaction programmatically in the onTearDown() method:

    (line 165 of AbstractTransactionalSpringContextTests)
    Code:
    this.transactionManager.rollback(this.transactionStatus);
    Since my test is calling methods that throw an exception (and are wrapped in a PROPAGATION_REQUIRED transaction) and this triggers a rollback, the transaction (which includes the outer transaction) gets marked as rollback-only, and even though I catch the exception, when I call another method that is wrapped in a transaction (also PROPAGATION_REQUIRED), it fails on commit with this error since the transaction has been set to rollback.
    Nilesh Kapadia
    http://www.nileshk.com

  7. #7
    Join Date
    Sep 2004
    Location
    Vancouver, BC, Canada
    Posts
    135

    Default

    Quote Originally Posted by nilesh
    The only thing unusual about this test is that it has multiple calls to the same advised method and in each case an exception is thrown and caught (it expects an exception).

    I just tried putting one of these method calls in its own unit test, and it does not exhibit this problem. It only appears when I have more than one calls.
    Hi, I have the exact same problem as nilesh when upgrading from Spring 1.1.5 to 1.2.3. I have a Transactional JUnit test that calls a transactionally advised method that returns an expected exception multiple times. For example:

    Code:
    public class MyTest extends AbstractTransactionalDataSourceSpringContextTests
    {
      ...
      public void testSaveClientForVariousExceptions()
      {
          ...
          try
          {
            clientService.saveClient(client); // transactionally advised
            fail("should have thrown SomeException");
          }
          catch (SomeException e)
          {      
          }
          ...
          try
          {
            clientService.saveClient(client); // second call causes UnexpectedRollbackException
            fail("should have thrown SomeOtherException"); 
          }
          catch (SomeOtherException e)
          {      
          }
      }
      ...
    }
    On the second call to the transactionally advised saveClient() method, I get the UnexpectedRollbackException. Like nilesh, my test worked fine with the older version of Spring.

    I think I understand Juergen's explanation but I'm wondering how to fix my test? I know one way is to split the test up so that each expected exception is tested in its own test method like so:

    Code:
    public class MyTest extends AbstractTransactionalDataSourceSpringContextTests
    {
      ...
      public void testSaveClientForSomeException()
      {
          ...
          try
          {
            clientService.saveClient(client); // transactionally advised
            fail("should have thrown SomeException");
          }
          catch (SomeException e)
          {      
          }
      }
    
      public void testSaveClientForSomeOtherException ()
      {
          ...
          try
          {
            clientService.saveClient(client);
            fail("should have thrown SomeOtherException");
          }
          catch (SomeOtherException e)
          {      
          }
      }
      ...
    }
    However, I'd rather not do that right now because some of my test methods test many for many exceptions. It would be a lot of work to refactor that right now.

    So, I wondering is there another way to fix this? For example, could I override something in AbstractTransactionalDataSourceSpringContextTests? Alternatively, is there something I can call in the catch{} to reset the transaction?
    Cheers,
    Joe
    "All your bean are belong to us" - Spring Framework's IOC Container

  8. #8
    Join Date
    Aug 2004
    Location
    Toronto, Canada
    Posts
    736

    Default

    First of all, I want to make the point that in the case of transactional methods wrapping other methods which are transactional with a PROPAGATION_REQUIRED propagation setting, which seems to be the case here (the base test class creates a transaction around the test method, but the service method invocations in the test just join in the transaction), it's a bit of a misnomer to talk about an outer and an inner transaction. There is only one transaction, which is being joined by the inner method call.

    Anyway, something weird is going on here if the calls from the inner saveClient() method calls are throwing the unexpected rollback exception. I can not think of any circumstance in this case under which this should ever be considered a real point to commit (and therefore cause the error on that commit), as the method invocation has not actually caused any transaction to be created, and the commit should only happen when the outer (test) method itself returns. So the state of the rollback flag in the transaction should be irrelevant. And if Spring behaves like EJB 2.1 (see section 17.6.2.2) that should certainly be the case.

    Joe, this is all with the same transaction manager, and with transactions set to PROPAGATION_REQUIRED? What is the transaction manager?

    I will ping Juergen here on this, as again, I can't think of a good reason why this should ever happen.

    Colin
    Colin Sampaleanu
    SpringSource - http://www.springsource.com

  9. #9
    Join Date
    Sep 2004
    Location
    Vancouver, BC, Canada
    Posts
    135

    Default

    Colin, thanks for your reply.

    Quote Originally Posted by Colin Sampaleanu
    There is only one transaction, which is being joined by the inner method call.
    Correct. There is only one transaction here, which begins when the test method begins.

    Quote Originally Posted by Colin Sampaleanu
    Anyway, something weird is going on here if the calls from the inner saveClient() method calls are throwing the unexpected rollback exception. I can not think of any circumstance in this case under which this should ever be considered a real point to commit (and therefore cause the error on that commit), as the method invocation has not actually caused any transaction to be created, and the commit should only happen when the outer (test) method itself returns. So the state of the rollback flag in the transaction should be irrelevant.
    I agree. I was confused by this behaviour too.

    Quote Originally Posted by Colin Sampaleanu
    Joe, this is all with the same transaction manager, and with transactions set to PROPAGATION_REQUIRED? What is the transaction manager?
    Yes, all with the same transaction manager. Yes, transactions were set to PROPAGATION_REQUIRED. The transaction manager was org.springframework.orm.hibernate3.HibernateTransa ctionManager. And in case it wasn't clear, saveClient() is set to PROPAGATION_REQUIRED.

    I have a similar situation with my tests for deleteClient(). The only difference is that the second call to deleteClient() is NOT expected to throw an exception, whereas the second call to saveClient() is expected to throw an exception. However, the result is the same in both test methods -- the second call to the transactionally advised service method results in a UnexpectedRollbackException, after the first call threw an application exception. Here is the code for testDeleteClient():

    Code:
    public class MyTest extends AbstractTransactionalDataSourceSpringContextTests
    {
      ...
      public void testDeleteClient()
      {
        final long someClientId = 1; // NOT allowed to delete this client
        Client client = (Client) clientService.loadClient(someClientId);
        try
        {
          clientService.deleteClient(client); // transactionally advised
          fail("should have thrown DeleteNotAllowedException"); 
        }
        catch (DeleteNotAllowedException e)
        {      
        }
        ...
        final long someOtherClientId = 2; // this client can be deleted
        client = (Client) clientService.loadClient(someOtherClientId);
        clientService.deleteClient(client); // second call causes UnexpectedRollbackException
        ...
      }
      ...
    }
    I'm not sure whether this is considered to be a bug in the framework or not. I leave that up to the Spring team to decide. If you do consider it a bug, please let me know and I'll happily post a bug in JIRA.

    Anyway, I'm still left with some tests that don't pass. Any advice as to how I should proceed? Should I restructure my tests so that each expected exception is tested in a separate test method? Or is there some way I can workaround this by subclassing AbstractTransactionalDataSourceSpringContextTests and overriding some behaviour?

    Cheers
    Cheers,
    Joe
    "All your bean are belong to us" - Spring Framework's IOC Container

  10. #10
    Join Date
    Jan 2005
    Location
    Bucharest, Romania
    Posts
    5,403

    Default

    Sorry for 'piggy-backing' on this post - I had a similar problem with the same exception when using readOnly transactions: http://forum.springframework.org/showthread.php?t=17730
    Costin Leau
    SpringSource - http://www.SpringSource.com- Spring Training, Consulting, and Support - "From the Source"
    http://twitter.com/costinl
    Please use [ c o d e ] [ / c o d e ] tags

Similar Threads

  1. How can I test authorization in Junit?
    By j2emmy in forum Security
    Replies: 16
    Last Post: Mar 21st, 2006, 05:23 PM
  2. ApplicationContext returns null in Junit Test Case
    By penku in forum Spring-Modules
    Replies: 2
    Last Post: Sep 17th, 2005, 01:28 PM
  3. Test with JUnit
    By cecaldas in forum Data
    Replies: 3
    Last Post: Aug 9th, 2005, 10:03 PM
  4. Junit test failure
    By mhazer in forum Data
    Replies: 1
    Last Post: May 2nd, 2005, 02:37 AM
  5. Junit Test in IntelliJ
    By nisha in forum Web
    Replies: 1
    Last Post: Apr 9th, 2005, 08:44 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •