TOTD #151: Transactional Interceptors using CDI - Extensible Java EE 6

One of the questions often asked in recently, and I've been wondering too, is how to enable transactions using CDI Interceptors. This Tip Of The Day (TOTD) will share some basic piece of code on how to author a CDI interceptor that enable transactions. TOTD #134 provide more details about interceptors. Weld documentation certainly provide good details about how all the pieces fit together.

Lets jump to the code straight away. Lets look at the transaction interceptor binding code:

package org.glassfish.samples;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;

@Inherited
@InterceptorBinding
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface TxInterceptorBinding {
}

And the actual interceptor:

package org.glassfish.samples;

import javax.annotation.Resource;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import javax.transaction.UserTransaction;

@TxInterceptorBinding @Interceptor
public class TxInterceptor {
    @Resource UserTransaction tx;
    
    @AroundInvoke
    public Object manageTransaction(InvocationContext context) throws Exception {
        tx.begin();
        System.out.println("Starting transaction");
        Object result = context.proceed();
        tx.commit();
        System.out.println("Committing transaction");
        
        return result;
    }
}

This interceptor is injecting a "UserTransaction" and sandwiching the business method invocation between start and end of trnasaction. This interceptor is not dealing with any rollback scenarios or exception handling and so will need to be modified for a real-life scenario.

"beans.xml" looks like:

<?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>org.glassfish.samples.TxInterceptor</class>
  </interceptors>
</beans>

This basically enables the interceptor.

With all the plumbing code now ready, lets use this interceptor on our application code as:

package org.glassfish.samples;

@TxInterceptorBinding
public class ShoppingCart {
    public void checkOut() {
        System.out.println("Checking out");
    }
}

The important part is "@TxInterceptorBinding" as a class-level annotation. Now this ShoppingCart can be injected in any Java EE resource such as:

@Inject ShoppingCart shoppingCart;

such as in a Servlet. Now invoking the servlet shows the following sequence of code in the server log:

INFO: Starting transaction
INFO: Checking out
INFO: Committing transaction

That's it folks!

The code for this sample application can be downloaded from here. Just download and unzip the code, open the project in NetBeans (tried with 7.0 beta) and run it on GlassFish or any other Java EE 6-compliant container.

If you are using EJBs in a WAR, which is now allowed per Java EE 6, then you can certainly leverage all the annotation-driven transactions within your web application instead of maintaining your own interceptor-driven transactions. A similar approach can be used for security as well.

How are you dealing with transactions in your web application - using CDI-based interceptors or let the container manage it all for you ?

Technorati: totd cdi javaee6 glassfish interceptors transactions

Comments:

I put together a similar library about a year ago -- take a look at http://smokeandice.blogspot.com/2009/12/cdi-and-declarative-transactions.html

M

Posted by Matt Corey on November 22, 2010 at 01:29 AM PST #

Thanks Matt, funny I didn't care to Google, your entry is indeed at the top :-)

Posted by Arun Gupta on November 22, 2010 at 01:44 AM PST #

I've followed the instructions here, but once I place the binding annotation on my CDI component, injection stops working:

@TxInterceptorBinding
@Named
public class ShoppingCart {
@PersistenceUnit(unitName="em")
private EntityManagerFactory emf;

public void checkOut() {
EntityManager em = emf.createEntityManager(); // NPE!
System.out.println("Checking out");
}
}

Have you run into that?

Posted by Jason Lee on November 23, 2010 at 03:45 AM PST #

Jason,

Did you try the sample included in the blog ? Did it work ?

In your code sample, does it print "Checking out" on server.log if "EntityManager em = emf.createEntityManager()" line is removed along with other statements from the interceptor ?

Posted by Arun Gupta on November 23, 2010 at 05:01 AM PST #

Hello,

You inject UserTransaction with @Resource. But CDI specification declares that you can inject Java EE user transaction via @Inject. Therefore injection may be done by

@Inject @Resouce UserTransaction transaction;

Posted by Gurkan Erdogdu on November 23, 2010 at 11:41 PM PST #

​It appears the resource injection problem appears in even the most recent GlassFish build. It's likely a Weld GlassFish integration problem. I followed up on the forum post on seamframework.org with an Arquillian test to demonstrate the failure.

Posted by Dan Allen on November 24, 2010 at 02:55 AM PST #

Although both are correct, the preferred way of injecting the UserTransaction is to use @Inject.

@Inject UserTransaction tx;

The UserTransaction is a inject that CDI is required to support and thus maximized portability.

Posted by Dan Allen on November 24, 2010 at 02:57 AM PST #

Arun,

This is a great tip to help developers get started with CDI-style interceptor bindings.

For developers that want to have transaction support on managed beans, I strongly recommend using one of the CDI extensions that are already out there that provide this feature. In fact, just about every CDI extension library is providing this very feature (a pretty good sign it should have been supported in the platform).

Take a look at Seam Persistence (http://seamframework.org/Seam3/PersistenceModule), MyFaces CODI (https://cwiki.apache.org/confluence/display/EXTCDI/Index) or SoftwareMill CDI (https://github.com/softwaremill/softwaremill-common). The reason being, there's a lot more to supporting transactions than just begin and commit.

Posted by Dan Allen on November 24, 2010 at 03:02 AM PST #

Post a Comment:
Comments are closed for this entry.
About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today