CDI: Using Interceptors across different modules / bean archives

Theo picture Theo · Oct 29, 2010 · Viewed 12.5k times · Source

My Java EE 6 application consists of a war and an ejb module packaged in ear file. I'm using CDI for DI (i.e. I have a beans.xml file in both modules). I want to use a logging interceptor that is defined in the ejb module in the war module, as well. I've enabled the interceptor in the ejb's beans.xml:

<beans>
    <interceptors>
        <class>com.test.interceptor.LoggingInterceptor</class>
    </interceptors>
</beans>

This is working only for the classes that are annotated with the interceptor in the ejb module. Classes in war module are not intercepted (although they're annotated with the interceptor too). I thought the solution would be to enable the interceptor in the war's interceptor, as well (like above). But the application cannot be deployed with the following message:

SEVERE: Exception while loading the app : WELD-001417 Enabled interceptor class class com.test.interceptor.LoggingInterceptor is neither annotated @Interceptor nor registered through a portable extension

My LoggingInterceptor looks like this:

@Log
@Interceptor
public class LoggingInterceptor {
    private static final Logger logger =  Logger.getLogger(LoggingInterceptor.class.getName());

    static {
        logger.setLevel(Level.ALL);
    }

    @AroundInvoke
    public Object logMethod(InvocationContext ctx) throws Exception {
        logger.log(Level.FINE, "ENTRY {0} {1}",
                new Object[]{ ctx.getTarget().getClass().getName(), ctx.getMethod().getName() });
        long startTime = System.nanoTime();
        try {
            return ctx.proceed();
        } finally {
            long diffTime = System.nanoTime() - startTime;
            logger.log(Level.FINE, "RETURN {0} {1}",
                new Object[]{ ctx.getTarget().getClass().getName(), ctx.getMethod().getName() });
            logger.log(Level.FINE, "{0} took {1} ms", new Object[]{ ctx.getMethod(),
                    TimeUnit.MILLISECONDS.convert(diffTime, TimeUnit.NANOSECONDS)});
        }
    }

}

And the interceptor binding:

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Log {}

How can I use the interceptor for both modules?

Answer

gortiz picture gortiz · Jul 10, 2013

J2EE 7 specification says (reference):

The interceptors that you specify in the beans.xml file apply only to classes in the same archive. Use the @Priority annotation to specify interceptors globally for an application that consists of multiple modules

This solution has the advantage of being vendor independent.

An Example:

@Logged
@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
public class LoggedInterceptor implements Serializable { ... }