diff --git a/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/AbstractConfigurationBridge.java b/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/AbstractConfigurationBridge.java index f8304fa30..db6bb99aa 100644 --- a/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/AbstractConfigurationBridge.java +++ b/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/AbstractConfigurationBridge.java @@ -16,19 +16,23 @@ import com.netflix.archaius.Config; import com.netflix.archaius.commons.CommonsToConfig; import com.netflix.archaius.config.CompositeConfig; +import com.netflix.archaius.config.DefaultConfigListener; import com.netflix.archaius.config.SettableConfig; +import com.netflix.archaius.exceptions.ConfigAlreadyExistsException; import com.netflix.archaius.exceptions.ConfigException; import com.netflix.archaius.inject.LibrariesLayer; import com.netflix.archaius.inject.RuntimeLayer; import com.netflix.config.AggregatedConfiguration; import com.netflix.config.DeploymentContext; +import com.netflix.config.DynamicPropertySupport; +import com.netflix.config.PropertyListener; /** * @see StaticArchaiusBridgeModule * @author elandau */ @Singleton -class AbstractConfigurationBridge extends AbstractConfiguration implements AggregatedConfiguration { +class AbstractConfigurationBridge extends AbstractConfiguration implements AggregatedConfiguration, DynamicPropertySupport { private final Config config; private final SettableConfig settable; @@ -37,7 +41,7 @@ class AbstractConfigurationBridge extends AbstractConfiguration implements Aggre @Inject public AbstractConfigurationBridge( - Config config, + final Config config, @LibrariesLayer CompositeConfig libraries, @RuntimeLayer SettableConfig settable, DeploymentContext context) { @@ -80,7 +84,11 @@ public void addConfiguration(AbstractConfiguration config) { public void addConfiguration(AbstractConfiguration config, String name) { try { libraries.addConfig(name, new CommonsToConfig(config)); - } catch (ConfigException e) { + } + catch (ConfigAlreadyExistsException e) { + // OK To ignore + } + catch (ConfigException e) { throw new RuntimeException("Unable to add configuration " + name, e); } } @@ -130,4 +138,24 @@ public boolean removeConfiguration(Configuration config) { public Configuration removeConfigurationAt(int index) { throw new UnsupportedOperationException(); } + + @Override + public void addConfigurationListener(final PropertyListener expandedPropertyListener) { + config.addListener(new DefaultConfigListener() { + @Override + public void onConfigAdded(Config config) { + expandedPropertyListener.configSourceLoaded(config); + } + + @Override + public void onConfigRemoved(Config config) { + expandedPropertyListener.configSourceLoaded(config); + } + + @Override + public void onConfigUpdated(Config config) { + expandedPropertyListener.configSourceLoaded(config); + } + }); + } } diff --git a/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/StaticAbstractConfiguration.java b/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/StaticAbstractConfiguration.java index 144584119..0fd9eb564 100644 --- a/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/StaticAbstractConfiguration.java +++ b/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/StaticAbstractConfiguration.java @@ -11,12 +11,14 @@ import com.netflix.config.AggregatedConfiguration; import com.netflix.config.ConfigurationManager; +import com.netflix.config.DynamicPropertySupport; +import com.netflix.config.PropertyListener; /** * @see StaticArchaiusBridgeModule * @author elandau */ -public class StaticAbstractConfiguration extends AbstractConfiguration implements AggregatedConfiguration { +public class StaticAbstractConfiguration extends AbstractConfiguration implements AggregatedConfiguration, DynamicPropertySupport { private static volatile AbstractConfigurationBridge delegate; @Inject @@ -109,4 +111,8 @@ protected void addPropertyDirect(String key, Object value) { delegate.addPropertyDirect(key, value); } + @Override + public void addConfigurationListener(PropertyListener expandedPropertyListener) { + delegate.addConfigurationListener(expandedPropertyListener); + } } diff --git a/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/StaticArchaiusBridgeModule.java b/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/StaticArchaiusBridgeModule.java index a5055a3d1..e8406426e 100644 --- a/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/StaticArchaiusBridgeModule.java +++ b/archaius2-archaius1-bridge/src/main/java/com/netflix/archaius/bridge/StaticArchaiusBridgeModule.java @@ -35,8 +35,6 @@ public class StaticArchaiusBridgeModule extends AbstractModule { protected void configure() { requestStaticInjection(StaticAbstractConfiguration.class); requestStaticInjection(StaticDeploymentContext.class); - bind(DeploymentContext.class).to(ConfigBasedDeploymentContext.class); - bind(AbstractConfigurationBridge.class).asEagerSingleton(); } } diff --git a/archaius2-archaius1-bridge/src/test/java/com/netflix/archaius/bridge/DynamicPropertyTest.java b/archaius2-archaius1-bridge/src/test/java/com/netflix/archaius/bridge/DynamicPropertyTest.java new file mode 100644 index 000000000..3140b5c9d --- /dev/null +++ b/archaius2-archaius1-bridge/src/test/java/com/netflix/archaius/bridge/DynamicPropertyTest.java @@ -0,0 +1,35 @@ +package com.netflix.archaius.bridge; + +import org.junit.Assert; +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.netflix.archaius.Property; +import com.netflix.archaius.PropertyFactory; +import com.netflix.archaius.config.SettableConfig; +import com.netflix.archaius.guice.ArchaiusModule; +import com.netflix.archaius.inject.RuntimeLayer; +import com.netflix.config.DynamicPropertyFactory; +import com.netflix.config.DynamicStringProperty; + +public class DynamicPropertyTest { + @Test + public void test() { + Injector injector = Guice.createInjector(new ArchaiusModule(), new StaticArchaiusBridgeModule()); + + Property prop2 = injector.getInstance(PropertyFactory.class).getProperty("foo").asString("default"); + + DynamicStringProperty prop = DynamicPropertyFactory.getInstance().getStringProperty("foo", "default"); + Assert.assertEquals("default", prop.get()); + Assert.assertEquals("default", prop2.get()); + + SettableConfig config = injector.getInstance(Key.get(SettableConfig.class, RuntimeLayer.class)); + config.setProperty("foo", "newvalue"); + + Assert.assertEquals("newvalue", prop.get()); + Assert.assertEquals("newvalue", prop2.get()); + + } +} diff --git a/archaius2-core/src/main/java/com/netflix/archaius/DefaultConfigLoader.java b/archaius2-core/src/main/java/com/netflix/archaius/DefaultConfigLoader.java index 7412b6727..699b4de86 100644 --- a/archaius2-core/src/main/java/com/netflix/archaius/DefaultConfigLoader.java +++ b/archaius2-core/src/main/java/com/netflix/archaius/DefaultConfigLoader.java @@ -19,6 +19,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Properties; @@ -172,15 +173,18 @@ public LinkedHashMap load(String resourceName) throws ConfigExce configs.put(resourceName, new MapConfig(overrides)); } - for (String permutationName : strategy.generate(resourceName, interpolator, lookup)) { - LOG.info("Attempting to load {}", permutationName); - for (ConfigReader loader : loaders) { - if (loader.canLoad(classLoader, permutationName)) { + List names = strategy.generate(resourceName, interpolator, lookup); + for (String name : names) { + LOG.info("Attempting to load {}", name); + for (ConfigReader reader : loaders) { + if (reader.canLoad(classLoader, name)) { try { - configs.put(permutationName, loader.load(classLoader, permutationName)); + configs.put(name, reader.load(classLoader, name)); + LOG.info("Loaded {} ", name); failIfNotLoaded = false; } catch (ConfigException e) { + LOG.info("Unable to load {}, {}", name, e.getMessage()); if (failIfNotLoaded == true) { throw new ConfigException("Failed to load configuration resource '" + resourceName + "'"); } @@ -192,15 +196,19 @@ public LinkedHashMap load(String resourceName) throws ConfigExce return configs; } - + @Override public Config load(URL url) { for (ConfigReader loader : loaders) { if (loader.canLoad(classLoader, url)) { try { - return loader.load(classLoader, url); + Config config = loader.load(classLoader, url); + LOG.info("Loaded " + url); + return config; } catch (ConfigException e) { - LOG.debug("Unable to load file '{}'", new Object[]{url, e.getMessage()}); + LOG.info("Unable to load file '{}'", new Object[]{url, e.getMessage()}); + } catch (Exception e) { + LOG.info("Unable to load file '{}'", new Object[]{url, e.getMessage()}); } } } diff --git a/archaius2-core/src/main/java/com/netflix/archaius/config/CompositeConfig.java b/archaius2-core/src/main/java/com/netflix/archaius/config/CompositeConfig.java index da8aea70d..75b2f7c29 100644 --- a/archaius2-core/src/main/java/com/netflix/archaius/config/CompositeConfig.java +++ b/archaius2-core/src/main/java/com/netflix/archaius/config/CompositeConfig.java @@ -30,6 +30,7 @@ import com.netflix.archaius.Config; import com.netflix.archaius.ConfigListener; +import com.netflix.archaius.exceptions.ConfigAlreadyExistsException; import com.netflix.archaius.exceptions.ConfigException; /** @@ -138,7 +139,7 @@ private synchronized void internalAddConfig(String name, Config child) throws Co } if (lookup.containsKey(name)) { - throw new ConfigException(String.format("Configuration with name '%s' already exists", name)); + throw new ConfigAlreadyExistsException(String.format("Configuration with name '%s' already exists", name)); } lookup.put(name, child); @@ -152,9 +153,9 @@ private synchronized void internalAddConfig(String name, Config child) throws Co postConfigAdded(child); } - public void addConfigs(LinkedHashMap configs) throws ConfigException { + public synchronized void addConfigs(LinkedHashMap configs) throws ConfigException { for (Entry entry : configs.entrySet()) { - addConfig(entry.getKey(), entry.getValue()); + internalAddConfig(entry.getKey(), entry.getValue()); } } diff --git a/archaius2-core/src/main/java/com/netflix/archaius/exceptions/ConfigAlreadyExistsException.java b/archaius2-core/src/main/java/com/netflix/archaius/exceptions/ConfigAlreadyExistsException.java new file mode 100644 index 000000000..a4a1f476e --- /dev/null +++ b/archaius2-core/src/main/java/com/netflix/archaius/exceptions/ConfigAlreadyExistsException.java @@ -0,0 +1,8 @@ +package com.netflix.archaius.exceptions; + +public class ConfigAlreadyExistsException extends ConfigException { + public ConfigAlreadyExistsException(String message) { + super(message); + } + +} diff --git a/archaius2-core/src/test/resources/log4j.properties b/archaius2-core/src/test/resources/log4j.properties index c701923a4..4ab7dc141 100644 --- a/archaius2-core/src/test/resources/log4j.properties +++ b/archaius2-core/src/test/resources/log4j.properties @@ -17,6 +17,7 @@ log4j.rootLogger=ERROR, console log4j.logger.com.netflix.archaius=DEBUG, console +log4j.additivity.com.netflix.archaius=false log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout diff --git a/archaius2-guice/src/main/java/com/netflix/archaius/guice/ArchaiusModule.java b/archaius2-guice/src/main/java/com/netflix/archaius/guice/ArchaiusModule.java index 77701025b..156d80f2e 100644 --- a/archaius2-guice/src/main/java/com/netflix/archaius/guice/ArchaiusModule.java +++ b/archaius2-guice/src/main/java/com/netflix/archaius/guice/ArchaiusModule.java @@ -16,14 +16,16 @@ package com.netflix.archaius.guice; import java.util.LinkedHashMap; +import java.util.Map.Entry; import java.util.Set; import javax.inject.Inject; import javax.inject.Provider; -import javax.inject.Singleton; import com.google.inject.AbstractModule; import com.google.inject.Provides; +import com.google.inject.Scopes; +import com.google.inject.Singleton; import com.google.inject.matcher.Matchers; import com.google.inject.multibindings.Multibinder; import com.google.inject.util.Providers; @@ -43,6 +45,7 @@ import com.netflix.archaius.config.EnvironmentConfig; import com.netflix.archaius.config.SettableConfig; import com.netflix.archaius.config.SystemConfig; +import com.netflix.archaius.exceptions.ConfigAlreadyExistsException; import com.netflix.archaius.exceptions.ConfigException; import com.netflix.archaius.inject.ApplicationLayer; import com.netflix.archaius.inject.LibrariesLayer; @@ -138,13 +141,11 @@ protected void configure() { } @Override - final protected void configure() { - ConfigurationInjectingListener listener = new ConfigurationInjectingListener(); - requestInjection(listener); - bindListener(Matchers.any(), listener); + protected void configure() { + bindListener(Matchers.any(), new ConfigurationInjectingListener()); Multibinder.newSetBinder(binder(), ConfigReader.class) - .addBinding().to(PropertiesConfigReader.class); + .addBinding().to(PropertiesConfigReader.class).in(Scopes.SINGLETON); Multibinder.newSetBinder(binder(), ConfigSeeder.class, RuntimeLayer.class); Multibinder.newSetBinder(binder(), ConfigSeeder.class, RemoteLayer.class); @@ -155,14 +156,14 @@ final protected void configure() { @Provides @Singleton @ApplicationLayer - final String getConfigName() { + String getConfigName() { return DEFAULT_CONFIG_NAME; } @Provides @Singleton @RuntimeLayer - final SettableConfig getSettableConfig() { + SettableConfig getSettableConfig() { return new DefaultSettableConfig(); } @@ -202,7 +203,7 @@ CompositeConfig getApplicationLayer() { */ @Provides @Singleton - final ConfigLoader getLoader( + ConfigLoader getLoader( @RootLayer Config config, CascadeStrategy defaultStrategy, Set readers @@ -218,40 +219,40 @@ final ConfigLoader getLoader( @Provides @Singleton @LibrariesLayer - final CompositeConfig getLibrariesLayer() { + CompositeConfig getLibrariesLayer() { return new CompositeConfig(); } @Provides @Singleton @RemoteLayer - final CompositeConfig getOverrideLayer() { + CompositeConfig getOverrideLayer() { return new CompositeConfig(); } @Provides @Singleton @RemoteLayer - final Config getOverrideLayer(@RemoteLayer CompositeConfig config) { + Config getOverrideLayer(@RemoteLayer CompositeConfig config) { return config; } @Provides @Singleton - final CascadeStrategy getCascadeStrategy() { + CascadeStrategy getCascadeStrategy() { return new NoCascadeStrategy(); } @Provides @Singleton - final Decoder getDecoder() { + Decoder getDecoder() { return DefaultDecoder.INSTANCE; } @Provides @Singleton @RootLayer - final Config getInternalConfig( + Config getInternalConfig( @RuntimeLayer SettableConfig settableLayer, @RemoteLayer Config overrideLayer, @ApplicationLayer CompositeConfig applicationLayer, @@ -283,10 +284,10 @@ final Config getInternalConfig( */ @Provides @Singleton - final Config getConfig( + public Config getConfig( @RootLayer Config config, @ApplicationLayer CompositeConfig applicationLayer, - @ApplicationLayer String appName, + @ApplicationLayer String configName, @RemoteLayer CompositeConfig overrideLayer, @RuntimeLayer SettableConfig runtimeLayer, ConfigLoader loader, @@ -296,7 +297,7 @@ final Config getConfig( ) throws Exception { // First load the application configuration - LinkedHashMap loadedConfigs = loader.newLoader().withCascadeStrategy(new NoCascadeStrategy()).load(appName); + LinkedHashMap loadedConfigs = loader.newLoader().withCascadeStrategy(new NoCascadeStrategy()).load(configName); if (loadedConfigs != null) { applicationLayer.addConfigs(loadedConfigs); } @@ -313,9 +314,17 @@ final Config getConfig( // Finally, load any cascaded configuration files for the // application - loadedConfigs = loader.newLoader().withCascadeStrategy(cascadeStrategy).load(appName); + loadedConfigs = loader.newLoader().withCascadeStrategy(cascadeStrategy).load(configName); if (loadedConfigs != null) { - applicationLayer.replaceConfigs(loadedConfigs); + applicationLayer.removeConfig(configName); + for (Entry entry : loadedConfigs.entrySet()) { + try { + applicationLayer.addConfig(entry.getKey(), entry.getValue()); + } + catch (ConfigAlreadyExistsException e) { + // OK to ignore + } + } } return config; } @@ -327,7 +336,7 @@ final Config getConfig( */ @Provides @Singleton - final PropertyFactory getPropertyFactory(final Config root) { + PropertyFactory getPropertyFactory(Config root) { return DefaultPropertyFactory.from(root); } } diff --git a/archaius2-guice/src/main/java/com/netflix/archaius/guice/ConfigurationInjectingListener.java b/archaius2-guice/src/main/java/com/netflix/archaius/guice/ConfigurationInjectingListener.java index 5a578f151..a1a1d5724 100644 --- a/archaius2-guice/src/main/java/com/netflix/archaius/guice/ConfigurationInjectingListener.java +++ b/archaius2-guice/src/main/java/com/netflix/archaius/guice/ConfigurationInjectingListener.java @@ -1,6 +1,9 @@ package com.netflix.archaius.guice; +import java.util.Map.Entry; + import javax.inject.Inject; +import javax.inject.Provider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,33 +24,34 @@ import com.netflix.archaius.annotations.Configuration; import com.netflix.archaius.annotations.ConfigurationSource; import com.netflix.archaius.config.CompositeConfig; +import com.netflix.archaius.exceptions.ConfigAlreadyExistsException; import com.netflix.archaius.exceptions.ConfigException; import com.netflix.archaius.inject.LibrariesLayer; -public class ConfigurationInjectingListener implements TypeListener, IoCContainer { +public class ConfigurationInjectingListener implements TypeListener { private static final Logger LOG = LoggerFactory.getLogger(ConfigurationInjectingListener.class); - @Inject - @LibrariesLayer - private CompositeConfig librariesConfig; - - @Inject - private Config rootConfig; - - @Inject - private Injector injector; - - @Inject - private ConfigMapper mapper; - - @Inject - private ConfigLoader loader; + static class Holder { + @Inject + private Config config; + + @Inject + private Injector injector; + + @Inject + private ConfigLoader loader; + + @Inject + private CascadeStrategy defaultStrategy; + + @Inject + private @LibrariesLayer CompositeConfig libraries; + } - @Inject - private CascadeStrategy defaultStrategy; + private ConfigMapper mapper = new ConfigMapper(true); @Override - public void hear(TypeLiteral typeLiteral, TypeEncounter encounter) { + public void hear(TypeLiteral typeLiteral, final TypeEncounter encounter) { Class clazz = typeLiteral.getRawType(); // @@ -55,18 +59,28 @@ public void hear(TypeLiteral typeLiteral, TypeEncounter encounter) { // ConfigurationSource source = clazz.getAnnotation(ConfigurationSource.class); if (source != null) { + final Provider holder = encounter.getProvider(Holder.class); + encounter.register(new InjectionListener() { @Override public void afterInjection(I injectee) { + System.out.println("AfterInjection : " + injectee); ConfigurationSource source = injectee.getClass().getAnnotation(ConfigurationSource.class); CascadeStrategy strategy = source.cascading() != ConfigurationSource.NullCascadeStrategy.class - ? injector.getInstance(source.cascading()) - : defaultStrategy; + ? holder.get().injector.getInstance(source.cascading()) + : holder.get().defaultStrategy; if (source != null) { for (String resourceName : source.value()) { try { - librariesConfig.replaceConfigs(loader.newLoader().withCascadeStrategy(strategy).load(resourceName)); + for (Entry config : holder.get().loader.newLoader().withCascadeStrategy(strategy).load(resourceName).entrySet()) { + try { + holder.get().libraries.addConfig(config.getKey(), config.getValue()); + } + catch (ConfigAlreadyExistsException e) { + // OK To ignore + } + } } catch (ConfigException e) { throw new ProvisionException("Unable to load configuration for " + resourceName + " at source " + injectee.getClass(), e); @@ -82,11 +96,18 @@ public void afterInjection(I injectee) { // Configuration configAnnot = clazz.getAnnotation(Configuration.class); if (configAnnot != null) { + final Provider holder = encounter.getProvider(Holder.class); + encounter.register(new InjectionListener() { @Override public void afterInjection(I injectee) { try { - mapper.mapConfig(injectee, rootConfig, ConfigurationInjectingListener.this); + mapper.mapConfig(injectee, holder.get().config, new IoCContainer() { + @Override + public T getInstance(String name, Class type) { + return holder.get().injector.getInstance(Key.get(type, Names.named(name))); + } + }); } catch (Exception e) { throw new ProvisionException("Unable to bind configuration to " + injectee.getClass(), e); @@ -95,9 +116,4 @@ public void afterInjection(I injectee) { }); } } - - @Override - public T getInstance(String name, Class type) { - return injector.getInstance(Key.get(type, Names.named(name))); - } } \ No newline at end of file