I have to #Override an abstract function and offer an array of strings (defining searchable fields for my datatable):
#Override
public String[] getQueryFields() {
return new String[] { "email", "firstname", "lastname", "lastLogin"};
}
When I look on my code I could imagine that I just reference a class and annotate the JPA column fields, e.g. by #Searchable to signal this capability and build my array:
#Column
#Getter
#Setter
...
#Searchable
private String email;
Is there a way to handle this?
You can certainly do anything that you want with Reflection. but you should try and explore for other options too. Here is a a crude example i just wrote. it will give you the fields that are annotated by #AnAnnotation.
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface AnAnnotation {
}
public class AnnotExample {
#AnAnnotation
private String b;
#AnAnnotation
private String c;
public static void getAnnotatedFields(final Class clazz) {
clazz.getAnnotation(AnAnnotation.class);
final Field[] declaredFields = clazz.getDeclaredFields();
for (final Field field : declaredFields) {
final AnAnnotation annotation2 = field.getAnnotation(AnAnnotation.class);
if (annotation2 != null) {
System.out.println(field.getName());
}
}
}
public static void main(final String[] args) {
AnnotExample.getAnnotatedFields(AnnotExample.class);
}
}
Related
I am a java beginner and I need help with testing this:
what I am trying to do: I need at least one of the fields with value to accept it.
How should test my implementation?
My classes:
#Constraint(validatedBy = BankOrBarPresenceValidador.class)
#Target({ TYPE, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Documented
public #interface VerifyBankOrBarPresence {
String message() default "{constraints.VerifyBankOrBarPresence.message}";
Class <?> [] groups() default {};
Class <? extends Payload> [] payload() default {};
String barCodeInvoice();
String bankDestinationNumber();
}
Implementation:
public class BankOrBarPresenceValidador implements ConstraintValidator<VerifyBankOrBarPresence, Object> {
private String barCodeInvoice;
private String bankDestinationNumber;
#Override
public void initialize(VerifyBankOrBarPresence constraintAnnotation) {
barCodeInvoice = constraintAnnotation.barCodeInvoice();
bankDestinationNumber = constraintAnnotation.bankDestinationNumber();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
try{
boolean barcodeIsPresent = barCodeInvoice.length() > 0;
boolean bankDestinationNumberIsPresent = bankDestinationNumber.length() > 0;
return barcodeIsPresent || bankDestinationNumberIsPresent;
}
catch (final Exception e) {
throw new BusinessException("Failed validating if bar code or bank destination number are present", e);
}
}
}
MY DTO:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
#VerifyBankOrBarPresence(barCodeInvoice = "barCodeInvoice", bankDestinationNumber="bankDestinationNumber", message = "Bar Code Invoice or bank Destination Number code not be null")
public class VoucherDTO implements Serializable {
private String barCodeInvoice;
private String bankDestinationNumber;
}
I want to be able to do something like:
#Email
public List<String> getEmailAddresses()
{
return this.emailAddresses;
}
In other words, I want each item in the list to be validated as an email address. Of course, it is not acceptable to annotate a collection like this.
Is there a way to do this?
Neither JSR-303 nor Hibernate Validator has any ready-made constraint that can validate each elements of Collection.
One possible solution to address this issue is to create a custom #ValidCollection constraint and corresponding validator implementation ValidCollectionValidator.
To validate each element of collection we need an instance of Validator inside ValidCollectionValidator; and to get such instance we need custom implementation of ConstraintValidatorFactory.
See if you like following solution...
Simply,
copy-paste all these java classes (and import relavent classes);
add validation-api, hibenate-validator, slf4j-log4j12, and testng jars on classpath;
run the test-case.
ValidCollection
public #interface ValidCollection {
Class<?> elementType();
/* Specify constraints when collection element type is NOT constrained
* validator.getConstraintsForClass(elementType).isBeanConstrained(); */
Class<?>[] constraints() default {};
boolean allViolationMessages() default true;
String message() default "{ValidCollection.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
ValidCollectionValidator
public class ValidCollectionValidator implements ConstraintValidator<ValidCollection, Collection>, ValidatorContextAwareConstraintValidator {
private static final Logger logger = LoggerFactory.getLogger(ValidCollectionValidator.class);
private ValidatorContext validatorContext;
private Class<?> elementType;
private Class<?>[] constraints;
private boolean allViolationMessages;
#Override
public void setValidatorContext(ValidatorContext validatorContext) {
this.validatorContext = validatorContext;
}
#Override
public void initialize(ValidCollection constraintAnnotation) {
elementType = constraintAnnotation.elementType();
constraints = constraintAnnotation.constraints();
allViolationMessages = constraintAnnotation.allViolationMessages();
}
#Override
public boolean isValid(Collection collection, ConstraintValidatorContext context) {
boolean valid = true;
if(collection == null) {
//null collection cannot be validated
return false;
}
Validator validator = validatorContext.getValidator();
boolean beanConstrained = validator.getConstraintsForClass(elementType).isBeanConstrained();
for(Object element : collection) {
Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>> ();
if(beanConstrained) {
boolean hasValidCollectionConstraint = hasValidCollectionConstraint(elementType);
if(hasValidCollectionConstraint) {
// elementType has #ValidCollection constraint
violations.addAll(validator.validate(element));
} else {
violations.addAll(validator.validate(element));
}
} else {
for(Class<?> constraint : constraints) {
String propertyName = constraint.getSimpleName();
propertyName = Introspector.decapitalize(propertyName);
violations.addAll(validator.validateValue(CollectionElementBean.class, propertyName, element));
}
}
if(!violations.isEmpty()) {
valid = false;
}
if(allViolationMessages) { //TODO improve
for(ConstraintViolation<?> violation : violations) {
logger.debug(violation.getMessage());
ConstraintViolationBuilder violationBuilder = context.buildConstraintViolationWithTemplate(violation.getMessage());
violationBuilder.addConstraintViolation();
}
}
}
return valid;
}
private boolean hasValidCollectionConstraint(Class<?> beanType) {
BeanDescriptor beanDescriptor = validatorContext.getValidator().getConstraintsForClass(beanType);
boolean isBeanConstrained = beanDescriptor.isBeanConstrained();
if(!isBeanConstrained) {
return false;
}
Set<ConstraintDescriptor<?>> constraintDescriptors = beanDescriptor.getConstraintDescriptors();
for(ConstraintDescriptor<?> constraintDescriptor : constraintDescriptors) {
if(constraintDescriptor.getAnnotation().annotationType().getName().equals(ValidCollection.class.getName())) {
return true;
}
}
Set<PropertyDescriptor> propertyDescriptors = beanDescriptor.getConstrainedProperties();
for(PropertyDescriptor propertyDescriptor : propertyDescriptors) {
constraintDescriptors = propertyDescriptor.getConstraintDescriptors();
for(ConstraintDescriptor<?> constraintDescriptor : constraintDescriptors) {
if(constraintDescriptor.getAnnotation().annotationType().getName().equals(ValidCollection.class.getName())) {
return true;
}
}
}
return false;
}
}
ValidatorContextAwareConstraintValidator
public interface ValidatorContextAwareConstraintValidator {
void setValidatorContext(ValidatorContext validatorContext);
}
CollectionElementBean
public class CollectionElementBean {
/* add more properties on-demand */
private Object notNull;
private String notBlank;
private String email;
protected CollectionElementBean() {
}
#NotNull
public Object getNotNull() { return notNull; }
public void setNotNull(Object notNull) { this.notNull = notNull; }
#NotBlank
public String getNotBlank() { return notBlank; }
public void setNotBlank(String notBlank) { this.notBlank = notBlank; }
#Email
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
ConstraintValidatorFactoryImpl
public class ConstraintValidatorFactoryImpl implements ConstraintValidatorFactory {
private ValidatorContext validatorContext;
public ConstraintValidatorFactoryImpl(ValidatorContext nativeValidator) {
this.validatorContext = nativeValidator;
}
#Override
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
T instance = null;
try {
instance = key.newInstance();
} catch (Exception e) {
// could not instantiate class
e.printStackTrace();
}
if(ValidatorContextAwareConstraintValidator.class.isAssignableFrom(key)) {
ValidatorContextAwareConstraintValidator validator = (ValidatorContextAwareConstraintValidator) instance;
validator.setValidatorContext(validatorContext);
}
return instance;
}
}
Employee
public class Employee {
private String firstName;
private String lastName;
private List<String> emailAddresses;
#NotNull
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
#ValidCollection(elementType=String.class, constraints={Email.class})
public List<String> getEmailAddresses() { return emailAddresses; }
public void setEmailAddresses(List<String> emailAddresses) { this.emailAddresses = emailAddresses; }
}
Team
public class Team {
private String name;
private Set<Employee> members;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
#ValidCollection(elementType=Employee.class)
public Set<Employee> getMembers() { return members; }
public void setMembers(Set<Employee> members) { this.members = members; }
}
ShoppingCart
public class ShoppingCart {
private List<String> items;
#ValidCollection(elementType=String.class, constraints={NotBlank.class})
public List<String> getItems() { return items; }
public void setItems(List<String> items) { this.items = items; }
}
ValidCollectionTest
public class ValidCollectionTest {
private static final Logger logger = LoggerFactory.getLogger(ValidCollectionTest.class);
private ValidatorFactory validatorFactory;
#BeforeClass
public void createValidatorFactory() {
validatorFactory = Validation.buildDefaultValidatorFactory();
}
private Validator getValidator() {
ValidatorContext validatorContext = validatorFactory.usingContext();
validatorContext.constraintValidatorFactory(new ConstraintValidatorFactoryImpl(validatorContext));
Validator validator = validatorContext.getValidator();
return validator;
}
#Test
public void beanConstrained() {
Employee se = new Employee();
se.setFirstName("Santiago");
se.setLastName("Ennis");
se.setEmailAddresses(new ArrayList<String> ());
se.getEmailAddresses().add("segmail.com");
Employee me = new Employee();
me.setEmailAddresses(new ArrayList<String> ());
me.getEmailAddresses().add("me#gmail.com");
Team team = new Team();
team.setMembers(new HashSet<Employee>());
team.getMembers().add(se);
team.getMembers().add(me);
Validator validator = getValidator();
Set<ConstraintViolation<Team>> violations = validator.validate(team);
for(ConstraintViolation<Team> violation : violations) {
logger.info(violation.getMessage());
}
}
#Test
public void beanNotConstrained() {
ShoppingCart cart = new ShoppingCart();
cart.setItems(new ArrayList<String> ());
cart.getItems().add("JSR-303 Book");
cart.getItems().add("");
Validator validator = getValidator();
Set<ConstraintViolation<ShoppingCart>> violations = validator.validate(cart, Default.class);
for(ConstraintViolation<ShoppingCart> violation : violations) {
logger.info(violation.getMessage());
}
}
}
Output
02:16:37,581 INFO main validation.ValidCollectionTest:66 - {ValidCollection.message}
02:16:38,303 INFO main validation.ValidCollectionTest:66 - may not be null
02:16:39,092 INFO main validation.ValidCollectionTest:66 - not a well-formed email address
02:17:46,460 INFO main validation.ValidCollectionTest:81 - may not be empty
02:17:47,064 INFO main validation.ValidCollectionTest:81 - {ValidCollection.message}
Note:- When bean has constraints do NOT specify the constraints attribute of #ValidCollection constraint. The constraints attribute is necessary when bean has no constraint.
I don't have a high enough reputation to comment this on the original answer, but perhaps it is worth noting on this question that JSR-308 is in its final release stage and will address this problem when it is released! It will at least require Java 8, however.
The only difference would be that the validation annotation would go inside the type declaration.
//#Email
public List<#Email String> getEmailAddresses()
{
return this.emailAddresses;
}
Please let me know where you think I could best put this information for others who are looking. Thanks!
P.S. For more info, check out this SO post.
It’s not possible to write a generic wrapper annotation like #EachElement to wrap any constraint annotation — due to limitations of Java Annotations itself. However, you can write a generic constraint validator class which delegates actual validation of every element to an existing constraint validator. You have to write a wrapper annotation for every constraint, but just one validator.
I’ve implemented this approach in jirutka/validator-collection (available in Maven Central). For example:
#EachSize(min = 5, max = 255)
List<String> values;
This library allows you to easily create a “pseudo constraint” for any validation constraint to annotate a collection of simple types, without writing an extra validator or unnecessary wrapper classes for every collection. EachX constraint is supported for all standard Bean Validation constraints and Hibernate specific constraints.
To create an #EachAwesome for your own #Awesome constraint, just copy&paste the annotation class, replace #Constraint annotation with #Constraint(validatedBy = CommonEachValidator.class) and add the annotation #EachConstraint(validateAs = Awesome.class). That’s all!
// common boilerplate
#Documented
#Retention(RUNTIME)
#Target({METHOD, FIELD, ANNOTATION_TYPE})
// this is important!
#EachConstraint(validateAs = Awesome.class)
#Constraint(validatedBy = CommonEachValidator.class)
public #interface EachAwesome {
// copy&paste all attributes from Awesome annotation here
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String someAttribute();
}
EDIT: Updated for the current version of library.
Thanks for great answer from becomputer06.
But I think the following annotations should be added to ValidCollection definition:
#Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = ValidCollectionValidator.class)
And I still don't understant what to do with collections of primitive type wrappers and constrains annotations like #Size, #Min, #Max etc., because value can't be passed through becomputer06's way.
Of course, I can create custom contraint annotations for all cases in my application, but anyway I have to add properties for these annotations to CollectionElementBean. And it seems to be a bad enough solution.
JSR-303 has the ability to extend the target types of built in constraints: See 7.1.2. Overriding constraint definitions in XML.
You can implement a ConstraintValidator<Email, List<String>> which does the same thing as the given answers, delegating to the primitive validator. Then you can keep your model definition and apply #Email on List<String>.
A very simple workaround is possible. You can instead validate a collection of your classes that wrap the simple value property. For this to work you need to use #Valid annotation on the collection.
Example:
public class EmailAddress {
#Email
String email;
public EmailAddress(String email){
this.email = email;
}
}
public class Foo {
/* Validation that works */
#Valid
List<EmailAddress> getEmailAddresses(){
return this.emails.stream().map(EmailAddress::new).collect(toList());
}
}
How to read annotation which is declared over an object.
For e.g
Annotation :
AuthorInfo.java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface AuthorInfo {
String author() default "Dushyant Kumar";
String login() default "dushyantashu";
}
What I am trying to do :
Book.java
#Data
public class Book {
private int serialNo;
private String review;
}
Main.java
public class Main {
#AuthorInfo (
author = "Barry Allen",
login = "theflash"
)
private static Book book = new Book();
public static void main(String[] args) {
showAnnotation(book);
}
private static void showAnnotation(Object object) {
// How to get values of annotation declared over this object.
}
}
My usecase is to generate this generic showAnnotation() method, that's why param is Object. How to achieve this? From what I explored, I only got ways to read annotation if it's declared over a class, or declared over a member of a class. Isn't there a way where given an object, if some annotation is present over it can be read?
Thanks
You can give a try with generics and reflection. Assume the Book class is annotated with AuthorInfo like below:
#AuthorInfo(author = "Ram", login = "ram")
public class Book {
}
Suppose if you want to know whether AuthorInfo is present in the object of Book, you can do like below. This is straight forward solution to know whether specific annotation is present in an object.
public class TestAnnotation {
public static void main(String[] args) {
Book book = new Book();
showAnnotation(book);
}
private static <T> void showAnnotation(T t) {
Class<? extends Object> aClass = t.getClass();
if (aClass.isAnnotationPresent(AuthorInfo.class)) {
System.out.println("AuthorInfo annotation present");
AuthorInfo authorInfo = aClass.getAnnotation(AuthorInfo.class);
System.out.println(authorInfo.author());
System.out.println(authorInfo.login());
}
}
}
Suppose, if you want to know all annotations on that object, something like below helps:
private static <T> void showAnnotation(T t) {
Class<? extends Object> aClass = t.getClass();
for (Annotation annotation : aClass.getAnnotations()) {
System.out.println(annotation.toString());
}
}
You can retrieve the object class and then explore it. Via Reflection you could get its fields and methods also, and check if any has annotations on it.
Annotations can be read using Reflection API. Like
Class<Main> clazz = Main.class;
Method[] methods = clazz.getDeclaredMethods();
Field[] fields = clazz.getDeclaredFields();
for (Method method : methods) {
Annotation[] annotation = method.getDeclaredAnnotations();
}
for (Field field : fields) {
//This will get #AuthorInfo annotation on book
Annotation[] annotation = field.getDeclaredAnnotations();
//This will get #Data annotation on Book class
Annotation[] annotationsOnFieldClass = field.getClass().getDeclaredAnnotations();
}
clazz.getDeclaredAnnotations();
Recently, I have been intersted with the JSR-269 annotation processing and I want to write a lib to eliminate some boilerplate code with it, such as json processing. I really generate the code, then, however, I encounter a fatal problem and spent a lot of time but could not slove it.
The problem is, the RoundEnvironment.getElementsAnnotatedWith() method alway return all the elements annotated with the annotation, I cannot tistinguish which one comes from a specific class. Maybe the problem isn' t clear. I show you the code below.
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.TYPE)
public #interface JsonObject {
}
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.FIELD)
public #interface JsonField {
String value() default "";
}
#JsonObject
public class Name {
#JsonField("last") public String last;
#JsonField("first") public String first;
}
#JsonObject
public class User {
#JsonField("age") public int age;
#JsonField("name") public String name;
#JsonField("sex") public boolean sex;
}
the fisrt 2 are the annotation, JsonObject indicates that the annotated type is an JsonObject and JsonField indicates that the annotated field is an json field.
the latter 2 are the sample POJO class that I want to gennerate json-parse code.
in the Processor class, the AbstractProcessor.process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) method, when I call roundEnv.roundEnv.getElementsAnnotatedWith(JsonField.class) each type(Name, User) in a loop, the return result is all the json field, in the sample above, the result is ["last", "first", "age", "name", "sex"]. In this situatin, I cannot distinguish which field belongs to which POJO.
May be the words cannot explain what I mean. Here is what I do in the process method.
// the set[Name, User]
Set<? extends Element> jsonObjects = roundEnv.getElementsAnnotatedWith(JsonObject.class);
for (Element jsonObject : jsonObjects) {
// the set[last, first, age, name, sex], **THIS IS THE PROBLEM**
Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(JsonField.class);
// other stuff...
}
Feel free to ask me anything I didn' t mention or not clear. Any suggestion is appreciated, Thanks in advance!
You can build a collection of your json object elements by calling the getElementsAnnotatedWith(JsonField.class) method and filering the result based on the annotation of the enclosing element.
Here is a complete example (using runtime annotation processing for simplicity):
#SupportedSourceVersion(SourceVersion.RELEASE_7)
#SupportedAnnotationTypes("*")
public class ElementFilterProcessor extends AbstractProcessor {
#Retention(RetentionPolicy.RUNTIME)
public static #interface JsonObject {}
#Retention(RetentionPolicy.RUNTIME)
public static #interface JsonField { String value(); }
#JsonObject
public class Name {
#JsonField("last") public String last;
#JsonField("first") public String first;
}
#JsonObject
public class User {
#JsonField("age") public int age;
#JsonField("name") public String name;
#JsonField("sex") public boolean sex;
}
public static void main(String[] args) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final JavaCompiler.CompilationTask task = compiler.getTask(
null,
null,
null,
null,
Collections.singleton(ElementFilterProcessor.class.getName()),
Collections.EMPTY_SET);
task.setProcessors(Collections.singleton(new ElementFilterProcessor()));
task.call();
}
#Override
public boolean process(
final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return true;
}
final Set<? extends Element> jsonFields =
roundEnv.getElementsAnnotatedWith(JsonField.class);
final Map<Element, List<Element>> jsonObjects = new HashMap<>();
for (final Element element : jsonFields) {
final Element classElement = element.getEnclosingElement();
if (classElement.getAnnotation(JsonObject.class) != null) {
List<Element> list = jsonObjects.get(classElement);
if (list == null) {
list = new ArrayList<>();
jsonObjects.put(classElement, list);
}
list.add(element);
}
}
System.out.println(jsonObjects);
return false;
}
}
Output:
{stackoverflow.annotation.ElementFilterProcessor.User=[age, name, sex], stackoverflow.annotation.ElementFilterProcessor.Name=[last, first]}
I'd also recommend to rather consider using a third-party library for mapping Java objects to JSON. For example, the Jackson processor
I have JSON, with differents levels field, so I want to convert to a single JSON with fields with one level for example:
{
"prop1":"value1",
"prob2":"value2",
"prop3": {
"prop4":"value4",
"prop5":"value5"
}
... many level fields
}
result
{
"prop1":"value1",
"prop2":"value2",
"prop4":"value4",
"prop5":"value5"
.......
}
I'm using Jackson with annotation #JsonProperty("field"), I haven't problem wih fields of first level , but I don´t know how to access field where to into more inside JSON , for this example are prop4 and prop5.
JsonUnwrapped is the annotation to use, it even works for multi-level nesting. For example:
#RunWith(JUnit4.class)
public class Sample {
#Test
public void testName() throws Exception {
SampleClass sample = new SampleClass("value1", "value2", new SubClass("value4", "value5", new SubSubClass("value7")));
new ObjectMapper().writeValue(System.out, sample);
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SampleClass {
private String prop1;
private String prop2;
#JsonUnwrapped
private SubClass prop3;
public SampleClass(String prop1, String prop2, SubClass prop3) {
this.prop1 = prop1;
this.prop2 = prop2;
this.prop3 = prop3;
}
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SubClass {
private String prop4;
private String prop5;
#JsonUnwrapped
private SubSubClass prop6;
public SubClass(String prop4, String prop5, SubSubClass prop6) {
this.prop4 = prop4;
this.prop5 = prop5;
this.prop6 = prop6;
}
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SubSubClass{
private String prop7;
public SubSubClass(String prop7) {
this.prop7 = prop7;
}
}
}
will generate
{"prop1":"value1","prop2":"value2","prop4":"value4","prop5":"value5","prop7":"value7"}
Try implementing the #JsonUnwrapped annotation. More information at http://jackson.codehaus.org/1.9.9/javadoc/org/codehaus/jackson/annotate/JsonUnwrapped.html