javacdiweld

is there a way to intercept all methods of all beans in CDI without touching their definition?


I would like to write a tool able to intercept all the method calls during the usage of an application that use CDI (Weld).

I have already tried with CDI interceptors, however, since I want to keep the beans unawares of the interceptor, I do not want to use the interceptor binding to observe the method calls.

Is there a way to intercept calls that satisfies this constraint?

Bonus question: with this vision in mind, is there any way of tracing the cascade of calls from a method? it is fine with both top down (retrieve all the other methods called from the body of a specific method) and bottom up strategy (retrieve the method the caller method of a specific method).


Solution

  • 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:

    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).