Minimum JDK for Groovy - java

I'm looking to write some Groovy code to perform tasks inside of install anywhere but because of the platforms we support I'm restricted to java 1.5. Any idea if this will be sufficient to run the latest groovy?

Java 1.5 should work. The Groovy docs state that you need at least 1.4. I use Groovy with Java 1.5, and everything works fine as far as I can tell.

As of early 2015, the build environment for groovy 2.4.0 (in the build.gradle file) indicates:
sourceCompatibility = 1.6
targetCompatibility = 1.6
So, for modern groovy, you want 1.6 or better. The documentation for 2.4 indicates JDK 1.5 support, which disagrees with the build environment, so be warned if you're stuck on 1.5.
In addition, there's support for use of the invokeDynamic instruction in later releases of JDK 1.7 (it was present but known to be buggy in releases older than JDK 7u60) which may offer some performance improvements.

Related

Which version of the Java JDK should I be using with MALLET?

I regularly use MALLET for topic modeling in the classes that I teach. Running MALLET requires users to have the Java Development Kit installed. I currently have JDK 8 update 241 installed on my main computer, and I know that MALLET works properly in this environment. That said, JDK is now up to v14.
Which version(s) of JDK does MALLET support?
I'm not altogether sure that you do need the JDK. They never say that on the website. The tarfile that I downloaded already includes compiled classes - you aren't expected to build it from source - so the JRE should be enough.
Strangely enough, the compiled classes in the class directory are targeted at 1.7 (bytecode version 51) whereas the pom indicates that it's supposed to target Java 1.6. So it's quite probable that by rebuilding it you could support an older version of Java.
In any case, the JDK is backwards compatible by design. Any version from 7 onwards will be able to run it (6+ if you were to rebuild it).
Running it on a newer version will benefit from the new features of the JDK, such as improvements to the garbage collector, so you may see some performance improvement there. If you are not concerned about that then it doesn't matter.

Verify backward-compatibility on API level of a library built with a newer JDK

Recently, a teammate used the following function in our Java 8 code: Matcher.replaceAll​(Function replacer).
The function was introduced in Java 9, but because he is using a newer compiler, the API function was simply found in the JDK's rt.jar and nobody noticed this won't work under real Java 8 environments.
The compatibility settings are correctly set, and the gradle subproject has the following settings:
sourceCompatibility = 1.8
targetCompatibility = 1.8
I had very similar issues at the time when I first used the Java 6 function String.isEmpty in Java 5 code - the code made it into the release and crashed there.
What can I do to enforce the usage of the correct API. As it is a shared library, do I have to use (and install, maintain..) a different JDK for this gradle subproject, or is there some kind of compatibility scanner which runs through a built jar and checks all rt references?
As you've noticed, the two compatibility configurations does not consider the APIs of older versions - only the syntax, semantics and the resulting byte code.
There are two options you can take. One is to have JDK 8 installed on your computer, and the configure Gradle to use it when compiling your project. It looks like this:
tasks.withType(JavaCompile) {
options.fork = true
options.forkOptions.executable = "$java8Home/bin/javac"
options.bootstrapClasspath = files("$java8Home/jre/lib/rt.jar")
}
The disadvantage here is that you will need to have JDK 8 installed in the first place, and as it will probably be installed in different locations, you will need probably want to configure it with an environment variable or property (I've called it java8Home here).
However, since Java 9, the JDK now knows about the documented APIs of previous versions, and you can select which one to use with a new --release flag. This is not going to work if you use undocumented APIs, but it means you can compile your project with any versions of Java and still make the resulting classes compatible with Java 8. You can do it like this:
tasks.withType(JavaCompile) {
if (JavaVersion.current() > JavaVersion.VERSION_1_8) {
options.compilerArgs.addAll(['--release', '8'])
}
}
Note that the 'if' statement is only there in case you still need to support running Gradle with Java 8 (through your JAVA_HOME variable). If you are only using later versions, it can be removed so you always set the 'compilerArgs'.
For some versions of Java, it is possible build Java code on a newer JDK to run on an older JDK / JRE. You have already discovered the --source and --target options for javac and the corresponding Gradle settings. The other thing you can do is to use --bootclasspath to tell javac to compile against the runtime libraries for an older version of Java.
Since you are using Gradle, check out "gradle-java-cross-compile-plugin" (https://github.com/nebula-plugins/gradle-java-cross-compile-plugin). I can't find any documentation for it, but it apparently deals with --target and --bootclasspath.
Having said that, I don't think cross-compiling Java is a good solution.
I would actually recommend that you set up a Continuous Integration (CI) server (e.g. Jenkins) with JDK installations for all of the Java versions you are interested in supporting. Then set up jobs to build your code and run your unit tests for each Java versions.
Note that simply compiling your code against the older Java libraries is not sufficient to verify backwards compatibility. Sometimes the behavior of libraries changes. You need to run your tests, and your tests need to cover the cases where compatibility issues may exist.

How to validate dependencies' java version when compiling using higher version JDK?

We're using java 8 for most modules/projects, but for some of the modules, we use java 6 (customer requirements).
The developers have java 8 installed and we compile the java 6 projects using these flags:
compileJava {
sourceCompatibility = 1.6
targetCompatibility = 1.6
}
We thought we're all good until we upgraded guava from v20 to latest - 28.1-jre.
To our surprise, the build was successful but failed at runtime.
We have a workaround for building for java 6 using a specific javac found in JDK 6. See more info here. This workaround wields the error class file has wrong version 52.0, should be 50.0 in compile time. The downside is that it requires a download+config+usage of JDK 6 for developers.
Is there a way to validate the dependencies' java version at compile time when using a higher java version? (without installing lower version java) Thanks.
Setting -source and -target values to 1.6 is insufficient to ensure that the resulting output is compatible with 1.6. The program itself must not have any library API dependencies on later versions, and the -source and -target options don't do that. (GhostCat said pretty much the same thing.)
For example, in Java 8, ConcurrentHashMap added a covariant override for the keySet method that returns a new type ConcurrentHashMap.KeySetView. This type didn't exist in earlier versions of Java. However, in the class binary, the return type is encoded at the call site. Thus, even if the source code is compiled with -source 1.6 -target 1.6, the resulting class file contains a dependency on the Java 8 class library API.
The only solution to this is to ensure that only Java 1.6 compatible libraries are in the classpath at compile time. This can be done using the -Xbootclasspath option to point to a JDK 1.6 class library, or it might be simpler just to use a JDK 1.6 installation in the first place.
This applies to external libraries in addition to the JDK, as you've discovered with Guava. The Animal Sniffer project provides plugins for Ant and Maven that checks library dependencies for version problems. Offhand I don't know if there is something similar for Gradle. There might be a way to get Animal Sniffer to work with Gradle, but I have no experience with doing that.
Is there a way to validate the dependencies' java version at compile time when using a higher java version? (without installing lower version java).
You specify your dependencies. When you tell your built system to explicitly use some library X in version Y, then you made a very clear statement.
And you see, it is not only about the class file version number. What if some person doesn't pay attention, and compiles something with Java8 ... with Java6 target, but forgets that the code bases uses Java8-only API calls?!
In other words: you are looking in the wrong place.
The person who makes updates to the build description, and changes a library version from Y to Y+8, that person needs to carefully assess that change. For example by reading release letters.
I agree that a really clever build system could check if libraries you are using come in with a matching class file version. But as said, that is only one aspect of the problem. So instead of looking into a technical solution, I think the real answer is: don't step version numbers because you can, but because you have to. And that manual step of changing that version number, that is something that requires due diligence (on the side of the human doing it).
Thus: I think the most sane approach here is to compile the Java6 deliverables within their own specific build setup. Which you only touch after careful inspection of such details. And sure: convince your customer to move on, and give up a long dead version of Java.

sourcecompatiblity targetcompatibility usage reasons

I'm currently using gradle and it seems like I can set sourcecompatibility and targetcompatibility through java-plugin
I wonder what's the reasons for us to use sourcecompatibility/targetcompatibility other than to be backward compatible with older JDK?
would it be easier to upgrade to latest java if sourcecompatibility/targetcompatibility isn't set?
Should sourcecompatibility and targetcompatibility be used at all?
The sourceCompatibility and targetCompatability variables are directly related to the -source and -target switches on javac. These are used for cross-compiling (relevant documentation for javac, relevant SO question). The sole purpose of these values is to make your class files backwards compatible. They don't really serve any other purpose. This is a feature that's added more or less for security purposes. Instead of installing an older, less secure JDK on your machine, you can install a newer one and use these values to compile your code.
These flags have one major caveat though: just because you set these values doesn't mean the code you write will work on older JVMs. This is because while javac will compile your classes to make them compatible with an older JVM, you can still end up binding to a method that doesn't exist in that older JVM.
For example, if you set your compatibility values to '1.6' and you're running your builds using JDK 8, you can still write code like this:
Objects.equals(obj1, obj2);
And the compiler will not complain. It will still build without complaining about that line. However, if you tried to run it with Java 6, you would get an error stating that Objects.equals doesn't exist because Objects was introduced in Java 7.
Put differently: -source and -target make your .class files compatible with older JVMs, but they do not validate classes and methods used by your code. The only way to do that is with -bootclasspath, at which point, you may as well just download and compile with the older JDK since you have to do that to get the rt.jar file needed for that (relevant SO question).
As far as upgrading is concerned, setting these values don't necessarily limit your ability to upgrade. Code you write will have to be compatible with whatever value you use if you plan on using it in that version, but nothing stops you from changing them and starting to use the newer features and APIs.

Will programs written against "Java 8" JDK be compatible with "Java 7" JREs?

My question is if Java JDK and JREs have to be compatible to run?
I mean: will Java applications written using JDK version 8 in future work with current JRE's?
It is possible to use cross-compilation options when compiling. Do that and it will be possible to compile code with SDK 8 that is compatible with Java 1.1. It won't be very advanced code for 1.1, but it will run.
The short answer is No.
If you develop your application in JDK 8 and run it with JRE 7, you would get an UnsupportedClassVersionError.
This question is two part:
JDK vs JRE
forward / backward compatibility.
JRE is the acronym for Java Runtime Environment. JDK is the acronym for Java Development Kit: a set of tools which you use to develop Java programs. The JDK also contains a full JRE. In general there is no compatibility issue between the two. But you might want to take care not to use libraries which are only available in the JDK (for example code generation or the tools.jar)
Java itself is compiling to bytecode, which is forward compatible. That means you can use bytecode of any Java version and run it with any newer version. The other way around generally doesn't work and is checked by using the class file version ("java.lang.UnsupportedClassVersionError: Test : Unsupported major.minor version 51.0").
Then there are Java libraries, including the core libraries. So far there was never anything removed from them, so they are forward compatible. This is probably going to change with Java 9 where a very small usually unused library functions are removed.
Regarding to backwards compatibility, this is possible by setting the Java compiler to produce Bytecode of an older version. Up until Java 8, the compiler was always able to produce bytecode of the last two major versions as well. However, you might successfully compile a Java 8 source to Java 6, but not be able to run it. That is the case when you use libraries that are only available on a never Java. For such cases there is for example the maven animalsniffer plugin which will verify that when you compile against an older version, you actually only use libraries existing in said version.

Categories

Resources