Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Consistency of processing environment members across compilation rounds

The Java Annotation Processing API as it stands in JDK 6 and 7 (not the apt tool API from 5) defines the lifecycle of an annotation processor. One is instantiated via the no-args constructor, the init method is called with a ProcessingEnvironment instance and then that processor is used via the process method. Only one instance of the processor is made and used throughout all subsequent processing rounds if applicable.

Since my processor class was getting a bit bloated, I've created handler classes for separate annotations it's supposed to deal with. In the methods of those classes I'm always passing in Elements and Types instances that I've obtained from the ProcessingEnvironment for their utility methods. This is making my method signatures rather long.

I'd rather just keep a reference to the Elements and Types instances in the handlers, as well as in the processor instance. I've done this by getting them from the ProcessingEnvironment passed into the init method. Now, I'm wondering if this is safe. The JavaDoc for Processor makes it clear that some methods are only called once for a processor, but init isn't mentioned here. I was kind of assuming this is implicitly understood, but I'm not 100% certain.

I'd also like to know if the Messager instance, which can be obtained from ProcessingEnvironment as well, remains the same across all processing rounds. I'd rather not have warnings/errors from one round showing up but others getting left out. I'm reasonably certain that it should be safe to use the same instances across rounds, but just would like some certainty.

like image 510
G_H Avatar asked Oct 16 '25 21:10

G_H


2 Answers

I've asked myself the same question and decided to always use the current round's ProcessingEnvironment's utilities provided by init. There doesn't seem to be any difference when using javac, but there are other annotation processing tools which may show different behaviour. I've already experienced a few differences between the processing tool in javac and the one used by eclipse, so I handle anything not explicit in the documentation with great care. The question is, do you want test all existing processing tools?

Also, I think if those processing helper tools were meant to never change, they would be arguments of the processor's constructor.

like image 95
kapex Avatar answered Oct 18 '25 09:10

kapex


It may be worth mentioning that the AbstractProcessor (which is suggested by the javadoc of the Process class as a subclass which could be used by implementors) throws IllegalStateException if its init method is called more than once.

It does not mean the passed ProcessingEnvironment could not return different values on subsequent getter calls or in different rounds although it would not be conventional. Anyway, it could be worth checking at the beginning of the process method:

private ProcessingEnvironment processingEnv;
private Elements elementUtils;
private Types typeUtils;

public Processor() {
}

@Override
public synchronized void init(final ProcessingEnvironment processingEnv) {
    this.processingEnv = processingEnv;
    elementUtils = processingEnv.getElementUtils();
    typeUtils = processingEnv.getTypeUtils();
}

private void checkEnvironmentChange() {
    checkSame(elementUtils, processingEnv.getElementUtils(), "elementUtils");
    checkSame(typeUtils, processingEnv.getTypeUtils(), "typeUtils");
}

private <T> void checkSame(final T object1, final T object2, final String name) {
    if (object1 != object2) {
        throw new IllegalStateException(name + " should not change");
    }
}

@Override
public boolean process(final Set<? extends TypeElement> annotations, 
        final RoundEnvironment roundEnv) {
    checkEnvironmentChange();
    ...
}
like image 44
palacsint Avatar answered Oct 18 '25 09:10

palacsint



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!