CDI and Transactions e.g. in JBoss-7.0.2

In Java EE applications you are safe to consider that every method in a session bean has an associated transaction, since there is an implicit declaration of the transaction attribute required. If you like to change this behavior you have to configure this proactively by adding the annotation @TransactionAttribute with another value (see enum TransactionAttributeType). Context and Dependency Injection (CDI) does not have such an implicit declaration and no direct container managed support for transactions. But it has a very nice realization of the interceptor concept. This post shows, how to facilitate an interceptor in order to add a transaction to every (or a selection) method in a CDI bean, if it does not already exist. This is the default behavior of required.

In CDI an interceptor can be binded to an annotation, so that annotating, e.g., a method or type adds an @AroundInvoke advice to the method or all methods of the type respectively. The binding annotation may look like this:

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiredTx {
}

The annotation @InterceptorBinding declares, that this annotation binds an interceptor. We will see this later on. Next we define the interceptor:

@RequiredTx
@Interceptor
public class RequiredTransactionInterceptor implements Serializable {
	@Resource
	private UserTransaction tx;
	@AroundInvoke
	public Object beginTransactionIfNotActive(InvocationContext ic)
			throws Throwable {
		boolean newTransaction = false;
		if (tx.getStatus() != Status.STATUS_ACTIVE) {
			utx.begin();
			newTransaction = true;
		}
		Object retVal = null;
		try {
			retVal = ic.proceed();
			if (newTransaction) {
				utx.commit();
			}
		}
		catch (Throwable t) {
			if (newTransaction) {
				tx.rollback();
			}
			throw t;
		}
		return retVal;
	}
}

The interceptor is annotated with the @Interceptor annotation and the binding annotation, in order to tell the container that this interceptor is binded with the latter and the method annotated with @AroundInvoke will be executed before the intercepted method. The wrapped method is invoked via ic.proceed().

Now we can bind the interceptor to our CDI beans like this:

@Named("dummyController")
@RequestScoped
@RequiredTx
public class DummyControllerImpl implements DummyController {
  // ...
}

It is possible to annotate a selection of methods in a bean, too. One ugly thing has to be done, though. Although everything has been annotated correctly and the information would be sufficient for the CDI container, it is necessary to activate the interceptor in the beans.xml situated in the META-INF folder (in maven projects in src/main/resources):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee 
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
	<interceptors>
		<class>de.dummy.core.RequiredTransactionInterceptor</class>
	</interceptors>  
</beans>

Unlike in Java EE, with implicit transaction handling, you can now add time measurement for the method and the transaction commit separately. This is a very nice feature for validating non-functional properties of your application.

4 thoughts on “CDI and Transactions e.g. in JBoss-7.0.2

  1. Nice post!

    BTW: that is going to be a bitch if transaction is in state STATUS_MARKED_ROLLBACK or other states in which you cannot start a new transaction:

    if (tx.getStatus() != Status.STATUS_ACTIVE) {
    utx.begin();
    }

  2. @Sven: Indirection “as it is” is a nice thing, but in a container environment like an application server you get it all “for free”, i.e., you don’t want all of it, but hey it’s there…

    CDI on the other hand gives you the control to add indirection and therefor container facilities step by step. If you think: “enough is enough” you may stop adding more indirection. But all that glistens is not gold.

  3. @Daniel: Uh, you are right my little example snippet is not feature complete. I would advice to throw an appropriate exception in that case… What do you think?

Leave a Reply

Your email address will not be published. Required fields are marked *

Captcha * Time limit is exhausted. Please reload CAPTCHA.