
com.baidu.disconf.client.addons.properties.ReloadingPropertyPlaceholderConfigurer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of disconf-client Show documentation
Show all versions of disconf-client Show documentation
https://github.com/knightliao/disconf/tree/master/disconf-client
package com.baidu.disconf.client.addons.properties;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionVisitor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ObjectUtils;
/**
* 具有 reloadable 的 property bean
* 扩展了 DefaultPropertyPlaceholderConfigurer
* 特性如下:
* 1. 启动时 监控 动态config,并维护它们与相应bean的关系
* 2. 当动态config变动时,此configurer会进行reload
* 3. reload 时会 compare config value, and set value for beans
*/
public class ReloadingPropertyPlaceholderConfigurer extends DefaultPropertyPlaceholderConfigurer implements
InitializingBean, DisposableBean, IReloadablePropertiesListener, ApplicationContextAware {
protected static final Logger logger = LoggerFactory.getLogger(ReloadingPropertyPlaceholderConfigurer.class);
// 默认的 property 标识符
private String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
private String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
private String beanName;
private BeanFactory beanFactory;
private Properties[] propertiesArray;
/**
* 对于被标记为动态的,进行 构造 property dependency
* 非动态的,则由原来的spring进行处理
*
* @param strVal
* @param props
* @param visitedPlaceholders
*
* @return
*
* @throws BeanDefinitionStoreException
*/
protected String parseStringValue(String strVal, Properties props, Set visitedPlaceholders)
throws BeanDefinitionStoreException {
DynamicProperty dynamic = null;
// replace reloading prefix and suffix by "normal" prefix and suffix.
// remember all the "dynamic" placeholders encountered.
StringBuffer buf = new StringBuffer(strVal);
int startIndex = strVal.indexOf(this.placeholderPrefix);
while (startIndex != -1) {
int endIndex = buf.toString().indexOf(this.placeholderSuffix, startIndex + this.placeholderPrefix.length());
if (endIndex != -1) {
if (currentBeanName != null && currentPropertyName != null) {
String placeholder = buf.substring(startIndex + this.placeholderPrefix.length(), endIndex);
placeholder = getPlaceholder(placeholder);
if (dynamic == null) {
dynamic = getDynamic(currentBeanName, currentPropertyName, strVal);
}
addDependency(dynamic, placeholder);
} else {
logger.warn("dynamic property outside bean property value - ignored: " + strVal);
}
startIndex = endIndex - this.placeholderPrefix.length() + this.placeholderPrefix.length() +
this.placeholderSuffix.length();
startIndex = strVal.indexOf(this.placeholderPrefix, startIndex);
} else {
startIndex = -1;
}
}
// then, business as usual. no recursive reloading placeholders please.
return super.parseStringValue(buf.toString(), props, visitedPlaceholders);
}
/**
* @param currentBeanName 当前的bean name
* @param currentPropertyName 当前它的属性
* @param orgStrVal 原来的值
*
* @return
*/
private DynamicProperty getDynamic(String currentBeanName, String currentPropertyName, String orgStrVal) {
DynamicProperty dynamic = new DynamicProperty(currentBeanName, currentPropertyName, orgStrVal);
DynamicProperty found = dynamicProperties.get(dynamic);
if (found != null) {
return found;
}
dynamicProperties.put(dynamic, dynamic);
return dynamic;
}
private Properties lastMergedProperties;
/**
* merge property and record last merge
*
* @return
*
* @throws IOException
*/
protected Properties mergeProperties() throws IOException {
Properties properties = super.mergeProperties();
this.lastMergedProperties = properties;
return properties;
}
/**
* 当配置更新时,被调用
*
* @param event
*/
public void propertiesReloaded(PropertiesReloadedEvent event) {
Properties oldProperties = lastMergedProperties;
try {
//
Properties newProperties = mergeProperties();
//
// 获取哪些 dynamic property 被影响
//
Set placeholders = placeholderToDynamics.keySet();
Set allDynamics = new HashSet();
for (String placeholder : placeholders) {
String newValue = newProperties.getProperty(placeholder);
String oldValue = oldProperties.getProperty(placeholder);
if (newValue != null && !newValue.equals(oldValue) || newValue == null && oldValue != null) {
if (logger.isInfoEnabled()) {
logger.info("Property changed detected: " + placeholder +
(newValue != null ? "=" + newValue : " removed"));
}
List affectedDynamics = placeholderToDynamics.get(placeholder);
allDynamics.addAll(affectedDynamics);
}
}
//
// 获取受影响的beans
//
Map> dynamicsByBeanName = new HashMap>();
Map beanByBeanName = new HashMap();
for (DynamicProperty dynamic : allDynamics) {
String beanName = dynamic.getBeanName();
List l = dynamicsByBeanName.get(beanName);
if (l == null) {
dynamicsByBeanName.put(beanName, (l = new ArrayList()));
Object bean = null;
try {
bean = applicationContext.getBean(beanName);
beanByBeanName.put(beanName, bean);
} catch (BeansException e) {
// keep dynamicsByBeanName list, warn only once.
logger.error("Error obtaining bean " + beanName, e);
}
//
// say hello
//
try {
if (bean instanceof IReconfigurationAware) {
((IReconfigurationAware) bean).beforeReconfiguration(); // hello!
}
} catch (Exception e) {
logger.error("Error calling beforeReconfiguration on " + beanName, e);
}
}
l.add(dynamic);
}
//
// 处理受影响的bean
//
Collection beanNames = dynamicsByBeanName.keySet();
for (String beanName : beanNames) {
Object bean = beanByBeanName.get(beanName);
if (bean == null) // problems obtaining bean, earlier
{
continue;
}
BeanWrapper beanWrapper = new BeanWrapperImpl(bean);
// for all affected ...
List dynamics = dynamicsByBeanName.get(beanName);
for (DynamicProperty dynamic : dynamics) {
String propertyName = dynamic.getPropertyName();
String unparsedValue = dynamic.getUnparsedValue();
// obtain an updated value, including dependencies
String newValue;
removeDynamic(dynamic);
currentBeanName = beanName;
currentPropertyName = propertyName;
try {
newValue = parseStringValue(unparsedValue, newProperties, new HashSet());
} finally {
currentBeanName = null;
currentPropertyName = null;
}
if (logger.isInfoEnabled()) {
logger.info("Updating property " + beanName + "." + propertyName + " to " + newValue);
}
// assign it to the bean
try {
beanWrapper.setPropertyValue(propertyName, newValue);
} catch (BeansException e) {
logger.error("Error setting property " + beanName + "." + propertyName + " to " + newValue, e);
}
}
}
//
// say goodbye.
//
for (String beanName : beanNames) {
Object bean = beanByBeanName.get(beanName);
try {
if (bean instanceof IReconfigurationAware) {
((IReconfigurationAware) bean).afterReconfiguration();
}
} catch (Exception e) {
logger.error("Error calling afterReconfiguration on " + beanName, e);
}
}
} catch (IOException e) {
logger.error("Error trying to reload net.unicon.iamlabs.spring.properties.example.net.unicon.iamlabs" +
".spring" + ".properties: " + e.getMessage(), e);
}
}
/**
*
*/
static class DynamicProperty {
final String beanName;
final String propertyName;
final String unparsedValue;
List placeholders = new ArrayList();
/**
* @param beanName
* @param propertyName
* @param unparsedValue
*/
public DynamicProperty(String beanName, String propertyName, String unparsedValue) {
this.beanName = beanName;
this.propertyName = propertyName;
this.unparsedValue = unparsedValue;
}
public void addPlaceholder(String placeholder) {
placeholders.add(placeholder);
}
public String getUnparsedValue() {
return unparsedValue;
}
public String getBeanName() {
return beanName;
}
public String getPropertyName() {
return propertyName;
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final DynamicProperty that = (DynamicProperty) o;
if (beanName != null ? !beanName.equals(that.beanName) : that.beanName != null) {
return false;
}
if (propertyName != null ? !propertyName.equals(that.propertyName) : that.propertyName != null) {
return false;
}
return true;
}
public int hashCode() {
int result;
result = (beanName != null ? beanName.hashCode() : 0);
result = 29 * result + (propertyName != null ? propertyName.hashCode() : 0);
return result;
}
}
private Map dynamicProperties = new HashMap();
private Map> placeholderToDynamics = new HashMap>();
/**
* 建立 placeholder 与 dynamic 的对应关系
*
* @param dynamic
* @param placeholder
*/
private void addDependency(DynamicProperty dynamic, String placeholder) {
List l = placeholderToDynamics.get(placeholder);
if (l == null) {
l = new ArrayList();
placeholderToDynamics.put(placeholder, l);
}
if (!l.contains(dynamic)) {
l.add(dynamic);
}
dynamic.addPlaceholder(placeholder);
}
/**
* 删除 placeholder 与 dynamic 的对应关系
*
* @param dynamic
*/
private void removeDynamic(DynamicProperty dynamic) {
List placeholders = dynamic.placeholders;
for (String placeholder : placeholders) {
List l = placeholderToDynamics.get(placeholder);
l.remove(dynamic);
}
dynamic.placeholders.clear();
dynamicProperties.remove(dynamic);
}
private String currentBeanName;
private String currentPropertyName;
/**
* copy & paste, just so we can insert our own visitor.
* 启动时 进行配置的解析
*/
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
BeanDefinitionVisitor visitor =
new ReloadingPropertyPlaceholderConfigurer.PlaceholderResolvingBeanDefinitionVisitor(props);
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (int i = 0; i < beanNames.length; i++) {
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in net.unicon.iamlabs.spring.properties.example.net
// .unicon.iamlabs.spring.properties file locations.
if (!(beanNames[i].equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
this.currentBeanName = beanNames[i];
try {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(beanNames[i]);
try {
visitor.visitBeanDefinition(bd);
} catch (BeanDefinitionStoreException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanNames[i],
ex.getMessage());
}
} finally {
currentBeanName = null;
}
}
}
}
/**
* afterPropertiesSet
* 将自己 添加 property listener
*/
public void afterPropertiesSet() {
for (Properties properties : propertiesArray) {
if (properties instanceof ReloadableProperties) {
logger.debug("add property listener: " + properties.toString());
((ReloadableProperties) properties).addReloadablePropertiesListener(this);
}
}
}
/**
* destroy
* 删除 property listener
*
* @throws Exception
*/
public void destroy() throws Exception {
for (Properties properties : propertiesArray) {
if (properties instanceof ReloadableProperties) {
logger.debug("remove property listener: " + properties.toString());
((ReloadableProperties) properties).removeReloadablePropertiesListener(this);
}
}
}
/**
* 替换掉spring的 config resolver,这样我们才可以解析掉自己的config
*/
private class PlaceholderResolvingBeanDefinitionVisitor extends BeanDefinitionVisitor {
private final Properties props;
public PlaceholderResolvingBeanDefinitionVisitor(Properties props) {
this.props = props;
}
protected void visitPropertyValues(MutablePropertyValues pvs) {
PropertyValue[] pvArray = pvs.getPropertyValues();
for (PropertyValue pv : pvArray) {
currentPropertyName = pv.getName();
try {
Object newVal = resolveValue(pv.getValue());
if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
pvs.addPropertyValue(pv.getName(), newVal);
}
} finally {
currentPropertyName = null;
}
}
}
protected String resolveStringValue(String strVal) throws BeansException {
return parseStringValue(strVal, this.props, new HashSet());
}
}
/**
* the application context is needed to find the beans again during reconfiguration
*/
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setProperties(Properties properties) {
setPropertiesArray(new Properties[] {properties});
}
public void setPropertiesArray(Properties[] propertiesArray) {
this.propertiesArray = propertiesArray;
super.setPropertiesArray(propertiesArray);
}
public void setPlaceholderPrefix(String placeholderPrefix) {
this.placeholderPrefix = placeholderPrefix;
super.setPlaceholderPrefix(placeholderPrefix);
}
public void setPlaceholderSuffix(String placeholderSuffix) {
this.placeholderSuffix = placeholderSuffix;
super.setPlaceholderSuffix(placeholderPrefix);
}
public void setBeanName(String beanName) {
this.beanName = beanName;
super.setBeanName(beanName);
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
super.setBeanFactory(beanFactory);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy