FULL PRODUCT VERSION :
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)
This is the JDK I am currently testing on. Other collaborators with diverse versions noticed the issue first.
ADDITIONAL OS VERSION INFORMATION :
Linux -?-?- 3.10.25-gentoo #2 SMP Sun Jul 13 16:00:50 EDT 2014 x86_64 Intel(R) Xeon(R) CPU X5650 @ 2.67GHz GenuineIntel GNU/Linux
This is the OS I am currently testing on. The issue has affected collaborators on other systems.
A DESCRIPTION OF THE PROBLEM :
A previously-working (tested in Java 6) feature in org.postgresql.pljava was found broken under Java 7, and what's failing is a call to javax.lang.model.util.Types.isAssignable made from the annotation processor's process() method during the final round.
isAssignable(tm, ktm) returns false in Java 7, when tm and ktm are both TypeMirrors representing java.lang.String. The first was the type argument obtained in an earlier round using getTypeArguments on a DeclaredType Iterator<String>. The second was obtained in the current round using javax.lang.model.util.Elements.getTypeElement("java.lang.String").asType().
I have confirmed that the ProcessingEnvironment.getElementUtils() returns the same Elements instance for all rounds, but calling the instance's getTypeElement method with the same class name string returns different TypeElement instances from one round to the next, and Types.isAssignable returns false when comparing them (more precisely, TypeMirrors from their asType methods).
In my limited testing, getTypeElement will return the same TypeElement instance for the same string argument in the very first round as it will from the Processor's init() method. However, it returns new different instances for every subsequent round (not only the final round).
This seems very likely to be related to JDK-8038455 (use single Context for all rounds), but I am making this separate report because 8038455 is classified as an enhancement only, and I did not see any other report that clearly described a related regression that broke working code.
So, I hope reviewing this report will at least allow you to confirm:
a. Is this in fact the same issue as 8038455, and therefore is it certain to be fixed in Java 9 (shown as the "fixed version" for 8038455?
b. Are the reasons it changed between Java 6 and 7 clearly understood? Can a specific changeset be identified?
c. Is there clear documentation for Java 7 and 8 that this usage isn't expected to work? As the author of the code that's now broken, I'll feel a bit sheepish if there was clear language saying "don't do it that way", but if there was I'll be happy to learn where I missed it, and if there wasn't there should be. :)
REGRESSION. Last worked in version 6u45
ADDITIONAL REGRESSION INFORMATION:
java version "1.6.0_81"
Java(TM) SE Runtime Environment (build 1.6.0_81-b08)
Java HotSpot(TM) Server VM (build 20.81-b05, mixed mode)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I haven't had time to write a minimal test case, but it is not hard to test by cloning https://github.com/tada/pljava.git and running mvn package. Then:
cd pljava-examples/src/main/java/org/postgresql/pljava/example/annotation
Edit UsingPropertiesAsScalarSet.java: change the annotation on the first method from @Function(complexType='VARCHAR') to plain @Function. (Adding the explicit type was a workaround put in when the automated mapping broke. This edit reverts it.)
mkdir Foo
javac -cp ../../../../../../../../../pljava-api/target/pljava-api-0.0.2-SNAPSHOT.jar -d Foo UsingPropertiesAsScalarSet.java
(yes, that's nine ../s).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
javac comples silently, creating Foo/pljava.ddr. Viewing that file, the function getProperties() is declared as "RETURNS SETOF varchar", which was automatically determined from its Java return type of Iterator<String>.
ACTUAL -
javac reports 'error: No known mapping to an SQL type' and produces a Foo/pljava.ddr where the SQL type is given as "RETURNS SETOF java.lang.String" instead of the correct "RETURNS SETOF varchar".
That happens because the annotation processor called Types.isAssignable(tm, ktm) where tm and ktm both represent java.lang.String, and isAssignable returned false.
tm was the type argument of Iterator<String> from the function return type, and ktm came from calling Elements.getElementType(...).asType() with the name java.lang.String from a table of known Java to SQL type mappings. Because tm was collected in a prior round and ktm was assigned in this round, isAssignable sees them as distinct.
REPRODUCIBILITY :
This bug can be reproduced always.
CUSTOMER SUBMITTED WORKAROUND :
It appears necessary to write an annotation processor so that all introspection on or comparison of types is confined within rounds, and no type information is accumulated across rounds with the hope of making sense of it in a later round.
However, that condition is strict enough to cramp what a processor can do, and as it does not seem to be clearly documented, and did not seem required in Java 6, I can't tell whether it was intended behavior or an unintentional regression.