The way (I know) to add an interceptor on any (or all) methods without touching the code is a CDI portable extension. This sounds intimidating at first but in reality it isn't. You will find plenty of resources online, but a high-level overview of the steps is:
- Create the extension class, a simple class that implements the marker interface
javax.enterprise.inject.spi.Extension
- Add a file under
/META-INF/services/javax.enterprise.inject.spi.Extension
that contains the name of the class implementing the extension
Done setting up!
Now for the interesting part: extension are simply CDI beans that contain observer methods for a set of events fired by CDI itself during the various phases of its initialization. Look here. Many of these events provide methods to tweak the container or what CDI knows for a bean. The idea is that your method @Observes
these events and adds an interceptor annotation to all bean classes.
So the next step, specific to your problem is to create the interceptor with the logic you want and the interceptor annotation, that goes with it, let's call it @MyInterceptor
. A very simple implementation of the extension class would be:
// There is a little ceremony for the annotation...
import javax.enterprise.util.AnnotationLiteral;
public class MyInterceptorLiteral extends AnnotationLiteral<MyInterceptor>
implements MyInterceptor {
// nothing more needed
}
// WARNING DEMO CODE, UNTESTED & SEE BELOW FOR POSSIBLE GOTCHAS
public class MyInterceptorExtension implements Extension {
public void onProcessAnnotatedType(@Observes ProcessAnnotatedType<?> event) {
event.configureAnnotatedType().add(new MyInterceptorLiteral());
}
}
This may need some tweaking, but the principle is to add your interceptor annotation on all bean classes discovered by CDI. I do not know if this catches bean instances produced by producer methods/fields, these might be trickier to handle.
As for tracing the cascade of calls, this is the job of your interceptor. Not trivial, but there are ways (e.g. ThreadLocal
context objects).