I'm working on an application that makes extensive use of Seam's EntityHome 
base class to manage entity bean instances.  This works great in a 
non-clustered environment, but is causing "java.lang.IllegalStateException: 
EntityManager is closed" exceptions to be thrown when the web application is 
clustered by marking it distributable in web.xml.

Stepping through the EntityHome code, I've found that if an EntityHome's 
entityManager is closed but not nulled at the end of a request, subsequent 
requests to the same EntityHome instance will result in 
"java.lang.IllegalStateException: EntityManager is closed".

We've circumvented this issue by implementing a "@PrePassivate" method on our 
EntityHomes that takes care of nulling out the entityManager prior to 
passivation.  This does the job in terms of getting rid of the "EntityManager 
is closed" problem, but another problem is introduced in its place.  When 
managing an entity bean instance in one request, and attempting to 
update/persist it in a subsequent request results in 
"org.hibernate.PersistentObjectException: detached entity passed to persist".

We've circumvented this 2nd issue by merging the EntityHome's associated entity 
bean instance into the current request's entityManager instance and re-setting 
the merged entity bean instance as the EntityHome's associated entity bean 
instance.  

Once we've gotten around both of these issues, we're able to deploy our app in 
a clustered environment and have it failover without any problems.  However, 
given that the Seam-managed persistence context is touted as being able to 
anonymous wrote : "...supports transparent failover of extended persisence 
contexts, without the need to replicate any persistence context state between 
nodes..." (Chapter 8 in the Seam Manual), I'm left scratching my head, 
wondering why we had to jump through all of these hoops to get our app to work 
in a clustered environment.

So, just to prove that there was nothing specific to our app that was causing 
the problem, I used seam-gen to create a new project and generate entities from 
a very simple (one table, 2 columns) existing database schema.  The app built, 
deployed and otherwise worked just as expected without me having to change a 
thing.  Then I went in and added distributable to web.xml, and the same 
problems that I described above began surfacing.

I was able to get the seam-genned app to work with  by implementing a 
@PrePassivate method and an "attachInstanceToEntityManager()" method that is 
called by overridden isManaged(), persist(), remove() and update() on my one 
and only EntityHome.  Code from this class is pasted in below, but does anyone 
have any deeper insight on Seam-managed persistence contexts used in 
EntityHomes in an app that is distributable?  I'm hoping that either a) we're 
taking the wrong approach when clustering our app, or b) this is something that 
will be cleaned up in an upcoming version of Seam.  It's easy enough to get 
around in simple cases, but could get pretty tedious when working with entity 
bean instances that are made up of complex object graphs.
package com.mydomain.widget;
  | 
  | import javax.ejb.PrePassivate;
  | 
  | import org.jboss.seam.annotations.Name;
  | import org.jboss.seam.annotations.Transactional;
  | import org.jboss.seam.framework.EntityHome;
  | 
  | @Name("widgetHome")
  | public class WidgetHome extends EntityHome<Widget> {
  | 
  |     public void setWidgetWidgetId(Integer id) {
  |         setId(id);
  |     }
  | 
  |     public Integer getWidgetWidgetId() {
  |         return (Integer) getId();
  |     }
  | 
  |     @Override
  |     protected Widget createInstance() {
  |         Widget widget = new Widget();
  |         return widget;
  |     }
  | 
  |     public void wire() {
  |     }
  | 
  |     public boolean isWired() {
  |         return true;
  |     }
  | 
  |     public Widget getDefinedInstance() {
  |         return isIdDefined() ? getInstance() : null;
  |     }
  | 
  |     ////
  |     // code below here was not auto-genned by seam-gen, but everything 
above was
  |     ////
  | 
  |     /**
  |      * Need to null out entityManagers prior to passivation so that they'll 
get
  |      * reinitialized upon subsequent reqests. Failing to do this in a 
clustered
  |      * environment will result in "EntityManager is closed" error messages.
  |      */
  |     @PrePassivate
  |     public void prePassivate() {
  |         setEntityManager(null);
  |     }
  | 
  |     /**
  |      * if super.isManaged() returns false, but getInstance() has an Id, then
  |      * instance needs to be re-attached to the entityManager in order to
  |      * prevent "detached entity passed to persist" exceptions.
  |      */
  |     private void attachInstanceToEntityManager() {
  |         if (!super.isManaged() && getInstance().getWidgetId() != null) {
  |             setInstance(getEntityManager().merge(getInstance()));
  |         }
  |     }
  | 
  |     @Override
  |     @Transactional
  |     public boolean isManaged() {
  |         attachInstanceToEntityManager();
  |         return super.isManaged();
  |     }
  | 
  |     @Override
  |     @Transactional
  |     public String persist() {
  |         attachInstanceToEntityManager();
  |         return super.persist();
  |     }
  | 
  |     @Override
  |     @Transactional
  |     public String remove() {
  |         attachInstanceToEntityManager();
  |         return super.remove();
  |     }
  | 
  |     @Override
  |     @Transactional
  |     public String update() {
  |         attachInstanceToEntityManager();
  |         return super.update();
  |     }
  | }

View the original post : 
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4108719#4108719

Reply to the post : 
http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=4108719
_______________________________________________
jboss-user mailing list
[email protected]
https://lists.jboss.org/mailman/listinfo/jboss-user

Reply via email to