[bv-dev] Metadata API to detect constrained methods and constructors
Emmanuel Bernard
emmanuel at hibernate.org
Tue Oct 16 13:06:45 EDT 2012
I put this proposal in.
On Tue 2012-09-11 18:35, Michael Nascimento wrote:
> Your proposal seems fine.
>
> Regards,
> Michael
>
> On Tue, Sep 11, 2012 at 6:32 PM, Emmanuel Bernard
> <emmanuel at hibernate.org> wrote:
> > 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
> > _______________________________________________
> > beanvalidation-dev mailing list
> > beanvalidation-dev at lists.jboss.org
> > https://lists.jboss.org/mailman/listinfo/beanvalidation-dev
> _______________________________________________
> beanvalidation-dev mailing list
> beanvalidation-dev at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/beanvalidation-dev
More information about the beanvalidation-dev
mailing list