Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring validation crashes with Hibernate Validation 5 style list constraint violations [SPR-15082] #19648

Closed
spring-projects-issues opened this issue Jan 2, 2017 · 1 comment
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jan 2, 2017

Sander van Schouwenburg opened SPR-15082 and commented

In Hibernate Validation 5 it is possible to let a validation error refer to a list element. However, the SpringValidatorAdapter does not cope with this situation.

Take the following code example:

public class ValidatorFactoryTests {
	
	public static class ListContainer {
		@NotXList
		private List<String> list = new LinkedList<>();
		
		public void addString(String value) {
			list.add(value);
		}
		
		public List<String> getList() {
			return list;
		}
	}

	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.FIELD)
	@Constraint(validatedBy=NotXListValidator.class)
	public static @interface NotXList {

		String message() default "Should not be X";

		Class<?>[] groups() default { };

		Class<? extends Payload>[] payload() default {};
	}


	public static class NotXListValidator implements ConstraintValidator<NotXList, List<String>> {

		@Override
		public void initialize(NotXList constraintAnnotation) {
		}

		@Override
		public boolean isValid(List<String> list, ConstraintValidatorContext context) {
			context.disableDefaultConstraintViolation();
			boolean valid = true;
			for (int i = 0; i < list.size(); i++) {
				if ("X".equals(list.get(i))) {
					context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addBeanNode().inIterable().atIndex(i).addConstraintViolation();
					valid = false;
				}
			}
			return valid;
		}
	}

	@Test
	public void testSpringValidatorWithListValidation() throws Exception {
		LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
		validator.afterPropertiesSet();

		ListContainer listContainer = new ListContainer();
		listContainer.addString("A");
		listContainer.addString("X");
		BeanPropertyBindingResult errors = new BeanPropertyBindingResult(listContainer, "listContainer");
		errors.initConversion(new DefaultConversionService());
		validator.validate(listContainer, errors);
		
		FieldError fieldError = errors.getFieldError("list[1]");
		assertEquals("X", errors.getFieldValue("list[1]"));
	}
}

This test will fail on the last line with the following IllegalArgumentException:

java.lang.IllegalArgumentException: Source to convert from must be an instance of [@org.springframework.validation.beanvalidation.ValidatorFactoryTests$NotXList java.lang.String]; instead it was a [java.util.LinkedList]
	at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:184)
	at org.springframework.validation.AbstractPropertyBindingResult.formatFieldValue(AbstractPropertyBindingResult.java:125)
	at org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:232)
	at org.springframework.validation.beanvalidation.ValidatorFactoryTests.testSpringValidatorWithListValidation(ValidatorFactoryTests.java:273)

This IllegalArgumentException mainly comes from the ConversionService, but without it the test will still fail, because errors.getFieldValue("list\[1\]") will result in the entire list, not just the failed element.

The problem (or potential fix) lies in SpringValidatorAdapter.getRejectedValue. The ConstraintViolation.getInvalidValue results in the value on which the annotation was defined (in this case the list itself). It seem to try to detect here whether it should it should re-obtain the value in case a custom path was supplied. However, it only checks for the existance of a "." in the fieldname or whether the validation annotation was on the entire command object. In this case the field was list\[1\] which does not pass the test.

A possible fix is to include the "[" character in the field test.

Affected Versions
I believe this affects all versions which have Hibernate Validation 5 support. Hibernate Validation 4 did not support a constraint violation of this type.


Affects: 4.3 GA

Issue Links:

Referenced from: commits d0e9328, de611cb

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jan 10, 2017

Juergen Hoeller commented

Good catch! Fixed for 4.3.6 now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants