How to write a Java annotation processor? - java

I may be just looking in the wrong direction but I find the JSE documentation on annotation processing very ... sparse. I want to write an annotation processor which processes annotated String fields and local variables to substitute them with a computed String expression. This should not be too complicated but I'm pretty lost in the Javadoc for javax.annotation.processing.
EDIT: I need to process annotations at compile time because I want to modify the generated code. It should replace annotated constant String expressions with a computed String expression.

This can not be done with a compile time annotation processor. Compile time time annotation processors can only generate new files (and classes) they can not modify existing classes. You can do reflection at runtime but strictly speaking you that is not called annotation processing. Also you won't have access to local variables.
If you're looking on how to write a compile time annotation processor check out https://github.com/pellaton/spring-configuration-validation-processor

Two tools that do this are Project Lombok and DuctileJ. Both of these tools existed at the time the question was originally asked; additional tools now surely exist.
The key idea is to write an annotation processor that traverses and modifies the program's AST (abstract syntax tree) during compilation, before code generation. The compiler won't change the source code on disk, but the generated .class file will reflect the changes that your annotation processor makes.
You may be able to adapt one of these tools to suit your needs, or you could implement your own tool inspired by their implementation techniques.
Compile-time processing has two advantages over class-file processing. One is that the compiler usually has more information than is available from compiled code. Another is that everything happens in one step, during compilation, rather than requiring the developer to run a separate tool to rewrite the .class files after compilation.

Check
Javassist http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/
ASM http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/
Byteman (for runtime) http://www.jboss.org/byteman/

Related

Can I create custom Annotation to initialize static variables using other field names? [duplicate]

I may be just looking in the wrong direction but I find the JSE documentation on annotation processing very ... sparse. I want to write an annotation processor which processes annotated String fields and local variables to substitute them with a computed String expression. This should not be too complicated but I'm pretty lost in the Javadoc for javax.annotation.processing.
EDIT: I need to process annotations at compile time because I want to modify the generated code. It should replace annotated constant String expressions with a computed String expression.
This can not be done with a compile time annotation processor. Compile time time annotation processors can only generate new files (and classes) they can not modify existing classes. You can do reflection at runtime but strictly speaking you that is not called annotation processing. Also you won't have access to local variables.
If you're looking on how to write a compile time annotation processor check out https://github.com/pellaton/spring-configuration-validation-processor
Two tools that do this are Project Lombok and DuctileJ. Both of these tools existed at the time the question was originally asked; additional tools now surely exist.
The key idea is to write an annotation processor that traverses and modifies the program's AST (abstract syntax tree) during compilation, before code generation. The compiler won't change the source code on disk, but the generated .class file will reflect the changes that your annotation processor makes.
You may be able to adapt one of these tools to suit your needs, or you could implement your own tool inspired by their implementation techniques.
Compile-time processing has two advantages over class-file processing. One is that the compiler usually has more information than is available from compiled code. Another is that everything happens in one step, during compilation, rather than requiring the developer to run a separate tool to rewrite the .class files after compilation.
Check
Javassist http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/
ASM http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/
Byteman (for runtime) http://www.jboss.org/byteman/

Annotating generated Java source nullability (for Kotlin)

I am generating Java source files for my project using a source generation tool (antlr). However, I am writing most, if not all of my code, in Kotlin.
Kotlin already offers great Java interop, so using the generated sources is not a problem. However, because of how Kotlin brings Java's nullable types into a null-safe system, I lose most of the null-safety that I use Kotlin for. At the very best I have warnings of platform types (make type explicit to avoid subtle bugs); at the worst I have unexpected crashes and subtle bugs.
Kotlin does, however, respect nullability annotations, such as JSR-305, FindBugs, Lombok, Eclipse, and JetBrains's respective forms of #Nullable/#NonNull, bringing those in as the appropriate non-null type or optional.
Because the code is generated and I have access to the source (and understand how it works), I know which functions can/not return null, and want to annotate them as such so they include neatly into my null-safe code. However, I cannot add annotations directly into the code, as it is generated during the build step and would overwrite any manual changes.
Is it possible to / what is the best way to annotate the nullability of generated java sources for the purpose of use in null-safe code?
The best way would be to modify the source code generator so that it includes the annotations that you require.
If you can't modify the generator (e.g. because the generator is proprietary and you don't have its source code) then you can do this using bytecode engineering. For example, this page on the ASM site gives one way to do it:
http://asm.ow2.org/doc/tutorial-annotations.html
Of course, in either case you need some way to tell the tool (the generator, the bytecode rewriter, whatever) which methods should be annotated.

How does the javac tool process the annotations

How does the javac tool process the annotations in the source code? Are they compiled and translated into bytecode or they will be treated in a way that resembles processing the comment lines?
Annotations are handled by the compiler in a special way. They get parsed, but most annotations do not mean anything to the compiler. Some do, for example, if you incorrectly use the #Override annotation, a compile-time error will occur. You can find a list of compiler-relevant annotations here.
After that, the behavior depends on the annotation type:
If an annotation has its rentention policy set to RUNTIME or CLASS, it will actually be included in the class file. If the retention policy is RUNTIME, the annotation can even be accessed at runtime via reflection. Many libraries use this to provide some additional comfort, e.g. JUnit uses #Test to mark methods which implement unit tests and looks for those at runtime.
In addition, the java compiler allows to implement so called 'annotation processors', which can access the AST of the compilation unit and process annotations. This is another proof that the compiler does indeed handle annotations.

Modern Java Annotation Processing

Is annotation processing still an active part of Java 6+, or is it something that has been deprecated/discouraged/obsolesced. If obsolesced, why (why is it no longer needed/useful)? And if it's still extremely useful and "active" (a new Java project developing against the Java 6+ JDK would still benefit from it), please confirm/correct my understanding of how annotation processors are used:
You create your own annotation class, say #MyAnnotation
You mark certain classes, methods, fields, etc. with #MyAnnotation
During the build process, you invoke your custom MyAnnotationProcessor (how?)
The processor scans your classpath for instances of #MyAnnotation
Typically, an annotation processor does dynamic bytecode injection, modifying/enhancing your compiled classes on-the-fly
Correct.
Correct.
Correct. Typically you will extend AbstractProcessor. You specify your MyAnnotationProcessor class using the ServiceLoader pattern - this makes it discoverable to the compiler. The Processor Javadoc contains some information on creating this class.
Correct.
This part is not correct. The annotation processor framework does not give you the ability to modify classes. You will need to use some post-compile process to do this as part of your build. What it does allow you to do is create new files. These may be simple resources, new Java source files (that will subsequently be required and eligible to the annotation processor during the same compile), or Java class files. You may also perform custom checks on source code and write errors to the log (causing the compile to fail).
I hope that addresses your understanding questions.

Retro-actively add Java annotations to methods?

Is there a way to modify .class files in order to add Java annotations to certain methods? Basically I want to traverse methods of each class file in a jar file and annotate certain ones. Note that this is not at run-time while using the jar file. Rather, after I'm done I want to have modified class files with the annotations.
I do have access to the source code, so if there's an automatic source code modifier, that would work as well...
I'm assuming I'll need a tool such as Javassist or ASM. If so, which one should I use and how would I go about it?
Actually, this is a classic use case for AspectJ:
declare #method : public * BankAccount+.*(..) : #Secured(role="supervisor")
While I will grant you that direct byte code manipulation is more powerful, AspectJ is much more user-friendly, and it immediately gives you compiler warnings when you are doing something wrong.
Also, if you use Load Time Weaving, you can leave the original library jar unchanged, because the weaving happens at class-load time.
Reference:
Declare Annotation
AspectJ in Action (book)
Googling for an hour or so turned this article up which seems to completely answer my question: use ASM. To write class files using the changed bytecode, use ClassWriter.
Well, time to get to work then, I guess. :)

Categories

Resources