Chapter 5. Events, interceptors and exception handling

Complementing the contextual component model, there are two further basic concepts
that facilitate the extreme loose-coupling that is the distinctive feature of Seam
applications. The first is a strong event model where events may be mapped to event
listeners via JSF-like method binding expressions. The second is the pervasive use
of annotations and interceptors to apply cross-cutting concerns to components which
implement business logic.

5.1. Seam events

The Seam component model was developed for use with event-driven
applications, specifically to enable the development of fine-grained,
loosely-coupled components in a fine-grained eventing model. Events in Seam come
in several types, most of which we have already seen:

JSF events

jBPM transition events

Seam page actions

Seam component-driven events

Seam contextual events

All of these various kinds of events are mapped to Seam components via JSF EL
method binding expressions. For a JSF event, this is defined in the JSF template:

<h:commandButton value="Click me!" action="#{helloWorld.sayHello}"/>

For a jBPM transition event, it is specified in the jBPM process definition or
pageflow definition:

If multiple wildcarded page actions match the current view-id, Seam
will call all the actions, in order of least-specific to most-specific.

The page action method can return a JSF outcome. If the outcome is
non-null, Seam will use the defined navigation rules to navigate to a view.

Furthermore, the view id mentioned in the <page>
element need not correspond to a real JSP or Facelets page! So, we can
reproduce the functionality of a traditional action-oriented framework
like Struts or WebWork using page actions. For example:

TODO: translate struts action into page action

This is quite useful if you want to do complex things in response to non-faces
requests (for example, HTTP GET requests).

The <param> declaration is bidirectional, just
like a value binding for a JSF input:

When a non-faces (GET) request for the view id occurs, Seam sets
the value of the named request parameter onto the model object,
after performing appropriate type conversions.

Any <s:link> or <s:button>
transparently includes the request parameter. The value of the parameter is
determined by evaluating the value binding during the render phase (when the
<s:link> is rendered).

Any navigation rule with a <redirect/> to
the view id transparently includes the request parameter. The value
of the parameter is determined by evaluating the value binding at
the end of the invoke application phase.

The value is transparently propagated with any JSF form submission
for the page with the given view id. (This means that view parameters
behave like PAGE-scoped context variables for
faces requests.

The essential idea behind all this is that however
we get from any other page to /hello.jsp (or from
/hello.jsp back to /hello.jsp),
the value of the model attribute referred to in the value binding is
"remembered", without the need for a conversation (or other server-side
state).

This all sounds pretty complex, and you're probably wondering if such an
exotic construct is really worth the effort. Actually, the idea is very
natural once you "get it". It is definitely worth taking the time to
understand this stuff. Page parameters are the most elegant way to
propagate state across a non-faces request. They are especially cool for
problems like search screens with bookmarkable results pages, where we
would like to be able to write our application code to handle both POST
and GET requests with the same code. Page parameters eliminate repetitive
listing of request parameters in the view definition and make redirects
much easier to code.

Note that you don't need an actual page action method binding to use
a page parameter. The following is perfectly valid:

The first form evaluates a value binding to determine the outcome value
to be used by the subsequent rules.
The second approach ignores the outcome and evaluates a value binding
for each possible rule.

Of course, when an update succeeds, we probably want to end the current
conversation. We can do that like this:

But ending the conversation loses any state associated with the conversation,
including the document we are currently interested in! One solution would be
to use an immediate render instead of a redirect:

If you have a lot of different page actions and page parameters,
or even just a lot of navigation rules,
you will almost certainly want to split the declarations up over
multiple files. You can define actions and parameters for a page
with the view id /calc/calculator.jsp in a
resource named calc/calculator.page.xml. The
root element in this case is the <page>
element, and the view id is implied:

5.1.2. Component-driven events

Seam components can interact by simply calling each others methods.
Stateful components may even implement the observer/observable pattern.
But to enable components to interact in a more loosely-coupled fashion
than is possible when the components call each others methods directly,
Seam provides component-driven events.

You might wonder why I've not mentioned anything about event objects in
this discussion. In Seam, there is no need for an event object to propagate
state between event producer and listener. State is held in the Seam
contexts, and is shared between components. However, if you really want
to pass an event object, you can:

org.jboss.seam.beginPageflow.<name> — called when the pageflow <name> begins

org.jboss.seam.endPageflow.<name> — called when the pageflow <name> ends

org.jboss.seam.createProcess.<name> — called when the process <name> is created

org.jboss.seam.endProcess.<name> — called when the process <name> ends

org.jboss.seam.initProcess.<name> — called when the process <name> is associated with the conversation

org.jboss.seam.initTask.<name> — called when the task <name> is associated with the conversation

org.jboss.seam.startTask.<name> — called when the task <name> is started

org.jboss.seam.endTask.<name> — called when the task <name> is ended

org.jboss.seam.postCreate.<name> — called when the component <name> is created

org.jboss.seam.preDestroy.<name> — called when the component <name> is destroyed

org.jboss.seam.beforePhase — called before the start of a JSF phase

org.jboss.seam.afterPhase — called after the end of a JSF phase

org.jboss.seam.postAuthenticate.<name> — called after a user is authenticated

org.jboss.seam.preAuthenticate.<name> — called before attempting to authenticate a user

org.jboss.seam.notLoggedIn — called there is no authenticated user and authentication is required

org.jboss.seam.rememberMe — occurs when Seam security detects the username in a cookie

Seam components may observe any of these events in just the same way they
observe any other component-driven events.

5.2. Seam interceptors

EJB 3.0 introduced a standard interceptor model for session bean components. To add an
interceptor to a bean, you need to write a class with a method annotated
@AroundInvoke and annotate the bean with an
@Interceptors annotation that specifies the name of the interceptor
class. For example, the following interceptor checks that the user is logged in before
allowing invoking an action listener method:

To apply this interceptor to a session bean which acts as an action listener, we must
annotate the session bean @Interceptors(LoggedInInterceptor.class).
This is a somewhat ugly annotation. Seam builds upon the interceptor framework in
EJB3 by allowing you to use @Interceptors as a meta-annotation. In
our example, we would create an @LoggedIn annotation, as follows:

You can even have a "client-side" interceptor, that runs around any of the built-in
functionality of EJB3:

@Interceptor(type=CLIENT)
public class LoggedInInterceptor
{
...
}

EJB interceptors are stateful, with a lifecycle that is the same as the component
they intercept. For interceptors which do not need to maintain state, Seam lets
you get a performance optimization by specifying
@Interceptor(stateless=true).

Much of the functionality of Seam is implemented as a set of built-in Seam interceptors,
including the interceptors named in the previous example. You don't have to explicitly
specify these interceptors by annotating your components; they exist for all interceptable
Seam components.

You can even use Seam interceptors with JavaBean components, not just EJB3 beans!

EJB defines interception not only for business methods (using @AroundInvoke),
but also for the lifecycle methods @PostConstruct, @PreDestroy,
@PrePassivate and @PostActive. Seam supports all these
lifecycle methods on both component and interceptor not only for EJB3 beans, but also for
JavaBean components (except @PreDestroy which is not meaningful for JavaBean
components).

5.3. Managing exceptions

JSF is surprisingly limited when it comes to exception handling. As a partial
workaround for this problem, Seam lets you define how a particular class of
exception is to be treated by annotating the exception class, or declaring
the exception class in an XML file. This facility is meant to be combined with
the EJB 3.0-standard @ApplicationException annotation which
specifies whether the exception should cause a transaction rollback.

5.3.1. Exceptions and transactions

EJB specifies well-defined rules that let us control whether an exception
immediately marks the current transaction for rollback when it is thrown by
a business method of the bean: system exceptions always
cause a transaction rollback, application exceptions do
not cause a rollback by default, but they do if
@ApplicationException(rollback=true)
is specified. (An application exception is any checked exception, or any
unchecked exception annotated @ApplicationException.
A system exception is any unchecked exception without an
@ApplicationException annotation.)

Note that there is a difference between marking a transaction for rollback,
and actually rolling it back. The exception rules say that the transaction
should be marked rollback only, but it may still be active after the
exception is thrown.

But these rules only apply in the Seam component layer. What about an exception
that is uncaught and propagates out of the Seam component layer, and out of the JSF
layer? Well, it is always wrong to leave a dangling transaction open, so Seam
rolls back any active transaction when an exception occurs and is uncaught
in the Seam component layer.

5.3.2. Enabling Seam exception handling

To enable Seam's exception handling, we need to make sure we have the master servlet
filter declared in web.xml:

You may also need to disable Facelets development mode in web.xml and
Seam debug mode in components.xml if you want your exception handlers
to fire.

5.3.3. Using annotations for exception handling

The following exception results in a HTTP 404 error whenever it propagates out of the
Seam component layer. It does not roll back the current transaction immediately when
thrown, but the transaction will be rolled back if it the exception is not caught by
another Seam component.