001: /*
002: * Copyright 2002-2005 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: * http://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:
017: package org.springframework.beans.factory.config;
018:
019: import java.util.Collections;
020: import java.util.Enumeration;
021: import java.util.HashSet;
022: import java.util.Properties;
023: import java.util.Set;
024:
025: import org.springframework.beans.BeansException;
026: import org.springframework.beans.factory.BeanInitializationException;
027:
028: /**
029: * A property resource configurer that overrides bean property values in an application
030: * context definition. It <i>pushes</i> values from a properties file into bean definitions.
031: *
032: * <p>Configuration lines are expected to be of the following form:
033: *
034: * <pre class="code">beanName.property=value</pre>
035: *
036: * Example properties file:
037: *
038: * <pre class="code">dataSource.driverClassName=com.mysql.jdbc.Driver
039: * dataSource.url=jdbc:mysql:mydb</pre>
040: *
041: * In contrast to PropertyPlaceholderConfigurer, the original definition can have default
042: * values or no values at all for such bean properties. If an overriding properties file does
043: * not have an entry for a certain bean property, the default context definition is used.
044: *
045: * <p>Note that the context definition <i>is not</i> aware of being overridden;
046: * so this is not immediately obvious when looking at the XML definition file.
047: *
048: * <p>In case of multiple PropertyOverrideConfigurers that define different values for
049: * the same bean property, the <i>last</i> one will win (due to the overriding mechanism).
050: *
051: * <p>Property values can be converted after reading them in, through overriding
052: * the <code>convertPropertyValue</code> method. For example, encrypted values
053: * can be detected and decrypted accordingly before processing them.
054: *
055: * @author Juergen Hoeller
056: * @author Rod Johnson
057: * @since 12.03.2003
058: * @see #convertPropertyValue
059: * @see PropertyPlaceholderConfigurer
060: */
061: public class PropertyOverrideConfigurer extends
062: PropertyResourceConfigurer {
063:
064: public static final String DEFAULT_BEAN_NAME_SEPARATOR = ".";
065:
066: private String beanNameSeparator = DEFAULT_BEAN_NAME_SEPARATOR;
067:
068: private boolean ignoreInvalidKeys = false;
069:
070: /** Contains names of beans that have overrides */
071: private Set beanNames = Collections.synchronizedSet(new HashSet());
072:
073: /**
074: * Set the separator to expect between bean name and property path.
075: * Default is a dot (".").
076: */
077: public void setBeanNameSeparator(String beanNameSeparator) {
078: this .beanNameSeparator = beanNameSeparator;
079: }
080:
081: /**
082: * Set whether to ignore invalid keys. Default is "false".
083: * <p>If you ignore invalid keys, keys that do not follow the
084: * 'beanName.property' format will just be logged as warning.
085: * This allows to have arbitrary other keys in a properties file.
086: */
087: public void setIgnoreInvalidKeys(boolean ignoreInvalidKeys) {
088: this .ignoreInvalidKeys = ignoreInvalidKeys;
089: }
090:
091: protected void processProperties(
092: ConfigurableListableBeanFactory beanFactory,
093: Properties props) throws BeansException {
094:
095: for (Enumeration names = props.propertyNames(); names
096: .hasMoreElements();) {
097: String key = (String) names.nextElement();
098: try {
099: processKey(beanFactory, key, props.getProperty(key));
100: } catch (BeansException ex) {
101: String msg = "Could not process key '" + key
102: + "' in PropertyOverrideConfigurer";
103: if (!this .ignoreInvalidKeys) {
104: throw new BeanInitializationException(msg, ex);
105: }
106: if (logger.isDebugEnabled()) {
107: logger.debug(msg, ex);
108: }
109: }
110: }
111: }
112:
113: /**
114: * Process the given key as 'beanName.property' entry.
115: */
116: protected void processKey(ConfigurableListableBeanFactory factory,
117: String key, String value) throws BeansException {
118:
119: int separatorIndex = key.indexOf(this .beanNameSeparator);
120: if (separatorIndex == -1) {
121: throw new BeanInitializationException("Invalid key '" + key
122: + "': expected 'beanName" + this .beanNameSeparator
123: + "property'");
124: }
125: String beanName = key.substring(0, separatorIndex);
126: String beanProperty = key.substring(separatorIndex + 1);
127: this .beanNames.add(beanName);
128: applyPropertyValue(factory, beanName, beanProperty, value);
129: if (logger.isDebugEnabled()) {
130: logger.debug("Property '" + key + "' set to value ["
131: + value + "]");
132: }
133: }
134:
135: /**
136: * Apply the given property value to the corresponding bean.
137: */
138: protected void applyPropertyValue(
139: ConfigurableListableBeanFactory factory, String beanName,
140: String property, String value) {
141:
142: BeanDefinition bd = factory.getBeanDefinition(beanName);
143: bd.getPropertyValues().addPropertyValue(property, value);
144: }
145:
146: /**
147: * Were there overrides for this bean?
148: * Only valid after processing has occurred at least once.
149: * @param beanName name of the bean to query status for
150: * @return whether there were property overrides for
151: * the named bean
152: */
153: public boolean hasPropertyOverridesFor(String beanName) {
154: return this.beanNames.contains(beanName);
155: }
156:
157: }
|