SeamFramework.orgCommunity Documentation
So far, we've seen a few examples of scope type annotations. The scope of a bean determines the lifecycle of instances of the bean. The scope also determines which clients refer to which instances of the bean. According to the CDI specification, a scope determines:
When a new instance of any bean with that scope is created
When an existing instance of any bean with that scope is destroyed
Which injected references refer to any instance of a bean with that scope
For example, if we have a session-scoped bean, CurrentUser
, all beans that are called in the
context of the same HttpSession
will see the same instance of CurrentUser
.
This instance will be automatically created the first time a CurrentUser
is needed in that
session, and automatically destroyed when the session ends.
There's actually no way to remove a bean from a context until the entire context is destroyed.
CDI features an extensible context model. It's possible to define new scopes by creating a new scope type annotation:
@ScopeType
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface ClusterScoped {}
Of course, that's the easy part of the job. For this scope type to be useful, we will also need to define a
Context
object that implements the scope! Implementing a Context
is
usually a very technical task, intended for framework development only. You can expect an implementation of the
business scope, for instance, in a future version of Seam.
We can apply a scope type annotation to a bean implementation class to specify the scope of the bean:
@ClusterScoped
public class SecondLevelCache { ... }
Usually, you'll use one of CDI's built-in scopes.
CDI defines four built-in scopes:
@RequestScoped
@SessionScoped
@ApplicationScoped
@ConversationScoped
For a web application that uses CDI:
any servlet request has access to active request, session and application scopes, and, additionally
any JSF request has access to an active conversation scope.
A CDI extension can support the conversation scope for other frameworks as well.
The request and application scopes are also active:
during invocations of EJB remote methods,
during EJB timeouts,
during message delivery to a message-driven bean, and
during web service invocations.
If the application tries to invoke a bean with a scope that does not have an active context, a
ContextNotActiveException
is thrown by the container at runtime.
Three of the four built-in scopes should be extremely familiar to every Java EE developer, so let's not waste time discussing them here. One of the scopes, however, is new.
The conversation scope is a bit like the traditional session scope in that it holds state associated with a user of the system, and spans multiple requests to the server. However, unlike the session scope, the conversation scope:
is demarcated explicitly by the application, and
holds state associated with a particular web browser tab in a JSF application (browsers tend to share domain cookies, and hence the session cookie, between tabs, so this is not the case for the session scope).
A conversation represents a task—a unit of work from the point of view of the user. The conversation context holds state associated with what the user is currently working on. If the user is doing multiple things at the same time, there are multiple conversations.
The conversation context is active during any JSF request. Most conversations are destroyed at the end of the request. If a conversation should hold state across multiple requests, it must be explicitly promoted to a long-running conversation.
CDI provides a built-in bean for controlling the lifecycle of conversations in a JSF application. This bean may be obtained by injection:
@Inject Conversation conversation;
To promote the conversation associated with the current request to a long-running conversation, call the
begin()
method from application code. To schedule the current long-running conversation
context for destruction at the end of the current request, call end()
.
In the following example, a conversation-scoped bean controls the conversation with which it is associated:
@ConversationScoped @Stateful
public class OrderBuilder {
private Order order;
private @Inject Conversation conversation;
private @PersistenceContext(type = EXTENDED) EntityManager em;
@Produces public Order getOrder() {
return order;
}
public Order createOrder() {
order = new Order();
conversation.begin();
return order;
}
public void addLineItem(Product product, int quantity) {
order.add(new LineItem(product, quantity));
}
public void saveOrder(Order order) {
em.persist(order);
conversation.end();
}
@Remove
public void destroy() {}
}
This bean is able to control its own lifecycle through use of the Conversation
API. But
some other beans have a lifecycle which depends completely upon another object.
The conversation context automatically propagates with any JSF faces request (JSF form submission) or redirect. It does not automatically propagate with non-faces requests, for example, navigation via a link.
We can force the conversation to propagate with a non-faces request by including the unique identifier of
the conversation as a request parameter. The CDI specification reserves the request parameter named
cid
for this use. The unique identifier of the conversation may be obtained from the
Conversation
object, which has the EL bean name conversation
.
Therefore, the following link propagates the conversation:
<a href="/addProduct.jsp?cid=#{conversation.id}">Add Product</a>
It's probably better to use one of the link components in JSF 2:
<h:link outcome="/addProduct.xhtml value="Add Product"> <f:param name="cid" value="#{conversation.id}"/> </h:link>
The conversation context propagates across redirects, making it very easy to implement the common POST-then-redirect pattern, without resort to fragile constructs such as a "flash" object. The container automatically adds the conversation id to the redirect URL as a request parameter.
The container is permitted to destroy a conversation and all state held in its context at any time in order to conserve resources. A CDI implementation will normally do this on the basis of some kind of timeout—though this is not required by the specification. The timeout is the period of inactivity before the conversation is destroyed (as opposed to the amount of time the conversation is active).
The Conversation
object provides a method to set the timeout. This is a hint to the
container, which is free to ignore the setting.
conversation.setTimeout(timeoutInMillis);
In addition to the four built-in scopes, CDI features the so-called dependent pseudo-scope. This is the default scope for a bean which does not explicitly declare a scope type.
For example, this bean has the scope type @Dependent
:
public class Calculator { ... }
An instances of a dependent bean is never shared between different clients or different injection points. It is strictly a dependent object of some other object. It is instantiated when the object it belongs to is created, and destroyed when the object it belongs to is destroyed.
CDI makes it easy to obtain a dependent instance of a bean, even if the bean is already declared as a bean with some other scope type.
The built-in qualifier @New
allows us to obtain a dependent object of a specified class.
@Inject @New Calculator calculator;
The class must be a valid managed bean or session bean, but need not be an enabled bean.
This works even if Calculator
is already declared with a different
scope type, for example:
@ConversationScoped
public class Calculator { ... }
So the following injected attributes each get a different instance of Calculator
:
public class PaymentCalc {
@Inject Calculator calculator;
@Inject @New Calculator newCalculator;
}
The calculator
field has a conversation-scoped instance of Calculator
injected. The newCalculator
field has a new instance of Calculator
injected, with a lifecycle that is bound to the owning PaymentCalc
.
This feature is particularly useful with producer methods, as we'll see in the next chapter.