Search code examples
jakarta-eeannotationsejbcdijboss-arquillian

CDI: beans.xml not used in an EJB project during integration testing with Arquillian


I'm trying to create a Java EE 7 project with explicit CDI. The project is packaged by Maven as an ejb project.

Despite Java EE 7 not requiring beans.xml anymore, I have one such file, located under src/main/resources/META-INF/beans.xml, as I want to use @Alternatives for testing purposes.

To be more precise, I want to use an H2 in-memory database during integration testing (With Arquillian and Weld), but a good 'ol Postgres one for non-local environments.

My problem:

It seems the beans.xml file is ignored. Tests:

  • When I remove one of the classes that @Produces EntityManager, it works as expected.
  • When I have both classes, but only one annotated @Alternative, it uses the other one, regardless of what beans.xml says.
  • When I have both classes with the @Alternative annotation, it fails with

org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [EntityManager] with qualifiers [@Default] at injection point [[field] @Inject private arquillian.ArquillianTestAbstract.em]

I kinda expect beans.xml to be in the wrong place or something, but I searched far and wide (including in this related question on SO) and I cannot find my mistake.

Some files:

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.mystuff</groupId>
        <artifactId>myparent</artifactId>
        <version>${project-version}</version>
    </parent>
    <artifactId>myproject</artifactId>
    <packaging>ejb</packaging>

(snip - various dependencies)       

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-ejb-plugin</artifactId>
                    <version>${ejb-plugin-version}</version>
                    <configuration>
                        <ejbVersion>${ejb-version}</ejbVersion>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>${surefire-plugin-version}</version>
                    <configuration>
                        <argLine>-Dfile.encoding=UTF-8</argLine>
                        <forkCount>1C</forkCount>
                        <redirectTestOutputToFile>false</redirectTestOutputToFile>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>  
    </build>

</project>

src/main/resources/META-INF/beans.xml:

<?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"
       bean-discovery-mode="all" version="1.1"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">

    <alternatives>
        <class>persistence.TestDatabaseProducer</class>
    </alternatives>

</beans> 

src/main/java/persistence/DatabaseProducerImpl.java:

@Singleton
@Alternative
public class DatabaseProducerImpl implements DatabaseProducer {

    @Produces
    @MyDatabase
    @Override
    public EntityManager createEntityManager() {
        return Persistence.createEntityManagerFactory("primary").createEntityManager();
    }

}

src/main/java/persistence/TestDatabaseProducer.java:

@Singleton
@Alternative
public class TestDatabaseProducer implements DatabaseProducer {

    @Produces
    @InternalDatabase
    @Override
    public EntityManager createEntityManager() {
        return Persistence.createEntityManagerFactory("internal").createEntityManager();
    }

}

Solution

  • In the end, I was searching in the wrong direction. There was nothing wrong with the beans.xml file, except that it wasn't taken into account during tests.

    This was specific to the testing on Weld through Arquillian, and due to the way Assets are packaged and sent to the embedded container.

    Standard practices for Arquillian testing encourages the use of ShrinkWrap to create the JavaArchive deployed on Weld, and ShrinkWrap does not embark beans.xml if you don't ask it to.

    This code solved the problem: (specifically, the .addAsManifestResource() call)

    @Deployment
    public static JavaArchive createDeployment() {
        final BeansDescriptor beansXml = Descriptors.create(BeansDescriptor.class);
    
        return ShrinkWrap.create(JavaArchive.class)
            .addPackages(true, "persistence")
            .addAsManifestResource(new StringAsset(beansXml.alternativeClass(TestDatabaseProducer.class).exportAsString()), beansXml.getDescriptorName());
    }