This is a post regarding the GitHub project Maven Multi Jar Webapp with Embedded Interceptor about how to enable interceptors in CDI and a Maven multi module project.
This is a minimal working example, a MWE, showing how to enable interceptors in a multi jar JavaEE CDI application.
What gave me a big aha! moment was the following tip out of JBoss Weld CDI for Java Platform Learn CDI concepts and develop modern web applications using JBoss Weld by Ken Finnigan:
Activation of an interceptor within beans.xml will activate it for all beans within the same archive only.
This is the root of many “why woun’t my interceptor work?” questions on StackOverflow.
This project consists of a root project POM (my standard issue POM for J2EE projects. It’s a bit heavy so don’t spend any time on it.), and three child projects/modules: application, beans and intercept.
intercept contains an interceptor binding, @ResourceModel
, and
it’s interceptor.
@InterceptorBinding
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ResourceModel {
}
and
@Interceptor
@ResourceModel
public class ResourceModelInterceptor {
private static final Logger log = LoggerFactory.getLogger(ResourceModelInterceptor.class);
@AroundInvoke
public Object aroundMethod(InvocationContext ctx) throws Exception {
log.info("Before");
return ctx.proceed();
}
}
application contains the JAX-RS application (“api”) and one REST
endpoint (“test”). The endpoint is marked by the @ResourceModel
annotation.
@ApplicationPath("/api")
public class TestApplication extends Application {}
and
@ResourceModel
@Path("test")
public class TestEndpoint {
private String resource = "Hello World!";
@GET
@Produces("test/plain")
public Response simpleGet() {
return Response.ok(resource)
.build();
}
}
beans lastly, contains another endpoint (“beans”). This is also
annotated with the @ResourceModel
interceptor binding.
@ResourceModel
@Path("beans")
public class BeansResource {
private String resource = "Hello Beans!";
@GET
@Produces("test/plain")
public Response simpleGet() {
return Response.ok(resource)
.build();
}
}
There is a beans.xml
template file enabling the
jollobajano.test.ResourceModelInterceptor
from the intercept
module.
<beans
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<interceptors>
<class>jollobajano.test.ResourceModelInterceptor</class>
</interceptors>
</beans>
This template can be found in the root of this project.
beans.xml
Taking the hint from the tip from the book…. we need to enable the
interceptor in all the modules where we want it to intercept. This
means that for beans we copy the file into the
src/main/resources/META-INF
directory.
Since application is a war
type module, we copy the interceptor
enabeling beans.xml
into the src/main/webapp/WEB-INF
of that
module.
I first experimented with presence and absence of the beans.xml
file
in the application and beans modules and was able to control
whether the interceptor would work in each particular module that way.
A reoccuring question on StackOverflow and other forums is “where do I
put the beans.xml
file?”. Experimenting with this project I
concluded that
in a JAR archive, we put the beans.xml
file in a META-INF
directory in the root of the archive,
in a WAR archive the beans.xml
file goes into the WEB-INF
folder.
The way I see it, enabeling an interceptor via the beans.xml
file
tells the CDI framework, e.g. Weld, to enable the interceptor for the
archive that contains the enabeling beans.xml
.