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