KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > beans > factory > config > PropertyPlaceholderConfigurer


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

16
17 package org.springframework.beans.factory.config;
18
19 import java.util.HashSet JavaDoc;
20 import java.util.Properties JavaDoc;
21 import java.util.Set JavaDoc;
22
23 import org.springframework.beans.BeansException;
24 import org.springframework.beans.factory.BeanDefinitionStoreException;
25 import org.springframework.beans.factory.BeanFactory;
26 import org.springframework.beans.factory.BeanFactoryAware;
27 import org.springframework.beans.factory.BeanNameAware;
28 import org.springframework.core.Constants;
29
30 /**
31  * A property resource configurer that resolves placeholders in bean property values of
32  * context definitions. It <i>pulls</i> values from a properties file into bean definitions.
33  *
34  * <p>The default placeholder syntax follows the Ant / Log4J / JSP EL style:
35  *
36  * <pre class="code">${...}</pre>
37  *
38  * Example XML context definition:
39  *
40  * <pre class="code">&lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;
41  * &lt;property name="driverClassName"&gt;&lt;value&gt;${driver}&lt;/value&gt;&lt;/property&gt;
42  * &lt;property name="url"&gt;&lt;value&gt;jdbc:${dbname}&lt;/value&gt;&lt;/property&gt;
43  * &lt;/bean&gt;</pre>
44  *
45  * Example properties file:
46  *
47  * <pre class="code">driver=com.mysql.jdbc.Driver
48  * dbname=mysql:mydb</pre>
49  *
50  * PropertyPlaceholderConfigurer checks simple property values, lists, maps,
51  * props, and bean names in bean references. Furthermore, placeholder values can
52  * also cross-reference other placeholders, like:
53  *
54  * <pre class="code">rootPath=myrootdir
55  * subPath=${rootPath}/subdir</pre>
56  *
57  * In contrast to PropertyOverrideConfigurer, this configurer allows to fill in
58  * explicit placeholders in context definitions. Therefore, the original definition
59  * cannot specify any default values for such bean properties, and the placeholder
60  * properties file is supposed to contain an entry for each defined placeholder.
61  *
62  * <p>If a configurer cannot resolve a placeholder, a BeanDefinitionStoreException
63  * will be thrown. If you want to check against multiple properties files, specify
64  * multiple resources via the "locations" setting. You can also define multiple
65  * PropertyPlaceholderConfigurers, each with its <i>own</i> placeholder syntax.
66  *
67  * <p>Default property values can be defined via "properties", to make overriding
68  * definitions in properties files optional. A configurer will also check against
69  * system properties (e.g. "user.dir") if it cannot resolve a placeholder with any
70  * of the specified properties. This can be customized via "systemPropertiesMode".
71  *
72  * <p>Note that the context definition <i>is</i> aware of being incomplete;
73  * this is immediately obvious to users when looking at the XML definition file.
74  * Hence, placeholders have to be resolved; any desired defaults have to be
75  * defined as placeholder values as well (for example in a default properties file).
76  *
77  * <p>Property values can be converted after reading them in, through overriding
78  * the {@link #convertPropertyValue} method. For example, encrypted values can
79  * be detected and decrypted accordingly before processing them.
80  *
81  * @author Juergen Hoeller
82  * @since 02.10.2003
83  * @see #setLocations
84  * @see #setProperties
85  * @see #setPlaceholderPrefix
86  * @see #setPlaceholderSuffix
87  * @see #setSystemPropertiesModeName
88  * @see System#getProperty(String)
89  * @see #convertPropertyValue
90  * @see PropertyOverrideConfigurer
91  */

92 public class PropertyPlaceholderConfigurer extends PropertyResourceConfigurer
93     implements BeanNameAware, BeanFactoryAware {
94
95     /** Default placeholder prefix: "${" */
96     public static final String JavaDoc DEFAULT_PLACEHOLDER_PREFIX = "${";
97
98     /** Default placeholder suffix: "}" */
99     public static final String JavaDoc DEFAULT_PLACEHOLDER_SUFFIX = "}";
100
101
102     /** Never check system properties. */
103     public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0;
104
105     /**
106      * Check system properties if not resolvable in the specified properties.
107      * This is the default.
108      */

109     public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1;
110
111     /**
112      * Check system properties first, before trying the specified properties.
113      * This allows system properties to override any other property source.
114      */

115     public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2;
116
117
118     private static final Constants constants = new Constants(PropertyPlaceholderConfigurer.class);
119
120     private String JavaDoc placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
121
122     private String JavaDoc placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
123
124     private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;
125
126     private boolean searchSystemEnvironment = true;
127
128     private boolean ignoreUnresolvablePlaceholders = false;
129
130     private String JavaDoc beanName;
131
132     private BeanFactory beanFactory;
133
134
135     /**
136      * Set the prefix that a placeholder string starts with.
137      * The default is "${".
138      * @see #DEFAULT_PLACEHOLDER_PREFIX
139      */

140     public void setPlaceholderPrefix(String JavaDoc placeholderPrefix) {
141         this.placeholderPrefix = placeholderPrefix;
142     }
143
144     /**
145      * Set the suffix that a placeholder string ends with.
146      * The default is "}".
147      * @see #DEFAULT_PLACEHOLDER_SUFFIX
148      */

149     public void setPlaceholderSuffix(String JavaDoc placeholderSuffix) {
150         this.placeholderSuffix = placeholderSuffix;
151     }
152
153     /**
154      * Set the system property mode by the name of the corresponding constant,
155      * e.g. "SYSTEM_PROPERTIES_MODE_OVERRIDE".
156      * @param constantName name of the constant
157      * @throws java.lang.IllegalArgumentException if an invalid constant was specified
158      * @see #setSystemPropertiesMode
159      */

160     public void setSystemPropertiesModeName(String JavaDoc constantName) throws IllegalArgumentException JavaDoc {
161         this.systemPropertiesMode = constants.asNumber(constantName).intValue();
162     }
163
164     /**
165      * Set how to check system properties: as fallback, as override, or never.
166      * For example, will resolve ${user.dir} to the "user.dir" system property.
167      * <p>The default is "fallback": If not being able to resolve a placeholder
168      * with the specified properties, a system property will be tried.
169      * "override" will check for a system property first, before trying the
170      * specified properties. "never" will not check system properties at all.
171      * @see #SYSTEM_PROPERTIES_MODE_NEVER
172      * @see #SYSTEM_PROPERTIES_MODE_FALLBACK
173      * @see #SYSTEM_PROPERTIES_MODE_OVERRIDE
174      * @see #setSystemPropertiesModeName
175      */

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

198     public void setSearchSystemEnvironment(boolean searchSystemEnvironment) {
199         this.searchSystemEnvironment = searchSystemEnvironment;
200     }
201
202     /**
203      * Set whether to ignore unresolvable placeholders. Default is "false":
204      * An exception will be thrown if a placeholder cannot be resolved.
205      */

206     public void setIgnoreUnresolvablePlaceholders(boolean ignoreUnresolvablePlaceholders) {
207         this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
208     }
209
210     /**
211      * Only necessary to check that we're not parsing our own bean definition,
212      * to avoid failing on unresolvable placeholders in properties file locations.
213      * The latter case can happen with placeholders for system properties in
214      * resource locations.
215      * @see #setLocations
216      * @see org.springframework.core.io.ResourceEditor
217      */

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

230     public void setBeanFactory(BeanFactory beanFactory) {
231         this.beanFactory = beanFactory;
232     }
233
234
235     protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties JavaDoc props)
236             throws BeansException {
237
238         BeanDefinitionVisitor visitor = new PlaceholderResolvingBeanDefinitionVisitor(props);
239         String JavaDoc[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
240         for (int i = 0; i < beanNames.length; i++) {
241             // Check that we're not parsing our own bean definition,
242
// to avoid failing on unresolvable placeholders in properties file locations.
243
if (!(beanNames[i].equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
244                 BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(beanNames[i]);
245                 try {
246                     visitor.visitBeanDefinition(bd);
247                 }
248                 catch (BeanDefinitionStoreException ex) {
249                     throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanNames[i], ex.getMessage());
250                 }
251             }
252         }
253     }
254
255     /**
256      * Parse the given String value recursively, to be able to resolve
257      * nested placeholders (when resolved property values in turn contain
258      * placeholders again).
259      * @param strVal the String value to parse
260      * @param props the Properties to resolve placeholders against
261      * @param visitedPlaceholders the placeholders that have already been visited
262      * during the current resolution attempt (used to detect circular references
263      * between placeholders). Only non-null if we're parsing a nested placeholder.
264      * @throws BeanDefinitionStoreException if invalid values are encountered
265      * @see #resolvePlaceholder(String, java.util.Properties, int)
266      */

267     protected String JavaDoc parseStringValue(String JavaDoc strVal, Properties JavaDoc props, Set JavaDoc visitedPlaceholders)
268         throws BeanDefinitionStoreException {
269
270         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(strVal);
271
272         // The following code does not use JDK 1.4's StringBuffer.indexOf(String)
273
// method to retain JDK 1.3 compatibility. The slight loss in performance
274
// is not really relevant, as this code will typically just run on startup.
275

276         int startIndex = strVal.indexOf(this.placeholderPrefix);
277         while (startIndex != -1) {
278             int endIndex = buf.toString().indexOf(
279                 this.placeholderSuffix, startIndex + this.placeholderPrefix.length());
280             if (endIndex != -1) {
281                 String JavaDoc placeholder = buf.substring(startIndex + this.placeholderPrefix.length(), endIndex);
282                 if (!visitedPlaceholders.add(placeholder)) {
283                     throw new BeanDefinitionStoreException(
284                             "Circular placeholder reference '" + placeholder + "' in property definitions");
285                 }
286                 String JavaDoc propVal = resolvePlaceholder(placeholder, props, this.systemPropertiesMode);
287                 if (propVal != null) {
288                     // Recursive invocation, parsing placeholders contained in the
289
// previously resolved placeholder value.
290
propVal = parseStringValue(propVal, props, visitedPlaceholders);
291                     buf.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
292                     if (logger.isTraceEnabled()) {
293                         logger.trace("Resolved placeholder '" + placeholder + "'");
294                     }
295                     startIndex = buf.toString().indexOf(this.placeholderPrefix, startIndex + propVal.length());
296                 }
297                 else if (this.ignoreUnresolvablePlaceholders) {
298                     // Proceed with unprocessed value.
299
startIndex = buf.toString().indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
300                 }
301                 else {
302                     throw new BeanDefinitionStoreException("Could not resolve placeholder '" + placeholder + "'");
303                 }
304                 visitedPlaceholders.remove(placeholder);
305             }
306             else {
307                 startIndex = -1;
308             }
309         }
310
311         return buf.toString();
312     }
313
314     /**
315      * Resolve the given placeholder using the given properties, performing
316      * a system properties check according to the given mode.
317      * <p>Default implementation delegates to <code>resolvePlaceholder
318      * (placeholder, props)</code> before/after the system properties check.
319      * <p>Subclasses can override this for custom resolution strategies,
320      * including customized points for the system properties check.
321      * @param placeholder the placeholder to resolve
322      * @param props the merged properties of this configurer
323      * @param systemPropertiesMode the system properties mode,
324      * according to the constants in this class
325      * @return the resolved value, of null if none
326      * @see #setSystemPropertiesMode
327      * @see System#getProperty
328      * @see #resolvePlaceholder(String, java.util.Properties)
329      */

330     protected String JavaDoc resolvePlaceholder(String JavaDoc placeholder, Properties JavaDoc props, int systemPropertiesMode) {
331         String JavaDoc propVal = null;
332         if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
333             propVal = resolveSystemProperty(placeholder);
334         }
335         if (propVal == null) {
336             propVal = resolvePlaceholder(placeholder, props);
337         }
338         if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
339             propVal = resolveSystemProperty(placeholder);
340         }
341         return propVal;
342     }
343
344     /**
345      * Resolve the given placeholder using the given properties.
346      * The default implementation simply checks for a corresponding property key.
347      * <p>Subclasses can override this for customized placeholder-to-key mappings
348      * or custom resolution strategies, possibly just using the given properties
349      * as fallback.
350      * <p>Note that system properties will still be checked before respectively
351      * after this method is invoked, according to the system properties mode.
352      * @param placeholder the placeholder to resolve
353      * @param props the merged properties of this configurer
354      * @return the resolved value, of <code>null</code> if none
355      * @see #setSystemPropertiesMode
356      */

357     protected String JavaDoc resolvePlaceholder(String JavaDoc placeholder, Properties JavaDoc props) {
358         return props.getProperty(placeholder);
359     }
360
361     /**
362      * Resolve the given key as JVM system property, and optionally also as
363      * system environment variable if no matching system property has been found.
364      * @param key the placeholder to resolve as system property key
365      * @return the system property value, or <code>null</code> if not found
366      * @see #setSearchSystemEnvironment
367      * @see java.lang.System#getProperty(String)
368      * @see java.lang.System#getenv(String)
369      */

370     protected String JavaDoc resolveSystemProperty(String JavaDoc key) {
371         try {
372             String JavaDoc value = System.getProperty(key);
373             if (value == null && this.searchSystemEnvironment) {
374                 value = System.getenv(key);
375             }
376             return value;
377         }
378         catch (Throwable JavaDoc ex) {
379             if (logger.isDebugEnabled()) {
380                 logger.debug("Could not access system property '" + key + "': " + ex);
381             }
382             return null;
383         }
384     }
385
386
387     /**
388      * BeanDefinitionVisitor that resolves placeholders in String values,
389      * delegating to the <code>parseStringValue</code> method of the
390      * containing class.
391      */

392     private class PlaceholderResolvingBeanDefinitionVisitor extends BeanDefinitionVisitor {
393
394         private final Properties JavaDoc props;
395
396         public PlaceholderResolvingBeanDefinitionVisitor(Properties JavaDoc props) {
397             this.props = props;
398         }
399
400         protected String JavaDoc resolveStringValue(String JavaDoc strVal) throws BeansException {
401             return parseStringValue(strVal, this.props, new HashSet JavaDoc());
402         }
403     }
404
405 }
406
Popular Tags