Should getters be considered methods during validation
by Emmanuel Bernard
For method validation, we have so far managed to get away with
requiring an annotation based metadata to direct how method validation
behaves.
One question that popped up during the recent write up is whether or not
getters should be considered regular methods and thus be intercepted and
validation by CDI or AspectJ interceptors.
I have my own ideas, but I'd like to get your opinion on the subject.
Emmanuel
11 years, 11 months
Wording clarification
by Emmanuel Bernard
Just to clarify to everyone as I am fixing a few discrepancies in the
spec around method validation.
Constraint *definition* means defining a new constraint type and its
validator implementations.
Constraint *declaration* means associating a constraint to a class,
property, parameter or return value.
Emmanuel
12 years, 1 month
Planned public draft for end of this week
by Emmanuel Bernard
For your info, to comply with some of the EE train constraints, I am
planning on providing the public draft of the specification to the JCP end of
this week.
Hardy, Gunnar and I are racing to add the various proposals that have
been floating around lately into the specification and API.
It feels natural to provide a new milestone release for all the work
that has been done lately. A good chunk of it is around refinements for
method validation but we will also have a few new features in.
A lot of work is being done to clarify and refine how CDI and Bean
Validation will interact. There has been a discussion with Pete Muir
(CDI spec lead) to try and find the best approach. These discussions are
mostly summarized in https://hibernate.onjira.com/browse/BVAL-307
I apologize for not giving a longer warning about this deadline. I have
been caught by JavaOne and us racing to put the features in concrete
words.
Emmanuel
12 years, 1 month
Metadata API to detect constrained methods and constructors
by Emmanuel Bernard
Hi all,
I am working on the section explaining how a interception technology
integrates with Bean Validation.
The proposal is also on the web site at http://beanvalidation.org/proposals/BVAL-274/
## Problem description
I was looking at how to compute whether or not method interception needs
to occur via on the metadata API.
The validation of method-level constraints comprises the following steps:
- Intercept the method call to be validated
- Validate the parameter values provided by the method caller using Validator#validateParameters() or Validator#validateConstructorParameters().
- If this validation yields a non-empty set of constraint violations, throw a ConstraintViolationException wrapping the violations. Otherwise proceed with the actual method invocation.
- Validate the result returned by the invoked method using Validator#validateReturnValue() or Validator#validateConstructorReturnValue().
- If this validation yields a non-empty set of constraint violations, throw a ConstraintViolationException wrapping the violations. Otherwise return the invocation result to the method caller.
I realised that we have a cumbersome API to detect whether or not
calling Bean Validation.
```
public boolean interceptMethod(Class<?> type, Method method) {
BeanDescriptor bean = validator.getConstraintsForClass( type );
MethodDescriptor methodDescriptor = bean.getConstraintsForMethod( method.getName(), method.getParameterTypes() );
return methodDescriptor != null;
}
public boolean validateMethodParametersWithFullNavigation(Class<?> type, Method method) {
BeanDescriptor bean = validator.getConstraintsForClass( type );
MethodDescriptor methodDescriptor = bean.getConstraintsForMethod( method.getName(), method.getParameterTypes() );
if ( methodDescriptor != null ) {
boolean validate = false;
for (ParameterDescriptor paramDescriptor : methodDescriptor.getParameterDescriptors()) {
validate = paramDescriptor.hasConstraints() || paramDescriptor.isCascaded();
if (validate) {
break;
}
}
return validate;
}
else {
return false;
}
}
public boolean validateReturnValueWithFullNavigation(Class<?> type, Method method) {
BeanDescriptor bean = validator.getConstraintsForClass( type );
MethodDescriptor methodDescriptor = bean.getConstraintsForMethod( method.getName(), method.getParameterTypes() );
if ( methodDescriptor != null ) {
boolean validate = false;
ReturnValueDescriptor returnValue = methodDescriptor.getReturnValueDescriptor();
if ( returnValue!=null ) {
return returnValue.isCascaded() || returnValue.hasConstraints();
}
else {
return false;
}
}
else {
return false;
}
}
```
`interceptMethod` is used to decide whether or not we intercept the method
at all. it can be used to disable the interceptor entirely.
`validateMethodParametersWithFullNavigation` is used to decide whether
or not we need to call `validator.validateParameters()`. If no violation
is found, we goa nd execute the method.
After the method returns, we call
`validateReturnValueWithFullNavigation` to decide whether or not to
validate return value.
There is of course the equivalent for constructor validation.
## Proposal
What I am proposing is to add two aggregate methods to
`MethodDescriptor` and `ConstructorDescriptor`.
```
/**
* Returns true if method parameters are constrained either:
* - because of a constraint on at least one of the parameters
* - because of a cascade on at least one of the parameters (via {@code @Valid})
* - because of at least a cross-parameter constraint
*/
boolean isConstrainedOnParameters();
/**
* Returns true if the method return value is constrained either:
* - because of a constraint on the return value
* - because validation is cascaded on the return value (via {@code @Valid})
*/
boolean isConstrainedOnReturnValue();
```
The implementation would then become
```
public boolean validateMethodParametersWithMethodDescriptorShortcuts(Class<?> type, Method method) {
BeanDescriptor bean = validator.getConstraintsForClass( type );
MethodDescriptor methodDescriptor = bean.getConstraintsForMethod( method.getName(), method.getParameterTypes() );
if ( methodDescriptor != null ) {
return methodDescriptor.isConstrainedOnParameters();
}
else {
return false;
}
}
public boolean validateMethodReturnValueWithMethodDescriptorShortcuts(Class<?> type, Method method) {
BeanDescriptor bean = validator.getConstraintsForClass( type );
MethodDescriptor methodDescriptor = bean.getConstraintsForMethod( method.getName(), method.getParameterTypes() );
if ( methodDescriptor != null ) {
return methodDescriptor.isConstrainedOnReturnValue();
}
else {
return false;
}
}
```
Of course the lookup of `BeanDescriptor` and `MethodDescriptor` only has
to be done once. The methods result is also likely being cached by the
interceptor being placed and these calls are probably only happening at
initialization time.
It seems to be it goes in the right
direction and expose things at a higher level for the metadata API
users.
Anyone against the proposed change?
An alternative solution is to use the fluent API to find constraints
public boolean validateMethodParametersWithFindConstraintAPI(Class<?> type, Method method) {
BeanDescriptor bean = validator.getConstraintsForClass( type );
MethodDescriptor methodDescriptor = bean.getConstraintsForMethod( method.getName(), method.getParameterTypes() );
if ( methodDescriptor != null ) {
return methodDescriptor.findConstraints().declaredOn( ElementType.PARAMETER ).hasConstraints();
}
else {
return false;
}
}
But it's not 100% in line with the `findConstraints()` API: it should only return constraints
that are on the element. Plus what is returned by the API is
`ConstraintDescriptor` which cannot differentiate where it comes from (parameter or method).
Emmanuel
12 years, 1 month
Why do we need @MethodValidated anyways?
by Emmanuel Bernard
I am still working on the chapter describing how interceptor tech
integrates bean validation. We have decided to convert most of the
recommendations into mandatory rules. In particular around having method
validation activated by default.
Having a annotation to control whether or not a method was validated
made sense when it was not enabled by default but I wonder if we still
need it.
I have a bunch of questions for you. I tried to keep them short and to
the point so feel free to answer them one by one.
## What's your use case for disabling method validation?
Why would you want to disable method validation on a given method or a
given class?
## What's your use case for changing the default group?
`(a)MethodValidated(groups=Heavy.class)` let's you change validation from
the `Default` group to the group of your choice - in this case `Heavy`.
Provided that we will offer support for group translation when cascading
<http://beanvalidation.org/proposals/BVAL-208/>
public class UserService {
public void createUser(
@NotEmpty @Email String email,
@Valid @ConvertGroup(from=Default.class, to=BasicPostal.class)
Address address ) {
...
}
}
do we really need the ability do decide which group to use to validate a
given method? What would be the use case?
To me it seems that it could makes sense to validate one group over
another based on:
- some environmental consideration
say a newbie user has more constraints on how it enters data
than an advanced user hence different groups
- the caller
say a branch of the code wants to apply different rules than
an other
In both case, it does not make sense to define the group via an
annotation on the method to be validated.
This would need to be a rather container specific behavior to let people
inject the right group for the right context.
## When would you want to only validate parameters or return values?
`(a)MethodValidated.validationMode` let's you validate both method
parameters as well as return value, or either one of them or none at all.
Do you have a use case in mind for such need?
## What inheritance rules make sense for `@MethodValidated`?
Assuming we have `@MethodValidated`, we need to define the overriding
rules.
We could decide that `@MethodValided` must be placed on the method to be
validated (no overriding rule), or we could try and add some or all of
the following rules:
1. `@MethodValidated` definitions on a method overrides the ones on a
class
2. `@MethodValidated` definition on a subclass overrides the ones on
superclasses
Interfaces make things harder as there would be no magic rule to decide
which definition has precedence over another in case of conflict.
We could consider that methods of a class implementing an interface
inherit the interface `@MethodValidated` definition (unless overridden).
And in case two interfaces define the same method, overriding the
`@MethodValidated` definition would be mandatory.
I can live with rule 1, I can support rule 2. but I feel that the rules
related to interfaces make things quite complex and not especially
readable. Plus I don't see why you would want to add `@MethodValidated`
on an interface. Not surprising as I don't see why one would do it on a
class method anyways ;)
## Conclusion
I realize that it must look like I am having a `@MethodValidated`
mid-life crisis but better now than later :D
12 years, 1 month