Tuesday, November 27, 2007

Extending the transaction boundaries beyond the EJB layer

A recent e-mail from a reader gave me the chance to write this post about transaction boundary extension. The configuration used is jboss 4.2.2, ejb3/jpa(over hibernate) with stripes as the web tier. Session bean methods that return pojos to the web tier always return detached objects.This is because the automatic transaction management starts the transaction when you call a session bean method and commits the transaction (and closes the associated hibernate session) when the session bean method returns. This means that you have to initialize every lazy field that you may use in the view layer, otherwise a lazy loading exception is waiting for you around the corner. One solution, therefore, is to initialize the lazy fields and do a very good design of your API taking care to return correctly initialized objects for each of your views. Another "lazy" solution is to extend the transaction boundary to the view layer thus keeping the peristence context active throughout each http request. This seems wrong at first glance (it violates layering !!!!), but for small applications I don't think that might be a problem. Nevertheless, each http request completes very quickly so that the transaction is not extended for large periods of time.

One way to do this extension is to use a servlet filter that manages the transactions at the request boundary.

public class TransactionFilter implements Filter {

    /**
     * Logger for the class
     */
    private final static Logger logger = Logger.getLogger(TransactionFilter.class);

    /**
     * @param request The request that we serve
     * @param response The response that we create
     * @param chain The chain of other filters that may have been defined
     * @throws IOException wraps any I/O related exception thrown during
     *             processing
     * @throws ServletException wraps any exception thrown during processing
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request instanceof HttpServletRequest) {
            UserTransaction utx = null;
            try {
                Context ctx = new InitialContext();
                Object ref = ctx.lookup("UserTransaction");
                utx = (UserTransaction) PortableRemoteObject.narrow(ref, UserTransaction.class);
                utx.begin();
                chain.doFilter(request, response);
                int status = utx.getStatus();
                if (status == Status.STATUS_ACTIVE)
                    utx.commit();
                else
                    utx.rollback();
            }
            catch (Throwable e){
                try {
                    if ((utx != null) && (utx.getStatus() == Status.STATUS_ACTIVE))
                        utx.rollback();
                }
                catch (Throwable e1) {
                    logger.error("Cannot rollback transaction", e1);
                }
                if (e instanceof ServletException)
                    throw (ServletException) e;
                if (e instanceof IOException)
                    throw (IOException) e;
                if (e instanceof RuntimeException)
                    throw (RuntimeException) e;
                throw new ServletException(e);
            }
        }
    }

    /* (non-Javadoc)
     * @see javax.servlet.Filter#destroy()
     */
    public void destroy() {
    }

    /* (non-Javadoc)
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    public void init(FilterConfig arg0) throws ServletException {
    }
}


The filter begins a transaction at the beginning of each http request and commits at the end. Each call to session bean methods has already an active transaction associated so the session bean does not start a new one (if it is configured with TransactionAttribute REQUIRED). Thus the transaction and the associated hibernate session are active throughout the request and we can use the pojos in the action classes (struts, stripes) or in jsps without caring about uninitialized lazy fields.

Don't forget to configure the filter to intercept every request associated with the view layer e.g.

    <filter-mapping>
        <filter-name>TransactionFilter</filter-name>
        <url-pattern>*.jsp</url-pattern>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>


    <filter-mapping>
        <filter-name>TransactionFilter</filter-name>
        <servlet-name>StripesDispatcher</servlet-name>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

Sunday, November 11, 2007

Welcome (back) to Stripes

A couple of weeks ago, we started an extension to an old project. It was one that started in 2004 if I remember correctly and went through several rounds of extensions (the customer is happy and constantly asks for new features :-)). The technology behind it was servlets with a custom template engine for the web tier, stateless session ejbs for the business tier and the dreaded CMP Entity EJBs for the data tier. This time, we thought that it 's a good idea to spice things up a little bit. "Why don't we use hibernate at least for the new functionality?" we asked ourselves. "Ok, but then why don't we switch to EJB3 too and use injection and other cool features ?". "Oh, and what about the web? Let 's use struts!! Oh, wait a minute. Why not stripes ????".

So here we are, mixing stripes, ejb3 session beans and jpa/hibernate with servlets, ejb2 session bean with CMP entities. It really seems we are looking for trouble but 'till now everything works great. And we did a port from jboss 4.0.5 to 4.2.1. Ok, this is not something to brag about, it was the easiest part.

I will write more about our troubles with this technology party in later posts. I hope not :-)