- Mastering JavaServer Faces 2.2
- Anghel Leonard
- 844字
- 2021-12-08 12:41:31
The conversation scope
The conversation scope allows developers to demarcate the lifespan of the session scope.
The conversation scope is committed to the user's interaction with JSF applications and represents a unit of work from the point of view of the user; a bean in this scope is able to follow a conversation with a user. We may charge the conversation scope as a developer-controlled session scope across multiple invocations of the JSF life cycle; while session scoped lives across unlimited requests, the conversation scopes lives only across a limited number of requests.
Note
The conversation scope bean might get passivated by the container and should be capable of passivity by implementing the java.io.Serializable
interface.
The developer can explicitly set the conversation scope boundaries and can start, stop, or propagate the conversation scope based on the business logic flow. All long-running conversations are scoped to a particular HTTP servlet session and may not cross session boundaries. In addition, conversation scope keeps the state associated with a particular Web browser window/tab in a JSF application.
Note
The conversation scope annotation is @ConversationScoped
and is defined in the javax.enterprise.context
package for CDI. This scope is not available in JSF!
Dealing with the conversation scope is slightly different from the rest of the scopes. First, you mark the bean with @ConversationScope
, represented by the javax.enterprise.context.ConversationScoped
class. Second, CDI provides a built-in bean (javax.enterprise.context.Conversation
) for controlling the life cycle of conversations in a JSF application—its main responsibility is to manage the conversation context. This bean may be obtained by injection, as shown in the following code:
private @Inject Conversation conversation;
By default, the Conversation
object is in transient state and it should be transformed into a long-running conversation by calling the begin
method. You also need to prepare for the destruction of the conversation by calling the end
method.
Note
If we try to call the begin
method when the conversation is active, or the end
method when the conversation is inactive, IllegalStateException
will be thrown. We can avoid this by testing the transitivity state of the Conversation
objects using the method named isTransient
, which returns a Boolean value.
Now, add the begin
, end
, and isTransient
methods together to the following conversations:
- For start conversation, the code is as follows:
if (conversation.isTransient()) { conversation.begin(); }
- For stop conversation, the code is as follows:
if (!conversation.isTransient()) { conversation.end(); }
For example, you can add the conversation scope in PlayersBean
as follows:
@Named @ConversationScoped public class PlayersBean implements Serializable { private @Inject Conversation conversation; final String[] players_list = {"Nadal, Rafael (ESP)","Djokovic, Novak (SRB)", "Ferrer, David (ESP)", "Murray, Andy (GBR)", "Del Potro, Juan Martin (ARG)"}; private ArrayList players = new ArrayList(); private String player; public PlayersBean() { } //getters and setters public void newPlayer() { int nr = new Random().nextInt(4); player = players_list[nr]; players.add(player); } public void startPlayerRnd() { if (conversation.isTransient()) { conversation.begin(); } } public void stopPlayerRnd() { if (!conversation.isTransient()) { conversation.end(); } } }
Besides injecting the built-in CDI bean, notice that you have defined a method (startPlayerRnd
) for demarcating the conversation start point and another method (stopPlayerRnd
) for demarcating the conversation stop point. In this example, both the methods are exposed to the user through two buttons, but you can control the conversation programmatically by calling them conditionally.
Running the example inside a conversation will reveal something as shown in the following screenshot:
The list of randomly extracted players will be empty or will contain only the current extracted player until the button labeled Start Conversation is clicked. At that moment the list will be stored in session, until the button labeled Stop Conversation is clicked.
Note
During the conversation, the user may execute AJAX/non-AJAX requests against the bean or perform navigations to other pages that still reference this same managed bean. The bean will keep its state across user interactions using a conversation identifier generated by the container, and this is why the conversation scope can be the right choice when you need to implement wizards. But it might be a good idea to take into account the new JSF 2.2 flow scope as well, which solves several gaps of the conversation scope. See the upcoming section!
In this example, the conversation context automatically propagates with any JSF faces request or redirection (this facilitates the implementation of the common POST-then-redirect pattern), but it does not automatically propagate with non-faces requests, such as links. In this case, you need to include the unique identifier of the conversation as a request parameter. The CDI specification reserves the request parameter cid
for this use. The following code will propagate the conversation context over a link:
<h:link outcome="/link.xhtml" value="Conversation Propagation"> <f:param name="cid" value="#{conversation.id}"/> </h:link>
Note
A method annotated with @PostConstruct
will be called for each request as long as the bean is not involved in a conversation. When the conversation begins, the method is called for that instance and subsequent requests will use this instance until the conversation ends. Therefore, be careful how you manage this method content.
This example is wrapped into the application named ch3_4
and is available in the code bundle of this chapter.