Can I change the property path in a ConstraintValidator for Method arguments?

robert_difalco picture robert_difalco · Mar 19, 2014 · Viewed 9.7k times · Source

If you are familiar with the Bean Validation Framework you know that you cannot get the name of a method argument. So if you do a @NotNull constraint on the first argument of a method and the validation fails the getPropertyPath will be something like "arg1".

I would like to create my own version of @NotNull that can take a value e.g. @NamedNotNull( "emailAddress" ). But I can't figure out how to override the #getPropertyPath in my Validator? Is there any way to do this or am I stuck with "arg1" or "arg2", etc.

EDIT

Based on the answer I received I was able to come up with the following implementation that allows me to take the value from my @QueryParam or @PathParam annotations and use those as the property path for Bean Validation annotations like @NotNull.

For Jersey you need to create the following class. Note the implementation of DefaultParameterNameProvider:

public class ValidationConfigurationContextResolver implements ContextResolver<ValidationConfig> {
    @Override
    public ValidationConfig getContext( final Class<?> type ) {
        final ValidationConfig config = new ValidationConfig();
        config.parameterNameProvider( new RestAnnotationParameterNameProvider() );
        return config;
    }

    static class RestAnnotationParameterNameProvider extends DefaultParameterNameProvider {

        @Override
        public List<String> getParameterNames( Method method ) {
            Annotation[][] annotationsByParam = method.getParameterAnnotations();
            List<String> names = new ArrayList<>( annotationsByParam.length );
            for ( Annotation[] annotations : annotationsByParam ) {
                String name = getParamName( annotations );
                if ( name == null )
                    name = "arg" + ( names.size() + 1 );

                names.add( name );
            }

            return names;
        }

        private static String getParamName( Annotation[] annotations ) {
            for ( Annotation annotation : annotations ) {
                if ( annotation.annotationType() == QueryParam.class ) {
                    return QueryParam.class.cast( annotation ).value();
                }
                else if ( annotation.annotationType() == PathParam.class ) {
                    return PathParam.class.cast( annotation ).value();
                }
            }

            return null;
        }
    }
}

Then in your RestConfig you need to add the following line:

register( ValidationConfigurationContextResolver.class );

That's it. Now your ConstraintValidationExceptions will contain the name of the QueryParam or PathParam they are annotated with. For example:

 public void getUser( 
     @NotNull @QueryParam( "emailAddress" ) String emailAddress,
     @NotNull @QueryParam( "password" ) String password ) 
 { ... }

Answer

Hardy picture Hardy · Mar 19, 2014

If you are familiar with the Bean Validation Framework you know that you cannot get the name of a method argument

That's not quite correct. Bean Validation specifies the concept of a ParameterNameProvider which allows you to provide your own implementation. Hibernate Validator integrates with ParaNamer to provide parameter names. See the Validator online docs for more information. Once Validator supports Java 8, it will also support the Java 8 parameter naming facility.

IMO, you should give ParaNamer a go.