Custom Annotation processor is not being invoked by tomcat. Following is the Annotation processor code that I am using :
#SuppressWarnings("restriction")
#SupportedAnnotationTypes("io.strati.rs.cxf2.bindings.MyAnnotation")
#SupportedSourceVersion( SourceVersion.RELEASE_8 )
public class SimpleAnnotationProcessor extends AbstractProcessor {
public static List<String> str = new ArrayList<>();
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.out.println("Into annotation processor ... 2");
for ( Element element : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {
System.out.println("Method name is:"+element.getSimpleName());
str.add(element.getSimpleName().toString());
}
return false;
}
}
This stores the method name of all the methods that have the custom annotation. This is what the annotation class looks like :
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(value = RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
}
I am trying to access the list in a tomcat application as follows :
#GET
#Path("/dummy")
#MyAnnotation
#Produces({APPLICATION_JSON, APPLICATION_XML, TEXT_XML})
public void print(){
System.out.println("List is:"+SimpleAnnotationProcessor.str);
}
The list is getting printed as empty even though the method has the annotation.I have specified the annotation in the maven compiler plugin as well as specified it in META-INF/services/javax.annotation.processing.Processor. Can someone tell me what are the possible reasons due to which the annotation processor is not getting invoked ?
I doubt Tomcat has anything to do with it. Annotation processing takes place at compile time and is often used to generate or modify code. An annotation processor can be seen as a compiler plugin.
https://www.javacodegeeks.com/2015/09/java-annotation-processors.html
Following the retention policy, the annotation is going to be retained by Java compiler in the class file during the compilation phase however it will not be (and should not be) available at runtime.
It's likely the annotation processor is in fact adding the print() method name to the list (check the build output), but again this only happens when compiling the code.
The deployed web service at runtime is never going to see the list filled by the processor at compile time, those are completely different environments.
Related
I have to prevent using specific annotations in my project. Due to some specific circumstances, I have to prevent using org.junit.After annotation in project test classes. If anyone uses this annotation, whole tests logic will be violated.
It doesn't matter for me if it will happen during compilation or at runtime. My idea was to create aspect using Spring AOP which throws an exception on call After annotated method. Unfortunately, test classes are not Spring beans so AOP will not work here.
How can I do that? Is it possible to put some compilator directives which will prohibit using specific annotation?
You can write an AnnotationProcessor to generate error in complie period
Here is the annotation processor (find code on github):
#AutoService(Processor.class)
#SupportedAnnotation(After.class)
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public class Q49706464 extends XAbstractProcessor {
#Override
public boolean processActual(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws AssertException {
roundEnv.getElementsAnnotatedWith(After.class)
.forEach(e -> error().log("#After is banned.", e));
return false;
}
}
For the following java file
import org.junit.After;
public class ForQ49706464 {
#After
public void after() {
}
}
You will get following compile error
ForQ49706464.java:7: error: #After is banned.
public void after() {
^
And you can integrate the annotation processor in your IDE to see the error message in text editor directly.
The XAbstractProcessor is from my library Annotation Processor Toolkit
There are many internal Java annotations like SuppressWarning, FunctionalInterface, etc. which can limit the members of the class with the annotation, expand classes or even specify compiler options, but how can a normal programmer compose such annotations?
I searched on the annotation topics and all I could find is adding some meta values to the annotation like this, and how to use annotations, but nothing I can find that explains how to implement advanced annotations. Any directions would be helpful.
What you are looking for is compile-time annotation .
Basically, annotation processing can be done based on its RetentionPolicy. As per the Java docs, there are 3 type of RetentionPolicy -
CLASS Annotations are to be recorded in the class file by the compiler
but need not be retained by the VM at run time.
RUNTIME Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
SOURCE Annotations are to be discarded by the compiler.
Compile-time annotation processing is related to RetentionPolicy.SOURCE because you want to process on source file at the time of compilation similar to other annotation like #Override.
Below is one example of a simple compile-time annotation processor -
Create Maven Project - Annotation_Compile_Time -
(A) Create a compile-time Annotation MyAnnotation in this project -
package xxx;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Documented
#Target(ElementType.TYPE)
#Inherited
#Retention(RetentionPolicy.SOURCE)
public #interface MyAnnotation {
}
(B) Create a annotation Processor MyAnnotationProcessor -
package xxx;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
#SupportedAnnotationTypes("xxx.MyAnnotation ")
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
public MyAnnotationProcessor () {
super();
}
#Override
public boolean process(Set<? extends TypeElement> typeElementSet, RoundEnvironment roundEnv) {
for (Element e : roundEnv.getRootElements()) {
String className = e.toString();
String message = "Annotated class - " + className;
System.out.println(message);
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message);
}
return false;
}
}
(C) Create javax.annotation.processing.Processor file in directory - src/main/resources/META-INF/services with below content -
xxx.MyAnnotationProcessor
(D) Update pom.xml with build configuration -
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<verbose>true</verbose>
<fork>true</fork>
<compilerArgument>-proc:none</compilerArgument>
</configuration>
</plugin>
</plugins>
</build>
(E) Compile and install this project using mvn clean install.
Create Another Maven Project - Annotation_User - This project will use annotation defined in above project. Create 2 source files in this project annotated with this annotation
(A) AnnotationUser1 -
package xxx.consumer;
import xxx.MyAnnotation;
#MyAnnotation
public class AnnotationUser1 {
private String message;
public AnnotationUser1(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
(B) AnnotationUser2 -
package xxx.consumer;
import xxx.MyAnnotation;
#MyAnnotation
public class AnnotationUser2 {
private String message;
public AnnotationUser1(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
(C) Update pom.xml with Annotation project dependency -
<dependency>
<groupId>xxx</groupId>
<artifactId>Annotation_Compile_Time</artifactId>
<version>1.0</version>
</dependency>
Now, whenever, you will compile this project using mvn clean compile, your annotation processor defined in project 1 will get called. Currently, processor is just printing the name of the classes annotated with this annotation.
You can also refer to this page for details.
Next step comes is to analyse the source file and calculate no. of methods. Since, it is compile time processing, so you can not use Reflection API for getting no. of methods. One solution is to use Eclipse AST for parsing source file and calculating no. of methods or you can write your own logic.
Project lombok is mainly based on Compile-time annotation processing. If you want to do something useful, it would be better to study Project lombok source code
I think you need annotation processing:
API documentation - https://docs.oracle.com/javase/8/docs/api/javax/annotation/processing/Processor.html
Pretty good article - https://www.javacodegeeks.com/2015/09/java-annotation-processors.html
Also you can read sources of lombok (which is all about annotation processing): https://github.com/rzwitserloot/lombok
UPD:
For some cases you can write javaagent instead which is much better at least because it is at least has some tools like bytebuddy (which is great IMHO)
I am sharing how I made a custom annotation to resolve an issue in my app.
Problem:
I was using Spring AOP to do the logging in my app. To people new to AOP, what it did in simple words is instead of writing logger.log() in every method and class, you can tell AOP to do something (in my case logging) after/before/after-and-before each method. Now the problem is since every method is going to get logged, how do I prevent a certain method(like authentication) or parameter(like password) from getting logged.
So to do this, I created an annotation SkipLogging
#Target(value = { ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER })
#Retention(value = RetentionPolicy.RUNTIME)
public #interface SkipLogging {
}
and in my AOP class, I put in a condition that if any thing has this annotation, AOP should not do logging on that. Maybe following (partial) code will make more sense:
#Around("within(com.test..*)
public Object logAround(final ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// Log only if class/method is not annotated with SkipLogging
if (!(signature.getDeclaringType().isAnnotationPresent(SkipLogging.class)
|| method.isAnnotationPresent(SkipLogging.class))) {
try {
// Log before method entry
logger.info("");
Object result = joinPoint.proceed();
// Log after method exit
logger.info("");
return result;
} catch (Exception e) {
// Log after exception
logger.error("");
throw e;
}
} else {
return joinPoint.proceed();
}
}
Without going into much detail, look at the condition:
if (!(signature.getDeclaringType().isAnnotationPresent(SkipLogging.class)
|| method.isAnnotationPresent(SkipLogging.class
)))
which prevents classes and methods annotated with SkipLogging from getting logged. Similarly, I had written some code for putting this annotation on parameters and skipping them.
In the next steps, I had created annotations like #DebugLogging, #ErrorLogging etc. and put in a check in my AOP, to write debug log or error log based on the annotation present.
Is it possible to force a annotation processor to RERUN after all other processors have generated their code? Basically what I'm running into right now is that Android Databinding is generating a type parameter for one of my classes causing the inherited class from its super not to propagate properly on the first pass of my processor. Any help would be much appreciated!
Thanks
Annotation:
#Inherited
#Retention(RetentionPolicy.CLASS)
#Target(ElementType.TYPE)
public #interface ContainsViewModel{
}
Example:
#ContainsViewModel
public class MyActivity extends ViewModelActivity<MyViewModel, GeneratedClass>{
//The GeneratedClass in this case is a SubClass of ViewDataBinding for the Android databinding library
#InjectHere
public void injectMethod(MyViewModel injected){
}
}
In the above example, the GeneratedClass will cause this class to never show up in
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
roundEnv.getElementsAnnotatedWith(ViewModelView.class)// does not have MyActivity
}
This seems to be because the GeneratedClass never gets generated until the last pass of my process. I can tell by doing,
roundEnv.getRootElements()
However, when I finally see the GeneratedClass show up, the roundEnv refuses to provide any elements with my ContainsViewModel annotation. Any ideas as to go about dealing with this? Thanks.
I have created my own annotation type like this:
public #interface NewAnnotationType {}
and attached it to a class:
#NewAnnotationType
public class NewClass {
public void DoSomething() {}
}
and I tried to get the class annotation via reflection like this :
Class newClass = NewClass.class;
for (Annotation annotation : newClass.getDeclaredAnnotations()) {
System.out.println(annotation.toString());
}
but it's not printing anything. What am I doing wrong?
The default retention policy is RetentionPolicy.CLASS which means that, by default, annotation information is not retained at runtime:
Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time. This is the default behavior.
Instead, use RetentionPolicy.RUNTIME:
Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
...which you specify using the #Retention meta-annotation:
#Retention(RetentionPolicy.RUNTIME)
public #interface NewAnnotationType {
}
Having the default Retention of an annotation does not mean that you can not read it at run-time.
Since
Annotations are to be recorded in the class file by the compiler
but need not be retained by the VM at run time. This is the default behavior.
It is possible to access them reading the .class file directly
This can be accomplished by using the ASM library (handling some corner cases, of course).
Check out its excellent User guide. In particular section 4.2 Annotations.
You may want to refer to the Spring framework's handling of such annotations (it uses shaded asm dependency):
SimpleAnnotationMetadataReadingVisitor
AnnotationMetadataReadingVisitor (deprecated)
For the following custom Java annotation
#CustomAnnotation(clazz=SomeClass.class)
public class MyApplicationCode
{
...
}
I basically want to be able to grab both the Class object for the MyApplicationCode and the clazz parameter at compile time to confirm some coding convention consistencies (another story). Basically I want to be able to access MyApplicationCode.class and Someclass.class code in the annotation processor. I'm almost there but I'm missing something. I have
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.SOURCE)
public #interface CustomAnnotation
{
public Class clazz();
}
Then I have for the processor:
public class CustomAnnotationProcessor extends AbstractProcessor
{
private ProcessingEnvironment processingEnvironment;
#Override
public synchronized void init(ProcessingEnvironment processingEnvironment)
{
this.processingEnvironment = processingEnvironment;
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment environment)
{
Set<? extends Element> elements = environment.getElementsAnnotatedWith(ActionCommand.class);
for(Element e : elements)
{
Annotation annotation = e.getAnnotation(CustomAnnotation.class);
Class clazz = ((CustomAnnotation)annotation).clazz();
// How do I get the actual CustomAnnotation clazz?
// When I try to do clazz.getName() I get the following ERROR:
// Attempt to access Class object for TypeMirror SomeClass
// Also, how do I get the Class object for the class that has the annotation within it?
// In other words, how do I get MyApplicationCode.class?
}
}
}
So what I'm trying to do in the process method is to grab SomeClass.class and MyApplication.class from the original code below to do some custom validation at compile time. I can't seem for the life of me figure out how to get those two values...
#CustomAnnotation(clazz=SomeClass.class)
public class MyApplicationCode
Update: The following post has a lot more details, and it's much closer. But the problem is that you still end up with a TypeMirror object from which to pull the class object from, which it doesn't explain: http://blog.retep.org/2009/02/13/getting-class-values-from-annotations-in-an-annotationprocessor/
Update2: You can get MyApplication.class by doing
String classname = ((TypeElement)e).getQualifiedName().toString();
I was going to point you in the direction of the blog http://blog.retep.org/2009/02/13/getting-class-values-from-annotations-in-an-annotationprocessor/, but it looks like you already found that one.
I see you figured out how to access the MyApplication Element, so I wont cover that....
The exception you see actually contains the type of the annotation property within it. So you can reference the annotation clazz value when you catch the exception:
public class CustomAnnotationProcessor extends AbstractProcessor
{
private ProcessingEnvironment processingEnvironment;
#Override
public synchronized void init(ProcessingEnvironment processingEnvironment)
{
this.processingEnvironment = processingEnvironment;
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment environment)
{
Set<? extends Element> elements = environment.getElementsAnnotatedWith(ActionCommand.class);
for(Element e : elements)
{
CustomAnnotation annotation = e.getAnnotation(CustomAnnotation.class);
TypeMirror clazzType = null;
try {
annotation.clazz();
} catch (MirroredTypeException mte) {
clazzType = mte.getTypeMirror();
}
System.out.println(clazzType); // should print out SomeClass
}
}
}
Yes, this is a total hack of a solution, and I'm not sure why the API developers decided to go this direction with the annotation processor feature. However, I have seen a number of people implement this (including myself), and the article mentioned describes this technique as well. This seems to be an acceptable solution at the moment.
In terms of "grabbing" the class values for MyApplicationCode and SomeClass, you will not be able to do so if they are classes being compiled. You can, however, use the Element and TypeMirror representations to perform some high level validation on your classes (Method, Field, Class names, annotations present, etc)
After reading this related SO question, I found this excellent page about the Java Annotation Processing Tool (APT). It's from 2005 so may not be the best way to do this these days.
APT [...] is an annotation processing tool for Java. More specificially, APT allows you to plug code in to handle annotations in a source file as the code compilation is occurring - and in that process, you can emit notes, warnings, and errors.
More information about APT in Java 6 from Oracle's docs.
Interesting blog post from someone at Oracle about APT.
Another example usage of APT -- this time from 2009.
This is only for Oracle's JDK.
It is compile time. I would think the compiler is not even finished up compiling the source code. You retrieve such information from AnnotatedElement instance which will give you relevant information of the type you have annotated, but not its runtime properties, thats not yet available since the relevant class files are not yet loaded by the virtual machine. And the compiler is not even guaranteed to be running under a virtual machine for java, so it is not mandated to be able to load class files. Its requirement is only to be able to produce bytecodes that any particular virtual machine can read.
So go check on the mirror Api, and for any relevant information on the class/method/field you have annotated, check on AnnotatedElement representing that instance.
And as aside note: this is information is just what i reasoned up, so it might not be the actual truth.