Vaadin EntityManager-Per-Request pattern using Server Push

Posted by BeCodeMonkey on August 18, 2015

Hibernate is one reason you mite need to do the following since Hibernate does not like keeping entity manager alive for long.

According the the Book of Vaadin section 19.9 they recommend using a Servlet filter to close the entity manager is used. This is all good until you start using Server Push since this will use Websockets and bypass the Servlet filter. What I found is Vaadin uses Atmosphere framework for the Websocket work. So you will need a Interceptor to do the same thing a Servlet filter does for servlet requests.

import org.atmosphere.config.service.AtmosphereInterceptorService;
import org.atmosphere.cpr.Action;
import org.atmosphere.cpr.AtmosphereConfig;
import org.atmosphere.cpr.AtmosphereInterceptor;
import org.atmosphere.cpr.AtmosphereResource;

import com.beond.db.EntityManagerHelper;

@AtmosphereInterceptorService
public class AtmosphereHibernateInterceptor implements AtmosphereInterceptor {

   @Override
   public void configure(AtmosphereConfig config) {
   }

   @Override
   public Action inspect(AtmosphereResource r) {
      return Action.CONTINUE;
   }

   @Override
   public void postInspect(AtmosphereResource r) {
      // This is called at the end so put your code here
      // TODO : Close entity manager here is one is open
      EntityManagerHelper.closeEntityManager();
   }
}

My version of LazyHibernateEntityManagerProvider is a bit different from the one in the Vaadin documentation in that I only create a entity manager on the first use since not every call requires a entity manager.

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EntityManagerHelper {

   private static final Logger log = LoggerFactory.getLogger(EntityManagerHelper.class);

   private static final EntityManagerFactory       emf;
   private static final ThreadLocal<EntityManager> threadLocal;

   static {
      emf = Persistence.createEntityManagerFactory("managerBeo");
      threadLocal = new ThreadLocal<>();
   }

   public static EntityManager getEntityManager() {
      EntityManager manager = threadLocal.get();
      if (manager == null || !manager.isOpen()) {
         manager = emf.createEntityManager();
         threadLocal.set(manager);
      }
      return manager;
   }

   public static void closeEntityManager() {
      EntityManager em = threadLocal.get();    
      threadLocal.set(null);
      if (em != null)
         em.close();
   }

   public static void shutdown() {
      try {
         if (emf != null)
            emf.close();
      } catch (Throwable e) {
         log.error(e.getLocalizedMessage(), e);
      }
   }
}

Now to add your Interceptor to Atmosphere just add the following initParams to your servelet

@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true, initParams = {
@WebInitParam(name = "org.atmosphere.cpr.AtmosphereInterceptor", value = "com.bla.AtmosphereHibernateInterceptor") })
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}

This can also be done with the web.xml optionally.

<init-param>
   <param-name>org.atmosphere.cpr.AtmosphereInterceptor</param-name>
   <param-value>com.bla.AtmosphereHibernateInterceptor</param-value>
</init-param>

Just remember you will still need the Servlet Filter to close the Entity Manager also since not all request will go via websockets.


Comments