I need your feedback on whether or not you need fine controls on method validation.
Some context
Bean Validation 1.1 introduces the idea of method validation. When the method is called, parameters and return value can be validated. The constraints are of course defined as Bean Validation constraint annotations.
I am working on the chapter describing how interceptor technologies like CDI, EJB, Spring, Guice, AspectJ should integrate it.
We have decided to convert most of the recommendations into mandatory rules. In particular, methods annotated with constraints should be validated by the integration technology by default.
Early in the design we have introduced an annotation @MethodValidated
that lets you control a few things:
- which group should be used for validation (defaulting to
Default
) - what part should be validated: parameters, return value, both or none
This annotation made sense when validation was not on by default but I am now questioning its usefulness.
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. They also go from easy to more convoluted. Are you up for the challenge?
Note that I have added a bonus question in the end.
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?
public class UserService {
@MethodValidated(validationMode=NONE)
public void createUser(
@NotEmpty @Email String email,
@Valid Address address ) {
...
}
}
If you have a use case, would it be fulfilled with the @MethodValidated
annotation as described?
What's your use case for changing the default group?
@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?
@MethodValidated.validationMode
let's you validate both method
parameters as well as return value, or either one of them or none at all.
@MethodValidated(validationMode=PARAMETERS)
public class UserService {
@Valid
public User createUser(
@NotEmpty @Email String email,
@Valid Address address ) {
...
}
}
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:
@MethodValidated
definitions on a method overrides the ones on a class@MethodValidated
definition on a subclass overrides the ones on superclasses
Here is an example
//example of rule 1
@MethodValidated(validationMode=PARAMETERS)
public class UserService {
@MethodValidated(validationMode=BOTH)
@Valid
public User createUser(
@NotEmpty @Email String email,
@Valid Address address ) {
...
}
}
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 hosted @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 ;)
What do you make of that?
You are a convinced @MethodValidated fan? What about the name?
We have never found a good name for this annotation anyways. If you like and want this annotation, how should it be named?
Yep that's the bonus question, sorry.
Conclusion
I realize that it must look like I am having a @MethodValidated
mid-life crisis but better now than later :D
Now that everybody is returning from their summer holidays, also the Bean Validation team is getting back to their desks in order to work with full steam towards revision 1.1.
As you know, the largest new feature will be method validation, that is the validation of method parameters and return values using constraint annotations. Bean Validation 1.1 early draft 1 lays the ground for this, and right now we're tackling some advanced questions still open in that area (btw. if you haven't yet tried out the reference implementation of ED1, this is the perfect time to do so and give us your feedback).
The problem
One question the EG currently is discussing is whether and, if so, how a refinement of method constraints should be allowed in sub-types. That is, if a class implements a method of an interface or overrides a method from a super class, should the sub-type be allowed to place any additional constraints?
The current draft defines the following rules for such cases (see the draft document for all the gory details):
- No parameter constraints may be specified in addition to those constraints defined on the method in the interface or super class.
- Return value constraints may be added in sub-types.
The rationale
The rationale behind this is the principle of
behavioral sub-typing, which
demands that wherever a given type T
is used, it should be possible to replace T
with
a sub-type S
of T
. This means that a sub-type must not strengthen a method's
preconditions (by adding parameter constraints), as this might cause client code working
correctly against T
to fail when working against S
. A sub-type may also not weaken a
method's postconditions. However, a sub-type may strengthen the method's postconditions
(by adding return value constraints), as client code working against T
still will work
against S
.
Can you show me some code, please?
To give you an example, the following shows a constraint declaration considered illegal as
of the current draft, as parameter constraints are added to the placeOrder()
method in a
sub-class of OrderService
:
public class OrderService {
void placeOrder(@NotNull String customerCode, @NotNull Item item, int quantity) { ... }
}
public class SimpleOrderService extends OrderService {
@Override
public void placeOrder(
@Size(min=3, max=20) String customerCode,
Item item,
@Min(1) int quantity) { ... }
}
Alternatives
While this approach works, follows principles of clean OO design and also is employed by other Programming by Contract solutions, some voices in the EG expressed doubts whether the handling of parameter constraints isn't too restrictive and thus may limit innovation in that area. In particular with respect to legacy code, the question was raised whether it shouldn't be allowed to add parameter constraints in sub-types.
One example may be a legacy interface, which technically has no constraints (that is, no parameter constraints are placed on its methods), but comes with a verbal description of preconditions in its documentation. In this case an implementor of that interface might wish to implement this contract by placing corresponding constraint annotations on the implementation.
An open question in this situation is what should the behavior be if the interface is being constrained afterwards?
Give use your feedback!
So what do you think, should such a refinement of parameter constraints be allowed or not? Possible alternatives:
- allow such a refinement by default
- have some sort of switch controlling the behavior (either standardized or provider-specific)
As there are pro's and con's of either approach, we'd very interested in user feedback on this.
Let us know what you think by posting a comment directly to this blog, shooting a message to the mailing list or participating in this Doodle vote. Which use cases you have encountered come to mind where the possibility to refine parameter constraints may help you?
Latest news
Stay up to date, subscribe to the news feed.