All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.appcrossings.config.HierarchicalPropertyPlaceholderConfigurer Maven / Gradle / Ivy

package com.appcrossings.config;

import java.io.IOException;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.beanutils.ConvertUtilsBean;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.StringUtils;

/**
 * 
 * @author Krzysztof Karski
 *
 */
public class HierarchicalPropertyPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer
    implements Config, EnvironmentAware, ApplicationContextAware {

  private class ReloadTask extends TimerTask {

    @Override
    public void run() {
      try {

        reload();

      } catch (Exception e) {
        logger.error("Error refreshing configs", e);
      }
    }
  }

  public final static String DEFAULT_PROPERTIES_FILE_NAME = "default.properties";
  private final static Logger log = LoggerFactory
      .getLogger(HierarchicalPropertyPlaceholderConfigurer.class);

  public final static boolean SEARCH_CLASSPATH = true;

  private final ConvertUtilsBean bean = new ConvertUtilsBean();

  private StandardPBEStringEncryptor encryptor = null;

  protected EnvironmentUtil envUtil = new EnvironmentUtil();

  private PropertyPlaceholderHelper helper;

  protected final String hostsFilePath;

  private ValueInjector injector;

  private final AtomicReference loadedProperties = new AtomicReference<>();

  protected String propertiesFileName = DEFAULT_PROPERTIES_FILE_NAME;

  protected boolean searchClasspath = SEARCH_CLASSPATH;

  private Timer timer = new Timer(true);

  /**
   * 
   * @param path The path of the hosts.properties file
   * @throws Exception
   */
  public HierarchicalPropertyPlaceholderConfigurer(String path) throws Exception {
    setLocalOverride(true);
    helper =
        new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
            this.valueSeparator, this.ignoreUnresolvablePlaceholders);
    this.hostsFilePath = path;
  }

  /**
   * 
   * @param path - The path of the hosts.properties file
   * @param refresh - The period in seconds at which the config properties should be refreshed. 0
   *        indicates no automated timer
   * @throws Exception
   */
  public HierarchicalPropertyPlaceholderConfigurer(String path, int refresh) throws Exception {
    this(path);
    setRefreshRate(refresh);
  }



  @Override
  public  T getProperty(String key, Class clazz) {

    String property;
    try {
      property =
          helper.replacePlaceholders(placeholderPrefix + key + placeholderSuffix,
              loadedProperties.get());

      if (property.equals(key))
        return null;

    } catch (IllegalArgumentException e) {
      return null;
    }

    if (clazz.equals(String.class))
      return (T) property;
    else if (property != null)
      return (T) bean.convert(property, clazz);
    else
      return null;

  }

  public  T getProperty(String key, Class clazz, T value) {

    T val = getProperty(key, clazz);

    if (val != null)
      return val;

    return value;

  }

  protected void init() {

    String environmentName = envUtil.detectEnvironment();
    String hostName = envUtil.detectHostName();

    logger.info("Loading property files...");

    Properties hosts = ResourcesUtil.loadHosts(hostsFilePath, encryptor);

    String propertiesFile = hosts.getProperty(hostName);

    // Attempt environment as a backup
    if (!StringUtils.hasText(propertiesFile) && StringUtils.hasText(environmentName)) {

      propertiesFile = hosts.getProperty(environmentName);

    } else if (!StringUtils.hasText(propertiesFile)) {

      propertiesFile = hosts.getProperty("*");// catch all

    }

    log.debug("Found properties starting at path: " + propertiesFile);
    
    Properties ps =
        ResourcesUtil
            .loadProperties(propertiesFile, propertiesFileName, searchClasspath, encryptor);

    if (ps.isEmpty()) {
      log.warn("Counldn't find any properties for host " + hostName + " or environment "
          + environmentName);
    }

    // Finally, propagate properties to PropertyPlaceholderConfigurer
    loadedProperties.set(ps);
    super.setProperties(loadedProperties.get());

    try {
      mergeProperties();
    } catch (IOException io) {
      // nothing
    }
  }

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
      throws BeansException {

    init();
    super.postProcessBeanFactory(beanFactory);

  }

  public void reload() {

    init();
    injector.reloadBeans(loadedProperties.get(), this.placeholderPrefix, this.placeholderSuffix);
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    if (injector == null)
      this.injector = new ValueInjector(applicationContext);
  }

  @Override
  public void setEnvironment(Environment environment) {
    super.setEnvironment(environment);
    envUtil.setEnvironment(environment);
  }

  public void setEnvironment(String environmentName) {
    envUtil.setEnvironmentName(environmentName);
  }

  public void setFileName(String fileName) {
    this.propertiesFileName = fileName;
  }

  public void setHostName(String hostName) {
    envUtil.setHostName(hostName);
  }

  /**
   * Set password on the encryptor. If an encryptor isn't configured, a BasicTextEncryptor will be
   * initialized and the password set on it. The basic assumed encryption algorithm is
   * PBEWithMD5AndDES. This can be changed by setting the StandardPBEStringEncryptor.
   * 
   * @param password
   */
  public void setPassword(String password) {

    this.encryptor = new StandardPBEStringEncryptor();
    EnvironmentStringPBEConfig configurationEncryptor = new EnvironmentStringPBEConfig();
    configurationEncryptor.setAlgorithm("PBEWithMD5AndDES");
    configurationEncryptor.setPassword(password);
    encryptor.setConfig(configurationEncryptor);

  };

  /**
   * Override default text encryptor (StandardPBEStringEncryptor). Enables overriding both password
   * and algorithm.
   * 
   * @param encryptor
   */
  public void setTextEncryptor(StandardPBEStringEncryptor config) {
    this.encryptor = config;
  }

  public void setRefreshRate(Integer refresh) {

    if (refresh == 0L || refresh == null) {
      timer.cancel();
      return;
    }

    synchronized (timer) {
      timer.cancel();
      timer = new Timer(true);
      timer.schedule(new ReloadTask(), refresh * 1000, refresh * 1000);
    }
  }

  public void setSearchClasspath(boolean searchClasspath) {
    this.searchClasspath = searchClasspath;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy