From f455b1e89aac57703b61e5ddcf20285cc95d7f1d Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Wed, 1 Dec 2010 09:01:58 +0000 Subject: [PATCH] Support default profile (SPR-7508, SPR-7778) 'default' is now a reserved profile name, indicating that any beans defined within that profile will be registered unless another profile or profiles have been activated. Examples below are expressed in XML, but apply equally when using the @Profile annotation. EXAMPLE 1: In the case above, the EmbeddedFooImpl 'foo' bean will be registered if: a) no profile is active b) the 'default' profile has explicitly been made active The ProdFooImpl 'foo' bean will be registered if the 'production' profile is active. EXAMPLE 2: Bean 'foo' will be registered if any of the following are true: a) no profile is active b) 'xyz' profile is active c) 'default' profile has explicitly been made active d) both (b) and (c) are true Note that the default profile is not to be confused with specifying no profile at all. When the default profile is specified, beans are registered only if no other profiles are active; whereas when no profile is specified, bean definitions are always registered regardless of which profiles are active. The default profile may be configured programmatically: environmnent.setDefaultProfile("embedded"); or declaratively through any registered PropertySource, e.g. system properties: -DdefaultSpringProfile=embedded Assuming either of the above, example 1 could be rewritten as follows: It is unlikely that use of the default profile will make sense in conjunction with a statically specified 'springProfiles' property. For example, if 'springProfiles' is specified as a web.xml context param, that profile will always be active for that application, negating the possibility of default profile bean definitions ever being registered. The default profile is most useful for ensuring that a valid set of bean definitions will always be registered without forcing users to explictly specify active profiles. In the embedded vs. production examples above, it is assumed that the application JVM will be started with -DspringProfiles=production when the application is in fact in a production environment. Otherwise, the embedded/default profile bean definitions will always be registered. git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3804 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../DefaultBeanDefinitionDocumentReader.java | 23 +++----- .../beans/factory/xml/spring-beans-3.1.xsd | 9 +++- .../xml/ProfileXmlBeanDefinitionTests.java | 42 ++++++++++++++- ...anDefinitionTests-customDefaultProfile.xml | 13 +++++ ...anDefinitionTests-defaultAndDevProfile.xml | 9 ++++ ...eXmlBeanDefinitionTests-defaultProfile.xml | 13 +++++ .../AnnotatedBeanDefinitionReader.java | 43 +++------------ ...athScanningCandidateComponentProvider.java | 25 +++------ .../annotation/ConfigurationClassParser.java | 24 +++------ .../context/annotation/Profile.java | 10 ++++ ...anningCandidateComponentProviderTests.java | 53 ++++++++++++++++++- .../core/env/AbstractEnvironment.java | 41 +++++++++++++- .../core/env/ConfigurableEnvironment.java | 7 +++ .../springframework/core/env/Environment.java | 13 +++++ .../core/env/DefaultEnvironmentTests.java | 53 +++++++------------ 15 files changed, 249 insertions(+), 129 deletions(-) create mode 100644 org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-customDefaultProfile.xml create mode 100644 org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-defaultAndDevProfile.xml create mode 100644 org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-defaultProfile.xml diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java index 1b6ed450d1a..ab79610932a 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java @@ -112,23 +112,12 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume protected void doRegisterBeanDefinitions(Element root) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); - boolean isCandidate = false; - if (profileSpec == null || profileSpec.equals("")) { - isCandidate = true; - } else { - String[] profiles = commaDelimitedListToStringArray(trimAllWhitespace(profileSpec)); - for (String profile : profiles) { - if (this.environment.getActiveProfiles().contains(profile)) { - isCandidate = true; - break; - } - } - } - - if (!isCandidate) { - // TODO SPR-7508 logging - // logger.debug(format("XML is targeted for environment [%s], but current environment is [%s]. Skipping", targetEnvironment, environment == null ? null : environment.getName())); - return; + if (StringUtils.hasText(profileSpec)) { + String[] specifiedProfiles = commaDelimitedListToStringArray(trimAllWhitespace(profileSpec)); + if (!this.environment.acceptsProfiles(specifiedProfiles)) { + // TODO SPR-7508: log that this bean is being rejected on profile mismatch + return; + } } // any nested elements will cause recursion in this method. in diff --git a/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.1.xsd b/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.1.xsd index 5fa65f6bbd7..814a63c7036 100644 --- a/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.1.xsd +++ b/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.1.xsd @@ -81,7 +81,14 @@ diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests.java index 2bd47474f57..a4841f0dae8 100644 --- a/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests.java +++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests.java @@ -25,6 +25,7 @@ import org.junit.Test; import org.junit.internal.matchers.TypeSafeMatcher; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.DefaultEnvironment; import org.springframework.core.io.ClassPathResource; @@ -40,6 +41,9 @@ public class ProfileXmlBeanDefinitionTests { private static final String ALL_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-noProfile.xml"; private static final String MULTI_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-multiProfile.xml"; private static final String UNKOWN_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-unknownProfile.xml"; + private static final String DEFAULT_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-defaultProfile.xml"; + private static final String CUSTOM_DEFAULT_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-customDefaultProfile.xml"; + private static final String DEFAULT_AND_DEV_ELIGIBLE_XML = "ProfileXmlBeanDefinitionTests-defaultAndDevProfile.xml"; private static final String PROD_ACTIVE = "prod"; private static final String DEV_ACTIVE = "dev"; @@ -80,11 +84,45 @@ public class ProfileXmlBeanDefinitionTests { assertThat(beanFactoryFor(UNKOWN_ELIGIBLE_XML, MULTI_ACTIVE), not(containsTargetBean())); } - private BeanDefinitionRegistry beanFactoryFor(String xmlName, String... activeProfileNames) { + @Test + public void testDefaultProfile() { + assertThat(beanFactoryFor(DEFAULT_ELIGIBLE_XML, NONE_ACTIVE), containsTargetBean()); + assertThat(beanFactoryFor(DEFAULT_ELIGIBLE_XML, "other"), not(containsTargetBean())); + + assertThat(beanFactoryFor(DEFAULT_AND_DEV_ELIGIBLE_XML, DEV_ACTIVE), containsTargetBean()); + assertThat(beanFactoryFor(DEFAULT_AND_DEV_ELIGIBLE_XML, NONE_ACTIVE), containsTargetBean()); + assertThat(beanFactoryFor(DEFAULT_AND_DEV_ELIGIBLE_XML, PROD_ACTIVE), not(containsTargetBean())); + } + + @Test + public void testCustomDefaultProfile() { + { + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); + ConfigurableEnvironment env = new DefaultEnvironment(); + env.setDefaultProfile("custom-default"); + reader.setEnvironment(env); + reader.loadBeanDefinitions(new ClassPathResource(DEFAULT_ELIGIBLE_XML, getClass())); + + assertThat(beanFactory, not(containsTargetBean())); + } + { + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); + ConfigurableEnvironment env = new DefaultEnvironment(); + env.setDefaultProfile("custom-default"); + reader.setEnvironment(env); + reader.loadBeanDefinitions(new ClassPathResource(CUSTOM_DEFAULT_ELIGIBLE_XML, getClass())); + + assertThat(beanFactory, containsTargetBean()); + } + } + + private BeanDefinitionRegistry beanFactoryFor(String xmlName, String... activeProfiles) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); DefaultEnvironment env = new DefaultEnvironment(); - env.setActiveProfiles(activeProfileNames); + env.setActiveProfiles(activeProfiles); reader.setEnvironment(env); reader.loadBeanDefinitions(new ClassPathResource(xmlName, getClass())); return beanFactory; diff --git a/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-customDefaultProfile.xml b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-customDefaultProfile.xml new file mode 100644 index 00000000000..fbd09ae0fc8 --- /dev/null +++ b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-customDefaultProfile.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-defaultAndDevProfile.xml b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-defaultAndDevProfile.xml new file mode 100644 index 00000000000..5fbc741ec85 --- /dev/null +++ b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-defaultAndDevProfile.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-defaultProfile.xml b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-defaultProfile.xml new file mode 100644 index 00000000000..a7d425abf1b --- /dev/null +++ b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/ProfileXmlBeanDefinitionTests-defaultProfile.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java index 47e4ff256c6..8aebaa07b96 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java @@ -106,45 +106,18 @@ public class AnnotatedBeanDefinitionReader { registerBean(annotatedClass, null, qualifiers); } - private boolean hasEligibleProfile(AnnotationMetadata metadata) { - boolean hasEligibleProfile = false; - if (!metadata.hasAnnotation(Profile.class.getName())) { - hasEligibleProfile = true; - } else { - for (String profile : (String[])metadata.getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME)) { - if (this.environment.getActiveProfiles().contains(profile)) { - hasEligibleProfile = true; - break; - } - } - } - return hasEligibleProfile; - } - public void registerBean(Class annotatedClass, String name, Class... qualifiers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); - if (!hasEligibleProfile(abd.getMetadata())) { - // TODO SPR-7508: log that this bean is being rejected on profile mismatch - return; - } - /* - if (metadata.hasAnnotation(Profile.class.getName())) { - if (this.environment == null) { - return; - } - Map profileAttribs = metadata.getAnnotationAttributes(Profile.class.getName()); - String[] names = (String[]) profileAttribs.get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME); - boolean go=false; - for (String pName : names) { - if (this.environment.getActiveProfiles().contains(pName)) { - go = true; - } - } - if (!go) { + AnnotationMetadata metadata = abd.getMetadata(); + + if (metadata.hasAnnotation(Profile.class.getName())) { + String[] specifiedProfiles = + (String[])metadata.getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME); + if (!this.environment.acceptsProfiles(specifiedProfiles)) { + // TODO SPR-7508: log that this bean is being rejected on profile mismatch return; - } + } } - */ ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java index 850fe621a7c..72662c44f62 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java @@ -37,6 +37,7 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternUtils; +import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; @@ -296,28 +297,18 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { - return hasEligibleProfile(metadataReader); + AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); + if (!metadata.hasAnnotation(Profile.class.getName())) { + return true; + } + String[] specifiedProfiles = + (String[])metadata.getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME); + return this.environment.acceptsProfiles(specifiedProfiles); } } return false; } - private boolean hasEligibleProfile(MetadataReader metadataReader) { - boolean hasEligibleProfile = false; - if (!metadataReader.getAnnotationMetadata().hasAnnotation(Profile.class.getName())) { - hasEligibleProfile = true; - } else { - for (String profile : (String[])metadataReader.getAnnotationMetadata().getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME)) { - if (this.environment.getActiveProfiles().contains(profile)) { - hasEligibleProfile = true; - break; - } - } - } - return hasEligibleProfile; - } - - /** * Determine whether the given bean definition qualifies as candidate. *

The default implementation checks whether the class is concrete diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 31c623f6ec2..f9270f545a5 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -101,26 +101,14 @@ class ConfigurationClassParser { } protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { - boolean hasEligibleProfile = false; - if (this.environment == null) { - hasEligibleProfile = true; - } else { - if (!configClass.getMetadata().hasAnnotation(Profile.class.getName())) { - hasEligibleProfile = true; - } else { - for (String profile : (String[])configClass.getMetadata().getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME)) { - if (this.environment.getActiveProfiles().contains(profile)) { - hasEligibleProfile = true; - break; - } - } + if (this.environment != null && configClass.getMetadata().hasAnnotation(Profile.class.getName())) { + String[] specifiedProfiles = + (String[])configClass.getMetadata().getAnnotationAttributes(Profile.class.getName()).get(Profile.CANDIDATE_PROFILES_ATTRIB_NAME); + if (!this.environment.acceptsProfiles(specifiedProfiles)) { + // TODO SPR-7508: log that this bean is being rejected on profile mismatch + return; } } - if (!hasEligibleProfile) { - //logger.debug("TODO SPR-7508: issue debug statement that this class is being excluded"); - // make sure XML has a symmetrical statement as well - return; - } AnnotationMetadata metadata = configClass.getMetadata(); while (metadata != null) { diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Profile.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Profile.java index 5d6cf21a63b..b3192d3d2fe 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Profile.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Profile.java @@ -24,6 +24,16 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** + * TODO SPR-7508: document + * + * Components not @Profile-annotated will always be registered + * @Profile("default") means that beans will be registered unless other profile(s) are active + * @Profile({"xyz,default"}) means that beans will be registered if 'xyz' is active or if no profile is active + * ConfigurableEnvironment.setDefaultProfileName(String) customizes the name of the default profile + * 'defaultSpringProfile' property customizes the name of the default profile (usually for use as a servlet context/init param) + * ConfigurableEnvironment.setActiveProfiles(String...) sets which profiles are active + * 'springProfiles' sets which profiles are active (typically as a -D system property) + * * @author Chris Beams * @since 3.1 */ diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProviderTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProviderTests.java index 0774a66cfd0..813f63665ea 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProviderTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProviderTests.java @@ -29,6 +29,7 @@ import java.util.regex.Pattern; import org.aspectj.lang.annotation.Aspect; import org.junit.Test; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.core.env.AbstractEnvironment; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.DefaultEnvironment; import org.springframework.core.type.filter.AnnotationTypeFilter; @@ -215,8 +216,44 @@ public class ClassPathScanningCandidateComponentProviderTests { assertThat(ctx.containsBean(ProfileAnnotatedComponent.BEAN_NAME), is(false)); } - private boolean containsBeanClass(Set candidates, Class beanClass) { - for (Iterator it = candidates.iterator(); it.hasNext();) { + @Test + public void testIntegrationWithAnnotationConfigApplicationContext_defaultProfile() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + // no active profiles are set + ctx.register(DefaultProfileAnnotatedComponent.class); + ctx.refresh(); + assertThat(ctx.containsBean(DefaultProfileAnnotatedComponent.BEAN_NAME), is(true)); + } + + @Test + public void testIntegrationWithAnnotationConfigApplicationContext_defaultAndDevProfile() { + Class beanClass = DefaultAndDevProfileAnnotatedComponent.class; + String beanName = DefaultAndDevProfileAnnotatedComponent.BEAN_NAME; + { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + // no active profiles are set + ctx.register(beanClass); + ctx.refresh(); + assertThat(ctx.containsBean(beanName), is(true)); + } + { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.getEnvironment().setActiveProfiles("dev"); + ctx.register(beanClass); + ctx.refresh(); + assertThat(ctx.containsBean(beanName), is(true)); + } + { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.getEnvironment().setActiveProfiles("other"); + ctx.register(beanClass); + ctx.refresh(); + assertThat(ctx.containsBean(beanName), is(false)); + } + } + + private boolean containsBeanClass(Set candidates, Class beanClass) { + for (Iterator it = candidates.iterator(); it.hasNext();) { ScannedGenericBeanDefinition definition = (ScannedGenericBeanDefinition) it.next(); if (beanClass.getName().equals(definition.getBeanClassName())) { return true; @@ -225,4 +262,16 @@ public class ClassPathScanningCandidateComponentProviderTests { return false; } + + @Profile(AbstractEnvironment.DEFAULT_PROFILE_NAME) + @Component(DefaultProfileAnnotatedComponent.BEAN_NAME) + private static class DefaultProfileAnnotatedComponent { + static final String BEAN_NAME = "defaultProfileAnnotatedComponent"; + } + + @Profile({AbstractEnvironment.DEFAULT_PROFILE_NAME,"dev"}) + @Component(DefaultAndDevProfileAnnotatedComponent.BEAN_NAME) + private static class DefaultAndDevProfileAnnotatedComponent { + static final String BEAN_NAME = "defaultAndDevProfileAnnotatedComponent"; + } } diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/AbstractEnvironment.java b/org.springframework.core/src/main/java/org/springframework/core/env/AbstractEnvironment.java index 7e6842b7c45..dd463a34c3f 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/env/AbstractEnvironment.java +++ b/org.springframework.core/src/main/java/org/springframework/core/env/AbstractEnvironment.java @@ -50,7 +50,17 @@ import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; */ public abstract class AbstractEnvironment implements ConfigurableEnvironment { - public static final String SPRING_PROFILES_PROPERTY_NAME = "springProfiles"; + public static final String ACTIVE_PROFILES_PROPERTY_NAME = "springProfiles"; + + public static final String DEFAULT_PROFILE_PROPERTY_NAME = "defaultSpringProfile"; + + /** + * Default name of the default profile. Override with + * {@link #setDefaultProfile(String)}. + * + * @see #setDefaultProfile(String) + */ + public static final String DEFAULT_PROFILE_NAME = "default"; protected final Log logger = LogFactory.getLog(getClass()); @@ -66,6 +76,8 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { private boolean explicitlySetProfiles; + private String defaultProfile = DEFAULT_PROFILE_NAME; + public void addPropertySource(PropertySource propertySource) { propertySources.push(propertySource); @@ -183,7 +195,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { if (explicitlySetProfiles) return; - String profiles = getProperty(SPRING_PROFILES_PROPERTY_NAME); + String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME); if (profiles == null || profiles.equals("")) { return; } @@ -267,6 +279,31 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { return doResolvePlaceholders(text, strictHelper); } + public boolean acceptsProfiles(String[] specifiedProfiles) { + boolean activeProfileFound = false; + Set activeProfiles = this.getActiveProfiles(); + for (String profile : specifiedProfiles) { + if (activeProfiles.contains(profile) + || (activeProfiles.isEmpty() && profile.equals(this.getDefaultProfile()))) { + activeProfileFound = true; + break; + } + } + return activeProfileFound; + } + + public String getDefaultProfile() { + String defaultSpringProfileProperty = getProperty(DEFAULT_PROFILE_PROPERTY_NAME); + if (defaultSpringProfileProperty != null) { + return defaultSpringProfileProperty; + } + return defaultProfile; + } + + public void setDefaultProfile(String defaultProfile) { + this.defaultProfile = defaultProfile; + } + private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) { return helper.replacePlaceholders(text, new PlaceholderResolver() { public String resolvePlaceholder(String placeholderName) { diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java b/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java index d9242f527ea..827ae5d4e5b 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java +++ b/org.springframework.core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java @@ -29,4 +29,11 @@ public interface ConfigurableEnvironment extends Environment, PropertySourceAggr */ void setActiveProfiles(String... profiles); + /** + * Set the default profile name to be used instead of 'default' + * + * @param defaultProfile + */ + void setDefaultProfile(String defaultProfile); + } diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/Environment.java b/org.springframework.core/src/main/java/org/springframework/core/env/Environment.java index 12fe953cc04..90c073c2367 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/env/Environment.java +++ b/org.springframework.core/src/main/java/org/springframework/core/env/Environment.java @@ -35,6 +35,19 @@ public interface Environment { */ Set getActiveProfiles(); + /** + * TODO SPR-7508: document + */ + String getDefaultProfile(); + + /** + * TODO SPR-7508: document + * returns true if: + * a) one or more of specifiedProfiles are active in the given environment - see {@link #getActiveProfiles()} + * b) specifiedProfiles contains default profile - see {@link #getDefaultProfile()} + */ + boolean acceptsProfiles(String[] specifiedProfiles); + /** * TODO SPR-7508: document */ diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/DefaultEnvironmentTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/DefaultEnvironmentTests.java index f912291e62e..ff7af9459d0 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/env/DefaultEnvironmentTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/env/DefaultEnvironmentTests.java @@ -28,10 +28,11 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.junit.matchers.JUnitMatchers.hasItem; import static org.junit.matchers.JUnitMatchers.hasItems; -import static org.springframework.core.env.AbstractEnvironment.SPRING_PROFILES_PROPERTY_NAME; +import static org.springframework.core.env.AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME; +import static org.springframework.core.env.AbstractEnvironment.DEFAULT_PROFILE_NAME; +import static org.springframework.core.env.AbstractEnvironment.DEFAULT_PROFILE_PROPERTY_NAME; import static org.springframework.core.env.DefaultEnvironmentTests.CollectionMatchers.isEmpty; -import java.io.IOException; import java.lang.reflect.Field; import java.security.AccessControlException; import java.security.Permission; @@ -48,8 +49,6 @@ import org.hamcrest.Matcher; import org.junit.Before; import org.junit.Test; import org.junit.internal.matchers.TypeSafeMatcher; -import org.springframework.core.type.AnnotationMetadata; -import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; /** * Unit tests for {@link DefaultEnvironment}. @@ -220,59 +219,47 @@ public class DefaultEnvironmentTests { public void systemPropertiesEmpty() { assertThat(environment.getActiveProfiles(), isEmpty()); - System.setProperty(SPRING_PROFILES_PROPERTY_NAME, ""); + System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, ""); assertThat(environment.getActiveProfiles(), isEmpty()); - System.getProperties().remove(SPRING_PROFILES_PROPERTY_NAME); + System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME); } @Test public void systemPropertiesResoloutionOfProfiles() { assertThat(environment.getActiveProfiles(), isEmpty()); - System.setProperty(SPRING_PROFILES_PROPERTY_NAME, "foo"); + System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo"); assertThat(environment.getActiveProfiles(), hasItem("foo")); // clean up - System.getProperties().remove(SPRING_PROFILES_PROPERTY_NAME); + System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME); } @Test public void systemPropertiesResoloutionOfMultipleProfiles() { assertThat(environment.getActiveProfiles(), isEmpty()); - System.setProperty(SPRING_PROFILES_PROPERTY_NAME, "foo,bar"); + System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo,bar"); assertThat(environment.getActiveProfiles(), hasItems("foo", "bar")); - System.setProperty(SPRING_PROFILES_PROPERTY_NAME, " bar , baz "); // notice whitespace + System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, " bar , baz "); // notice whitespace assertThat(environment.getActiveProfiles(), not(hasItems("foo", "bar"))); assertThat(environment.getActiveProfiles(), hasItems("bar", "baz")); - System.getProperties().remove(SPRING_PROFILES_PROPERTY_NAME); + System.getProperties().remove(ACTIVE_PROFILES_PROPERTY_NAME); } - /* - static class WithNoProfile { } - @Profile("test") - static class WithTestProfile { } - @Test - public void accepts() throws IOException { - - assertThat(environment.accepts(metadataForClass(WithNoProfile.class)), is(true)); - assertThat(environment.accepts(metadataForClass(WithTestProfile.class)), is(false)); - assertThat(environment.accepts("foo,bar"), is(false)); - assertThat(environment.accepts("test"), is(false)); - assertThat(environment.accepts("test,foo"), is(false)); - - environment.setActiveProfiles("test"); - assertThat(environment.accepts(metadataForClass(WithNoProfile.class)), is(true)); - assertThat(environment.accepts(metadataForClass(WithTestProfile.class)), is(true)); - assertThat(environment.accepts("foo,bar"), is(false)); - assertThat(environment.accepts("test"), is(true)); - assertThat(environment.accepts("test,foo"), is(true)); + public void environmentResolutionOfDefaultSpringProfileProperty_noneSet() { + assertThat(environment.getDefaultProfile(), equalTo(DEFAULT_PROFILE_NAME)); + } + + @Test + public void environmentResolutionOfDefaultSpringProfileProperty_isSet() { + testProperties.setProperty(DEFAULT_PROFILE_PROPERTY_NAME, "custom-default"); + assertThat(environment.getDefaultProfile(), equalTo("custom-default")); } - */ @Test public void systemPropertiesAccess() { @@ -413,10 +400,6 @@ public class DefaultEnvironmentTests { } } - private AnnotationMetadata metadataForClass(Class clazz) throws IOException { - return new SimpleMetadataReaderFactory().getMetadataReader(clazz.getName()).getAnnotationMetadata(); - } - public static class CollectionMatchers { public static Matcher> isEmpty() {