I have a custom annotation which is declared as below and have some implementation for this.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface SampleTestCase {
public int caseID() default -1;
public int suiteId() default -1;
}
Now, I am trying to use this annotation and trying to send runtime parameters to it.
ConfigHelper config = new ConfigHelper();
int caseId = config.getTestCaseID();
#SampleTestCase(caseID=caseId,suiteId="Test")
public void testCaseOne(){
Assert.assertTrue(true);
}
Getting an error as "The value for annotation attribute TivoTestCase.caseID must be a constant expression".
Is there any way to pass dynamic parameters to an annotation other than this way??
Related
Is it possible, when using custom oval annotation and custom class for check, to access the annotation and retrieve the used annotation attributes ?
Reference for oval: https://sebthom.github.io/oval/USERGUIDE.html#custom-constraint-annotations
Minimal example
Lets assume we have class Foo.
It has two annotated fields.
Each time, the annotation has a different myValue – a and b.
class Foo {
#CustomAnnotation(myValue = "a")
public String first;
#CustomAnnotation(myValue = "b")
public String second;
}
This is the annotation.
It is noted that a check should be performed using MyCheck.class, also setting some default value for myValue.
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
#Constraint(checkWith = MyCheck.class)
public #interface CustomAnnotation {
String myValue() default "";
}
Now we want to use oval to validate this field.
Most importantly, we want to extract the value a or b from the annotation's myValue and use it inside our validation logic.
public class MyCheck extends AbstractAnnotationCheck<CustomAnnotation> {
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
// how to get the value of `myValue`, which is `a` or `b` or empty string as default
}
}
What I have tried and failed:
validatedObject is Foo.class. You can easily get its fields and annotations. However, there is no way to differentiate between the two annotations.
valueToValidate is in this case String value – what first or second holds.
context not useful, you can get compile time type from it, which is String
validator not useful ?
After some digging in the superclass I have found that you can override method
configure
This method gets as the only parameter the annotation that is currently being checked at the field.
You can then read the myValue.
public class MyCheck extends AbstractAnnotationCheck<CustomAnnotation> {
private String myValue;
#Override
public void configure(CustomAnnotation customAnnotation) {
super.configure(customAnnotation);
this.myValue = customAnnotation.myValue();
}
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
if (myValue.equals("a")) {}
else if (myValue.equals("b")){}
else {}
}
My annotation:
#Target({ElementType.TYPE, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface ObjectName {
String name() default "";
String field() default "";
}
Some class with my annotation
#ObjectName("a_")
public class A {
#ObjectName("field_")
String filed;
}
Problem - when i get all my "ObjectName" annotations from class above, how can i get annotation's ElementType value (field, class or method type)?
So i want something like this
public void process(Class<?> clazz) {
Annotation[] annotations = clazz.getAnnotations();
for (Annotation anno : annotations) {
if (anno instanceof ObjectName) {
ObjectName annObjName = (ObjectName) anno;
Target target = anno.getAnnotation(Target.class);
if (target.getType().equals(ElementType.TYPE)
doThat(annObjName.name());
else if (target.getType().equals(ElementType.FIELD)
doThis(annObjName.field());
}
}
}
Can i even do this?
How can i do this or how can i find out if this annotation declared on filed or class?
You can't.
All you can do is look at where you call getAnnotations(), because you seem to incorrectly believe that clazz.getAnnotations() will return all annotations on everything in the class. That is false. When you call clazz.getAnnotations(), you will only get the annotations directly on the class. To get annotations on fields, you must call clazz.getFields(), and then call getAnnotations() on the Field elements. So there's no risk of getting them mixed up as long as you keep those straight.
I am trying to declare custom annotation in following way:
Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface InnerAnnotation {
}
Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface OuterAnnotation {
public String default "";
public InnerAnnotation innerAnnotation(); //here I wanted to do "public InnerAnnotation innerAnnotation() default {some default value}"
}
I wanted to use it in a way:
class first{
#OuterAnnotation(value = "new") //wanted to declare something like this without need to define innerAnnotation
public void func(){
}
}
I wanted to assign some default value to inner annotation usage(so that I don't have to provide any mandatory value while using it), but some how I am not able to do that as compiler asks for compile time constant for this.Can any please suggest how to use inner annotation with any default value ?
The syntax for what you what is as follows:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface OuterAnnotation {
public String default "";
public InnerAnnotation innerAnnotation() default #InnerAnnotation(); //this does the trick;
}
my custom annotation is:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CacheClear {
long versionId() default 0;
}
I want to achieve something like this, in which I can pass the method param "versionTo" to my custom annotation.
#CacheClear(versionId = {versionTo})
public int importByVersionId(Long versionTo){
......
}
What should I do?
That's not possible.
Annotations require constant values and a method parameter is dynamic.
You cannot pass the value, but you can pass the path of that variable in Spring Expression and use AOP's JoinPoint and Reflection to get and use it. Refer below:
Your Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CacheClear {
String pathToVersionId() default 0;
}
Annotation Usage:
#CacheClear(pathToVersionId = "[0]")
public int importByVersionId(Long versionTo){
......
}
Aspect Class:
#Component
#Aspect
public class YourAspect {
#Before ("#annotation(cacheClear)")
public void preAuthorize(JoinPoint joinPoint, CacheClear cacheClear) {
Object[] args = joinPoint.getArgs();
ExpressionParser elParser = new SpelExpressionParser();
Expression expression = elParser.parseExpression(cacheClear.pathToVersionId());
Long versionId = (Long) expression.getValue(args);
// Do whatever you want to do with versionId
}
}
Hope this helps someone who wants to do something similar.
My query is this. Say, i have a custom annotation as follows:
//rest of the code left out for the sake of brevity
interface #Name{
String myName();
}
Now, in the class where am using this annotation on say, a field or a method, i want to pass a value to "myName" from a property file, something like this:
#Name(myName="${read.my.name}")
public void something(){}
Could anyone please suggest how can i read the value passed to 'myName' in my annotation-processor from the property file? I have read a bit about the use of placeholders and then using #Value but am not sure i can/should use this approach in say, a service class where i just want to have an annotated field or method marked with this annotation? Any guidance would be much appreciated.
Thanks and regards!
Here's my method-level annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Name {
public String myName();
}
Here's a dummy class that declares the annotation:
public class Z {
#Name(myName = "George")
public void something() {
}
}
Here's how to get the value:
final Method method = Z.class.getMethod("something");
if (method.isAnnotationPresent(Name.class)) {
final Annotation annotation = method.getAnnotation(Name.class);
final Name name = (Name) annotation;
System.out.println(name.myName()); // Prints George
}