Log included profiles according to the processing order

This commit includes some refactoring of active profiles
processing. Previously, there was a LIFO Queue for adding active
profiles. Profiles that were added last, were processed first.
Because of this reverse ordering, profiles were prepended to the
environment to preserve the order in which they were logged.
This however didn't work for "included" profiles as they were
prepended to the environment even though they were processed after
the active profile. In this commit, profiles are processed in a FIFO manner
and processed as they're found.

Fixes gh-11380
pull/13066/head
Madhura Bhave 7 years ago
parent 4ef7ea324f
commit cab9bff4f4

@ -26,7 +26,6 @@ import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@ -302,7 +301,7 @@ public class ConfigFileApplicationListener
private final List<PropertySourceLoader> propertySourceLoaders;
private Queue<Profile> profiles;
private LinkedList<Profile> profiles;
private List<Profile> processedProfiles;
@ -321,7 +320,7 @@ public class ConfigFileApplicationListener
}
public void load() {
this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
@ -343,62 +342,62 @@ public class ConfigFileApplicationListener
* properties that are already set.
*/
private void initializeProfiles() {
Set<Profile> initialActiveProfiles = initializeActiveProfiles();
this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles));
if (this.profiles.isEmpty()) {
//The default profile for these purposes is represented as null. We add it
// first so that it is processed first and has lowest priority.
this.profiles.add(null);
Set<Profile> activatedViaProperty = getProfilesActivatedViaActiveProfileProperty();
processOtherActiveProfiles(activatedViaProperty);
// Any pre-existing active activeProfiles set via property sources (e.g. System
// properties) take precedence over those added in config files.
addActiveProfiles(activatedViaProperty);
if (this.profiles.size() == 1) { //only has null profile
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
if (!this.profiles.contains(defaultProfile)) {
this.profiles.add(defaultProfile);
}
ConfigFileApplicationListener.Profile defaultProfile = new ConfigFileApplicationListener.Profile(
defaultProfileName, true);
this.profiles.add(defaultProfile);
}
}
// The default profile for these purposes is represented as null. We add it
// last so that it is first out of the queue (active profiles will then
// override any settings in the defaults when the list is reversed later).
this.profiles.add(null);
}
private Set<Profile> initializeActiveProfiles() {
private Set<Profile> getProfilesActivatedViaActiveProfileProperty() {
if (!this.environment.containsProperty(ACTIVE_PROFILES_PROPERTY)
&& !this.environment.containsProperty(INCLUDE_PROFILES_PROPERTY)) {
return Collections.emptySet();
}
// Any pre-existing active profiles set via property sources (e.g. System
// properties) take precedence over those added in config files.
Binder binder = Binder.get(this.environment);
Set<Profile> activeProfiles = new LinkedHashSet<>();
activeProfiles.addAll(getProfiles(binder, ACTIVE_PROFILES_PROPERTY));
activeProfiles.addAll(getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
maybeActivateProfiles(activeProfiles);
return activeProfiles;
}
/**
* Return the active profiles that have not been processed yet. If a profile is
* enabled via both {@link #ACTIVE_PROFILES_PROPERTY} and
* {@link ConfigurableEnvironment#addActiveProfile(String)} it needs to be
* filtered so that the {@link #ACTIVE_PROFILES_PROPERTY} value takes precedence.
* <p>
* Concretely, if the "cloud" profile is enabled via the environment, it will take
* less precedence that any profile set via the {@link #ACTIVE_PROFILES_PROPERTY}.
* @param initialActiveProfiles the profiles that have been enabled via
* {@link #ACTIVE_PROFILES_PROPERTY}
* @return the unprocessed active profiles from the environment to enable
*/
private List<Profile> getUnprocessedActiveProfiles(
Set<Profile> initialActiveProfiles) {
List<Profile> unprocessedActiveProfiles = new ArrayList<>();
for (String profileName : this.environment.getActiveProfiles()) {
Profile profile = new Profile(profileName);
if (!initialActiveProfiles.contains(profile)) {
unprocessedActiveProfiles.add(profile);
}
private void processOtherActiveProfiles(Set<Profile> activatedViaProperty) {
List<Profile> otherActiveProfiles = Arrays.stream(this.environment.getActiveProfiles())
.map(Profile::new)
.filter(o -> !activatedViaProperty.contains(o)).collect(Collectors.toList());
this.profiles.addAll(otherActiveProfiles);
}
void addActiveProfiles(Set<Profile> profiles) {
if (this.activatedProfiles || profiles.isEmpty()) {
return;
}
// Reverse them so the order is the same as from getProfilesForValue()
// (last one wins when properties are eventually resolved)
Collections.reverse(unprocessedActiveProfiles);
return unprocessedActiveProfiles;
addProfiles(profiles);
this.logger.debug("Activated activeProfiles "
+ StringUtils.collectionToCommaDelimitedString(profiles));
this.activatedProfiles = true;
removeUnprocessedDefaultProfiles();
}
void addProfiles(Set<Profile> profiles) {
for (Profile profile : profiles) {
this.profiles.add(profile);
addProfileToEnvironment(profile.getName());
}
}
private void removeUnprocessedDefaultProfiles() {
this.profiles.removeIf(profile -> (profile != null && profile.isDefaultProfile()));
}
private DocumentFilter getPositiveProfileFilter(Profile profile) {
@ -520,7 +519,7 @@ public class ConfigFileApplicationListener
List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
if (filter.match(document)) {
maybeActivateProfiles(document.getActiveProfiles());
addActiveProfiles(document.getActiveProfiles());
addProfiles(document.getIncludeProfiles());
loaded.add(document);
}
@ -587,58 +586,16 @@ public class ConfigFileApplicationListener
for (String profileName : profileNames) {
profiles.add(new Profile(profileName));
}
Collections.reverse(profiles);
return new LinkedHashSet<>(profiles);
}
private void maybeActivateProfiles(Set<Profile> profiles) {
if (profiles.isEmpty()) {
return;
}
if (this.activatedProfiles) {
this.logger.debug("Profiles already activated, '" + profiles
+ "' will not be applied");
return;
}
addProfiles(profiles);
this.logger.debug("Activated profiles "
+ StringUtils.collectionToCommaDelimitedString(profiles));
this.activatedProfiles = true;
removeUnprocessedDefaultProfiles();
}
private void removeUnprocessedDefaultProfiles() {
this.profiles.removeIf(Profile::isDefaultProfile);
}
private void addProfiles(Set<Profile> profiles) {
for (Profile profile : profiles) {
this.profiles.add(profile);
if (!environmentHasActiveProfile(profile.getName())) {
// If it's already accepted we assume the order was set
// intentionally
prependProfile(this.environment, profile);
}
}
}
private boolean environmentHasActiveProfile(String profile) {
private void addProfileToEnvironment(String profile) {
for (String activeProfile : this.environment.getActiveProfiles()) {
if (activeProfile.equals(profile)) {
return true;
return;
}
}
return false;
}
private void prependProfile(ConfigurableEnvironment environment,
Profile profile) {
Set<String> profiles = new LinkedHashSet<>();
environment.getActiveProfiles(); // ensure they are initialized
// But this one should go first (last wins in a property key clash)
profiles.add(profile.getName());
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
this.environment.addActiveProfile(profile);
}
private Set<String> getSearchLocations() {

@ -758,6 +758,8 @@ public class ConfigFileApplicationListenerTests {
assertThat(environment).has(matchingProfile("morespecific"));
assertThat(environment).has(matchingProfile("yetmorespecific"));
assertThat(environment).doesNotHave(matchingProfile("missing"));
assertThat(this.out.toString())
.contains("The following profiles are active: includeprofile,specific,morespecific,yetmorespecific");
}
@Test

Loading…
Cancel
Save