7 Replies Latest reply on Sep 14, 2006 12:21 PM by Todd Main

    Exception Handling (w/ interceptor)

    Todd Main Newbie

      I created a "global" interceptor (below) to simply catch unhandled exceptions and send the user to a nice error screen. I put it in ejb-jar.xml in front of the Seam interceptor, since Seam itself is source of Exceptions (no doubt because of my abuse of the framework).

      My concern was by catching Exception and not rethrowing it, I was bypassing the EJB container's rollback. Sure enough, that's true. That is, the user sees the error page, but the transaction completes normally.

      My question is this: what is a good pattern for this? Should my transactional methods (save, delete) always catch exceptions, roll back the transaction manually, then rethrow them so it goes to my error page? Is there a way to send the user to an error page without bypassing the EJB transaction processing?

      BTW, I tried using the new @Interceptor(type=CLIENT) tag to see if I could move my exception handling outside of the EJB calls. This required removing the entry from ejb-jar.xml and adding an @Interceptors(MyExceptionInterceptor.class) to the top of my session bean. Unfortunately, that resulted in the user seeing the Seam debug page, which is helpful but not pretty. Obviously, I don't fully understand the use of the @Interceptor tag...

      public class MyExceptionInterceptor {
      
       @AroundInvoke
       public Object handleException(InvocationContext invocation) throws Exception {
      
       try {
       return invocation.proceed();
       }
       catch (Exception t) {
       return "error";
       }
       }
      
      }


        • 1. Re: Exception Handling (w/ interceptor)
          Gavin King Master

          Just call setRollbackOnly().

          This is a perfectly reasonable pattern, Seam 1.1's built-in exception handling does much the same thing.

          • 2. Re: Exception Handling (w/ interceptor)
            Todd Main Newbie

            I tried the following and my transaction is still not rolling back! (Notice I'm purposefully generating a null pointer exception in my save so I can test this.)

            Also note that, as described above, the EJBException that I'm throwing is being handled in my interceptor which is not rethrowing it.

            @Name("viewUser")
            @Stateful
            @Conversational
            public class ViewUserAction implements ViewUser {
            
             @In(required = false)
             @Out(required = false)
             private User editUser;
            
             @PersistenceContext(type = PersistenceContextType.EXTENDED)
             private EntityManager em;
            
             (other attributes and methods removed...)
            
             public String save() {
             try {
             if (editUser.getId() == 0) {
             em.persist(editUser);
             }
            
             // throw an exception
             String user = null;
             user.toString();
             }
             catch (Exception e) {
             try {
             em.getTransaction().setRollbackOnly();
             }
             catch (Exception xe) {}
            
             throw new EJBException(e);
             }
            
             return "edituser";
             }
            
            }
            
            


            • 3. Re: Exception Handling (w/ interceptor)
              Gavin King Master

              em.getTransaction() is NOT the right way to get access to the JTA transaction (it should be throwing an exception).

              instead you need to call EJBContext.setRollbackOnly(). You can get the EJBContext from JNDI or by injecting it using @Resource.

              • 4. Re: Exception Handling (w/ interceptor)
                Todd Main Newbie

                Oops - stepped in it again! Thanks for your patience.

                • 5. Re: Exception Handling (w/ interceptor)
                  Todd Main Newbie

                  I did get this working, but I had to back out the Seam jars from CVS, as I couldn't figure out how to avoid hitting the Seam "ExceptionInterceptor". I'm sure there's a simple way to define my interceptor to occur just before that one, but at the moment I'm content with Seam 1.0.1.

                  One question - how did the Seam ExceptionInterceptor class bypass my "catch" clause? In the "save()" method that I included above, the catch clause was never reached if I used the 9/10/2006 CVS jars. Instead, the Seam exception interceptor kicked in, even if I changed my catch to "Throwable". That is, I was single stepping through the code, and once I threw the null pointer with "user.toString()", it went straight to your interceptor!

                  • 6. Re: Exception Handling (w/ interceptor)
                    Gavin King Master

                     

                    I did get this working, but I had to back out the Seam jars from CVS, as I couldn't figure out how to avoid hitting the Seam "ExceptionInterceptor".



                    @Interceptor(within=ExceptionInterceptor.class)

                    One question - how did the Seam ExceptionInterceptor class bypass my "catch" clause? In the "save()" method that I included above, the catch clause was never reached if I used the 9/10/2006 CVS jars. Instead, the Seam exception interceptor kicked in, even if I changed my catch to "Throwable". That is, I was single stepping through the code, and once I threw the null pointer with "user.toString()", it went straight to your interceptor!


                    This is really difficult to believe....

                    • 7. Re: Exception Handling (w/ interceptor)
                      Todd Main Newbie

                      Well, I'll see if I can package up an example, but with my luck, it will not work as I described... :-)