001/*
002 * Copyright 2002-2012 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.io.IOException;
020import java.util.Enumeration;
021import java.util.Properties;
022
023import org.springframework.beans.BeansException;
024import org.springframework.beans.factory.BeanInitializationException;
025import org.springframework.core.Ordered;
026import org.springframework.core.PriorityOrdered;
027import org.springframework.core.io.support.PropertiesLoaderSupport;
028import org.springframework.util.ObjectUtils;
029
030/**
031 * Allows for configuration of individual bean property values from a property resource,
032 * i.e. a properties file. Useful for custom config files targeted at system
033 * administrators that override bean properties configured in the application context.
034 *
035 * <p>Two concrete implementations are provided in the distribution:
036 * <ul>
037 * <li>{@link PropertyOverrideConfigurer} for "beanName.property=value" style overriding
038 * (<i>pushing</i> values from a properties file into bean definitions)
039 * <li>{@link PropertyPlaceholderConfigurer} for replacing "${...}" placeholders
040 * (<i>pulling</i> values from a properties file into bean definitions)
041 * </ul>
042 *
043 * <p>Property values can be converted after reading them in, through overriding
044 * the {@link #convertPropertyValue} method. For example, encrypted values
045 * can be detected and decrypted accordingly before processing them.
046 *
047 * @author Juergen Hoeller
048 * @since 02.10.2003
049 * @see PropertyOverrideConfigurer
050 * @see PropertyPlaceholderConfigurer
051 */
052public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport
053                implements BeanFactoryPostProcessor, PriorityOrdered {
054
055        private int order = Ordered.LOWEST_PRECEDENCE;  // default: same as non-Ordered
056
057
058        /**
059         * Set the order value of this object for sorting purposes.
060         * @see PriorityOrdered
061         */
062        public void setOrder(int order) {
063                this.order = order;
064        }
065
066        @Override
067        public int getOrder() {
068                return this.order;
069        }
070
071
072        /**
073         * {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and
074         * {@linkplain #processProperties process} properties against the given bean factory.
075         * @throws BeanInitializationException if any properties cannot be loaded
076         */
077        @Override
078        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
079                try {
080                        Properties mergedProps = mergeProperties();
081
082                        // Convert the merged properties, if necessary.
083                        convertProperties(mergedProps);
084
085                        // Let the subclass process the properties.
086                        processProperties(beanFactory, mergedProps);
087                }
088                catch (IOException ex) {
089                        throw new BeanInitializationException("Could not load properties", ex);
090                }
091        }
092
093        /**
094         * Convert the given merged properties, converting property values
095         * if necessary. The result will then be processed.
096         * <p>The default implementation will invoke {@link #convertPropertyValue}
097         * for each property value, replacing the original with the converted value.
098         * @param props the Properties to convert
099         * @see #processProperties
100         */
101        protected void convertProperties(Properties props) {
102                Enumeration<?> propertyNames = props.propertyNames();
103                while (propertyNames.hasMoreElements()) {
104                        String propertyName = (String) propertyNames.nextElement();
105                        String propertyValue = props.getProperty(propertyName);
106                        String convertedValue = convertProperty(propertyName, propertyValue);
107                        if (!ObjectUtils.nullSafeEquals(propertyValue, convertedValue)) {
108                                props.setProperty(propertyName, convertedValue);
109                        }
110                }
111        }
112
113        /**
114         * Convert the given property from the properties source to the value
115         * which should be applied.
116         * <p>The default implementation calls {@link #convertPropertyValue(String)}.
117         * @param propertyName the name of the property that the value is defined for
118         * @param propertyValue the original value from the properties source
119         * @return the converted value, to be used for processing
120         * @see #convertPropertyValue(String)
121         */
122        protected String convertProperty(String propertyName, String propertyValue) {
123                return convertPropertyValue(propertyValue);
124        }
125
126        /**
127         * Convert the given property value from the properties source to the value
128         * which should be applied.
129         * <p>The default implementation simply returns the original value.
130         * Can be overridden in subclasses, for example to detect
131         * encrypted values and decrypt them accordingly.
132         * @param originalValue the original value from the properties source
133         * (properties file or local "properties")
134         * @return the converted value, to be used for processing
135         * @see #setProperties
136         * @see #setLocations
137         * @see #setLocation
138         * @see #convertProperty(String, String)
139         */
140        protected String convertPropertyValue(String originalValue) {
141                return originalValue;
142        }
143
144
145        /**
146         * Apply the given Properties to the given BeanFactory.
147         * @param beanFactory the BeanFactory used by the application context
148         * @param props the Properties to apply
149         * @throws org.springframework.beans.BeansException in case of errors
150         */
151        protected abstract void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
152                        throws BeansException;
153
154}