Hibernate Validator : Custom Meassages - java

i have a java project and use a custom hibernate validator in that. According to Hibernate Docs , custom error messages should be defined as key-value in ValidateMessages.properties and this file must be created in "classpath" directory.
my problem is that classpath is under "target" directory and it will be deleted after clean-build the project so the created .properties file will be gone. how it can be solved?
`#Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = NCValidator.class)
#Documented
public #interface NC {
String message() default "{msg}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};}
///////////////////////////////
` public class NCValidator implements
ConstraintValidator<NC, String> {
#Override
public void initialize(NC constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
#Override
public boolean isValid(String string, ConstraintValidatorContext
context) {
...
...
}
}`
and use this custom validator in a class like this:
`#ValidateNC
default public String getNC() {
return (String) get("nC");
}
`

Put your .properties file under your resources folder or anywhere in your system and point to it.

Related

How to use application.properties values in javax.validation annotations

I have a variable called notification.max-time-to-live in application.yaml file and want to use this as the value of javax.validation.constraints.#Max() annotation.
I've tried in many ways (using env.getProperty(), #Value, etc) and it says it must be a constant value, is there any way to do this?
I know this does not directly answer my question and as M. Deinum already said the answer is no. Nonetheless it's a simple workaround.
It's true that #Max and other javax annotations do not let us use dynamic values, however, we can create a custom annotation (as M. Deinum suggested) that uses values from application.yaml with spring #Value.
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = ValidTimeToLiveValidator.class)
public #interface ValidTimeToLive {
String message() default "must be less than or equal to %s";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
And the respective validator.
public class ValidTimeToLiveValidator implements ConstraintValidator<ValidTimeToLive, Integer> {
#Value("${notification.max-time-to-live}")
private int maxTimeToLive;
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
// leave null-checking to #NotNull
if (value == null) {
return true;
}
formatMessage(context);
return value <= maxTimeToLive;
}
private void formatMessage(ConstraintValidatorContext context) {
String msg = context.getDefaultConstraintMessageTemplate();
String formattedMsg = String.format(msg, this.maxTimeToLive);
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(formattedMsg)
.addConstraintViolation();
}
}
Now we just need to add this custom annotation in the respective class.
public class Notification {
private String id;
#ValidTimeToLive
private Integer timeToLive;
// ...
}

Java validator annotation for value from properties file

I am trying to write a Validator that should validate the value of a property in application.properties
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = BaseUrlStartsWithHttpsValidator.class)
public #interface CheckBaseUrlStartsWithHttps {
String message() default "Base url does not start with https:// check your configuration, "
+ "Found: ${validatedValue}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String value() default "";
}
It's a simple validation I am just checking if the String starts with https://.
and the way I am trying to use it by annotating the field with it so:
#CheckBaseUrlStartsWithHttps
#Value("${my.base.url}")
private String baseUrl;
But it seems not to do the trick I have tried changing the #Target type is it even possible to validate properties this way, I am using Spring Framework.
So figured it out my self upon reading, reading, reading and trying different things. Turns out that in the class where I am reading in the property I had to annotate the class itself with #Validated and #ConfigurationProperties(prefix = "my") then my check was working as supposed to. So the end product is:
public class BaseUrlValidator implements ConstraintValidator<CheckBaseUrl, String> {
#Override
public boolean isValid(#Nullable String value, #Nullable ConstraintValidatorContext context) {
if (value == null) {
return false;
}
return value.startsWith("https://");
}
}
and
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = BaseUrlValidator.class)
public #interface CheckBaseUrl {
String message() default "Base URL should start with https://. Found: ${validatedValue}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String value() default "";
}
and
#Service
#Validated
#ConfigurationProperties(prefix = "my")
public class MyService {
#CheckBaseUrl
#Value("${my.base.url}")
private String baseUrl;
...
}
Only thing that might be a bit annoying though is that this will make the application fail on startup if the urls is not configured correctly, which is in its own probably a good thing such that it can be fixed right away, but I would rather want it to fail on runtime when it is accessed and throw a RumetimeException instead. Anyway this seems to do the trick.

JSR-303 custom annotation

I'm trying to implement a custom annotation to validate my fields. The idea is that the validation fails whenever the annotated field is null. Something like this.
#RequiredProperty
public abstract Person getPerson();
Now if this returns a null Person, I'd like the validation to fail (ideally with a custom message "Person field is null").
I tried to do it like this.
#Documented
#Constraint(validatedBy = RequiredPropertyValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE})
#ReportAsSingleViolation
public #interface RequiredProperty {
String message() default "{javax.validation.constraints.RequiredProperty.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And validator.
public class RequiredPropertyValidator implements ConstraintValidator<RequiredProperty, Object> {
#Override
public void initialize(RequiredProperty constraintAnnotation) {
}
#Override
public boolean isValid(Object property, ConstraintValidatorContext context) {
return property != null;
}
}
However this won't work. It doesn't validate at all. Object property is never even passed to the isValid method. Any advice on how to get it working?
UPDATE
Removing the empty initialize method got it working. However, I'm not sure how to create a custom error message that the "Person" field is null. Is that possible?
I created a custom message in .properties file, but this is just a static message, and I'd like to capture the actual field from runtime.

ConstraintValidation not getting applied to the list

I have List of Objects that I need to run some validation on
#KeyValid
#Valid
protected List<KeyValue> keyValues;
and I have a the following annotation created for it:
#Target({ElementType.TYPE, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = KeyValidator.class)
public #interface KeyValid{
String message() default "invalid_parameter_default_message";
String[] checks() default {};
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And this is my validator:
public class KeyValidator implements ConstraintValidator<KeyValid, KeyValue> {
#Override
public void initialize(KeyValid keyValid) {
}
#Override
public boolean isValid(KeyValue keyValue, ConstraintValidatorContext constraintValidatorContext) {
return true;
}
}
I had read somewhere that collections can be validated in bulk if the list or map or set is annotated by custom constraint then all of the elements of the collection call the validator but the above code throws the following error
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'com.util.Validators.KeyValid' validating type 'java.util.List<com.model.KeyValue>'. Check configuration for 'keyValue'
Your constraint would get the actual List passed not its elements. If you are using Java 8 and the latest version of Hibernate Validator, you can use type argument constraints. You just have to make sure to also add ElementType.TYPE_USE to #Target in your constraint. Type argument constraints are not yet official part of Bean Validation, but will be in the next version of it (BV 2.0).
You would have something like this:
protected List<#KeyValid KeyValue> keyValues;
Alternatively, could you not put the #KeyValid constraint as class level constraint on KeyValue?

How to define an error message inside a custom #annotation

I'm building my own #annotation that valids many fields of a class (so it's a class level annotation and not a field level annotation).
When there is an error I add a ConstraintViolation and I print an error message taken from .properties file. The error is something like :
The field {1} must be less than the field {2}
What I need is the way to fill the variables {1} and {2} . And I have to do it inside the method isValid(), since is there that I dinamically define what are values to show inside the error message in place of {1} and {2}
This is my annotations:
#EsempioAnnotationClassLevel(dateFromNomeCampo={"dataDiNascitaFrom","dataLavoroFrom",...})
This is my interface:
#Constraint(validatedBy = EsempioAnnotationClassLevelValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface EsempioAnnotationClassLevel {
String[] dateFromNomeCampo();
String message() default "Errore FatherSearchInterface;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
this is my class that implements ConstraintValidator:
public class EsempioAnnotationClassLevelValidator implements ConstraintValidator<EsempioAnnotationClassLevel, Object>{
...
public boolean isValid(Object object, ConstraintValidatorContext cxt) {
...
cxt.buildConstraintViolationWithTemplate("errorMessage").addNode("field").addConstraintViolation();
...
}
...
}
Could you please check if the following question can help as reference?
Can you change an annotation message at run time?

Categories