7 Replies Latest reply on Feb 22, 2013 3:15 AM by cfillot

    Exceptions and transaction rollback

    cfillot

      Hello,

       

      I have some EJBs that throw checked exceptions. I know that I can use @ApplicationException(rollback=true) to automatically rollback the transaction. However, I would like the transaction to be rollbacked at EJB container boundary (ie when the exception is sent to the web layer).

      For example, consider servlet -> ejb A -> ejb B: if B throws exception X (caught by A with a try/catch), the transaction wouldn't be rollbacked.

      But if A throws X the rollback would happen.

       

      Is there a way to achieve this ?

       

      Thanks in advance for any suggestion

        • 1. Re: Exceptions and transaction rollback
          jaikiran

          Is this what you are after:

           

          public class MyEJBA {
          
              @Resource
              private EJBContext ejbContext;
          
              public void myMethodOnEJBA() {
                  ....
                  MyEJBB bBean = getBBean();
                  try {
                      bBean.doSomething();
                  } catch (MyAppException expected) {
                      ...
                      // do whatever
                  }            
                  // do some other things
                  if (shouldIThrowMyAppExceptionAndRollbackTx()) {
                      // mark tx for rollback
                      ejbContext.setRollbackOnly();
                      // throw our app exception
                      throw new MyAppException();
                  }
              }
              ...
          }    
          
          • 2. Re: Exceptions and transaction rollback
            cfillot

            Hi Jaikiran,

             

            Thanks for taking a look at my problem. My example was incorrect, in fact this is the method in EJB "B" that can be called either from another EJB ("A") in

            my example, or directly from the web layer (servlet). So this method can throw an AppException, but I would like that the rollback happens only when this

            method is called directly from the web layer, but not when EJB "A" calls it. If I put @ApplicationException(rollback=true), the rollback will happen in both

            situations. Of course I give the example for 2 EJBs and 1 method, but the real application is more complex and I would like to have a generic method to do

            that.

             

            First I thought writing an interceptor, but I don't think I can determine if the method invocation is from another EJB or from the web layer (otherwise I would

            have been able to catch the exception, and if the call was from the web layer, calling ejbContext.setRollbackOnly() would have done the job).

            • 3. Re: Exceptions and transaction rollback
              cfillot

              I thought to this solution, based on an Interceptor and ThreadLocal variable.

               

              public class EJBBoundaryMark {
                  private static ThreadLocal<Boolean> mark = new ThreadLocal<Boolean>();
              
                  public static ThreadLocal<Boolean> getMark() {
                      if (mark.get() == null)
                          mark.set(true);
                      
                      return mark;
                  }
              
                  public static void setMark(boolean m) {
                      mark.set(m);
                  }
                  
                  public static void destroy() {
                      mark.remove();
                  }
              }
              
              import javax.annotation.Resource;
              import javax.ejb.EJBContext;
              import javax.interceptor.AroundInvoke;
              import javax.interceptor.InvocationContext;
              
              public class EJBBoundaryInterceptor {
                  @Resource
                  private EJBContext ejbContext;
              
                  @AroundInvoke
                  public Object intercept(InvocationContext ctx) throws Exception { 
                      Boolean mark = EJBBoundaryMark.getMark().get();
                      EJBBoundaryMark.setMark(false);
              
                      try {
                          return ctx.proceed();
                      } catch(Exception e) {
                          if (mark == true)
                              ejbContext.setRollbackOnly();
                          throw e;
                      } finally {
                          if (mark == true)
                              EJBBoundaryMark.destroy();
                      }
                  }
              }
              

               

              Is it something totally dangerous/stupid/... ?

              • 4. Re: Exceptions and transaction rollback
                sfcoy

                If you're using default CMT, then EJB A and EJB B will be particpating in the same transaction. So if you catch the exception before it crosses the transaction boundary it will not be rolled back.

                 

                To rollback the transaction after catching the checked exception, just wrap the checked exception in an EJBException and throw that.

                 

                You really don't want to make this stuff more complicated than it needs to be as presumably it's a rare event.

                • 5. Re: Exceptions and transaction rollback
                  cfillot

                  If you're using default CMT, then EJB A and EJB B will be particpating in the same transaction. So if you catch the exception before it crosses the transaction boundary it will not be rolled back.

                   

                  Just to see if I understand correctly:

                   

                  - If the exception is a checked exception without @ApplicationException annotation, and crosses the transaction boundary, there is no rollback at all.

                   

                  - If the exception is a checked exception with @ApplicationException(rollback=true), with the following situation: servlet -> EJB A -> EJB B,

                  the exception being thrown by B but catch by A (so the exception does not cross the transaction boundary), the transaction will be put in rollback.

                   

                  My problem is that the @ApplicationException(rollback=true) put the transaction in rollback as soon as an EJB throws the exception, whereas

                  I would like the rollback occur only if the exception crosses the transaction boundary.

                  • 6. Re: Exceptions and transaction rollback
                    sfcoy

                    I think your summary is correct.

                     

                    Basically, you're asking for the same method to behave differently, depending upon where it's called from.

                     

                    It seems like you need two different methods to me. Perhaps using a delegate method  like:

                     

                     

                    {code:java}

                    @ApplicationException(rollback=true, inherited=false)

                    public MyApplicationException extends Exception {

                              ...

                    }

                     

                     

                    public MyRollbackFreeApplicationException extends MyApplicationException{

                              ...

                    }

                     

                     

                     

                     

                    @Stateless

                    public class MyEJBA {

                     

                     

                              public void someOperation() throws MyApplicationException {

                                  ...

                                  // oops

                                  throw new MyApplicationException();

                              }

                     

                              public void someRollbackFreeOperation() throws MyApplicationException {

                                   try {

                                             someOperation();

                                   } catch (MyApplicationException e) {

                                             throw new MyRollbackFreeApplicationException(e);

                                   }

                              }

                     

                    }

                     

                     

                    @Stateless

                    public class MyEJBB {

                     

                              @EJB

                              private MyEJBA myEJBA;

                     

                              public void someOtherOperation() {

                                        try {

                                                  myEJBA.someRollbackFreeOperation();

                                        } catch (MyApplicationException e) {

                                                  // do recovery stuff

                                        }

                              }

                     

                    }

                    {code}

                    1 of 1 people found this helpful
                    • 7. Re: Exceptions and transaction rollback
                      cfillot

                      Thanks Stephen, I think your suggestion should work fine!