Maven: different java compiler versions for project and plugin - java

We have a Java 7 project which uses a plugin built with Java 8. Is it possible to use the Java 8 plugin in Java 7 pom by setting the compiler version to use only for the plugin?

I just found that on the internet :
http://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html
You should just add a configuration section specifying the jdk wanted as
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.4</source>
<target>1.4</target>
</configuration>
</plugin>
I hope it helps !

It is only possible, if the plugin forks into a new JVM process and allows you to configure the JVM which it uses to execute. Otherwise it will use the JVM, which the whole build uses. You would then have to run the build with a JRE/JDK 8 (which shouldn't be a problem when you set the source and target version appropriately, like in the answer from Juan Wolf (just set 1.7 instead of 1.4)

Related

Compiling for execution under JRE 6 with Maven

I have a requirement to compile an app for use under JRE 6.
Initially the app was compiled for use under JRE 8.
So I added:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
to pom.xml
After fixing all the errors caused by unavaiable diamond operator, string in switch statement etc. I ran the app under JRE 6 and got the error.
Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: java/nio/file/Paths
Ok. This class is available only since Java 7. But the question arises: what does the above maven setting exactly? Does it only check the syntax? If so, then it's not quite useful.
What needs to be done to configure maven so that it notifies me about missing classes in particular Java version too?
java.nio.file.Paths was introduced in Java 7. You will have to change the implementation of your code to only use classes available in Java 6.
And yes, the Maven property only tells javac to use Java 6 syntax. That's very useful. Maven will not magically change your code for you. javac started by mvn package will tell you about unresolveable imports.
The purpose is to instruct maven to pass the -source and -target to the javac compile executable. It makes sure you only use language features that are available in the given JRE (the diamond operator is a great example), but it does not prevent use of APIs that only came along later.
Have a read of this
In particular the part at the bottom of the page:
Merely setting the target option does not guarantee that your code actually runs on a JRE with the specified version. The pitfall is unintended usage of APIs that only exist in later JREs...
I think it's unfair to say the parameters are not useful they certainly are. But they are not magical tools that know which libraries may be available at runtime.
The Animal Sniffer plugin for maven verifies that classes compiled with a newer JDK/API are compatible with an older JDK/API.
https://www.mojohaus.org/animal-sniffer/
Ok. I figured out how to do it.
You don't have to install the target JDK or JRE. All you need is rt.jar from that JRE.
Then you have to set the path to the jar file in bootclasspath in pom.xml.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<compilerArguments>
<verbose />
<bootclasspath>C:/data/tmp/rt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>

`class file has wrong version` after Jenkins upgrade

A couple of days ago, I upgraded Jenkins to version 1.643. Before, we were using Jenkins 1.593. Starting with Jenkins 1.612, Jenkins requires Java 7, see changelog, announcement and issue. Our Jenkins server has Java 8.
I have a Maven project consisting of submodules.
In the job configuration in Jenkins, I have configured the build to use JDK 1.6.
When looking at the build environment, it's indeed 1.6:
JAVA_HOME=/var/lib/jenkins/tools/hudson.model.JDK/1.6
One of the submodules fails to build on Jenkins, with this error:
[ERROR] /var/lib/jenkins/<REDACTED>.java:[15,-1] cannot access java.lang.Object
bad class file: java/lang/Object.class(java/lang:Object.class)
class file has wrong version 52.0, should be 50.0
According to what I can Google, class file version 52.0 is JDK 1.8 while the compiler is expecting version 50.0, which is JDK 1.6. I assuming that class file 52.0 refers to rt.jar (Java Runtime) which contains java.lang.Object (see also pom.xml snippet below).
I have found this SO question (and others that are duplicate of it), but they are all in the context of someone building from their IDE (IntelliJ) or from command prompt, and after reading them, I don't see how I could apply the suggested solutions. They involve setting $JAVA_HOME, which is already done by Jenkins.
My question is different because the issue is in the context of Jenkins (and Maven), and only occurred after the Jenkins upgrade. When I execute mvn clean install on my own desktop (with JDK 1.8), the error does not occur. If I execute the file command on the offending class file, but on the desktop where compilation succeeded, I get compiled Java class data, version 50.0 (Java 1.6). For me, this confirms that my pom.xml is (probably) still correct and it's (probably) a Jenkins configuration issue.
That specific submodule has this in the pom.xml, which may or may not be relevant:
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArguments>
<verbose />
<bootclasspath>${java.home}/lib/rt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
So, as you can see, it takes rt.jar from the current $JAVA_HOME so it can cross compile with a target of 1.6.
I'm a bit lost about the origin of this Java 8. Before the Jenkins upgrade, we were already using Java 8 on the server and cross compiling with a target of Java 6. What am I missing here?
EDIT
Do I even need this? If I comment out
<compilerArguments>
<verbose />
<bootclasspath>${java.home}/lib/rt.jar</bootclasspath>
</compilerArguments>
in pom.xml, I can still cross compile on my desktop and the class files are still version 50.0.
EDIT
When I take that part out, the build does not fail any more.
Which means I solved it myself.
I want to change the question to: why did it fail in the first place? And why didn't it fail before on Jenkins 1.593?
I changed my pom.xml to exactly how it is described in this SO answer: Maven release plugin: specify java compiler version
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
<bootclasspath>${java.home}/lib/rt.jar</bootclasspath>
</configuration>
</plugin>
</plugins>
</build>
As you can see:
I explicitly set the groupId
I explicitly set the version to the latest version, 3.3
The configuration parameters are formatted a bit differently.
My educated guess is that the Maven on the Jenkins server didn't pick up configuration inside compilerArguments and was only happy when it is directly inside configuration. I leave it to the comments to explain how and why, but for me the issue is solved.
I think you have a few errors in your assumptions.
compilerArguments is deprecated. It's been superseded by compilerArgs.
Like you can see from compiler plugin documentation, compilerArguments/compilerArgs is meant to be used only for arguments not supported by configuration section itself. As bootclasspath is supported, using it in compilerArgs/compilerArguments section is generally incorrect.
compilerArgs/compilerArguments is only used if fork is set to true, which was not correct for your configuration.
The third point was probably the most important reason why it didn't work for you. Using configuration section for your use case there should be no issues, and indeed based on your question/answer, this seems to be the case.
Also note that java.home is not JAVA_HOME. I've expanded on that on my other answer here. I'd guess that is related to why you see changes between Jenkins versions.

Using maven library in android project

I have a certain library project which does a certain work. The library is built using maven.
I now want to include this library in an android project. I added the library jar as a compile dependency in gradle and I can successfully use the library in my android code.
I have JDK 8 installed and I build the library using it. But, as I have read, android uses Java 7. Since the library is built using JDK 8, can this cause a problem?
If it can cause problems, I don't think building the library using JDK 7 would solve it either, since the library depends on other maven libraries from external maven repository. Is there anything I can do about it?
This is a common JDK lifecycle problem. The good news is there are things you can do to make sure that everything in your build is compliant with a certain JDK.
First off, make sure that your module is indeed compiled with the latest JDK version you are willing to accept. You can set the compiler plugin to only generate bytecode that is compliant to certain version, for instance JDK 7.
For example:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>THE_JDK_VERSION_YOU_WANT</source>
<target>THE_JDK_VERSION_YOU_WANT</target>
</configuration>
</plugin>
</plugins>
</build>
NOTE
There is a drawback to this and that is that while setting a specific source and target to the compiler, the code may still use JDK features that aren't available in the target environment's JRE. For example, having set JAVA_HOME to JDK8 and source and target to 1.7 will still allow the code to use (say) ConcurrentHashMap.mappingCount() which came in JDK8.
The answer to this problem is to use the animal-sniffer plugin. This plugin will check your code for any usage of disallowed API:s, such as JDK8, if you configure it that way. This plugin was previously hosted by Codehaus, but it will come up if Google around a bit. I can provide you with a working example tomorrow when I get back to work.
Next, as you pointed out, you have your dependencies. Fortunately, the enforcer plugin can use an extended rule-set, called <enforceBytecodeVersion> that can check that all of your dependencies are also compliant with a specific bytecode version. All the details are available here.
EDITED
Here comes the configuration for the animal-sniffer plugin. There's a newer version available (1.14), but it didn't work for me. Maybe you'll have better luck. The API signatures for the JDK are available at Maven Central.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.13</version>
<executions>
<execution>
<id>check-for-jdk6-compliance</id>
<phase>test</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java16</artifactId>
<version>1.1</version>
</signature>
</configuration>
</plugin>

IntelliJ won't accept Java8 and diamonds at the end of List/Map

I'm using the latest JDK and everywhere from project creation to now everything is set to Java8 or SDK 8.
Still, intelliJ gives me this issue:
The red lamp tells me to change to Java7.
This is my project settings:
and this is the Modules section:
As you can see; I specifically changed it from the SDK default to java 8 when I got the error, but no result.
The compiler settings look like this:
I'm on a macbook and the intelliJ is the community version. Does anyone know why this is happening and how I fix it?
Try to run the project, if this is your error message:
Then I suggest you have a little look into your pom-file.
This project was built using the intelliJ maven project setting, and it was missing this lovely line of code:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
IntelliJ doesn't (at least in my case) generate the version in the pom (despite that I picked all the settings for it).
I tried specifying the compiler version in the plugin too, to no avail. Have you tried invalidating the cache? It's File -> Invalidate Caches/Restart.... I would probably restart as well for good measure.

Enforcing java 6 source compatibility in maven and/or eclipse

The method java.lang.Long.compare(x, y) exists in Java 7 but not Java 6. So obviously this causes a NoSuchMethodException if code using this method is deployed to a server running Java 6.
However, either Maven nor Eclipse were picking up the error despite having set the source-compliance level to 1.6 in eclipse and the maven compiler source & target to 1.6.
Is there a way to enforce full Java 6 compliance in Eclipse, apart from downgrading my JRE to 6?
This is exactly why the animalsniffer-maven-plugin was introduced:
Animal Sniffer provides tools to assist verifying that classes compiled with a newer JDK/API are compatible with an older JDK/API.
This is what I put in my POM.
<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" >
...
<properties>
<java-version>1.6</java-version>
</properties>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
</plugins>
</build>
</project>
Once I have that defined in my POM I right click the project and go to Maven > Update project. This will apply any settings in the pom to the eclipse project which ensures the two are in sync.
In terms of JDK I use the latest JDK but compiled to the lower release. We're currently using JDK 7 but compile to 6. This gives you the best of both worlds in that the compiler will still make use of any optimizations that were introduced with 7 that are compatible with version 6.
There is a way to cross-compile Java source code, but the easiest way is to use the min. required JDK version when creating your artifacts (JARs etc.)/ during development.
-> use JDK 6 during development and releasing.
Note however that Oracle's JDK 6 reached End of Life and it is strongly recommended to upgrade to Java 7 if you don't have special support contracts with Oracle (or using a JDK from a different vendor).
Note: the source-level only makes sure you're using syntax constructs supported by that version and the target-level only makes sure the resulting class file is bytecode compliant to the specified version.

Categories

Resources