Regression in JDK8 build 78: javac complains about missing Class#isAnnotationPresent
Uwe Schindler
uschindler at apache.org
Fri Mar 1 00:44:25 PST 2013
Hi Joe,
thanks for the response! I fully understand the reasoning behind your first response to my original to the mailing list. Actually there are 3 settings that can be used to make older code compile against newer JDKs. Most projects unfortunately use “-source” + “-target” at the same time, although only “-target” is needed to create class files. But javac dictates that -source must also be –target (it otherwise complains). So the only workaround is to use a bootclasspath and give both options to be identical and corresponding to the bootclasspath. But if you try to set this up, you already have the older JDK installed, so you can also compile your code against the older JDK J.
I understand that this is the recommended solution as suggested by Oracle. Unfortunately to set this up, developers and people who want to compile open source projects have to take care of additionally installing an old JDK version (or at least somewhere download the old rt.jar). If rt.jar reference editions would be available in the public (just to be used as bootclasspath), e.g. as artifacts on Maven Central, the setup would be easy: Maven/Ivy could download rt.jar as a dependency and compile against it with any JDK installed on the computer of the user. If this would work, we would all be happy and your recommended approach would be easy to set up!
But this is not the case: So open source project generally use the following approach:
- They use –target and –source with an older JDK version. This allows them to produce class files that can be read with older versions. This always worked until today.
- While releasing the project they are using the “official JDK version” to build the artifacts (and also in nightly builds/continuous testing), so they can easily detect calls to APIs only available in newer JDKs. The developers (often people working on Mac OSX, where e.g. no JDK 1.5 and soon no JDK 1.6 is available anymore) can then easily develop the code using newer JDKs, but continuous testing would ensure that the code is actually compatible. For this “-source/-target” would not be needed.
- “-source/-target” is used to make the build somehow “reproducible” and is officially suggested e.g. by Maven guidelines for writing pom.xml files (and also Apache Ant suggests to always use source/target). It is also used in the build files, to allow people (e.g. from Linux distributions) to use newer JDKs to produce class files that work with older JDKs. This type of cross compiling works fine if you check the above compatibility while being in the release process of the source code. This is the real reason why projects like Lucene use source and target: To be able to produce old class files, ignoring possible API incompatibilities checked otherwise.
In addition, lots of projects use an additional approach to check for compatibility with older JDK versions because the older rt.jar for the bootclasspath is not easily accessible: There is a plugin called “animal-sniffer” (http://mojo.codehaus.org/animal-sniffer/) that has pre-compiled signature lists of older JDK versions (public available on Maven Central). You can then check the byte code of your class output and validate that no JDK methods are used that are not in the signatures files. In that case, it’s also enough to compile using source/target 1.6 with any newer JDK, and then check all method signatures in the byte code. This is much easier to set up for developers than downloading complete JDKs just to get older rt.jar files. Again, if older rt.jar files would be accessible as artifacts on Maven Central, this would be obsolete, too.
As mentioned in response to Jon’s mail, I would suggest to use default method interfaces only for *new* methods. You already mentioned that, too. I would suggest to revert http://hg.openjdk.java.net/jdk8/awt/jdk/rev/e04467fa13af because the changes inside are only cosmetics to the class library code and bring no new features for the user.
My heavy complaining here was targeted to direct your attention to side-effects of your changes to the class library/compiler. Oracle and Sun always kept hard backwards compatibility, so the change in this commit breas more builds outside than it helps. Sorry for the heated discussion.
Uwe
-----
Uwe Schindler
uschindler at apache.org
Apache Lucene PMC Member / Committer
Bremen, Germany
http://lucene.apache.org/
From: Joe Darcy [mailto:joe.darcy at oracle.com]
Sent: Friday, March 01, 2013 2:25 AM
To: Uwe Schindler
Cc: 'Maurizio Cimadamore'; lambda-dev at openjdk.java.net; compiler-dev at openjdk.java.net; Robert Muir
Subject: Re: Regression in JDK8 build 78: javac complains about missing Class#isAnnotationPresent
On 2/28/2013 2:36 PM, Uwe Schindler wrote:
Thanks Maurizio,
that’s true, somebody added the –Xlint:-options tot he ANT build file.
This is still no answer to the source of the problem
As I'm composing this message, it is less than four hours after your initial email was first sent to OpenJDK lists. In that time, your email has gotten responses from multiple people working on the JDK. As we are over six months before the planned ship date for JDK 8, addressing this particular issue does not necessarily need preempt other ongoing work so that it can be fully resolved today.
making again a new Oracle Java version unusable to compile millions of open source projects (Lucene is just an example) – this status quo seems to repeat on every new major version. Last time it was the serious Hotspot bug corrumpting loops in Java7 GA.
Well, it sounds like Lucene is testing with JDK 8 more than a few weeks ahead of its ship date, so there should be plenty of time to fix correctness bugs that are found and reported ;-)
It is very helpful for external projects to run tests of their code on JDK 8 builds along the way to supplement the testing done by the JDK team.
As for the technical details of what is going on, the isAnnotationPresent method in the java.lang.reflect.AnnotatedElement interface has been turned into a default method:
http://bugs.sun.com/view_bug.do?bug_id=8007113
This change was introduced in b77 and allows some sharing of code among the various classes implementing AnnotatedElement, including java.lang.Class, java.lang.reflect.{Method, Constructor}.
For reasons I'll describe shortly, when compiling under a -source setting earlier than source 8, code that references isAnnotationPresent in the implementing classes (or the declaring interface) will not see the method as being present. Code that does not reference this method will continue to compile under "-source OLD -target OLD" (with the usual caveats) and code that references this method will see the method as expected under -source 8.
Now, for the central technical question: (using concrete values) how should new-in-Java-8 language constructs in the bootclasspath appear to code being compiled under -source 6? This question is outside of the Java Language Specification, which assumes only a single source version exists at any one time, and has no clear-cut answer. As of today, as a matter of policy javac makes default methods invisible to code being compiled under source versions earlier than 8. Since most default methods added in JDK 8 will be for new methods on interfaces, this will usually do what people want when compiling for older releases (but setting the bootclasspath is the way to be sure you're getting the version of the libraries you should have).
Since AnnoatedElement.isAnnotationPresent is an existing method that is now a default method, is it rendered invisible to code being compiled under older source versions (when the recommendation of setting bootclasspath has not been followed).
Now javac could be made to special-case isAnnotationPresent or change its policy and make all default methods visible under older source levels, but we are more likely to partially revert the library change to the public implementations of AnnotatedElement by giving them implementations that called the method defined in the interface.
Cheers,
-Joe
Every public Java project has somewhere in their build file “-target 1.6 -source 1.6” (or another older version), because otherwise you cannot run the generated class files with older Java versions. Compiling with a custom bootclasspath is a good idea to *validate* that your code actually is compliant. But if you need to use the bootclasspath, you need to have the older JDK version installed - in that case you could compile with it, too.
The intention here is to just “test” the class files with newer JDK versions which is now impossible, without hacking the whole build in an incompatible way. If this is not fixed, we will suggest to users, not to upgrade to JDK8 once it is out – because we cannot test the class files with JDK 8 (and we have already seen lots of hotspot issues making Apache Lucene crash/fail). The same procedure as every year – ahm Java version.
Of course to produce Java 6 class file format, one could use only “-target 1.6” and don’t pass “-source 1.6” (so let it default to 1.8), but this does not help if you build your project with Apache Ant (maybe doesn’t help with Maven, too):
[javac] Compiling 113 source files to C:\Users\Uwe Schindler\Projects\lucene\trunk-lusolr3\lucene\build\test-framework\classes\java
[javac]
[javac] WARNING
[javac]
[javac] The -source switch defaults to 1.8 in JDK 1.8.
[javac] If you specify -target 1.6 you now must also specify -source 1.6.
[javac] Ant will implicitly add -source 1.6 for you. Please change your build file.
So you no longer have the chance to produce javac 1.6 compliant class files without bootclasspath (using common build tools like Apache ANT). This makes JDK8 unusable.
Sorry for complaining,
But that’s a major regression!
Uwe
-----
Uwe Schindler
uschindler at apache.org
Apache Lucene PMC Member / Committer
Bremen, Germany
http://lucene.apache.org/
From: Maurizio Cimadamore [mailto:maurizio.cimadamore at oracle.com]
Sent: Thursday, February 28, 2013 11:15 PM
To: Uwe Schindler
Cc: 'Joe Darcy'; lambda-dev at openjdk.java.net; compiler-dev at openjdk.java.net
Subject: Re: Regression in JDK8 build 78: javac complains about missing Class#isAnnotationPresent
On 28/02/13 21:31, Uwe Schindler wrote:
I know about this warning and it generally appears on Java 7, but it was *not* printed out in that case (8b78)! The log I posted was the complete printout from javac.
This warning is a Xlint warning - so it's possible that you are compiling the project with a custom set of Xlint warnings that doesn't include this category (called 'options').
Maurizio
More information about the lambda-dev
mailing list