Monday, November 17, 2014

Java Validation standard JSR-303

Every application needs to do some validation, and it varies from simple checking on values to complex business rules, like verifying if the user bill has been paid and payment received before we are able to call the print bill API.

 So the java people have created a standard for us under the number JSR-303 that creates a pretty nice way to validate our entities. javax.validatio, inside the validation-api.jar, is the API package for this standard.

The most popular implementation for that API is Hibernate-validator gradle dependencies : compile 'org.hibernate:hibernate-validator:5.0.1.Final'




What mainly is in the validation-api jar are the constraints and the generic interface ConstraintValidator, these are what I'm going to work with here.

there are two methods in this interface the one that does the work is the isValid method, this what will be called, and this what we need to implement  if we need a custom validation rule.

the standard rules are the following :
AssertFalse, AssertTrue, DecimalMax, DecimalMin, Digits, Future, Max, Min, NotNull, Null, Past,
Pattern, Size.

and they are used like this

class MyClass {

@NotNull
private String id;

}

this is the simplest way of using the validation API, you dont have to do more, just put the hibernate validator in the class path, and instantiate your validator and use it, see this very basic example :
[ it's important that you understand the very basics about validation to continue]
http://hibernate.org/validator/documentation/getting-started/

but in most cases we need to tweak things a bit for our business needs. and what we need more is also integrating this to be an autonomous process, and not call validator in every method, unless there is an explicit need for that.

so the way to do that is by using aspects, and the way I like it is on business logic APIs, but it is up to you to choose where you need to put that and I'll explain two ways, but first let's delegate the validator instantiation to the Spring framework

Spring supports the hibernate validator out of the box (the spring jar for this is spring-context.jar)

in our container configuration we add the following:


this will handle creating the validator instance for us.
the messageSource( ) is my custom messages source for i18n purposes, but it's not required by every one since hibernate validator already provide default messages, you can check them out here:

anyway let's proceed with the two possible locations where we can use the validation

1- If we are using Spring dispatcher servlet, we can out of the box put validation on our Rest/MVC controllers

2- On the business logic beans api.



General preparation:

now we add the validation rules to the user entity:


This what you will think at first will be enough, but actually there are couple of problems in this approach..

You see, now the business objects like the User object is no longer a dummy object just to hold data, it has business logic and that logic is in the validation annotations..it's now part of the API itself so we should use it that way and the best way I found to do that is to create a Business object for each call..

 otherwise, there may be conflicts in validation rules, like in the update method we don't want to validate the password again, it shouldn't be in the user object in the first place, another thing is that we may add another field : passwordConfirmation and validate that it match the password field.

so to correctly place our logic, I prefer to create a Business object for different APIs, example for create API:


and for the update API BO :


this is better, each API has it's own validation rule and does one thing, and does it right.
it's not repetition as it may seem, these are totally different APIs and each has it's own work, but it all comes down to your business logic.

To implement the rule that the password and passwordConfirmation should be the same, we should do that with custom validation annotation.




and the validator :



and based on this our BO :



Note that the last rule is put on the validated class instead of the field and the constraint validator takes Object as the class parameter.


Now regarding where to tell Spring to validate an object or not:


Option #1 - validate on web tier

Spring people like everything to be done on the controller level and they have their reasons and I can think one reason like:

what to do if business services call each other, should we validate or assume we are working with validated data?

Assume our controller will look like this without validation:



Reminder: make sure you provide the validator( ) bean with the same name, don't change it to something like : beanValidator( ) since Spring won't be happy.

What we need to do to tell spring to validate the objects is to add @Valid annotation



Spring will throw MethodArgumentNotValidException [ this is a custom spring exception ] in case of invalid objects, you can handle this exception using @ExceptionHandler annotation, I'll do another blog about ExceptionHandling.

Option#2- Validation on BL


To validate on Business layer API we need use aspects, to do that we need to register a bean MethodValidationPostProcessor :










and to trigger the validation we need to do 2 things

1- add @Valid annotation to the method parameters
2- add @Validated to the class/interface we are calling


this will throw ConstraintViolationException [original exception by validation-api]

here is how I handle this and convert it into my custom object :



Notes:

Database validation

You can use the custom ConstraintValidator to communicate with spring beans, it will do that by default if you use validation on Controller layer, but in case of method validator [BL layer], make sure to register the beans correctly.

here is an example of my code using bean to validate object:


Rules Order can be defined by the annotation @GroupSequence



and that's all you can gracefully start using the validation-api JSR-303

1 comment:

  1. Thank you for this great article.

    I just have a problem with MethodValidationPostProcessor: if my Controller calls a bean annotated with @Transactional, the annotation is no longer handled and no transaction is created.

    It is an annoying side effect, do you have a work around ?

    Thanks !

    ReplyDelete

Note: Only a member of this blog may post a comment.

Istio —simple fast way to start

istio archeticture (source istio.io) I would like to share with you a sample repo to start and help you continue your jou...