is it possible to read variable values using it's annotation class - java

The following code is my annotation class
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;
#Documented
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Search {
public String name();
public String value();
}
Following class is a normal class
public class MyClass{
#Search(name = "first_name", value = "srivas")
private String first_name;
#Search(name = "last_name", value = "sam")
private String last_name;
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String customername) {
this.customername = customername;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name= last_name;
}
}
Here I am going to read variable values
public class MyService{
public void getvalues(){
MyClass myvals = new MyClass();
myvals.setFirst_name("Vikram");
myvals.setLast_name("Kumar");
for(Field f: MyClass.class.getDeclaredFields()){
MyClass searchfields = f.getAnnotation(MyClass.class);
if (searchfields != null)
System.out.println(searchfields.name()+" = "+searchfields .value());
}
}
}
I am getting the following output
first_name = srivas,
last_name = sam,
but I am expecting.
first_name = vikram,
last_name = Kumar,
Is it possible to read please help me if any posibility is there

No
Also it really makes no sense to do what you are trying to do.
The Annotation is used to perform an action at compile time, it's not a store for variable data.
You could use reflection to find an annotation and work out which variable it's related to, but it's a lot of effort when you could just use a getter to get the value of the variable without all of that hassle.

Related

Spring Boot validation - one from two not null

I'm trying to validate if one of two fields are not null in Spring Boot?
I have set that in the method class for the main object:
#NotNull(message = "Username field is required")
private String username;
#NotNull(message = "Email field is required")
private String email;
but that will require to have both fields not null. Then I went with custom validation described here https://lmonkiewicz.com/programming/get-noticed-2017/spring-boot-rest-request-validation/ but I wasn't able to get that example to work. I have to stuck on
User class declaration:
#CombinedNotNull(fields = {"username","email"})
public class User implements {
private long id = 0L;
#NotNull(message = "First name field is required")
private String firstName;
#NotNull(message = "Last name field is required")
private String lastName;
private String username;
private String email;
#NotNull(message = "Status field is required")
private String status;
...all methods here...
...setters and getters...
}
CombibnedNotNull class:
#Documented
#Retention(RUNTIME)
#Target({ TYPE, ANNOTATION_TYPE })
#Constraint(validatedBy = userValidator.class)
public #interface CombinedNotNull {
String message() default "username or email is required";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
userValidator class:
#Component
public class userValidator implements ConstraintValidator<CombinedNotNull, User> {
#Override
public void initialize(final CombinedNotNull combinedNotNull) {
fields = combinedNotNull.fields();
}
#Override
public boolean isValid(final User user, final ConstraintValidatorContext context) {
final BeanWrapperImpl beanWrapper = new BeanWrapperImpl(user);
for (final String f : fields) {
final Object fieldValue = beanWrapper.getPropertyValue(f);
if (fieldValue == null) {
return false;
}
}
return true;
}
}
Is there any other way to get this done or should I go with the "complex" example from that page?
I'm assuming username OR email must not be null. Not XOR.
Add this getter in User class:
#AssertTrue(message = "username or email is required")
private boolean isUsernameOrEmailExists() {
return username != null || email != null;
}
In my experience, the method name must follow the getter name convention otherwise this won't work. For examples, getFoo or isBar.
This has a small problem: the field name from this validation error would be usernameOrEmailExists, only 1 error field. If that is not a concern, this might help.
But If you want to have username and email fields when errors occur, you can use this workaround:
public String getUsername() {
return username;
}
public String getEmail() {
return email;
}
#AssertTrue(message = "username or email is required")
private boolean isUsername() {
return isUsernameOrEmailExists();
}
#AssertTrue(message = "username or email is required")
private boolean isEmail() {
return isUsernameOrEmailExists();
}
private boolean isUsernameOrEmailExists() {
return username != null || email != null;
}
get... methods are just simple getters for general use, and is... are for validation. This will emit 2 validation errors with username and email fields.
I'll try to implement it for you (even if I'm without an IDE).
Inside ConstraintValidator#initialize you can get a hold of the configured fields' names which cannot be null.
#Override
public void initialize(final CombinedNotNull combinedNotNull) {
fields = combinedNotNull.fields();
}
Inside ConstraintValidator#isValid you can use those fields' names to check the Object fields.
#Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
final BeanWrapperImpl beanWrapper = new BeanWrapperImpl(value);
for (final String f : fields) {
final Object fieldValue = beanWrapper.getPropertyValue(f);
if (fieldValue == null) {
return false;
}
}
return true;
}
Annotation:
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Retention(RUNTIME)
#Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Constraint(validatedBy = CombinedNotNullValidator.class)
public #interface CombinedNotNull {
String message() default "username or email is required";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Fields to validate against null.
*/
String[] fields() default {};
}
The annotation could be applied as
#CombinedNotNull(fields = {
"fieldName1",
"fieldName2"
})
public class MyClassToValidate { ... }
To learn how to create a Class-level constraint annotation, refer always to the official documentation. Docs
If you want to validate that exactly one is set and the others are null:
Annotation
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Retention(RUNTIME)
#Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Constraint(validatedBy = OneNotNullValidator.class)
public #interface OneNotNull {
String message() default "Exactly one of the fields must be set and the other must be null";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Fields to validate against null.
*/
String[] fields() default {};
}
Validator
import java.util.Arrays;
import java.util.Objects;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.stereotype.Component;
#Component
public class OneNotNullValidator implements ConstraintValidator<OneNotNull, Object> {
private String[] fields;
#Override
public void initialize(final OneNotNull combinedNotNull) {
fields = combinedNotNull.fields();
}
#Override
public boolean isValid(final Object obj, final ConstraintValidatorContext context) {
final BeanWrapperImpl beanWrapper = new BeanWrapperImpl(obj);
return Arrays.stream(fields)
.map(beanWrapper::getPropertyValue)
.filter(Objects::isNull)
.count()
== 1;
}
}
Usage
#OneNotNull(
fields = {"username","email"},
message="Either username or email must be set"
)
public class User {
private String username;
private String email;
// ...
}

Messing around with JPA and getting an Error creating user - could not set field value

I am following the Spring tutorial to get started with JPA, and I have a basic mysql db with a user table.
I've set up User.java as follows:
package com.test.models;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.Id;
import java.util.UUID;
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
#Column(name="id")
private UUID id;
#Column(name="first_name")
String first_name;
#Column(name="last_name")
String last_name;
#Column(name="username")
String username;
public User(String first_name, String last_name, String username) {
this.first_name = first_name;
this.last_name = last_name;
this.username = username;
}
public User() { }
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
I've set up the UserRepo.java:
package com.test.models;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import java.util.UUID;
#RepositoryRestResource
public interface UserRepo extends CrudRepository<User, UUID>{
User findByUsername(String username);
}
And finally the UserController.java:
package com.test.controllers;
import com.test.models.UserRepo;
import com.test.models.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class UserController {
#Autowired
private UserRepo userRepo;
#RequestMapping("/create")
#ResponseBody
public String create(String first_name, String last_name, String username) {
String userId = "";
try {
User user = new User(first_name, last_name, username);
userRepo.save(user);
userId = String.valueOf(user.getId());
}
catch (Exception ex) {
return "Error creating the user: " + ex.toString();
}
return "User successfully created with id = " + userId;
}
}
I've also set up my db connection:
spring.datasource.url=jdbc:mysql://localhost:3306/test?autoReconnect=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=fakepass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
However, when I use postman to make a POST to localhost:8080/create with
{
"first_name": "Jim",
"last_name": "Smith",
"username": "jim.smith"
}
I get this error:
Error creating the user:
org.springframework.orm.jpa.JpaSystemException: Could not set field
value [ff80818158b79f8e0158b7a285b60001] value by reflection : [class
com.test.models.User.id] setter of com.test.models.User.id; nested
exception is org.hibernate.PropertyAccessException: Could not set
field value [ff80818158b79f8e0158b7a285b60001] value by reflection :
[class com.test.models.User.id] setter of com.test.models.User.id
I had the same problem. Your strategy in #GenericGenerator is set incorrectly.
Try:
#GenericGenerator(name = "system-uuid", strategy = "org.hibernate.id.UUIDGenerator")
All JPA entity classes should have the empty constructors and your User class is missing empty constructor which is causing this issue, so change your User Entity class as shown below and add User() constructor:
public class User {
public User() {//empty constructor
}
//add existing code here
}
The entity class must have a no-arg constructor. The entity class may
have other constructors as well. The no-arg constructor must be public
or protected.
You can look here for the spec.
in dataBase exists UUID type, try to use UUID instead of varchar(36).

JSON Jackson, how to use read only property alias for backwards compatibilty?

We have a structure that represents configuration of some sort. We have had a typo in the word periodicity, it was wrongly spelled with 'o' as period*o*city. Below example source is the corrected one. However, I need to be able to read the old configuration files to maintain backwards compatibility.
Can I make JSON Jackson recognize the misspelled field/property on deserialization but ignore it on serialization?
We are using version 2.6.6 of JSON Jackson.
package foo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
#JsonInclude(Include.NON_EMPTY)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Rule {
private LogPeriodicity periodicityLevel;
private Integer periodicity;
// ctors and some other methods omitted for brevity
public LogPeriodicity getPeriodicityLevel() {
return periodicityLevel;
}
public void setPeriodicityLevel(LogPeriodicity periodicityLevel) {
this.periodicityLevel = periodicityLevel;
}
public Integer getPeriodicity() {
return periodicity;
}
public void setPeriodicity(Integer periodicity) {
this.periodicity = periodicity;
}
}
If i got your question right you want something like this?
MyClass obj = mapper.readValue("{ \"name\" : \"value\"}", MyClass.class);
String serialized = mapper.writeValueAsString(obj);
MyClass obj2 = mapper.readValue("{ \"name2\" : \"value\"}", MyClass.class);
String serialized2 = mapper.writeValueAsString(obj2);
if( Objects.equals(serialized2, serialized))
System.out.println("Success " + serialized + " == " + serialized2 );
if you don't want extra field in POJO you can just add setter like this:
public static class MyClass {
#JsonProperty
private String name = null;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#JsonSetter
public void setName2(String name2) {
setName(name2);
}
}
You can probably also register legacy Mixin instead of #JsonSetter
public abstract class LegacyMyClassMixIn{
#JsonProperty("name")
private String name;
#JsonGetter("name")
public abstract String getName();
#JsonSetter("name2")
public abstract void setName(String name) ;
}
And use it like this:
SimpleModule module = new SimpleModule();
module.setMixInAnnotation(MyClass.class, LegacyMyClassMixIn.class);
mapper2.registerModule(module);
Btw in Gson it can be done with just 1 line #SerializedName(value="name", alternate={"name2"}) public String name = null;

How to implement customized serialization feature in fasterxml

My JSON:
{
"name": "asdf",
"age": "15",
"address": {
"street": "asdf"
}
}
If street is null, with JsonSerialize.Inclusion.NON_NULL, I can get..
{
"name": "asdf",
"age": "15",
"address": {}
}
But I want to get something like this.. (when address is not null, it is a new/empty object. But street is null.)
{
"name": "asdf",
"age": "15"
}
I thought to have custom serialization feature like JsonSerialize.Inclusion.VALID_OBJECT.
Adding isValid() method in the Address class then if that returns true serialize else don't serialize.
But I don't know how to proceed further/which class to override. Is this possible or any other views on this? Please suggest.
Added classes
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
Customer customer = new Customer();
customer.setName("name");
customer.setAddress(new Address());
mapper.writeValue(new File("d:\\customer.json"), customer);
}
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class Customer {
private String name;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class Address {
private String street;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
Note: I am not worrying about deserialization now. i.e, loss of address object.
Thanks in advance.
Customized JSON Object using Serialization is Very Simple.
I have wrote a claas in my project i am giving u a clue that how to Implement this in Projects
Loan Application (POJO Class)
import java.io.Serializable;
import java.util.List;
import org.webservice.business.serializer.LoanApplicationSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
#JsonSerialize(using=LoanApplicationSerializer.class)
public class LoanApplication implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private double amount;
private User borrowerId;
private String businessType;
private String currency;
private int duration;
private Date lastChangeDate;
private long loanApplicationId;
private String myStory;
private String productCategory;
private String purpose;
private Date startDate;
private String status;
private String type;
private String salesRepresentative;
Now LoanApplicationSerializer class that contains the Customization using Serialization Logic................
package org.ovamba.business.serializer;
import java.io.IOException;
import org.webservice.business.dto.LoanApplication;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class LoanApplicationSerializer extends JsonSerializer<LoanApplication> {
#Override
public void serialize(LoanApplication prm_objObjectToSerialize, JsonGenerator prm_objJsonGenerator, SerializerProvider prm_objSerializerProvider) throws IOException, JsonProcessingException {
if (null == prm_objObjectToSerialize) {
} else {
try {
prm_objJsonGenerator.writeStartObject();
prm_objJsonGenerator.writeNumberField("applicationId", prm_objObjectToSerialize.getLoanApplicationId());
prm_objJsonGenerator.writeStringField("status", prm_objObjectToSerialize.getStatus());
prm_objJsonGenerator.writeNumberField("amount", prm_objObjectToSerialize.getAmount());
prm_objJsonGenerator.writeNumberField("startdate", prm_objObjectToSerialize.getStartDate().getTime());
prm_objJsonGenerator.writeNumberField("duration", prm_objObjectToSerialize.getDuration());
prm_objJsonGenerator.writeStringField("businesstype", prm_objObjectToSerialize.getBusinessType());
prm_objJsonGenerator.writeStringField("currency", prm_objObjectToSerialize.getCurrency());
prm_objJsonGenerator.writeStringField("productcategory", prm_objObjectToSerialize.getProductCategory());
prm_objJsonGenerator.writeStringField("purpose", prm_objObjectToSerialize.getPurpose());
prm_objJsonGenerator.writeStringField("mystory", prm_objObjectToSerialize.getMyStory());
prm_objJsonGenerator.writeStringField("salesRepresentative", prm_objObjectToSerialize.getSalesRepresentative());
} catch (Exception v_exException) {
//ExceptionController.getInstance().error("Error while Serializing the Loan Application Object", v_exException);
} finally {
prm_objJsonGenerator.writeEndObject();
}
}
}
}
Hope This may help u alot. Thanks..
You can do it by annotating your class with #JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
Example:
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public myClass{
// attributes and accessors
}
You can find some useful informations at Jackson faster xml

Audit fields of a class using a class level annotation (aspectj)

I'm writing a simple auditing framework with aspectj, which allows me to audit the fields of a class which are annotated with an #Audit annotation.
As value the #Audit annotation expects an array of field names to be watched
Example Usage:
#Audit({"name","phoneNumber"})
class User {
private String name;
private String phoneNumber;
public getName(){
return name;
};
public setName(String name){
this.name=name;
}
}
How does the Aspect look that watches the assignment of fields that are annotated like in the above example?
Here the stub of my first try:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
public #interface Audit {
String[] value()
}
#Aspect
class AuditAspect {
#Pointcut("????")
public void markedFieldWasModified(){}
#AfterReturning("markedFieldWasModified()")
public void addFieldToModifiedFields(JoinPoint jp, AuditableEO eo){
eo.addModifiedField(jp.getSignature().getName());
}
// inter Type declarations
public interface IAuditableEO {
public Iterator<String> modifiedFields();
public boolean modified();
public boolean addModifiedField(String field);
};
}
according to https://eclipse.org/aspectj/doc/next/quick5.pdf
you should be able to do set(* *.*) && #target(Audit)
you then have to check the joinpoint if an auditable field is being modified.
How about not over-engineering the whole thing and directly annotating fields instead of classes? You can also skip the IAuditableEO interface IMO, I cannot see why it would be useful. Here is a simple example similar to yours, just with the aspect in code-style syntax (I prefer it to annotation-style syntax for clarity, but you can easily convert it by yourself):
Audit annotation for fields (not classes):
package de.scrum_master.app;
import java.lang.annotation.*;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD})
public #interface Audit {}
User class with a sample main method:
package de.scrum_master.app;
public class User {
private int id;
#Audit private String name;
#Audit private String phoneNumber;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPhoneNumber() { return phoneNumber; }
public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; }
public static void main(String[] args) {
User user = new User();
user.setId(11);
user.setName("John Doe");
user.setPhoneNumber("+49-1111-23456789");
System.out.println("User(" + user.getId() + ", " + user.getName() + ", " + user.getPhoneNumber() + ")");
}
}
Audit aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Audit;
public aspect AuditAspect {
pointcut fieldModification() : set(#Audit * *);
after() : fieldModification() {
System.out.println(thisJoinPointStaticPart);
}
}
Sample output:
set(String de.scrum_master.app.User.name)
set(String de.scrum_master.app.User.phoneNumber)
User(11, John Doe, +49-1111-23456789)
As you can see, only the annotated fields are caught, not the ID field. This permits for fine-granular auditing on a per-field basis. Furthermore in the advide you have everything you need if you want to record anything in and audit database: field type and name, class name and so forth.

Categories

Resources