In Log4j, there is a feature wherein the system can be initialized to do a configure and watch with an interval. This allows for the log4j system to reload its properties whenever the property file is changed. Does the spring framework have such a Configuration Observer facility wherein the Configuration is reloaded when it changed. The Configuration that needs reloading is not the Springs's applicationContext.xml but various other configuration files that are initialized using the Spring initialization beans.
Spring – Automatic configuration reinitialization in Spring
spring
Related Solutions
You're going to have a boostrap problem since log4j has to be initialized before Spring. Whether you're using a custom configuration or Log4j's standard initializer, it has to be up before application context is.
Now, you could in theory make your custom appenders "lazily" initialize themselves (either via approach you've suggested above or by making appenders themselves "semi" singletons - e.g. appender class has a static instance field which gets populated by afterPropertiesSet()
method; that way you can create appender itself as bean within Spring) but it seems somewhat messy and inconsistent.
Another approach is to dynamically reconfigure Log4j once Spring context is initialized; e.g. write a listener to catch a ContextStartedEvent
, obtain all beans of type Appender
from the context and add them to Log4j configuration. This will also allow you to create your appenders as beans but avoid singleton mess somewhat.
Ok. I solved it. The problem is both of my PropertyPlaceholders are BeanFactoryPostProcessor those get processed after the context is loaded but the properties are set after. So it is impossible to populate one PropertyPlaceholder with another.
Here is my solution in code ;-)
package property.util;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Properties;
/**
* ConfigurablePropertyPlaceholder takes instructions which SystemProperty
* contains the path to the propertyfile to load.
*
* @author Gabe Kaelin
*
*/
public class ConfigurablePropertyPlaceholder extends PropertyPlaceholderConfigurer {
private String propertyLocationSystemProperty;
private String defaultPropertyFileName;
public String getPropertyLocationSystemProperty() {
return propertyLocationSystemProperty;
}
public void setPropertyLocationSystemProperty(String propertyLocationSystemProperty) {
this.propertyLocationSystemProperty = propertyLocationSystemProperty;
}
public String getDefaultPropertyFileName() {
return defaultPropertyFileName;
}
public void setDefaultPropertyFileName(String defaultPropertyFileName) {
this.defaultPropertyFileName = defaultPropertyFileName;
}
/**
* Overridden to fill the location with the path from the {@link #propertyLocationSystemProperty}
*
* @param props propeties instance to fill
* @throws IOException
*/
@Override
protected void loadProperties(Properties props) throws IOException {
Resource location = null;
if(StringUtils.isNotEmpty(propertyLocationSystemProperty)){
String propertyFilePath = System.getProperties().getProperty(propertyLocationSystemProperty);
StringBuilder pathBuilder = new StringBuilder(propertyFilePath);
if(StringUtils.isNotEmpty(defaultPropertyFileName) && !propertyFilePath.endsWith(defaultPropertyFileName)){
pathBuilder.append("/").append(defaultPropertyFileName);
}
location = new FileSystemResource(pathBuilder.toString());
}
setLocation(location);
super.loadProperties(props);
}
}
The according applicationContext.xml entry
<bean id="propertyConfigurer" class="property.util.ConfigurablePropertyPlaceholder">
<property name="propertyLocationSystemProperty" value="propertyPath" />
<property name="defaultPropertyFileName" value="server.properties" />
<property name="ignoreResourceNotFound" value="false"/>
</bean>
the java process can be started with
java -DpropertyPath=/path/to/properties
and it loads the properties and they are available in the applicationContext.xml
Best Answer
I found a utility that does something similar to Log4J here. It's basically an extension to PropertyPlaceholderConfigurer that reloads properties when they change.