6.1.2. Seam and the back button

6.1.2. Seam and the back button

When JSF or Seam navigation rules are used, Seam lets the user freely navigate via the back, forward and refresh buttons. It is the responsibility of the application to ensure that conversational state remains internally consistent when this occurs. Experience with the combination of web application frameworks like Struts or WebWork - that do not support a conversational model - and stateless component models like EJB stateless session beans or the Spring framework has taught many developers that this is close to impossible to do! However, our experience is that in the context of Seam, where there is a well-defined conversational model, backed by stateful session beans, it is actually quite straightforward. Usually it is as simple as combining the use of no-conversation-view-id with null checks at the beginning of action listener methods. We consider support for freeform navigation to be almost always desirable.

In this case, the no-conversation-view-id declaration goes in pages.xml. It tells Seam to redirect to a different page if a request originates from a page rendered during a conversation, and that conversation no longer exists:

<page view-id="/checkout.xhtml" 
        no-conversation-view-id="/main.xhtml"/>

On the other hand, in the stateful model, backbuttoning is interpreted as an undefined transition back to a previous state. Since the stateful model enforces a defined set of transitions from the current state, back buttoning is be default disallowed in the stateful model! Seam transparently detects the use of the back button, and blocks any attempt to perform an action from a previous, "stale" page, and simply redirects the user to the "current" page (and displays a faces message). Whether you consider this a feature or a limitation of the stateful model depends upon your point of view: as an application developer, it is a feature; as a user, it might be frustrating! You can enable backbutton navigation from a particular page node by setting back="enabled".

<page name="checkout" 
        view-id="/checkout.xhtml" 
        back="enabled">
    <redirect/>
    <transition to="checkout"/>
    <transition name="complete" to="complete"/>
</page>

This allows backbuttoning from the checkout state to any previous state!

Of course, we still need to define what happens if a request originates from a page rendered during a pageflow, and the conversation with the pageflow no longer exists. In this case, the no-conversation-view-id declaration goes into the pageflow definition:

<page name="checkout" 
        view-id="/checkout.xhtml" 
        back="enabled" 
        no-conversation-view-id="/main.xhtml">
    <redirect/>
    <transition to="checkout"/>
    <transition name="complete" to="complete"/>
</page>