001/*
002 * Copyright 2012-2017 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 *      http://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.boot.autoconfigure.validation;
018
019import org.springframework.beans.BeansException;
020import org.springframework.beans.factory.DisposableBean;
021import org.springframework.beans.factory.InitializingBean;
022import org.springframework.beans.factory.NoSuchBeanDefinitionException;
023import org.springframework.boot.validation.MessageInterpolatorFactory;
024import org.springframework.context.ApplicationContext;
025import org.springframework.context.ApplicationContextAware;
026import org.springframework.validation.Errors;
027import org.springframework.validation.SmartValidator;
028import org.springframework.validation.Validator;
029import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean;
030import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
031
032/**
033 * {@link Validator} implementation that delegates calls to another {@link Validator}.
034 * This {@link Validator} implements Spring's {@link SmartValidator} interface but does
035 * not implement the JSR-303 {@code javax.validator.Validator} interface.
036 *
037 * @author Stephane Nicoll
038 * @author Phillip Webb
039 * @since 2.0.0
040 */
041public class ValidatorAdapter implements SmartValidator, ApplicationContextAware,
042                InitializingBean, DisposableBean {
043
044        private final SmartValidator target;
045
046        private final boolean existingBean;
047
048        ValidatorAdapter(SmartValidator target, boolean existingBean) {
049                this.target = target;
050                this.existingBean = existingBean;
051        }
052
053        public final Validator getTarget() {
054                return this.target;
055        }
056
057        @Override
058        public boolean supports(Class<?> clazz) {
059                return this.target.supports(clazz);
060        }
061
062        @Override
063        public void validate(Object target, Errors errors) {
064                this.target.validate(target, errors);
065        }
066
067        @Override
068        public void validate(Object target, Errors errors, Object... validationHints) {
069                this.target.validate(target, errors, validationHints);
070        }
071
072        @Override
073        public void setApplicationContext(ApplicationContext applicationContext)
074                        throws BeansException {
075                if (!this.existingBean && this.target instanceof ApplicationContextAware) {
076                        ((ApplicationContextAware) this.target)
077                                        .setApplicationContext(applicationContext);
078                }
079        }
080
081        @Override
082        public void afterPropertiesSet() throws Exception {
083                if (!this.existingBean && this.target instanceof InitializingBean) {
084                        ((InitializingBean) this.target).afterPropertiesSet();
085                }
086        }
087
088        @Override
089        public void destroy() throws Exception {
090                if (!this.existingBean && this.target instanceof DisposableBean) {
091                        ((DisposableBean) this.target).destroy();
092                }
093        }
094
095        /**
096         * Return a {@link Validator} that only implements the {@link Validator} interface,
097         * wrapping it if necessary.
098         * <p>
099         * If the specified {@link Validator} is not {@code null}, it is wrapped. If not, a
100         * {@link javax.validation.Validator} is retrieved from the context and wrapped.
101         * Otherwise, a new default validator is created.
102         * @param applicationContext the application context
103         * @param validator an existing validator to use or {@code null}
104         * @return the validator to use
105         */
106        public static Validator get(ApplicationContext applicationContext,
107                        Validator validator) {
108                if (validator != null) {
109                        return wrap(validator, false);
110                }
111                return getExistingOrCreate(applicationContext);
112        }
113
114        private static Validator getExistingOrCreate(ApplicationContext applicationContext) {
115                Validator existing = getExisting(applicationContext);
116                if (existing != null) {
117                        return wrap(existing, true);
118                }
119                return create();
120        }
121
122        private static Validator getExisting(ApplicationContext applicationContext) {
123                try {
124                        javax.validation.Validator validator = applicationContext
125                                        .getBean(javax.validation.Validator.class);
126                        if (validator instanceof Validator) {
127                                return (Validator) validator;
128                        }
129                        return new SpringValidatorAdapter(validator);
130                }
131                catch (NoSuchBeanDefinitionException ex) {
132                        return null;
133                }
134        }
135
136        private static Validator create() {
137                OptionalValidatorFactoryBean validator = new OptionalValidatorFactoryBean();
138                validator.setMessageInterpolator(new MessageInterpolatorFactory().getObject());
139                return wrap(validator, false);
140        }
141
142        private static Validator wrap(Validator validator, boolean existingBean) {
143                if (validator instanceof javax.validation.Validator) {
144                        if (validator instanceof SpringValidatorAdapter) {
145                                return new ValidatorAdapter((SpringValidatorAdapter) validator,
146                                                existingBean);
147                        }
148                        return new ValidatorAdapter(
149                                        new SpringValidatorAdapter((javax.validation.Validator) validator),
150                                        existingBean);
151                }
152                return validator;
153        }
154
155}