@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Arrays ;
import java.util.Arrays ;
import java.util.Collection ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.Collections ;
import java.util.Iterator ;
import java.util.LinkedHashSet ;
import java.util.LinkedHashSet ;
import java.util.LinkedList ;
import java.util.LinkedList ;
import java.util.List ;
import java.util.List ;
@ -326,9 +327,9 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
private PropertySourcesLoader propertiesLoader ;
private PropertySourcesLoader propertiesLoader ;
private Queue < String > profiles ;
private Queue < Profile > profiles ;
private List < String > processedProfiles ;
private List < Profile > processedProfiles ;
private boolean activatedProfiles ;
private boolean activatedProfiles ;
@ -341,16 +342,17 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
public void load ( ) throws IOException {
public void load ( ) throws IOException {
this . propertiesLoader = new PropertySourcesLoader ( ) ;
this . propertiesLoader = new PropertySourcesLoader ( ) ;
this . activatedProfiles = false ;
this . activatedProfiles = false ;
this . profiles = Collections . asLifoQueue ( new LinkedList < String > ( ) ) ;
this . profiles = Collections . asLifoQueue ( new LinkedList < Profile > ( ) ) ;
this . processedProfiles = new LinkedList < String > ( ) ;
this . processedProfiles = new LinkedList < Profile > ( ) ;
// Pre-existing active profiles set via Environment.setActiveProfiles()
// Pre-existing active profiles set via Environment.setActiveProfiles()
// are additional profiles and config files are allowed to add more if
// are additional profiles and config files are allowed to add more if
// they want to, so don't call addActiveProfiles() here.
// they want to, so don't call addActiveProfiles() here.
Set < String > initialActiveProfiles = initializeActiveProfiles ( ) ;
Set < Profile > initialActiveProfiles = initializeActiveProfiles ( ) ;
this . profiles . addAll ( getUnprocessedActiveProfiles ( initialActiveProfiles ) ) ;
this . profiles . addAll ( getUnprocessedActiveProfiles ( initialActiveProfiles ) ) ;
if ( this . profiles . isEmpty ( ) ) {
if ( this . profiles . isEmpty ( ) ) {
for ( String defaultProfile : this . environment . getDefaultProfiles ( ) ) {
for ( String defaultProfileName : this . environment . getDefaultProfiles ( ) ) {
Profile defaultProfile = new Profile ( defaultProfileName , true ) ;
if ( ! this . profiles . contains ( defaultProfile ) ) {
if ( ! this . profiles . contains ( defaultProfile ) ) {
this . profiles . add ( defaultProfile ) ;
this . profiles . add ( defaultProfile ) ;
}
}
@ -363,7 +365,7 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
this . profiles . add ( null ) ;
this . profiles . add ( null ) ;
while ( ! this . profiles . isEmpty ( ) ) {
while ( ! this . profiles . isEmpty ( ) ) {
String profile = this . profiles . poll ( ) ;
Profile profile = this . profiles . poll ( ) ;
for ( String location : getSearchLocations ( ) ) {
for ( String location : getSearchLocations ( ) ) {
if ( ! location . endsWith ( "/" ) ) {
if ( ! location . endsWith ( "/" ) ) {
// location is a filename already, so don't search for more
// location is a filename already, so don't search for more
@ -382,13 +384,13 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
addConfigurationProperties ( this . propertiesLoader . getPropertySources ( ) ) ;
addConfigurationProperties ( this . propertiesLoader . getPropertySources ( ) ) ;
}
}
private Set < String > initializeActiveProfiles ( ) {
private Set < Profile > initializeActiveProfiles ( ) {
if ( ! this . environment . containsProperty ( ACTIVE_PROFILES_PROPERTY ) ) {
if ( ! this . environment . containsProperty ( ACTIVE_PROFILES_PROPERTY ) ) {
return Collections . emptySet ( ) ;
return Collections . emptySet ( ) ;
}
}
// Any pre-existing active profiles set via property sources (e.g. System
// Any pre-existing active profiles set via property sources (e.g. System
// properties) take precedence over those added in config files.
// properties) take precedence over those added in config files.
Set < String > activeProfiles = getProfilesForValue (
Set < Profile > activeProfiles = getProfilesForValue (
this . environment . getProperty ( ACTIVE_PROFILES_PROPERTY ) ) ;
this . environment . getProperty ( ACTIVE_PROFILES_PROPERTY ) ) ;
maybeActivateProfiles ( activeProfiles ) ;
maybeActivateProfiles ( activeProfiles ) ;
return activeProfiles ;
return activeProfiles ;
@ -406,10 +408,11 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
* { @link # ACTIVE_PROFILES_PROPERTY }
* { @link # ACTIVE_PROFILES_PROPERTY }
* @return the unprocessed active profiles from the environment to enable
* @return the unprocessed active profiles from the environment to enable
* /
* /
private List < String > getUnprocessedActiveProfiles (
private List < Profile > getUnprocessedActiveProfiles (
Set < String > initialActiveProfiles ) {
Set < Profile > initialActiveProfiles ) {
List < String > unprocessedActiveProfiles = new ArrayList < String > ( ) ;
List < Profile > unprocessedActiveProfiles = new ArrayList < Profile > ( ) ;
for ( String profile : this . environment . getActiveProfiles ( ) ) {
for ( String profileName : this . environment . getActiveProfiles ( ) ) {
Profile profile = new Profile ( profileName ) ;
if ( ! initialActiveProfiles . contains ( profile ) ) {
if ( ! initialActiveProfiles . contains ( profile ) ) {
unprocessedActiveProfiles . add ( profile ) ;
unprocessedActiveProfiles . add ( profile ) ;
}
}
@ -420,7 +423,7 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
return unprocessedActiveProfiles ;
return unprocessedActiveProfiles ;
}
}
private void load ( String location , String name , String profile )
private void load ( String location , String name , Profile profile )
throws IOException {
throws IOException {
String group = "profile=" + ( profile = = null ? "" : profile ) ;
String group = "profile=" + ( profile = = null ? "" : profile ) ;
if ( ! StringUtils . hasText ( name ) ) {
if ( ! StringUtils . hasText ( name ) ) {
@ -434,7 +437,7 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
// Try the profile-specific file
// Try the profile-specific file
loadIntoGroup ( group , location + name + "-" + profile + "." + ext ,
loadIntoGroup ( group , location + name + "-" + profile + "." + ext ,
null ) ;
null ) ;
for ( String processedProfile : this . processedProfiles ) {
for ( Profile processedProfile : this . processedProfiles ) {
if ( processedProfile ! = null ) {
if ( processedProfile ! = null ) {
loadIntoGroup ( group , location + name + "-"
loadIntoGroup ( group , location + name + "-"
+ processedProfile + "." + ext , profile ) ;
+ processedProfile + "." + ext , profile ) ;
@ -453,7 +456,7 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
}
}
private PropertySource < ? > loadIntoGroup ( String identifier , String location ,
private PropertySource < ? > loadIntoGroup ( String identifier , String location ,
String profile ) throws IOException {
Profile profile ) throws IOException {
Resource resource = this . resourceLoader . getResource ( location ) ;
Resource resource = this . resourceLoader . getResource ( location ) ;
PropertySource < ? > propertySource = null ;
PropertySource < ? > propertySource = null ;
StringBuilder msg = new StringBuilder ( ) ;
StringBuilder msg = new StringBuilder ( ) ;
@ -461,7 +464,7 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
String name = "applicationConfig: [" + location + "]" ;
String name = "applicationConfig: [" + location + "]" ;
String group = "applicationConfig: [" + identifier + "]" ;
String group = "applicationConfig: [" + identifier + "]" ;
propertySource = this . propertiesLoader . load ( resource , group , name ,
propertySource = this . propertiesLoader . load ( resource , group , name ,
profile ) ;
( profile = = null ? null : profile . getName ( ) ) ) ;
if ( propertySource ! = null ) {
if ( propertySource ! = null ) {
msg . append ( "Loaded " ) ;
msg . append ( "Loaded " ) ;
handleProfileProperties ( propertySource ) ;
handleProfileProperties ( propertySource ) ;
@ -475,7 +478,7 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
}
}
msg . append ( "config file " ) ;
msg . append ( "config file " ) ;
msg . append ( getResourceDescription ( location , resource ) ) ;
msg . append ( getResourceDescription ( location , resource ) ) ;
if ( StringUtils. hasLength ( profile ) ) {
if ( profile ! = null ) {
msg . append ( " for profile " ) . append ( profile ) ;
msg . append ( " for profile " ) . append ( profile ) ;
}
}
if ( resource = = null | | ! resource . exists ( ) ) {
if ( resource = = null | | ! resource . exists ( ) ) {
@ -503,15 +506,15 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
}
}
private void handleProfileProperties ( PropertySource < ? > propertySource ) {
private void handleProfileProperties ( PropertySource < ? > propertySource ) {
Set < String > activeProfiles = getProfilesForValue (
Set < Profile > activeProfiles = getProfilesForValue (
propertySource . getProperty ( ACTIVE_PROFILES_PROPERTY ) ) ;
propertySource . getProperty ( ACTIVE_PROFILES_PROPERTY ) ) ;
maybeActivateProfiles ( activeProfiles ) ;
maybeActivateProfiles ( activeProfiles ) ;
Set < String > includeProfiles = getProfilesForValue (
Set < Profile > includeProfiles = getProfilesForValue (
propertySource . getProperty ( INCLUDE_PROFILES_PROPERTY ) ) ;
propertySource . getProperty ( INCLUDE_PROFILES_PROPERTY ) ) ;
addProfiles ( includeProfiles ) ;
addProfiles ( includeProfiles ) ;
}
}
private void maybeActivateProfiles ( Set < String > profiles ) {
private void maybeActivateProfiles ( Set < Profile > profiles ) {
if ( this . activatedProfiles ) {
if ( this . activatedProfiles ) {
if ( ! profiles . isEmpty ( ) ) {
if ( ! profiles . isEmpty ( ) ) {
this . logger . debug ( "Profiles already activated, '" + profiles
this . logger . debug ( "Profiles already activated, '" + profiles
@ -524,18 +527,33 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
this . logger . debug ( "Activated profiles "
this . logger . debug ( "Activated profiles "
+ StringUtils . collectionToCommaDelimitedString ( profiles ) ) ;
+ StringUtils . collectionToCommaDelimitedString ( profiles ) ) ;
this . activatedProfiles = true ;
this . activatedProfiles = true ;
removeUnprocessedDefaultProfiles ( ) ;
}
}
}
}
private Set < String > getProfilesForValue ( Object property ) {
private void removeUnprocessedDefaultProfiles ( ) {
for ( Iterator < Profile > iterator = this . profiles . iterator ( ) ; iterator
. hasNext ( ) ; ) {
if ( iterator . next ( ) . isDefaultProfile ( ) ) {
iterator . remove ( ) ;
}
}
}
private Set < Profile > getProfilesForValue ( Object property ) {
String value = ( property = = null ? null : property . toString ( ) ) ;
String value = ( property = = null ? null : property . toString ( ) ) ;
return asResolvedSet ( value , null ) ;
Set < String > profileNames = asResolvedSet ( value , null ) ;
Set < Profile > profiles = new LinkedHashSet < Profile > ( ) ;
for ( String profileName : profileNames ) {
profiles . add ( new Profile ( profileName ) ) ;
}
return profiles ;
}
}
private void addProfiles ( Set < String > profiles ) {
private void addProfiles ( Set < Profile > profiles ) {
for ( String profile : profiles ) {
for ( Profile profile : profiles ) {
this . profiles . add ( profile ) ;
this . profiles . add ( profile ) ;
if ( ! this . environment . acceptsProfiles ( profile ) ) {
if ( ! this . environment . acceptsProfiles ( profile .getName ( ) )) {
// If it's already accepted we assume the order was set
// If it's already accepted we assume the order was set
// intentionally
// intentionally
prependProfile ( this . environment , profile ) ;
prependProfile ( this . environment , profile ) ;
@ -543,11 +561,12 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
}
}
}
}
private void prependProfile ( ConfigurableEnvironment environment , String profile ) {
private void prependProfile ( ConfigurableEnvironment environment ,
Profile profile ) {
Set < String > profiles = new LinkedHashSet < String > ( ) ;
Set < String > profiles = new LinkedHashSet < String > ( ) ;
environment . getActiveProfiles ( ) ; // ensure they are initialized
environment . getActiveProfiles ( ) ; // ensure they are initialized
// But this one should go first (last wins in a property key clash)
// But this one should go first (last wins in a property key clash)
profiles . add ( profile );
profiles . add ( profile .getName ( ) );
profiles . addAll ( Arrays . asList ( environment . getActiveProfiles ( ) ) ) ;
profiles . addAll ( Arrays . asList ( environment . getActiveProfiles ( ) ) ) ;
environment . setActiveProfiles ( profiles . toArray ( new String [ profiles . size ( ) ] ) ) ;
environment . setActiveProfiles ( profiles . toArray ( new String [ profiles . size ( ) ] ) ) ;
}
}
@ -612,6 +631,53 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
}
}
private static class Profile {
private final String name ;
private final boolean defaultProfile ;
Profile ( String name ) {
this ( name , false ) ;
}
Profile ( String name , boolean defaultProfile ) {
Assert . notNull ( name , "Name must not be null" ) ;
this . name = name ;
this . defaultProfile = defaultProfile ;
}
public String getName ( ) {
return this . name ;
}
public boolean isDefaultProfile ( ) {
return this . defaultProfile ;
}
@Override
public String toString ( ) {
return this . name ;
}
@Override
public int hashCode ( ) {
return this . name . hashCode ( ) ;
}
@Override
public boolean equals ( Object obj ) {
if ( obj = = this ) {
return true ;
}
if ( obj = = null | | obj . getClass ( ) ! = getClass ( ) ) {
return false ;
}
return ( ( Profile ) obj ) . name . equals ( this . name ) ;
}
}
/ * *
/ * *
* Holds the configuration { @link PropertySource } s as they are loaded can relocate
* Holds the configuration { @link PropertySource } s as they are loaded can relocate
* them once configuration classes have been processed .
* them once configuration classes have been processed .