This project is about experimenting with annotation processing.
It does not have much unit tests since the point is to look at the log produced by the compiler.
cd [clone of this Git project]
mvn clean install
I have used this project to investigate how the Javac compiler (1.7) builds rounds of annotation processing and trying to figure out if given a lot of source files, they will be split in more than one round.
- 999 classes with annotation
@Annotation1
in a single package - 999 classes with annotation
@Annotation2
in a single package Processor1
is registered to process@Annotation1
Processor2
is registered to process@Annotation2
Result : only one round with all source files.
- same clases as above
- 9999 classes with annotation
@Annotation1
in a addition subpackage
Result : the same, only one round with all source files.
I have used this project to investigate how the Javac compiler (1.7) builds rounds of annotation processing and trying to figure out if given a lot of annotation processors, they will be split in more than one round.
- 50 annotations
- one annotation pocessor for each one
- one class for each annotation on its own subpackage
Result : only one round with all source files and all annotation processors
This is a multi-module Maven project composed of two modules.
Defines a single Annotation Processor OptionAnnotationProcessor
.
This Annotation Processor extends AbstractProcessor
and uses annotations to defines that is supports all annotations and SourceVersion.RELEASE_7
.
It does not defines any supported options, as, we will see later, this is just useless with Javac
.
Defines a single class SomeClass
, annotated with @Deprecated
so that annotation processing actually occurs.
The Maven compiler plugin is configured to display annotation processing logs and to pass several options to the Annotation Processor using the -A
argument of Javac
:
-Aoption1 -AOption2=valueOfOption2 -AA -AB= -Acom.acme.Processor.enable
I initially wrote OptionAnnotationProcessor
with a @SupportedOptions
annotation which declared several option names. I intentionally used various case flavours to test case sensitivity. I compiled the project and noticed the Map
returned by ProcessingEnvironment#getOptions()
contained all the values above.
I added extra ones, all there.
I then removed the @SupportedOptions
completely and the content of the ProcessingEnvironment#getOptions()
was still the same.
I ran this test with Java 7 and Java 8, same result.
To make sure the filtering of the options is not disabled when there is only one Annotation Processor, I added another Annotation Processor to the build. SecondProcessor
does declare supporting some specific options. Behavior observed: both Annotation Processor have access to a Map
with the same content: all the options.
Round 1:
input files: {fr.javatronic.experiments.annotationprocessing.options.SomeClass}
annotations: [java.lang.Deprecated]
last round: false
OptionAnnotationProcessor Supported options=[]
Options received:
option1 -> null
Option2 -> valueOfOption2
A -> null
B -> null
com.acme.Processor.enable -> null
Processor fr.javatronic.experiments.annotationprocessing.options.OptionAnnotationProcessor matches [java.lang.Deprecated] and returns false.
SecondProcessor Supported options=[com.acme.Processor]
Options received:
option1 -> null
Option2 -> valueOfOption2
A -> null
B -> null
com.acme.Processor.enable -> null
Processor fr.javatronic.experiments.annotationprocessing.options.SecondProcessor matches [java.lang.Deprecated] and returns false.
Round 2:
input files: {}
annotations: []
last round: true
Conclusion: the value returned by Processor#getSupportedOptions
is simply ignored by Javac
and the Map
returned by ProcessingEnvironment#getOptions()
containes the values of all the Javac
arguments starting with -A
.
This module is made of two submodules.
- processor-versions: defines a single Annotation Processor:
VersionProcessor
- supports annotation
@Deprecated
- supports annotation
- test-versions: module compiled with the
VersionProcessor
to test versions- just defines a single class
SomeClass
annotated with@Deprecated
to trigger annotation processing
- just defines a single class
Here is how to experiment with versions using these two modules. The following are supposed to be done in sequence. You will need a JDK 1.7 and JDK 1.8 installed.
Run mvn clean install
directly in the experimenting-versions
directory.
If you're are compiling with a Java 1.7 JDK, compilation will be ok as VersionProcessor
declares supported SourceVersion.RELEASE_7
.
Modify VersionProcessor
to support SourceVersion.RELEASE_6
and recompile using JDK 1.7.
Build is also ok as Java is fully backward compatible.
Change it to SourceVersion.RELEASE_8
.
You get a compilation error because value RELEASE_8
does not exist in enum SourceVersion
in JDK 1.7.
annotation-processing-experimentations/experimenting-versions/processor-versions/src/main/java/fr/javatronic/experiments/annotationprocessing/versions/VersionProcessor.java:[17,38] cannot find symbol
Change your JAVA_HOME
to point to a Java 8 JDK and recompile.
It works (of course!).
Change your JAVA_HOME
back to JDK 1.7 and compile only the test-versions
module.
You are going to compile a module with Java 1.7 using an Annotation Processor VersionProcessor
compile using Java 1.8 and declaring supporting SourceVersion.RELEASE_8
.
You get another compilation error!
Fatal error compiling: java.lang.EnumConstantNotPresentException: javax.lang.model.SourceVersion.RELEASE_8
Here we can really wonder what is the point of declaring a suppported version:
- declare a lower version that the one used to compile, it works anyway since Java is backward compatible
- declare a higher version the the one used to compile, the compilation will fail because the version is indicated via an enum value, which can not exist in the current of Java since it is older.
This module is made of two submodules.
- processor-locale: defines a single Annotation Processor:
LocaleProcessor
- supports annotation
@Deprecated
- supports annotation
- test-locale: module compiled with the
LocaleProcessor
to test value returned byProcessingEnvironment#getLocale()
- just defines a single class
SomeClass
annotated with@Deprecated
to trigger annotation processing
- just defines a single class
Compile the experimenting-locale
and look for the line starting with getLocale()=
: mvn clean compile | grep "getLocale()="
The locale you will see should be the default locale of your system.
By default, Javac
picks up the current language and country from the environnent and the getLocale
method will return the corresponding Locale
value.
The default language and country can be override in Javac
with the -J-Duser.language
and -J-Duser.country
command line arguments (e.g. -J-Duser.language=fr -J-Duser.country=FR
).
Unless the Javac
process is forked, -J
command line arguments can not be specified directly in the configuration of the maven-compiler-plugin
. The reason is that Maven is running javac
through the internal javac API of its own Java process and this does not accept -J
arguments (obviously, it is not possible to modify an already existing Java
process).
To change javac
locale, one need to change the locale of the Java process of Maven, you can either use the -D
arguments of the mvn
command (e.g. mvn clean install -Duser.language=fr -Duser.country=FR
) or change the language environment variable of your system (e.g. on linux LANG=fr_FR.UTF-8 mvn clean install
).
The problem with forking the javac
process is that its logs won't be visible at all in the console. Here, this is hardly something we want to do.
In conclusion, to experiment with the value of the locale, you can specify user.language
and user.country
command line arguments: mvn clean compile -Duser.language=fr -Duser.country=FR
.