2

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();
    }

}
Community
  • 1
  • 1
Silver Quettier
  • 2,045
  • 2
  • 26
  • 53
  • This isn't the correct way to use alternatives. They're not matching as they are producing two different qualified entity managers. You would need to produce the same one. But even then, there are ways other than alternatives to solve this, especially if you're using arquillian. Is using an alternative a hard requirement? – John Ament May 23 '16 at 11:33
  • @JohnAment No, the use of alternative is not enforced. It seemed to be a good way to do all the configuration through Maven profiles (with a beans-test.xml file to swap with the original). Both some details about the Entity Managers qualifiers' problem or an other approach to this would be appreciated. There might be some aspects of CDI that I don't fully understand. (Yet :) ) – Silver Quettier May 23 '16 at 12:02

1 Answers1

0

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());
} 
Silver Quettier
  • 2,045
  • 2
  • 26
  • 53