What is the method signature means? String value() default ""; - java

I came across this method signature in Spring Component interface.
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Indexed
public #interface Component
{
String value() default "";
}
What is the method signature String value() default ""; means?
How and When should we define such syntax for our coding purposes?

This is no method signature. it means that you can pass a String as parameter to the Component annotation, like this:
#Component(value = "value")
If you don't specify a value your self, the default value "" will be used.
If it had been like this:
String value(); // without the default
value would have been a mandatory parameter. Trying to use Component like this:
#Component()
would lead to an Exception, since you didn't provide a value.
EDIT: when to use.
If you don't know much about this syntax, or annotations in general, you shouldn't use them. About everything that can be done using annotations, especially custom made ones, can also be done without annotations.
Let's say you want to create an annotation to validate the value of a field.
I'll be using the example of Belgian postal codes. They all are 4 digits, and are between 1000 and 9999.
#Target( {ElementType.FIELD})
#Retention( RetentionPolicy.RUNTIME)
#Constraint( validatedBy = ValidatePostalCodeImpl.class)
public #interface ValidatePostalCode{
String message() default "You have entered an invalid postal code";
Class<?>[] groups() default {}; // needed for the validation
Class<? extends Payload>[] payload() default{}; // needed for the validation
int maxValue() default 9999; // so, by default, this will validate based
int minValue() default 1000; // on these values, but you will be able to
// override these
}
/* Validation implementation */
public class ValidatePostalCodeImpl implements ConstraintValidator<ValidatePostalCode, Integer> {
int upperValue;
int lowerValue;
#Override
public void initialize(ValidatePostalCode validate) {
this.upperValue = validate.maxValue(); // here you call them as if they indeed were regular methods
this.lowerValue = validate.minValue();
}
#Override
public boolean isValid(Integer integer, ConstraintValidatorContext context) {
return integer >= lowerValue && integer <= upperValue;
}
}
/* Usage */
#Entity
#Table(name = "addresses")
public class Addresses {
// home address -> In Belgium, so has to be between the default values:
#ValidatePostalCode
Integer belgianPostalCode;
// vacation home in another country, let's say the PC's there are between
// 12000 and 50000
#ValidatePostalCode(minValue = 12000, maxValue = 50000)
Integer foreignPostalCode;
}
Sure, this is a very limited example, but it should get you an idea.

The #interface keyword is used to define an annotation. This annotation has a property called value, which you could specify explicitly:
#Component(value = "myValue")
Or in the shorthand form:
#Component("myValue")
If you don't specify the value, it will default to "", as defined by the default keyword.

Related

openapi 3.0 valid minimum and maximum values

I have below schema definition to represent commission amount in my openapi contract.
commissionAmount:
type: number
minimum: -99999.99
maximum: 99999.99
Generated Code:
#Valid
#DecimalMin("-99999.99") #DecimalMax("99999.99")
public BigDecimal getCommissionAmount() {
return commAmt;
}
The generated code is good and as expected. I just wanted to know are these -99999.99 and 99999.99 valid values for minimum and maximum.
The reason for asking this question is it does not check the limit in the fractional part. For example, I expect 12345.678 is invalid , 12345.67 is valid. But it marks both as valid.
I read #Digits is used to check for the digit limit of integer and fractional part. How do I tell openapi-generator-maven-plugin to annotate Digits as well?
Expected Generated Code:
#Valid
#Digits(integer = 5, fraction = 2)
#DecimalMin("-99999.99") #DecimalMax("99999.99")
public BigDecimal getCommissionAmount() {
return commAmt;
}
The way to specify this in OpenAPI would be using multipleOf:
commissionAmount:
type: number
minimum: -99999.99
maximum: 99999.99
multipleOf: 0.01
However, using the OpenAPI Generator will not produce an annotation for this. The reason being is that there is no javax.validation annotation that can represent multipleOf effectively (imagine trying to express multipleOf: 0.02- #Digits would be insufficient).
However, you can create your own annotation as this user has: https://github.com/OpenAPITools/openapi-generator/issues/2192#issuecomment-575132233
With the following annotation and validator:
#Target({METHOD, FIELD})
#Retention(RUNTIME)
#Repeatable(MultipleOf.List.class)
#Constraint(validatedBy = MultipleOfValidator.class)
public #interface MultipleOf {
double value();
String message() default "{error.multipleOf}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default { };
#Target({ METHOD, FIELD })
#Retention(RUNTIME)
#Documented
#interface List {
MultipleOf[] value();
}
}
public class MultipleOfValidator implements ConstraintValidator<MultipleOf, Number> {
private double value;
#Override
public void initialize(MultipleOf constraintAnnotation) {
this.value = constraintAnnotation.value();
}
#Override
public boolean isValid(Number value, ConstraintValidatorContext context) {
return (value == null) || (value.doubleValue() / this.value) % 1 == 0;
}
}
You would then be able to fork the generator and add your new annotation to the template: https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/Java/beanValidationCore.mustache
With a line like this:
{{#multipleOf}}#MultipleOf({{multipleOf}}){{/multipleOf}}

Validate UUID Restful service

I have a RESTful service which receives POST request with UUID values and writes them in DB. So the problem is to validate if UUID is valid or not. For this purpose I implemented custom annotation:
#Constraint(validatedBy = {})
#Target({ElementType.FIELD})
#Retention(RUNTIME)
#Pattern(regexp = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[34][0-9a-fA-F]{3}-[89ab][0-9a-fA-F]{3}-[0-9a-fA-F]{12}")
public #interface validUuid {
String message() default "{invalid.uuid}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
But for some reason it doesn't work, even if I pass valid UUID I constantly get an exception:
javax.validation.UnexpectedTypeException: HV000030: No validator
could be found for constraint 'javax.validation.constraints.Pattern'
validating type 'java.util.UUID'
Are there any options to validate UUID properly?
You cannot apply the #Pattern annotation to something (java.util.UUID) that is not a CharSequence. From the #Pattern annotation documentation (emphesizes mine):
Accepts CharSequence. null elements are considered valid.
Moreover, as far as I see you try to extend the behavior of the validation annotation handler by passing it to the new annotation definition.
If you wish to perform more complex validation, simply create your annotation without another validation annotations - their combining doesn't work like this. There must be something to recognize annotations and validate them.
#Target({ElementType.FIELD})
#Retention(RUNTIME)
#Constraint(validatedBy = UuidValidator.class)
public #interface ValidUuid {
String message() default "{invalid.uuid}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Now, create a validator which implements ConstraintValidator<ValidUuid, UUID> and override the methods performing the validation itself.
public class UuidValidator implements ConstraintValidator<ValidUuid, UUID> {
private final String regex = "....." // the regex
#Override
public void initialize(ValidUuid validUuid) { }
#Override
public boolean isValid(UUID uuid, ConstraintValidatorContext cxt) {
return uuid.toString().matches(this.regex);
}
}
And apply the annotation:
#ValidUuid
private UUID uuId;
you can use UUID.fromString(...); and catch IllegalArgumentException

Making a Jersey bean validation annotation generic

I have a Jersey Rest API like this:
#POST
#Path("/doorder")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces("text/plain")
public String doOrder(#BeanParam final #Valid OrderBean order) {
// Some implementation here
}
All my inputs are store in this bean:
#AddressAtLeastOne
public final class OrderBean {
#FormDataParam("address")
private String address;
#FormDataParam("city")
private String city;
#FormDataParam("postcode")
private String postcode;
// Other member variables
// Getters and setters
}
I added an annotation to validate the address (#AddressAtLeastOne). The address is valid if at least one of the 3 fields has a value.
Here's the annotation definition:
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
#Retention(RUNTIME)
#Constraint(validatedBy = AddressAtLeastOneValidator.class)
#Documented
public #interface AddressAtLeastOne {
String message() default "Address requires at least one field";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And here's the validator:
public class AddressAtLeastOneValidator implements ConstraintValidator<AddressAllOrNone, OrderBean> {
#Override
public boolean isValid(OrderBean demoBean, ConstraintValidatorContext constraintValidatorContext) {
// Check for at least one value
if((demoBean.getAddress() != null && !demoBean.getAddress().equals("") ||
(demoBean.getCity() != null && !demoBean.getCity().equals("")) ||
(demoBean.getPostcode() != null && !demoBean.getPostcode().equals("")))) {
return true;
}
return false;
}
}
Everything is fine! But now I want to rename the annotation #AddressAtLeastOne to #AtLeastOne and make it generic, so that I can apply it to any class. I need a mechanism where I can specify which member variables are part of the group I want to validate with #AtLeastOne. How can I do that?
One approach of doing this is to use Reflection -
Create a custom annotation suppose #GroupNotNullField and apply this annotation to all fields in bean class in which at least one field should have value. By this way, you can skip some fields in which validation is not required.
In the validator class, get all the fields of the bean class using Reflection
Check all the fields which are annotated with #GroupNotNullField annotation
Get the value of all such fields and check that at least one has value.
Return true or false depending on validation check.

Using Spring #Value inside #Size

I am using Spring Boot and javax validation, particularly #Size.
I am trying to grab the values for size constraints from the application.properties file:
#Size(min= #Value("${device.name.minsize}"), max=#Value("${device.name.maxsize}"))
private String name;
But I receive the following compile time error:
Error:(26, 16) java: annotation not valid for an element of type int
Trying to fix this issue I'm attempting the following:
#Size(min=Integer.parseInt( #Value("${device.name.minsize}") ), max=Integer.parseInt( #Value("${device.name.maxsize}") ) )
But this has multiple errors as well.
How can I convert the #Value annotations correctly?
Am I headed down the wrong path?
What I am looking for is a clean way to pull size limitations out of code and into configuration that I can access server side and in my templated angularJS/html.
I don't think you'll be able to do that. Annotations require constant values as their parameters, since they need to be handled at compile time.
You could externalize the xml:
http://beanvalidation.org/1.1/spec/#xml-config
Alternatively, if you just want to use JSR-303 annotation metadata in AngularJS, you might have a look at Valdr and Valdr BeanValidation:
https://github.com/netceteragroup/valdr
https://github.com/netceteragroup/valdr-bean-validation
For yet another approach take a look at https://github.com/jirutka/validator-spring.
It allows you to use SpSEL expressions in bean validation annotations including config properties.
You won't be able to use the standard annotations like #Size though, you'd have to formulate the constraints as SpEL expressions.
The bad news: there's no way to do what you want with standard annotations from Java Validation API.
The good news: you can easily create a custom annotation that does exactly what you want.
You need to create a custom validation annotation (let's call it #ConfigurableSize) that takes as parameters two strings, one for the name of the property holding the min size and one for the name of the property holding the max size.
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
#Repeatable(ConfigurableSize.List.class)
#Constraint(validatedBy = {ConfigurableSizeCharSequenceValidator.class})
public #interface ConfigurableSize {
String message() default "size is not valid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String minProperty() default "";
String maxProperty() default "";
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
#Documented
#interface List {
ConfigurableSize[] value();
}
}
The validator will retrieve the property values upon initialization, then it will perform the exact same validation checks as the #Size constraint. Even the constraint violation will have the exact same message. Please notice that if the property name is omitted the min and max will default respectively to 0 and Integer.MAX_VALUE, i.e. the same defaults for #Size.
public class ConfigurableSizeCharSequenceValidator implements ConstraintValidator<ConfigurableSize, CharSequence> {
private final PropertyResolver propertyResolver;
private int min;
private int max;
#Autowired
public ConfigurableSizeCharSequenceValidator(PropertyResolver propertyResolver) {
this.propertyResolver = propertyResolver;
}
#Override
public void initialize(ConfigurableSize configurableSize) {
String minProperty = configurableSize.minProperty();
String maxProperty = configurableSize.maxProperty();
this.min = "".equals(minProperty) ? 0 :
propertyResolver.getRequiredProperty(minProperty, Integer.class);
this.max = "".equals(maxProperty) ? Integer.MAX_VALUE :
propertyResolver.getRequiredProperty(maxProperty, Integer.class);
validateParameters();
}
private void validateParameters() {
if (this.min < 0) {
throw new IllegalArgumentException("The min parameter cannot be negative.");
} else if (this.max < 0) {
throw new IllegalArgumentException("The max parameter cannot be negative.");
} else if (this.max < this.min) {
throw new IllegalArgumentException("The length cannot be negative.");
}
}
#Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
if (value == null) {
return true;
} else {
int length = value.length();
boolean retVal = length >= this.min && length <= this.max;
if (!retVal) {
HibernateConstraintValidatorContext hibernateContext =
context.unwrap(HibernateConstraintValidatorContext.class);
hibernateContext.addMessageParameter("min", this.min)
.addMessageParameter("max", this.max);
hibernateContext.disableDefaultConstraintViolation();
hibernateContext
.buildConstraintViolationWithTemplate("{javax.validation.constraints.Size.message}")
.addConstraintViolation();
}
return retVal;
}
}
}
You apply the custom annotation in your bean
public class Device {
#ConfigurableSize(minProperty = "device.name.minsize", maxProperty = "device.name.maxsize")
private String name;
}
Then finally in your application.properties you'll define the properties
device.name.minsize=4
device.name.maxsize=8
And that's it. You can find more details and a full example in this blog post:
https://codemadeclear.com/index.php/2021/03/22/easily-configure-validators-via-properties-in-a-spring-boot-project/

Can a custom validator have multiple messages based on what validation failed in hibernate validator?

I have a custom validation to check an email field in a class.
annotation interface:
#ReportAsSingleViolation
#NotBlank
#Email
#Target({ CONSTRUCTOR, FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = CustomEmailValidator.class)
#Documented
public #interface CustomEmail {
String message() default "Failed email validation.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
CustomEmailValidator class:
public class CustomEmailValidator implements ConstraintValidator<CustomEmail, String> {
public void initialize(CustomEmail customEmail) {
// nothing to initialize
}
public boolean isValid(String email, ConstraintValidatorContext arg1) {
if (email != null) {
String domain = "example.com";
String[] emailParts = email.split("#");
return (emailParts.length == 2 && emailParts[1].equals(domain));
} else {
return false;
}
}
}
I use a ValidationMessages.properties file for all of my custom messages. in the properties file I reference a failure in the above code using:
CustomEmail.email=The provided email can not be added to the account.
The problem is that this error message is used for all failures during validation, so even if the user provided a blank string it will print that message. What I want to do is if the validation fails on #NotBlank then print a "required field message", if it fails on #Email provide a "invalid email" message. Then only for when it fails the custom validation would it print the CustomEmail.email message. Also in my annotation interface do the #NotBlank and #Email occur in order or are they randomly run. Then what ever validation is run first is returned as the error? My validation requires that they run in the order they are listed #NotBlank followed by #Email followed by CustomEmail.
Turn off #ReportAsSingleViolation. You'll get a ConstraintViolation for each violation, and can work accordingly.
As for order, following #Hardy's answer.
Note that in your custom constraints there is no order defined. Even so you first list #NotBlank and then #Email, there is no order guarantee. Java itself does not define any order in annotations. If you want to define an order you need to use groups and/or group sequence. In this case you also cannot use a composed constraints, since group definition on the composing constraints are ignored. Only the group of the main constraint applies.
You could create an Enum defining the types of expected errors, and then use its value to retrieve the proper error message from your configuration. Something like
/**
<P>Defines the type of email-formatting error message.</P>
**/
public enum EmailErrorTypeIs {
/**
<P>...</P>
#see #INVALID
#see #OTHER
**/
BLANK,
/**
<P>...</P>
#see #BLANK
**/
INVALID,
/**
<P>...</P>
#see #BLANK
**/
OTHER;
};
Which you would use like this:
if(input == null || input.length() == 0) {
throw new IllegalArgumentException(getErrorFromConfig(EmailErrorTypeIs.BLANK));
} else if...

Categories

Resources