All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.config.DynamicPropertyFactory Maven / Gradle / Ivy

There is a newer version: 2.0.0-rc.7
Show newest version
/**
 * Copyright 2014 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.config;


import com.netflix.config.jmx.BaseConfigMBean;
import com.netflix.config.sources.URLConfigurationSource;
import org.apache.commons.configuration.AbstractConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A factory that creates instances of dynamic properties and associates them with an underlying configuration
 * or {@link DynamicPropertySupport} where the properties could be changed dynamically at runtime.
 * 

* It is recommended to initialize this class with a configuration or DynamicPropertySupport before the first call to * {@link #getInstance()}. Otherwise, it will be lazily initialized with a {@link ConcurrentCompositeConfiguration}, * where a SystemConfiguration and {@link DynamicURLConfiguration} will be added. You can also disable installing the default configuration * by setting system property {@value #DISABLE_DEFAULT_CONFIG} to be true. *

* If system property {@value #ENABLE_JMX} is set to true, when this class is initialized with a configuration, * the configuration will also be exposed to JMX via an instance of {@link BaseConfigMBean}, where you can update the properties * via jconsole. *

* Example:

 *    import com.netflix.config.DynamicProperty;
 *
 *    class MyClass {
 *        private static DynamicIntProperty maxWaitMillis
 *            = DynamicPropertyFactory.getInstance().getIntProperty("myclass.sleepMillis", 250);
 *           // ...
 *
 *           // add a callback when this property is changed
 *           maxWaitMillis.addCallback(new Runnable() {
 *               public void run() {
 *                   int currentValue = maxWaitMillis.get();
 *                   // ...
 *               }
 *           });
 *           // ...
 *           // Wait for a configurable amount of time for condition to become true.
 *           // Note that the time can be changed on-the-fly.
 *           someCondition.wait(maxWaitMillis.get());
 *
 *        // ...
 *    }
 * 
*

* Please note that you should not cache the property value if you expect the value to change on-the-fly. For example, * in the following code the change of the value is ineffective: *

*

 *    int maxWaitMillis = DynamicPropertyFactory.getInstance().getIntProperty("myclass.sleepMillis", 250).get();
 *    // ...
 *    someCondition.wait(maxWaitMillis);
 * 
* * @author awang */ public class DynamicPropertyFactory { private static DynamicPropertyFactory instance = new DynamicPropertyFactory(); private volatile static DynamicPropertySupport config = null; private volatile static boolean initializedWithDefaultConfig = false; private static final Logger logger = LoggerFactory.getLogger(DynamicPropertyFactory.class); /** * @deprecated Moved to ConfigurationManager in 0.5.12 */ @Deprecated public static final String URL_CONFIG_NAME = ConfigurationManager.URL_CONFIG_NAME; /** * @deprecated Moved to ConfigurationManager in 0.5.12 */ @Deprecated public static final String SYS_CONFIG_NAME = ConfigurationManager.SYS_CONFIG_NAME; /** * Boolean system property to define whether a configuration MBean should be registered with * JMX so that properties can be accessed via JMX console. Default is "unset". */ public static final String ENABLE_JMX = "archaius.dynamicPropertyFactory.registerConfigWithJMX"; /** * System property name that defines whether {@link #getInstance()} should throw * {@link MissingConfigurationSourceException} if there is no proper configuration source * at the time of call. */ public static final String THROW_MISSING_CONFIGURATION_SOURCE_EXCEPTION = "archaius.dynamicProperty.throwMissingConfigurationSourceException"; private volatile static boolean throwMissingConfigurationSourceException = Boolean.getBoolean(THROW_MISSING_CONFIGURATION_SOURCE_EXCEPTION); /** * System property to disable adding SystemConfiguration to the default ConcurrentCompositeConfiguration * * @deprecated Moved to ConfigurationManager in 0.5.12 */ @Deprecated public static final String DISABLE_DEFAULT_SYS_CONFIG = ConfigurationManager.DISABLE_DEFAULT_SYS_CONFIG; /** * System property to determine whether DynamicPropertyFactory should be lazily initialized with * default configuration for {@link #getInstance()}. Default is false (not set). */ public static final String DISABLE_DEFAULT_CONFIG = "archaius.dynamicProperty.disableDefaultConfig"; private DynamicPropertyFactory() { } /** * Initialize the factory with an AbstractConfiguration. *

* The initialization will register a ConfigurationListener with the configuration so that {@link DynamicProperty} * will receives a callback and refresh its value when a property is changed in the configuration. *

* If the factory is already initialized with a default configuration source (see {@link #getInstance()}), it will re-register * itself with the new configuration source passed to this method. Otherwise, this method will throw IllegalArgumentException * if it has been initialized with a different and non-default configuration source. This method should be only called once. * * @param config Configuration to be attached with DynamicProperty * @return the instance of DynamicPropertyFactory * @throws IllegalArgumentException if the factory has already been initialized with a non-default configuration source */ public static DynamicPropertyFactory initWithConfigurationSource(AbstractConfiguration config) { synchronized (ConfigurationManager.class) { if (config == null) { throw new NullPointerException("config is null"); } if (ConfigurationManager.isConfigurationInstalled() && config != ConfigurationManager.instance) { throw new IllegalStateException("ConfigurationManager is already initialized with configuration " + ConfigurationManager.getConfigInstance()); } if (config instanceof DynamicPropertySupport) { return initWithConfigurationSource((DynamicPropertySupport) config); } return initWithConfigurationSource(new ConfigurationBackedDynamicPropertySupportImpl(config)); } } /** * Return whether the factory is initialized with the default ConcurrentCompositeConfiguration. */ public static boolean isInitializedWithDefaultConfig() { return initializedWithDefaultConfig; } /** * Get the backing configuration from the factory. This can be cased to a {@link ConcurrentCompositeConfiguration} * if the default configuration is installed. *

For example: *

     *     Configuration config = DynamicPropertyFactory.getInstance().getBackingConfigurationSource();
     *     if (DynamicPropertyFactory.isInitializedWithDefaultConfig()) {
     *         ConcurrentCompositeConfiguration composite = (ConcurrentCompositeConfiguration) config;
     *         // ...
     *     }
     * 
* * @return the configuration source that DynamicPropertyFactory is initialized with. This will be an instance of * Configuration, or {@link DynamicPropertySupport}, or null if DynamicPropertyFactory has not been initialized. */ public static Object getBackingConfigurationSource() { if (config instanceof ConfigurationBackedDynamicPropertySupportImpl) { return ((ConfigurationBackedDynamicPropertySupportImpl) config).getConfiguration(); } else { return config; } } /** * Set the boolean value to indicate whether {@link #getInstance()} should throw * {@link MissingConfigurationSourceException} if there is no proper configuration source * at the time of call. * * @param value to set */ public static void setThrowMissingConfigurationSourceException(boolean value) { throwMissingConfigurationSourceException = value; } /** * @return the boolean value to indicate whether {@link #getInstance()} should throw * {@link MissingConfigurationSourceException} if there is no proper configuration source * at the time of call. */ public static boolean isThrowMissingConfigurationSourceException() { return throwMissingConfigurationSourceException; } /** * Initialize the factory with a {@link DynamicPropertySupport}. *

* The initialization will register a {@link PropertyListener} with the DynamicPropertySupport so that DynamicProperty * will receives a callback and refresh its value when a property is changed. *

* If the factory is already initialized with a default configuration source (see {@link #getInstance()}), it will re-register * itself with the new configuration source passed to this method. Otherwise, this method will throw IllegalArgumentException * if it has been initialized with a non-default configuration source. This method should be only called once. * * @param dynamicPropertySupport DynamicPropertySupport to be associated with the DynamicProperty * @return the instance of DynamicPropertyFactory * @throws IllegalArgumentException if the factory has already been initialized with a different and non-default configuration source */ public static DynamicPropertyFactory initWithConfigurationSource(DynamicPropertySupport dynamicPropertySupport) { synchronized (ConfigurationManager.class) { if (dynamicPropertySupport == null) { throw new IllegalArgumentException("dynamicPropertySupport is null"); } AbstractConfiguration configuration = null; if (dynamicPropertySupport instanceof AbstractConfiguration) { configuration = (AbstractConfiguration) dynamicPropertySupport; } else if (dynamicPropertySupport instanceof ConfigurationBackedDynamicPropertySupportImpl) { configuration = ((ConfigurationBackedDynamicPropertySupportImpl) dynamicPropertySupport).getConfiguration(); } if (initializedWithDefaultConfig) { config = null; } else if (config != null && config != dynamicPropertySupport) { throw new IllegalStateException("DynamicPropertyFactory is already initialized with a diffrerent configuration source: " + config); } if (ConfigurationManager.isConfigurationInstalled() && (configuration != null && configuration != ConfigurationManager.instance)) { throw new IllegalStateException("ConfigurationManager is already initialized with configuration " + ConfigurationManager.getConfigInstance()); } if (configuration != null && configuration != ConfigurationManager.instance) { ConfigurationManager.setDirect(configuration); } setDirect(dynamicPropertySupport); return instance; } } static void setDirect(DynamicPropertySupport support) { synchronized (ConfigurationManager.class) { config = support; DynamicProperty.registerWithDynamicPropertySupport(support); initializedWithDefaultConfig = false; } } /** * Get the instance to create dynamic properties. If the factory is not initialized with a configuration source * (see {@link #initWithConfigurationSource(AbstractConfiguration)} and {@link #initWithConfigurationSource(DynamicPropertySupport)}), * it will fist try to initialize itself with a default {@link ConcurrentCompositeConfiguration}, with the following two * sub configurations: *

    *
  • A SystemConfiguration, which contains all the system properties. You can disable adding this Configuration by setting * system property {@value #DISABLE_DEFAULT_SYS_CONFIG} to true *
  • A {@link DynamicURLConfiguration}, which at a fixed interval polls * a configuration file (see {@link URLConfigurationSource#DEFAULT_CONFIG_FILE_NAME}) on classpath and a set of URLs specified via a system property * (see {@link URLConfigurationSource#CONFIG_URL}). *
* Between the two sub-configurations, the SystemConfiguration will take the precedence when determining property values. *

* You can disable the initialization with the default configuration by setting system property {@value #DISABLE_DEFAULT_CONFIG} to "true". */ public static DynamicPropertyFactory getInstance() { if (config == null) { synchronized (ConfigurationManager.class) { if (config == null) { AbstractConfiguration configFromManager = ConfigurationManager.getConfigInstance(); if (configFromManager != null) { initWithConfigurationSource(configFromManager); initializedWithDefaultConfig = !ConfigurationManager.isConfigurationInstalled(); logger.info("DynamicPropertyFactory is initialized with configuration sources: " + configFromManager); } } } } return instance; } private static void checkAndWarn(String propName) { if (config == null) { logger.warn("DynamicProperty " + propName + " is created without a configuration source for callback."); } } /** * Create a new property whose value is a string and subject to change on-the-fly. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration */ public DynamicStringProperty getStringProperty(String propName, String defaultValue) { return getStringProperty(propName, defaultValue, null); } /** * Create a new property whose value is a string and subject to change on-the-fly. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration * @param propertyChangeCallback a Runnable to be called when the property is changed */ public DynamicStringProperty getStringProperty(String propName, String defaultValue, final Runnable propertyChangeCallback) { checkAndWarn(propName); DynamicStringProperty property = new DynamicStringProperty(propName, defaultValue); addCallback(propertyChangeCallback, property); return property; } /** * Create a new property whose value is an integer and subject to change on-the-fly.. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration */ public DynamicIntProperty getIntProperty(String propName, int defaultValue) { return getIntProperty(propName, defaultValue, null); } /** * Create a new property whose value is an integer and subject to change on-the-fly. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration * @param propertyChangeCallback a Runnable to be called when the property is changed */ public DynamicIntProperty getIntProperty(String propName, int defaultValue, final Runnable propertyChangeCallback) { checkAndWarn(propName); DynamicIntProperty property = new DynamicIntProperty(propName, defaultValue); addCallback(propertyChangeCallback, property); return property; } /** * Create a new property whose value is a long and subject to change on-the-fly.. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration */ public DynamicLongProperty getLongProperty(String propName, long defaultValue) { return getLongProperty(propName, defaultValue, null); } /** * Create a new property whose value is a long and subject to change on-the-fly. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration * @param propertyChangeCallback a Runnable to be called when the property is changed */ public DynamicLongProperty getLongProperty(String propName, long defaultValue, final Runnable propertyChangeCallback) { checkAndWarn(propName); DynamicLongProperty property = new DynamicLongProperty(propName, defaultValue); addCallback(propertyChangeCallback, property); return property; } /** * Create a new property whose value is a boolean and subject to change on-the-fly.. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration */ public DynamicBooleanProperty getBooleanProperty(String propName, boolean defaultValue) { return getBooleanProperty(propName, defaultValue, null); } /** * Create a new property whose value is a boolean and subject to change on-the-fly. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration * @param propertyChangeCallback a Runnable to be called when the property is changed */ public DynamicBooleanProperty getBooleanProperty(String propName, boolean defaultValue, final Runnable propertyChangeCallback) { checkAndWarn(propName); DynamicBooleanProperty property = new DynamicBooleanProperty(propName, defaultValue); addCallback(propertyChangeCallback, property); return property; } /** * Create a new property whose value is a float and subject to change on-the-fly.. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration */ public DynamicFloatProperty getFloatProperty(String propName, float defaultValue) { return getFloatProperty(propName, defaultValue, null); } /** * Create a new property whose value is a float and subject to change on-the-fly. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration * @param propertyChangeCallback a Runnable to be called when the property is changed */ public DynamicFloatProperty getFloatProperty(String propName, float defaultValue, final Runnable propertyChangeCallback) { checkAndWarn(propName); DynamicFloatProperty property = new DynamicFloatProperty(propName, defaultValue); addCallback(propertyChangeCallback, property); return property; } /** * Create a new property whose value is a double and subject to change on-the-fly.. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration */ public DynamicDoubleProperty getDoubleProperty(String propName, double defaultValue) { return getDoubleProperty(propName, defaultValue, null); } /** * Create a new property whose value is a double and subject to change on-the-fly. * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration * @param propertyChangeCallback a Runnable to be called when the property is changed */ public DynamicDoubleProperty getDoubleProperty(String propName, double defaultValue, final Runnable propertyChangeCallback) { checkAndWarn(propName); DynamicDoubleProperty property = new DynamicDoubleProperty(propName, defaultValue); addCallback(propertyChangeCallback, property); return property; } /** * Create a new contextual property of type T * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration * @param the type of the property value */ public DynamicContextualProperty getContextualProperty(String propName, T defaultValue) { return getContextualProperty(propName, defaultValue, null); } /** * Create a new contextual property of type T * * @param propName property name * @param defaultValue default value if the property is not defined in underlying configuration * @param propertyChangeCallback a Runnable to be called when the property is changed * @param the type of the property value */ public DynamicContextualProperty getContextualProperty(String propName, T defaultValue, final Runnable propertyChangeCallback) { checkAndWarn(propName); DynamicContextualProperty property = new DynamicContextualProperty(propName, defaultValue); addCallback(propertyChangeCallback, property); return property; } private static void addCallback(Runnable callback, PropertyWrapper wrapper) { if (callback != null) { wrapper.prop.addCallback(callback); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy