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}