18

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?

Theo
  • 3,074
  • 7
  • 39
  • 54
  • Have you found the solution to this problem? It would interesting to see it. – mik May 28 '12 at 07:28
  • 1
    @milk no, I haven't. I consolidated all modules into one war module, which is possible since Java EE 6. – Theo May 29 '12 at 10:17
  • wow, thanks for laying this out so clearly. I was having the exact same problem and consolidating all of it into one module solved it. Sometimes J2EE just tries to make things hard on ya... – josh-cain Nov 06 '15 at 02:59

6 Answers6

12

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 { ... }
schnatterer
  • 7,525
  • 7
  • 61
  • 80
gortiz
  • 140
  • 1
  • 6
  • This only works if the dependency is under your control, otherwise the only option is to write a cdi Extension – cen Oct 03 '19 at 15:05
5

It is too late, but if somebody still having this problem. Both modules should be loaded by the same classloader to make usage of interceptor across different modules possible, at least in WebSphere 8b2. In WebSphere this setting can be switched in administration console: Applications > Application Types > WebSphere enterprise applications > [your app name] > Class loading and update detection > WAR class loader policy = Single class loader for application.
Interceptor must be enabled only ONCE in beans.xml.

Dmitry
  • 357
  • 3
  • 9
  • 2
    Even if it is allowed (and common) in WebSphere, this isn't recommended for JEE application. The class loaders are separate for a reason. – Archimedes Trajano Jul 27 '15 at 02:49
1

I wonder if your WAR lacks classloader visbility into your ejb-jar? I think ideally the 299 interceptors would be in their own jar, visible to both the EJB and web modules and enabled in both of their beans.xml.

covener
  • 17,402
  • 2
  • 31
  • 45
0

I had exactly the same problem with my logging interceptor on JBoss 7 and fixed it by overlaying the complete interceptor's jar into the application.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <overlays>
                    <overlay>
                        <groupId>com.github.t1</groupId>
                        <artifactId>logging-interceptor</artifactId>
                        <type>jar</type>
                        <targetPath>WEB-INF/classes</targetPath>
                    </overlay>
                </overlays>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>com.github.t1</groupId>
        <artifactId>logging-interceptor</artifactId>
        <version>1.1</version>
        <optional>true</optional>
    </dependency>
</dependencies>

You'll still have to activate the interceptor in the application's breans.xml.

Not nice, but it works. In Java EE 7, it works without the activation by annotating the interceptor as @Priority.

rü-
  • 2,129
  • 17
  • 37
0

I have the same issue on JBoss AS 6.0 / 6.1 (nightly build) and fixed it by disabling separate classloaders (option 1), but be extremely careful with this. The separation of classloaders hasn't been introduced for no reason, so apparently there are new problems on the road ahead...

This is the jira report, please vote it up :-)

Jan Groth
  • 14,039
  • 5
  • 40
  • 55
0

If you have no control over the external dependency and you still want to enable interceptors without beans.xml you can write a CDI extension:

package my.package;

import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterTypeDiscovery;
import javax.enterprise.inject.spi.Extension;

public class MyCdiExtension implements Extension {

    public void observeAfterTypeDiscovery(@Observes AfterTypeDiscovery afterTypeDiscovery) {
        afterTypeDiscovery.getInterceptors().add(SomeExternalInterceptor.class);
    }
}

Add file resources/META-INF/services/javax.enterprise.inject.spi.Extension with content:

my.package.MyCdiExtension
cen
  • 2,873
  • 3
  • 31
  • 56