-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add another example for a constraint validator with an injected bean #46130
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -411,6 +411,44 @@ The scope you choose for your `ConstraintValidator` bean is important: | |||||
- If the `ConstraintValidator` bean implements the `initialize(A constraintAnnotation)` method and depends on the state of the constraint annotation, use the `@Dependent` scope to make sure each annotation context has a separate and properly configured instance of the `ConstraintValidator` bean. | ||||||
==== | ||||||
|
||||||
When injecting beans relying on the runtime configuration, use `@Inject Instance<..>`. Since constraints are initialised | ||||||
at build time, it is not possible to completely preconfigure the constraint in the `initialize(..)` method, | ||||||
as the runtime information will be missing at that point. The `initialize(..)` method in such scenarios can still be used | ||||||
to read the constraint annotation parameters, and perform any work not relying on the runtime configuration. | ||||||
Hence, it is recommended that any heavy configuration work happens as part of the injected bean initialisation | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
and methods used in the `ConstraintValidator#isValid(..)` implementation are as fast as possible: | ||||||
|
||||||
[source,java] | ||||||
---- | ||||||
@ApplicationScoped | ||||||
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, String> { | ||||||
|
||||||
@Inject | ||||||
Instance<MyService> service; | ||||||
|
||||||
@Override | ||||||
public boolean isValid(String value, ConstraintValidatorContext context) { | ||||||
if (value == null) { | ||||||
return true; | ||||||
} | ||||||
|
||||||
return service.get().validate(value); | ||||||
} | ||||||
} | ||||||
|
||||||
@ApplicationScoped | ||||||
public class MyService { | ||||||
|
||||||
public MyService(..) { | ||||||
// perform all possible "initialisation" work | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're talking about runtime configuration but then we don't see it in the example? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hey 😃 👋🏻 would the following work here: @ApplicationScoped
public class MyService {
private final Predicate<String> validationFunction;
@Inject
public MyService(MyRuntimeConfigProperties config) {
// perform all possible "initialization" work, e.g.:
if (config.complexValidationEnabled()) {
validationFunction = s -> ...
} else {
validationFunction = String::isBlank;
}
}
public boolean validate(String value) {
// perform the validation
return validationFunction.test(value);
}
} |
||||||
} | ||||||
|
||||||
public boolean validate(String value) { | ||||||
// perform the validation | ||||||
} | ||||||
} | ||||||
---- | ||||||
|
||||||
=== Validation and localization | ||||||
|
||||||
By default, constraint violation messages will be returned in the build system locale. | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package io.quarkus.it.hibernate.validator.injection; | ||
|
||
import jakarta.enterprise.context.ApplicationScoped; | ||
import jakarta.enterprise.inject.Instance; | ||
import jakarta.inject.Inject; | ||
import jakarta.validation.ConstraintValidator; | ||
import jakarta.validation.ConstraintValidatorContext; | ||
|
||
@ApplicationScoped | ||
public class InjectedRuntimeConstraintValidator | ||
implements ConstraintValidator<InjectedRuntimeConstraintValidatorConstraint, String> { | ||
|
||
@Inject | ||
Instance<MyRuntimeService> service; | ||
|
||
@Override | ||
public boolean isValid(String value, ConstraintValidatorContext context) { | ||
if (value == null) { | ||
return true; | ||
} | ||
|
||
return service.get().validate(value); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package io.quarkus.it.hibernate.validator.injection; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
import jakarta.validation.Constraint; | ||
import jakarta.validation.Payload; | ||
|
||
@Target({ ElementType.TYPE_USE, ElementType.FIELD }) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Constraint(validatedBy = { InjectedRuntimeConstraintValidator.class }) | ||
public @interface InjectedRuntimeConstraintValidatorConstraint { | ||
|
||
String message() default "{InjectedRuntimeConstraintValidatorConstraint.message}"; | ||
|
||
Class<?>[] groups() default {}; | ||
|
||
Class<? extends Payload>[] payload() default {}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package io.quarkus.it.hibernate.validator.injection; | ||
|
||
import io.smallrye.config.ConfigMapping; | ||
|
||
@ConfigMapping(prefix = "io.quarkus.it.hibernate.validator.injection") | ||
public interface MyRuntimeConfiguration { | ||
|
||
String pattern(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package io.quarkus.it.hibernate.validator.injection; | ||
|
||
import java.util.regex.Pattern; | ||
|
||
import jakarta.enterprise.context.ApplicationScoped; | ||
import jakarta.inject.Inject; | ||
|
||
@ApplicationScoped | ||
public class MyRuntimeService { | ||
|
||
private final Pattern pattern; | ||
|
||
@Inject | ||
public MyRuntimeService(MyRuntimeConfiguration configuration) { | ||
this.pattern = Pattern.compile(configuration.pattern()); | ||
} | ||
|
||
public boolean validate(String value) { | ||
return pattern.matcher(value).matches(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
MyCustomConstraint.message = invalid MyOtherBean | ||
InjectedConstraintValidatorConstraint.message = InjectedConstraintValidatorConstraint violation | ||
InjectedRuntimeConstraintValidatorConstraint.message = InjectedRuntimeConstraintValidatorConstraint violation | ||
pattern.message=Value is not in line with the pattern |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use US English given the method is called
initialize()
: