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
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user