Is possible to set validation order with Hibernate Validator? - java

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.

Related

Validating proxied Java Beans doesn't compare against correct values

I'm trying to make a CDI extension which will validate a Java object which is bound to configuration values.
public class ExampleConfig {
#Range(min = 1000, max = 9999)
private int value;
#Inject
public ExampleConfig(#ConfigProperty(name = "value") int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
When I make the above class with the #Singleton annotation, it works correctly. On startup the CDI extension validates the class which reads an environment variable called "value".
Class: com.example.ExampleConfig
Property: value
Value: 22222
Reason: must be between 1000 and 9999
When I replace #Singleton with #ApplicationScoped instead, when injecting and using this class myself, it works as intended, but in the CDI extension, javax.validation.Validator appears to always treat the value as 0.
Class: com.example.ExampleConfig$Proxy$_$$_WeldClientProxy
Property: value
Value: 0
Reason: must be between 1000 and 9999
I'm struggling to see why this is the case, is anyone able to provide guidance on how to read the value correctly?
Two things I've been trying to achieve to no avail is:
Have the extension enforce initialization on startup for respective classes.
Make the CDI extension wait until the bean has initialized.
The following is how I'm calling #validate:
public void afterDeploymentValidation(#Observes AfterDeploymentValidation adv, BeanManager bm) {
Set<ConstraintViolation<?>> allViolations = new HashSet<>();
for (Class<?> type : types)
{
final Object typeImpl = BeanProvider.getContextualReference(bm, type, false);
Set<ConstraintViolation<?>> violations = (Set<ConstraintViolation<?>>)(Object)validator.validate(typeImpl);
allViolations.addAll(violations);
}
// Omitted for brevity.
}
Several things:
First of all, if all you're trying to do is get Bean Validation working, just put the Hibernate Validator CDI project on your runtime classpath. Nothing else needed; the end.
If you're doing something else, you're probably running into the fact that a contextual reference to a bean in a normal scope is a client proxy. In less stuffy terms, that means it's a proxy, a shell, a holder—and its "innards" (its referent, the thing it is proxying) is not "inflated" until some method is called on the proxy, like toString() or a business method. I'm guessing that what's happening in your case is the validator is looking for validatable fields directly on the proxy.
One way to "inflate" a contextual reference is to just call toString() on it right away before doing something else. So just call typeImpl.toString() before you do anything else to "inflate" the reference.
I don't think there's any guarantee that the proxy will somehow magically make the proxied object's fields available to you. For that, you'll need to get the inner proxied object. Each CDI implementation does this a little differently, but in Weld you can get this programmatically with some casting.

Map a collection with parameter with mapstruct

To map a certain object with mapstruct I need some custom post processing which needs an additional parameter to do it's work:
#Mapper
public abstract class AlertConfigActionMapper {
#Mappings({ #Mapping(target = "label", ignore = true)})
public abstract AlertConfigActionTO map (AlertConfigAction action, Locale userLanguage);
#AfterMapping
public void setLabel (AlertConfigAction action, #MappingTarget AlertConfigActionTO to, Locale userLanguage) {
for (AlertConfigActionLabel label : action.getAlertConfigActionLabels()) {
if (label.getLanguage().equals(userLanguage)) {
to.setLabel(label.getLabel());
break;
} else if (label.getLanguage().equals(Locale.ENGLISH)) {
to.setLabel(label.getLabel());
}
}
}
}
This works just fine.
The problem starts when I add following method to this mapper:
public abstract ArrayList<AlertConfigActionTO> mapList (List<AlertConfigAction> actions, Locale userLanguage);
I need to pass this parameter (userLanguage) as well but mapstruct seems to 'break down' in this case: I generates following code for this part (which naturally gives a compilation error):
#Override
public List<AlertConfigActionTO> mapList(List<AlertConfigAction> actions, Locale userLanguage) {
if ( actions == null && userLanguage == null ) {
return null;
}
List<AlertConfigActionTO> list = new List<AlertConfigActionTO>();
return list;
}
I'm sure it is related to the parameter since if I remove it (from all mapping methods) then the mapList method is generated correctly.
What is needed to be done to allow custom parameters in this case?
What you describe is not possible (yet). Could you open a feature request in our issue tracker? We should provide means of denoting parameters as some sort of "context" which is passed down the call stack.
As a work-around for the time being, you might take a look at using a ThreadLocal which you set before invoking the mapping routine and which you access in your after-mapping customization. It's not elegant - and you need to make sure to clean up the thread local to avoid memory leaks - but it should do the trick.
I know that this question is quiet old, but I run into this issue, and starting at version 1.2 of mapstruct you can resolve it using #Context
So declaring the mapping for the list need to be like this :
public abstract ArrayList<AlertConfigActionTO> mapList (List<AlertConfigAction> actions, #Context Locale userLanguage);
Now, you juste need to add another non abstract mapping like this :
public AlertConfigActionTO mapConcrete (AlertConfigAction action, #Context Locale userLanguage){
return map (action, userLanguage);
}
I don't think it is possible. At least not that way. Problem is that you prepare interface/abstract class - and rest is done by the engine. And that engine expects methods with one parameter... There are decorators, but they go the same way. I would try to inject language. Create bean, mark it as session scoped, and find out. With Spring, you would use ScopedProxyMode for that... Not sure how that goes with CDI.
Other option is more workaround, then solution - maybe that AlertConfigAction can pass that information?

How can I make Spring Framework 's #Cachable work with lastModified property of a File as key?

This is the code I have:
#Cacheable(value = "configurationCache", key = "#myFile.lastModified()")
private Object foo(File myFile) throws IOException {
System.out.println(myFile.lastModified());
try {
Thread.sleep(6000);
} catch (InterruptedException ignored) {
}
final Object foo = new SomeObjectFromFile(myFile);
return foo;
}
I call this method twice passing file objects that have the same lastmodified value but caching does not work, the method will wait for 6 seconds.
Here is the output I am getting:
1456298573000
1456298573000
What am I doing wrong?
key = "#myFile.lastModified"
did not work either..
I am sure my configuration with ehcache is fine.
Juliens answer is probably the right one assuming you do not use aspectj. its not alone invoking a public method, but invoking a public method of an object where spring had the chance to wrap it's proxies around. So make sure you are injecting the service that you want to have enhanced with cacheable support.
For example
#Service
public SomeService {
#Autowired
private CacheEnhancedService css;
public void doSomething() {
css.getConfig(new File("./file"));
}
}
#Service
public CacheEnhancedService {
#Cacheable(value = "configurationCache", key = "#myFile.lastModified()")
public Object getConfig(File myFile) {
...
}
}
}
The issue lies with the fact that your method is private.
As mentioned in the documentation of the Spring Framework:
Method visibility and cache annotations
When using proxies, you should apply the cache annotations only to
methods with public visibility. If you do annotate protected, private
or package-visible methods with these annotations, no error is raised,
but the annotated method does not exhibit the configured caching
settings. Consider the use of AspectJ (see below) if you need to
annotate non-public methods as it changes the bytecode itself.
[...]
In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
caching at runtime even if the invoked method is marked with
#Cacheable - considering using the aspectj mode in this case. Also,
the proxy must be fully initialized to provide the expected behaviour
so you should not rely on this feature in your initialization code,
i.e. #PostConstruct.
You should either switch to a public method and make and external call or user AspectJ.

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!

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

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.

Categories

Resources