The target system, on which my application is supposed to run, uses Java 6. On my development machine, I have Java 7. Can I do the development, without downloading Java 6?
I found on http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html one example for cross compilation:
javac -source 1.6 -target 1.6 -bootclasspath C:\jdk1.6.0\lib\rt.jar -extdirs "" OldCode.java
However, this too requires the existence of a rt.jar, which belongs to Java 6. Is there a simpler way?
New Java versions generally change both the Java Language (source and class file format) and the Java API.
The Java compiler can emit class files in the old format, even if the source is in a new format (these versions are specified by -target and -source, respectively). Therefore, you don't need the old compiler to target an old JVM.
However, the changes to the Java API are somewhat harder to account for. The easiest is to compile using the API of the Java version you target (-bootclasspath). Of course, you may feel confident that you are not using newer APIs, and skip this step, but the only way to make sure is actually compiling against, and testing on, the old runtime library.
In short, while cross compilation is helpful in that the same source can be used with different Java versions, you should compile and test against the actual Java version you intend to use, which does require the old JRE (or JDK).
BTW, all of these settings are also available in Java IDEs. For instance, in eclipse, one would set the compliance level in the project's compiler settings, and add the appropriate "JRE System Library" to the project's "Build Path":
The below command should suffice to meet your requirement.
'javac -source 1.6 -target 1.6 OldCode.java'
With this command you are telling that the compiler should generate class file that is compatible with java 6. Any java 7 specific will result in compilation error. Regarding rt.jar, you don't need to have java6 specific version. As mentioned the above command automatically ensures output is java6 compatible.
UPDATE/CORRECTION:
After going through the following link http://www.javaworld.com/article/2077388/core-java/what-version-is-your-java-code.html it is clear why it is recommended and is important to use -bootstrap flag along with -source and/or -target flags.
Related
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.
For some time I tried to understand, but I still don't get exactly what "compiler compliance level" for a project in Eclipse means. I looked all over this site and Google and couldn't find an answer I could understand.
Let's say I want my program to be able to run on JRE 6.
I can do: Project > Preferences > Java Build Path > Libraries, and set the JRE library I use to JRE 6.
Why isn't this enough?
I never understood why I also need to set the compiler compliance setting to JRE 6.
I'd like to understand the difference between using JRE 6 in a project, and setting the project's compiler compliance setting to JRE 6.
What exactly does compiler compliance level mean?
The compiler compliance setting tells the compiler to pretend it's a different version of Java.
The Java 8 compiler will produce class files in the Java 8 version of the class file format, and accept Java 8 source files. JRE 6 can't load this version, because it was created after JRE 6 was.
If you set the compliance level to "JRE 6", it will instead compile Java 6 source files into Java 6 class files.
It's like saving a Word document as "Word 97-2003 format" - so that Word 97-2003 can read your document. You're saving the class files in Java 6 format so that Java 6 can read them.
It tells you what version of the JDK you are adhering to; specifically, which JRE are you targeting with your build.
It is the MINIMUM JRE needed to run your code
The Java compiler can compile code that will run on prior versions of the JVM. Even if you have JDK 6 installed, you could be writing code targeted for people that use JDK 5. The Compiler Compliance level tells Eclipse to use appropriate settings when compiling your project to ensure you code will work on the target JVM you specify. By default, if I recall, Eclipse picks Java 5 Compliance. If you want to use code features specific to Java 6 or Java 7, you will have to change the compliance level.
The simplest way to describe the "Java compiler compliance" setting is that it determines which Java Virtual Machine instructions may be used in the "compiled" Java code ... and which library class versions are to be used. Each release of the JVM (and its libraries) may introduce new instructions (the VM) or adjust the class libraries that are delivered along with the JVM.
You should set the setting to the lowest level of JVM that you expect your product to be run on.
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.
I have a PowerMac and it is giving me bad version number on some .jars. I would love to make it seem like I am running Java 6. How would I spoof the version? Let me also say I am running PowerPC and Leopard
The most likely problem is that you have Java 6 JAR files and you are trying to run them on an old Java installation.
How would I spoof the version?
The answer to your question is that you can't. The way to run Java 6 specific JAR files it to use a Java 6 (or later) JRE or JDK.
The problem is that the format of Java class files has changed, and your installation can't cope with the new format. And this is not a gratuitous change that you can pretend doesn't exist. Java 6 (actually Java 5) has support for generic types, enums, annotations and other things. Assuming that the JARs contain code that uses these new language features, an older JRE simply won't know what to do with them.
There are two solutions:
Upgrade your Java installations to the required level on all machines. This is the best solution ... if it is an option ... because it means your users will get the benefit of security and bug fixes and performance enhancements. (And progress of your project won't be held back by the constraint of supporting legacy platforms.)
Compile all of your code for compatibility with the oldest version of Java that you still have to use. Either compile on the corresponding old JDK, or on a more recent JDK using appropriate -source / -target / -Xbootclasspath options ... as described by the javac manual page.
The catch with the second solution is that if the source code for the JAR files in question uses recently added Java language features or APIs, then recompiling for the older platform will fail. To fix this you will need to rewrite your code to replace the nice modern stuff with archaic stuff. Not a good solution, IMO.
The other possibility is that you are seeing corrupted JAR files. This is unlikely, but it can happen if you are using applets or webstart, and the server is delivering error pages instead of JAR files.
The third possibility is that you simply haven't configured your Mac's Java installation's correctly. Making Java 7 the default should allow you to run everything without class version problems. (Thanks #paulsm4) Note that I can't help you with that ... 'cos I don't use Java on a Mac.
The article "Don’t be fooled by javac -target 1.4" shows that compiling with -target 1.4 still can create class files which will not run on JRE 1.4.
It also describes how the -bootclasspath parameter can solve this problem.
Now my question: if I compile with the Sun JDK 1.6 version of javac, using the source and target parameters set to 1.3, is safe to assume that the compiled classes will work on a JRE 1.3?
They won't work if you use any methods or classes in the standard library that didn't exist in 1.3, as these files are not compiled into your program, but are part of the JVM's libraries.
In fact, like the article says, your code will be bytecode compatible with 1.3, but not linkable with 1.3. As a consequence, it is possible for you to use post 1.3 classes or method (as states their example) as long as you use a rt.jar coming from a version more recent than the expected 1.3.
As a consequence, the only sure way to develop for 1.3 is to use a 1.3 JDK ... or to rely on verification tools, like ... oh crap ! i'm sure there is one maven reporting plugin that can check your code is compatible with expected JDK, but can't find back how it's named.
However, notice there exist tools like Retrotranslator, that will take your post 1.5 code to adapt it to previous versions ... as long as your code solely relys on JDK, as they may not be able to handle all external APIs dependencies