Race Condition in JaCoCo merge when generating a report? - race-condition

So this is something, that a colleague called a "Schroedingers Bug" - it worked fine, until something was pointed out. Something was changed and now the bug is there. And changing back didn't help - the bug is still there :-/
In our Maven project we use JaCoCo for our code coverage (maven-jacoco-plugin version 0.8.7). The surefire plugin (version 2.22.2) is making the unit tests, the failsave (version 3.0.0-M5) is doing the integration tests.
This is our POM:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${maven-jacoco-plugin.version}</version>
<executions>
<execution>
<id>before-unit-test-execution</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>${project.build.directory}/jacoco-output/jacoco-unit-tests.exec</destFile>
<propertyName>surefire.jacoco.args</propertyName>
</configuration>
</execution>
<execution>
<id>after-unit-test-execution</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>${project.build.directory}/jacoco-output/jacoco-unit-tests.exec</dataFile>
<outputDirectory>${project.reporting.outputDirectory}/jacoco-unit-test-coverage-report
</outputDirectory>
</configuration>
</execution>
<execution>
<id>before-integration-test-execution</id>
<phase>pre-integration-test</phase>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>${project.build.directory}/jacoco-output/jacoco-integration-tests.exec</destFile>
<propertyName>failsafe.jacoco.args</propertyName>
</configuration>
</execution>
<execution>
<id>after-integration-test-execution</id>
<phase>post-integration-test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>${project.build.directory}/jacoco-output/jacoco-integration-tests.exec</dataFile>
<outputDirectory>
${project.reporting.outputDirectory}/jacoco-integration-test-coverage-report
</outputDirectory>
</configuration>
</execution>
<execution>
<id>merge-unit-and-integration</id>
<phase>post-integration-test</phase>
<goals>
<goal>merge</goal>
</goals>
<configuration>
<fileSets>
<fileSet>
<directory>${project.build.directory}/jacoco-output/</directory>
<includes>
<include>jacoco-integration-tests.exec</include>
<include>jacoco-unit-tests.exec</include>
<!-- <include>*.exec</include>-->
</includes>
</fileSet>
</fileSets>
<destFile>${project.build.directory}/jacoco-output/merged.exec</destFile>
</configuration>
</execution>
<execution>
<id>create-merged-report</id>
<phase>post-integration-test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>${project.build.directory}/jacoco-output/merged.exec</dataFile>
<outputDirectory>${project.reporting.outputDirectory}/jacoco-merged-test-coverage-report
</outputDirectory>
</configuration>
</execution>
<execution>
<id>check</id>
<goals>
<!-- check is bound to the verify phase by default -->
<goal>check</goal>
</goals>
<configuration>
<dataFile>${project.build.directory}/jacoco-output/merged.exec</dataFile>
<rules>
<rule>
<element>CLASS</element>
<excludes>
<exclude>*Test</exclude>
<exclude>configuration/*</exclude>
</excludes>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>${jacoco.min.line.coverage}</minimum>
</limit>
<limit>
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>${jacoco.min.branch.coverage}</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
In between you see the commented part - that is the one that changed.
Now this will sometimes throw an EOFException (only when run locally - not when it is exectued on the Gitlab CI/CD pipeline).
The file in question is the "jacoco-integration-tests.exec". Looking up that file shows, that it has not been finished writing. It is much smaller than the other file in the folder ("jacoco-unit-tests.exec").
As this bug is not reliably produced (just now it worked fine after I deleted the target folder) we suspect some race condition going on. As if JaCoCo is still writing that integration test file and then the next step already wants to access it and that crashes. But I don't really know and information about this is hard to come by. Less so, how to fix it.
Does anyone know where this bug stems from and how to fix it?
Thank you very much.

Caused by: java.io.EOFException
at java.io.DataInputStream.readByte (DataInputStream.java:272)
at org.jacoco.core.internal.data.CompactDataInput.readBooleanArray (CompactDataInput.java:64)
at org.jacoco.core.data.ExecutionDataReader.readExecutionData (ExecutionDataReader.java:150)
at org.jacoco.core.data.ExecutionDataReader.readBlock (ExecutionDataReader.java:116)
at org.jacoco.core.data.ExecutionDataReader.read (ExecutionDataReader.java:93)
at org.jacoco.core.tools.ExecFileLoader.load (ExecFileLoader.java:60)
at org.jacoco.core.tools.ExecFileLoader.load (ExecFileLoader.java:74)
at org.jacoco.maven.ReportSupport.loadExecutionData (ReportSupport.java:83)
at org.jacoco.maven.ReportMojo.loadExecutionData (ReportMojo.java:61)
at org.jacoco.maven.AbstractReportMojo.executeReport (AbstractReportMojo.java:191)
at org.jacoco.maven.AbstractReportMojo.execute (AbstractReportMojo.java:180)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
I hope this is enough to go on. jacoco 0.8.7, Windows 10, JDK 11.0.11. It produces the jacoco.exec file but fails when creating the html report.

Related

maven-replacer-plugin with multiple files

I am trying to use maven-replacer-plugin to replace values in several files.
First thing is I already read this topic and no answer work for me. I havn't enough point to comment and have neither the time nor the will to stupidly farm points on SOF. So, sorry for this duplicate but I need to step forward.
Back to the point, here is my pom
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<version>1.5.1</version><!-- already tried with 1.5.2 & 1.5.3 -->
<configuration>
<includes>
<include>${basedir}/target/mailtools.properties</include>
<include>${basedir}/target/digishop-config.properties</include>
</includes>
<replacements>
<replacement>
<token>$${dev.varA}</token>
<value>something</value>
</replacement>
<replacement>
<token>$${dev.varB}</token>
<value>somethingElse</value>
</replacement>
<replacement>
<token>${dev.</token>
<value>${</value>
</replacement>
</replacements>
<regex>false</regex>
</configuration>
</plugin>
mvn replacer:replace
[INFO] --- replacer:1.5.1:replace (default-cli) # digishop-a ---
[INFO] Replacement run on 0 file.
I tried the solution with
<filesToInclude>${basedir}/target/mailtools.properties,${basedir}/target/digishop-config.properties</filesToInclude>
and it didn't work either.
I manage to work around this issue by making multiple "single replacement" executions of the plugin
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<inherited>false</inherited>
<executions>
<execution>
<id>replace-xxx.properties</id>
<phase>install</phase>
<goals>
<goal>replace</goal>
</goals>
<inherited>false</inherited>
<configuration>
<file>target/xxx.properties</file>
<replacements>
<replacement>
<token>$${dev.mail.server.address}</token>
<value>xxx</value>
</replacement>
<replacement>
<token>$${dev.mail.server.port}</token>
<value>yyyy</value>
</replacement>
<replacement>
<token>${dev.</token>
<value>${</value>
</replacement>
</replacements>
<regex>false</regex>
</configuration>
</execution>
<execution>
<id>replace-zzz-config.properties</id>
<phase>install</phase>
<goals>
<goal>replace</goal>
</goals>
<inherited>false</inherited>
<configuration>
<file>target/zzz-config.properties</file>
<replacements>
<replacement>
<token>$${dev.hazelcast.client.group.name}</token>
<value>ttt</value>
</replacement>
<replacement>
<token>${dev.</token>
<value>${</value>
</replacement>
</replacements>
<regex>false</regex>
</configuration>
</execution>
<execution>
<id>replace-aaa-security.properties</id>
<phase>install</phase>
<goals>
<goal>replace</goal>
</goals>
<inherited>false</inherited>
<configuration>
<file>target/aaa-security.properties</file>
<replacements>
<replacement>
<token>${dev.</token>
<value>${</value>
</replacement>
</replacements>
<regex>false</regex>
</configuration>
</execution>
</executions>
</plugin>
Use basedir before add as much includes as you want, e.g.
<basedir>${basedir}/target</basedir>
<includes>
<include>mailtools.properties</include>
<include>digishop-config.properties</include>
</includes>

The maven assembly plugin is not using the finalName for installing with attach=true?

I have configured following assembly:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<executions>
<execution>
<id>${project.name}-test-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<finalName>${project.name}-test</finalName>
<filters>
<filter>src/assemble/test/distribution.properties</filter>
</filters>
<descriptors>
<descriptor>src/assemble/distribution.xml</descriptor>
</descriptors>
<attach>true</attach>
</configuration>
</execution>
<execution>
<id>${project.name}-prod-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<finalName>${project.name}-prod</finalName>
<filters>
<filter>src/assemble/prod/distribution.properties</filter>
</filters>
<descriptors>
<descriptor>src/assemble/distribution.xml</descriptor>
</descriptors>
<attach>true</attach>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
This produced two zip-files:
distribution-prod.zip
distribution-test.zip
My expectation for the property attach=true is, that the two zip-files are installed with the name as given in property finalName.
But the result is, only one file is installed (attached) to the artifact.
The maven protocol is:
distrib-0.1-SNAPSHOT.zip
distrib-0.1-SNAPSHOT.zip
The plugin is using the artifact-id instead of property finalName!
Is this a bug?
The last installation is overwriting the first one.
What can i do to install this two files with different names?
Thanks for your investigation.
Roland
The last installation is overwriting the first one. What can i do to install this two files with different names?
As expected (I don't know if this is a bug or not but that's how the assembly plugin works). To avoid this, you will have to set the appendAssemblyId properties to true and, to obtain an equivalent result, to change the finalName to ${project.name} and the assemby id to test and prod (i.e. to use two assembly descriptors). Something like this:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<executions>
<execution>
<id>${project.name}-test-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>true</appendAssemblyId>
<finalName>${project.name}</finalName>
<filters>
<filter>src/assemble/test/distribution.properties</filter>
</filters>
<descriptors>
<descriptor>src/assemble/distribution-test.xml</descriptor>
</descriptors>
</configuration>
</execution>
<execution>
<id>${project.name}-prod-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>true</appendAssemblyId>
<finalName>${project.name}</finalName>
<filters>
<filter>src/assemble/prod/distribution.properties</filter>
</filters>
<descriptors>
<descriptor>src/assemble/distribution-prod.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Where distribution-test.xml and distribution-prod.xml declare assembly id test and prod respectively.

Maven WAR plugin not reading configuration when running in <execution> tag

I'm trying to get Maven to perform several executions with the WAR plugin. It works fine as long as it's defined in the following way:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>1.0</version>
<configuration>
(...)
</configuration>
But not in the following manner
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>package</phase>
<configuration>
(...)
</configuration>
</execution>
</executions>
</plugin>
Where Maven can't find any of the resources I defined in the <configuration> tag. Have I missed anything important, and/or is there a better way of constructing multiple WAR files in a single build?
I didn't see how to turn off the war that's generated by default, but you can use one configuration outside the <executions> element and the rest inside:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1-beta-1</version>
<configuration>
<classifier>with-junk</classifier>
<!-- temp directory that the webapp is assembled in (each must be different) -->
<webappDirectory>${project.build.directory}/build-with-junk</webappDirectory>
<webResources>
<resource>
<directory>junk</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</webResources>
</configuration>
<executions>
<execution>
<id>add-other-junk</id>
<phase>package</phase>
<goals>
<goal>war</goal>
</goals>
<!-- exclude prior configuration -->
<inherited>false</inherited>
<configuration>
<classifier>with-other-junk</classifier>
<webappDirectory>${project.build.directory}/build-other-junk</webappDirectory>
<webResources>
<resource>
<directory>other-junk</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</webResources>
</configuration>
</execution>
</executions>
</plugin>
For me, this builds artifact-0.1-with-junk.war and artifact-0.1-with-other-junk.war and both have the correct files included.
The second version applies the configuration only to the phase you've specified. I'm not able to confirm this right now, but I'd guess it is not being applied because you haven't specified a goal for the configuration to be applied to.
If you add the war goal definition into the execution does it get applied? Like so:
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>war</goal>
</goals>
<configuration>
(...)
</configuration>
</execution>
</executions>

Can maven sign not only produced jar, but also dependencies

I managed to create main jar, copy dependencies to a single directory, the only step left is to sign all jars.
I can sign my own produced jar as a part of jar:sign, but how do i sign dependencies?
Thanks
Here are a couple of options:
Use the Maven ant task to run jarsigner from the JDK against all the dependencies.
Use the webstart plugin which can sign all your JARs, even if you aren't using it for the purpose of JNLP-izing your app. I'm using it to actually JNLPize one app.
Look at what the webstart plugin source is doing to iterate over all dependencies and sign them and start a new Maven Plugin/Mojo that does the same thing, sans JNLP.
Onejar your app and its dependencies and just sign that.
add to plug-in config <archiveDirectory>target</archiveDirectory>
If you are using maven-jar-plugin, you can specify which single jar to sign using the "jarPath" setting. The following configuration causes the jar-with-dependencies file to be signed instead of the dependency-less jar file:
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>sign</goal>
</goals>
</execution>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- NOTE: The secret key is in shared version control. The
password is in shared version control. This IS NOT
SECURE. It's intended to help avoid accidentally
loading the wrong class, nothing more. -->
<jarPath>${project.build.directory}/${project.build.FinalName}-${project.packaging}-with-dependencies.${project.packaging}</jarPath>
<keystore>${basedir}/keystore</keystore>
<alias>SharedSecret</alias>
<storepass>FOO</storepass>
</configuration>
</plugin>
If you want to sign both, I don't know how to do that with maven-jar-plugin, so you may need to look into the other options mentioned above.
One can also create a single JAR using the maven-assembly-plugin.
Together with the other suggestion by Eric Anderson (of signing another JAR) one can then sign this assembled JAR (instead of the original JAR). Note that the order of the plugin definitions matters here.
It is assumed that sign.keystore.file etc are set elsewhere (e.g. in a profile).
<build>
<plugins>
<!-- It seems that maven-assembly-plugin must be declared before the maven-jar-plugin,
so that it is executed first in the package phase,
and then the signing of the packaged jar can succeed. -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifestEntries>
<!-- ... -->
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>sign</goal>
</goals>
<configuration>
<jarPath>${project.build.directory}/${project.build.FinalName}-${project.packaging}-with-dependencies.${project.packaging}</jarPath>
<keystore>${sign.keystore.file}</keystore>
<type>${sign.keystore.type}</type>
<storepass>${sign.keystore.storepass}</storepass>
<alias>${sign.keystore.alias}</alias>
<verify>true</verify>
<verbose>false</verbose>
<removeExistingSignatures>true</removeExistingSignatures>
</configuration>
</execution>
</executions>
<configuration>
<archive>
<manifest>
<!-- <addClasspath>true</addClasspath> -->
</manifest>
<manifestEntries>
<!-- ... -->
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>

Running maven goal in multiple lifecycles

I have a case where I want to run the cobertura plugin in both the verify phase and the reporting phase. I have two profiles and they should both be running the cobertura plugin, but in profile A, I only want to create the xml/html output, but in profile B, I will be generating full site documentation that includes these results.
I have cobertura configured as a plugin that runs as part of the verify phase, but if I do that, even if I run mvn verify site, the cobertura report does not appear in the site documentation. It seems as though I need to have it listed in both the plugins and the reporting section (since I won't be running site in profile A, it won't get called in that profile if I only have it in the plugins). So far the plugins section of my POM includes:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin </artifactId>
<version>2.2</version>
<configuration>
<instrumentation>
<excludes>
<exclude>com/somepkg/**</exclude>
</excludes>
</instrumentation>
<formats>
<format>xml</format>
<format>html</format>
</formats>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>cobertura</goal>
</goals>
</execution>
</executions>
</plugin>
I don't want to copy this into the reporting section too since this is a lot to duplicate. Is there a good way to accomplish this otherwise?
Thanks,
Jeff
Define this:
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>cobertura</goal>
</goals>
</execution>
<execution>
<phase>pre-site</phase>
<goals>
<goal>cobertura</goal>
</goals>
</execution>
</executions>