Why is my custom constraintValidator throwing null pointer? - java

I'm creating a custom ConstraintValidator to validate that my JodaTime object's hours are within a certain window when entered from a spring form.
My annotation:
#Target({ElementType.METHOD, ElementType.FIELD})
#Documented
#Constraint(validatedBy = InputHoursValidator.class)
#Retention(RetentionPolicy.RUNTIME)
public #interface InputHoursConstraint {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
My Validator
public class InputHoursValidator implements ConstraintValidator<InputHoursConstraint, DateTime> {
private static final DateTimeFormatter HOURS_TIME_FORMAT = DateTimeFormat.forPattern("hh:mma");
private static final String EARLIEST_START_TIME = "5:00pm";
private static final String LATEST_END_TIME = "4:00am";
#Override
public void initialize(InputHoursConstraint constraintAnnotation) {
}
#Override
public boolean isValid(DateTime value, ConstraintValidatorContext context) {
return !value.isBefore(DateTime.parse(EARLIEST_START_TIME, HOURS_TIME_FORMAT))
&& !value.isAfter(DateTime.parse(LATEST_END_TIME, HOURS_TIME_FORMAT).plusDays(1));
}
}
And my mojo with the annotation
public class HoursTrackingForm {
#NotNull(message = "Please enter a valid time in AM or PM")
#DateTimeFormat(pattern = "hh:mma")
#InputHoursConstraint(message = "Start time was before 5:00pm or after 4:00am")
private DateTime startTime;
#NotNull(message = "Please enter a valid time in AM or PM")
#DateTimeFormat(pattern = "hh:mma")
#InputHoursConstraint(message = "End time was before 5:00pm or after 4:00am")
private DateTime endTime;
//getters and setters
}
It all looks fine to me but when I submit my object for validation, DateTime in the validator is always null.

My issues were two fold.
1) if I was testing a null scenario I didn't realize all of the constraints still get validated even if one finds an error. Therefore while not null caused an validation error my custom constraint would still throw an NPE. Solution for that was to remove the #NotNull and do that check as well in #InputHoursConstraint.
2) In my validation annotation I added ElementType.TYPE and ElementType.LOCAL_VARIABLE in #Target which appears to allow it to work. Still doing research into why because based on my understanding I only needed ElementType.FIELD

Related

Can I use an autowired variable that's defined in application.yml in javax.validation.constraints.Max?

This is quite a niche question but hopefully someone knows the answer:
I have a SpringFramework RestController with a GetMapping, that has some parameter validation using javax.validation annotations. I would like to use a property that's defined in application.yml, as the max value for a url parameter, but I get the compiler error that the variable should be a constant.
See code:
#RestController
#Validated
public class AssetController {
private final int maxWaitMillis;
#Autowired
public AssetController(#Value("${maxWaitMillis}") int maxWaitMillis) {
this.maxWaitMillis = maxWaitMillis;
}
#GetMapping(value = "/asset", produces = MediaType.IMAGE_JPEG_VALUE)
#ResponseBody
public ResponseEntity<byte[]> getAsset(#RequestParam
#NotBlank(message = "id should not be empty") String id,
#RequestParam(defaultValue = "100", name = "timeout-ms")
#Min(value = 0, message = "minimum timeout is 0 ms")
#Max(value = maxWaitMillis, message = "max timeout is " +
maxWaitMillis) int timeoutMs) {
...
}
application.yml:
maxWaitMillis: 5000
At #Max(value = maxWaitMillis it says maxWaitMillis should be constant, because I guess the check within the annotation is done before the constructor can get the value from application.yml.
If I check the value of maxWaitMillis within the constructor or method body it does have a value of 5000.
Does anyone know if there's a way to use the value from application.yml in the validation annotations?
Thanks!
Create a custom validator as follows
#Target({ElementType.PARAMETER})
#Retention(RUNTIME)
#Constraint(validatedBy = MaxWaitMillisValidator.class)
#Documented
public #interface MaxWaitMillis {
String message() default "Invalid max wait";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class MaxWaitMillisValidator implements ConstraintValidator<MaxWaitMillis, Integer> {
#Value("${maxWaitMillis}")
private int maxWaitMillis;
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return value <= maxWaitMillis;
}
}
You can then use #MaxWaitMillis in place of #Max in the controller.
The value for an annotation must be a compile time constant, so there is no simple way of doing what you are trying to do.
This is a constant variable, a type of constant expression:
private final int maxWaitMillis = 5000;
This is not:
private final int maxWaitMillis;
#Autowired
public AssetController(#Value("${maxWaitMillis}") int maxWaitMillis) {
this.maxWaitMillis = maxWaitMillis;
}

Spring - validate String field in bean which is in fact a Date

I'm writing tests for a bean that is a parameter in #RestController's method.
Bean POJO:
public class AddTownRequestBean
{
#NotEmpty(message = "INVALID_REQUEST")
#Length(min = 1, max = 30, message = "PARAMETER_OUT_OF_BOUNDS")
private String name;
#NotEmpty(message = "INVALID_REQUEST")
#Length(min = 3, max = 4, message = "PARAMETER_OUT_OF_BOUNDS")
private String typeCreated;
#DateTimeFormat(pattern = "yyyy-MM-dd") //style = "S-", iso = DateTimeFormat.ISO.DATE,
private String foundationDate;
getters and setters...
}
My question is related to #DateTimeFormat annotation. In documentation it is stated that this annotation:
Can be applied to java.util.Date,
java.util.Calendar, Long (for millisecond timestamps) as well as
JSR-310 java.time and Joda-Time value types.
As one can see, there's no support of simple String type, but my POJO's date field is String. I already tested using #DateTimeFormat as outlined above, also with commented parameters, mutually excluded every time. And obviously it didn't work out.
So the question itself - is there any annotation or similar workaround to add a (let's call it) "validator" for specific date format in a String type variable that's meant to be a date?
This question or similar one previously asked and answered. Below is the link to previous question. Please see if that answer helps you.
Java String Date Validation Using Hibernate API
You can create custom validator annotation for this case. Example
DateTimeValid.class
#Constraint(validatedBy = DateTimeValidator.class)
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface DateTimeValid{
public String message() default "Invalid datetime!";
public String fomart() default "MM/dd/yyyy";
public Class<?>[] groups() default {};
public Class<? extends Payload>[] payload() default {};
}
DateTimeValidator.class
public class DateTimeValidator implements ConstraintValidator<DateTimeValid, String> {
private String dateFormat;
#Override
public void initialize(DateTimeValid constraintAnnotation) {
dateFormat = constraintAnnotation.fomart();
}
#Override
public boolean isValid(String strDate, ConstraintValidatorContext context) {
try {
DateFormat sdf = new SimpleDateFormat(this.dateFormat);
sdf.setLenient(false);
sdf.parse(strDate);
} catch (Exception e) {
return false;
}
return true;
}
}
Usage
#DateTimeValid(fomart="...", message="...")
private String foundationDate;
Edit: #Ramu: This code from my project I have done before. But yeah, I read the link and it is the same idea with above code

Annotation for hibernate validator for a date at least 24 hours in the future

I know that exist annotation #Future.
If I annotate field with this annotation
#Future
private Date date;
date must be in future means after current moment.
Now I need to validate that date was at least 24 hours after current moment.
How can I make it?
AfterTomorrow.java:
#Target({ FIELD, METHOD, PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = AfterTomorrowValidator.class)
#Documented
public #interface AfterTomorrow {
String message() default "{AfterTomorrow.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
AfterTomorrowValidator.java:
public class AfterTomorrowValidator
implements ConstraintValidator<AfterTomorrow, Date> {
public final void initialize(final AfterTomorrow annotation) {}
public final boolean isValid(final Date value,
final ConstraintValidatorContext context) {
Calendar c = Calendar.getInstance();
c.setTime(value);
c.add(Calendar.DATE, 1);
return value.after(c.getTime());
}
}
Additionally, you can add the default AfterTomorrow.message message in ValidationMessages.properties
Finally, annotate your field:
#AfterTomorrow
private Date date;

Spring validation annotation - How can I verify that a 2 character entry into a string is an actual US state?

I am trying to use spring to check user online input to ensure that the two characters they enter is an actual US state, is there any way of doing this, hopefully using a preset pattern? like, #State or something (if that was a legit annotation). Also, is there a good annotation commonly used for a String street, and String city field? That is other than #NotNull and #NotEmpty
Any help would be greatly appreciated!!
Unfortunately there is no out of the box however you can create your own #State annotation , all you need is to define your annotation and class implementing ConstraintValidator(which handles the validation logic) E.g.
#Constraint(validatedBy = StateConstraintValidator.class)
#Target( { ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface State {
String message() default "{State}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class StateConstraintValidator implements ConstraintValidator<String, String> {
private static final Set<String> CODE_MAP = new HashSet<>(){
{add("AR");}
{add("AK");} //add more codes ...
};
#Override
public void initialize(String state) { }
#Override
public boolean isValid(String value, ConstraintValidatorContext cxt) {
if(value == null) {
return false;
}
return CODE_MAP.contains(value);
}
}
In the similar manner you can create other annotations.

Cross field validation (JSR 303) problem

I have a simple bean, i.e.:
public class MyBean {
private boolean selected;
private String someString;
...
}
So if selected is true, I want someString to be #NotNull etc. .
Any hints, links how to achieve this behaviour?
Thanks
Jonny
If you’re using Spring Framework then you can use Spring Expression Language (SpEL) for that. I’ve wrote small library that provides JSR-303 validator based on SpEL that makes cross-field validations very easy. Take a look at https://github.com/jirutka/validator-spring.
And there’s example for your case:
#SpELAssert(value = "someString != null", applyIf = "selected",
message = "{validator.missing_some_string}")
public class MyBean {
private boolean selected;
private String someString;
...
}
Actually this was too easy. Try something more interesting, maybe an equality of password fields when one of them is not null.
#SpELAssert(value = "password.equals(passwordVerify)",
applyIf = "password || passwordVerify",
message = "{validator.passwords_not_same}")
public class User {
private String password;
private String passwordVerify;
}
And you can even use your own “helper methods” in these expressions!
Compared to Hibernate Validator’s #ScriptAssert annotation, this is pure Java solution, it’s not using any JSR-223 compliant scripting language which may be a little problematic. On the other side, this solution is interesting only for Spring-based applications.
You could do this by annotating MyBean with a custom validator, for example:
#ValidMyBean
public class MyBean {
private boolean selected;
private String someString;
...
}
ValidMyBean:
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = MyBeanValidator.class)
public #interface ValidMyBean {
boolean allViolationMessages() default true;
Class<?>[] constraints() default {};
Class<?>[] groups() default {};
String message() default "{ValidMyBean.message}";
Class<? extends Payload>[] payload() default {};
}
MyBeanValidator:
public final class MyBeanValidator implements
ConstraintValidator<ValidMyBean, MyBean> {
#Override
public void initialize(
#SuppressWarnings("unused") final ValidMyBean constraintAnnotation) {
}
#Override
public boolean isValid(final MyBean value,
final ConstraintValidatorContext context) {
boolean isValid = true;
//your validation here
return isValid;
}
}

Categories

Resources