001/* 002 * Copyright 2012-2018 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 * http://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.boot.context.properties.source; 018 019import java.util.Collections; 020import java.util.stream.Stream; 021 022import org.springframework.core.env.ConfigurableEnvironment; 023import org.springframework.core.env.Environment; 024import org.springframework.core.env.MutablePropertySources; 025import org.springframework.core.env.PropertySource; 026import org.springframework.core.env.PropertySource.StubPropertySource; 027import org.springframework.core.env.PropertySources; 028import org.springframework.core.env.PropertySourcesPropertyResolver; 029import org.springframework.util.Assert; 030 031/** 032 * Provides access to {@link ConfigurationPropertySource ConfigurationPropertySources}. 033 * 034 * @author Phillip Webb 035 * @since 2.0.0 036 */ 037public final class ConfigurationPropertySources { 038 039 /** 040 * The name of the {@link PropertySource} {@link #adapt adapter}. 041 */ 042 private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties"; 043 044 private ConfigurationPropertySources() { 045 } 046 047 /** 048 * Determines if the specific {@link PropertySource} is the 049 * {@link ConfigurationPropertySource} that was {@link #attach(Environment) attached} 050 * to the {@link Environment}. 051 * @param propertySource the property source to test 052 * @return {@code true} if this is the attached {@link ConfigurationPropertySource} 053 */ 054 public static boolean isAttachedConfigurationPropertySource( 055 PropertySource<?> propertySource) { 056 return ATTACHED_PROPERTY_SOURCE_NAME.equals(propertySource.getName()); 057 } 058 059 /** 060 * Attach a {@link ConfigurationPropertySource} support to the specified 061 * {@link Environment}. Adapts each {@link PropertySource} managed by the environment 062 * to a {@link ConfigurationPropertySource} and allows classic 063 * {@link PropertySourcesPropertyResolver} calls to resolve using 064 * {@link ConfigurationPropertyName configuration property names}. 065 * <p> 066 * The attached resolver will dynamically track any additions or removals from the 067 * underlying {@link Environment} property sources. 068 * @param environment the source environment (must be an instance of 069 * {@link ConfigurableEnvironment}) 070 * @see #get(Environment) 071 */ 072 public static void attach(Environment environment) { 073 Assert.isInstanceOf(ConfigurableEnvironment.class, environment); 074 MutablePropertySources sources = ((ConfigurableEnvironment) environment) 075 .getPropertySources(); 076 PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME); 077 if (attached != null && attached.getSource() != sources) { 078 sources.remove(ATTACHED_PROPERTY_SOURCE_NAME); 079 attached = null; 080 } 081 if (attached == null) { 082 sources.addFirst(new ConfigurationPropertySourcesPropertySource( 083 ATTACHED_PROPERTY_SOURCE_NAME, 084 new SpringConfigurationPropertySources(sources))); 085 } 086 } 087 088 /** 089 * Return a set of {@link ConfigurationPropertySource} instances that have previously 090 * been {@link #attach(Environment) attached} to the {@link Environment}. 091 * @param environment the source environment (must be an instance of 092 * {@link ConfigurableEnvironment}) 093 * @return an iterable set of configuration property sources 094 * @throws IllegalStateException if not configuration property sources have been 095 * attached 096 */ 097 public static Iterable<ConfigurationPropertySource> get(Environment environment) { 098 Assert.isInstanceOf(ConfigurableEnvironment.class, environment); 099 MutablePropertySources sources = ((ConfigurableEnvironment) environment) 100 .getPropertySources(); 101 ConfigurationPropertySourcesPropertySource attached = (ConfigurationPropertySourcesPropertySource) sources 102 .get(ATTACHED_PROPERTY_SOURCE_NAME); 103 if (attached == null) { 104 return from(sources); 105 } 106 return attached.getSource(); 107 } 108 109 /** 110 * Return {@link Iterable} containing a single new {@link ConfigurationPropertySource} 111 * adapted from the given Spring {@link PropertySource}. 112 * @param source the Spring property source to adapt 113 * @return an {@link Iterable} containing a single newly adapted 114 * {@link SpringConfigurationPropertySource} 115 */ 116 public static Iterable<ConfigurationPropertySource> from(PropertySource<?> source) { 117 return Collections.singleton(SpringConfigurationPropertySource.from(source)); 118 } 119 120 /** 121 * Return {@link Iterable} containing new {@link ConfigurationPropertySource} 122 * instances adapted from the given Spring {@link PropertySource PropertySources}. 123 * <p> 124 * This method will flatten any nested property sources and will filter all 125 * {@link StubPropertySource stub property sources}. Updates to the underlying source, 126 * identified by changes in the sources returned by its iterator, will be 127 * automatically tracked. The underlying source should be thread safe, for example a 128 * {@link MutablePropertySources} 129 * @param sources the Spring property sources to adapt 130 * @return an {@link Iterable} containing newly adapted 131 * {@link SpringConfigurationPropertySource} instances 132 */ 133 public static Iterable<ConfigurationPropertySource> from( 134 Iterable<PropertySource<?>> sources) { 135 return new SpringConfigurationPropertySources(sources); 136 } 137 138 private static Stream<PropertySource<?>> streamPropertySources( 139 PropertySources sources) { 140 return sources.stream().flatMap(ConfigurationPropertySources::flatten) 141 .filter(ConfigurationPropertySources::isIncluded); 142 } 143 144 private static Stream<PropertySource<?>> flatten(PropertySource<?> source) { 145 if (source.getSource() instanceof ConfigurableEnvironment) { 146 return streamPropertySources( 147 ((ConfigurableEnvironment) source.getSource()).getPropertySources()); 148 } 149 return Stream.of(source); 150 } 151 152 private static boolean isIncluded(PropertySource<?> source) { 153 return !(source instanceof StubPropertySource) 154 && !(source instanceof ConfigurationPropertySourcesPropertySource); 155 } 156 157}