Package org.springframework.boot.context.properties

Source Code of org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor$FlatPropertySources

/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.context.properties;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.boot.env.PropertySourcesLoader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.PropertySources;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

/**
* {@link BeanPostProcessor} to bind {@link PropertySources} to beans annotated with
* {@link ConfigurationProperties}.
*
* @author Dave Syer
* @author Phillip Webb
* @author Christian Dupuis
*/
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor,
    BeanFactoryAware, ResourceLoaderAware, EnvironmentAware, ApplicationContextAware,
    InitializingBean, DisposableBean, PriorityOrdered {

  public static final String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";

  private static final String[] VALIDATOR_CLASSES = { "javax.validation.Validator",
      "javax.validation.ValidatorFactory" };

  private ConfigurationBeanFactoryMetaData beans = new ConfigurationBeanFactoryMetaData();

  private PropertySources propertySources;

  private Validator validator;

  private boolean ownedValidator = false;

  private ConversionService conversionService;

  private DefaultConversionService defaultConversionService;

  private BeanFactory beanFactory;

  private ResourceLoader resourceLoader = new DefaultResourceLoader();

  private Environment environment = new StandardEnvironment();

  private ApplicationContext applicationContext;

  private int order = Ordered.HIGHEST_PRECEDENCE + 1;

  /**
   * @param order the order to set
   */
  public void setOrder(int order) {
    this.order = order;
  }

  /**
   * @return the order
   */
  @Override
  public int getOrder() {
    return this.order;
  }

  /**
   * @param propertySources
   */
  public void setPropertySources(PropertySources propertySources) {
    this.propertySources = propertySources;
  }

  /**
   * @param validator the validator to set
   */
  public void setValidator(Validator validator) {
    this.validator = validator;
  }

  /**
   * @param conversionService the conversionService to set
   */
  public void setConversionService(ConversionService conversionService) {
    this.conversionService = conversionService;
  }

  /**
   * @param beans the bean meta data to set
   */
  public void setBeanMetaDataStore(ConfigurationBeanFactoryMetaData beans) {
    this.beans = beans;
  }

  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    this.beanFactory = beanFactory;
  }

  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }

  @Override
  public void setEnvironment(Environment environment) {
    this.environment = environment;
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
  }

  @Override
  public void afterPropertiesSet() throws Exception {

    if (this.propertySources == null) {
      this.propertySources = deducePropertySources();
    }

    if (this.validator == null) {
      this.validator = getOptionalBean(VALIDATOR_BEAN_NAME, Validator.class);
      if (this.validator == null && isJsr303Present()) {
        this.validator = new Jsr303ValidatorFactory()
            .run(this.applicationContext);
        this.ownedValidator = true;
      }
    }

    if (this.conversionService == null) {
      this.conversionService = getOptionalBean(
          ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
          ConversionService.class);
    }
  }

  private boolean isJsr303Present() {
    for (String validatorClass : VALIDATOR_CLASSES) {
      if (!ClassUtils.isPresent(validatorClass,
          this.applicationContext.getClassLoader())) {
        return false;
      }
    }
    return true;
  }

  @Override
  public void destroy() throws Exception {
    if (this.ownedValidator) {
      ((DisposableBean) this.validator).destroy();
    }
  }

  private PropertySources deducePropertySources() {
    PropertySourcesPlaceholderConfigurer configurer = getSinglePropertySourcesPlaceholderConfigurer();
    if (configurer != null) {
      // Flatten the sources into a single list so they can be iterated
      return new FlatPropertySources(configurer.getAppliedPropertySources());
    }

    if (this.environment instanceof ConfigurableEnvironment) {
      MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment)
          .getPropertySources();
      return new FlatPropertySources(propertySources);
    }

    // empty, so not very useful, but fulfils the contract
    return new MutablePropertySources();
  }

  private PropertySourcesPlaceholderConfigurer getSinglePropertySourcesPlaceholderConfigurer() {
    // Take care not to cause early instantiation of all FactoryBeans
    if (this.beanFactory instanceof ListableBeanFactory) {
      ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
      Map<String, PropertySourcesPlaceholderConfigurer> beans = listableBeanFactory
          .getBeansOfType(PropertySourcesPlaceholderConfigurer.class, false,
              false);
      if (beans.size() == 1) {
        return beans.values().iterator().next();
      }
    }
    return null;
  }

  private <T> T getOptionalBean(String name, Class<T> type) {
    try {
      return this.beanFactory.getBean(name, type);
    }
    catch (NoSuchBeanDefinitionException ex) {
      return null;
    }
  }

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    ConfigurationProperties annotation = AnnotationUtils.findAnnotation(
        bean.getClass(), ConfigurationProperties.class);
    if (annotation != null || bean instanceof ConfigurationPropertiesHolder) {
      postProcessBeforeInitialization(bean, beanName, annotation);
    }
    annotation = this.beans.findFactoryAnnotation(beanName,
        ConfigurationProperties.class);
    if (annotation != null) {
      postProcessBeforeInitialization(bean, beanName, annotation);
    }
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName)
      throws BeansException {
    return bean;
  }

  private void postProcessBeforeInitialization(Object bean, String beanName,
      ConfigurationProperties annotation) {
    Object target = (bean instanceof ConfigurationPropertiesHolder ? ((ConfigurationPropertiesHolder) bean)
        .getTarget() : bean);
    PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
        target);
    if (annotation != null && annotation.locations().length != 0) {
      factory.setPropertySources(loadPropertySources(annotation.locations(),
          annotation.merge()));
    }
    else {
      factory.setPropertySources(this.propertySources);
    }
    factory.setValidator(determineValidator(bean));
    // If no explicit conversion service is provided we add one so that (at least)
    // comma-separated arrays of convertibles can be bound automatically
    factory.setConversionService(this.conversionService == null ? getDefaultConversionService()
        : this.conversionService);
    if (annotation != null) {
      factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
      factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
      factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
      factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
      String targetName = (StringUtils.hasLength(annotation.value()) ? annotation
          .value() : annotation.prefix());
      if (StringUtils.hasLength(targetName)) {
        factory.setTargetName(targetName);
      }
    }
    try {
      factory.bindPropertiesToTarget();
    }
    catch (Exception ex) {
      throw new BeanCreationException(beanName, "Could not bind properties", ex);
    }
  }

  private Validator determineValidator(Object bean) {
    if (ClassUtils.isAssignable(Validator.class, bean.getClass())) {
      if (this.validator == null) {
        return (Validator) bean;
      }
      return new ChainingValidator(this.validator, (Validator) bean);
    }
    return this.validator;
  }

  private PropertySources loadPropertySources(String[] locations,
      boolean mergeDefaultSources) {
    try {
      PropertySourcesLoader loader = new PropertySourcesLoader();
      for (String location : locations) {
        Resource resource = this.resourceLoader.getResource(this.environment
            .resolvePlaceholders(location));
        String[] profiles = this.environment.getActiveProfiles();
        for (int i = profiles.length; i-- > 0;) {
          String profile = profiles[i];
          loader.load(resource, profile);
        }
        loader.load(resource);
      }

      MutablePropertySources loaded = loader.getPropertySources();
      if (mergeDefaultSources) {
        for (PropertySource<?> propertySource : this.propertySources) {
          loaded.addLast(propertySource);
        }
      }
      return loaded;
    }
    catch (IOException ex) {
      throw new IllegalStateException(ex);
    }
  }

  private ConversionService getDefaultConversionService() {
    if (this.defaultConversionService == null) {
      DefaultConversionService conversionService = new DefaultConversionService();
      for (Converter<?, ?> converter : ((ListableBeanFactory) this.beanFactory)
          .getBeansOfType(Converter.class, false, false).values()) {
        conversionService.addConverter(converter);
      }
      this.defaultConversionService = conversionService;
    }
    return this.defaultConversionService;
  }

  /**
   * Factory to create JSR 303 LocalValidatorFactoryBean. Inner class to prevent class
   * loader issues.
   */
  private static class Jsr303ValidatorFactory {

    public Validator run(ApplicationContext applicationContext) {
      LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
      validator.setApplicationContext(applicationContext);
      validator.afterPropertiesSet();
      return validator;
    }

  }

  /**
   * {@link Validator} implementation that wraps {@link Validator} instances and chains
   * their execution.
   */
  private static class ChainingValidator implements Validator {

    private Validator[] validators;

    public ChainingValidator(Validator... validators) {
      Assert.notNull(validators, "Validators must not be null");
      this.validators = validators;
    }

    @Override
    public boolean supports(Class<?> clazz) {
      for (Validator validator : this.validators) {
        if (validator.supports(clazz)) {
          return true;
        }
      }
      return false;
    }

    @Override
    public void validate(Object target, Errors errors) {
      for (Validator validator : this.validators) {
        if (validator.supports(target.getClass())) {
          validator.validate(target, errors);
        }
      }
    }

  }

  /**
   * Convenience class to flatten out a tree of property sources without losing the
   * reference to the backing data (which can therefore be updated in the background).
   */
  private static class FlatPropertySources implements PropertySources {

    private PropertySources propertySources;

    public FlatPropertySources(PropertySources propertySources) {
      this.propertySources = propertySources;
    }

    @Override
    public Iterator<PropertySource<?>> iterator() {
      MutablePropertySources result = getFlattened();
      return result.iterator();
    }

    @Override
    public boolean contains(String name) {
      return get(name) != null;
    }

    @Override
    public PropertySource<?> get(String name) {
      return getFlattened().get(name);
    }

    private MutablePropertySources getFlattened() {
      MutablePropertySources result = new MutablePropertySources();
      for (PropertySource<?> propertySource : this.propertySources) {
        flattenPropertySources(propertySource, result);
      }
      return result;
    }

    private void flattenPropertySources(PropertySource<?> propertySource,
        MutablePropertySources result) {
      Object source = propertySource.getSource();
      if (source instanceof ConfigurableEnvironment) {
        ConfigurableEnvironment environment = (ConfigurableEnvironment) source;
        for (PropertySource<?> childSource : environment.getPropertySources()) {
          flattenPropertySources(childSource, result);
        }
      }
      else {
        result.addLast(propertySource);
      }
    }

  }

}
TOP

Related Classes of org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor$FlatPropertySources

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.