Conversation timeout seems to be a commonly misunderstood Seam concept. I often see postings on the Seam forums claiming that the conversation-timeout doesn’t work! Well, actually it does, you simply have to understand the semantics.
Configuring the conversation-timeout period can be accomplished through the following in your components.xml:
<core:manager conversation-timeout="900000" />
At first glance most developers relate conversation-timeout to session timeout, where any conversation will simply timeout after the configured conversation timeout period. As you will quickly notice during testing, this is not the case. To execute a quick experiment, try the following configuration in the components.xml of the seam-booking example:
<core:manager conversation-timeout="60000" />
Because the conversation-timeout is set in milliseconds, the above configuration sets the conversation-timeout to 1 minute. Now in the web.xml set the session timeout to 5 minutes:
<session-config> <session-timeout>5</session-timeout> </session-config>
Now in the destroy method of the HotelBookingAction add the following line:
...
@Destroy @Remove
public void destroy() {
log.info("Destroying HotelBookingAction...");
}
...
This will log our message when the conversation ends and the HotelBookingAction is destroyed. Now deploy the seam-booking example to your local JBoss instance and start up a conversation. This can be accomplished by logging in, navigating to the hotels listing, and selecting a hotel for booking. At this point, wait for the 1 minute period… nothing happens. Now wait for another 4 minutes, the message is displayed. The conversation timed out along with the session.
Foreground vs. Background Conversations
So why didn’t our conversation timeout as configured? This is because the conversation-timeout only affects background conversations. The foreground conversation will only timeout when the session times out. Foreground, background, what? The foreground conversation is the conversation that the user last interacted with. A background conversation is any other conversation in the user’s session. Thus, in our previous scenario the foreground conversation timed out with the session as expected.
Now lets try another approach. Perform the same steps as before to proceed to the booking screen. Now open a new window and perform the same steps. We now have a foreground conversation and a background conversation in progress. Again, wait 1 minute. Nothing happened. If you wait an additional 4 minutes, both conversations will timeout. So what is going on here, I thought we had a background conversation? We did, Seam simply checks the conversation timeout on each request. Thus, if I interact with the foreground conversation after 1 minute, the background conversation times out. Try it, perform the same steps, wait 1 minute and then click on the window of the foreground conversation and you will see the log message.
This is very desirable behavior. Essentially when a user leaves his or her desk for a period of time and comes back, if the session is still active it would be desirable to maintain the state the user was previously in. This is the foreground conversation state. All other background conversation state is left to timeout after the configured conversation-timeout period which reduces overall memory consumption. This enables a developer to think less about memory usage and cleaning up state and more about developing business logic. That’s why we’re here right?
Letting the user choose
So you may be asking at this point why the conversation-timeout doesn’t use polling. As we said, you must interact with the foreground conversation to cause the background conversations to timeout after the conversation-timeout period. Imagine that the user had many windows open and leaves his or her desk. Based on which window the user clicks on when they return, that becomes the foreground conversation timing out any other background conversations. This gives the user a chance to resume whichever conversation he or she chooses, not the one the developer chooses.


Jacob,
Interesting article and contains very useful info. It will help to also show how different conversations (parent/nested) on the conversation stack time-out based on the specified conversation time out. Use 2-3 different scenarios.
Jacob,
One point to note that if a parent conversation is the foreground conversation, then all of its children (or nested) conversations at any level of depth would not time-out. Am I correct?
That is definitely a good point. Nested vs. parent conversations presents a very interesting scenario for timeouts with respect to the conversation stack. I will post a follow up that discusses this point.
No, if a parent conversation is the foreground conversation, only conversations down the stack (toward the root) would now be in the foreground. Any conversations nested on the parent would now be background conversations. Let’s take the following scenario:
cid1 -> cid2 -> cid3
These are conversations on the stack. Let’s say that the user started cid3 but backs up to cid2 and begins to interact with the application again. cid2 is the foreground conversation, so cid3 (being nested on cid2) is now a background conversation. On the other hand, cid1 is the parent of cid2 so will not timeout.
As you can imagine, the more nesting is done, the more interesting the timeout scenario could get. Hope that helps.
I like to keep foreground conversation timeout as session-timeout, and background conversation timeout as conversation-timeout. It is reasonable in a real application. Since the background conversations mostly are those which users propagate away from. So it is better to let system to clear them in a short time.
However, it is hard to convert a foreground conversation to background one in an application, and start a new foreground conversation. So, it is difficult to apply conversation scenario in a real production application based on my understanding.
The Seam booking example described in this posting demonstrates an approach to apply this in practice. If a hotel is selected a conversation is started. If the user then uses the back button or opens a new window and selects another hotel, a different conversation is started. The previous conversation becomes a background conversation which leaves it to timeout. You can refer to the source to see how this is accomplished. Hope that helps.
Hi Jacob, Thank you for your reply. However, I think the booking example only works on the SAME foreground conversation bean, for example, I start a conversation A from Conversation Bean A by clicking @Begin from A, then if I go back to click @Begin from the same Conversation Bean A, Seam will start a new foreground conversation, BUT instead, if I go back to click @Begin from Conversation B, the whole system will throw exception (need @Begin (join=true)) and the system will be down.
I have not reproduced the bug you are describing. Have you modified the source of the example? If you are getting an exception with the booking example without modification to the source I would recommend reporting it in the Seam JIRA. Make sure you provide a reliable test scenario in the JIRA issue and someone will certainly have a look at it.
Essentially, if we discuss in the context of the booking example, a conversation should @Begin each time a hotel is selected on the main page:
... @Begin public void selectHotel(Hotel selectedHotel) { hotel = em.merge(selectedHotel); } ...If I was to use the back-button to return to the main view and select another hotel, an exception should not be thrown on selection. This is because by backing up, I am no longer in the context of a long-running conversation.
Due to this, selecting a hotel can safely @Begin a new foreground conversation without joining or nesting. The conversation I backed up from would then become a background conversation.
For more information on this, the article I wrote on nested conversations may help. It describes a little more in-depth how the current long-running conversation is determined when a user interacts with the view.
Hi Jacob, Please take a look on this post: Thank you and Happy new year!
There is something I don’t understand in the paragraph:”Foreground vs. Background Conversations”
When we opened another window, isn’t this conversation supposed to be foreground conversation and the previous conversation is now a background one?
Hi Hussein,
Actually in the booking example a long-running conversation is not started until a hotel is selected as shown in a previous comment. But, yes, the new window would be a foreground conversation as a temporary conversation is started when the main view is rendered and the previous conversation would now be a background conversation. The example steps you through starting long-running conversations so that it makes a little more sense to readers. Hope that helps.
Jacob
This is a fantastic article. It’s a shame that Dan Allen spends 50 pages in his book and still doesn’t explain anything important leaving the reader confused.
Thanks, I’m glad you enjoyed it! If you want even more, our book goes in-depth on the topic of conversations as well as many other topics (SMPCs, transactions, events, caching, etc). I like to make sure we give our readers what they really need to know. I hope you get a chance to read it!
Hi all onlines,
I am workng on JBoss-Seam. I want to change the message “User Session Timeout”. I just want to know that from whre this message comes and by which Event.
I am using icefaces. in my seam application.
Thanks.
Hi,
this was definitely a very important article on an important Seam concept. It is covered briefly in Seam in Action:
“When a background conversation is restored using a switcher component, the conversation comes into the foreground and the user is redirected to the last view ID recorded for that conversation.”
However, it would be nice if a Seam book covered the Conversation API in depth. I am currently trying to write a generic component that will show a modalPanel as a warning to the user (similar to BofA when your session is about to timeout) when any background conversation is about to timeout (e.g. less than 1 minute to timeout). If they click the continue button, the app resets that LRC’s lastRequestTime.
I’m not sure how to determine how much time is left prior to timeout for any given LRC (whether foreground or background). I’ve looked at the conversationList and ConversationEntry API. This is not explained clearly anywhere.
It would be very nice if this info was added to the Seam ref doc.
Also, it would be nice to know exactly what does Conversation.instance() return.
here: http://docs.jboss.org/seam/2.1.0.GA/api/org/jboss/seam/core/Conversation.html
there is no description for the instance() method!
thx for the post Jacob!
@Jacob:
regarding this statement:
“If a hotel is selected a conversation is started. If the user then uses the back button or opens a new window and selects another hotel, a different conversation is started. The previous conversation becomes a background conversation which leaves it to timeout.”
I have been doing Seam development since 1.2.1 and never knew that if you click the back button a different LRC is started. I always believed that if you used @Begin(join=true), then Seam would use the pre-existing LRC. In fact, a project I’m currently working on was exhibiting this behavior when using the browser back button many times and re-executing use cases (I would see more than the expected # of LRCs in the debug.seam page).
Wow, this really needs to be covered in the Seam ref doc. Not sure if I read this concept in Seam in Action or not…
it would be VERY helpful if there was an enhancement done to the degug.xhtml such that a type column was added to indicate if each LRC was active or background, otherwise how do we know which one is the active one?
plus how does Seam handle activation of a background LRC? If the user click on a different tab, will it immediately become active or when an action on the page is taken (i.e. a4j:support even=”onblur”… or h:commandButton action fires submitting the form)?
will there be an author’s forum for your new book like manning has for Seam in Action?
Take a look at the ConversationEntry component. The time of creation and last accessed are there. You can retrieve all ConversationEntry instances through injection: @In List conversationEntries
Take another look
=> JIRA. I have actually added this in a custom debug page I use and I agree, it can be helpful at times.
Once the conversation is accessed on the server-side through a request the conversation becomes foreground. So, if you were to use event=”onblur” with an AJAX request to a conversation context, then yes, that conversation would become foreground.
There are no plans for this at the moment, but please send a request to Prentice if you feel this would be useful. Perhaps we could get something set up.
Hi Jackob,
I have a simple question about conversation timeout:
Given that I have a conversational POJO component,
Is it possible to intercept timeout event of the conversation
into which my component lives ?
I need this to call some cleanup on the component itself.
thanks in advance
Hi John,
You can always observe the event:
org.jboss.seam.conversationTimeoutThe ID of the conversation being timed out is also provided as an optional parameter.
Hi again,
Thanks for the answer.
However, this does not allow me to get the conversation scoped component
so that I do some clean up (to remove a remote SFSB).
As you’ve mentioned above, the event org.jboss.seam.conversationTimeout allows me to get the conversation being timed out. This means that the conversation scoped component is already destroyed…
Is it possible to intercept the removal of a conversation scoped component just before its destruction because of a timeout ?
Thanks in advance,
No, not yet. Actually the event is fired just before the conversation context is destroyed which means if you can get a handle to the object, you can perform any necessary operations.
Just looking through the details of it, it’s not that easy to get a handle to the object in the conversation being timed out. You have the details you need to do it (the cid), but it’s not trivial to pull an object from another conversation. If you decide to go this route, take a look at how the
Contexts.destroyConversationContext()method temporarily retrieves a conversation context. I haven’t tested this, but I believe this should work for your needs.Another option may be to annotate a method on the component to perform this cleanup as
@Destroy. Since you want the cleanup to only occur on timeout, you could set an EVENT scoped variable that indicates a conversation timeout occurred for a particular cid:@Observer(Manager.EVENT_CONVERSATION_TIMEOUT)
public void markTimeoutEvent(String cid)
{
Contexts.getEventContext().set("conversationTimeout", cid);
}
The disadvantage to this approach is that the component would need to know the id of the conversation it was created within to determine whether or not to act on this.