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