Duplicate class errors with Clover and JAXB or JAXB2 plugin

Still need help?

The Atlassian Community is here for you.

Ask the community

Symptoms

Project uses JAXB or JAXB2 plugin, which generates sources into src/main/java directory:

<plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb2-plugin</artifactId>
    <version>0.8.0</version>
    <configuration>
        <generateDirectory>src/main/java</generateDirectory>
    </configuration>
    <executions>
        <execution>
            <id>generate</id>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

 

When integrating Clover into a build, for example:

mvn clean clover:setup test clover:aggregate clover:clover

 

the build fails with a number of "duplicate class" errors:

[INFO] [compiler:compile {execution: default-compile}]
[DEBUG] Using compiler 'javac'.
[DEBUG] Source directories: [/bamboohome/xml-data/build-dir/MYPLAN-JOB1/mymodule/target/clover/src-instrumented
/bamboohome/xml-data/build-dir/MYPLAN-JOB1/mymodule/src/main/java]
...
[INFO] Compiling 123 source files to /bamboohome/xml-data/build-dir/MYPLAN-JOB1/mymodule/target/classes
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure

/bamboohome/xml-data/build-dir/MYPLAN-JOB1/mymodule/target/clover/src-instrumented/com/acme/Foo.java:[16,7] duplicate class: com.acme.Foo

Cause

A build has a following sequence of events:

1. clover:setup is called - Clover instruments regular sources found in 'src/main/java' and changes the source root from 'src/main/java' to 'target/clover/src-instrumented'
2. jaxb2:generate is called - JAXB2 generates sources and adds 'src/main/java' directory as a source root again
3. javac gets two source roots - 'src/main/java' and 'target/clover/src-instrumented' - thus it gets "regular" sources twice (original and instrumented), what leads to the duplicate class error

 

Resolution

There are three solutions possible:

 

1. Use separate source root for generated sources

Thanks to this, the Clover-for-Maven Plugin will instrument regular sources found in "src/main/java" and redirect source root to the "target/clover/src-instrumented", while the JAXB/JAXB2 plugin will put generated sources into another source root. Clover can instrument or not these generated sources, depending on:

  • the order in which JAXB's 'generate' and Clover's 'setup' goals are called
  • the includesAllSourceRoots configuration setting for Clover

 

Example:

<plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb2-plugin</artifactId>
    <version>0.8.0</version>
    <configuration>
        <generateDirectory>target/generated-sources/jaxb</generateDirectory>
    </configuration>
    <executions>
        <execution>
            <id>generate</id>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

 

2. Call JAXB/JAXB2 'generate' goal directly from a command line

Thanks to this, source generation will happen before clover:setup is called and thus:

  • the 'src/main/java' will contain both regular and generated sources
  • the 'src/main/java' will be redirected to 'target/clover/src-instrumented'
  • the javac will see only one source root


Example:

mvn clean jaxb2:generate clover:setup test clover:aggregate clover:clover

 

3. Reconfigure Clover so that the 'clover:setup' is called after a source generation phase

This requires configuring Clover in the POM (it means that the "Automatic Clover integration " feature in Bamboo cannot be used). Thanks to this, Clover will be able to "see" generated sources and could instrument them as well. Set the includesAllSourceRoots property to true (default is false) if you're interested in code coverage for JAXB generated sources. For instance, the 'jaxb2:generate' goal can be bound to the 'generate-sources' phase, while the 'clover:setup' goal to the 'process-sources' phase.

 

Example:

<plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb2-plugin</artifactId>
    <version>0.8.0</version>
    <configuration>
        <generateDirectory>src/main/java</generateDirectory>
    </configuration>
    <executions>
        <execution>
            <id>generate</id>
			<phase>generate-sources</phase>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>com.atlassian.maven.plugins</groupId>
    <artifactId>clover-maven-plugin</artifactId> <!-- Before 4.1.1 it was maven-clover2-plugin -->
    <version>4.1.1</version>
    <configuration>
        <!-- Instrument all source files, also generated by JAXB. Set to false if
        you're not interested in such details (default is false) -->
        <includesAllSourceRoots>true</includesAllSourceRoots>
    </configuration>
    <executions>
        <execution>
            <id>instrument</id>
			<phase>process-sources</phase>
            <goals>
                <goal>setup</goal>
            </goals>
        </execution>
    </executions>
</plugin>
mvn clean test clover:aggregate clover:clover

 

References

 

Code samples

Checkout sources of the clover-maven-plugin from Bitbucket:

hg clone https://bitbucket.org/atlassian/maven-clover2-plugin

Open the src/it/jaxb directory. It contains an example how to configure JAXB and Clover in pom.xml.

 

Last modified on Dec 8, 2015

Was this helpful?

Yes
No
Provide feedback about this article
Powered by Confluence and Scroll Viewport.