Open Source Repository

Home /spring/spring-beans-3.0.5 | Repository Home



org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java
/*
 * Copyright 2002-2010 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.beans.factory.config;

import java.util.Properties;
import java.util.Set;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.Constants;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
import org.springframework.util.StringValueResolver;

/**
 * A property resource configurer that resolves placeholders in bean property values of
 * context definitions. It <i>pulls</i> values from a properties file into bean definitions.
 *
 <p>The default placeholder syntax follows the Ant / Log4J / JSP EL style:
 *
 <pre class="code">${...}</pre>
 *
 * Example XML context definition:
 *
 <pre class="code">&lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;
 *   &lt;property name="driverClassName"&gt;&lt;value&gt;${driver}&lt;/value&gt;&lt;/property&gt;
 *   &lt;property name="url"&gt;&lt;value&gt;jdbc:${dbname}&lt;/value&gt;&lt;/property&gt;
 * &lt;/bean&gt;</pre>
 *
 * Example properties file:
 *
 <pre class="code">driver=com.mysql.jdbc.Driver
 * dbname=mysql:mydb</pre>
 *
 * PropertyPlaceholderConfigurer checks simple property values, lists, maps,
 * props, and bean names in bean references. Furthermore, placeholder values can
 * also cross-reference other placeholders, like:
 *
 <pre class="code">rootPath=myrootdir
 * subPath=${rootPath}/subdir</pre>
 *
 * In contrast to PropertyOverrideConfigurer, this configurer allows to fill in
 * explicit placeholders in context definitions. Therefore, the original definition
 * cannot specify any default values for such bean properties, and the placeholder
 * properties file is supposed to contain an entry for each defined placeholder.
 *
 <p>If a configurer cannot resolve a placeholder, a BeanDefinitionStoreException
 * will be thrown. If you want to check against multiple properties files, specify
 * multiple resources via the "locations" setting. You can also define multiple
 * PropertyPlaceholderConfigurers, each with its <i>own</i> placeholder syntax.
 *
 <p>Default property values can be defined via "properties", to make overriding
 * definitions in properties files optional. A configurer will also check against
 * system properties (e.g. "user.dir") if it cannot resolve a placeholder with any
 * of the specified properties. This can be customized via "systemPropertiesMode".
 *
 <p>Note that the context definition <i>is</i> aware of being incomplete;
 * this is immediately obvious to users when looking at the XML definition file.
 * Hence, placeholders have to be resolved; any desired defaults have to be
 * defined as placeholder values as well (for example in a default properties file).
 *
 <p>Property values can be converted after reading them in, through overriding
 * the {@link #convertPropertyValue} method. For example, encrypted values can
 * be detected and decrypted accordingly before processing them.
 *
 @author Juergen Hoeller
 @since 02.10.2003
 @see #setLocations
 @see #setProperties
 @see #setPlaceholderPrefix
 @see #setPlaceholderSuffix
 @see #setSystemPropertiesModeName
 @see System#getProperty(String)
 @see #convertPropertyValue
 @see PropertyOverrideConfigurer
 */
public class PropertyPlaceholderConfigurer extends PropertyResourceConfigurer
    implements BeanNameAware, BeanFactoryAware {

  /** Default placeholder prefix: "${" */
  public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";

  /** Default placeholder suffix: "}" */
  public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";

  /** Default value separator: ":" */
  public static final String DEFAULT_VALUE_SEPARATOR = ":";


  /** Never check system properties. */
  public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0;

  /**
   * Check system properties if not resolvable in the specified properties.
   * This is the default.
   */
  public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1;

  /**
   * Check system properties first, before trying the specified properties.
   * This allows system properties to override any other property source.
   */
  public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2;


  private static final Constants constants = new Constants(PropertyPlaceholderConfigurer.class);

  private String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;

  private String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;

  private String valueSeparator = DEFAULT_VALUE_SEPARATOR;

  private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;

  private boolean searchSystemEnvironment = true;

  private boolean ignoreUnresolvablePlaceholders = false;

  private String nullValue;

  private String beanName;

  private BeanFactory beanFactory;


  /**
   * Set the prefix that a placeholder string starts with.
   * The default is "${".
   @see #DEFAULT_PLACEHOLDER_PREFIX
   */
  public void setPlaceholderPrefix(String placeholderPrefix) {
    this.placeholderPrefix = placeholderPrefix;
  }

  /**
   * Set the suffix that a placeholder string ends with.
   * The default is "}".
   @see #DEFAULT_PLACEHOLDER_SUFFIX
   */
  public void setPlaceholderSuffix(String placeholderSuffix) {
    this.placeholderSuffix = placeholderSuffix;
  }

  /**
   * Specify the separating character between the placeholder variable
   * and the associated default value, or <code>null</code> if no such
   * special character should be processed as a value separator.
   * The default is ":".
   */
  public void setValueSeparator(String valueSeparator) {
    this.valueSeparator = valueSeparator;
  }

  /**
   * Set the system property mode by the name of the corresponding constant,
   * e.g. "SYSTEM_PROPERTIES_MODE_OVERRIDE".
   @param constantName name of the constant
   @throws java.lang.IllegalArgumentException if an invalid constant was specified
   @see #setSystemPropertiesMode
   */
  public void setSystemPropertiesModeName(String constantNamethrows IllegalArgumentException {
    this.systemPropertiesMode = constants.asNumber(constantName).intValue();
  }

  /**
   * Set how to check system properties: as fallback, as override, or never.
   * For example, will resolve ${user.dir} to the "user.dir" system property.
   <p>The default is "fallback": If not being able to resolve a placeholder
   * with the specified properties, a system property will be tried.
   * "override" will check for a system property first, before trying the
   * specified properties. "never" will not check system properties at all.
   @see #SYSTEM_PROPERTIES_MODE_NEVER
   @see #SYSTEM_PROPERTIES_MODE_FALLBACK
   @see #SYSTEM_PROPERTIES_MODE_OVERRIDE
   @see #setSystemPropertiesModeName
   */
  public void setSystemPropertiesMode(int systemPropertiesMode) {
    this.systemPropertiesMode = systemPropertiesMode;
  }

  /**
   * Set whether to search for a matching system environment variable
   * if no matching system property has been found. Only applied when
   * "systemPropertyMode" is active (i.e. "fallback" or "override"), right
   * after checking JVM system properties.
   <p>Default is "true". Switch this setting off to never resolve placeholders
   * against system environment variables. Note that it is generally recommended
   * to pass external values in as JVM system properties: This can easily be
   * achieved in a startup script, even for existing environment variables.
   <p><b>NOTE:</b> Access to environment variables does not work on the
   * Sun VM 1.4, where the corresponding {@link System#getenv} support was
   * disabled - before it eventually got re-enabled for the Sun VM 1.5.
   * Please upgrade to 1.5 (or higher) if you intend to rely on the
   * environment variable support.
   @see #setSystemPropertiesMode
   @see java.lang.System#getProperty(String)
   @see java.lang.System#getenv(String)
   */
  public void setSearchSystemEnvironment(boolean searchSystemEnvironment) {
    this.searchSystemEnvironment = searchSystemEnvironment;
  }

  /**
   * Set whether to ignore unresolvable placeholders.
   <p>Default is "false": An exception will be thrown if a placeholder fails
   * to resolve. Switch this flag to "true" in order to preserve the placeholder
   * String as-is in such a case, leaving it up to other placeholder configurers
   * to resolve it.
   */
  public void setIgnoreUnresolvablePlaceholders(boolean ignoreUnresolvablePlaceholders) {
    this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
  }

  /**
   * Set a value that should be treated as <code>null</code> when
   * resolved as a placeholder value: e.g. "" (empty String) or "null".
   <p>Note that this will only apply to full property values,
   * not to parts of concatenated values.
   <p>By default, no such null value is defined. This means that
   * there is no way to express <code>null</code> as a property
   * value unless you explictly map a corresponding value here.
   */
  public void setNullValue(String nullValue) {
    this.nullValue = nullValue;
  }

  /**
   * Only necessary to check that we're not parsing our own bean definition,
   * to avoid failing on unresolvable placeholders in properties file locations.
   * The latter case can happen with placeholders for system properties in
   * resource locations.
   @see #setLocations
   @see org.springframework.core.io.ResourceEditor
   */
  public void setBeanName(String beanName) {
    this.beanName = beanName;
  }

  /**
   * Only necessary to check that we're not parsing our own bean definition,
   * to avoid failing on unresolvable placeholders in properties file locations.
   * The latter case can happen with placeholders for system properties in
   * resource locations.
   @see #setLocations
   @see org.springframework.core.io.ResourceEditor
   */
  public void setBeanFactory(BeanFactory beanFactory) {
    this.beanFactory = beanFactory;
  }


  @Override
  protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
      throws BeansException {

    StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
    BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

    String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
    for (String curName : beanNames) {
      // Check that we're not parsing our own bean definition,
      // to avoid failing on unresolvable placeholders in properties file locations.
      if (!(curName.equals(this.beanName&& beanFactoryToProcess.equals(this.beanFactory))) {
        BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
        try {
          visitor.visitBeanDefinition(bd);
        }
        catch (Exception ex) {
          throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage());
        }
      }
    }

    // New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
    beanFactoryToProcess.resolveAliases(valueResolver);

    // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
    beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
  }

  /**
   * Resolve the given placeholder using the given properties, performing
   * a system properties check according to the given mode.
   <p>Default implementation delegates to <code>resolvePlaceholder
   * (placeholder, props)</code> before/after the system properties check.
   <p>Subclasses can override this for custom resolution strategies,
   * including customized points for the system properties check.
   @param placeholder the placeholder to resolve
   @param props the merged properties of this configurer
   @param systemPropertiesMode the system properties mode,
   * according to the constants in this class
   @return the resolved value, of null if none
   @see #setSystemPropertiesMode
   @see System#getProperty
   @see #resolvePlaceholder(String, java.util.Properties)
   */
  protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {
    String propVal = null;
    if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
      propVal = resolveSystemProperty(placeholder);
    }
    if (propVal == null) {
      propVal = resolvePlaceholder(placeholder, props);
    }
    if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
      propVal = resolveSystemProperty(placeholder);
    }
    return propVal;
  }

  /**
   * Resolve the given placeholder using the given properties.
   * The default implementation simply checks for a corresponding property key.
   <p>Subclasses can override this for customized placeholder-to-key mappings
   * or custom resolution strategies, possibly just using the given properties
   * as fallback.
   <p>Note that system properties will still be checked before respectively
   * after this method is invoked, according to the system properties mode.
   @param placeholder the placeholder to resolve
   @param props the merged properties of this configurer
   @return the resolved value, of <code>null</code> if none
   @see #setSystemPropertiesMode
   */
  protected String resolvePlaceholder(String placeholder, Properties props) {
    return props.getProperty(placeholder);
  }

  /**
   * Resolve the given key as JVM system property, and optionally also as
   * system environment variable if no matching system property has been found.
   @param key the placeholder to resolve as system property key
   @return the system property value, or <code>null</code> if not found
   @see #setSearchSystemEnvironment
   @see java.lang.System#getProperty(String)
   @see java.lang.System#getenv(String)
   */
  protected String resolveSystemProperty(String key) {
    try {
      String value = System.getProperty(key);
      if (value == null && this.searchSystemEnvironment) {
        value = System.getenv(key);
      }
      return value;
    }
    catch (Throwable ex) {
      if (logger.isDebugEnabled()) {
        logger.debug("Could not access system property '" + key + "': " + ex);
      }
      return null;
    }
  }

  /**
   * Parse the given String value for placeholder resolution.
   @param strVal the String value to parse
   @param props the Properties to resolve placeholders against
   @param visitedPlaceholders the placeholders that have already been visited
   * during the current resolution attempt (ignored in this version of the code)
   @deprecated as of Spring 3.0, in favor of using {@link #resolvePlaceholder}
   * with {@link org.springframework.util.PropertyPlaceholderHelper}.
   * Only retained for compatibility with Spring 2.5 extensions.
   */
  @Deprecated
  protected String parseStringValue(String strVal, Properties props, Set visitedPlaceholders) {
    PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper(
        placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders);
    PlaceholderResolver resolver = new PropertyPlaceholderConfigurerResolver(props);
    return helper.replacePlaceholders(strVal, resolver);
  }


  private class PlaceholderResolvingStringValueResolver implements StringValueResolver {

    private final PropertyPlaceholderHelper helper;

    private final PlaceholderResolver resolver;

    public PlaceholderResolvingStringValueResolver(Properties props) {
      this.helper = new PropertyPlaceholderHelper(
          placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders);
      this.resolver = new PropertyPlaceholderConfigurerResolver(props);
    }

    public String resolveStringValue(String strValthrows BeansException {
      String value = this.helper.replacePlaceholders(strVal, this.resolver);
      return (value.equals(nullValuenull : value);
    }
  }


  private class PropertyPlaceholderConfigurerResolver implements PlaceholderResolver {

    private final Properties props;

    private PropertyPlaceholderConfigurerResolver(Properties props) {
      this.props = props;
    }

    public String resolvePlaceholder(String placeholderName) {
      return PropertyPlaceholderConfigurer.this.resolvePlaceholder(placeholderName, props, systemPropertiesMode);
    }
  }

}