How to use an array constant in an annotation - java

I would like to use constants for annotation values.
interface Client {
#Retention(RUNTIME)
#Target(METHOD)
#interface SomeAnnotation { String[] values(); }
interface Info {
String A = "a";
String B = "b";
String[] AB = new String[] { A, B };
}
#SomeAnnotation(values = { Info.A, Info.B })
void works();
#SomeAnnotation(values = Info.AB)
void doesNotWork();
}
The constants Info.A and Info.B can be used in the annotation but not the array Info.AB as it has to be an array initializer in this place. Annotation values are restricted to values that could be inlined into the byte code of a class. This is not possible for the array constant as it has to be constructed when Info is loaded. Is there a workaround for this problem?

No, there is no workaround.

Why not make the annotation values an enum, which are keys to the actual data values you want?
e.g.
enum InfoKeys
{
A("a"),
B("b"),
AB(new String[] { "a", "b" }),
InfoKeys(Object data) { this.data = data; }
private Object data;
}
#SomeAnnotation (values = InfoKeys.AB)
This could be improved for type safety, but you get the idea.

It is because arrays' elements can be changed at runtime (Info.AB[0] = "c";) while the annotation values are constant after compile time.
With that in mind someone will inevitably be confused when they try to change an element of Info.AB and expect the annotation's value to change (it won't). And if the annotation value were allowed to change at runtime it would differ than the one used at compile time. Imagine the confusion then!
(Where confusion here means that there is a bug that someone may find and spend hours debugging.)

While there is no way to pass an array directly as an annotation parameter value, there is a way to effectively get similar behavior (depending on how you plan on using your annotations, this may not work for every use case).
Here's an example -- let's say we have a class InternetServer and it has a hostname property. We'd like to use regular Java Validation to ensure that no object has a "reserved" hostname. We can (somewhat elaborately) pass an array of reserved hostnames to the annotation that handles hostname validation.
caveat- with Java Validation, it would be more customary to use the "payload" to pass in this kind of data. I wanted this example to be a bit more generic so I used a custom interface class.
// InternetServer.java -- an example class that passes an array as an annotation value
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.Pattern;
public class InternetServer {
// These are reserved names, we don't want anyone naming their InternetServer one of these
private static final String[] RESERVED_NAMES = {
"www", "wwws", "http", "https",
};
public class ReservedHostnames implements ReservedWords {
// We return a constant here but could do a DB lookup, some calculation, or whatever
// and decide what to return at run-time when the annotation is processed.
// Beware: if this method bombs, you're going to get nasty exceptions that will
// kill any threads that try to load any code with annotations that reference this.
#Override public String[] getReservedWords() { return RESERVED_NAMES; }
}
#Pattern(regexp = "[A-Za-z0-9]{3,}", message = "error.hostname.invalid")
#NotReservedWord(reserved=ReservedHostnames.class, message="error.hostname.reserved")
#Getter #Setter private String hostname;
}
// NotReservedWord.java -- the annotation class
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Target({FIELD, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy=ReservedWordValidator.class)
#Documented
public #interface NotReservedWord {
Class<? extends ReservedWords> reserved ();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String message() default "{err.reservedWord}";
}
// ReservedWords.java -- the interface referenced in the annotation class
public interface ReservedWords {
public String[] getReservedWords ();
}
// ReservedWordValidator.java -- implements the validation logic
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ReservedWordValidator implements ConstraintValidator<NotReservedWord, Object> {
private Class<? extends ReservedWords> reserved;
#Override
public void initialize(NotReservedWord constraintAnnotation) {
reserved = constraintAnnotation.reserved();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) return true;
final String[] words = getReservedWords();
for (String word : words) {
if (value.equals(word)) return false;
}
return true;
}
private Map<Class, String[]> cache = new ConcurrentHashMap<>();
private String[] getReservedWords() {
String[] words = cache.get(reserved);
if (words == null) {
try {
words = reserved.newInstance().getReservedWords();
} catch (Exception e) {
throw new IllegalStateException("Error instantiating ReservedWords class ("+reserved.getName()+"): "+e, e);
}
cache.put(reserved, words);
}
return words;
}
}

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;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Handler {
enum MessageType { MESSAGE, OBJECT };
String value() default "";
MessageType type() default MessageType.MESSAGE;
}

As already was mentioned in previous posts, annotation vales are compile-time constants and there is no way to use an array value as a parameter.
I solved this problem a bit differently.
If you're owning the processing logic, take advantage of it.
For example, give an additional parameter to your annotation:
#Retention(RUNTIME)
#Target(METHOD)
#interface SomeAnnotation {
String[] values();
boolean defaultInit() default false;
}
Use this parameter:
#SomeAnnotation(defaultInit = true)
void willWork();
And this will be a marker to the AnnotationProcessor, which can do anything - initialize it with an array, use String[], or use Enums like Enum.values() and map them to String[].
Hope this will guide someone who has the similar situation in the right direction.

Related

Jackson ignore subtype property when deserialize

I'm using a subType property in Jackson, and I want to using this property when deserializing json.
package com.gaosoft.ai.kg.commons.sphinx.strategy;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.sankuai.ai.kg.commons.sphinx.model.FAQRecord;
import com.sankuai.ai.kg.commons.sphinx.model.FAQRequest;
import org.springframework.beans.factory.BeanFactory;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "strategyType"
)
#JsonSubTypes({
#JsonSubTypes.Type(value = StrategyEmpty.class, name = "empty"),
#JsonSubTypes.Type(value = StrategyNormal.class, name = "normal"),
#JsonSubTypes.Type(value = StrategyDummy.class, name = "dummy")
}
)
public abstract class Strategy implements Serializable {
private String strategyName;
private String strategyType;
private Map<String, Object> args = new HashMap<>();
public String getStrategyType() {
return strategyType;
}
public void setStrategyType(String strategyType) {
this.strategyType = strategyType;
}
public Map<String, Object> getArgs() {
return args;
}
public void setArgs(Map<String, Object> args) {
this.args = args;
}
public String getStrategyName() {
return strategyName;
}
public void setStrategyName(String strategyName) {
this.strategyName = strategyName;
}
public abstract void init(BeanFactory beanFactory);
public abstract List<FAQRecord> fetchFAQ(FAQRequest request);
}
Like my code says, there are 3 subtype of abstract class Strategy, and I want to retain the subclass type name in strategyType property.
Is there a way to fill strategyType when using jackson in this way?
(Sorry about my poor English)
I think what you're asking for is the #JsonTypeInfo#visible property:
Note on visibility of type identifier: by default, deserialization (use during reading of JSON) of type identifier is completely handled by Jackson, and is not passed to deserializers. However, if so desired, it is possible to define property visible = true in which case property will be passed as-is to deserializers (and set via setter or field) on deserialization.
So in your example,
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "strategyType",
visible = true
)
That said, this seems like a design smell. Is it truly valid that you can set a StrategyEmpty's strategyType to dummy? If not, and StrategyEmpty should always have a strategyType of empty, then why not just have an abstract getStrategyType() that each subclass implements with a hardcoded value?

Using NotNull Annotation in method argument

I just started using the #NotNull annotation with Java 8 and getting some unexpected results.
I have a method like this:
public List<Found> findStuff(#NotNull List<Searching> searchingList) {
... code here ...
}
I wrote a JUnit test passing in the null value for the argument searchingList. I was expecting some type of error to happen but it went through as though the annotation was not there. Is this expected behavior? From what I understood, this was to allow you to skip writing the boilerplate null check code.
An explanation of what exactly #NotNull is supposed to do would be greatly appreciated.
#Nullable and #NotNull do nothing on their own. They are supposed to act as Documentation tools.
The #Nullable Annotation reminds you about the necessity to introduce an NPE check when:
Calling methods that can return null.
Dereferencing variables (fields, local variables, parameters) that can be null.
The #NotNull Annotation is, actually, an explicit contract declaring the following:
A method should not return null.
A variable (like fields, local variables, and parameters) cannot should not hold null value.
For example, instead of writing:
/**
* #param aX should not be null
*/
public void setX(final Object aX ) {
// some code
}
You can use:
public void setX(#NotNull final Object aX ) {
// some code
}
Additionally, #NotNull is often checked by ConstraintValidators (eg. in spring and hibernate).
The #NotNull annotation doesn't do any validation on its own because the annotation definition does not provide any ConstraintValidator type reference.
For more info see:
Bean validation
NotNull.java
Constraint.java
ConstraintValidator.java
As mentioned above #NotNull does nothing on its own. A good way of using #NotNull would be using it with Objects.requireNonNull
public class Foo {
private final Bar bar;
public Foo(#NotNull Bar bar) {
this.bar = Objects.requireNonNull(bar, "bar must not be null");
}
}
To make #NonNull active you need Lombok:
https://projectlombok.org/features/NonNull
import lombok.NonNull;
Follow: Which #NotNull Java annotation should I use?
If you are using Spring, you can force validation by annotating the class with #Validated:
import org.springframework.validation.annotation.Validated;
More info available here:
Javax #NotNull annotation usage
You could also use #NonNull from projectlombok (lombok.NonNull)
SO #NotNull just is a tag...If you want to validate it, then you must use something like hibernate validator jsr 303
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<List<Searching>> violations = validator.validate(searchingList);
I do this to create my own validation annotation and validator:
ValidCardType.java(annotation to put on methods/fields)
#Constraint(validatedBy = {CardTypeValidator.class})
#Documented
#Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface ValidCardType {
String message() default "Incorrect card type, should be among: \"MasterCard\" | \"Visa\"";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And, the validator to trigger the check:
CardTypeValidator.java:
public class CardTypeValidator implements ConstraintValidator<ValidCardType, String> {
private static final String[] ALL_CARD_TYPES = {"MasterCard", "Visa"};
#Override
public void initialize(ValidCardType status) {
}
public boolean isValid(String value, ConstraintValidatorContext context) {
return (Arrays.asList(ALL_CARD_TYPES).contains(value));
}
}
You can do something very similar to check #NotNull.
To test your method validation in a test, you have to wrap it a proxy in the #Before method.
#Before
public void setUp() {
this.classAutowiredWithFindStuffMethod = MethodValidationProxyFactory.createProxy(this.classAutowiredWithFindStuffMethod);
}
With MethodValidationProxyFactory as :
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
public class MethodValidationProxyFactory {
private static final StaticApplicationContext ctx = new StaticApplicationContext();
static {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.afterPropertiesSet(); // init advisor
ctx.getBeanFactory()
.addBeanPostProcessor(processor);
}
#SuppressWarnings("unchecked")
public static <T> T createProxy(T instance) {
return (T) ctx.getAutowireCapableBeanFactory()
.applyBeanPostProcessorsAfterInitialization(instance, instance.getClass()
.getName());
}
}
And then, add your test :
#Test
public void findingNullStuff() {
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> this.classAutowiredWithFindStuffMethod.findStuff(null));
}
I resolved it with
#JsonSetter(nulls = Nulls.AS_EMPTY)
#NotBlank
public String myString;
Request Json:
{
myString=null
}
Response:
error must not be blank

Custom annotation on method argument is not being executed

I have implemented a custom annotation #Password to perform validation on an argument of my method setPassword(). The annotation is defined like this:
// Password.java
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import javax.validation.*;
#Target({METHOD, FIELD, PARAMETER, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = PasswordValidator.class)
#Documented
public #interface Password {
String message() default PasswordValidator.message;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And the current implementation of the validator is this:
// PasswordValidator.java
package utils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PasswordValidator implements ConstraintValidator<Password, String> {
/* Default error message */
final static public String message = "error.invalid.password";
/**
* Validator init
* Can be used to initialize the validation based on parameters
* passed to the annotation.
*/
public void initialize(Password constraintAnnotation) {
System.out.println("in initialize()");
}
public boolean isValid(String string, ConstraintValidatorContext constraintValidatorContext) {
System.out.println("in isValid()");
return false;
}
}
Note that in the current implementation isValid() always returns false. The reason will be apparent shortly.
My usage of the validator is in a class User. For brevity I won't post the whole source here, but the relevant parts are:
package models;
import utils.Password;
// other imports omitted
#Entity
#Table(name = "users", schema="public")
public class User {
#Required
private String password;
...
public void setPassword(#Password String clearPassword) {
try {
this.password = HashHelper.createPassword(clearPassword);
} catch (AppException e) {
e.printStackTrace();
}
}
...
}
The basic idea is that I use the User class to store a hashed password for a user, but before setting the hashed password, I (would like to) run validation on the unhashed password (i.e. clearPassword).
The problem I am having is that this validation is not taking place. In the current implementation, it should (according to my understanding) always throw a ConstraintViolationException because isValid() always returns false, but this is not the case.
I have checked that the annotation is being attached to the method argument by calling (in another part of the application) something along the lines of:
Method method = user.getClass().getMethod("setPassword", new Class[] { String.class });
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
System.out.println(method.getName() + ": " + parameterAnnotations[0][0]);
which produces the following output:
setPassword:#utils.Password(message=error.invalid.password, payload=[], groups=[])
So this tells me the annotation is being applied to the method argument. But I can't understand why I'm not getting the ConstraintViolationException when I actually call the method. I also never see the output "in initialize()" or "in isValid()" that I added to these methods as a check to see if they're being fired.
As another test, I also added the #Password annotation to the member variable password in User.class. This causes the ConstraintViolationException to be thrown as expected, e.g. when I try to persist a User object.
Can anyone shed light as to why the annotation on the method argument is not working properly? Thanks in advance!
I think you are missing this declaration in your Password annotation:
ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT;
From the docs:
validationAppliesTo is used at constraint declaration time to clarify what the constraint targets (i.e. the annotated element, the method return value or the method parameters).
The element validationAppliesTo must only be present for constraints that are both generic and cross-parameter, it is mandatory in this situation. A ConstraintDefinitionException is raised if these rules are violated.
The type of the validationAppliesTo parameter is ConstraintTarget. The default value must be ConstraintTarget.IMPLICIT.
It won't get validated because hibernate validator validates on user object not on the method setPassword. In case you want to throw that exception, you have to get the ExecutableValidator instance, call validateParameters method and then throw the ConstraintViolationException by yourself.

LLVM vmkit java annotation

I am a novice in Java. I want to annotate (with a string) different Java variables that can be translated into LLVM IR (and get them by using llvm.var.annotation or llvm.global.annotations). In the case of C/C++, I use:
__attribute__((annotate("RED"))) static int a;
So a is annotated with the value "RED". My question is, how do I make this in Java (using vmkit for LLVM) ? I think I have to use #, but I do not know what libs do I have to add to vmkit and also how annotations in Java work?
Thank you for your answer !
Look for annotation tutorial in this link
http://docs.oracle.com/javase/tutorial/java/annotations/
what you need to do is to define your annotation than make some sort of reflection.
this is the #Red annotation
package test;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Red {
}
and this how to use it
public class AnyClass {
#Red
public int a = 5;
}
here is a simple test to get the annotated field
package test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class TestClass {
/**
* #param args
*/
public static void main(String[] args) {
AnyClass anyClass = new AnyClass();
Class clasz = anyClass.getClass();
Field [] fArray = clasz.getFields();
Annotation[] anArray = clasz.getAnnotations();
for(Field f : fArray) {
System.out.println("wink" + f.getAnnotations()[0].annotationType());
}
}
}

How to create custom annotation in java?

I want to create custom annotation in java for DirtyChecking. Like I want to compare two string values using this annotation and after comparing it will return a boolean value.
For instance: I will put #DirtyCheck("newValue","oldValue") over properties.
Suppose I made an interface:
public #interface DirtyCheck {
String newValue();
String oldValue();
}
My Questions are:
Where I make a class to create a method for comparison for two string values? I mean, how this annotation notifies that this method I have to call?
How to retreive returning values of this method ?
First you need to mark if annotation is for class, field or method. Let's say it is for method: so you write this in your annotation definition:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface DirtyCheck {
String newValue();
String oldValue();
}
Next you have to write let's say DirtyChecker class which will use reflection to check if method has annotation and do some job for example say if oldValue and newValue are equal:
final class DirtyChecker {
public boolean process(Object instance) {
Class<?> clazz = instance.getClass();
for (Method m : clazz.getDeclaredMethods()) {
if (m.isAnnotationPresent(DirtyCheck.class)) {
DirtyCheck annotation = m.getAnnotation(DirtyCheck.class);
String newVal = annotation.newValue();
String oldVal = annotation.oldValue();
return newVal.equals(oldVal);
}
}
return false;
}
}
Cheers,
Michal
To answer your second question: your annotation can't return a value. The class which processes your annotation can do something with your object. This is commonly used for logging for example.
I'm not sure if using an annotation for checking if an object is dirty makes sense except you want to throw an exception in this case or inform some kind of DirtyHandler.
For your first question: you could really spent some effort in finding this yourself. There are enough information here on stackoverflow and the web.
CustomAnnotation.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CustomAnnotation {
int studentAge() default 21;
String studentName();
String stuAddress();
String stuStream() default "CS";
}
How to use the field of Annotation in Java?
TestCustomAnnotation.java
package annotations;
import java.lang.reflect.Method;
public class TestCustomAnnotation {
public static void main(String[] args) {
new TestCustomAnnotation().testAnnotation();
}
#CustomAnnotation(
studentName="Rajesh",
stuAddress="Mathura, India"
)
public void testAnnotation() {
try {
Class<? extends TestCustomAnnotation> cls = this.getClass();
Method method = cls.getMethod("testAnnotation");
CustomAnnotation myAnno = method.getAnnotation(CustomAnnotation.class);
System.out.println("Name: "+myAnno.studentName());
System.out.println("Address: "+myAnno.stuAddress());
System.out.println("Age: "+myAnno.studentAge());
System.out.println("Stream: "+myAnno.stuStream());
} catch (NoSuchMethodException e) {
}
}
}
Output:
Name: Rajesh
Address: Mathura, India
Age: 21
Stream: CS
Reference

Categories