How to get custom annotation from source code in java? - java

I'm trying to use a custom annotation to get some statistics for unit test. Another guy has defined a custom annotation as follows:
#Retention(RetentionPolicy.SOURCE)
#Target( { ElementType.METHOD, ElementType.TYPE})
public #interface TimsID {
String id();
String description() default "";
}
What I need to do is extracting this annotation from all unit tests in our project.
Here comes the problem:
the RetentionPolicy is defined as SOURCE, I don't know how to get it in the unit test.
I know that if it's a RUNTIME, it may be read reflectively like this:
Class<TestExample> obj = TestExample.class;
// Process #TimsID
if (obj.isAnnotationPresent(TimsID.class)) {
Annotation annotation = obj.getAnnotation(TimsID.class);
TimsID TimsID = (TimsID) annotation;
}
But now it's 'SOURCE', annotations will not be recorded in the class file by the compiler or retained by the VM at run time, so they can't be read reflectively.
The guy who defined the custom annotation said the reason he chooses "SOURCE" is that we just need to statistic this annotation in source code, we don't need to write these custom annotations in class file or even runtime, so we need annotation analysis only in source code.
I've accomplished this work, and here is the step and code.

SOURCE retention is aimed to be used only during compilation process. You may look into APT (Annotation Processing Tool) for more information on how to perform such kind of compile-time annotation processing logic. (However I wonder if it can do what you want)

You'll have to change the RetentionPolicy in the source code, unfortunately. There's no other way to make the annotation available for reflection at runtime, even in tests.

Related

Understanding the use of 'Annotations'

I'm trying to understand the use of 'Annotations' a bit better.
I understand that:
How to access annotations in my code for example via this complete tutorial.
I can create methods to perform desired operations
To understand this better, I created a virtual problem as following:
There are Annotations TestAnnotation1, TestAnnotation2, TestAnnotation3(definition is available latter in the question). I wants to execute the methods of class MethodsExecutorClass as following:
When TestClass.java compiles then execute CommonMethod() and RetentionPolicySOURCEMethod()
When TestClass.class loads then execute CommonMethod() and RetentionPolicyCLASSMethod()
Whenever testMethod() method of TestClass.java executes then execute CommonMethod() and RetentionPolicyRUNTIMEMethod()
By this example I wants to understand following:
Can I instruct Java compiler (javac) or Java Runtime Environment (jvm) to execute a method in my class(e.g. CommonMethod()andRetentionPolicySOURCEMethod()methods ofMethodsExecutorClass`).
Can I delegate the monitoring (i.e. searching the methods/classes which are using my annotation etc.) to any other entity(which is available in Java SE).
I want to do something like #Override and #deprecated annotations. We don't do something extra. Although on Oracle javadoc site, here it is clearly mentioned that The Java platform has always had various ad hoc annotation mechanisms. and #deprecated is one of them. But I wondered If I can do something like this.
Definitions should look like as following:
MyAnnotations.java:
#Retention(RetentionPolicy.SOURCE)
public #interface TestAnnotation1
{
String className();
}
#Retention(RetentionPolicy.CLASS)
public #interface TestAnnotation2
{
String className();
}
#Retention(RetentionPolicy.RUNTIME)
public #interface TestAnnotation3
{
String className();
String methodName();
}
MethodsExecutorClass.java:
class MethodsExecutorClass
{
public static void CommonMethod()
{
System.out.println("In method: CommonMethod()");
}
public void RetentionPolicySOURCEMethod()
{
System.out.println("In method: RetentionPolicySOURCEMethod()");
//Also print annotation arguments e.g. Class name etc
}
public void RetentionPolicyCLASSMethod()
{
System.out.println("In method: RetentionPolicyCLASSMethod()");
//Also print annotation arguments e.g. Class name etc
}
public void RetentionPolicyRUNTIMEMethod()
{
System.out.println("In method: RetentionPolicyRUNTIMEMethod()");
//Also print annotation arguments e.g. Class name etc
}
}
TestClass.java:
#TestAnnotation1(TestClass.class)
#TestAnnotation2(TestClass.class)
class TestClass
{
#TestAnnotation2(TestClass.class, "testMethod()")
public void testMethod()
{
System.out.println("In method: testMethod()");
}
}
May you help me in achieving this? (Please no guess or assumptions, but presumptions would be also helpful).
I'm not sure if this can be achieve, but looking forward.
Annotations with retention policy RetentionPolicy.SOURCE are only available during compilation time of the code so your compiler should support your annotation to use it, otherwise it's not possible to handle the annotation. Usually, such annotations are used to detect possible problems at compilation time, for example, annotation #Override. That's why your first problem can't be implemented in usual ways.
Annotations with retention policy RetentionPolicy.CLASS are available only in .class files and can be used via JVMs. Please see this answer how it can be used. The second your problem also can't be implemented via standard ways.
Commonly used annotations are with retention policy RetentionPolicy.RUNTIME that are available via reflection mechanism in Java. But to solve your third problem you have to use some method invocation interceptors, for example, via Aspect Oriented Programming. After that you can get method's annotations via method.getDeclaredAnnotations().
Can I instruct Java compiler (javac) or Java Runtime Environment (jvm)
to execute a method in my class(e.g.
CommonMethod()andRetentionPolicySOURCEMethod()methods
ofMethodsExecutorClass`).
No, you can't.
Can I delegate the monitoring (i.e. searching the methods/classes
which are using my annotation etc.) to any other entity(which is
available in Java SE).
You can do it via AOP, for example, use the library AspectJ.

execute some code before and after of any method execution only by giving my custom annotation in java

I am trying to write a code for custom annotation. when I use this annotation on any method, then before execution and after execution of method some simple print msg should execute. I tried like this :
import java.lang.annotation.*;
#Retention(RetentionPolicy.RUNTIME)
#interface DemoAnnotation {
String value();
String value1();
}
// Applying annotation
class CustomAnnotationExample {
#DemoAnnotation(value = "code is started!!!", value1= "code is completed!!!")
public void sayHello() {
System.out.println("hello Annotation Example");
}
}
and in another main method I called it like :
CustomAnnotationExample h=new CustomAnnotationExample();
Method m=h.getClass().getMethod("sayHello");
DemoAnnotation anno=m.getAnnotation(DemoAnnotation.class);
System.out.println(anno.value());
h.sayHello();
System.out.println(anno.value1());
I want to print values from annotation without using System.out.println() in main method . when I just call sayHello() method . annotation values should get printed before and after execution of sayHello() method.
Please help me on this.
There are two ways, both of them very complex, runtime and compile time solution:
The runtime solution relies on specific framework which is used to instantiate the application. The common way is to create wrapping proxies for the final object and do the stuff from the proxy before (or after) calling the original object method.
For spring for example the solution is to register BeanPostProcessor object which would intercept the instantiation of the bean and check whether some of the method contains the DemoAnnotation annotation. In case it does, it would create a proxy to that object and return the proxy as the real bean.
Second solution is compile time solution and is based on annotation processors which can modify the java compiler behavior. You need to create and register annotation processor and after parsing the source file checking the annotations on the method and add the relevant code during the compilation time. There are many helpers, you can for example register on TreeScanner.visitMethod() method and invoke the TreeScanner from your annotation processor.
Generally the good example can be found in lombok which does similar things in terms of modifying the code during the compile time.

How can I make annotations more semantic?

I am writing a library, so I don't often use the methods in my classes within the same project. As such, my IDE (IntelliJ IDEA) keeps warning me that the methods are unused.
Of course, the obvious solution is to place #SuppressWarnings("unused") before the classes. I don't like this; it doesn't describe the reason I'm writing that annotation and is very verbose. I would love to make an annotation like #LibraryClass which is just an alias of #SuppressWarnings("unused").
In short, I want to be able to change this:
#SuppressWarnings("unused")
public class MyLibraryClass {
public void myLibraryMethod() {
doSomething();
}
}
to this:
#LibraryClass
public class MyLibraryClass {
public void myLibraryMethod() {
doSomething();
}
}
but I have no idea how to do this! I tried all this, and it compiles, but the IDE still warns of unused methods:
#SuppressWarnings("unused")
#Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
#Retention(RetentionPolicy.SOURCE)
public #interface Library {
SuppressWarnings superAnnotation() default #SuppressWarnings("unused");
String[] value() default {"unused"};
}
To do one aspect of what you're asking for - attaching some kind of compile-time logic to annotations - you need to look into annotation processing. An annotation processor hooks into the Java runtime, like an agent, and gets informed about annotations and given an option to process it. To use that, you'd have to put your annotation-processor jar on the IDE's classpath.
Some links:
http://hannesdorfmann.com/annotation-processing/annotationprocessing101
http://programmaticallyspeaking.com/playing-with-java-annotation-processing.html
However, that wouldn't allow you to change the way that Intellij detects unused methods, which seems to be closer to your specific use case. What you could do there is to modify the Intellij 'unused method' inspection so that it incorporates a check for the custom annotation you've defined. YMMV, I've never had to do that at the class level before.
https://gist.github.com/itzg/5e90609cde1473ef9d4d

Annotation with multiple parameters with conditional default value

I'm creating a custom annotation named #Skip as shown below.
#Retention (RetentionPolicy.RUNTIME)
#Target ({ElementType.TYPE, ElementType.METHOD})
#Inherited
public #interface Skip {
public String comment() default "";
public String bug() default "";
}
Is it possible to set conditional default value on the comment and bug? What I'm trying to achieve is, if comment is provided, bug must be optional, if bug is provided comment is optional. I can do this check at run-time, but I want to find out if we can do it at compile time. This way, eclipse will show a compilation error if at least one of these is not provided by the developer.
You could check these things at compilation time using an annotation processor.
Have a look at the Javadoc for a starting point.
If you package the processor in the same jar as the annotation and register the processor as a service, it will be exucted automagically when compiling an annotated class (must be in a different jar).

How can I create an annotation processor that processes a Local Variable?

I'm trying to create an annotation for a local variable. I know that I can't retain the annotation in the generated bytecode, but I should be able to have access to the information at compile time by doing something like this:
#Target({ElementType.LOCAL_VARIABLE})
#Retention(RetentionPolicy.SOURCE)
public #interface Junk {
String value();
}
only, this doesn't get processed by apt, or javac when I specify a ProcessorFactory that has "Junk" in it's supported types in the following:
class JunkTester {
public static void main(String[] args) {
#Junk String tmp = "Hello World";
System.out.println(tmp);
}
}
It will however work when I move the #Junk annotation before public static
Thoughts and or workarounds?
Did some quick tests and searched a little, and it's looking like hooking into LOCAL_VARIABLE isn't really supported...yet:
http://forums.sun.com/thread.jspa?threadID=775449
http://www.cs.rice.edu/~mgricken/research/laptjavac/
https://checkerframework.org/jsr308/
Could be totally wrong, but that's how it's looking...
It seems that the Type Annotations Specification (JSR 308), will hopefully address this subject in the future (JDK 8 ?).
As of Java 8, local variable annotations are stored in the classfile.
A standard Java annotation processor does not process the bodies of methods.
However, the Checker Framework enables you to write an annotation processor that processes every annotation including on local variables. Its annotation processors can even examine every statement of the program, whether annotated or not.

Categories

Resources