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>
2 comments:
Neat, I was thinking about how to solve the issues with lazily loading entities from the view layer (this is just *too* convenient to do without), but then I found this blog post which should cure all my problems :)
However, apparently this behaviour shouldn't be taken as granted -- apparently according to a presentation by Sun engineers at http://developers.sun.com/learning/javaoneonline/2007/pdf/TS-4593.pdf there is no guarantee that whatever is behind the filter actually gets executed in the same thread as the filter.
True. It is mentioned in the presentation that the servlet 's service method may run in a thread different than the filter 's. I just can't see why. Anyway, I forgot to mention that I use jboss and until now it seems that each request is served by a single thread, so with jboss there should not be a problem.
Post a Comment