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.
It seems the beans.xml
file is ignored. Tests:
@Produces EntityManager
, it works as expected.@Alternative
, it uses the other one, regardless of what beans.xml
says.@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.
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();
}
}
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());
}