Friday, June 28, 2013

CDI and Guice with jsr-300 javax.inject In Same Webapp

Things can get weird when java SE code developed with IOC is deployed into a java EE container that implements CDI. For example - the littleware code I work with implements simple modules and life cycle management with guice, so when I deploy that code in a webapp to a glassfish server I want guice's runtime to manage littleware's dependency injection, but I might also want CDI to inject a glassfish-managed JPA EntityManager into a servlet for me, and both guice and CDI are scanning for the same javax.inject runtime annotations. Fortunately - this kind of thing worked fine for me until recently, since CDI 1 as introduced in java EE 6 ignored jars that did not include a META-INF/beans.xml file.

Java Enterprise Edition 6 introduced JSR-330 (javax.inject annotations and provider interfaces) and JSR-299 (CDI - contexts and dependency injection) to the java-ee platform. The javax.inject package standardized the annotations and interfaces used by java's various inversion of control and dependency-injection systems (guice, spring, dagger, pico container, ...), so that code written with one IOC system in mind could be more easily used in an application that deployed to another system.

CDI introduced an IOC implementation for java enterprise edition (EE) technologies (servlets, JSF, EJB, JPA, ...) deployed in EE containers like glassfish, jboss, weblogic, and websphere.

I discovered CDI's behavior changed slightly in the new java EE 7 runtime when a webapp that ran fine in glassfish version 3 (a java EE 6 container) failed to deploy under glassfish 4 (the EE-7 reference implementation). The errors in the glassfish-4 logs lead me to this ticket in guava's bug database. It turns out that CDI's new version 1.1 runtime automatically scans all jars deployed with an enterprise application, and guava-14+ includes a couple classes with javax.inject annotations that CDI processed in an unintended way (littleware still uses guice's com.google.inject package annotations and interfaces, so CDI ignored those).

I eventually found one easy solution to this javax.inject - CDI 1.1 mix-up is to add a META-INF/beans.xml CDI config file to jars that include javax.inject annotations that require special or no handling by CDI. I added the following META-INF/beans.xml with a bean-discover-mode attribute set to "none" to the guava jar in my webapp, and that solved the problem. Hopefully guava's maintainers will add a similar beans.xml file to guava's binary distribution on maven central.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="none">
</beans>

No comments: