Dependencies of a JAR - java

I created a Jar with Java 6. Now I'm creating a release document and recording the dependencies. How can I find the earliest version of Java that can successfully run the Jar, and the earliest version of Java that can successfully compile the source into a Jar?

I only know a manuel solution: try it out. There are, however, two things to consider.
For which version is the code language compatible?
For which JRE will it execute?
The first you can do with your current JDK, just iterate over the -source and -target arguments which you pass to your javac compiler. This will, however, not prevent you from using classes and methods from the JDK you are using. If you do, the code will not execute for a lower JRE, if you are using classes or methods that where not present back then.
The savest way would be to install all different JDKs along and try to compile the code with each of their compilers.

If you created the jar with java 6 and did not specify a different version of output bytecode, the generated class files will require Java 6 or greater. You can experiment to see what versions of bytecode you can generate with your source with the -target command line option if you're compiling manually, if you're using eclipse or some other IDE, most have settings that control the generated bytecode version in project options or somewhere similar.
A related post about determining the bytecode versions of class files: What version of javac built my jar?

Related

Does Gradle require JDK to compile Java?

Does Gradle requires JDK to compile Java source files?
or it is using its own internal compiler?
Thanks in advance
Short version: JRE for installing and running, Groovy build script compiled by Gradle own compiler, JDK for Java source code compilation.
Long version: There are multiple points of interest:
1) Installing & running
In order to install Gradle and run Gradle build you need a JDK or JRE which version is at least 7.
Gradle uses mainly Groovy as a language but it comes with its own library and ignores any other Groovy library that is installed. Therefore the compiler is the Groovy compiler of the Groovy library that Gradle comes with.
As a side note, you can also use Kotlin for scripting builds as described here. Which of course involves a Kotlin compiler.
You can check the JVM version by running gradle -v.
This means that during installation the runtime configured in JAVA_HOME will be used.
Also the same happens for running a build, unless you are explicitly overriding JAVA_HOME in gradle.properties using the following property org.gradle.java.home. This property and others are defined here.
JAVA_HOME can point to a JDK or JRE. In this case does suffice to have only a JRE installation.
2) Source Code Compilation
To remove any confusion, source code, means that you have your project sources, written in a language, which need to be compiled into bytecode. At previous point the code being written is the actual build script code which is a separate thing, compiled and handled by Gradle.
Source code is being compiled using Gradle plugins, being it java, groovy, kotlin, or anything else.
You can compile your JAVA source code by using the java plugin or the newer java-library plugin. The two are similar.
The JDK being used will be the one configured in JAVA_HOME or can be explicitly defined in gradle.properties using the following property org.gradle.java.home.
JAVA_HOME needs to point to the JDK not a JRE for this case.
As stated above, Gradle can only run on Java 7 or higher. But it can be configured to compile, run, test, javadoc for Java 6 by following these steps. Therefore this advanced customization can be used to handle some corner cases which may not have a resolution in simply changing gradle.properties.
The two plugins provide also two properties:
sourceCompatibility - Java version compatibility to use when compiling Java source
targetCompatibility - Java version to generate classes for.
These are related to the source language version used as input and the target bytecode version to be generated.

Java Virtual Machine Error

I have been working on an application which was originally built with JDK 1.5.0. While playing around with the application I have somehow regenerated the classes to version 1.6.0.
Due to classes being updated the java compiler version 1.5 cannot run these classes anymore. Is there any way i can rebuilt the classes to use JDK 1.5.0. Moreover, when i try to use jdk 1.5 to compile my code, i get a JVM error saying "Could not find the main Class. Program will exit"
Please help.
Is there any way I can rebuild the classes to use JDK 1.5.0.
You can use the -source and -target options to instruct javac to target an earlier JVM.
If you are using Eclipse's compiler, instead of the JDK's javac, then you set the compiler settings in the project properties.
http://help.eclipse.org/mars/topic/org.eclipse.jdt.doc.user/gettingStarted/qs-with-j2se50.htm?cp=1_1_2
http://help.eclipse.org/mars/topic/org.eclipse.jdt.doc.user/reference/preferences/java/ref-preferences-compiler.htm?cp=1_4_2_0_3
"Could not find the main Class. Program will exit"
This means the CLASSPATH (and/or bootclasspath) does not contain the class specified as the 'main' class to run.
How are you running javac? Are you maybe trying to compile using a JRE 5? That won't work as the JRE does not contain the compiler; you need a JDK.
Note that both 1.5 and 1.6 have been End-of-Life for a few years now (since 2009 and 2013 respectively). I recommend using a currently-supported JVM.

Does the JDK version being used to compile the code matter?

In a conversation with someone else, they claimed that the version of JDK being used to compile the code does not matter vs. the JVM version being used to run the compiled code.
That seems strange to me - are there never instances where the JDK version matters? Is functionality always only dependent on the JVM the code is running on, and not on how the code is being compiled?
https://stackoverflow.com/a/19554706/2687324 talks about backwards compatibility. Is Java code also forwards compatible?
No, you can't necessarily run code compiled with a new JDK on an old JRE. Compiled classes contain a version number for the class file format; if this is newer than the runtime expects, it will refuse to load the class.
Most Java compilers support an option to target an older JRE, generating an older class file format than the compiler was built for. However, you can still run into trouble if you don't also compile against an older version of the Java runtime library. Your code might use new API that isn't in the older version of Java. The compiler, with its current version of the API, won't catch this even when you specify an older target.
For the standard javac compiler in OpenJDK, these options are -target and -bootclasspath. You might also want to set the -source option to catch usage of newer language features that require support the older class files don't provide.

JDK compliance - a false safety?

I have Java 7 installed but set my projects JDK compliance to Java 6. Unfortunately it turned out that this is not the same as compiling with Java 6.
For example, the interface javax.imageio.stream.ImageInputStream
extends Closable in Java 7 which is not the case in Java 6. Using an ImageInputStream where a Closable is expected compiles fine under my settings (Java 7 complying to Java 6) but gives an compile error when using Java 6.
Is this supposed to be this way?
In order to compile Java code for an older JRE, you need to do two things:
Set the compliance level appropriately. This, as explained by dystroy, makes sure the compiler produces bytecode that the old JVM can understand.
Use the old Java system libraries. This makes sure you compile against the version of the Java system libraries (java.lang.* , java.net.* etc.) that shipped with the old JRE.
You have covered the first point, but not the second, hence your problem.
How to do this depends on your build environment.
In Eclipse, the Java system library to use is set as part of the build path: Go to Project properties / Java Build Path / Libraries, then remove the wrong "JRE System Library" and add the right one using "Add Library...".
When compiling with plain javac, you use option -bootclasspath. Example: javac -target 1.5 -bootclasspath jdk1.5.0/lib/rt.jar OldCode.java (from the javac manpage, section "Cross-Compilation Options").
What you have set with the JDK compliance is mostly the class format.
There were several evolutions in the bytecode format of classes. A JDK can compile in a previous format while a VM can't execute a class compiled on a more recent format.
But the used libraries always are the one available in the JDK you use for execution. The JDK 6 version of javax.imageio.stream.ImageInputStream isn't bundled with Java 7.
Use the bootclasspath option when compiling. I thought the JDK 7 warned of not doing so, if doing cross-compilation.

Will compiling for Java 1.5 on Java 1.7 still work?

I've recently moved to Java 7 in one of my projects. I claim that it can run on Java 1.5 simply because there's nothing I depend on that is in Java 6 or 7. However when compiling today I noticed this:
bootstrap class path not set in conjunction with -source 1.5
Google has found little information on this warning. Does this mean that you can't compile to Java 1.5 from Java 1.7?
This Oracle blog explains the warning:
http://blogs.oracle.com/darcy/entry/bootclasspath_older_source
The reason is, that if you fail to set rt.jar for the older platform, then:
If the second step is not taken, javac will dutifully use the old
language rules combined with new libraries, which can result in class
files that do not work on the older platform since references to
non-existent methods can get included.
Does this mean that you can't compile to Java 1.5 from Java 1.7?
No it doesn't. It means that there is a right way and a wrong way to do this ... and you are doing it the wrong way.
The right way to compile for the Java 1.5 on a Java 1.7 JDK is:
Get hold of a copy of the "rt.jar" from Java 1.5 and put it on the compilation bootclasspath.
Compile with -source 1.5 and -target 1.5.
The warning message is telling you that you haven't done the first of these.
The way that you are building right now is implicitly using the 1.7 version of "rt.jar" for the Java runtime APIs. This may work! (Indeed, it should work assuming that you've made no changes to the code since it last built on 1.5.) However, there is a risk that you may accidentally introduce dependencies on classes or methods added in Java 1.6 or 1.7. That would result in runtime errors when you try to run your application on Java 1.5.
You better be setting -source and -target 1.5.
To be really sure that you aren't accidentally incorporating dependencies on newer classes, methods, or fields, use the maven-animal-sniffer plugin or something like it.
--source 1.5 will make sure the source files comply with Java 5 conventions. --target 1.5 will make sure the generated class files comply with Java 5 conventions. Neither of these will protect you from using Java 6 or 7 library methods. You must either compile against the appropriate rt.jar using --bootclasspath, or use something like the animal-sniffer-plugin (if you are using maven) which will inspect everything's type signature, and compare with published profiles.
With the animal-sniffer-plugin, you may be in for a treat, because you can bump into 3rd party libraries that use Java 6 APIs, which may cause your build process to fail given you are pursing Java 5.

Categories