
com.netflix.archaius.DefaultAppConfig Maven / Gradle / Ivy
/**
* Copyright 2015 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.archaius;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.archaius.cascade.SimpleCascadeStrategy;
import com.netflix.archaius.config.CascadingCompositeConfig;
import com.netflix.archaius.config.EnvironmentConfig;
import com.netflix.archaius.config.SimpleDynamicConfig;
import com.netflix.archaius.config.SystemConfig;
import com.netflix.archaius.exceptions.ConfigException;
import com.netflix.archaius.interpolate.CommonsStrInterpolatorFactory;
import com.netflix.archaius.loaders.PropertiesConfigReader;
import com.netflix.archaius.property.DefaultPropertyContainer;
import com.netflix.archaius.property.PropertyFactoryDynamicConfigListener;
/**
* Main AppConfig to be used as the top level entry point for application configuration.
* This implementation is provided as a best practices approach to dealing with
* application configuration by extending composite configuration for a specific override
* structure.
*
* Override structure
* The {@link DefaultAppConfig} is a CompositeConfig and as such serves as the top level container
* for retrieving configurations. Configurations follow a specific override structure,
*
* RUNTIME - Properties set via code have absolute priority
* OVERRIDE - Properties loaded from a remote override service. DynamicConfig derived
* objects are added to this layer by calling {@link DefaultAppConfig#addConfigXXX()}
* SYSTEM - System.getProperties()
* ENVIRONMENT - System.getenv()
* APPLICATION - Properties loaded at startup from 'config.properties' and variants
* LIBRARY - Properties loaded by libraries or subsystems of the application.
* Calling {@link DefaultAppConfig#addConfigXXX()} loads Configs into this layer.
*
* Dynamic configuration
*
* In addition to static configurations AppConfig exposes an API to fetch {@link PropertyDsl}
* objects through which client code can receive update notification for properties. Note that
* updates to ObservableProperty are pushed once an underlying DynamicConfig configuration
* changes. Multiple DynamicConfig's may be added to the ConfigManager and all will be automatically
* subscribed to for configuration changes.
*
* @author elandau
*
*/
public class DefaultAppConfig extends CascadingCompositeConfig implements AppConfig {
private static final Logger LOG = LoggerFactory.getLogger(DefaultAppConfig.class);
private static final String DEFAULT_APP_CONFIG_NAME = "config";
private static final String NAME = "ROOT";
private static final String OVERRIDE_LAYER = "OVERRIDE";
private static final String DYNAMIC_LAYER = "DYNAMIC";
private static final String APPLICATION_LAYER = "APPLICATION";
private static final String LIBRARY_LAYER = "LIBRARY";
private static final SimpleCascadeStrategy DEFAULT_CASCADE_STRATEGY = new SimpleCascadeStrategy();
public static class Builder {
private StrInterpolatorFactory interpolator;
private final List loaders = new ArrayList();
private CascadeStrategy defaultStrategy = DEFAULT_CASCADE_STRATEGY;
private boolean failOnFirst = true;
private boolean enableSystemLayer = true;
private boolean enableEnvironmentLayer = true;
private boolean enableOverrideLayer = true;
private String configName = DEFAULT_APP_CONFIG_NAME;
private Properties props;
private Decoder decoder;
public Builder withStrInterpolator(StrInterpolatorFactory interpolator) {
this.interpolator = interpolator;
return this;
}
/**
* Call to include or exclude SystemConfig. Default is true.
*/
public Builder withSystemLayer(boolean flag) {
this.enableSystemLayer = flag;
return this;
}
/**
* Call to include or exclude EnvironmentConfig. Default is true.
*/
public Builder withEnvironmentLayer(boolean flag) {
this.enableEnvironmentLayer = flag;
return this;
}
/**
* Call to include or exclude DynamicConfig. Default is true.
*/
public Builder withOverrideLayer(boolean flag) {
this.enableOverrideLayer = flag;
return this;
}
/**
* Can be called multiple times to add multiple ConfigLoader to be used when
* loading application and library properties. If no loaders are added AppConfig
* will use PropertiesConfigLoader.
*/
public Builder withConfigReader(ConfigReader loader) {
this.loaders.add(loader);
return this;
}
/**
* Default cascade strategy to use for loading application and library properties.
* Library cascade strategies may be configured on the loader returned by newLoader.
*/
public Builder withDefaultCascadingStrategy(CascadeStrategy strategy) {
this.defaultStrategy = strategy;
return this;
}
/**
* Enable/disable failure if the first file in a cascade list of properties fails
* to load.
*/
public Builder withFailOnFirst(boolean flag) {
this.failOnFirst = flag;
return this;
}
/**
* Properties to load into the runtime layer at startup.
*/
public Builder withProperties(Properties props) {
this.props = props;
return this;
}
/**
* Name of application configuration to load at startup. Default is 'config'.
*/
public Builder withApplicationConfigName(String name) {
this.configName = name;
return this;
}
/**
* Decoder used to decode properties to arbitrary types.
*/
public Builder withDecoder(Decoder decoder) {
this.decoder = decoder;
return this;
}
public DefaultAppConfig build() {
if (this.interpolator == null) {
this.interpolator = new CommonsStrInterpolatorFactory();
}
if (this.loaders.isEmpty()) {
this.loaders.add(new PropertiesConfigReader());
}
return new DefaultAppConfig(this);
}
}
public static Builder builder() {
return new Builder();
}
public static DefaultAppConfig createDefault() {
return new Builder().build();
}
private final SimpleDynamicConfig runtime;
private final CascadingCompositeConfig override;
private final CascadingCompositeConfig library;
private final DefaultConfigLoader loader;
private final PropertyFactoryDynamicConfigListener dynamicObserver;
public DefaultAppConfig(Builder builder) {
super(NAME);
try {
if (builder.decoder != null) {
setDecoder(builder.decoder);
}
this.setStrInterpolator(builder.interpolator.create(this));
// The following are added first, before application configuration
// to allow for replacements in the application configuration cascade
// loading.
super.addConfig(runtime = new SimpleDynamicConfig(OVERRIDE_LAYER));
runtime.setProperties(builder.props);
if (builder.enableOverrideLayer) {
super.addConfig(override = new CascadingCompositeConfig(DYNAMIC_LAYER));
}
else {
override = null;
}
if (builder.enableSystemLayer) {
super.addConfig(new SystemConfig());
}
if (builder.enableEnvironmentLayer) {
super.addConfig(new EnvironmentConfig());
}
loader = DefaultConfigLoader.builder()
.withConfigReader(builder.loaders)
.withDefaultCascadingStrategy(builder.defaultStrategy)
.withFailOnFirst(builder.failOnFirst)
.withStrInterpolator(getStrInterpolator())
.build();
super.addConfig(loader.newLoader().withName(APPLICATION_LAYER).load(builder.configName));
super.addConfig(library = new CascadingCompositeConfig(LIBRARY_LAYER));
this.dynamicObserver = new PropertyFactoryDynamicConfigListener(new PropertyFactory() {
@Override
public PropertyContainer getProperty(String propName) {
return new DefaultPropertyContainer(propName, DefaultAppConfig.this);
}
});
library.addListener(dynamicObserver);
if (override != null) {
override.addListener(dynamicObserver);
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void addOverrideConfig(Config child) throws ConfigException {
LOG.info("Adding configuration : " + child.getName());
this.override.addConfig(child);
}
@Override
public void addConfig(Config child) throws ConfigException {
LOG.info("Adding configuration : " + child.getName());
this.library.addConfig(child);
}
public Loader newLoader() {
return loader.newLoader();
}
@Override
public PropertyContainer getProperty(String propName) {
return dynamicObserver.create(propName);
}
@Override
public void setProperty(String propName, Object propValue) {
PropertyContainer prop = dynamicObserver.get(propName);
runtime.setProperty(propName, propValue);
if (prop != null) {
prop.update();
}
}
@Override
public void clearProperty(String propName) {
PropertyContainer prop = dynamicObserver.get(propName);
runtime.clearProperty(propName);
if (prop != null) {
prop.update();
}
}
@Override
public void setProperties(Properties properties) {
runtime.setProperties(properties);
dynamicObserver.invalidate();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy