001/*
002 * Copyright 2002-2017 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.core.env;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021
022import org.springframework.util.Assert;
023import org.springframework.util.ObjectUtils;
024
025/**
026 * Abstract base class representing a source of name/value property pairs. The underlying
027 * {@linkplain #getSource() source object} may be of any type {@code T} that encapsulates
028 * properties. Examples include {@link java.util.Properties} objects, {@link java.util.Map}
029 * objects, {@code ServletContext} and {@code ServletConfig} objects (for access to init
030 * parameters). Explore the {@code PropertySource} type hierarchy to see provided
031 * implementations.
032 *
033 * <p>{@code PropertySource} objects are not typically used in isolation, but rather
034 * through a {@link PropertySources} object, which aggregates property sources and in
035 * conjunction with a {@link PropertyResolver} implementation that can perform
036 * precedence-based searches across the set of {@code PropertySources}.
037 *
038 * <p>{@code PropertySource} identity is determined not based on the content of
039 * encapsulated properties, but rather based on the {@link #getName() name} of the
040 * {@code PropertySource} alone. This is useful for manipulating {@code PropertySource}
041 * objects when in collection contexts. See operations in {@link MutablePropertySources}
042 * as well as the {@link #named(String)} and {@link #toString()} methods for details.
043 *
044 * <p>Note that when working with @{@link
045 * org.springframework.context.annotation.Configuration Configuration} classes that
046 * the @{@link org.springframework.context.annotation.PropertySource PropertySource}
047 * annotation provides a convenient and declarative way of adding property sources to the
048 * enclosing {@code Environment}.
049 *
050 * @author Chris Beams
051 * @since 3.1
052 * @see PropertySources
053 * @see PropertyResolver
054 * @see PropertySourcesPropertyResolver
055 * @see MutablePropertySources
056 * @see org.springframework.context.annotation.PropertySource
057 */
058public abstract class PropertySource<T> {
059
060        protected final Log logger = LogFactory.getLog(getClass());
061
062        protected final String name;
063
064        protected final T source;
065
066
067        /**
068         * Create a new {@code PropertySource} with the given name and source object.
069         */
070        public PropertySource(String name, T source) {
071                Assert.hasText(name, "Property source name must contain at least one character");
072                Assert.notNull(source, "Property source must not be null");
073                this.name = name;
074                this.source = source;
075        }
076
077        /**
078         * Create a new {@code PropertySource} with the given name and with a new
079         * {@code Object} instance as the underlying source.
080         * <p>Often useful in testing scenarios when creating anonymous implementations
081         * that never query an actual source but rather return hard-coded values.
082         */
083        @SuppressWarnings("unchecked")
084        public PropertySource(String name) {
085                this(name, (T) new Object());
086        }
087
088
089        /**
090         * Return the name of this {@code PropertySource}
091         */
092        public String getName() {
093                return this.name;
094        }
095
096        /**
097         * Return the underlying source object for this {@code PropertySource}.
098         */
099        public T getSource() {
100                return this.source;
101        }
102
103        /**
104         * Return whether this {@code PropertySource} contains the given name.
105         * <p>This implementation simply checks for a {@code null} return value
106         * from {@link #getProperty(String)}. Subclasses may wish to implement
107         * a more efficient algorithm if possible.
108         * @param name the property name to find
109         */
110        public boolean containsProperty(String name) {
111                return (getProperty(name) != null);
112        }
113
114        /**
115         * Return the value associated with the given name,
116         * or {@code null} if not found.
117         * @param name the property to find
118         * @see PropertyResolver#getRequiredProperty(String)
119         */
120        public abstract Object getProperty(String name);
121
122
123        /**
124         * This {@code PropertySource} object is equal to the given object if:
125         * <ul>
126         * <li>they are the same instance
127         * <li>the {@code name} properties for both objects are equal
128         * </ul>
129         * <p>No properties other than {@code name} are evaluated.
130         */
131        @Override
132        public boolean equals(Object obj) {
133                return (this == obj || (obj instanceof PropertySource &&
134                                ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) obj).name)));
135        }
136
137        /**
138         * Return a hash code derived from the {@code name} property
139         * of this {@code PropertySource} object.
140         */
141        @Override
142        public int hashCode() {
143                return ObjectUtils.nullSafeHashCode(this.name);
144        }
145
146        /**
147         * Produce concise output (type and name) if the current log level does not include
148         * debug. If debug is enabled, produce verbose output including the hash code of the
149         * PropertySource instance and every name/value property pair.
150         * <p>This variable verbosity is useful as a property source such as system properties
151         * or environment variables may contain an arbitrary number of property pairs,
152         * potentially leading to difficult to read exception and log messages.
153         * @see Log#isDebugEnabled()
154         */
155        @Override
156        public String toString() {
157                if (logger.isDebugEnabled()) {
158                        return getClass().getSimpleName() + "@" + System.identityHashCode(this) +
159                                        " {name='" + this.name + "', properties=" + this.source + "}";
160                }
161                else {
162                        return getClass().getSimpleName() + " {name='" + this.name + "'}";
163                }
164        }
165
166
167        /**
168         * Return a {@code PropertySource} implementation intended for collection comparison purposes only.
169         * <p>Primarily for internal use, but given a collection of {@code PropertySource} objects, may be
170         * used as follows:
171         * <pre class="code">
172         * {@code List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
173         * sources.add(new MapPropertySource("sourceA", mapA));
174         * sources.add(new MapPropertySource("sourceB", mapB));
175         * assert sources.contains(PropertySource.named("sourceA"));
176         * assert sources.contains(PropertySource.named("sourceB"));
177         * assert !sources.contains(PropertySource.named("sourceC"));
178         * }</pre>
179         * The returned {@code PropertySource} will throw {@code UnsupportedOperationException}
180         * if any methods other than {@code equals(Object)}, {@code hashCode()}, and {@code toString()}
181         * are called.
182         * @param name the name of the comparison {@code PropertySource} to be created and returned.
183         */
184        public static PropertySource<?> named(String name) {
185                return new ComparisonPropertySource(name);
186        }
187
188
189        /**
190         * {@code PropertySource} to be used as a placeholder in cases where an actual
191         * property source cannot be eagerly initialized at application context
192         * creation time.  For example, a {@code ServletContext}-based property source
193         * must wait until the {@code ServletContext} object is available to its enclosing
194         * {@code ApplicationContext}.  In such cases, a stub should be used to hold the
195         * intended default position/order of the property source, then be replaced
196         * during context refresh.
197         * @see org.springframework.context.support.AbstractApplicationContext#initPropertySources()
198         * @see org.springframework.web.context.support.StandardServletEnvironment
199         * @see org.springframework.web.context.support.ServletContextPropertySource
200         */
201        public static class StubPropertySource extends PropertySource<Object> {
202
203                public StubPropertySource(String name) {
204                        super(name, new Object());
205                }
206
207                /**
208                 * Always returns {@code null}.
209                 */
210                @Override
211                public String getProperty(String name) {
212                        return null;
213                }
214        }
215
216
217        /**
218         * @see PropertySource#named(String)
219         */
220        static class ComparisonPropertySource extends StubPropertySource {
221
222                private static final String USAGE_ERROR =
223                                "ComparisonPropertySource instances are for use with collection comparison only";
224
225                public ComparisonPropertySource(String name) {
226                        super(name);
227                }
228
229                @Override
230                public Object getSource() {
231                        throw new UnsupportedOperationException(USAGE_ERROR);
232                }
233
234                @Override
235                public boolean containsProperty(String name) {
236                        throw new UnsupportedOperationException(USAGE_ERROR);
237                }
238
239                @Override
240                public String getProperty(String name) {
241                        throw new UnsupportedOperationException(USAGE_ERROR);
242                }
243        }
244
245}