About VM/Jar versions - java

A jar is being ran with JVM 8 , which is compiled with java 6.
Can this jar load another one as a 'library'/plugin that is compiled with java 8 which has
dependency the first jar?

Sure, it can. You can mix jars compiled against different Java versions.
Also note that there's couple of things when talking about Java version used. One is the Java Class Library you compile your code against. This defines APIs you can use in your code.
The second thing is java bytecode version. You can instruct Java 8 to compile code to be Java 6 binary compatible. This doesn't mean that your code will execute against Java 6 though. If you use calls/classes added in Java 7 or 8 then you will hit problems at runtime.
These two are the most important aspects when talking about Java compatibility. Google for Java compatibility or Java source vs. binary compatibility to get more info on the subject.

Related

How to deal with multiple jre and a single jar file

I am currently working on a java program, but I feel a little bit lost about how to run the jar file on multiple OS which don't have the same jre version.
Let me explain :
The program I am developing is written in 1.7
The client need it to work on Mac OS (jre 1.6) and Windows (jre 1.7)
I generate a .jar executable file using Maven in Eclipse
How can I make it run on his MAC OS and Windows at the same time ?
Is there a way to export the corresponding jre with the jar file ?
Thanks !
You are a number of distinct questions here.
The program I am developing is written in 1.7. The client need it to work on Mac OS (JRE 1.6) and Windows (JRE 1.7)
As a general rule, if a Java application can be compiled for an older platform (e.g. Java 6), then the compiled code should run on a newer platform. But the converse does not apply. There are two main reasons for this.
The Java .class file format is versioned, and a newer JRE can load classes with an older format. But an older JRE cannot load class with a newer format.
Moreover, the changes to the class file format are made to support new language features. So if your source code uses features added in (say) Java 7, then you won't be able to compile the code to run on (say) Java 6.
Each version of Java adds new classes and interfaces to the class library, and adds new methods to existing classes. If you compile code against the Java 6 SE APIs, then it should be compatible with a Java SE 7 platform. (All the required classes should be present.) However, if you compile the code against Java 7 SE APIs, the code may have dependencies on classes and methods that don't exist in Java 6.
In short, if you want your application to run on Java 6, you need to compile it and test it on Java 6. You also need versions of all of the application's dependencies that are compatible with Java 6.
The other aspect of the above question is supporting different operating systems (Mac OS and Windows). If your application is Pure Java and doesn't have threading problems, it should be OK. Problems arise if:
your application depends on platform specific things; e.g. pathname syntax, external commands, native libraries, or
your application includes native code, or uses Unsafe or abstraction breaking reflection, or it depends on JRE internal APIs, or
your application makes unwarranted assumptions about the thread scheduler, or doesn't strictly follow the rules governing memory visibility (defined in the Java Memory Model), or
your application uses 3rd-party libraries with any of the above issues.
I generate a .jar executable file using Maven in Eclipse
The way that you generate the JAR file doesn't matter. What matters is what Java target the code was compiled for and what Java APIs it was compiled against.
Is there a way to export the corresponding JRE with the JAR file ?
Not with Java 6 or Java 7. The JRE is not part of the JAR file. For this vintage of Java, the options are to get the client to install a JRE as separate step, OR create an installer that will install both your application and the JRE that it requires.
Java 9 introduced a new utility called jlink that will generate a self-contained Java installable from JAR files. These don't require a separate JRE because they have a cut-down JRE as part of the installable.
Finally, a word of advice. Consider carefully whether supporting a Java 6 customer is really in your long term interest
Both Java 6 and Java 7 are end of life. Furthermore, many providers of 3rd party libraries have stopped supporting versions prior to Java 8. This can present serious problems (even show stoppers) if you undertake to support a client who needs Java 6.
In addition, supporting this client will mean that your application codebase will be unable to make use of the new language features and improved APIs in Java 8 and later. Some of the more important language and API changes are:
try-with-resources (Java 7)
Lambdas, method references, type inference, streams and improved date/time handling. (Java 8)
Modules (Java 9)
var (Java 10)
Records (Java 15)
This is liable to lead to your codebase becoming increasingly old fashioned, and in need of a rewrite.
Check the maven-compiler-plugin: https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html but keep in mind that if you're using Java 7 features (e.g try-with-resources), the code will fail when your client(1.6) runs it. So I would suggest to use both source and target as 1.6.

Compatibility and migration from Java 9 to Java 8

I'm reading the document about the new Java 9 module system.
The paragraph 3 Compatibility & migration explains how to migrate code
from Java 8 to Java 9, but says nothing on how "migrate" or run an application
written in Java 9 to pre Java 9 runtimes.
So, if I have, say a JAR application written in a modularity way (every module has a module descriptor) what does happen if I deploy it on, i.e, a JDK 8 runtime?
If your class files are compiled with --release 8 flag, then they should run fine on Java 8. module-info.class files will be ignored by the older JVMs.
If your Java 8 project is maven-based, you can easily configure it to include module-info.java: https://maven.apache.org/plugins/maven-compiler-plugin/examples/module-info.html
Also, take a look at JEP 238 (Multi-Release JAR Files). This allows you to take even more advantages of Java 9 features without breaking compatibility with Java 8.
You cannot start a Java application with a JRE which is less than the one it is built for.
If you just use javac without any special options it will produces classes which do run on JREs equal or bigger than the one of the used JDK.
However javac supports cross compilation. You can use JDK 8 to compile JDK 6 compatible class files. Search for Cross-Compilation Options in the javac docs.
Java Class files contain version information. As with any Java version it should not be possible to execute a class file (or jar) that was compiled with a newer major version than your runtime. See: https://stackoverflow.com/a/4692743/6239524

Jar compatiblity with Java 8

I'm doing some upgrade work now. From java 7 to java 8, and also upgrade some jar files. How do I know if the jar file is compatible with java 8? Is there any website telling that?
Classes that were compiled with Java 7 almost always run on Java 8. This is backward compatibility requirement. However, there are some incompatibilities that are documented here
If there is some issue with a particular library, you should look for this information on the library's website.

Does compiling Java with different versions of JDK and same target and source version guaranties same execution?

We are going to update our CI system that creates builds from Java 7 to Java 8. Later we would like to migrate projects to Java 8 one by one. Of course, we would like to be able to create bugfix releases for older versions that still use Java 7.
Can we be sure that no issues will appear if we transfer building same source, target version and source version from JDK 7 to JDK 8? We did testing on development machines without any issues.
Before that, we are also gradually updating deployment servers from JRE 7 to JRE 8.
Note that we would like to keep single Java JDK installation on CI system, it gets complicated otherwise.
To make it more clear:
I am more interested in same execution outcome, not so much about the same bytecode. Same bytecode would just confirm the first (running on same JRE), if I understand that correctly.
We will not use Java 8 features in code until we have all deployments running on Java 8. So compatibility is not an issue.
Does compiling Java with different versions of JDK and same target and source version produce same bytecode?
There are no guarantees.
Indeed, two .class files created from the same source code using the same compiler with the same options on the same hardware will have the same bytecode, but the files may still be different because of embedded compilation timestamp attributes.
However, if you wanted to compare notionally equivalent bytecode files, this Q&A offers some possible leads:
Is there a tool that we could use to compare two Jars at Binary/Byte code level?
I expect that you will find that there are some differences between the bytecodes generated by Java 7 and Java 8 compilers, but that this won't matter.
As specified in this blog#https://blogs.oracle.com/darcy/entry/source_target_class_file_version.
Given the same source code, compilers from different releases configured to use the same source and same target (and same bootclasspath!), can still generate different class files because of bug fixes or changes to compiler-internal contracts.
But this problem will not happen to you since you have the same compiler that compiles to different target versions. It just have to emit the specific magic number and byte code compatible for that version. It looks like keeping your version in 1.8 and specifying the target that is either a lower or equal version would work. Looking at this past defect that was addressed, it appears it should work and Oracle works to fix such incompatibilities.
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4175911
Additional resources that should help:
http://www.javaworld.com/article/2077388/core-java/what-version-is-your-java-code.html?page=2
http://www.oracle.com/technetwork/java/javase/8-compatibility-guide-2156366.html
yeah!! it actually depends on the code. in some cases it might generate a different bytecode but normally it generate the same bytecode in normal cases.
Different JDKs will have different Java compilers which may emit different bytecodes for the same source code. The javac compiler has evolved over time.
Different major versions of the Java often emit class files that conform to different versions of the classfile specification.
Even if you restrict yourself to one JDK installation, two runs of the compiler on the same source file will produce non-identical .class files.

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