Tuesday, May 28, 2013

Developing Web Application with Apache Wicket + JPA (Eclipselink) without dependency injection - Easy way

It's been a while I was struggling to get Apache Wicket working with JPA (Eclipselink). Initially tried a lot to get working solution with Wicket 6 + JPA + Spring DI/AOP & later with Wicket 6 + JPA + Google Guice, but the solutions wereworking with lot of restrictions since Apache wicket is an unmanaged Framework ( https://cwiki.apache.org/WICKET/spring.html ).

Finally found out a very simple & elegant solution to make this work seamlessly without any Dependency Injection frameworks.

Here we go,  what is that we really require ? Creating an Entity Manager for each request and closing it at the end of the request like OpenEntityManagerInViewFilter filter which is implemented in Spring.

Somewhere I saw a good saying about wicket is that use Wicket framework itself to do whatever you want it, like for Custom Session Management use wickets WebSession, and don't create any more filters rather use Wickets Request Life Cycle framework etc.

At last that is where I found a way to implement JPA based web application using Wicket, lets dive into the steps without digging into further details

1) Configuration Required in Wicket Application class

WicketApplication extends AuthenticatedWebApplication{
    EntityManagerFactory entityManagerFactory;
    @Override
    public void init() {
        ------------
        ------------
        entityManagerFactory=Persistence.createEntityManagerFactory("persistence_unit");

        getRequestCycleListeners().add(new OpenEntityManagerListener()); //---->
    }

    public EntityManagerFactory getEntityManagerFactory(){
        return entityManagerFactory;
    }
}

2) Create a class OpenEntityManagerListener which implements IRequestCycleListener

OpenEntityManagerListener implements IRequestCycleListener{

    ThreadLocal<EntityManager> 
                   entityManagerThreadLocal = new ThreadLocal<EntityManager>();

      @Override
      public void onBeginRequest(RequestCycle cycle) {
EntityManagerFactory 
                    entityManagerFactory= ((WicketApplication)Application.get()).getEntityManagerFactory();   
                entityManagerThreadLocal.set(entityManagerFactory.createEntityManager());    
                getEntityManager().getTransaction().begin();
      }

      @Override
       public void onEndRequest(RequestCycle cycle) {
                EntityManager entityManager= entityManagerThreadLocal.get();
                if(entityManager!=null && entityManager.isOpen()){
entityManager.getTransaction().rollback();
entityManager.close();
}
       }

       @Override
public void onDetach(RequestCycle cycle) {
                entityManagerThreadLocal.remove();
        }

        public EntityManager getEntityManager() {
                return entityManagerThreadLocal.get();
        }

}

3. Now we need a way to access EntityManager which is created at beginning of request, here we go

public class OpenEntityManager {

public static EntityManager getEntityManager() {
Iterator<IRequestCycleListener> listenerIterator = RequestCycle.get().getListeners().iterator();

while (listenerIterator.hasNext()) {
IRequestCycleListener listener = listenerIterator.next();

if (listener instanceof RequestCycleListenerCollection) {
Iterator<IRequestCycleListener> iterator = ((RequestCycleListenerCollection) listener).iterator();

while (iterator.hasNext()) {
IRequestCycleListener appListener = iterator.next();

if (appListener instanceof OpenEntityManagerListener) {

return ((OpenEntityManagerListener) appListener).getEntityManager();

}
}
}
}
return null;
}
}

4. What's next? Lest's use the EntityManager in Wicket Pages, Forms, SortableDataProviders, etc.

public class CoursePage extends TemplatePage {
       -------------
       -------------
       @Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form1) {
                EntityManager entityManager = OpenEntityManager.getEntityManager();
                CourseService service = new CourseService(entityManager);
                service.create(formModel);
       }

}


The above is the first version, there is a flaw here i.e. irrespective of whether we use EntityManager (or) not we are creating an instance of EntityManager. Let's fix this so that EntityManager instance will be created only when we try to use EntityManager at first time during the request processing. The change is required in OpenEntityManagerListener class. The below is the refined class

public class OpenEntityManagerListener implements IRequestCycleListener{
ThreadLocal<EntityManager> entityManagerThreadLocal=new ThreadLocal<EntityManager>();

@Override
public void onBeginRequest(RequestCycle cycle) {

}

@Override
public void onEndRequest(RequestCycle cycle) {
System.out.println("inside OpenEntityManagerListener onEndRequest");
EntityManager entityManager= entityManagerThreadLocal.get();
if(entityManager!=null && entityManager.isOpen()){
entityManager.getTransaction().commit();
entityManager.close();
System.out.println("inside OpenEntityManagerListener onEndRequest== close");
}

}

@Override
public void onDetach(RequestCycle cycle) {
System.out.println("inside OpenEntityManagerListener onDetach");
entityManagerThreadLocal.remove();
}

        --------------------------
public EntityManager getEntityManager() {
synchronized (entityManagerThreadLocal) {
if(entityManagerThreadLocal.get()==null){
EntityManagerFactory entityManagerFactory =       
                                     ((WicketApplication)Application.get()).getEntityManagerFactory();
entityManagerThreadLocal.set(entityManagerFactory.createEntityManager());

getEntityManager().getTransaction().begin();

System.out.println("inside OpenEntityManagerListener getEntityManager == create entity manager if null");
}
return entityManagerThreadLocal.get();
}
}
}

Hope this helps !!!!