SeamFramework.orgCommunity Documentation
So far, we've seen a few examples of scope type annotations. The scope of a Web Bean determines the lifecycle of instances of the Web Bean. The scope also determines which clients refer to which instances of the Web Bean. According to the Web Beans specification, a scope determines:
When a new instance of any Web Bean with that scope is created
When an existing instance of any Web Bean with that scope is destroyed
Which injected references refer to any instance of a Web Bean with that scope
For example, if we have a session scoped Web Bean, CurrentUser
,
all Web 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.
Web Beans features an extensible context model. It is possible to define new scopes by creating a new scope type annotation:
@Retention(RUNTIME)
@Target({TYPE, METHOD})
@ScopeType
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.
We can apply a scope type annotation to a Web Bean implementation class to specify the scope of the Web Bean:
@ClusterScoped
public class SecondLevelCache { ... }
Usually, you'll use one of Web Beans' built-in scopes.
Web Beans defines four built-in scopes:
@RequestScoped
@SessionScoped
@ApplicationScoped
@ConversationScoped
For a web application that uses Web Beans:
any servlet request has access to active request, session and application scopes, and, additionally
any JSF request has access to an active conversation scope.
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 Web Bean with a scope that does not have
an active context, a ContextNotActiveException
is thrown by the
Web Bean manager 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 Web Beans 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.
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. However, 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.
Web Beans provides a built-in Web Bean for controlling the lifecyle of conversations in a JSF application. This Web Bean may be obtained by injection:
@Current 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 Web Bean controls the conversation with which it is associated:
@ConversationScoped @Stateful
public class OrderBuilder {
private Order order;
private @Current 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 Web Bean is able to control its own lifecycle through use of the
Conversation
API. But some other Web Beans have a lifecycle
which depends completely upon another object.
The conversation context automatically propagates with any JSF faces request (JSF form submission). 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 Web Beans 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 Web Beans name conversation
.
Therefore, the following link propagates the conversation:
<a href="/addProduct.jsp?cid=#{conversation.id}">Add Product</a>
The Web Bean manager is also required to propagate conversations across any redirect, even if the conversation is not marked long-running. This makes it very easy to implement the common POST-then-redirect pattern, without resort to fragile constructs such as a "flash" object. In this case, the Web Bean manager automatically adds a request parameter to the redirect URL.
The Web Bean manager is permitted to destroy a conversation and all state held in its context at any time in order to preserve resources. A Web Bean manager implementation will normally do this on the basis of some kind of timeoutthough this is not required by the Web Beans specification. The timeout is the period of inactivity before the conversation is destroyed.
The Conversation
object provides a method to set
the timeout. This is a hint to the Web Bean manager, which is free to ignore
the setting.
conversation.setTimeout(timeoutInMillis);
In addition to the four built-in scopes, Web Beans features the so-called dependent pseudo-scope. This is the default scope for a Web Bean which does not explicitly declare a scope type.
For example, this Web Bean has the scope type @Dependent
:
public class Calculator { ... }
When an injection point of a Web Bean resolves to a dependent Web Bean, a new instance of the dependent Web Bean is created every time the first Web Bean is instantiated. Instances of dependent Web Beans are never shared between different Web Beans or different injection points. They are dependent objects of some other Web Bean instance.
Dependent Web Bean instances are destroyed when the instance they depend upon is destroyed.
Web Beans makes it easy to obtain a dependent instance of a Java class or EJB bean, even if the class or EJB bean is already declared as a Web Bean with some other scope type.
The built-in @New
binding annotation allows
implicit definition of a dependent Web Bean at an injection point.
Suppose we declare the following injected field:
@New Calculator calculator;
Then a Web Bean with scope @Dependent
, binding type
@New
, API type Calculator
, implementation class
Calculator
and deployment type @Standard
is
implicitly defined.
This is true 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 {
@Current Calculator calculator;
@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.