The neverending story: Conversation leftovers!
jlemire Nov 12, 2009 8:03 PMWARNING: This is a fresh thread on an old subject. I have been surprised to find out that a problem that seems so obvious to me (and others) in typical use cases did not get a real solution and was dismissed as 'left as a proof to the reader' by the seam guys. While I agree there are workarounds, I seem to miss the obvious solution that does not deviate too much from the usual Seam philosophy
I have been following a few threads on the forum of users asking how to really
end a conversation on redirect. I will explain what I mean by really
ending a conversation, but let me first give you my very simple and I think typical use case:
----- USE CASE: Creating/editing patients -----
- The user logs onto the site
- The user navigates to the patients list
- The user clicks the
edit
link on a given patient in the list. He is directed to the creation/edition page. - The user corrects the last name of the patient and clicks save. He is brought back to the users list. In the h:messages section, he sees
The patient's information was successfully updated.
. - The user clicks on the Create user button, which directs him to the creation/edition page.
When the user executes the last step, the patientHome already has a value and he falls on the edit user page for the user he edited a few steps before. This happens because both my list and my detail creation/edition pages define long-running conversations. As long as you go from one LRC to another LRC, the target LRC keeps all the variables defined in the previous LRC.
Now you may argue that in this particular case:
- I should not use a LRC on the list and/or the edition screen: There are many occasions where a conversational page may navigate to another one when it is done. This is a navigation concern. The originating code should not care about that.
- I should not use the same page for multiple purposes: just make it that the edit and creation pages are different and the problem persists
- I should not reuse the same seam conversational component in different use cases: Oh, come on.
-------- END OF USE CASE --------
What I expected from Seam is that when a conversation is ended, the variables within its scope are destroyed. I do not really care when this happens. I just want it to be transparent, i.e. the next conversation does not contain stuff I didn't put there. However, that's not how it works. Conversation buckets can and will be reused, depending on the next page's conversation propagation configuration.
For the record: when you end a conversation, you have two choices:
- After redirect (default): Seam demotes the conversation to temporary, but re-promotes it again before redirect so the conversation context is propagated. It is however marked as
should be demoted
and is demoted to temporary conversation as soon as it is restored on the GET. - Before redirect (as an attribute in page.xml or on the End annotation): Conversation is demoted to temporary and consequently destroyed at the end of the request.
In the first case, the conversation gets destroyed as long as the next page does not initiate a LRC. If it does, the whole bucket is re-promoted along with its previous content. As long as the user goes from one LRC to the next, it accumulates garbage.
The second case does indeed destroy the conversation. Great! you say. But at least one conversational Seam component does benefit use the conversation's temporary promotion: facesMessages (and derivatives). And since the messages are not resolved yet, it also needs the whole context along with it. This means you losing the conversation makes you lose your messages.
So there are two built-in workarounds to my problem:
- use beforeRedirect=true and lose the facesmessages (maybe other stuff to?)
- make sure all the conversational components and their dependencies clean up their context themselves on conversation End.
The first solution works, but I still need the messages. I would hate to tell the people here that we cannot output feedback without an intermediary page (a la Mantis) after each action because of platform limitations.
The second workaround is as impractical and error prone as the original problem.
Seam enables and encourages a seamless post-redirect behavior in order to be restful while keeping JSF's functionality (i.e. facesmessages). Doing this, along with the extended persistence context, Seam also encourages a general use of conversations. On the functional side, I can see no reason why one would like to reuse the whole conversation state cross-redirect on conversation end.
Shouldn't there be a way, on conversation end, to clear up the current conversation bucket, propagating only what's necessary for the next page to display? That's what we have elected to do here for the time being. Either by always ending our conversation with beforeRedirect=true and keep the resolved facesmessages in a bucket we pass to the next page (creating our own mini-conversation
), or playing with the current conversation objects to try to empty the conversation's contents on demotion and keep only the resolved messages to the temporary conversation.
Anybody has a better solution?