jsr342-experts@javaee-spec.java.net

[jsr342-experts] Re: Transactional interceptors and exceptions

From: David Blevins <david.blevins_at_gmail.com>
Date: Thu, 19 Jan 2012 23:38:45 -0800

On Jan 19, 2012, at 7:52 AM, reza_rahman_at_lycos.com wrote:

> From what I have seen, the exclusion/inclusion lists are rarely used. Most people just use the paradigm of runtime exception == rollback, checked-exception == commit and handle the problem in code. Similarly, I've also rarely seen @ApplicationException in use.
>
> That being said some EJB folks seem to have been calling for greater control, so I assume someone needs this:http://blog.dblevins.com/2010/07/applicationexception-is-evil-sort-of.html?

I knew I could feel my ears tingling :)

To add to that, during the small EJB BoF at JavaOne, the question was raised as to why a method that didn't start the transaction needed to cause rollback of the transaction. Handled is handled. There's no real issue unless runtime exceptions are being thrown outside the transactional boundary.

There's definitely a range where things go from black and white to gray. I don't have a solid solution, but here's the landscape as I see it.

On one extreme you have the method that started the transaction. One could easily argue that any exception, checked or unchecked, thrown from that method should cause rollback and not wanting a rollback would certainly be an edge case -- a perhaps obtuse and rare edge case at that.

On the other extreme you have normal exception handling of things going on inside the transaction. Lot's of very normal throwing, catching and handling. Checked vs unchecked is really a style question at that level. As long as it's happening inside the transaction, it's business logic. It's hard to make a blanket policy about someone else's ability to use normal code in a transaction.

The issue with flagging the exception class itself as "good" or "bad" is that it really puts these two extremes at odds.

Say I want to be a clever programmer and wrap the EntityManager with a stateless bean that also implements the EntityManager interface and will instead delegate all calls to the real EntityManager. I'll push it back out into my app with a producer method. In my wrapper I'll mark all the methods as @TransactionAttribute(MANDATORY) because few people understand detachment and it's elegant to force transactions to be in progress before the wrapped EntityManager can be used. I can even do fun things like add interceptors to collect stats on persistence times, or use getCallerPrincipal() and log who does what and generally have fun.

In this made up scenario all is lost as I can not throw a PersistenceException from my wrapper without dooming the transaction. It didn't even start the transaction. And why can an EntityManager throw a runtime exception without dooming the transaction but I can't? A bit of a double standard.

At this point my only option is to mark PersistenceException as an @ApplicationException. I'm definitely not doing that. That's just asking for it. There's no way I can make the call that everyone on my team can handle it.

A tempting solution is to say "if you started the transaction and you throw an exception, it gets rolled back, otherwise business as usual" Then not have a way to flag exceptions as exempt from that rule (could easily be added later). All exceptions are fine unless you throw one outside the transactional boundary. Until then, we give you the control to handle it. Anyone can call setRollbackOnly at any time if they want, so it isn't like we haven't added a clean and clear way doom the transaction.


Not a proposal, but some thoughts.


-David


>
> Jan 18, 2012 07:53:44 PM, jsr342-experts_at_javaee-spec.java.net wrote:
> Supporting a superset of Spring and EJB would result in something
> very complex. In particular, the way Spring allows control over
> the rollback behavior using include lists and exclude lists and
> class names and Class objects is very complex. We were hoping to
> satisfy most developers with something much simpler than Spring
> or EJB.
>
> Which of the features from Spring's @Transactional annotation do you
> think are the most used?
>
> reza_rahman_at_lycos.com wrote on 01/17/12 09:47:
> > Sorry for the delay in responding. Things are hectic these days...
> >
> > I actually think EJB 3 application vs. system exception is a good idea (except
> > for that I don't think the special treatment of RemoteException is really needed
> > -- in Spring, system exception == runtime exception). It's easy to explain,
> > works in the real world and corresponds nicely with exception handling best
> > practices: http://onjava.com/pub/a/onjava/2003/11/19/exceptions.html?page=1.
> > Now, I do think it would be very useful to provide a mechanism to override
> > default exception level behavior (no
> > annotation/checked/unchecked/_at_ApplicationException) per bean/method. Basically,
> > I would like to see the super-set of Spring
> > (http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/transaction/annotation/Transactional.html)
> > and EJB exception handling.
> >
> > As to wrapping exceptions, I do agree it's completely overkill and
> > counter-intuitive (Spring does not do it, for example).
> >
> > Hope it helps.
> >
> > Jan 10, 2012 03:14:50 PM, jsr342-experts_at_javaee-spec.java.net
> > <mailto:jsr342-experts_at_javaee-spec.java.net> wrote:
> >
> >
> > It looks like we're in general agreement on the transactional interceptor
> > approach I proposed earlier.
> >
> > Aside from the more obvious open issues around naming, there is an
> > issue related to the handling of exceptions that we need to resolve.
> >
> > The issue relates to what should happen with regard to the state of a
> > transaction when an exception is thrown that reaches a transactional
> > interceptor.
> >
> >
> > First, some background on how EJB handles container-managed transactions....
> >
> > The EJB spec distinguishes between "system exceptions", which
> > cause the container to mark the transaction for rollback, and
> > "application exceptions", which by default do not cause the
> > transaction to be marked for rollback.
> >
> > An application exception is either a checked exception (other than the
> > RemoteException) or a runtime exception annotated with @ApplicationException.
> >
> > @Target(TYPE) @Retention(RUNTIME)
> > public @interface ApplicationException {
> >
> > /**
> > * Indicates whether the container should cause the transaction to
> > * rollback when the exception is thrown.
> > */
> > boolean rollback() default false;
> >
> > /**
> > * Indicates whether the application exception designation should
> > * apply to subclasses of the annotated exception class.
> > */
> > boolean inherited() default true;
> > }
> >
> >
> > An EJB system exception is a subclass of RemoteException or a subclass
> > of RuntimeException that has not been designated as an application
> > exception.
> >
> > When the EJB container intercepts a system exception (at a business
> > method boundary), it causes the current transaction to be marked for
> > rollback. When the called method runs in the context of the caller's
> > transaction, the container throws the EJBTransactionRolledBackException
> > (or some variant thereof). If a new transaction was *started* for the
> > called method, the container throws the EJBException. In both these
> > cases, the original exception is wrappered. Application exceptions
> > are thrown back to the caller as is.
> >
> > If the caller receives an application exception, the caller presumably
> > knows based on the exception type whether the exception causes
> > rollback or not. Unless the caller is familiar with the transaction
> > attribute of the called method, however, the caller doesn't know
> > whether its own transaction was marked for rollback or whether only
> > the callee's transaction was rolled back. However, the caller can
> > invoke the getRollbackOnly method to determine the state of its own
> > transaction.
> >
> > In the case of a system exception, if the caller receives the
> > EJBException, the caller can assume that the callee's transaction was
> > rolled back, but that the caller's own transaction was not affected.
> > If the caller receives the EJBTransactionRolledBackException, the
> > caller knows that its own transaction was marked for rollback (and
> > that the callee was run within the context of that same transaction).
> >
> >
> > So much for background.... Now, what should the behavior be for
> > transactional interceptors?
> >
> > While we could adopt the EJB approach (modulo some renaming), Bill and
> > I are concerned that it is too complex. (See Chapter 14 ("Exception
> > Handling") of the EJB 3.1 spec for the gory details.)
> >
> > We propose a simpler approach along the following lines:
> >
> > By default, all exceptions should result in the current transaction
> > being marked for rollback. If the developer believes that the
> > exception should not cause rollback, he or she annotates the exception
> > class with @DontRollbackTransaction (real name for this exception *TBD*).
> >
> > The DontRollbackTransaction annotation is simply:
> >
> > @Target(TYPE)
> > @Retention(RUNTIME)
> > @Inherited
> > public @interface DontRollbackTransaction {}
> >
> > All exceptions are thrown back without any wrappering. While we can
> > understand the rationale for wrappering an exception that marks the
> > caller's transaction for rollback in a TransactionRolledbackException,
> > we're not convinced that this is needed. Likewise, we're not
> > convinced of the need to make a distinction by means of exception
> > wrappering as to whether the callee's transaction was rolled back.
> >
> >
> > We'd like your feedback on this and on your experience with regard
> > to how developers are actually making use of the current EJB facility,
> > particularly in view of the assumptions that we are making.
> >
> > Again, we are assuming the following:
> >
> > (1) It is not essential that the caller be able to identify from an
> > exception that it receives whether its own transaction was marked
> > for rollback, as the caller can use the getRollbackOnly method
> > to determine this.
> >
> > (2) It is not essential that the caller be able to identify by means
> > of the exception that it receives back whether a transaction
> > was started on behalf of a method that it invoked, as it can
> > use its knowledge of the exception received and getRollbackOnly
> > status to determine this if it does not already know this information.
> >
> > (3) Subclasses of dont-rollback exceptions will typically be dont-rollback
> > exceptions, and, if not, they can be defined to not extend dont-rollback
> > exceptions. (Note that if we receive compelling feedback from
> > developers to the contrary on this point, we could always add a
> > an element to the annotation to control this at a later time. If
> > we think this is at all likely, we should choose an annotation
> > name accordingly.)
> >
> >
> > Please post your feedback.
> >
> > Other specleads should feel free to forward this message to your expert
> > groups for
> > further input.
> >
> >
> > thanks,
> >
> > -Linda
> >
>