According to this answer, you can use the Spring Batch class org.springframework.batch.support.SystemPropertyInitializer
to set a System Property during startup of a Spring Context.
In particular, I was hoping to be able to use it to set ENVIRONMENT
because part of Spring Batch config reads:
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/org/springframework/batch/admin/bootstrap/batch.properties</value>
<value>classpath:batch-default.properties</value>
<value>classpath:batch-${ENVIRONMENT:hsql}.properties</value>
</list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="false" />
<property name="order" value="1" />
</bean>
But SystemPropertyInitializer
uses afterPropertiesSet()
to set the System Property, and apparently this happens after the configuration of PropertyPlaceholderConfigurer
.
Is it possible to achieve this?
The easiest solution would be to pass in the environment property as a command-line argument, so it can be resolved as a system property.
If that's not an option you can implement a ApplicationContextInitializer
that promotes environment properties to system properties.
public class EnvironmentPropertyInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
boolean override = false; //change if you prefer envionment over command line args
@Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
for (Entry<String, String> environmentProp : System.getenv().entrySet()) {
String key = environmentProp.getKey();
if (override || System.getProperty(key) == null) {
System.setProperty(key, environmentProp.getValue());
}
}
}
}
Here it looks like you're using Spring Batch Admin, so you can register your initializer with a slight addition to the web.xml
file:
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>org.your.package.EnvironmentPropertyInitializer</param-value>
</context-param>
Adding Background Since a Comment Didn't Seem Sufficient: Here's the relevant classes and the order in which they are called/evaluated.
ApplicationContextInitializer
tells the Spring Application how to load an application context and can be used to set bean profiles, and change other aspects of the context. This gets executed before the context gets completely created.PropertyPlaceholderConfigurer
is a BeanFactoryPostProcessor
and calls postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
. This modifies the BeanFactory
to allow for resolution of properties like ${my.property:some.default}
when setting the properties of a bean as it is created by the BeanFactory
.SystemPropertyInitializer
implements InitializingBean
and calls afterPropertiesSet()
. This method runs after a bean is instantiated and the properties have been set.So you're right that the SystemPropertyInitializer
will not help here since it evaluates after the properties are set on the PropertyPlaceholderConfigurer
. The ApplicationContextInitializer
, however, will be able to promote those environment properties to system properties so they can be interpreted by the XML.
And one more note that I forgot to mention, one of the first declared beans will need to be:
<context:property-placeholder/>
Though it seems redundant, it will allow your PropertyPlaceholderConfigurer
bean to evaluate ${ENVIRONMENT:hsql}
correctly by using the environment properties you just promoted.