A Little Less Conversation…
One thing that I wrote that I haven’t really gotten around to examining and verifying in closer detail and validating my position on is the production of the conversational entity manager in the Knappsack archetypes. This article looks at this and re-evaluates my thinking on the use of conversational contexts in CDI.
Defaulting to Request Scoped
It’s one of those things that you write and it works, but something feels wrong with it, and I haven’t had time to go back and look at it. The entity manager is scoped to the conversation so that it will work with as both conversational and request scoped. When an object is scoped to the conversation and there is no long running conversation then the conversation itself is limited to the request so your data, although conversation scoped becomes request scoped. This was a handy way to default data that was mostly request scoped to a request scope while giving it the ability to partake in active conversations. In Seam, this was fairly standard practice since it allowed your model to live in a conversational entity manager without having to worry about detachment issues. However, CDI conversations are different from Seam conversations and their use needs to be a little more focused.
The problem is that CDI is supported in a number of environments within Java EE 6 while the conversation scope is not. To name a few, servlets and web services don’t have default support for conversations and Arquillian tests doesn’t handle conversations well. While we may be able to work around non-conversational servlets, web services and arquillian tests, the problem really goes deeper.
Logically we are thinking about using these beans as request scoped beans because there is no long running conversation. However, the conversational context still needs to be initialized and available to hold these beans. CDI doesn’t magically know to put the conversational beans in the request scope context when there’s no long running conversation, it always puts it in the conversational context.
So, the answer you say is to use request scoped beans and relegate a conversational scope to those cases where you really do need a conversational scope, and not use it everywhere you want request scoped but might need a conversation scoped bean. While this is true, our request scoped beans depend on business logic beans that in turn use the data layer beans that uses our entity manager to access the database. The problem is that the entity manager is conversation scoped so all our beans that depend on it are broken.
The end result is that using some of the tools and API’s with CDI has been rather frustrating since nearly every bean depends on the entity manager at the end of the day and without conversation support it typically breaks with an obscure error message.
The solution is to treat our entity manager like our beans, we only use conversations where absolutely necessary. The question you may be asking yourselves is how to deal with code that is both request and conversation scoped. For example, you load an entity at the start of a wizard into a conversation scoped bean using a conversation scoped entity manager because you want to keep this bean attached for the duration of the conversation. At the end of the conversation, you might want to use the standard request scoped entity Dao to save the entity. Will the request scoped Dao will use a different entity manager from the conversation scoped bean or not? The good news is that we can re-use the same entity manager sourced from the same place and offered up as a request scoped entity manager to get around this so they will use the same entity manager.
Additionally, the introduction of some of the new JSF 2.0 scopes helps out since we can use the flash or view scopes to pass objects from one page to the next or to maintain state between requests. The only problem here is that these scopes are in JSF and not compatible with CDI, or implemented in CDI by default. The Seam 3 faces module provides CDI compatible alternatives and I would put good money on those scopes making it into the next version of CDI.
Lesson Learned
One take away from this is to always make sure everything will play nice together before committing to a particular strategy or design. It would have been a shame to implement our data access layer and find out that we couldn’t use it in our web services or testing.
I’ll be trying to spend a little time working all this out and seeing what the best strategies are, but hopefully this will remove some of the barriers to demonstrating and adding third party integrations into the Knappsack archetypes. This will probably get changed for the next version of the Knappsack archetypes.
5 thoughts on “A Little Less Conversation…”
Comments are closed.
Hi,
I really enjoyed reading this article.
I do have a question regarding the wizard example above: do I really need an entity to model the data the wizard is gathering through all of its pages? I think a DTO is enough. At the end of the wizard, you get an entity manager which will fetch the entity from DB and will update its fields based on the DTO. In this way, all the problems related to request/conversational scope are gone. The DTO will live inside a conversation, but the entity to be updated will live inside a request scope. What do you say ?
Hi Bogdan, the entity in a conversation example was just off the top of my head. You can use a DTO that isn’t persisted directly. However, there might be cases where you need to interact with entity data over the course of several requests, even if it is not part of some wizard.
Cheers,
Andy
So, in the end, for a CDI application to behave predictably, you must fallback to good old stateless architecture?
Well I think that you should always go as stateless as possible and conversations should be kept to a minimum. The problem is that with Seam, there was/is a tendency to use a conversational scope by default, whereas now it needs to be the exception.
If not, it’s not so much that CDI won’t behave, but introducing conversation scope means you’ll have to do a bit of work to integrate it with other frameworks.
I think one of two things will happen, there will eventual integration with the conversation scope by these additional frameworks (very likely), or people will code around it in a stateless fashion, and use conversation scope for very few things, again, especially with the new JSF scopes.
Cheers,
Andy
Hi Andy,
Can you elaborate a little on “The solution is to treat our entity manager like our beans” and “the same entity manager sourced from the same place and offered up as a request scoped entity manager” ?
My solution is basically a no-solution: manual propagation of the entity manager to the data layer from the boundary/facade post-construct method (where the entity manager is injected) [1]
A use case I have in mind with the problem is the following: Assume you use a conversation-scoped bean for editing an order and you want to select a customer with a popup search dialog. Now, you probably want customerDao.getCustomers(criteria) to be using a request-scoped PC (to avoid caching one-off search results) and customer.getCustomerById(selected) to be using the same conversation-scoped PC as the order editing bean (to avoid loading the selected customer multiple times).
[1] I think CDI does not support constructor injection, but may be wrong …