001/* 002 * Copyright 2002-2019 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.beans.factory.config; 018 019import java.util.Properties; 020import java.util.Set; 021 022import org.springframework.beans.BeansException; 023import org.springframework.core.Constants; 024import org.springframework.core.SpringProperties; 025import org.springframework.core.env.AbstractEnvironment; 026import org.springframework.util.PropertyPlaceholderHelper; 027import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; 028import org.springframework.util.StringValueResolver; 029 030/** 031 * {@link PlaceholderConfigurerSupport} subclass that resolves ${...} placeholders against 032 * {@link #setLocation local} {@link #setProperties properties} and/or system properties 033 * and environment variables. 034 * 035 * <p>As of Spring 3.1, {@link org.springframework.context.support.PropertySourcesPlaceholderConfigurer 036 * PropertySourcesPlaceholderConfigurer} should be used preferentially over this implementation; it is 037 * more flexible through taking advantage of the {@link org.springframework.core.env.Environment} and 038 * {@link org.springframework.core.env.PropertySource} mechanisms also made available in Spring 3.1. 039 * 040 * <p>{@link PropertyPlaceholderConfigurer} is still appropriate for use when: 041 * <ul> 042 * <li>the {@code spring-context} module is not available (i.e., one is using Spring's 043 * {@code BeanFactory} API as opposed to {@code ApplicationContext}). 044 * <li>existing configuration makes use of the {@link #setSystemPropertiesMode(int) "systemPropertiesMode"} 045 * and/or {@link #setSystemPropertiesModeName(String) "systemPropertiesModeName"} properties. 046 * Users are encouraged to move away from using these settings, and rather configure property 047 * source search order through the container's {@code Environment}; however, exact preservation 048 * of functionality may be maintained by continuing to use {@code PropertyPlaceholderConfigurer}. 049 * </ul> 050 * 051 * @author Juergen Hoeller 052 * @author Chris Beams 053 * @since 02.10.2003 054 * @see #setSystemPropertiesModeName 055 * @see PlaceholderConfigurerSupport 056 * @see PropertyOverrideConfigurer 057 * @see org.springframework.context.support.PropertySourcesPlaceholderConfigurer 058 */ 059public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport { 060 061 /** Never check system properties. */ 062 public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0; 063 064 /** 065 * Check system properties if not resolvable in the specified properties. 066 * This is the default. 067 */ 068 public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1; 069 070 /** 071 * Check system properties first, before trying the specified properties. 072 * This allows system properties to override any other property source. 073 */ 074 public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2; 075 076 077 private static final Constants constants = new Constants(PropertyPlaceholderConfigurer.class); 078 079 private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK; 080 081 private boolean searchSystemEnvironment = 082 !SpringProperties.getFlag(AbstractEnvironment.IGNORE_GETENV_PROPERTY_NAME); 083 084 085 /** 086 * Set the system property mode by the name of the corresponding constant, 087 * e.g. "SYSTEM_PROPERTIES_MODE_OVERRIDE". 088 * @param constantName name of the constant 089 * @see #setSystemPropertiesMode 090 */ 091 public void setSystemPropertiesModeName(String constantName) throws IllegalArgumentException { 092 this.systemPropertiesMode = constants.asNumber(constantName).intValue(); 093 } 094 095 /** 096 * Set how to check system properties: as fallback, as override, or never. 097 * For example, will resolve ${user.dir} to the "user.dir" system property. 098 * <p>The default is "fallback": If not being able to resolve a placeholder 099 * with the specified properties, a system property will be tried. 100 * "override" will check for a system property first, before trying the 101 * specified properties. "never" will not check system properties at all. 102 * @see #SYSTEM_PROPERTIES_MODE_NEVER 103 * @see #SYSTEM_PROPERTIES_MODE_FALLBACK 104 * @see #SYSTEM_PROPERTIES_MODE_OVERRIDE 105 * @see #setSystemPropertiesModeName 106 */ 107 public void setSystemPropertiesMode(int systemPropertiesMode) { 108 this.systemPropertiesMode = systemPropertiesMode; 109 } 110 111 /** 112 * Set whether to search for a matching system environment variable 113 * if no matching system property has been found. Only applied when 114 * "systemPropertyMode" is active (i.e. "fallback" or "override"), right 115 * after checking JVM system properties. 116 * <p>Default is "true". Switch this setting off to never resolve placeholders 117 * against system environment variables. Note that it is generally recommended 118 * to pass external values in as JVM system properties: This can easily be 119 * achieved in a startup script, even for existing environment variables. 120 * @see #setSystemPropertiesMode 121 * @see System#getProperty(String) 122 * @see System#getenv(String) 123 */ 124 public void setSearchSystemEnvironment(boolean searchSystemEnvironment) { 125 this.searchSystemEnvironment = searchSystemEnvironment; 126 } 127 128 /** 129 * Resolve the given placeholder using the given properties, performing 130 * a system properties check according to the given mode. 131 * <p>The default implementation delegates to {@code resolvePlaceholder 132 * (placeholder, props)} before/after the system properties check. 133 * <p>Subclasses can override this for custom resolution strategies, 134 * including customized points for the system properties check. 135 * @param placeholder the placeholder to resolve 136 * @param props the merged properties of this configurer 137 * @param systemPropertiesMode the system properties mode, 138 * according to the constants in this class 139 * @return the resolved value, of null if none 140 * @see #setSystemPropertiesMode 141 * @see System#getProperty 142 * @see #resolvePlaceholder(String, java.util.Properties) 143 */ 144 protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) { 145 String propVal = null; 146 if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) { 147 propVal = resolveSystemProperty(placeholder); 148 } 149 if (propVal == null) { 150 propVal = resolvePlaceholder(placeholder, props); 151 } 152 if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) { 153 propVal = resolveSystemProperty(placeholder); 154 } 155 return propVal; 156 } 157 158 /** 159 * Resolve the given placeholder using the given properties. 160 * The default implementation simply checks for a corresponding property key. 161 * <p>Subclasses can override this for customized placeholder-to-key mappings 162 * or custom resolution strategies, possibly just using the given properties 163 * as fallback. 164 * <p>Note that system properties will still be checked before respectively 165 * after this method is invoked, according to the system properties mode. 166 * @param placeholder the placeholder to resolve 167 * @param props the merged properties of this configurer 168 * @return the resolved value, of {@code null} if none 169 * @see #setSystemPropertiesMode 170 */ 171 protected String resolvePlaceholder(String placeholder, Properties props) { 172 return props.getProperty(placeholder); 173 } 174 175 /** 176 * Resolve the given key as JVM system property, and optionally also as 177 * system environment variable if no matching system property has been found. 178 * @param key the placeholder to resolve as system property key 179 * @return the system property value, or {@code null} if not found 180 * @see #setSearchSystemEnvironment 181 * @see System#getProperty(String) 182 * @see System#getenv(String) 183 */ 184 protected String resolveSystemProperty(String key) { 185 try { 186 String value = System.getProperty(key); 187 if (value == null && this.searchSystemEnvironment) { 188 value = System.getenv(key); 189 } 190 return value; 191 } 192 catch (Throwable ex) { 193 if (logger.isDebugEnabled()) { 194 logger.debug("Could not access system property '" + key + "': " + ex); 195 } 196 return null; 197 } 198 } 199 200 201 /** 202 * Visit each bean definition in the given bean factory and attempt to replace ${...} property 203 * placeholders with values from the given properties. 204 */ 205 @Override 206 protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) 207 throws BeansException { 208 209 StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props); 210 doProcessProperties(beanFactoryToProcess, valueResolver); 211 } 212 213 /** 214 * Parse the given String value for placeholder resolution. 215 * @param strVal the String value to parse 216 * @param props the Properties to resolve placeholders against 217 * @param visitedPlaceholders the placeholders that have already been visited 218 * during the current resolution attempt (ignored in this version of the code) 219 * @deprecated as of Spring 3.0, in favor of using {@link #resolvePlaceholder} 220 * with {@link org.springframework.util.PropertyPlaceholderHelper}. 221 * Only retained for compatibility with Spring 2.5 extensions. 222 */ 223 @Deprecated 224 protected String parseStringValue(String strVal, Properties props, Set<?> visitedPlaceholders) { 225 PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper( 226 placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders); 227 PlaceholderResolver resolver = new PropertyPlaceholderConfigurerResolver(props); 228 return helper.replacePlaceholders(strVal, resolver); 229 } 230 231 232 private class PlaceholderResolvingStringValueResolver implements StringValueResolver { 233 234 private final PropertyPlaceholderHelper helper; 235 236 private final PlaceholderResolver resolver; 237 238 public PlaceholderResolvingStringValueResolver(Properties props) { 239 this.helper = new PropertyPlaceholderHelper( 240 placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders); 241 this.resolver = new PropertyPlaceholderConfigurerResolver(props); 242 } 243 244 @Override 245 public String resolveStringValue(String strVal) throws BeansException { 246 String resolved = this.helper.replacePlaceholders(strVal, this.resolver); 247 if (trimValues) { 248 resolved = resolved.trim(); 249 } 250 return (resolved.equals(nullValue) ? null : resolved); 251 } 252 } 253 254 255 private final class PropertyPlaceholderConfigurerResolver implements PlaceholderResolver { 256 257 private final Properties props; 258 259 private PropertyPlaceholderConfigurerResolver(Properties props) { 260 this.props = props; 261 } 262 263 @Override 264 public String resolvePlaceholder(String placeholderName) { 265 return PropertyPlaceholderConfigurer.this.resolvePlaceholder(placeholderName, 266 this.props, systemPropertiesMode); 267 } 268 } 269 270}