How to hook custom compiler logic based on annotations - java

I want to define a few annotations that will allow extra warnings/errors to be reported during compilation (similar in concept to the #Nullable and #NotNull annotations in IntelliJ).
I would like to be able to write some compiler hooks that will also add my compilation logic based on those attributes.
I want a generic hook if possible, however since we are using Eclipse - it would also be a benefit if we had that ability.
I'd like to know:
Is it possible? (any of the options above)
Where do I start?
I had little experience with annotations so far, so if I'm going about this the wrong way - I'd like to know that and if possible get a better direction to go with.
Thanks.

You can use the Java Annotation Processor (see JSR 269: Pluggable Annotation Processing API) for that. From Annotation checking at compile time with Java Annotation Processor:
The JSR 269 states that you can
implement a plug-in for the compiler
which can handle the annotations. This
plug-in can be given as parameter at
compile time, so your code will be
called when one of your annotation
appears in source code.
The mentioned link provides an example that will get you started.
See also:
Annotation Processing Tool (apt)

Related

I want to suppress deprecated API usage

Environment: IntelliJ IDEA Ultimate 2022.2 Build 222.3345.118
I have overridden the method with #Deprecated annotation.
I want to suppress deprecated API usage on certain methods. But the Option+Enter action has only useless suggestions.
We tried to suppress it by means of SupressWornings and //noinspection ScalaDeprecation. I would like to disable Inspection as a last resort because there are some warnings that I want to suppress and others that I do not want to.
In Scala you can use the #nowarn annotation to this end. You can see a very nice rundown of how to use both this annotation locally or other options on the overall project on this blog post by Lukas Rytz.
In your case, I believe you can use the following annotation:
#nowarn("cat=deprecation&origin=your\.package\.YourDeprecatedClass")
You can see an example of how I used this on this commit.
As you can see, the annotation can be applied even to individual expression via type ascription, but you can also apply it to methods. In general, I like to keep my #nowarns as specific and fine-scoped as possible. You can also tell the compiler to warn you when a #nowarn is not doing anything, so that you don't have to keep unused annotations around the code.
If you have deprecated APIs that you need to use (e.g. even though they are deprecated, you still need to test them) and the usages are all over the place, you might want to evaluate the option of configuring the warning suppression as a compiler option with something like
-Wconf:cat=deprecation&origin=your\.package\.YourDeprecatedClass
passed to the compiler. Here is an example of this as well (we use Bazel instead of SBT but I'm fairly sure that setting up SBT for this is quite simple).
For any other information, the blog post I linked above is an excellent source.

What is the fundamental difference between Custom Annotations, Pluggable Annotation Processing and AOP (Aspect Oriented Programming)?

I am having difficulties to differentiate between all these annotations tools and their use cases.
My guess, although the literature is confusing, is:
Custom Annotations are meant to be used in runtime retention policy as markers to be interpreted by means of Reflection API
Annotation Processing is meant to be used in class (for static checking at compile time) and source (for source generation) retention policies
AOP is meant to modify the code at runtime
So, do Custom Annotations and AOP make sense out of Runtime Retention Policy? Do Annotation Processing make sense out of Class/Source Retention Policies? Is the difference between AOP and Custom Annotations the sole fact that the laters are passive (you need to receive the annotated object as a param in order to do something)? Why is a framework like Checker needed at all?
Annotations are just a Java syntactic means to add information to classes, methods, fields or method parameters. There are three retention types specifying where the annotations are available:
SOURCE means that the annotations are only in the source code and being discarded during compilation.
CLASS means that the annotations are in the class file and available for bytecode processing steps after compilation. This is the default value.
RUNTIME means that you can read the annotations during runtime via reflection.
Annotation processing enables you to read annotations from your source code (i.e. even those with SOURCE retention) and use that information in order to generate more source code which can later be compiled. It is a kind of pre-processing step helping you to create boiler-plate code automatically instead of typing it manually.
AOP (Aspect-Oriented Programming) is an unrelated concept, please read Wikipedia or so in order to learn more. It is not meant to modify your code but its behaviour by weaving cross-cutting concerns into your main application logic. There are several ways to implement and apply AOP, e.g.
during runtime via dynamic proxies and delegation as Spring AOP does it or
via compile-time, binary or load-time weaving, all three of which are supported by AspectJ.
Incidentally, AOP tools like Spring AOP or AspectJ can access RUNTIME annotations. It does not mean you need to use annotations in order to apply aspects to your code, they are just one of several ways to mark and identify joinpoints in your application logic where aspects should be applied.
Besides, the AspectJ compiler also supports an annotation processing step, i.e. you can for example write an annotation processor creating new aspects or application classes from your source code annotations before compilation.
In order to explain all of this in more detail I would need to write a series of articles or a book, so please take it from here by yourself and learn about these concepts by reading other websites or books.

custom annotation processing

I need a help on java custom annotations, I know how to create annotations but I do not know how to process that.
I have gone through some information where I saw APT which is com.sun.mirror.* and another javax.annotation.processing.*, I got confused between two.
Can any one please guide me to process custom annotations and provide useful link.
There is a difference between the old apt (annotation processing tool, in com.sun.mirror) and its successor, the Pluggable Annotation API (which is a part of javac since 1.6).
The new API used for processing is in javax.annotation.processing.
The API used for analysing declaration elements of the source code is the Mirror API, its package is in javax.lang.model, that API has similarities with the Reflection API.
Many sources will probably talk about apt, but are still valid for the processor tool in javac. Just the packages and the way to run the processing tool have changed.
Here is a tutorial.
If you need to process annotations at/before compile time (i.e. for producing "side files") then use apt.
OTOH if you need to process annotations at runtime then just use java reflection to get annotation on particular class.

Do Java annotations add notation or functionality?

Are Java annotations used for adding functionality to Java code besides just adding documentation about what's going on in the code? What's the most advanced/complex functionality you could add to your code through an annotation?
Annotation are basically not more than a tag (with optional additional data) on a class/method/field. Other code (libraries or tools) can discover these tags and execute functionality dependant on the annotations found. I don't see a real limit on the complexity of the functionality possibly added by annotations. This can for example emulate AOP (adding functionality before or after a method with an annotation).
Annotations as such only add information (metadata) to a class.
One can easily build a system that uses that metadata to provide additional functionality, however.
For example you can use apt to generate classes based on the information provided by the annotation.
An annotation needs a tool to react to it. If such a tool does not exist the annotation is merely a notation. The "tool" can be an APT based agent or some piece of code that uses reflection (for instance, JUnit's #Test).
Several annotations are recognized by the Java compiler and thus have pre-defined semantics: #Override, #Deprecated, #Target.
I would understand Annotations as a way to document your code in a machine readable way.
For example in Hibernate you can specify the whole persistence information for your objects as annotations. This is directly readable for you and not in a distant xml file. But is also readable for the tool to generate configurations, database schemes etc.

The drawbacks of annotation processing in Java?

I am considering starting a project which is used to generate code in Java using annotations (I won't get into specifics, as it's not really relevant). I am wondering about the validity and usefulness of the project, and something that has struck me is the dependence on the Annontation Processor Tool (apt).
What I'd like to know, as I can't speak from experience, is what are the drawbacks of using annotation processing in Java?
These could be anything, including the likes of:
it is hard to do TDD when writing the processor
it is difficult to include the processing on a build system
processing takes a long time, and it is very difficult to get it to run fast
using the annotations in an IDE requires a plugin for each, to get it to behave the same when reporting errors
These are just examples, not my opinion. I am in the process of researching if any of these are true (including asking this question ;-) )
I am sure there must be drawbacks (for instance, Qi4J specifically list not using pre-processors as an advantage) but I don't have the experience with it to tell what they are.
The ony reasonable alternative to using annotation processing is probably to create plugins for the relevant IDEs to generate the code (it would be something vaguely similar to override/implement methods feature that would generate all the signatures without method bodies). However, that step would have to be repeated each time relevant parts of the code changes, annotation processing would not, as far as I can tell.
In regards to the example given with the invasive amount of annotations, I don't envision the use needing to be anything like that, maybe a handful for any given class. That wouldn't stop it being abused of course.
I created a set of JavaBean annotations to generate property getters/setters, delegation, and interface extraction (edit: removed link; no longer supported)
Testing
Testing them can be quite trying...
I usually approach it by creating a project in eclipse with the test code and building it, then make a copy and turn off annotation processing.
I can then use Eclipse to compare the "active" test project to the "expected" copy of the project.
I don't have too many test cases yet (it's very tedious to generate so many combinations of attributes), but this is helping.
Build System
Using annotations in a build system is actually very easy. Gradle makes this incredibly simple, and using it in eclipse is just a matter of making a plugin specifying the annotation processor extension and turning on annotation processing in projects that want to use it.
I've used annotation processing in a continuous build environment, building the annotations & processor, then using it in the rest of the build. It's really pretty painless.
Processing Time
I haven't found this to be an issue - be careful of what you do in the processors. I generate a lot of code in mine and it runs fine. It's a little slower in ant.
Note that Java6 processors can run a little faster because they are part of the normal compilation process. However, I've had trouble getting them to work properly in a code generation capacity (I think much of the problem is eclipse's support and running multiple-phase compiles). For now, I stick with Java 5.
Error Processing
This is one of the best-thought-through things in the annotation API. The API has a "messenger" object that handles all errors. Each IDE provides an implementation that converts this into appropriate error messages at the right location in the code.
The only eclipse-specific thing I did was to cast the processing environment object so I could check if it was bring run as a build or for editor reconciliation. If editing, I exit. Eventually I'll change this to just do error checking at edit time so it can report errors as you type. Be careful, though -- you need to keep it really fast for use during reconciliation or editing gets sluggish.
Code Generation Gotcha
[added a little more per comments]
The annotation processor specifications state that you are not allowed to modify the class that contains the annotation. I suspect this is to simplify the processing (further rounds do not need to include the annotated classes, preventing infinite update loops as well)
You can generate other classes, however, and they recommend that approach.
I generate a superclass for all of the get/set methods and anything else I need to generate. I also have the processor verify that the annotated class extends the generated class. For example:
#Bean(...)
public class Foo extends FooGen
I generate a class in the same package with the name of the annotated class plus "Gen" and verify that the annotated class is declared to extend it.
I have seen someone use the compiler tree api to modify the annotated class -- this is against spec and I suspect they'll plug that hole at some point so it won't work.
I would recommend generating a superclass.
Overall
I'm really happy using annotation processors. Very well designed, especially looking at IDE/command-line build independence.
For now, I would recommend sticking with the Java5 annotation processors if you're doing code generation - you need to run a separate tool called apt to process them, then do the compilation.
Note that the API for Java 5 and Java 6 annotation processors is different! The Java 6 processing API is better IMHO, but I just haven't had luck with java 6 processors doing what I need yet.
When Java 7 comes out I'll give the new processing approach another shot.
Feel free to email me if you have questions. (scott#javadude.com)
Hope this helps!
I think if annotation processor then definitely use the Java 6 version of the API. That is the one which will be supported in the future. The Java 5 API was still in the in the non official com.sun.xyz namespace.
I think we will see a lot more uses of the annotation processor API in the near future. For example Hibernate is developing a processor for the new JPA 2 query related static meta model functionality. They are also developing a processor for validating Bean Validation annotations. So annotation processing is here to stay.
Tool integration is ok. The latest versions of the mainstream IDEs contain options to configure the annotation processors and integrate them into the build process. The main stream build tools also support annotation processing where maven can still cause some grief.
Testing I find a big problem though. All tests are indirect and somehow verify the end result of the annotation processing. I cannot write any simple unit tests which just assert simple methods working on TypeMirrors or other reflection based classes. The problem is that one cannot instantiate these type of classes outside the processors compilation cycle. I don't think that Sun had really testability in mind when designing the API.
One specific which would be helpful in answering the question would be as opposed to what? Not doing the project, or doing it not using annotations? And if not using annotations, what are the alternatives?
Personally, I find excessive annotations unreadable, and many times too inflexible. Take a look at this for one method on a web service to implement a vendor required WSDL:
#WebMethod(action=QBWSBean.NS+"receiveResponseXML")
#WebResult(name="receiveResponseXML"+result,targetNamespace = QBWSBean.NS)
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public int receiveResponseXML(
#WebParam(name = "ticket",targetNamespace = QBWSBean.NS) String ticket,
#WebParam(name = "response",targetNamespace = QBWSBean.NS) String response,
#WebParam(name = "hresult",targetNamespace = QBWSBean.NS) String hresult,
#WebParam(name = "message",targetNamespace = QBWSBean.NS) String message) {
I find that code highly unreadable. An XML configuration alternative isn't necessarily better, though.

Categories

Resources