From 8f2b24e77e991f951b3fcb126fb368d2a4f068c9 Mon Sep 17 00:00:00 2001 From: colin-lee <39513055@qq.com> Date: Mon, 5 Oct 2015 17:25:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96config-web=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../src/main/resources/application.properties | 6 +- .../src/main/resources/applicationContext.xml | 21 ++-- .../ReloadablePropertyPostProcessor.java | 38 +++--- ...ePropertySourcesPlaceholderConfigurer.java | 109 ++++++++---------- .../autoconf/spring/ReloadableTest.java | 26 ++--- .../src/test/resources/applicationContext.xml | 27 ++--- .../resources/autoconf/application.properties | 7 -- .../src/test/resources/logback-test.xml | 2 +- 9 files changed, 100 insertions(+), 137 deletions(-) delete mode 100644 spring-support/src/test/resources/autoconf/application.properties diff --git a/.gitignore b/.gitignore index 23dc8b8..2c41792 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +rebel.xml .svn .git .idea diff --git a/config-web/src/main/resources/application.properties b/config-web/src/main/resources/application.properties index f104c03..8ba68dc 100644 --- a/config-web/src/main/resources/application.properties +++ b/config-web/src/main/resources/application.properties @@ -1,3 +1,3 @@ -mysql.url= -mysql.user=autoconf -mysql.password=!Love4Config~ +mysql.url=jdbc:mysql://127.0.0.1:3306/autoconf?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true +mysql.user=root +mysql.password= diff --git a/config-web/src/main/resources/applicationContext.xml b/config-web/src/main/resources/applicationContext.xml index 548744a..aed0ba5 100644 --- a/config-web/src/main/resources/applicationContext.xml +++ b/config-web/src/main/resources/applicationContext.xml @@ -1,5 +1,7 @@ - - - + - - - - - + class="com.github.autoconf.spring.reloadable.ReloadablePropertySourcesPlaceholderConfigurer" + p:ignoreResourceNotFound="true" + p:ignoreUnresolvablePlaceholders="false" + p:configName="" + p:location="application.properties"> + + log4j.properties + diff --git a/spring-support/src/main/java/com/github/autoconf/spring/reloadable/ReloadablePropertyPostProcessor.java b/spring-support/src/main/java/com/github/autoconf/spring/reloadable/ReloadablePropertyPostProcessor.java index 2b44680..6dc101a 100644 --- a/spring-support/src/main/java/com/github/autoconf/spring/reloadable/ReloadablePropertyPostProcessor.java +++ b/spring-support/src/main/java/com/github/autoconf/spring/reloadable/ReloadablePropertyPostProcessor.java @@ -43,10 +43,10 @@ */ @Component public class ReloadablePropertyPostProcessor extends InstantiationAwareBeanPostProcessorAdapter { + private static final Logger LOG = LoggerFactory.getLogger(ReloadablePropertyPostProcessor.class); private final PropertyChangedEventNotifier eventNotifier; private final PropertyConversionService propertyConversionService; private final ReloadablePropertySourcesPlaceholderConfigurer placeholderConfigurer; - private Logger log = LoggerFactory.getLogger(getClass()); private Multimap beanPropertySubscriptions = HashMultimap.create(); @Autowired @@ -59,7 +59,7 @@ public ReloadablePropertyPostProcessor(final ReloadablePropertySourcesPlaceholde @PostConstruct protected void init() { - log.info("Registering ReloadablePropertyProcessor for properties file changes"); + LOG.info("Registering ReloadablePropertyProcessor for properties file changes"); registerPropertyReloader(); } @@ -67,7 +67,7 @@ protected void init() { * Utility method to unregister the class from receiving events about property files being changed. */ public final void unregisterPropertyReloader() { - log.info("Unregistering ReloadablePropertyProcessor from property file changes"); + LOG.info("Unregistering ReloadablePropertyProcessor from property file changes"); this.eventNotifier.unregister(this); } @@ -88,9 +88,9 @@ public final void registerPropertyReloader() { */ @Subscribe public void handlePropertyChange(final PropertyModifiedEvent event) { - Collection holders = - this.beanPropertySubscriptions.get(event.getPropertyName()); - for (final BeanPropertyHolder bean : holders) { + Collection c = beanPropertySubscriptions.get(event.getPropertyName()); + System.err.println("====================" + event + ", holders:" + c); + for (final BeanPropertyHolder bean : c) { updateField(bean, event); } } @@ -103,29 +103,28 @@ public void updateField(final BeanPropertyHolder holder, final PropertyModifiedE final Object convertedProperty = convertPlaceHolderForField(fieldToUpdate, rawValue); try { - log.info("Reloading property [{}] on field [{}] for class [{}]", event.getPropertyName(), fieldToUpdate.getName(), canonicalName); + LOG.info("Reloading property [{}] on field [{}] for class [{}]", event.getPropertyName(), fieldToUpdate.getName(), canonicalName); fieldToUpdate.set(beanToUpdate, convertedProperty); } catch (final IllegalAccessException e) { - log.error("Unable to reloading property [{}] on field [{}] for class [{}]\n Exception [{}]", event.getPropertyName(), fieldToUpdate.getName(), canonicalName, e.getMessage()); + LOG.error("Unable to reloading property [{}] on field [{}] for class [{}]\n Exception [{}]", event.getPropertyName(), fieldToUpdate.getName(), canonicalName, e.getMessage()); } } @Override public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException { - if (log.isDebugEnabled()) { - log.debug("Setting Reloadable Properties on [{}]", beanName); + if (LOG.isDebugEnabled()) { + LOG.debug("Setting Reloadable Properties on [{}]", beanName); } setPropertiesOnBean(bean); - Collection dynamicProperties = - placeholderConfigurer.getPlaceHolders().get(beanName); - for (DynamicProperty property : dynamicProperties) { + for (DynamicProperty property : placeholderConfigurer.getPlaceHolders().get(beanName)) { String name = property.getPropertyName(); Field field = ReflectionUtils.findField(bean.getClass(), name); if (field != null) { ReflectionUtils.makeAccessible(field); validateFieldNotFinal(bean, field); for (String holder : property.getPlaceholders()) { - subscribeBeanToPropertyChangedEvent(holder, new BeanPropertyHolder(bean, field, property.getRawValue())); + BeanPropertyHolder bp = new BeanPropertyHolder(bean, field, property.getRawValue()); + subscribeBeanToPropertyChangedEvent(holder, bp); } } else { String methodName = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1); @@ -138,13 +137,13 @@ public boolean postProcessAfterInstantiation(final Object bean, final String bea ReflectionUtils.makeAccessible(i); i.invoke(bean, value); } catch (Exception e) { - log.error("cannot invoke {}.{}({})", bean.getClass(), methodName, value); + LOG.error("cannot invoke {}.{}({})", bean.getClass(), methodName, value); } break; } } if (!found) { - log.error("cannot find {} in class: {}", methodName, bean.getClass()); + LOG.error("cannot find {} in class: {}", methodName, bean.getClass()); } } } @@ -164,17 +163,17 @@ public void doWith(final Field field) throws IllegalArgumentException, IllegalAc validatePropertyAvailableOrDefaultSet(bean, field, annotation, property); if (null != property) { - log.info("Attempting to convert and set property [{}] on field [{}] for class [{}] to type [{}]", property, field.getName(), bean.getClass().getCanonicalName(), field.getType()); + LOG.info("Attempting to convert and set property [{}] on field [{}] for class [{}] to type [{}]", property, field.getName(), bean.getClass().getCanonicalName(), field.getType()); final Object convertedProperty = convertPropertyForField(field, annotation.value()); - log.info("Setting field [{}] of class [{}] with value [{}]", field.getName(), bean.getClass().getCanonicalName(), convertedProperty); + LOG.info("Setting field [{}] of class [{}] with value [{}]", field.getName(), bean.getClass().getCanonicalName(), convertedProperty); field.set(bean, convertedProperty); subscribeBeanToPropertyChangedEvent(annotation.value(), new BeanPropertyHolder(bean, field, annotation.value())); } else { - log.info("Leaving field [{}] of class [{}] with default value", field.getName(), bean.getClass().getCanonicalName()); + LOG.info("Leaving field [{}] of class [{}] with default value", field.getName(), bean.getClass().getCanonicalName()); } } } @@ -204,6 +203,7 @@ private boolean fieldDoesNotHaveDefault(final Field field, final Object value) t } private void subscribeBeanToPropertyChangedEvent(final String property, final BeanPropertyHolder fieldProperty) { + System.err.println("-------------property:" + property + ", bp: " + fieldProperty); this.beanPropertySubscriptions.put(property, fieldProperty); } diff --git a/spring-support/src/main/java/com/github/autoconf/spring/reloadable/ReloadablePropertySourcesPlaceholderConfigurer.java b/spring-support/src/main/java/com/github/autoconf/spring/reloadable/ReloadablePropertySourcesPlaceholderConfigurer.java index 4d17009..95fc4b4 100644 --- a/spring-support/src/main/java/com/github/autoconf/spring/reloadable/ReloadablePropertySourcesPlaceholderConfigurer.java +++ b/spring-support/src/main/java/com/github/autoconf/spring/reloadable/ReloadablePropertySourcesPlaceholderConfigurer.java @@ -30,6 +30,7 @@ import org.springframework.util.StringValueResolver; import java.io.IOException; +import java.lang.reflect.Field; import java.nio.file.Path; import java.util.List; import java.util.Properties; @@ -41,21 +42,16 @@ * @author James Morgan */ public class ReloadablePropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer implements EventPublisher { - private Logger log = LoggerFactory.getLogger(getClass()); + private static final Logger LOG = + LoggerFactory.getLogger(ReloadablePropertySourcesPlaceholderConfigurer.class); private PropertyChangedEventNotifier eventNotifier; private Multimap placeHolders = HashMultimap.create(); private PropertySourcesPropertyResolver propertyResolver; private IConfigFactory configFactory; - private Resource[] locations; /** * 在cms系统中配置的名称,可以是逗号分隔的多个名字 */ private String configName; - /** - * the application context is needed to find the beans again during reconfiguration - */ - private BeanFactory beanFactory; - private String beanName; public ReloadablePropertySourcesPlaceholderConfigurer() { } @@ -82,7 +78,6 @@ protected void loadProperties(final Properties props) throws IOException { IChangeableConfig config = configFactory.getConfig(configName, new IChangeListener() { @Override public void changed(IConfig config) { - log.info("CMS: {} changed", config.getName()); reloadCmsConfig(config); } }, false); @@ -91,9 +86,9 @@ public void changed(IConfig config) { } private void reloadCmsConfig(IConfig config) { - final Properties reloadedProperties = new Properties(); - reloadedProperties.putAll(config.getAll()); - updateProperty(reloadedProperties); + final Properties p = new Properties(); + p.putAll(config.getAll()); + updateProperty(p); } private void updateProperty(Properties reloadedProperties) { @@ -133,18 +128,13 @@ private void updateProperty(Properties reloadedProperties) { } } - @Override - public void setLocations(final Resource[] locations) { - super.setLocations(locations); - this.locations = locations; - } - @Override public void onResourceChanged(final Resource resource) { try { + LOG.info("{} changed", resource); updateProperty(PropertiesLoaderUtils.loadProperties(resource)); } catch (final IOException e) { - log.error("Failed to reload properties file once change", e); + LOG.error("Failed to reload properties file once change", e); } } @@ -152,25 +142,44 @@ public void startWatching() { if (null == this.eventNotifier) { throw new BeanInitializationException("Event bus not setup, you should not be calling this method...!"); } - if (this.locations != null) { + Resource[] resources = (Resource[]) getFieldValue("locations"); + if (resources == null) { + return; + } + try { + // Here we actually create and set a FileWatcher to monitor the given locations + for (Resource i : resources) { + final LocalConfig c = new LocalConfig(i.getFilename(), i.getFile().toPath()); + FileUpdateWatcher.getInstance().watch(c.getPath(), new IFileListener() { + @Override + public void changed(Path path, byte[] content) { + LOG.info("{} changed", path); + c.copyOf(content); + reloadCmsConfig(c); + } + }); + } + } catch (Exception e) { + LOG.error("Unable to start properties file watcher", e); + } + } + + private Object getFieldValue(String name) { + Class clz = super.getClass(); + while (clz != Object.class) { try { - // Here we actually create and set a FileWatcher to monitor the given locations - for (Resource i : locations) { - final LocalConfig c = new LocalConfig(i.getFilename(), i.getFile().toPath()); - FileUpdateWatcher.getInstance().watch(c.getPath(), new IFileListener() { - @Override - public void changed(Path path, byte[] content) { - log.info("{} changed", path); - c.copyOf(content); - reloadCmsConfig(c); - } - }); - reloadCmsConfig(c); + Field f = clz.getDeclaredField(name); + if (!f.isAccessible()) { + f.setAccessible(true); } - } catch (Exception e) { - log.error("Unable to start properties file watcher", e); + return f.get(this); + } catch (NoSuchFieldException ignored) { + clz = clz.getSuperclass(); + } catch (IllegalAccessException e) { + LOG.error("cannot getFieldValue('{}')", name, e); } } + return null; } public PropertySourcesPropertyResolver getPropertyResolver() { @@ -188,36 +197,6 @@ public Object resolveProperty(final Object property) { return getPropertyResolver().getProperty(property.toString()); } - /** - * Only necessary to check that we're not parsing our own bean definition, - * to avoid failing on unresolvable placeholders in properties file locations. - * The latter case can happen with placeholders for system properties in - * resource locations. - * - * @see #setLocations - * @see org.springframework.core.io.ResourceEditor - */ - @Override - public void setBeanName(String beanName) { - super.setBeanName(beanName); - this.beanName = beanName; - } - - /** - * Only necessary to check that we're not parsing our own bean definition, - * to avoid failing on unresolvable placeholders in properties file locations. - * The latter case can happen with placeholders for system properties in - * resource locations. - * - * @see #setLocations - * @see org.springframework.core.io.ResourceEditor - */ - @Override - public void setBeanFactory(BeanFactory beanFactory) { - super.setBeanFactory(beanFactory); - this.beanFactory = beanFactory; - } - private PropertySourcesPropertyResolver buildPropertyResolver(PropertySources sources) { PropertySourcesPropertyResolver propertyResolver = new PropertySourcesPropertyResolver(sources); propertyResolver.setPlaceholderPrefix(this.placeholderPrefix); @@ -229,10 +208,12 @@ private PropertySourcesPropertyResolver buildPropertyResolver(PropertySources so protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) { BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver); String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames(); + String selfName = (String) getFieldValue("beanName"); + BeanFactory selfFactory = (BeanFactory) getFieldValue("beanFactory"); for (String curName : beanNames) { // Check that we're not parsing our own bean definition, // to avoid failing on unresolvable placeholders in properties file locations. - if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) { + if (!(curName.equals(selfName) && beanFactoryToProcess.equals(selfFactory))) { BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName); try { findDynamicProperties(curName, bd); diff --git a/spring-support/src/test/java/com/github/autoconf/spring/ReloadableTest.java b/spring-support/src/test/java/com/github/autoconf/spring/ReloadableTest.java index a11ee0b..557f3d6 100644 --- a/spring-support/src/test/java/com/github/autoconf/spring/ReloadableTest.java +++ b/spring-support/src/test/java/com/github/autoconf/spring/ReloadableTest.java @@ -1,11 +1,8 @@ package com.github.autoconf.spring; import com.github.autoconf.base.Config; -import com.google.common.io.Closeables; +import com.github.autoconf.spring.reloadable.ReloadableProperty; import com.google.common.io.Files; -import org.apache.curator.test.TestingServer; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -30,23 +27,12 @@ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) public class ReloadableTest { - private static TestingServer server; + @ReloadableProperty("log4j.appender.Console") + private String appender; @Autowired private Article article; - @BeforeClass - public static void beforeClass() throws Exception { - server = new TestingServer(); - //设置环境变量,覆盖application.properties配置 - System.setProperty("zookeeper.servers", server.getConnectString()); - } - - @AfterClass - public static void afterClass() throws Exception { - Closeables.close(server, true); - } - private void setVariable(String var) throws Exception { File app = ResourceUtils.getFile("classpath:autoconf/app.properties"); List lines = Files.readLines(app, Config.UTF8); @@ -63,15 +49,17 @@ private void setVariable(String var) throws Exception { @Test public void testPlaceHolder() throws Exception { + assertThat(appender, is("org.apache.log4j.ConsoleAppender")); + setVariable("colinli"); - Thread.sleep(1000); + Thread.sleep(3000); assertThat(article.getAuthor(), is("colinli")); assertThat(article.getContent(), is("hello colinli, welcome")); assertThat(article.getTitle(), is("config title")); assertThat(article.getDynamic(), is("dynamicContent")); setVariable("lirui"); - Thread.sleep(10000); + Thread.sleep(3000); assertThat(article.getAuthor(), is("lirui")); assertThat(article.getContent(), is("hello lirui, welcome")); diff --git a/spring-support/src/test/resources/applicationContext.xml b/spring-support/src/test/resources/applicationContext.xml index a56cad9..b6d0b2d 100644 --- a/spring-support/src/test/resources/applicationContext.xml +++ b/spring-support/src/test/resources/applicationContext.xml @@ -1,23 +1,20 @@ - + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> - - - + - - - - - - - - + diff --git a/spring-support/src/test/resources/autoconf/application.properties b/spring-support/src/test/resources/autoconf/application.properties deleted file mode 100644 index 31397de..0000000 --- a/spring-support/src/test/resources/autoconf/application.properties +++ /dev/null @@ -1,7 +0,0 @@ -zookeeper.servers=127.0.0.1:2181,127.0.0.1:2181 -zookeeper.authenticationType=digest -zookeeper.authentication=MyPassword -zookeeper.basePath=/cms/config -process.name=spring-reloadable -process.profile=dev - diff --git a/spring-support/src/test/resources/logback-test.xml b/spring-support/src/test/resources/logback-test.xml index 51d9502..ed48519 100644 --- a/spring-support/src/test/resources/logback-test.xml +++ b/spring-support/src/test/resources/logback-test.xml @@ -9,7 +9,7 @@ - +