com.netflix.client.config.DefaultClientConfigImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.netflix.client.config.DefaultClientConfigImpl.java

Source

/*
*
* Copyright 2013 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.client.config;

import com.google.common.base.Strings;
import com.netflix.client.VipAddressResolver;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicProperty;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;

import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * Default client configuration that loads properties from Archaius's ConfigurationManager.
 * <p>
 * The easiest way to configure client and load balancer is through loading properties into Archaius that conform to the specific format:
    
<pre>{@code
<clientName>.<nameSpace>.<propertyName>=<value>
}</pre>
<p>
You can define properties in a file on classpath or as system properties. If former, ConfigurationManager.loadPropertiesFromResources() API should be called to load the file.
<p>
By default, "ribbon" should be the nameSpace.
<p>
If there is no property specified for a named client, {@code com.netflix.client.ClientFactory} will still create the client and
load balancer with default values for all necessary properties. The default
values are specified in this class as constants.
<p>
If a property is missing the clientName, it is interpreted as a property that applies to all clients. For example
    
<pre>{@code
ribbon.ReadTimeout=1000
}</pre>
    
This will establish the default ReadTimeout property for all clients.
<p>
You can also programmatically set properties by constructing instance of DefaultClientConfigImpl. Follow these steps:
<ul>
<li> Get an instance by calling {@link #getClientConfigWithDefaultValues(String)} to load default values,
and any properties that are already defined with Configuration in Archaius
<li> Set all desired properties by calling {@link #setProperty(IClientConfigKey, Object)} API.
<li> Pass this instance together with client name to {@code com.netflix.client.ClientFactory} API.
</ul>
<p><p>
If it is desired to have properties defined in a different name space, for example, "foo"
    
<pre>{@code
myclient.foo.ReadTimeout=1000
}</pre>
    
You should use {@link #getClientConfigWithDefaultValues(String, String)} - in the first step above.
 *
 * @author Sudhir Tonse
 * @author awang
 *
 */
public class DefaultClientConfigImpl implements IClientConfig {

    public static final Boolean DEFAULT_PRIORITIZE_VIP_ADDRESS_BASED_SERVERS = Boolean.TRUE;

    public static final String DEFAULT_NFLOADBALANCER_PING_CLASSNAME = "com.netflix.loadbalancer.DummyPing"; // DummyPing.class.getName();

    public static final String DEFAULT_NFLOADBALANCER_RULE_CLASSNAME = "com.netflix.loadbalancer.AvailabilityFilteringRule";

    public static final String DEFAULT_NFLOADBALANCER_CLASSNAME = "com.netflix.loadbalancer.ZoneAwareLoadBalancer";

    public static final boolean DEFAULT_USEIPADDRESS_FOR_SERVER = Boolean.FALSE;

    public static final String DEFAULT_CLIENT_CLASSNAME = "com.netflix.niws.client.http.RestClient";

    public static final String DEFAULT_VIPADDRESS_RESOLVER_CLASSNAME = "com.netflix.client.SimpleVipAddressResolver";

    public static final String DEFAULT_PRIME_CONNECTIONS_URI = "/";

    public static final int DEFAULT_MAX_TOTAL_TIME_TO_PRIME_CONNECTIONS = 30000;

    public static final int DEFAULT_MAX_RETRIES_PER_SERVER_PRIME_CONNECTION = 9;

    public static final Boolean DEFAULT_ENABLE_PRIME_CONNECTIONS = Boolean.FALSE;

    public static final int DEFAULT_MAX_REQUESTS_ALLOWED_PER_WINDOW = Integer.MAX_VALUE;

    public static final int DEFAULT_REQUEST_THROTTLING_WINDOW_IN_MILLIS = 60000;

    public static final Boolean DEFAULT_ENABLE_REQUEST_THROTTLING = Boolean.FALSE;

    public static final Boolean DEFAULT_ENABLE_GZIP_CONTENT_ENCODING_FILTER = Boolean.FALSE;

    public static final Boolean DEFAULT_CONNECTION_POOL_CLEANER_TASK_ENABLED = Boolean.TRUE;

    public static final Boolean DEFAULT_FOLLOW_REDIRECTS = Boolean.FALSE;

    public static final float DEFAULT_PERCENTAGE_NIWS_EVENT_LOGGED = 0.0f;

    public static final int DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER = 1;

    public static final int DEFAULT_MAX_AUTO_RETRIES = 0;

    public static final int DEFAULT_BACKOFF_INTERVAL = 0;

    public static final int DEFAULT_READ_TIMEOUT = 5000;

    public static final int DEFAULT_CONNECTION_MANAGER_TIMEOUT = 2000;

    public static final int DEFAULT_CONNECT_TIMEOUT = 2000;

    public static final Boolean DEFAULT_ENABLE_CONNECTION_POOL = Boolean.TRUE;

    @Deprecated
    public static final int DEFAULT_MAX_HTTP_CONNECTIONS_PER_HOST = 50;

    @Deprecated
    public static final int DEFAULT_MAX_TOTAL_HTTP_CONNECTIONS = 200;

    public static final int DEFAULT_MAX_CONNECTIONS_PER_HOST = 50;

    public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 200;

    public static final float DEFAULT_MIN_PRIME_CONNECTIONS_RATIO = 1.0f;

    public static final String DEFAULT_PRIME_CONNECTIONS_CLASS = "com.netflix.niws.client.http.HttpPrimeConnection";

    public static final String DEFAULT_SEVER_LIST_CLASS = "com.netflix.loadbalancer.ConfigurationBasedServerList";

    public static final int DEFAULT_CONNECTION_IDLE_TIMERTASK_REPEAT_IN_MSECS = 30000; // every half minute (30 secs)

    public static final int DEFAULT_CONNECTIONIDLE_TIME_IN_MSECS = 30000; // all connections idle for 30 secs

    protected volatile Map<String, Object> properties = new ConcurrentHashMap<String, Object>();

    protected Map<IClientConfigKey<?>, Object> typedProperties = new ConcurrentHashMap<IClientConfigKey<?>, Object>();

    private static final Logger LOG = LoggerFactory.getLogger(DefaultClientConfigImpl.class);

    private String clientName = null;

    private VipAddressResolver resolver = null;

    private boolean enableDynamicProperties = true;
    /**
     * Defaults for the parameters for the thread pool used by batchParallel
     * calls
     */
    public static final int DEFAULT_POOL_MAX_THREADS = DEFAULT_MAX_TOTAL_HTTP_CONNECTIONS;
    public static final int DEFAULT_POOL_MIN_THREADS = 1;
    public static final long DEFAULT_POOL_KEEP_ALIVE_TIME = 15 * 60L;
    public static final TimeUnit DEFAULT_POOL_KEEP_ALIVE_TIME_UNITS = TimeUnit.SECONDS;
    public static final Boolean DEFAULT_ENABLE_ZONE_AFFINITY = Boolean.FALSE;
    public static final Boolean DEFAULT_ENABLE_ZONE_EXCLUSIVITY = Boolean.FALSE;
    public static final int DEFAULT_PORT = 7001;
    public static final Boolean DEFAULT_ENABLE_LOADBALANCER = Boolean.TRUE;

    public static final String DEFAULT_PROPERTY_NAME_SPACE = "ribbon";

    private String propertyNameSpace = DEFAULT_PROPERTY_NAME_SPACE;

    public static final Boolean DEFAULT_OK_TO_RETRY_ON_ALL_OPERATIONS = Boolean.FALSE;

    public static final Boolean DEFAULT_ENABLE_NIWS_EVENT_LOGGING = Boolean.TRUE;

    public static final Boolean DEFAULT_IS_CLIENT_AUTH_REQUIRED = Boolean.FALSE;

    private final Map<String, DynamicStringProperty> dynamicProperties = new ConcurrentHashMap<String, DynamicStringProperty>();

    public Boolean getDefaultPrioritizeVipAddressBasedServers() {
        return DEFAULT_PRIORITIZE_VIP_ADDRESS_BASED_SERVERS;
    }

    public String getDefaultNfloadbalancerPingClassname() {
        return DEFAULT_NFLOADBALANCER_PING_CLASSNAME;
    }

    public String getDefaultNfloadbalancerRuleClassname() {
        return DEFAULT_NFLOADBALANCER_RULE_CLASSNAME;
    }

    public String getDefaultNfloadbalancerClassname() {
        return DEFAULT_NFLOADBALANCER_CLASSNAME;
    }

    public boolean getDefaultUseIpAddressForServer() {
        return DEFAULT_USEIPADDRESS_FOR_SERVER;
    }

    public String getDefaultClientClassname() {
        return DEFAULT_CLIENT_CLASSNAME;
    }

    public String getDefaultVipaddressResolverClassname() {
        return DEFAULT_VIPADDRESS_RESOLVER_CLASSNAME;
    }

    public String getDefaultPrimeConnectionsUri() {
        return DEFAULT_PRIME_CONNECTIONS_URI;
    }

    public int getDefaultMaxTotalTimeToPrimeConnections() {
        return DEFAULT_MAX_TOTAL_TIME_TO_PRIME_CONNECTIONS;
    }

    public int getDefaultMaxRetriesPerServerPrimeConnection() {
        return DEFAULT_MAX_RETRIES_PER_SERVER_PRIME_CONNECTION;
    }

    public Boolean getDefaultEnablePrimeConnections() {
        return DEFAULT_ENABLE_PRIME_CONNECTIONS;
    }

    public int getDefaultMaxRequestsAllowedPerWindow() {
        return DEFAULT_MAX_REQUESTS_ALLOWED_PER_WINDOW;
    }

    public int getDefaultRequestThrottlingWindowInMillis() {
        return DEFAULT_REQUEST_THROTTLING_WINDOW_IN_MILLIS;
    }

    public Boolean getDefaultEnableRequestThrottling() {
        return DEFAULT_ENABLE_REQUEST_THROTTLING;
    }

    public Boolean getDefaultEnableGzipContentEncodingFilter() {
        return DEFAULT_ENABLE_GZIP_CONTENT_ENCODING_FILTER;
    }

    public Boolean getDefaultConnectionPoolCleanerTaskEnabled() {
        return DEFAULT_CONNECTION_POOL_CLEANER_TASK_ENABLED;
    }

    public Boolean getDefaultFollowRedirects() {
        return DEFAULT_FOLLOW_REDIRECTS;
    }

    public float getDefaultPercentageNiwsEventLogged() {
        return DEFAULT_PERCENTAGE_NIWS_EVENT_LOGGED;
    }

    public int getDefaultMaxAutoRetriesNextServer() {
        return DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER;
    }

    public int getDefaultMaxAutoRetries() {
        return DEFAULT_MAX_AUTO_RETRIES;
    }

    public int getDefaultReadTimeout() {
        return DEFAULT_READ_TIMEOUT;
    }

    public int getDefaultConnectionManagerTimeout() {
        return DEFAULT_CONNECTION_MANAGER_TIMEOUT;
    }

    public int getDefaultConnectTimeout() {
        return DEFAULT_CONNECT_TIMEOUT;
    }

    @Deprecated
    public int getDefaultMaxHttpConnectionsPerHost() {
        return DEFAULT_MAX_HTTP_CONNECTIONS_PER_HOST;
    }

    @Deprecated
    public int getDefaultMaxTotalHttpConnections() {
        return DEFAULT_MAX_TOTAL_HTTP_CONNECTIONS;
    }

    public int getDefaultMaxConnectionsPerHost() {
        return DEFAULT_MAX_CONNECTIONS_PER_HOST;
    }

    public int getDefaultMaxTotalConnections() {
        return DEFAULT_MAX_TOTAL_CONNECTIONS;
    }

    public float getDefaultMinPrimeConnectionsRatio() {
        return DEFAULT_MIN_PRIME_CONNECTIONS_RATIO;
    }

    public String getDefaultPrimeConnectionsClass() {
        return DEFAULT_PRIME_CONNECTIONS_CLASS;
    }

    public String getDefaultSeverListClass() {
        return DEFAULT_SEVER_LIST_CLASS;
    }

    public int getDefaultConnectionIdleTimertaskRepeatInMsecs() {
        return DEFAULT_CONNECTION_IDLE_TIMERTASK_REPEAT_IN_MSECS;
    }

    public int getDefaultConnectionidleTimeInMsecs() {
        return DEFAULT_CONNECTIONIDLE_TIME_IN_MSECS;
    }

    public VipAddressResolver getResolver() {
        return resolver;
    }

    public boolean isEnableDynamicProperties() {
        return enableDynamicProperties;
    }

    public int getDefaultPoolMaxThreads() {
        return DEFAULT_POOL_MAX_THREADS;
    }

    public int getDefaultPoolMinThreads() {
        return DEFAULT_POOL_MIN_THREADS;
    }

    public long getDefaultPoolKeepAliveTime() {
        return DEFAULT_POOL_KEEP_ALIVE_TIME;
    }

    public TimeUnit getDefaultPoolKeepAliveTimeUnits() {
        return DEFAULT_POOL_KEEP_ALIVE_TIME_UNITS;
    }

    public Boolean getDefaultEnableZoneAffinity() {
        return DEFAULT_ENABLE_ZONE_AFFINITY;
    }

    public Boolean getDefaultEnableZoneExclusivity() {
        return DEFAULT_ENABLE_ZONE_EXCLUSIVITY;
    }

    public int getDefaultPort() {
        return DEFAULT_PORT;
    }

    public Boolean getDefaultEnableLoadbalancer() {
        return DEFAULT_ENABLE_LOADBALANCER;
    }

    public Boolean getDefaultOkToRetryOnAllOperations() {
        return DEFAULT_OK_TO_RETRY_ON_ALL_OPERATIONS;
    }

    public Boolean getDefaultIsClientAuthRequired() {
        return DEFAULT_IS_CLIENT_AUTH_REQUIRED;
    }

    /**
     * Create instance with no properties in default name space {@link #DEFAULT_PROPERTY_NAME_SPACE}
     */
    public DefaultClientConfigImpl() {
        this.dynamicProperties.clear();
        this.enableDynamicProperties = false;
    }

    /**
     * Create instance with no properties in the specified name space
     */
    public DefaultClientConfigImpl(String nameSpace) {
        this();
        this.propertyNameSpace = nameSpace;
    }

    public void loadDefaultValues() {
        putDefaultIntegerProperty(CommonClientConfigKey.MaxHttpConnectionsPerHost,
                getDefaultMaxHttpConnectionsPerHost());
        putDefaultIntegerProperty(CommonClientConfigKey.MaxTotalHttpConnections,
                getDefaultMaxTotalHttpConnections());
        putDefaultBooleanProperty(CommonClientConfigKey.EnableConnectionPool, getDefaultEnableConnectionPool());
        putDefaultIntegerProperty(CommonClientConfigKey.MaxConnectionsPerHost, getDefaultMaxConnectionsPerHost());
        putDefaultIntegerProperty(CommonClientConfigKey.MaxTotalConnections, getDefaultMaxTotalConnections());
        putDefaultIntegerProperty(CommonClientConfigKey.ConnectTimeout, getDefaultConnectTimeout());
        putDefaultIntegerProperty(CommonClientConfigKey.ConnectionManagerTimeout,
                getDefaultConnectionManagerTimeout());
        putDefaultIntegerProperty(CommonClientConfigKey.ReadTimeout, getDefaultReadTimeout());
        putDefaultIntegerProperty(CommonClientConfigKey.MaxAutoRetries, getDefaultMaxAutoRetries());
        putDefaultIntegerProperty(CommonClientConfigKey.MaxAutoRetriesNextServer,
                getDefaultMaxAutoRetriesNextServer());
        putDefaultBooleanProperty(CommonClientConfigKey.OkToRetryOnAllOperations,
                getDefaultOkToRetryOnAllOperations());
        putDefaultBooleanProperty(CommonClientConfigKey.FollowRedirects, getDefaultFollowRedirects());
        putDefaultBooleanProperty(CommonClientConfigKey.ConnectionPoolCleanerTaskEnabled,
                getDefaultConnectionPoolCleanerTaskEnabled());
        putDefaultIntegerProperty(CommonClientConfigKey.ConnIdleEvictTimeMilliSeconds,
                getDefaultConnectionidleTimeInMsecs());
        putDefaultIntegerProperty(CommonClientConfigKey.ConnectionCleanerRepeatInterval,
                getDefaultConnectionIdleTimertaskRepeatInMsecs());
        putDefaultBooleanProperty(CommonClientConfigKey.EnableGZIPContentEncodingFilter,
                getDefaultEnableGzipContentEncodingFilter());
        String proxyHost = ConfigurationManager.getConfigInstance()
                .getString(getDefaultPropName(CommonClientConfigKey.ProxyHost.key()));
        if (proxyHost != null && proxyHost.length() > 0) {
            setProperty(CommonClientConfigKey.ProxyHost, proxyHost);
        }
        Integer proxyPort = ConfigurationManager.getConfigInstance()
                .getInteger(getDefaultPropName(CommonClientConfigKey.ProxyPort), (Integer.MIN_VALUE + 1)); // + 1 just to avoid potential clash with user setting
        if (proxyPort != (Integer.MIN_VALUE + 1)) {
            setProperty(CommonClientConfigKey.ProxyPort, proxyPort);
        }
        putDefaultIntegerProperty(CommonClientConfigKey.Port, getDefaultPort());
        putDefaultBooleanProperty(CommonClientConfigKey.EnablePrimeConnections, getDefaultEnablePrimeConnections());
        putDefaultIntegerProperty(CommonClientConfigKey.MaxRetriesPerServerPrimeConnection,
                getDefaultMaxRetriesPerServerPrimeConnection());
        putDefaultIntegerProperty(CommonClientConfigKey.MaxTotalTimeToPrimeConnections,
                getDefaultMaxTotalTimeToPrimeConnections());
        putDefaultStringProperty(CommonClientConfigKey.PrimeConnectionsURI, getDefaultPrimeConnectionsUri());
        putDefaultIntegerProperty(CommonClientConfigKey.PoolMinThreads, getDefaultPoolMinThreads());
        putDefaultIntegerProperty(CommonClientConfigKey.PoolMaxThreads, getDefaultPoolMaxThreads());
        putDefaultLongProperty(CommonClientConfigKey.PoolKeepAliveTime, getDefaultPoolKeepAliveTime());
        putDefaultTimeUnitProperty(CommonClientConfigKey.PoolKeepAliveTimeUnits,
                getDefaultPoolKeepAliveTimeUnits());
        putDefaultBooleanProperty(CommonClientConfigKey.EnableZoneAffinity, getDefaultEnableZoneAffinity());
        putDefaultBooleanProperty(CommonClientConfigKey.EnableZoneExclusivity, getDefaultEnableZoneExclusivity());
        putDefaultStringProperty(CommonClientConfigKey.ClientClassName, getDefaultClientClassname());
        putDefaultStringProperty(CommonClientConfigKey.NFLoadBalancerClassName,
                getDefaultNfloadbalancerClassname());
        putDefaultStringProperty(CommonClientConfigKey.NFLoadBalancerRuleClassName,
                getDefaultNfloadbalancerRuleClassname());
        putDefaultStringProperty(CommonClientConfigKey.NFLoadBalancerPingClassName,
                getDefaultNfloadbalancerPingClassname());
        putDefaultBooleanProperty(CommonClientConfigKey.PrioritizeVipAddressBasedServers,
                getDefaultPrioritizeVipAddressBasedServers());
        putDefaultFloatProperty(CommonClientConfigKey.MinPrimeConnectionsRatio,
                getDefaultMinPrimeConnectionsRatio());
        putDefaultStringProperty(CommonClientConfigKey.PrimeConnectionsClassName,
                getDefaultPrimeConnectionsClass());
        putDefaultStringProperty(CommonClientConfigKey.NIWSServerListClassName, getDefaultSeverListClass());
        putDefaultStringProperty(CommonClientConfigKey.VipAddressResolverClassName,
                getDefaultVipaddressResolverClassname());
        putDefaultBooleanProperty(CommonClientConfigKey.IsClientAuthRequired, getDefaultIsClientAuthRequired());
        // putDefaultStringProperty(CommonClientConfigKey.RequestIdHeaderName, getDefaultRequestIdHeaderName());
        putDefaultBooleanProperty(CommonClientConfigKey.UseIPAddrForServer, getDefaultUseIpAddressForServer());
        putDefaultStringProperty(CommonClientConfigKey.ListOfServers, "");
    }

    public Boolean getDefaultEnableConnectionPool() {
        return DEFAULT_ENABLE_CONNECTION_POOL;
    }

    protected void setPropertyInternal(IClientConfigKey propName, Object value) {
        setPropertyInternal(propName.key(), value);
    }

    private String getConfigKey(String propName) {
        return (clientName == null) ? getDefaultPropName(propName) : getInstancePropName(clientName, propName);
    }

    protected void setPropertyInternal(final String propName, Object value) {
        String stringValue = (value == null) ? "" : String.valueOf(value);
        properties.put(propName, stringValue);
        if (!enableDynamicProperties) {
            return;
        }
        String configKey = getConfigKey(propName);
        final DynamicStringProperty prop = DynamicPropertyFactory.getInstance().getStringProperty(configKey, null);
        Runnable callback = new Runnable() {
            @Override
            public void run() {
                String value = prop.get();
                if (value != null) {
                    properties.put(propName, value);
                } else {
                    properties.remove(propName);
                }
            }

            // equals and hashcode needed
            // since this is anonymous object is later used as a set key

            @Override
            public boolean equals(Object other) {
                if (other == null) {
                    return false;
                }
                if (getClass() == other.getClass()) {
                    return toString().equals(other.toString());
                }
                return false;
            }

            @Override
            public String toString() {
                return propName;
            }

            @Override
            public int hashCode() {
                return propName.hashCode();
            }

        };
        prop.addCallback(callback);
        dynamicProperties.put(propName, prop);
    }

    // Helper methods which first check if a "default" (with rest client name)
    // property exists. If so, that value is used, else the default value
    // passed as argument is used to put into the properties member variable
    protected void putDefaultIntegerProperty(IClientConfigKey propName, Integer defaultValue) {
        Integer value = ConfigurationManager.getConfigInstance().getInteger(getDefaultPropName(propName),
                defaultValue);
        setPropertyInternal(propName, value);
    }

    protected void putDefaultLongProperty(IClientConfigKey propName, Long defaultValue) {
        Long value = ConfigurationManager.getConfigInstance().getLong(getDefaultPropName(propName), defaultValue);
        setPropertyInternal(propName, value);
    }

    protected void putDefaultFloatProperty(IClientConfigKey propName, Float defaultValue) {
        Float value = ConfigurationManager.getConfigInstance().getFloat(getDefaultPropName(propName), defaultValue);
        setPropertyInternal(propName, value);
    }

    protected void putDefaultTimeUnitProperty(IClientConfigKey propName, TimeUnit defaultValue) {
        TimeUnit value = defaultValue;
        String propValue = ConfigurationManager.getConfigInstance().getString(getDefaultPropName(propName));
        if (propValue != null && propValue.length() > 0) {
            value = TimeUnit.valueOf(propValue);
        }
        setPropertyInternal(propName, value);
    }

    String getDefaultPropName(String propName) {
        return getNameSpace() + "." + propName;
    }

    public String getDefaultPropName(IClientConfigKey propName) {
        return getDefaultPropName(propName.key());
    }

    protected void putDefaultStringProperty(IClientConfigKey propName, String defaultValue) {
        String value = ConfigurationManager.getConfigInstance().getString(getDefaultPropName(propName),
                defaultValue);
        setPropertyInternal(propName, value);
    }

    protected void putDefaultBooleanProperty(IClientConfigKey propName, Boolean defaultValue) {
        Boolean value = ConfigurationManager.getConfigInstance().getBoolean(getDefaultPropName(propName),
                defaultValue);
        setPropertyInternal(propName, value);
    }

    public void setClientName(String clientName) {
        this.clientName = clientName;
    }

    /* (non-Javadoc)
    * @see com.netflix.niws.client.CliengConfig#getClientName()
    */
    @Override
    public String getClientName() {
        return clientName;
    }

    /**
     * Load properties for a given client. It first loads the default values for all properties,
     * and any properties already defined with Archaius ConfigurationManager.
     */
    @Override
    public void loadProperties(String restClientName) {
        enableDynamicProperties = true;
        setClientName(restClientName);
        loadDefaultValues();
        Configuration props = ConfigurationManager.getConfigInstance().subset(restClientName);
        for (Iterator<String> keys = props.getKeys(); keys.hasNext();) {
            String key = keys.next();
            String prop = key;
            try {
                if (prop.startsWith(getNameSpace())) {
                    prop = prop.substring(getNameSpace().length() + 1);
                }
                setPropertyInternal(prop, getStringValue(props, key));
            } catch (Exception ex) {
                throw new RuntimeException(String.format("Property %s is invalid", prop));
            }
        }
    }

    /**
     * This is to workaround the issue that {@link AbstractConfiguration} by default
     * automatically convert comma delimited string to array
     */
    protected static String getStringValue(Configuration config, String key) {
        try {
            String values[] = config.getStringArray(key);
            if (values == null) {
                return null;
            }
            if (values.length == 0) {
                return config.getString(key);
            } else if (values.length == 1) {
                return values[0];
            }

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < values.length; i++) {
                sb.append(values[i]);
                if (i != values.length - 1) {
                    sb.append(",");
                }
            }
            return sb.toString();
        } catch (Exception e) {
            Object v = config.getProperty(key);
            if (v != null) {
                return String.valueOf(v);
            } else {
                return null;
            }
        }
    }

    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "DC_DOUBLECHECK")
    private VipAddressResolver getVipAddressResolver() {
        if (resolver == null) {
            synchronized (this) {
                if (resolver == null) {
                    try {
                        resolver = (VipAddressResolver) Class
                                .forName((String) getProperty(CommonClientConfigKey.VipAddressResolverClassName))
                                .newInstance();
                    } catch (Throwable e) {
                        LOG.error("Cannot instantiate VipAddressResolver", e);
                    }
                }
            }
        }
        return resolver;

    }

    @Override
    public String resolveDeploymentContextbasedVipAddresses() {
        String deploymentContextBasedVipAddressesMacro = (String) getProperty(
                CommonClientConfigKey.DeploymentContextBasedVipAddresses);
        if (deploymentContextBasedVipAddressesMacro == null) {
            return null;
        }
        return getVipAddressResolver().resolve(deploymentContextBasedVipAddressesMacro, this);
    }

    public String getAppName() {
        String appName = null;
        Object an = getProperty(CommonClientConfigKey.AppName);
        if (an != null) {
            appName = "" + an;
        }
        return appName;
    }

    public String getVersion() {
        String version = null;
        Object an = getProperty(CommonClientConfigKey.Version);
        if (an != null) {
            version = "" + an;
        }
        return version;
    }

    /* (non-Javadoc)
    * @see com.netflix.niws.client.CliengConfig#getProperties()
    */
    @Override
    public Map<String, Object> getProperties() {
        return properties;
    }

    /* (non-Javadoc)
    * @see com.netflix.niws.client.CliengConfig#setProperty(com.netflix.niws.client.ClientConfigKey, java.lang.Object)
    */
    @Override
    public void setProperty(IClientConfigKey key, Object value) {
        setPropertyInternal(key.key(), value);
    }

    public DefaultClientConfigImpl withProperty(IClientConfigKey key, Object value) {
        setProperty(key, value);
        return this;
    }

    public IClientConfig applyOverride(IClientConfig override) {
        if (override == null) {
            return this;
        }
        Map<String, Object> props = override.getProperties();
        for (Map.Entry<String, Object> entry : props.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if (key != null && value != null) {
                setPropertyInternal(key, value);
            }
        }
        return this;
    }

    protected Object getProperty(String key) {
        if (enableDynamicProperties) {
            String dynamicValue = null;
            DynamicStringProperty dynamicProperty = dynamicProperties.get(key);
            if (dynamicProperty != null) {
                dynamicValue = dynamicProperty.get();
            }
            if (dynamicValue == null) {
                dynamicValue = DynamicProperty.getInstance(getConfigKey(key)).getString();
                if (dynamicValue == null) {
                    dynamicValue = DynamicProperty.getInstance(getDefaultPropName(key)).getString();
                }
            }
            if (dynamicValue != null) {
                return dynamicValue;
            }
        }
        return properties.get(key);
    }

    /* (non-Javadoc)
    * @see com.netflix.niws.client.CliengConfig#getProperty(com.netflix.niws.client.ClientConfigKey)
    */
    @Override
    public Object getProperty(IClientConfigKey key) {
        String propName = key.key();
        Object value = getProperty(propName);
        return value;
    }

    /* (non-Javadoc)
    * @see com.netflix.niws.client.CliengConfig#getProperty(com.netflix.niws.client.ClientConfigKey, java.lang.Object)
    */
    @Override
    public Object getProperty(IClientConfigKey key, Object defaultVal) {
        Object val = getProperty(key);
        if (val == null) {
            return defaultVal;
        }
        return val;
    }

    public static Object getProperty(Map<String, Object> config, IClientConfigKey key, Object defaultVal) {
        Object val = config.get(key.key());
        if (val == null) {
            return defaultVal;
        }
        return val;
    }

    public static Object getProperty(Map<String, Object> config, IClientConfigKey key) {
        return getProperty(config, key, null);
    }

    public boolean isSecure() {
        Object oo = getProperty(CommonClientConfigKey.IsSecure);
        if (oo != null) {
            return Boolean.parseBoolean(oo.toString());
        } else {
            return false;
        }
    }

    /* (non-Javadoc)
    * @see com.netflix.niws.client.CliengConfig#containsProperty(com.netflix.niws.client.ClientConfigKey)
    */
    @Override
    public boolean containsProperty(IClientConfigKey key) {
        Object oo = getProperty(key);
        return oo != null ? true : false;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        String separator = "";

        sb.append("ClientConfig:");
        for (IClientConfigKey key : CommonClientConfigKey.values()) {
            final Object value = getProperty(key);

            sb.append(separator);
            separator = ", ";
            sb.append(key).append(":");
            if (key.key().endsWith("Password") && value instanceof String) {
                sb.append(Strings.repeat("*", ((String) value).length()));
            } else {
                sb.append(value);
            }
        }
        return sb.toString();
    }

    public void setProperty(Properties props, String restClientName, String key, String value) {
        props.setProperty(getInstancePropName(restClientName, key), value);
    }

    public String getInstancePropName(String restClientName, IClientConfigKey configKey) {
        return getInstancePropName(restClientName, configKey.key());
    }

    public String getInstancePropName(String restClientName, String key) {
        return restClientName + "." + getNameSpace() + "." + key;
    }

    @Override
    public String getNameSpace() {
        return propertyNameSpace;
    }

    public static DefaultClientConfigImpl getEmptyConfig() {
        return new DefaultClientConfigImpl();
    }

    public static DefaultClientConfigImpl getClientConfigWithDefaultValues(String clientName) {
        return getClientConfigWithDefaultValues(clientName, DEFAULT_PROPERTY_NAME_SPACE);
    }

    public static DefaultClientConfigImpl getClientConfigWithDefaultValues() {
        return getClientConfigWithDefaultValues("default", DEFAULT_PROPERTY_NAME_SPACE);
    }

    public static DefaultClientConfigImpl getClientConfigWithDefaultValues(String clientName, String nameSpace) {
        DefaultClientConfigImpl config = new DefaultClientConfigImpl(nameSpace);
        config.loadProperties(clientName);
        return config;
    }

    @Override
    public int getPropertyAsInteger(IClientConfigKey key, int defaultValue) {
        Object rawValue = getProperty(key);
        if (rawValue != null) {
            try {
                return Integer.parseInt(String.valueOf(rawValue));
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;

    }

    @Override
    public String getPropertyAsString(IClientConfigKey key, String defaultValue) {
        Object rawValue = getProperty(key);
        if (rawValue != null) {
            return String.valueOf(rawValue);
        }
        return defaultValue;
    }

    @Override
    public boolean getPropertyAsBoolean(IClientConfigKey key, boolean defaultValue) {
        Object rawValue = getProperty(key);
        if (rawValue != null) {
            try {
                return Boolean.valueOf(String.valueOf(rawValue));
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T get(IClientConfigKey<T> key) {
        Object obj = getProperty(key.key());
        if (obj == null) {
            return null;
        }
        Class<T> type = key.type();
        try {
            return type.cast(obj);
        } catch (ClassCastException e) {
            if (obj instanceof String) {
                String stringValue = (String) obj;
                if (Integer.class.equals(type)) {
                    return (T) Integer.valueOf(stringValue);
                } else if (Boolean.class.equals(type)) {
                    return (T) Boolean.valueOf(stringValue);
                } else if (Float.class.equals(type)) {
                    return (T) Float.valueOf(stringValue);
                } else if (Long.class.equals(type)) {
                    return (T) Long.valueOf(stringValue);
                } else if (Double.class.equals(type)) {
                    return (T) Double.valueOf(stringValue);
                } else if (TimeUnit.class.equals(type)) {
                    return (T) TimeUnit.valueOf(stringValue);
                }
                throw new IllegalArgumentException("Unable to convert string value to desired type " + type);
            } else {
                throw e;
            }
        }
    }

    @Override
    public <T> DefaultClientConfigImpl set(IClientConfigKey<T> key, T value) {
        properties.put(key.key(), value);
        return this;
    }

    @Override
    public <T> T get(IClientConfigKey<T> key, T defaultValue) {
        T value = get(key);
        if (value == null) {
            value = defaultValue;
        }
        return value;
    }
}