Current datetime shouldnt pass the validation using #Past annotation - bean-validation

I need the #Past to error when the field is set to now. I realize that the now value on the field, and the now value used when the validator is comparing would be slightly different, thus the need to set the tolerance in hibernate validator.
Problem is that i can not get this to work. Here is the junit:
#Test
public void testHibernateValidator_withPast_withTodayDate() {
// populates with 'now'
MyFormWithPast form = new MyFormWithPast();
form.setDt(OffsetDateTime.now(Clock.systemUTC()));
ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
.configure()
.clockProvider(() -> Clock.systemUTC())
// adds tolerance so that when comparing, the form dt and 'now' is considered equal,
// therefore dt is not a past datetime
.temporalValidationTolerance(Duration.ofMinutes(1))
.buildValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<MyFormWithPast>> errors = validator.validate(form);
// needs to fail, since 'now' shouldn't be considered 'past'
assertFalse("now shoudnt be considered as Past", errors.isEmpty());
}
public static class MyFormWithPast {
#Past
private OffsetDateTime dt;
public void setDt(OffsetDateTime dt) {
this.dt = dt;
}
public OffsetDateTime getDt() {
return dt;
}
}
I expect the validation to fail when i put in 'now' in the field, as 'now' shouldnt be considered as 'past'. What did i miss ?

The temporal validation tolerance was designed to be more lenient, not stricter. You want it to be stricter.
I think you will need your own constraints to deal with what you want to do.

Just want to share my current solution, adding a default of 1 minute forward tolerance so that inputted 'now' is not considered a 'past'.
The annotation:
/**
* Validates that the date is of the past, with forward tolerance of 1 minute,
* to offset the time to create a 'now' instance to compare to.
* The usage is when user selects 'today' in the UI, we dont want it to be considered as 'Past'
* https://stackoverflow.com/questions/60341963/current-datetime-shouldnt-pass-the-validation-using-past-annotation
* Annotation is applicable to {#link OffsetDateTime}.
*/
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy=StrictPastValidator.class)
public #interface StrictPast {
public static final String MESSAGE = "{constraints.StrictPast.message}";
/**
* #return The error message template.
*/
String message() default MESSAGE;
/**
* #return The groups the constraint belongs to.
*/
Class<?>[] groups() default { };
/**
* #return The payload associated to the constraint
*/
Class<? extends Payload>[] payload() default {};
}
The validator:
public class StrictPastValidator implements ConstraintValidator<StrictPast, Object> {
#Override
public void initialize(StrictPast annotation) {
}
#Override
public boolean isValid(Object input, ConstraintValidatorContext ignored) {
if (input == null) {
return true;
} else if (input instanceof OffsetDateTime) {
return isValidOffsetDateTime((OffsetDateTime) input);
}
throw new IllegalStateException("StrictPastValidator is not applicable to the field type " + input.getClass().getName());
}
private boolean isValidOffsetDateTime(OffsetDateTime input) {
OffsetDateTime plusSecondsDt = input.plusSeconds(Duration.ofMinutes(1).getSeconds());
return plusSecondsDt.isBefore(OffsetDateTime.now(Clock.systemUTC()));
}
}

Related

How to use hibernate-validator to validate interface parameters

I have the following code to implement interface input parameter validation and now want to use hibernate-validator to do this
public class Order
{
private String orderNo;
private String orderId;
private String status;
private String startTime;
private String endTime;
//getter and setter...
}
public class OrderService
{
public Object search(Order order) throws Exception
{
String message = "";
if (order.getOrderId().isEmpty() && order.getOrderNo().isEmpty() && order.getStatus().isEmpty())
{
if (order.getStartTime().isEmpty() && order.getEndTime().isEmpty())
message = "xxx";
}
if (!message.isEmpty())
throw new Exception(message);
Object result = null;
// splice sql according to the attribute of order and get the result
// result = sql query result
return result;
}
}
I tried to use Hibernate-validator's group to achieve this, but if there are more parameters, I need to write a lot of groups, which seems stupid. I have more than 100 interfaces, and will be added later, using Class-level constraints would be a good idea choice?
Below is the code trying to use Hibernate-validator's group implementation:
public class Order
{
#Empty(groups = One.class)
#NotEmpty(groups = Two.class)
private String orderNo;
#Empty(groups = One.class)
#NotEmpty(groups = Three.class)
private String orderId;
#Empty(groups = One.class)
#NotEmpty(groups = Four.class)
private String status;
#NotEmpty(groups = One.class)
private String startTime;
#NotEmpty(groups = One.class)
private String endTime;
}
public class BeanValidatorUtils
{
static Validator validator;
static
{
HibernateValidatorConfiguration configuration = Validation.byProvider(HibernateValidator.class).configure();
ValidatorFactory factory = configuration.failFast(true).buildValidatorFactory();
validator = factory.getValidator();
}
public static <T> void validation(T beanParam) throws AppException
{
if (!containsGroup(beanParam, One.class))
return;
Set<ConstraintViolation<T>> validate = validator.validate(beanParam, One.class);
ConstraintViolation<T> constraintViolation = validate.iterator().next();
String firstViolationMessage = constraintViolation.getMessage();
if (!validate.isEmpty() && containsGroup(beanParam, Two.class))
{
validate = validator.validate(beanParam, Two.class);
}
if (!validate.isEmpty() && containsGroup(beanParam, Three.class))
{
validate = validator.validate(beanParam, Three.class);
}
if (!validate.isEmpty())
throw new AppException(firstViolationMessage);
}
private static boolean containsGroup(Object bean, Class<?> groupClazz)
{
// ...
}
}
Is there any other way to use Hibernate-validator to verify the Order in the search method?
As you are trying to make a validation decision based on the state of multiple properties of the Order you might want to explore these 3 options:
Class level constraint
This would mean that you have to create your own constraint annotation (let's say #ValidOrder) and a corresponding ValidOrderValidator
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = { ValidOrderValidator.class })
#interface ValidOrder {
String message() default "{message.key}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
public class ValidOrderValidator implements ConstraintValidator<ValidOrder, Order> {
#Override
public boolean isValid(Order order, ConstraintValidatorContext constraintValidatorContext) {
//null values are valid
if ( order == null ) {
return true;
}
if (order.getOrderId().isEmpty() && order.getOrderNo().isEmpty() && order.getStatus().isEmpty()) {
if ( order.getStartTime().isEmpty() && order.getEndTime().isEmpty() ) { return false; }
}
return true;
}
}
You can also check this post for more detailed info on how to add new constraints using ServiceLoader.
#ScriptAssert constraint
If your validation logic is relatively simple and you either already have a dependency or are willing to add one for a scripting engine, you can consider using the #ScriptAssert constraint. This is similar to the previous option but you don't need to create annotations and validator implementations you just have to put script logic into this constraint:
#ScriptAssert(lang = "groovy", script = "your validation script logic")
class Order {
//...
}
#AssertTrue constraint
Last but not least, one of the easiest ways to address such validation is to use #AssertTrue constraint on a getter with validation logic inside the Order class:
class Order {
//...
#AssertTrue
public boolean isValidOrder() {
// your validation logic
}
}
Using any of these 3 approaches, you'd be able to make a validation decision based on multiple properties of the Order class.
As for validation group usage - you can leverage using the groups if you need to pass the same Order object into multiple different methods/interfaces where a different set of validation rules need to be applied in each of them. Let's say, in one case, you have to create an order, and half of the fields can be null, but then in the other - you want to update it, and everything should be present.

Custom validator throws exception javax.validation.UnexpectedTypeException: HV000030

I want to force user to send numeric value for a field in request, as user may enter char as well.
Since I haven't found any built in solution in spring mvc validation, I chose to create my own custom validator to check the entered value is number or not.
Please find below code snippet.
Constraint interface :
#Documented
#Constraint(validatedBy = {IntegerValidator.class})
#Target({ METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
public #interface IntegerConstraint {
String message() default "Please enter numers only...!!!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validator class :
public class IntegerValidator implements ConstraintValidator<IntegerConstraint, String> {
#Override
public void initialize(IntegerConstraint contactNumber) {
}
#Override
public boolean isValid(String reqParam, ConstraintValidatorContext cxt) {
return !StringUtils.isEmpty(reqParam) && reqParam.matches("^(0|[1-9][0-9]*)$");
}
}
DTO class field :
#IntegerConstraint
#PositiveOrZero(message = "Sorting number either can be positive or zero...!!!")
private Integer sortOrd;
Controller :
public ModelAndView addDetail(#Valid #ModelAttribute("fooDetails") FooDTO footDTO,
BindingResult result, HttpServletRequest request)
Error log :
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'com.eps.customvalidator.IntegerConstraint' validating type 'java.lang.Integer'. Check configuration for 'sortOrd'
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getExceptionForNullValidator(ConstraintTree.java:108) ~[hibernate-validator-6.0.10.Final.jar:6.0.10.Final]
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getInitializedConstraintValidator(ConstraintTree.java:140) ~[hibernate-validator-6.0.10.Final.jar:6.0.10.Final]
at org.hibernate.validator.internal.engine.constraintvalidation.SimpleConstraintTree.validateConstraints(SimpleConstraintTree.java:55) ~[hibernate-validator-6.0.10.Final.jar:6.0.10.Final]
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:73) ~[hibernate-validator-6.0.10.Final.jar:6.0.10.Final]
This might happened because your IntegerValidator is implementing ConstraintValidator<IntegerConstraint, String>, thus it is expected to validate String fields. But you are applying it on an Integer field.
You should either consider to change ConstraintValidator<IntegerConstraint, String> to ConstraintValidator<IntegerConstraint, Integer> or to change your DTO to private String sortOrd;

JavaFX TableView with simple xml model

For configuration I use simple xml. I also use this model for TableView. My problem is using of boolean. TableView needs BooleanProperty but simple xml cannot access to this object, obviously. How can I combine this without write big code?
Model
#Root(name="scriptdata")
#Order(elements={"title", "active"})
public class ScriptData {
#Element (required=true)
private String title;
#Element (required=false)
private BooleanProperty active;
/**
*
* #param title
* #param active
*/
public ScriptData() {
this.active = new SimpleBooleanProperty(active);
}
public boolean isActive() {
return active.getValue();
}
public void setActive(boolean active) {
this.active.set(active);
}
CellFactory
modulActiveColumn.setCellValueFactory(new PropertyValueFactory<>("active"));
modulActiveColumn.setCellFactory(CheckBoxTableCell.forTableColumn(modulActiveColumn));
modulActiveColumn.setOnEditCommit((EventHandler<CellEditEvent>) t -> {
((ScriptData) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setActive((boolean) t.getNewValue());
}
My problem is using of boolean. TableView needs BooleanProperty
You're wrong. In fact the TableView never gains access to the BooleanProperty object stored in the active field of it's items.
PropertyValueFactory uses reflection to
Access a property object by invoking a method with the constructor parameter concatenated with "Property". (This method would be called activeProperty() in your case).
If the above doesn't work it wraps the value returned by a the getter for the property in a ObservableValue. (The name of the getter in this case is getActive() or isActive).
In your case the cellValueFactory does something similar to the following factory
modulActiveColumn.setCellValueFactory(cellData -> new SimpleBooleanProperty(cellData.getValue().isActive()));
Using a boolean field to store the data achieves exactly the same result in your case. The drawback of this approach is that programatic updates of the property do not trigger an update of the TableView and the edits need to be handled manually.
#Root(name="scriptdata")
#Order(elements={"title", "active"})
public class ScriptData {
#Element (required=true)
private String title;
#Element (required=false)
private boolean active;
/**
*
* #param title
* #param active
*/
public ScriptData() {
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}

Type check in JSR-303 custom validator initialize method

I'm attempting to create a class level JSR-303 validation definition that checks that one property occurs before another in time. Because the this validation only makes sense for Calendar properties I was wondering if it is possible to test the property type in the initialize method.
My annotation definition is:
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = TemporalSequenceValidator.class)
#Documented
public #interface TemporalSequence {
String message() default "{uk.co.zodiac2000.vcms.constraints.TemporalSequence}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String first();
String second();
}
and the validator implementation:
public class TemporalSequenceValidator implements
ConstraintValidator<TemporalSequence, Object> {
private String firstFieldName;
private String secondFieldName;
#Override
public void initialize(final TemporalSequence constraintAnnotation) {
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
// Is it possible to test type of firstFieldName and
// secondFieldName properties here?
}
#Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
// omitted
}
}
Is this a sensible thing to do? What approach would you suggest I use if it is? And what action should occur if the properties are not of the correct type?
You can't really do the check in initialize() since you can't access the validated object there. Instead you could check the type of the fields of the validated object in isValid() using reflection:
if ( !Calendar.class.isAssignableFrom(
value.getClass().getField( firstFieldName ).getType() ) ) {
throw new ValidationException( "Field " + firstFieldName + " is not of type Calendar." );
}

Grails bind request parameters to enum

My Grails application has a large number of enums that look like this:
public enum Rating {
BEST("be"), GOOD("go"), AVERAGE("av"), BAD("ba"), WORST("wo")
final String id
private RateType(String id) {
this.id = id
}
static public RateType getEnumFromId(String value) {
values().find {it.id == value }
}
}
If I have a command object such as this:
class MyCommand {
Rating rating
}
I would like to (for example) automatically convert a request parameter with value "wo" to Rating.WORST.
The procedure for defining custom converters is described here (in the context of converting Strings to Dates). Although this procedure works fine, I don't want to have to create a class implementing PropertyEditorSupport for each of my enums. Is there a better alternative?
I found a solution I'm pretty happy with.
Step 1: Create an implementation of PropertyEditorSupport to convert text to/from the relevant Enum
public class EnumEditor extends PropertyEditorSupport {
private Class<? extends Enum<?>> clazz
public EnumEditor(Class<? extends Enum<?>> clazz) {
this.clazz = clazz
}
public String getAsText() {
return value?.id
}
public void setAsText(String text) {
value = clazz.getEnumFromId(text)
}
}
Step 2: Define a class that registers EnumEditor as a converter for the various enum classes. To change the list of enum classes that are bindable by id, just modify BINDABLE_ENUMS
public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
private static final String REQUIRED_METHOD_NAME = 'getEnumFromId'
// Add any enums that you want to bind to by ID into this list
private static final BINDABLE_ENUMS = [Rating, SomeOtherEnum, SomeOtherEnum2]
public void registerCustomEditors(PropertyEditorRegistry registry) {
BINDABLE_ENUMS.each {enumClass ->
registerEnum(registry, enumClass)
}
}
/**
* Register an enum to be bound by ID from a request parameter
* #param registry Registry of types eligible for data binding
* #param enumClass Class of the enum
*/
private registerEnum(PropertyEditorRegistry registry, Class<? extends Enum<?>> enumClass) {
boolean hasRequiredMethod = enumClass.metaClass.methods.any {MetaMethod method ->
method.isStatic() && method.name == REQUIRED_METHOD_NAME && method.parameterTypes.size() == 1
}
if (!hasRequiredMethod) {
throw new MissingMethodException(REQUIRED_METHOD_NAME, enumClass, [String].toArray())
}
registry.registerCustomEditor(enumClass, new EnumEditor(enumClass))
}
}
Step 3: Make Spring aware of the registry above by defining the following Spring bean in grails-app/conf/spring/resources.grooovy
customPropertyEditorRegistrar(CustomPropertyEditorRegistrar)
So the default Databinding binds on the Enum name and not a separately defined property of the Enum. You can either create your own PropertyEditor as you have mentioned or do a work-around similar to this:
class MyCommand {
String ratingId
Rating getRating() {
return Rating.getEnumFromId(this.ratingId)
}
static constraints = {
ratingId(validator:{val, obj -> Rating.getEnumFromId(val) != null })
}
}

Resources