How to create my own annotation, nothing works - java

I'm trying to make my own getter annotation, as it is done in lombok:
package testAnns.anns;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.FIELD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Get {
Access value() default Access.PUBLIC;
Value[] method() default {};
#Retention(RetentionPolicy.RUNTIME)
#Target({})
#interface Value {}
}
Tried to specify:
package testAnns;
import testAnns.anns.Get;
public class TestAnns {
#Get public int l = 10;
}
I try to call:
public static void main(String[] args) {
TestAnns ann = new TestAnns();
System.out.println(ann.getL()); // error
}
Nothing works.
What to do, how to be, why does not work?
Connoisseurs, please help me figure it out, I don’t understand why it doesn’t work ...

Related

Is there a way to make Java Annotation required if another is used?

I have not used java.lang.annotation much at all, but for the simplest way of metadata.
I was wondering if there is a way to setup within an annotation or a group of annotations such that if conditional A is meet, then annotation A.B or B needs to be present.
For example
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Independent {
public boolean isDependent() default false;
public String[] dependency() default "";
}
So if isDependent is set to true then the developer/user would need to set dependency with some values (ideally, what the dependency(-ies) is).
Is this possible to capture this meta-data here? If so, how to enforce it?
Restriction: Project has it's own "framework", so cannot import Spring Framework, which had some #Required annonations.
Did see this for Conditionally required property using data annotations but it's for C# not Java.
Thank you.
You can create "meta-annotations" to annotate your annotations.
For example this annotation can be used on an annotation to list the dependent annotations it requires.
package com.acme;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* If your annotation requires other annotations to be present as well,
* you can list them in your annotation declaration using this annotation.
*/
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.ANNOTATION_TYPE})
public #interface RequiresAnnotation {
String[] annotations();
}
A sample processor for this #RequiresAnnotation meta-annotation:
#Processor("com.acme.RequiresAnnotation")
void processRequiresAnnotation(Target target, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
RequiresAnnotation requiresAnnotation = target.getAnnotation()
.<RequiresAnnotation>getAnnotation(RequiresAnnotation.class);
List<String> requiredAnnotations = Arrays.asList(requiresAnnotation.annotations());
for (Element element : target.getElements()) {
debug(target.getAnnotation().getQualifiedName() + " -> " + RequiresAnnotation.class.getName()
+ " is found on [" + element.getSimpleName() + "]");
List<? extends AnnotationMirror> annos = element.getAnnotationMirrors();
List<String> foundAnnos = new LinkedList<String>();
for (AnnotationMirror anno : annos) {
String name = ((TypeElement) anno.getAnnotationType().asElement()).getQualifiedName().toString();
debug("found [" + name + "] annotation in [" + element + "]");
foundAnnos.add(name);
}
for (String required : requiredAnnotations) {
if (!foundAnnos.contains(required))
throw new RuntimeException("[" + ((TypeElement) element).getQualifiedName()
+ "] must be annotated with [" + required + "]");
}
}
}
...
void debug(Object message) {
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message.toString());
}
To use it, assume you have a MyAnnotation that requires another annotation called MyOtherAnnotation.
package com.acme;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import com.acme.RequiresAnnotation;
#Documented
#Retention(RUNTIME)
#Target(TYPE)
#RequiresAnnotation(annotations = {"com.acme.MyOtherAnnotation" })
public #interface MyAnnotation {
}
package com.acme;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Documented
#Retention(RUNTIME)
#Target(TYPE)
public #interface MyOtherAnnotation {
}
Now if you annotate a class with MyAnnotation, you will have to add MyOtherAnnotation as well:
package com.acme;
#MyAnnotation
#MyOtherAnnotation
public class MyClass {
...
}
If you don't, you will get compilation error:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project annotations-tests: Fatal error compiling: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException: [com.acme.MyClass] must be annotated with [com.acme.MyOtherAnnotation] -> [Help 1]
I have extracted the above info from a small meta-annotations library that i had written some time ago, but it is not available publicly unfortunately. I am sure there are some other libraries out there as well though..

Hibernate Custom Annotation for Local Variables

I'm trying to create a custom annotation for local variables that pretty much do the same thing as #NotNull #NotEmpty and #NotBlank. I can't use those annotations since they don't apply for local variables so I decided to make my own annotation. Here's the annotation:
import controllers.validation.validator.NotNullOrEmptyValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
#Target(LOCAL_VARIABLE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = NotNullOrEmptyValidator.class)
#ReportAsSingleViolation
public #interface NotNullOrEmpty {
String message() default "something is wrong!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Target(LOCAL_VARIABLE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#interface List {
NotNullOrEmpty[] value();
}
}
And here's the validator:
import controllers.validation.constraints.NotNullOrEmpty;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class NotNullOrEmptyValidator implements ConstraintValidator<NotNullOrEmpty, Object> {
#Override
public void initialize(NotNullOrEmpty constraintAnnotation) {
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return value!=null && !value.toString().isEmpty();
}
}
But for some reason when I try to use it like this:
public class Foo {
public void doSomething(HttpServletRequest request) {
#NotNullOrEmpty(message = "headerpiece cannot be empty or null.")
String headerPiece = request.get("something");
}
}
It just doesn't fire. I've tried passing in that something value in the header with an empty string and tried not passing it in at all but the annotation just doesn't fire and neither does the validator. What am I doing wrong here?
Make sure it is deployed as a webapp. In my case it is not firing for test cases but gets fired in actual web application.

getAnnotations method in a joinPoint is not listing one of the annotations on the method

I'm working with AOP in spring:
I have written an annotation
#Retention(RetentionPolicy.RUNTIME)
public #interface TestAnnotation {
}
And I use it on a controller method:
#ResponseBody
#TestAnnotation
#RequestMapping(method = RequestMethod.PUT, value = "/path/{variable}")
public return_type controller_call(#PathVariable String variable) {
return service.methodName(variable);
}
In the advice I have written the following code:
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getMethod().getName();
Class<?>[] parameterTypes = signature.getMethod().getParameterTypes();
Annotation[] annotations = joinPoint.getTarget().getClass().getMethod(methodName, parameterTypes).getAnnotations();
This lists the RequestMapping and the ResponseBody annotation but it doesn't list my TestAnnotation.
Any idea why??
For me this works, maybe you are doing something wrong. Probably your sample code does not really reflect your situation. I have replicated this situation in a plain Java + AspectJ setup, merely putting the Spring libs on the classpath, but not running with Spring AOP. It should be the same result with Spring AOP, though, because pointcut matching is just like in native AspectJ.
Sample annotation:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface TestAnnotation {}
Sample class with entry point:
package de.scrum_master.app;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
public class Application {
#ResponseBody
#TestAnnotation
#RequestMapping(method = RequestMethod.PUT, value = "/path/{variable}")
public String controller_call(#PathVariable String variable) {
return "dummy value";
}
public static void main(String[] args) {
new Application().controller_call("my/path");
}
}
Aspect with sample pointcut/advice:
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
#Aspect
public class MyAspect {
#Before("execution(!static * *..Application.*(..))")
public void myAdvice(JoinPoint joinPoint) throws Throwable {
System.out.println(joinPoint);
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getMethod().getName();
Class<?>[] parameterTypes = signature.getMethod().getParameterTypes();
Annotation[] annotations = joinPoint.getTarget().getClass().getMethod(methodName, parameterTypes).getAnnotations();
for (Annotation annotation : annotations)
System.out.println(annotation);
}
}
Console output:
execution(String de.scrum_master.app.Application.controller_call(String))
#org.springframework.web.bind.annotation.ResponseBody()
#de.scrum_master.app.TestAnnotation()
#org.springframework.web.bind.annotation.RequestMapping(headers=[], name=, value=[/path/{variable}], produces=[], method=[PUT], params=[], consumes=[])
I had the same problem, and the solution was to set ensure that the runtime retention policy is set and the target type is a method.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation { }

Can annotations be nested in a class in Java?

I'm trying to understand how this annotation is invoked #WebMethod
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
#WebService //<----- is this annotation nested in WebService class
#SOAPBinding(style = Style.RPC)
public interface TimeServer {
#WebMethod String getTimeAsString(); //<-------is this nested in a class too
#WebMethod long getTimeAsElapsed();
}
According to my import javax.jws.WebMethod, and to Java docs
http://docs.oracle.com/javase/7/docs/api/javax/jws/WebMethod.html
public #interface WebMethod is described.
Is the #WebMethod annotation defined as the WebMethod class?
Could the source code for the WebMethod class be something like this?
Public class WebMethod{
//members
//methods
public #interface WebMethod{ //annotation definition in class, is this possible
}
}
If this is not the case, please let me how it's done with a simple example.
No, like it says in the Javadoc you linked to, this is defined as
#Retention(value=RUNTIME)
#Target(value=METHOD)
public #interface WebMethod
So this is an annotation (#interface) that you put on a method (#Target(value=METHOD)).
#WebMethod is not "nested" into #WebService, those are two independent annotations (but of course, they work in concert). That one goes on a method and the other on a class is defined by the #Target.
While that does not seems to be the case in this specific instance I ran across the same question of whether inner annotations where ok.
According to my experiments this works
Annotation definition:
// pkg2/I.java
package pkg2;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
public final class I {
private I(){
}
#Retention(RetentionPolicy.RUNTIME)
public static #interface Inner {}
}
Usage:
// pkg/C.java
package pkg;
import pkg2.I;
#I.Inner
public class C {
public static void main(String[] args) throws Exception {
System.out.println(C.class.getAnnotation(I.Inner.class));
}
}
Running:
$ java pkg/C
#pkg2.I$Inner()

Reflection getAnnotations() returns null

Searchable.java
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Searchable { }
Obj.java
public class Obj {
#Searchable
String myField;
}
void main(String[] args)
Annotation[] annotations = Obj.class.getDeclaredField("myField").getAnnotations();
I would expect annotations to be containing my #Searchable. Though it is null. According to documentation, this method:
Returns all annotations present on this element. (Returns an array of length zero if this element has no annotations.) The caller of this method is free to modify the returned array; it will have no effect on the arrays returned to other callers.
Which is even more weird (to me), since it returns null instead of Annotation[0].
What am I doing wrong here and more important, how will I be able to get my Annotation?
I just tested this for you, and it just works:
public class StackOverflowTest {
#Test
public void testName() throws Exception {
Annotation[] annotations = Obj.class.getDeclaredField("myField").getAnnotations();
System.out.println(annotations[0]);
}
}
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface Searchable {
}
class Obj {
#Searchable
String myField;
}
I ran it, and it produces the following output:
#nl.jworks.stackoverflow.Searchable()
Can you try running the above class in your IDE? I tried it with IntelliJ, openjdk-6.
Your code is correct. The problem is somewhere else. I just copied and run your code and it works.
It is possible that you are importing the wrong Obj class in your code you may want to check that first.
In my case, i had forgotten to add
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
to the method, so in the end it should look like:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
}
In my case, the error was in my own annotation.
I fixed a couple of things, and it finally ended up like this:
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Target( { METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
public #interface MyAnnotation{
}
It works now

Categories