Problem running tests with enabled preview features in surefire and failsafe - java

I'm trying to migrate a project to Java 12, with --enable-preview.
I added --enable-preview in compiler settings:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>12</release>
<compilerArgs>
<arg>--enable-preview</arg>
</compilerArgs>
</configuration>
</plugin>
And also added it in argLine for surefire and failsafe:
<properties>
<argLine>--enable-preview</argLine>
</properties>
And do a mvn clean verify results in:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.0.0-M3:test (default-test) on project lombok-jdk10: Execution default-test of goal org.apache.maven.plugins:maven-surefire-plugin:3.0.0-M3:test failed: java.lang.UnsupportedClassVersionError: Preview features are not enabled for com/kirela/lombok/BarTest (class file version 56.65535). Try running with '--enable-preview' -> [Help 1]
I also tried adding argLine directly to surefire/failsafe configuration, but the result is same.
What am I missing here?
I this a bug in surefire/failsafe?
EDIT2: Surefire and failsafe config:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<forkCount>2</forkCount>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M3</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<forkCount>2</forkCount>
</configuration>
</plugin>
EDIT3:
Minimal working example is here: https://github.com/krzyk/lombok-jdk10-example
The project fails with --enable-preview, but works when I remove it.

This works for me:
mvn clean install works (with junit tests)
IDEA recognizes module language level correctly as 12 (Preview) - Switch expressions
junit tests in IDEA work
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>12</release>
<compilerArgs>
<arg>--enable-preview</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
Environment:
Ubuntu 18.04.3 x64
IDEA 2019.2.1
Maven 3.6.0
jdk:
IMPLEMENTOR="Oracle Corporation"
JAVA_VERSION="12"
JAVA_VERSION_DATE="2019-03-19"
ADDITION: Similarly this approach works for java 13.
ADDITION: Similarly this approach works for java 17.

There are two solutions:
Add --enable-preview to MAVEN_OPTS environment variable.
Explanation by the maintainer of surefire:
The argLine does what it has to do without any issue.
The plugin runs JUnit filter which finally selects relevant classes to run in one or multiple JVMs.
So the JUnit engine runs twice. Once in plugin JVM, and second in the forked JVM.
Due to the classes are compiled with different major or minor version (in bytecode of *.class files) than the version of Java runtime supports in Maven, this JRE fails because Java in Maven does not understand the bytecode. So, it is curious that the same JVM (javac) produced two major versions depending on JVM option and java from the same JVM does not understand it been incompatible for itself. Although version in forked JVM is totally fine and understands the the classes compiled by javac because javac and forked JVM start with the same option --enable-preview.
It is the same situation as if you compiled your sources with Java 12 by maven-compiler-plugin using the toolchain and run the whole Maven build with Java 11. So the classes would be compiled with higher version (in bytecode) than the JRE could understand in Maven process.
We have a wish to rework providers and perform the filtering inside of the forked JVM but this is very compilicated change and still questionary.
The issue is that I used forkCount, it appears surefire doesn't pass parameters to JVM run in fork.
Remove the forkCount parameter from surefire/failsafe configuration.
This will of course cause the tests to run in a single JVM, so if you wanted to speed up the tests using the forks, it won't work now.

Add a configuration for surefire and failsafe maven plugin
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>

Related

Maven incremental compilation for tests only

I know, that by using the useIncrementalCompilation parameter, it's possible to enable incremental compilation, for example:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
But in this case, both classes in src and tests are compiled incrementally.
I would like it to be for tests only. Is it possible?
My environment:
Java 1.8
Apache Maven 3.8.2
maven-compiler-plugin 3.1
I'm build project by this way: mvn clean install
Maybe there is some plugin that would allow me to compile tests (tests only) incrementally?

How do I compile multi module Maven project where modules have different Java versions? Possibly using Jenv? [duplicate]

My maven project has a few modules: server, web, etc.
I would like to build all but my server module on Java 6. For the server module, I'd like to compile it with Java 7.
Here's my pom.xml below, but I think that if I modify it to 1.7, then all of my modules will be compiled with Java 7. Also, does maven use the JAVA_HOME environment variable to determine which Java version to use?
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<memmax>2048m</memmax>
</configuration>
</plugin>
EDIT Also, does the below output of
maven --version
indicate that maven is compiling my java code with 1.7?
vagrant#dev:~/bin/apache-tomcat-7.0.29/bin$ mvn --version
Apache Maven 3.0.4 (r1232337; 2012-01-17 08:44:56+0000)
Maven home: /home/vagrant/bin/apache-maven-3.0.4
Java version: 1.7.0_07, vendor: Oracle Corporation
Java home: /home/vagrant/bin/jdk1.7.0_07/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.2.0-23-generic", arch: "amd64", family: "unix"
Thanks,
Kevin
There are a number of hacks out there for compiling source code with a different version of the JDK than you are using to run Maven, for example you can use something like
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<executable><!-- path-to-javac --></executable>
</configuration>
</plugin>
</plugins>
[...]
</build>
[...]
</project>
The issue with this approach is that you now have hard-coded the path to the JDK into your POM. Everything will work just fine on your machine but when you have to rebuild your machine because the HDD failed, or when you want to build on a different machine, you will be stuck as the path will most likely not match up.
The correct best practice way to handle this is via Toolchains. This will see you creating a ~/.m2/toolchains.xml file that describes where each of the different toolchains in your system are. Then the version of the JDK can be applied by the Maven Toolchains Plugin, e.g.
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-toolchains-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>toolchain</goal>
</goals>
</execution>
</executions>
<configuration>
<toolchains>
<jdk>
<version>1.6</version>
</jdk>
</toolchains>
</configuration>
</plugin>
...
</plugins>
The next thing is that you don't need this as often as you would think. For example by using the source and target values you can generate the correct bytecode for the JRE that you are targeting... the only issue that you will then hit is the use of methods that are new in JRE 1.7... which is where Mojo's Animal Sniffer Plugin comes in. Animal Sniffer can be used to ensure that you only use the methods of the JRE that you are targeting. The general community consensus is that the use of source and target configuration options in the Maven Compiler Plugin configuration coupled with the use of Mojo's Animal Sniffer virtually eliminates the need for toolchains on the Compiler end of things.... on the Surefire end of things there is still need for toolchains... and I have a few edge cases that I need to update the compiler plugin and the toolchains plugins for to handle but, realistically you will not hit those edge cases ;-)
Just to be sure that your original question is completely answered (since the above answers the question you wanted to ask - as opposed to the one you asked)
At present you are compiling with JDK 1.7 however depending on the version of the Maven Compiler Plugin you are using, you may be compiling with either <source>1.4</source><target>1.4</target> or <source>1.5</source><target>1.5</target> unless you have changed the configuration of the Maven Compiler Plugin in your pom.xml. That will dictate which language features are available to you, but not which classes... so you would be generating code that will work on JRE 1.7 and provided you have not used any new classes/methods introduced since 1.4/1.5 (Such as String.isEmpty()) should also work on JRE 1.4/1.5... the only way to be sure if it works on such an old JVM is to either: run it on the old JVM OR use Animal Sniffer.
Maven Toolchains
To use multiple Java versions, you need to use Maven Toolchains, which require you to create a toolchains.xml file in your ~/.m2 Maven folder, containing all Java versions installed on your machine:
<toolchains>
<toolchain>
<type>jdk</type>
<provides>
<id>Java13</id>
<version>13</version>
</provides>
<configuration>
<jdkHome>${env.JAVA_HOME_13}</jdkHome>
</configuration>
</toolchain>
<toolchain>
<type>jdk</type>
<provides>
<id>Java9</id>
<version>9</version>
</provides>
<configuration>
<jdkHome>${env.JAVA_HOME_9}</jdkHome>
</configuration>
</toolchain>
<toolchain>
<type>jdk</type>
<provides>
<id>Java8</id>
<version>8</version>
</provides>
<configuration>
<jdkHome>${env.JAVA_HOME_8}</jdkHome>
</configuration>
</toolchain>
<toolchain>
<type>jdk</type>
<provides>
<id>Java7</id>
<version>7</version>
</provides>
<configuration>
<jdkHome>${env.JAVA_HOME_7}</jdkHome>
</configuration>
</toolchain>
<toolchain>
<type>jdk</type>
<provides>
<id>Java6</id>
<version>6</version>
</provides>
<configuration>
<jdkHome>${env.JAVA_HOME_6}</jdkHome>
</configuration>
</toolchain>
</toolchains>
The JAVA_HOME_13, JAVA_HOME_9, JAVA_HOME_8, JAVA_HOME_7, JAVA_HOME_6 environment variables are configured so that they reference the path where the associated Java version is installed.
The FlexyPool parent pom.xml configuration file
The parent pom.xml Maven configuration file of the FlexyPool project defines the global Java version settings
<properties>
<jdk.version>8</jdk.version>
...
</properties>
Now, we need to instruct both the compiler and the test plugins to use the configured java version.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-toolchains-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>toolchain</goal>
</goals>
</execution>
</executions>
<configuration>
<toolchains>
<jdk>
<version>${jdk.version}</version>
</jdk>
</toolchains>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
</plugins>
</build>
The FlexyPool child Maven module pom.xml using a different Java version
The flexy-pool-core-java9 child Maven module that requires a different Java version only needs to override the default jdk.version Maven property:
<properties>
<jdk.version>9</jdk.version>
</properties>
And that's it, we can now build each module using its own minimum viable Java version.
use the setup for the JDK6 on your top pom, it will be inherited by all the module, and overwrite it for your server pom with the different configuration required.
As for the path of the JDK, you can specify it, see here: http://maven.apache.org/plugins/maven-compiler-plugin/examples/compile-using-different-jdk.html

Travis Java build using different SDK for compilation and tests

I have a library that uses Java 8 classes if they are available and for older JRE versions provides a fallback implementation. It means I have to compile using Java 8 (or higher) but I want to execute the tests with JDK 7 to test the fallback. I can not figure out how to do it in Travis.
The easiest way is to create special Maven profile for Travis in pom.xml
<profile>
<id>travis</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<verbose>true</verbose>
<fork>true</fork>
<executable>/usr/lib/jvm/java-8-oracle/bin/javac</executable>
</configuration>
</plugin>
</plugins>
</build>
</profile>
And then activate it in .travis.yml
script: mvn install -P travis

Maven: excluding files from compilation doesn't work in IntelliJ IDEA

I want to temporarily exclude some directory from compilation, so I configure maven-compiler-plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/foo/**/*.java</exclude>
</excludes>
</configuration>
</plugin>
Everything works fine from the command line and Eclipse, but in IntelliJ IDEA I got compilation errors in excluded directory. Any idea what might be causing the problem?
It's an open issue, please follow for updates.
I had the same issue migrating maven projects.
I got it working by choosing below option:
File > Settings > Build, Execution, Deployment > Build Tools > Maven >
Runner
Check the 'Delegate IDE build/run actions to Maven' option.
Instruction is available here:
IntelliJ documentation
I found this question while trying to solve Java 9 module-info compilation in IntelliJ IDEA.
I want my library compiled with Java 8, but at the same time I want it to have module-info (which can be compiled only with JDK 9+). A solution of this problem suggested by Maven: exclude module-info from compilation with JDK 8 and include if JDK 9 is used. It works with command line but IDEA fails to compile such project. It is possible to adjust IDEA Compile configuration by adding module info to Excludes, but I wanted more clear solution.
It is possible to trick IDEA by adding Maven Profile with extended list of source directories:
<profile>
<!-- This profile enables two pass compilation:
1. Compile all sources with Java 9 target version (including module-info.java)
2. Recompile all sources with Java 8 target version, but module-info.java
After this all classes will have Java 8 bytecode (version 52), while
module-info.class will have Java 9 bytecode (version 53) -->
<id>J9-module</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>8</release>
</configuration>
<executions>
<execution>
<id>default-compile</id>
<goals>
<goal>compile</goal>
</goals>
<phase>compile</phase>
<configuration>
<compileSourceRoots>
<sourceRoot>${project.basedir}/src/main/java</sourceRoot>
<sourceRoot>${project.basedir}/src/main/java9</sourceRoot>
</compileSourceRoots>
<release>9</release>
</configuration>
</execution>
<execution>
<id>java-8-recompile</id>
<goals>
<goal>compile</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
With the profile shown above IDEA itself will compile only standard src/main/java.

IntelliJ Maven integration

I imported a Maven project into IntelliJ, but it seems like it's ignoring the <configuration> specified for the surefire plugin
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<argLine>-Djava.endorsed.dirs=${settings.localRepository}/com/sun/metro/webservices-api/2.0.1
</argLine>
<excludes>
<exclude>**/CacheStoreTest.java</exclude>
</excludes>
</configuration>
</plugin>
When I run the tests from within the IDE, it picks up the webservices-api.jar bundled with JDK6 instead of the version from my Maven repository and it also runs the tests in CacheStoreTest even though I've indicated they should be excluded.
It's a known issue, please watch/vote.

Categories