001/*
002 * Copyright 2002-2012 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.validation.beanvalidation;
018
019import java.util.Iterator;
020import java.util.Set;
021import javax.validation.ConstraintViolation;
022import javax.validation.Validation;
023import javax.validation.Validator;
024import javax.validation.ValidatorFactory;
025
026import org.springframework.beans.BeansException;
027import org.springframework.beans.factory.BeanInitializationException;
028import org.springframework.beans.factory.InitializingBean;
029import org.springframework.beans.factory.config.BeanPostProcessor;
030
031/**
032 * Simple {@link BeanPostProcessor} that checks JSR-303 constraint annotations
033 * in Spring-managed beans, throwing an initialization exception in case of
034 * constraint violations right before calling the bean's init method (if any).
035 *
036 * @author Juergen Hoeller
037 * @since 3.0
038 */
039public class BeanValidationPostProcessor implements BeanPostProcessor, InitializingBean {
040
041        private Validator validator;
042
043        private boolean afterInitialization = false;
044
045
046        /**
047         * Set the JSR-303 Validator to delegate to for validating beans.
048         * <p>Default is the default ValidatorFactory's default Validator.
049         */
050        public void setValidator(Validator validator) {
051                this.validator = validator;
052        }
053
054        /**
055         * Set the JSR-303 ValidatorFactory to delegate to for validating beans,
056         * using its default Validator.
057         * <p>Default is the default ValidatorFactory's default Validator.
058         * @see javax.validation.ValidatorFactory#getValidator()
059         */
060        public void setValidatorFactory(ValidatorFactory validatorFactory) {
061                this.validator = validatorFactory.getValidator();
062        }
063
064        /**
065         * Choose whether to perform validation after bean initialization
066         * (i.e. after init methods) instead of before (which is the default).
067         * <p>Default is "false" (before initialization). Switch this to "true"
068         * (after initialization) if you would like to give init methods a chance
069         * to populate constrained fields before they get validated.
070         */
071        public void setAfterInitialization(boolean afterInitialization) {
072                this.afterInitialization = afterInitialization;
073        }
074
075        @Override
076        public void afterPropertiesSet() {
077                if (this.validator == null) {
078                        this.validator = Validation.buildDefaultValidatorFactory().getValidator();
079                }
080        }
081
082
083        @Override
084        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
085                if (!this.afterInitialization) {
086                        doValidate(bean);
087                }
088                return bean;
089        }
090
091        @Override
092        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
093                if (this.afterInitialization) {
094                        doValidate(bean);
095                }
096                return bean;
097        }
098
099
100        /**
101         * Perform validation of the given bean.
102         * @param bean the bean instance to validate
103         * @see javax.validation.Validator#validate
104         */
105        protected void doValidate(Object bean) {
106                Set<ConstraintViolation<Object>> result = this.validator.validate(bean);
107                if (!result.isEmpty()) {
108                        StringBuilder sb = new StringBuilder("Bean state is invalid: ");
109                        for (Iterator<ConstraintViolation<Object>> it = result.iterator(); it.hasNext();) {
110                                ConstraintViolation<Object> violation = it.next();
111                                sb.append(violation.getPropertyPath()).append(" - ").append(violation.getMessage());
112                                if (it.hasNext()) {
113                                        sb.append("; ");
114                                }
115                        }
116                        throw new BeanInitializationException(sb.toString());
117                }
118        }
119
120}