May 19, 2010

The deep dark hell that can be JBoss Seam

Forgive me the tone of the title. I just spent four days at work figuring out what was going wrong with our project, and I have had quite enough. We managed to fix it though, and I wanted to share the problem and the fix for those unfortunate enough to follow our tracks.

The problem. You want to add a seam-enabled web-app on top of a pure EJB3 model. The model can't have any seam in it; so no seam annotations in the code. Why ? Because we want to reuse the model in non-seam contexts. But we do want to take advantage of seam in the web-app. To me that sounds reasonable.

The idea. Enable the entity beans and session beans as seam components by defining them in a components.xml in the war. Define a seam entity manager in the conversation scope. Inject the seam entity manager wherever an EJB expects an entity manager which would otherwise get injected by @PersistenceContext. Inject other beans wherever an EJB expects something injected by @EJB. Simple enough right ?

The problem I wasted four days on. Constant exceptions related to "entityManager is closed". Tracing the cause of these took a long time, in part because it's hard to see what happens to the entity manager at run-time, in part because you also get secondary exceptions which hide the real problem (such as "entity not managed"). I also lost time to some side issues which muddied the waters (duplicate jar in the deployment); so I won't blame seam for all of the lost time.

The cause. Our sessions beans were stateless, yet they retained the same entity manager over the course of their lifetime. The first time that's ok; the entity manager is open and usable. The second time is less fun: the entity manager is now closed, and a new one was not injected. So when you try to use it: kablammo! Exception! See if you can figure out this fun stack trace, why don't ya!

A solution. Not sure if this is the best way to do it, but after four days I'm just happy it's working: make your beans stateful. Yeah, that's not quit ideal from an EJB point of view, but it's either that or redoing the front-end without seam.

Parting thoughts. For all the publicity about the flexibility of seam and how easy to use it is, I find it very strange that seemingly no one has tried to add seam on top of pure EJBs. Does everyone just give up and add annotations to the source code (seam top-to-bottom) ? If so, do they then have two versions of their EJBs lying around ? If not, what other solution is there ?

Feel free to make fun of my noob Seam skills, but if you do please give me a better solution. If not, I'll feel free to delete your comment. :-)

6 comments:

Crimson13 said...

This story reminds me of my time working on Windows drivers. I often had to spend three days searching for a problem that took two lines of code to fix. Writing software can be so much fun sometimes. :-(

Joram Barrez said...

What about wrapping your EJB's in another EJB that is Seam enabled, injecting the original one? :p

KrisDS said...

@Joram: would that put the entity manager in the correct state ? Or do I need some magic for that as well ?

Joram Barrez said...

I was actually just joking ;-) But it could work in theory. Perhaps you must programmatically set the entityManager every time ...

404 Error! said...

I am facing the same problem as you have. I haved achieved a solution similar to yours but I am just wondering how would you "propagate" the SMPC.

Let's say you have two EJBs: ServiceA and ServiceB, each bean injects an entity manager using @PersistenceContext. And in ServiceA you have:

@EJB ServiceBLocal serviceB;

At some point, serviceA will invoke a method in serviceB, which may potentially call entity manager in serviceB. Even if you define both beans in components.xml, serviceB will still be managed by the EJB container rather than the Seam Context (as it is marked by @EJB), which leads to detached entities.

KrisDS said...

Ah yeah, the fun continues. :-)

For reference: we finally gave up on this and did not let Seam take over the persistence context. Instead we went old school and initialise everything in the objects which is needed in the front-end ourselves. That's not much fun, but at least it works in the way you expect it to.

If anyone figures out the right way to do this with Seam then I would still very much like to know!