Skip to content

Instantly share code, notes, and snippets.

@dsyer
Created March 30, 2011 07:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dsyer/894004 to your computer and use it in GitHub Desktop.
Save dsyer/894004 to your computer and use it in GitHub Desktop.
Bootstrap Spring with @feature
target
bin
.classpath
.project
.settings
*.swp
*/src/main/java/META-INF
nohup.out
.idea
*.iml
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<version>1</version>
<pluginVersion><![CDATA[2.5.1.201011101000-RELEASE]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
<enableImports><![CDATA[true]]></enableImports>
<configs>
<config>src/test/resources/org/springframework/bootstrap/config/ProfileConfigurationTests-context.xml</config>
<config>src/test/resources/org/springframework/bootstrap/config/EnvironmentConfigurationTests-context.xml</config>
<config>src/test/resources/org/springframework/bootstrap/config/ActivationProfileConfigurationTests-context.xml</config>
<config>src/test/resources/org/springframework/bootstrap/config/ApplicationIdentityConfigurationTests-context.xml</config>
<config>src/test/resources/org/springframework/bootstrap/config/GenericFeatureConfigurationTests-context.xml</config>
<config>src/test/resources/org/springframework/bootstrap/config/BeanFactoryGenericFeatureConfigurationTests-context.xml</config>
<config>src/test/resources/org/springframework/bootstrap/config/NamespaceGenericFeatureConfigurationTests-context.xml</config>
<config>src/test/resources/org/springframework/bootstrap/config/NoGenericFeatureConfigurationTests-context.xml</config>
<config>src/test/resources/org/springframework/bootstrap/config/Log4jConfigurationTests-context.xml</config>
</configs>
<configSets>
<configSet>
<name><![CDATA[test]]></name>
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
<incomplete>false</incomplete>
<configs>
</configs>
</configSet>
</configSets>
</beansProjectDescription>

#Summary

What I like best is the SpecificationContext - it has everything you need to manipulate the bean factory / registry / context before it is used. Even if it doesn't have everything, it is headed in the right direction. In contrast a BFPP can always do everything, but the API is not as nice and it is a more dangerous opportunity to abuse the container (e.g. a BFPP always risks causing a cascade of early initialization if it touches something that it shouldn't).

My top issues are

  • Lack of consistency between @Configuration and XML.
  • Unclear semantics wrt ordering and alternatives. When do the feature specs get executed and why? Why would I use a FeatureSpecification and when ContextInitializer and when BeanFactoryPostProcessor?
  • Lack of support for BeanFactory goodies like externalization and DI in XML parser products (e.g. look at the MvcResources feature).

I'm wondering if ContextInitializer might be redundant - I would prefer to do everything I do there in a FeatureSpecification - or maybe that is the only choice for strict ordering (it happens before anything else).
In any case there are quite a few CI use cases that would work just as well as a FS, and they would be easier use becauser they would favour a declarative approach. Danger is we end up with duplication, so there would need to be an abstraction that they can share (e.g. SpecificationContext).

I have been able to achieve some quite nice results, but the bottom line is I could have done it with BFPP. That would have allowed me to use DI and externalization without any trickery. On the other hand, and this could be important, BFPP always suffer from early initialization syndrome. If we are stricter about the semantics of the new features then we can ensure that they are always segregated from the main ApplicationContext (like in my <bootstrap:context/>). I can also see opportunities for late sub-context creation, taking advantage of parent-child relations.

There are also some major hacks in my parsers. I would like to expose some more utility methods in parser space.

Comments

  • I don't really understand why this:

      <bean class="com.foo.MyFeatureSpecification"/>
    

    is different to this:

      <bean class="com.foo.MyConfiguration"/>
      @FeatureConfiguration
      public class MyConfiguration {
          @Feature
          public FeatureSpecification specification() {
              return new MyFeatureSpecification();
          }
      }
    

    (The latter works, the former does not.)

  • With the example above it's annoying because to add a property to MyFeatureSpecification I have to add it to MyConfiguration as well.

  • Note: Environment.getDefaultProfiles() has no callers, but acceptsProfiles() does.

  • Using a @Feature to set up a default profile might work, but doesn't because the <beans/> sub-elements are parsed before the feature is executed. E.g. this doesn't work

          <!-- set up the default profile to "bar" -->
      	<bean class="org.springframework.context.EnvironmentConfiguration"/>
          <beans profile="bar">
              <bean class="..."/>
              ...
          </beans>
    

    Maybe we need a default-profile attribute in <beans/> anyway?

    Later: it appears that "" is the default profile. That works, I guess, but it would be nice to be able to declare it somewhere (or make it a bit more obvious).

    Later still: actually "" is not a default profile. In fact there is an explicit test for empty string in DefaultBeanDefinitionReader.doRegisterBeanDefinitions and those beans are ignored. Odd that because using @ConfigurationLocation works, but loading the context file directly does not(!?).

  • FeatureSpecification instances are executed by a BFPP (ConfigurationClassPostProcessor) and that is not registered by default in a GenericXmlApplicationContext. It suffices to add <context:annotation-config/> to the context, but no-one would expect to have to do that. Maybe just documentation?

  • There seems to be a lack of consistency in the conventions for detecting and using features. A bean definition that resolves to a @FeatureConfiguration works, but a @Bean does not (you have to @Import it).

  • A ManagedProperties is a Properties but doesn't behave like one - it's really a hack taking advantage of the fact that Properties is a Hashtable (yuck). To parse a <props/> and convert to an actual Properties you therefore have to create a BeanFactory. There should be a better way?

  • A @Feature might be a good way to implement the profile validation that a lot of people have asked for (e.g. barf if an active profile is not isted in a white list).

  • MapPropertySource has no public constructor. PropertySource has some fishy looking public factory methods - all its subclasses get them as well, but they don't create instances of the subclass. Either the conxtructor should be public or the class should be less visible.

  • The GenericFeatureSpecificationParser could actually execute all instances of FeatureSpecification that it finds in its sub-context, instead of returning a single on eto be executed by its base class. It has to get a SpecificationContext from somewhere. AbstractSpecificationBeanDefinitionParser has a private method to set up the SpecificationContext, but would be better as a public static method somewhere else (e.g. utility class).

  • A @Feature might be a bood way to activate profiles as well.

  • The name of a PropertySource could be used to locate a property precisely, sinstead of relying on searching the whole Environment (a bit like JSF/Webflow scope hierarchies).

  • There is no easy way to re-use existing reader code to load bean definitions from a fragment of XML (i.e. an Element). There is support at the Document level, but that's not convenient unless you have the whole thing to parse. There are some vicious hacks in GenericFeatureSpecificationParser as a result, and it limits the schema choices - you basically have to use <beans/> to nest child context otherwise there's no way to parse it.

  • Writing XML parsers from FeatureSpecifications doesn't really work that well. The current implementation of the abstract feature parser does not use BeanDefinitions to define the content, so it cannot easily accept expressions and placeholders as parameters. E.g. the MVC ResourcesBeanDefinitionParser works around this by having a cachePeriod in the specification of type Object(!). WTF? We can do better than this.

  • The Javadocs for FeatureSpecification suggest really strongly that you use a fluent API for the implementation, but that's so unfriendly for DI that it makes writing the XML parsers too fiddly for me. Is it so bad to write two lines of code instead of one in a @Feature? There are relaxed rules for setter signatures in the bean factory now, so that helps, but they don't extend to methods not starting with "set".

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:bootstrap="http://www.springframework.org/schema/bootstrap"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/bootstrap http://www.springframework.org/schema/bootstrap/spring-bootstrap-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:property-placeholder />
<bean id="service" class="org.springframework.bootstrap.config.ExampleService">
<property name="message" value="${foo:Hello World}"></property>
</bean>
<bootstrap:context>
<bean class="org.springframework.bootstrap.feature.ProfileActivationFeatureSpecification">
<property name="activeProfiles" value="spam" />
<property name="condition" value="#{environment['foo']==null}" />
</bean>
</bootstrap:context>
<beans profile="bar">
<bootstrap:environment>
<prop key="foo">bar</prop>
</bootstrap:environment>
</beans>
<beans profile="spam">
<bootstrap:environment>
<prop key="foo">spam</prop>
</bootstrap:environment>
</beans>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:bootstrap="http://www.springframework.org/schema/bootstrap"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/bootstrap http://www.springframework.org/schema/bootstrap/spring-bootstrap-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:property-placeholder />
<bean id="service" class="org.springframework.bootstrap.config.ExampleService">
<property name="message" value="${foo:Hello World}"></property>
</bean>
<bootstrap:application-identity id="test"/>
</beans>
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.config;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.bootstrap.feature.ApplicationIdentityFeatureSpecification;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Feature;
import org.springframework.context.annotation.FeatureConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
public class ApplicationIdentityConfigurationTests {
@Autowired
@Test
public void testSimpleProperties() throws Exception {
GenericXmlApplicationContext context = new GenericXmlApplicationContext(getClass(), getClass().getSimpleName()+"-context.xml");
assertEquals("test", context.getId());
}
@Test
public void testJavaConfig() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);
assertEquals("test", context.getId());
}
@Configuration
@Import(Features.class)
public static class TestConfiguration {
@Bean
public Service service(@Value("${foo:Hello World}") String message) {
ExampleService service = new ExampleService();
service.setMessage(message);
return service;
}
@Bean
public PropertySourcesPlaceholderConfigurer configurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
return configurer;
}
}
@FeatureConfiguration
public static class Features implements ApplicationContextAware {
private ApplicationContext context;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
@Feature
public FeatureSpecification specification() {
ApplicationIdentityFeatureSpecification specification = new ApplicationIdentityFeatureSpecification();
specification.setApplicationContext(context);
specification.setId("test");
return specification;
}
}
}
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.feature;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.context.config.SpecificationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.util.Assert;
/**
* @author Dave Syer
*
*/
public class ApplicationIdentityFeatureSpecification implements FeatureSpecification, ApplicationContextAware {
private ApplicationContext applicationContext;
private String id;
public void setId(String id) {
this.id = id;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public boolean validate(ProblemReporter problemReporter) {
return true;
}
public void execute(SpecificationContext specificationContext) {
ConfigurableApplicationContext context = null;
if (applicationContext instanceof ConfigurableApplicationContext) {
context = (ConfigurableApplicationContext) applicationContext;
} else if (specificationContext.getRegistry() instanceof ConfigurableApplicationContext) {
context = (ConfigurableApplicationContext) specificationContext.getRegistry();
} else if (specificationContext.getResourceLoader() instanceof ConfigurableApplicationContext) {
context = (ConfigurableApplicationContext) specificationContext.getResourceLoader();
}
Assert.state(context != null, "ConfigurableApplicationContext could not be located");
if (id != null) {
context.setId(id);
}
Assert.state(specificationContext.getEnvironment() instanceof ConfigurableEnvironment,
"Environment is not configurable");
ConfigurableEnvironment environment = (ConfigurableEnvironment) specificationContext.getEnvironment();
Properties properties = new Properties();
properties.setProperty("id", context.getId());
environment.getPropertySources().addLast(new PropertiesPropertySource("application", properties));
}
}
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.config;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.bootstrap.feature.ApplicationIdentityFeatureSpecification;
import org.springframework.context.config.AbstractSpecificationBeanDefinitionParser;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* @author Dave Syer
*
*/
public class ApplicationIdentityFeatureSpecificationParser extends AbstractSpecificationBeanDefinitionParser {
@Override
protected FeatureSpecification doParse(Element element, ParserContext parserContext) {
ApplicationIdentityFeatureSpecification specification = new ApplicationIdentityFeatureSpecification();
String id = element.getAttribute("id");
if (StringUtils.hasText(id)) {
specification.setId(id);
}
return specification ;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:bootstrap="http://www.springframework.org/schema/bootstrap"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/bootstrap http://www.springframework.org/schema/bootstrap/spring-bootstrap-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:property-placeholder />
<bean id="service" class="org.springframework.bootstrap.config.ExampleService">
<property name="message" value="#{bootstrap.string}"/>
</bean>
<bootstrap:context id="bootstrap">
<bean id="string" class="java.lang.String">
<constructor-arg value="bucket" />
</bean>
</bootstrap:context>
</beans>
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.config;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* Namespace handler for Bootstrap.
*
* @author Dave Syer
*/
public class BootstrapNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("environment", new EnvironmentFeatureSpecificationParser());
registerBeanDefinitionParser("application-identity", new ApplicationIdentityFeatureSpecificationParser());
registerBeanDefinitionParser("context", new GenericFeatureSpecificationParser());
}
}
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.config;
import java.util.Properties;
import org.springframework.bootstrap.feature.EnvironmentFeatureSpecification;
import org.springframework.context.annotation.Feature;
import org.springframework.context.annotation.FeatureConfiguration;
import org.springframework.context.config.FeatureSpecification;
/**
* @author Dave Syer
*
*/
@FeatureConfiguration
public class EnvironmentConfiguration {
private EnvironmentFeatureSpecification specification = new EnvironmentFeatureSpecification();
public void setOrder(int order) {
specification.setOrder(order);
}
/**
* @param properties the properties to set
*/
public void setProperties(Properties properties) {
specification.setProperties(properties);
}
@Feature
public FeatureSpecification specification() {
return specification;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:bootstrap="http://www.springframework.org/schema/bootstrap"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/bootstrap http://www.springframework.org/schema/bootstrap/spring-bootstrap-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:property-placeholder />
<bean id="service" class="org.springframework.bootstrap.config.ExampleService">
<property name="message" value="${foo:Hello World}"></property>
</bean>
<bootstrap:environment>
<prop key="foo">bar</prop>
</bootstrap:environment>
</beans>
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.config;
import static org.junit.Assert.assertEquals;
import java.util.Properties;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.bootstrap.feature.EnvironmentFeatureSpecification;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Feature;
import org.springframework.context.annotation.FeatureConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
public class EnvironmentConfigurationTests {
@Test
public void testSimpleProperties() throws Exception {
GenericXmlApplicationContext context = new GenericXmlApplicationContext(getClass(), getClass().getSimpleName()+"-context.xml");
Service service = context.getBean(Service.class);
assertEquals("bar", service.getMessage());
}
@Test
public void testJavaConfig() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);
Service service = context.getBean(Service.class);
assertEquals("bar", service.getMessage());
}
@Configuration
@Import(Features.class)
public static class TestConfiguration {
@Bean
public Service service(@Value("${foo}") String message) {
ExampleService service = new ExampleService();
service.setMessage(message);
return service;
}
@Bean
public PropertySourcesPlaceholderConfigurer configurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
return configurer;
}
}
@FeatureConfiguration
public static class Features {
@Feature
public FeatureSpecification specification() {
EnvironmentFeatureSpecification specification = new EnvironmentFeatureSpecification();
Properties properties = new Properties();
properties.setProperty("foo", "bar");
specification.setProperties(properties );
return specification;
}
}
}
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.feature;
import java.util.Properties;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.context.config.SpecificationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.util.Assert;
/**
* @author Dave Syer
*
*/
public class EnvironmentFeatureSpecification implements FeatureSpecification, Ordered, BeanNameAware {
private Properties properties = new Properties();
private int order = Ordered.LOWEST_PRECEDENCE;
private String name = "feature";
public void setName(String name) {
this.name = name;
}
public void setBeanName(String name) {
if (this.name == null) {
this.name = name;
}
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public boolean validate(ProblemReporter problemReporter) {
return true;
}
public void execute(SpecificationContext specificationContext) {
Assert.state(specificationContext.getEnvironment() instanceof ConfigurableEnvironment, "Environment is not configurable");
ConfigurableEnvironment environment = (ConfigurableEnvironment) specificationContext.getEnvironment();
environment.getPropertySources().addFirst(new PropertiesPropertySource(name, properties));
}
}
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.config;
import java.util.Properties;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.bootstrap.feature.EnvironmentFeatureSpecification;
import org.springframework.context.config.AbstractSpecificationBeanDefinitionParser;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.w3c.dom.Element;
/**
* @author Dave Syer
*
*/
public class EnvironmentFeatureSpecificationParser extends AbstractSpecificationBeanDefinitionParser {
@Override
protected FeatureSpecification doParse(Element element, ParserContext parserContext) {
GenericApplicationContext context = new GenericApplicationContext();
Environment environment = parserContext.getDelegate().getEnvironment();
if (environment instanceof ConfigurableEnvironment) {
context.setEnvironment((ConfigurableEnvironment) environment);
}
Properties properties = parserContext.getDelegate().parsePropsElement(element);
BeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(EnvironmentFeatureSpecification.class)
.addPropertyValue("properties", properties).getBeanDefinition();
String name = parserContext.getReaderContext().generateBeanName(
BeanDefinitionBuilder.genericBeanDefinition(getClass()).getRawBeanDefinition());
context.registerBeanDefinition(name, beanDefinition);
context.refresh();
try {
return context.getBean(FeatureSpecification.class);
} finally {
context.close();
}
}
}
package org.springframework.bootstrap.config;
/**
* {@link Service} with hard-coded input data.
*/
public class ExampleService implements Service {
private String message;
/**
* @param message the message to set
*/
public void setMessage(String message) {
this.message = message;
}
/**
* Reads next record from input
*/
public String getMessage() {
return message;
}
}
package org.springframework.bootstrap.config;
import org.springframework.bootstrap.config.ExampleService;
import junit.framework.TestCase;
public class ExampleServiceTests extends TestCase {
private ExampleService service = new ExampleService();
public void testReadOnce() throws Exception {
assertEquals(null, service.getMessage());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:bootstrap="http://www.springframework.org/schema/bootstrap"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/bootstrap http://www.springframework.org/schema/bootstrap/spring-bootstrap-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:property-placeholder />
<bean id="service" class="org.springframework.bootstrap.config.ExampleService">
<property name="message" value="${foo:Hello World}" />
</bean>
<bootstrap:context id="bootstrap">
<beans>
<bean class="org.springframework.bootstrap.feature.EnvironmentFeatureSpecification">
<property name="properties" ref="properties" />
</bean>
<bean id="properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="foo">bar</prop>
</props>
</property>
</bean>
</beans>
</bootstrap:context>
</beans>
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.config;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.springframework.context.support.GenericXmlApplicationContext;
public class GenericFeatureConfigurationTests {
@Test
public void testFeaturesExposed() throws Exception {
GenericXmlApplicationContext context = new GenericXmlApplicationContext(getClass(), getClass().getSimpleName()
+ "-context.xml");
Service service = context.getBean("service", Service.class);
assertEquals("bar", service.getMessage());
}
@Test
public void testFeaturesNotExposed() throws Exception {
GenericXmlApplicationContext context = new GenericXmlApplicationContext(getClass(), "No"
+ getClass().getSimpleName() + "-context.xml");
Service service = context.getBean("service", Service.class);
assertEquals("spam", service.getMessage());
}
@Test
public void testBeanFactoryExposed() throws Exception {
GenericXmlApplicationContext context = new GenericXmlApplicationContext(getClass(), "BeanFactory"
+ getClass().getSimpleName() + "-context.xml");
Service service = context.getBean("service", Service.class);
assertEquals("bucket", service.getMessage());
}
@Test
public void testNamespaceParsingInSubContext() throws Exception {
GenericXmlApplicationContext context = new GenericXmlApplicationContext(getClass(), "Namespace"
+ getClass().getSimpleName() + "-context.xml");
Service service = context.getBean("service", Service.class);
assertEquals("crap", service.getMessage());
}
}
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.config;
import java.lang.reflect.Field;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.ComponentRegistrarAdapter;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.parsing.ReaderContext;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.xml.XmlReaderContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.Lifecycle;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.context.config.SpecificationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.DefaultEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* @author Dave Syer
*
*/
public class GenericFeatureSpecificationParser implements BeanDefinitionParser {
/** Constant for the id attribute */
public static final String ID_ATTRIBUTE = "id";
/** Constant for the expose-features attribute */
private static final String EXPOSE_FEATURES_ATTRIBUTE = "expose-features";
public BeanDefinition parse(Element element, ParserContext parserContext) {
final GenericApplicationContext bootstrapApplicationContext = new GenericApplicationContext();
Environment environment = parserContext.getDelegate().getEnvironment();
if (environment instanceof ConfigurableEnvironment) {
bootstrapApplicationContext.setEnvironment((ConfigurableEnvironment) environment);
}
registerBootstrapApplicationContext(bootstrapApplicationContext, element, parserContext);
registerBeanDefinitionsInBootstrapApplicationContext(bootstrapApplicationContext, element, parserContext);
bootstrapApplicationContext.refresh();
if ("true".equals(element.getAttribute(EXPOSE_FEATURES_ATTRIBUTE))) {
exposeFeatures(bootstrapApplicationContext, parserContext);
}
return null;
}
/**
* Copied from AbstractSpecificationBeanDefinitionParser. Adapt the given ParserContext into a SpecificationContext.
*/
private static SpecificationContext specificationContextFrom(ParserContext parserContext) {
SpecificationContext specContext = new SpecificationContext();
specContext.setRegistry(parserContext.getRegistry());
specContext.setRegistrar(new ComponentRegistrarAdapter(parserContext));
specContext.setResourceLoader(parserContext.getReaderContext().getResourceLoader());
try {
// again, STS constraints around the addition of the new getEnvironment()
// method in 3.1.0 (it's not present in STS current spring version, 3.0.5)
// TODO 3.1 GA: remove this block prior to 3.1 GA
specContext.setEnvironment(parserContext.getDelegate().getEnvironment());
} catch (NoSuchMethodError ex) {
specContext.setEnvironment(new DefaultEnvironment());
}
try {
// access the reader context's problem reporter reflectively in order to
// compensate for tooling (STS) constraints around introduction of changes
// to parser context / reader context classes.
// TODO 3.1 GA: remove this block prior to 3.1 GA
Field field = ReaderContext.class.getDeclaredField("problemReporter");
field.setAccessible(true);
ProblemReporter problemReporter = (ProblemReporter) field.get(parserContext.getReaderContext());
specContext.setProblemReporter(problemReporter);
} catch (Exception ex) {
throw new IllegalStateException("Could not access field 'ReaderContext#problemReporter' on object "
+ parserContext.getReaderContext(), ex);
}
return specContext;
}
public void registerBootstrapApplicationContext(final GenericApplicationContext context, Element element,
ParserContext parserContext) {
BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(ContextHolder.class)
.addConstructorArgValue(context).getBeanDefinition();
String id = element.getAttribute(ID_ATTRIBUTE);
String beanName = parserContext.getReaderContext().generateBeanName(beanDefinition);
if (StringUtils.hasText(id)) {
context.setId(id);
beanName = id;
}
parserContext.getRegistry().registerBeanDefinition(beanName, beanDefinition);
}
public void exposeFeatures(final GenericApplicationContext context, ParserContext parserContext) {
SpecificationContext specificationContext = specificationContextFrom(parserContext);
for (FeatureSpecification specification : context.getBeansOfType(FeatureSpecification.class).values()) {
specification.execute(specificationContext);
}
}
private void registerBeanDefinitionsInBootstrapApplicationContext(BeanDefinitionRegistry registry, Element element,
ParserContext parserContext) {
LocalXmlBeanDefinitionReader beanDefinitionReader = new LocalXmlBeanDefinitionReader( //
registry, //
parserContext.getDelegate().getEnvironment(), //
parserContext.getReaderContext().getResource()//
);
beanDefinitionReader.register(element);
}
private static class LocalXmlBeanDefinitionReader extends XmlBeanDefinitionReader {
private Resource resource;
public LocalXmlBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment, Resource resource) {
super(registry);
this.resource = resource;
setEnvironment(environment);
}
public void register(Element element) {
XmlReaderContext readerContext = createReaderContext(resource);
LocalBeanDefinitionDocumentReader documentReader = new LocalBeanDefinitionDocumentReader(
this.getEnvironment());
documentReader.registerBeanDefinitions(element, readerContext);
}
}
private static class LocalBeanDefinitionDocumentReader extends DefaultBeanDefinitionDocumentReader {
public LocalBeanDefinitionDocumentReader(Environment environment) {
super.setEnvironment(environment);
}
public void registerBeanDefinitions(Element element, XmlReaderContext readerContext) {
try {
registerBeanDefinitions((Document) null, readerContext);
} catch (NullPointerException e) {
// Hack: ignore
}
BeanDefinitionParserDelegate delegate = createHelper(readerContext, element, null);
for (Element child : DomUtils.getChildElements(element)) {
if ("bean".equals(child.getNodeName())) {
processBeanDefinition(child, delegate);
} else {
doRegisterBeanDefinitions(child);
}
}
}
}
private static class ContextHolder implements BeanFactory, Lifecycle, DisposableBean {
final ConfigurableApplicationContext context;
@SuppressWarnings("unused")
public ContextHolder(ConfigurableApplicationContext context) {
super();
this.context = context;
}
public void destroy() throws Exception {
context.close();
}
public void start() {
context.start();
}
public void stop() {
context.stop();
}
public boolean isRunning() {
return context.isRunning();
}
public Object getBean(String name) throws BeansException {
return context.getBean(name);
}
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return context.getBean(name, requiredType);
}
public <T> T getBean(Class<T> requiredType) throws BeansException {
return context.getBean(requiredType);
}
public Object getBean(String name, Object... args) throws BeansException {
return context.getBean(name, args);
}
public boolean containsBean(String name) {
return context.containsBean(name);
}
public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return context.isSingleton(name);
}
public boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException {
return context.isTypeMatch(name, targetType);
}
public String[] getAliases(String name) {
return context.getAliases(name);
}
public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
return context.isPrototype(name);
}
public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return context.getType(name);
}
}
}
log4j.rootCategory=TRACE, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%p (%c{2}): <%m>%n
# log4j.category.test.jdbc=DEBUG
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.category.org.apache.activemq=ERROR
log4j.category.org.springframework.batch=DEBUG
log4j.category.org.springframework.transaction=INFO
log4j.category.org.hibernate.SQL=DEBUG
# for debugging datasource initialization
# log4j.category.test.jdbc=DEBUG
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.feature;
import java.io.FileNotFoundException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.context.config.SpecificationContext;
import org.springframework.util.Log4jConfigurer;
/**
* @author Dave Syer
*
*/
public class Log4jConfigurationFeatureSpecification implements FeatureSpecification {
private String location = "classpath:log4j.properties";
public void setLocation(String location) {
this.location = location;
}
public boolean validate(ProblemReporter problemReporter) {
return true;
}
public void execute(SpecificationContext specificationContext) {
try {
Log4jConfigurer.initLogging(location);
} catch (FileNotFoundException e) {
throw new BeanDefinitionStoreException("Could not locate config for Log4J: " + location, e);
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:bootstrap="http://www.springframework.org/schema/bootstrap"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/bootstrap http://www.springframework.org/schema/bootstrap/spring-bootstrap-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="service" class="org.springframework.bootstrap.config.ExampleService">
<property name="message" value="spam" />
</bean>
<bootstrap:context id="bootstrap">
<beans>
<!-- This context needs placeholder resolution (otherwise only System properties will be used by the Log4jConfigurer -->
<context:property-placeholder />
<bean class="org.springframework.bootstrap.feature.Log4jConfigurationFeatureSpecification">
<property name="location" value="classpath:log4j-${ENVIRONMENT:default}.properties" />
</bean>
</beans>
</bootstrap:context>
</beans>
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.config;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.springframework.context.support.GenericXmlApplicationContext;
public class Log4jConfigurationTests {
@Test
public void testSimpleProperties() throws Exception {
GenericXmlApplicationContext context = new GenericXmlApplicationContext(getClass(), getClass().getSimpleName()+"-context.xml");
Service service = context.getBean(Service.class);
assertEquals("spam", service.getMessage());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:bootstrap="http://www.springframework.org/schema/bootstrap"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/bootstrap http://www.springframework.org/schema/bootstrap/spring-bootstrap-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:property-placeholder />
<bean id="service" class="org.springframework.bootstrap.config.ExampleService">
<property name="message" value="${foo:Hello World}" />
</bean>
<bootstrap:context id="bootstrap">
<beans>
<bean class="org.springframework.bootstrap.feature.EnvironmentFeatureSpecification">
<property name="properties" ref="properties" />
</bean>
<util:properties id="properties">
<prop key="foo">crap</prop>
</util:properties>
</beans>
</bootstrap:context>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:bootstrap="http://www.springframework.org/schema/bootstrap"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/bootstrap http://www.springframework.org/schema/bootstrap/spring-bootstrap-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:property-placeholder />
<bean id="service" class="org.springframework.bootstrap.config.ExampleService">
<property name="message" value="${foo:spam}" />
</bean>
<bootstrap:context id="bootstrap" expose-features="false">
<beans>
<bean class="org.springframework.bootstrap.feature.EnvironmentFeatureSpecification">
<property name="properties" ref="properties" />
</bean>
<bean id="properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="foo">bar</prop>
</props>
</property>
</bean>
</beans>
</bootstrap:context>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples.spring</groupId>
<artifactId>spring-utility</artifactId>
<version>1.0.0.CI-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Spring Utility</name>
<url>http://www.springframework.org</url>
<description>
<![CDATA[
This project is a minimal jar utility with Spring configuration.
]]>
</description>
<properties>
<maven.test.failure.ignore>true</maven.test.failure.ignore>
<spring.framework.version>3.1.0.M1</spring.framework.version>
</properties>
<profiles>
<profile>
<id>strict</id>
<properties>
<maven.test.failure.ignore>false</maven.test.failure.ignore>
</properties>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<inherited>false</inherited>
<configuration>
<descriptorRefs>
<descriptorRef>project</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!--forkMode>pertest</forkMode -->
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<distributionManagement>
<downloadUrl>http://www.springframework.org/download</downloadUrl>
<site>
<id>staging</id>
<url>file:///${user.dir}/target/staging/org.springframework.batch.archetype/${pom.artifactId}</url>
</site>
<repository>
<id>spring-release</id>
<name>Spring Release Repository</name>
<url>file:///${user.dir}/target/staging/release</url>
</repository>
<snapshotRepository>
<id>spring-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>file:///${user.dir}/target/staging/snapshot</url>
</snapshotRepository>
</distributionManagement>
</project>
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.config;
import org.springframework.bootstrap.feature.ProfileActivationFeatureSpecification;
import org.springframework.context.annotation.Feature;
import org.springframework.context.annotation.FeatureConfiguration;
import org.springframework.context.config.FeatureSpecification;
/**
* @author Dave Syer
*
*/
@FeatureConfiguration
public class ProfileActivation {
private ProfileActivationFeatureSpecification specification = new ProfileActivationFeatureSpecification();
public void setActiveProfiles(String... profiles) {
specification.setActiveProfiles(profiles);
}
@Feature
public FeatureSpecification specification() {
return specification;
}
}
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.feature;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.context.config.SpecificationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert;
/**
* @author Dave Syer
*
*/
public class ProfileActivationFeatureSpecification implements FeatureSpecification {
private String[] profiles;
private boolean condition;
public void setCondition(boolean condition) {
this.condition = condition;
}
public void setActiveProfiles(String... profiles) {
this.profiles = profiles;
}
public boolean validate(ProblemReporter problemReporter) {
return true;
}
public void execute(SpecificationContext specificationContext) {
if (!condition) {
return;
}
Assert.state(specificationContext.getEnvironment() instanceof ConfigurableEnvironment, "Environment is not configurable");
ConfigurableEnvironment environment = (ConfigurableEnvironment) specificationContext.getEnvironment();
Collection<String> list = new LinkedHashSet<String>(Arrays.asList(environment.getActiveProfiles()));
list.addAll(Arrays.asList(profiles));
environment.setActiveProfiles(list.toArray(new String[list.size()]));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:bootstrap="http://www.springframework.org/schema/bootstrap"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/bootstrap http://www.springframework.org/schema/bootstrap/spring-bootstrap-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:property-placeholder />
<!-- You need this to get the EnvironmentConfiguration to load (because it is in a @FeatureSpecification) -->
<context:annotation-config/>
<bean id="service" class="org.springframework.bootstrap.config.ExampleService">
<property name="message" value="${foo:Hello World}"></property>
</bean>
<beans profile="">
<description>The default profile is ""</description>
<bean class="org.springframework.bootstrap.config.EnvironmentConfiguration">
<property name="properties">
<props>
<prop key="foo">bar</prop>
</props>
</property>
</bean>
</beans>
<beans profile="spam">
<bean class="org.springframework.bootstrap.config.EnvironmentConfiguration">
<property name="properties">
<props>
<prop key="foo">spam</prop>
</props>
</property>
</bean>
</beans>
</beans>
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.config;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class ProfileConfigurationTests {
@Autowired
private Service service;
@Test
public void testContextLoader() throws Exception {
assertEquals("bar", service.getMessage());
}
@Test
public void testGenericContext() throws Exception {
GenericXmlApplicationContext context = new GenericXmlApplicationContext(getClass(), getClass().getSimpleName()+"-context.xml");
Service service = context.getBean(Service.class);
assertEquals("bar", service.getMessage());
}
@Test
public void testProfileActivation() throws Exception {
GenericXmlApplicationContext context = new GenericXmlApplicationContext(getClass(), "Activation"+getClass().getSimpleName()+"-context.xml");
Service service = context.getBean(Service.class);
assertEquals("spam", service.getMessage());
}
}
package org.springframework.bootstrap.config;
public interface Service {
String getMessage();
}
<xsd:schema xmlns="http://www.springframework.org/schema/bootstrap" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:beans="http://www.springframework.org/schema/beans" targetNamespace="http://www.springframework.org/schema/bootstrap"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:import namespace="http://www.springframework.org/schema/tool" />
<xsd:element name="environment">
<xsd:annotation>
<xsd:documentation><![CDATA[
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:propsType"/>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="application-identity">
<xsd:annotation>
<xsd:documentation><![CDATA[
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
<xsd:element name="context">
<xsd:annotation>
<xsd:documentation><![CDATA[
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:choice minOccurs="1" maxOccurs="1">
<xsd:element ref="beans:beans" minOccurs="0"/>
<xsd:element ref="beans:bean" minOccurs="0"/>
</xsd:choice>
<xsd:attribute name="id" type="xsd:string">
</xsd:attribute>
<xsd:attribute name="expose-features" type="xsd:boolean" default="true">
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>
http\://www.springframework.org/schema/bootstrap=org.springframework.bootstrap.config.BootstrapNamespaceHandler
http\://www.springframework.org/schema/bootstrap/spring-bootstrap-1.0.xsd=org/springframework/bootstrap/config/spring-bootstrap-1.0.xsd
http\://www.springframework.org/schema/bootstrap/spring-bootstrap.xsd=org/springframework/bootstrap/config/spring-bootstrap-1.0.xsd
# Tooling related information for the bootstrap namespace
http\://www.springframework.org/schema/bootstrap@name=Bootstrap Namespace
http\://www.springframework.org/schema/bootstrap@prefix=bootstrap
http\://www.springframework.org/schema/bootstrap@icon=org/springframework/context/bootstrap/config/spring-bootstrap.gif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment