[bv-dev] Metadata API to detect constrained methods and constructors

Michael Nascimento misterm at gmail.com
Tue Sep 11 17:35:26 EDT 2012


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


More information about the beanvalidation-dev mailing list