How can I change annotations/Hibernate validation rules at runtime? - java

If have a Java class with some fields I want to validate using Hibernate Validator.
Now I want my users to be able to configure at runtime which validations take place.
For example:
public class MyPojo {
...
#NotEmpty
String void getMyField() {
...
}
...
}
Let's say I want to remove the NotEmpty check or replace it with Email or CreditCardNumber, how can I do it? Is it even possible? I guess it comes down to changing annotations at runtime...

You can't do it normally.
Here's what I've done to get more dynamic validations working via Hibernate Validator.
Extend the ClassValidator class.
Override the getInvalidVaues(Object myObj) method. First, call super.getInvalidValues(myObj), then add the hook to your customized validation.
Instantiate your custom validator and call getInvalidValues to validate. Any hibernate annotated validations will kick off at this point, and your custom dynamic validations (anything not supported by annotations) will kick off as well.
Example:
public class MyObjectValidator extends ClassValidator<MyObject>
{
public MyObjectValidator()
{
super(MyObject.class);
}
public InvalidValue[] getInvalidValues(MyObject myObj)
{
List<InvalidValue> invalids = new ArrayList<InvalidValue>();
invalids.addAll(Arrays.asList(super.getInvalidValues(myObj)));
// add custom validations here
invalids.addAll(validateDynamicStuff(myObj));
InvalidValue[] results = new InvalidValue[invalids.size()];
return invalids.toArray(results);
}
private List<InvalidValue> validateDynamicStuff(MyObject myObj)
{
// ... whatever validations you want ...
}
}
So your custom validation code can contain logic like "Do this validation, if the user configured it, otherwise do that one", etc. You may or may not be able to leverage the same code that powers the hibernate validations, but either way, what you are doing is more involved that the 'normal' use case for hibernate validator.

Actually it is possible in hibernate validator 4.1. Just read the documentation about programatic constraint creation.

I don't think you'll be able to remove or change the annotation, it's part of the class definition. You can build a new class, which is possible at runtime but a little involved. Hibernate may support programmatic access to the validations and allow you to override the annotation, I don't know the API that well. Hibernate does a bit of runtime class building itself... that might be a good place to learn how to do it if you're interested.

Related

Play Framework annotation order

When I call fooMethod, I want to process first class annotation (with First.class - in my project this checks if user is logged) and then method annotation (with Second.class - in my project this checks if uses has desired rights to access this specific method. So I need to ensure user is logged first). Is there a way to do that?
#With(First.class)
public class Foo{
#With(Second.class)
public static void fooMethod(){
}
}
Also I wonder why custum action ignores annotation. Code below doesn't process anotation #With(First.class).
public class Foo2 extends Action<CustomAnnotation> {
#Override
#With(First.class)
public Promise<Result> call(Http.Context context) throws Throwable {
return delegate.call(context);
}
}
}
Similar unanswered question: Java + Play Framework 2 with nested action compositions in the same class
In Play 2.4 there appears to be an option for this from the docs here:
Note: If you want the action composition annotation(s) put on a
Controller class to be executed before the one(s) put on action
methods set play.http.actionComposition.controllerAnnotationsFirst =
true in application.conf. However, be aware that if you use a third
party module in your project it may rely on a certain execution order
of its annotations.
The note is only present in the 2.4 docs, so presumably it doesn't work in previous versions.
It looks like at least in Java 8 you have an order in annotation of the same kind: Java Annotations Reflection Ordering.

How do I manually invoke the Java Validation API?

Is there any particulars on when to use the annotations in java, or to put it more precisely, is the answer to the following question Yes or No
Only under this condition, can this particular annotations be used.
Obviously, the this in the above statement can be anything or nothing.
The reason I ask this question is that I was given this code base and in their they had used some annotations to validate the classes, properties etc. Now, this is a web application and it makes use of all the spring beans and what not.
I tried the following code. (This is just normal java code, no spring or anything.This is the entire code)
public class AnnotationsTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
TestMe t = new TestMe();
System.out.println(t.getTest1());
}
}
class TestMe{
#NotNull
private String test1;
public String getTest1() {
return test1;
}
public void setTest1(String test1) {
this.test1 = test1;
}
}
This one prints out null as output. Why is not any validation happening?
Well, what I am guessing is that I've provided the annotations for this, but have not really validated. So I went to this link. Therein he had all sorts of ValidationFactory and what not.
So I wrote,
public class AnnotationsTest{
private static Validator validator;
public static void main(String[] args) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
TestMe t = new TestMe();
Set<ConstraintViolation<TestMe>> constraintViolations =
validator.validate(t);
assertEquals(1, constraintViolations.size());
assertEquals("may not be null", constraintViolations.iterator().next().getMessage());
}
}
But this one give the following error
Exception in thread "main" javax.validation.ValidationException: Unable to find a default provider
at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:264)
at javax.validation.Validation.buildDefaultValidatorFactory(Validation.java:111)
at AnnotationsTest.main(AnnotationsTest.java:29)
So here's my questions:
1. What exactly happened in the two cases above?
2. I thought that annotations were enough, as in Autowired and stuff, they would just inject the classes on it's own. But here why do I need to do the extra stuff to validate. Why doesnt #NotNull validates the things on it's own.
Thanks.
What you need to understand about annotations is they are just metadata on code. Nothing more, nothing less.
In order for all the black magic to happen (in your case Validation, or in the case of #Autowired as you mentioned the registering into the Spring container), some other piece of code needs to read the class definitions, check and see if the annotations are present and if so, perform a while bunch of steps (which may include simple things like registering objects or more advanced things like creating proxies, manipulating bytecode etc.).
So in your case, although the metadata is present on the class in the form of annotations, however no code is present that actually handles them. In the case of validation, that code would be Hibernate Validator.
In order to use Hibernate Validator you could check out this link

How to distinguish between instances of class?

I have class Validator, which manage all validation criteria from files and database. But this criteria are loaded by Loader like this:
Validator validator = Loader.load("clients"); //get all from clients.cfg file
What is the best approach to determine from another class, which criteria are currently loaded?
Importer importer;
Validator clientsValidator = Loader.load("clients");
Validator addressValidator = Loader.load("address"); ...
importer.validate(data, clientsValidator, addressValidator);
public class Importer{
public void validate(Data data, Validator... validator){
...
validateClient(data, one of validators);
validateAddress(data, another of validator);
...
}
}
I need to know in Importer class, which Validator is for Clients, which for Addresses... Any good approaches?
The best way would be for you to be add a field and accompanying method to Validator to return the identifier (e.g. "clients") with which it was created.
Alternatively, if by using a different identifier when calling Loader.load() you get back instances of different classes implementing the Validator interface, then you can use the Object.getClass() method to tell those classes apart. If those classes are within a pretty small set you might even get away with using instanceof directly.
We would need more information, such as what Loader does exactly, what Validator is and how much you are allowed to change their code before being able to provide a more concrete answer.
EDIT:
Quite honestly, perhaps you should reconsider a redesign of your data model. As it stands, you can apparently mix clients and addresses without any checks. You should restructure your code to be able to rely on the type safety features of Java.
One way would be to have a generic class/interface Validator<T>, where T would the class of the validated objects:
public interface Validator<T> {
public boolean validate(T object);
}
You could then have specific Data subclasses for your data, such as Address or Client, and set typed Validator objects to Importer through specific methods:
public class Importer {
public void addAddressValidator(Validator<Address> validator) {
...
}
public void addClientValidator(Validator<Client> validator) {
...
}
}
This is far safer than mixing all validator objects in a single variadic method call, and it is also the preferred approach of most common frameworks in the wild.
Why not have a getSource() in Validator which gets set when Loader loads the source.
Thinking more about the specific question below :
I need to know in Importer class, which Validator is for Clients,
which for Addresses... Any good approaches?
Actually a better way to do this is if Loader can return a ClientValidator (implementation of Validator) for client and AddressValidator for addresses.
That way you can avoid the if-else conditions and directly call validate on the Validator class
Pass the validators by position. You must also check if the specific validator is null or not before you use.
public void validate(Data data,
Validator clientsValidator,
Validator addressValidator) {
...
if (clientsValidator != null) {
validateClient(data, clientsValidator);
}
if (addressValidator != null) {
validateAddress(data, addressValidator);
}
...
}

User defined Validations coupling issue

Have an API defined that clients will be able to utilize. Clients will be connected via a variety of means. One of them is java-to-java. In this specific case, am having an issue. Obviously, the API should be as decoupled from the implementation as possible. I haven't had the chance to test this yet, but won't user defined validations break this model?
I am enabling the Validation via Spring #Validated on the API server-side implementation. Did not want to put this into #Controller class as that is not the only way into the service (API).
For example, if I have this method defined in the Interface:
SomeObject updateOperation( AnInputClass param) ...
I can then annotate with JSR-303 validations and still be de-coupled:
#NonNull
SomeObject updateOperation( #NonNull AnInputClass param) ...
But if I want custom validation on the various pieces/parts of the input "param", I need to make my own Annotation, which has an #Constraint(validatedBy) part This part will tie to the validation implementation. The abbreviated form of this would look like:
SomeObject updateOperation ( #CheckInput AnInputClass param)...
...where the annotation is defined as
...
#Constraint(validatedBy = CheckInputValidator.class) // this is the coupling issue
public #interface CheckInput { ....
Since this all happens server-side, there should be no need to have Java clients have to have this CheckInputValidator class; however, I am seeing no options. First, I like having the validations in the API - they tell users what will be validated. If I could break the dependency and move the validation down to the implementation that would seem like an acceptable tradeoff. However that results in the exception below so it seems like I am stuck. Can anyone help?
javax.validation.ConstraintDeclarationException: Only the root method of an
overridden method in an inheritance hierarchy may be annotated with parameter
constraints, but there are parameter constraints defined at all of the
following overridden methods
Found the answer myself, I should have realized this earlier!
All I needed to do was to use the "#Valid" annotation in the interface/API layer. Then, making sure the #Target annotation on the User Defined / Custom annotation has "TYPE" defined, apply the #CheckInput annotation to the desired class and everything works perfectly!

Is possible to set validation order with Hibernate Validator?

I use #AssertTrue annotation to ensure the execution of a method that sets some default values (always returns true). These set values are validated as #NotEmpty (these are Strings). So I need to guarantee that method annotated with #AssertTrue is executed strictly before that fields annotated with #NotEmpty.
Simplified code example (not included Hibernate annotations):
public class MyClass {
#NotEmpty
private String myField = null;
#SuppressWarnings("unused")
#AssertTrue
private boolean fillDefaultValues() {
if (this.myField == null) {
this.myField = "default value";
}
return true;
}
}
This seems to me like a hack. For two reasons:
you always return true and assert it for the sole purpose of executing an initialization code
you expect the framework to access and validate your bean in a specific order in order to execute an initialization code
The thing in common is "initialization code". In order to achieve what you want, you can register a listener and execute the initialization method before the validation happens. Here's the documentation of hibernate-validator - it tells you about event listeners.
You can also manually set the default values in your service layer (since you seem to be using anemic data model). Since this seems like a business logic, it'd better be in the service method, before the object is persisted.
Finally I have solved my problem.
Debuging validator stack trace, I have seen that first it process beanValidators, and then, memberValidators. So the only I have to do is define my initialization code in a class constraint.
I have defined a new class annotation where, depending on the type of the pojo received, I set default values.
I have verified that this code is executed before than any other (member) constarint, like #NotEmpty, etc.

Categories

Resources