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}