Java backward compatibility doesn't work - java

I've created library for encoding/decoding property files. Library has two main purposes:
Encode property file and save it to another file.
Return key value from encoded file (decode file, store result as string in memory, load string to Properties object and return result from properties object).
Everything seems to work fine but today I've noticed that library doesn't work on java 1.5. I've noticed that problem occurs on decoding side so let's focus on this code. Assume that code responsible for decoding looks like that:
String props = "key1=val1\nkey2=val2";
Properties p = new Properties();
p.load(new StringReader(props));
p.list(System.out);
After few tests I saw that the problem is with this line:
p.load(new StringReader(props));
I found that Properties class in java 1.5 doesn't have load(Reader) declaration. To meet java 1.5 API requirements I changed this line to load(InputStream). Everyting works fine now but here is the question.
I use gradle to compile project and I knew that this library should work on java 1.5+ ( I've java 1.7 installed on my computer) so I added to build.gradle those two lines
sourceCompatibility = '1.5'
targetCompatibility = '1.5'
I thought that java compiler will know that I want to compile code with compatibility to java 1.5 and will show appropriate errors. To be sure that it isn't gradle problem I compiled java code from command line but with the same result (compiler doesn't show any errors). So why compiler doesn't show any errors while compiling?
Java 1.5 Properties class API: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Properties.html
Java 1.6 Properties class API: http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html
[UPDATE]
Neither -source or -target will check API compatibility. If so how can I check it in gradle? As millimoose wrote maven has this plugin (http://mojo.codehaus.org/animal-sniffer-maven-plugin/index.html) but what with gradle?

See the sections of the javac documents named "cross-compiling" and "Cross-Compilation Example".
http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javac.html#crosscomp-options
Specifically this part:
It is important to use -bootclasspath and -extdirs when
cross-compiling; see Cross-Compilation Example below ....... If you do not
specify the correct version of bootstrap classes, the compiler will
use the old language rules (in this example, it will use version 1.6
of the Java programming language) combined with the new bootstrap
classes, which can result in class files that do not work on the older
platform (in this case, Java SE 6) because reference to non-existent
methods can get included.

The -source switch only instructs the compiler to give a compilation error if you use a language construct not supported in the specified version. For example using try-with-resources with -source 1.6 will result in a compilation error, as it is only supported in Java 7 and higher. Its use is more a sanity check (ie: is my code still compatible with Java version 1.x)
The -target switch instructs the compiler to emit byte code compatible with the specified version. That is: the compiled code can run on the virtual machines of the specified version.
However neither of these switches make the compiler check for compatibility with Java libraries of an earlier Java version. That is why since Java 7, the compiler gives a warning if you use -target 1.6 (or earlier), that you should also specify the -bootclasspath to point to a Java runtime library set of that java version so that it can check if your code is only using classes and methods of that Java version.

Related

Can't find bootclasspath on jdk9+

The openjdk document "JEP 261: Module System" states that:
"A related option, -Xbootclasspath/a, allows files and directories to be appended to the default bootstrap class path. This option, and the related API in the java.lang.instrument package, is sometimes used by instrumentation agents, so for compatibility it is still supported at run time.
Its value, if specified, is reported via the JDK-specific system property jdk.boot.class.path.append."
However, when I try to use this feature, it does not work. Specifically, I am using Java Agents to do instrumentation.
All works fine on JDK7 & JDK8. On JDK9+ the -Xbootclasspath/a seems to work ok, but the system property jdk.boot.class.path.append is always null.
On java 7/8:
String bootclasspath = System.getProperty(`sun.boot.class.path`);
works as expected.
On java 9:
String bootclasspath = System.getProperty("jdk.boot.class.path.append");
always return null string.
I added some debug code to dump all the system properties, and there appears to be no such property.
I have tried jdk-9.0.4 and jdk-11.0.3 with similar results.
Any ideas on how to get the bootclasspath on JDK9+?

A problem with build task and sourceCompatibility = 1.7, gradle 5.3

I use sourceCompatibility = 1.7 in gradle.properties, Gradle JVM(Settings, Gradle, Intellij IDEA) is 1.8.0_171 and
wrapper {
gradleVersion = '5.3'
distributionType = 'ALL'
}
in build.gradle.
When I run build task(Tasks->build->build Intellij IDEA) I get "error: incompatible types: inferred type does not conform to equality constraint(s)" during compileJava task.
If I set up Gradle JVM is 1.7.0_80 and
wrapper {
gradleVersion = '4.3'
distributionType = 'ALL'
}
build is fine.
What I'm doing wrong? How can I build JAVA 7 project using gradle 5.3?
This is probably caused by a subtle change in the type-checking rules for generics between Java 7 & Java 8 as described in Why does this program compile with Java 7 but not Java 8?
So what is the solution?
I doubt you will find a magic compiler switch or something to just make it work. Realistically, I think that your choices are:
You could just compile on a Java 7 platform as you did to start with. But this is not a good long-term solution.
If this is a supported product, lodge a support request.
If this is an open source product, check the product's issue checker to see if someone has already reported the problem. They may have reported a fix as well.
Look at the source code that you are trying to compile, identify the cause of the compilation error and fix it. If this is an open source product, submit your fix as a patch.
Note that it is possible that these compilation errors are reporting a latent bug that could under some circumstances lead to unexpected runtime exceptions.
You said:
I thought sourceCompatibility = 1.7 will set up java 7 for compile *.java.
It does. However, there are degrees of compatibility.
When you run a Java 8 compiler with -source 1.7 you are actually just turning off support for new language features. Under the hood, the compiler is still a Java 8 compiler. If there have been subtle changes in (for example) the type checker, it is plausible that the Java engineers did not implement a backwards compatibility mode for the old behavior.
(The extra mode may make an already complex piece of software too difficult to maintain. Type checking and type inference is one of the more difficult aspects of compiler implementation.)

How to detect the version of the compiler that is compiling my java source file

Hi I am writing a Java class that enables some of its functionalities when it is compiled with java7 upwards.
I know how to find the major/minor magic number from a compiled class file. But I need to find a way to detect that when the compiler is compiling my source code.
A simple snippet for this:
```
public class Hey
{
public static final boolean JAVA_VER = ???; // how to do this??
public static foo() {
if (JAVA_VER >= JAVA7) {
// use the fancy way introduced in java7
} else {
// go with the slow way of java6
}
}
}
```
Code is not executed during compilation. During compilation it is checked for syntactical errors and converted to byte code inside a .class file. Code lines can be executed when it is run and hence the only thing you can find out in your code is java runtime version. Java runtime can be found using java.version system property. Here is the code for that:
String version = System.getProperty("java.version");
If you want to check the java compiler version, perhaps then you use a script to compile your code. Maybe you can have two different versions of your file, each one apt for one particular java version. In your script check the available java version and compile the desired version of file. You may want to use single output common output directory.
This should be impossible. The compiler requires that the JVM running the code have the same version as the code being run - in essence, you cannot compile that for a lower version because the higher version code needs a higher version compiler.
In the case you were able to get around this, I would probably pack a script as mentioned before, or programmatically do so in a bootstrap by executing a byte code dump (which specified the major/minor version) either with Process or source decoding from the File bytes.

How to express Java Double Array type (fixing disassembled code)

I have some code that depends on jars that were compiled using Java 1.7. I am currently working on OSX, where I only have access to Java 1.6. I am currently attempting to recompile these jars locally. However, the jars only contained the .class files. I downloaded a disassembler and saved the resultant .java files. Now, there are some errors that I am currently trying to debug. One of the files checks to see if some parameter is equal to a class or type. The problem I'm having is that there is the expression
if (paramType.equals([D.class)) { ... }
which is causing a compiler error. What is the proper way of expressing a double array class?
Assuming it's an array of (primitive) double:
if (paramType.equals(double[].class)) { ... }
Or if it's an array of (wrapper type) java.lang.Double:
if (paramType.equals(Double[].class)) { ... }
If the classes don't link against any new library classes introduced in Java 1.7, then they should work just fine in Java 1.6, as the only bytecode features introduced in 1.7 are not actually used in Java.
All you have to do is change the 8th byte of every file from 51 to 50. You don't even have to disassemble and reassemble them.

JDE for Emacs for JDK 6.10

Is there a version of JDE for emacs that supports the JDK 6.10? I haven't been able to find any information on this. While it runs, every time I attempt to compile files the JDE says that it doesn't recognize my JDK version and reverts to assuming it is a Java5 version.
I've made following customizations for JDE:
'(jde-bug-debugger-host-address "127.0.0.1")
'(jde-bug-jre-home "/usr/lib/jvm/java-6-sun")
'(jde-compile-option-debug (quote ("all" (t nil nil))))
'(jde-debugger (quote ("jdb")))
'(jde-global-classpath (quote ("." "/usr/share/java/" "/usr/lib/jvm/java-6-sun/")))
'(jde-jdk-doc-url "/usr/share/doc/sun-java6-jdk/html/api/index.html")
'(jde-jdk-registry (quote (("1.5" . "/usr/lib/jvm/java-6-sun"))))
'(jde-regexp-jar-file "/usr/share/java/regexp.jar")
'(jde-sourcepath (quote (".")))
So it compiles without complaints, although I have jdk 1.6.0.07.
You can set your paths up in the configuration settings by "registering" a JDK version using M-x customize-variable and choosing jde-jdk-registry. Save that state, then do M-x customize-variable again, customize jde-jdk and pick the one you want.
That should do it; if not, give us a little more detailed information.
Yes, I've done that. The problem is when I call 'jde-compile, The message 'The JDE does not recognize JDK6.0.10 JDK. Assume JDK 1.5 Javac?" appears. Also, It doesn't look like the Java6 constructs, such as annotations, have been defined in the syntax checking or indentation rules.

Categories

Resources