About noisy Interceptors
Interceptors allow you to wrap cross-cutting concerns - call them aspects if you prefer - around bean instances without modifying the bean implementation. Think about tracking, auditing, authorization, measuring call time, exception mapping, whatever.
EJB containers actually use this technique themselves to implement exception wrapping for example.
I first approach is to design one interceptor per aspect and stack them up at the class declaration. This may look like this for a stateless session bean:
@ExceptionMapped
@Tracked
@Audited
@Stateless
public class TheBean {
}
Good? Well, in theory. However, it fails in practice when you hit an Exception in your bean's code.
Interceptors produce a considerable amount of noise in a stacktrace. Since there are already a number of interceptors in place without your interceptor code (I observed this for EJBs in Glassfish 3), the actual exception point gets cut off in the stacktrace, which you print into a log, for example. The stacktraces become absurdly long.
You are blind!
So, what's the solution then? I am sorry, but you need to bundle some aspects together in one interceptor. That's what I did. The interceptor can still delegate to specialized classes, which take care of the aspects.
I rather have a less elegant design but see the point of an exception in my EJB's code.
About Stereotypes (or: knowing me, knowing you NOT)
Stereotypes are cool. Identify recurring annotation patterns in a design and put them together into a stereotype.
So I thought. Then I fell into the gap between EJB and CDI.
In my design, I have EJBs, which serve as the transactional entry point for JSF backing beans. They carry the interceptors (@ExceptionMapped and @Logged) and the usual EJB annotations:
@ExceptionMapped
@Logged
@Stateless
public class ServiceBean {
}
Further, I have the repository EJBs, which are internal to the service layer and cannot be called by a backing bean, because they are declared with MANDATORY transaction.
@Logged
@Stateless
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class RepositoryServiceBean {
}
Wouldn't it be much nicer to have two stereotypes containing everything:
@PublicService
public class ServiceBean {...}
@RepositoryService
public class RepositoryServiceBean {...}
So I stuffed all the annotations into the stereotypes and failed.
- The @Stateless annotation needs to stay with the class definition, otherwise the deployment of the EJB fails. From Glassfish's point of view, there simply wasn't any EJB declared to deploy.
- The @TransactionAttribute silently ceased to have effect and also needed to stay with the class.
- In both cases, WELD swallowed the nonsense without complaining.
It kind of makes sense, when you think about it. EJB was there first and CDI has been integrated later. CDI knows EJB but not the other way round. Is this temporary and will JEE finally unite both containers? I really hope so. In the current state, it is quite confusing in some corners.
Hi Hermann
ReplyDeleteExcellent points. I agree entirely on stack trace lengths. If you ever see excessive stack traces with Weld as the culprit, or JBoss AS 7, you should file issues in their issue trackers - issues.jboss.org/browse/CDI and issues.jboss.org/browse/AS7.
For Java EE 7 we are actively looking at how we can extend the stereotype concept beyond CDI and use it in other specs. David Blevins is championing this, so chat to him :-)