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:
public class LoggedInInterceptor {
@AroundInvoke
public Object checkLoggedIn(InvocationContext invocation) throws Exception {
boolean isLoggedIn = Contexts.getSessionContext().get("loggedIn")!=null;
if (isLoggedIn) {
//the user is already logged in
return invocation.proceed();
}
else {
//the user is not logged in, fwd to login page
return "login";
}
}
}
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:
@Target(TYPE)
@Retention(RUNTIME)
@Interceptors(LoggedInInterceptor.class)
public @interface LoggedIn {}
We can now simply annotate our action listener bean with @LoggedIn
to apply the interceptor.
@Stateless
@Name("changePasswordAction")
@LoggedIn
@Interceptors(SeamInterceptor.class)
public class ChangePasswordAction implements ChangePassword {
...
public String changePassword() { ... }
}
If interceptor ordering is important (it usually is), you can add @Interceptor
annotations to your interceptor classes to specify a partial order of interceptors.
@Interceptor(around={BijectionInterceptor.class,
ValidationInterceptor.class,
ConversationInterceptor.class},
within=RemoveInterceptor.class)
public class LoggedInInterceptor
{
...
}
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).