Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to catch and wrap exceptions thrown by JTA when a container-managed-tx EJB commits?
    primarykey
    data
    text
    <p>I'm struggling with a problem with an EJB3 class that manages a non-trivial data model. I have constraint validation exceptions being thrown when my container-managed transactional methods commit. I want to prevent them from being wrapped in <code>EJBException</code>, instead throwing a sane application exception that callers can handle.</p> <p>To wrap it in a suitable application exception, I must be able to catch it. Most of the time a simple try/catch does the job because the validation exception is thrown from an <code>EntityManager</code> call I've made.</p> <p>Unfortunately, some constraints are only checked at commit time. For example, violation of <code>@Size(min=1)</code> on a mapped collection is only caught when the container managed transaction commits, once it leaves my control at the end of my transactional method. I can't catch the exception thrown when validation fails and wrap it, so the container wraps it in a <code>javax.transaction.RollbackException</code> and wraps <em>that</em> in a cursed <code>EJBException</code>. The caller has to catch all <code>EJBException</code>s and go diving in the cause chain to try to find out if it's a validation issue, which is <em>really</em> not nice.</p> <p>I'm working with container managed transactions, so my EJB looks like this:</p> <pre><code>@Stateless @TransactionManagement(TransactionManagementType.CONTAINER class TheEJB { @Inject private EntityManager em; @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public methodOfInterest() throws AppValidationException { try { // For demonstration's sake create a situation that'll cause validation to // fail at commit-time here, like someEntity.getCollectionWithMinSize1().removeAll(); em.merge(someEntity); } catch (ValidationException ex) { // Won't catch violations of @Size on collections or other // commit-time only validation exceptions throw new AppValidationException(ex); } } } </code></pre> <p>... where <code>AppValidationException</code> is a checked exception or an unchecked exception annotated <code>@ApplicationException</code> so it doesn't get wrapped by EJB3.</p> <p>Sometimes I can trigger an early constraint violation with an <code>EntityManager.flush()</code> and catch that, but not always. Even then, I'd <em>really</em> like to be able to trap database-level constraint violations thrown by deferred constraint checks at commit time too, and those will only <em>ever</em> arise when JTA commits.</p> <p>Help?</p> <hr> <p><strong>Already tried:</strong></p> <p><strong>Bean managed transactions</strong> would solve my problem by allowing me to trigger the commit within code I control. Unfortunately they aren't an option because bean managed transactions don't offer any equivalent of <code>TransactionAttributeType.REQUIRES_NEW</code> - there's no way to suspend a transaction using BMT. One of the annoying oversights of JTA.</p> <p>See: </p> <ul> <li><a href="http://blog.bitronix.be/2011/02/why-we-need-jta-2-0/" rel="noreferrer">Why we need JTA 2.0</a></li> <li><a href="http://onjava.com/pub/a/onjava/2005/07/20/transactions.html" rel="noreferrer">Bean-Managed Transaction Suspension in J2EE</a> (don't do this!)</li> </ul> <p>... but see answers for caveats and details.</p> <p><code>javax.validation.ValidationException</code> is a JDK exception; I can't modify it to add <strong>an <code>@ApplicationException</code> annotation</strong> to prevent wrapping. I can't subclass it to add the annotation; it's thrown by EclpiseLink, not my code. I'm not sure that marking it <code>@ApplicationException</code> would stop Arjuna (AS7's JTA impl) wrapping it in a <code>RollbackException</code> anyway.</p> <p>I tried to use a <strong>EJB3 interceptor</strong> like this:</p> <pre><code>@AroundInvoke protected Object exceptionFilter(InvocationContext ctx) throws Exception { try { return ctx.proceed(); } catch (ValidationException ex) { throw new SomeAppException(ex); } } </code></pre> <p>... but it appears that interceptors fire <em>inside</em> JTA (which is sensible and usually desirable) so the exception I want to catch hasn't been thrown yet.</p> <p>I guess what I want is to be able to define an exception filter that's applied <strong>after</strong> JTA does its thing. Any ideas?</p> <hr> <p>I'm working with JBoss AS 7.1.1.Final and EclipseLink 2.4.0. EclipseLink is installed as a JBoss module as per <a href="https://community.jboss.org/wiki/HowToUseEclipseLinkWithAS7" rel="noreferrer">these instructions</a>, but that doesn't matter much for the issue at hand.</p> <hr> <p><strong>UPDATE:</strong> After more thought on this issue, I've realised that in addition to JSR330 validation exceptions, I really also need to be able to trap <a href="http://docs.oracle.com/javase/6/docs/api/java/sql/SQLIntegrityConstraintViolationException.html" rel="noreferrer">SQLIntegrityConstraintViolationException</a> from the DB and <a href="http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html" rel="noreferrer">deadlock or serialization failure rollbacks with SQLSTATE 40P01 and 40001 respectively</a>. That's why an approach that just tries to make sure commit will never throw won't work well. Checked application exceptions can't be thrown through a JTA commit because the JTA interfaces naturally don't declare them, but unchecked <code>@ApplicationException</code> annotated exceptions should be able to be.</p> <p>It seems that anywhere I can usefully catch an application exception I can also - albeit less prettily - catch an EJBException and delve inside it for the JTA exception and the underlying validation or JDBC exception, then do decision making based on that. Without an exception filter feature in JTA I'll probably have to.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload