JSR-303 validated property does not have a corresponding accessor for Spring data binding

10,181

This seems to me a problem of the SpringValidatorAdapter. It seems that this adapter post processes the constraint violations and expects that the violations match existing entity properties. This is not a requirement from the Bean Validation side. I can see that in your custom constraint validator in some cases you are building a custom error. Is this the case when the exception occurs?

I am not a Spring expert, but I think there are multiple ways to integrate Bean Validation in Spring, especially if you only need JSR 303. Have you for example looked at the LocalValidatorFactoryBean?

Share:
10,181
Admin
Author by

Admin

Updated on June 21, 2022

Comments

  • Admin
    Admin almost 2 years

    0.4 and hibernate validator 4.1.0.Final, I created a custom validator for validating password and confirmPassword fields, but the validator thrown the following exception:

    00:16:11,891 DEBUG [org.hibernate.validator.resourceloading.PlatformResourceBundleLocator] (http--192.168.1.20-8080-6) ValidationMessages not found.
    00:16:11,892 DEBUG [org.hibernate.validator.resourceloading.PlatformResourceBundleLocator] (http--192.168.1.20-8080-6) org.hibernate.validator.ValidationMessages found
    00:16:11,894 DEBUG [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver] (http--192.168.1.20-8080-6) Resolving exception from handler [deliveries.manager.controller.AdminSignUpController@455901d2]: org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Failed to invoke handler method [public java.lang.String deliveries.manager.controller.AdminSignUpController.submitProduct(javax.servlet.http.HttpServletRequest,org.springframework.ui.Model,deliveries.manager.pagebean.AdminSignupBean,org.springframework.validation.BindingResult,org.springframework.ui.ModelMap)]; nested exception is java.lang.IllegalStateException: JSR-303 validated property 'password.confirmPassword' does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)
    00:16:11,897 DEBUG [org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver] (http--192.168.1.20-8080-6) Resolving exception from handler [deliveries.manager.controller.AdminSignUpController@455901d2]: org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Failed to invoke handler method [public java.lang.String deliveries.manager.controller.AdminSignUpController.submitProduct(javax.servlet.http.HttpServletRequest,org.springframework.ui.Model,deliveries.manager.pagebean.AdminSignupBean,org.springframework.validation.BindingResult,org.springframework.ui.ModelMap)]; nested exception is java.lang.IllegalStateException: JSR-303 validated property 'password.confirmPassword' does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)
    00:16:11,900 DEBUG [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (http--192.168.1.20-8080-6) Resolving exception from handler [deliveries.manager.controller.AdminSignUpController@455901d2]: org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Failed to invoke handler method [public java.lang.String deliveries.manager.controller.AdminSignUpController.submitProduct(javax.servlet.http.HttpServletRequest,org.springframework.ui.Model,deliveries.manager.pagebean.AdminSignupBean,org.springframework.validation.BindingResult,org.springframework.ui.ModelMap)]; nested exception is java.lang.IllegalStateException: JSR-303 validated property 'password.confirmPassword' does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)
    00:16:11,902 DEBUG [org.springframework.web.servlet.DispatcherServlet] (http--192.168.1.20-8080-6) Could not complete request: org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Failed to invoke handler method [public java.lang.String deliveries.manager.controller.AdminSignUpController.submitProduct(javax.servlet.http.HttpServletRequest,org.springframework.ui.Model,deliveries.manager.pagebean.AdminSignupBean,org.springframework.validation.BindingResult,org.springframework.ui.ModelMap)]; nested exception is java.lang.IllegalStateException: JSR-303 validated property 'password.confirmPassword' does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)
            at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:181) [spring-web-3.0.4.RELEASE.jar:3.0.4.RELEASE]
            at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:427) [spring-webmvc-3.0.4.RELEASE.jar:3.0.4.RELEASE]
    

    Here is the bean class:

    @CompareStrings(propertyNames={"password", "confirmPassword"}, message="Password and Comfirm Password must be matched.")
    public class AdminSignupBean {
       private String password = "";
            private String confirmPassword = "";
       private String emailAddress = "";
            private String firstName = "";
            private String lastName = "";
            private String gender = "M";
            private String loginName = "";
            private String address1 = "";
            private String address2 = "";
            private String suburb = "";
            private String state = "";
            private String postcode = "";
            private String phone = "";
    ...
    <settings and getters>
    

    Validator class:

    public class CompareStringsValidator implements ConstraintValidator<CompareStrings, Object> {
    
        public enum StringComparisonMode {
            EQUAL, EQUAL_IGNORE_CASE, NOT_EQUAL, NOT_EQUAL_IGNORE_CASE
        }
    
        private String[] propertyNames;
        private StringComparisonMode comparisonMode;
        private boolean allowNull;
    
        @Override
        public void initialize(CompareStrings constraintAnnotation) {
            this.propertyNames = constraintAnnotation.propertyNames();
            this.comparisonMode = constraintAnnotation.matchMode();
            this.allowNull = constraintAnnotation.allowNull();
        }
    
        @Override
        public boolean isValid(Object target, ConstraintValidatorContext context) {
            boolean isValid = true;
            List<String> propertyValues = new ArrayList<String> (propertyNames.length);
            for(int i=0; i<propertyNames.length; i++) {
                String propertyValue = ConstraintValidatorHelper.getPropertyValue(String.class, propertyNames[i], target);
                if(propertyValue == null) {
                    if(!allowNull) {
                        isValid = false;
                        break;
                    }
                } else {
                    propertyValues.add(propertyValue);
                }
            }
    
            if(isValid) {
                isValid = ConstraintValidatorHelper.isValid(propertyValues, comparisonMode);
            }
    
            if (!isValid) {
              /*
               * if custom message was provided, don't touch it, otherwise build the
               * default message
               */
              String message = context.getDefaultConstraintMessageTemplate();
              message = (message.isEmpty()) ?  ConstraintValidatorHelper.resolveMessage(propertyNames, comparisonMode) : message;
    
              context.disableDefaultConstraintViolation();
              ConstraintViolationBuilder violationBuilder = context.buildConstraintViolationWithTemplate(message);
              for (String propertyName : propertyNames) {
                NodeBuilderDefinedContext nbdc = violationBuilder.addNode(propertyName);
                nbdc.addConstraintViolation();
              }
            }    
    
            return isValid;
        }
    }
    

    Controller class:

    public String submitProduct(HttpServletRequest req, Model model, 
              @ModelAttribute("adminsignup") @Valid final AdminSignupBean adminsignup,
                             BindingResult result, ModelMap map)
       {   
          UUID pbid=adminsignup.getPbid();
                    logger.debug("====adminsignup:"+adminsignup.toString());
    
                    if (result.hasErrors()) {
                        AdminSignupBean admin = products.get(pbid);
                        req.getSession().setAttribute("pbid", pbid);
                        BeanUtils.copyProperties(adminsignup, admin, new String[] {"pbid", "mpf", "password", "confirmPassword"});
                        map.addAttribute("adminsignupbean",admin);
    
                        return "adminsignup/createadmin";
                    }
                    Set<ConstraintViolation<AdminSignupBean>> violations 
                            = validatorFactory.getValidator().validate(adminsignup);
                    for(ConstraintViolation<AdminSignupBean> violation : violations) {
                        logger.debug("=========Violation Message:- " + violation.getMessage());
                    }
                    if (!violations.isEmpty()) {
                        logger.debug("=====violations messages:"+violations.toString());
                        AdminSignupBean admin = products.get(pbid);
                        req.getSession().setAttribute("pbid", pbid);
                        BeanUtils.copyProperties(adminsignup, admin, new String[] {"pbid", "mpf", "password", "confirmPassword"});
                        map.addAttribute("adminsignupbean",admin);
    
                        return "adminsignup/createadmin";
                    }
    

    Any suggestion is very appreciated. Thanks Sam