DELTASPIKE-1322 mv more config pieces over 2 impl

Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/5b07e197
Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/5b07e197
Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/5b07e197

Branch: refs/heads/master
Commit: 5b07e197cb808cee108e1935cae18fc199c82a9f
Parents: bd638aa
Author: Mark Struberg <strub...@apache.org>
Authored: Mon Mar 5 15:51:53 2018 +0100
Committer: Mark Struberg <strub...@apache.org>
Committed: Mon Mar 5 15:51:53 2018 +0100

----------------------------------------------------------------------
 .../deltaspike/core/api/config/Config.java      | 148 +++--
 .../core/api/config/ConfigResolver.java         | 615 ++-----------------
 .../core/api/config/ConfigResolverContext.java  |  73 ---
 .../deltaspike/core/impl/config/ConfigImpl.java | 320 +++++-----
 .../core/impl/config/ConfigResolverContext.java |  73 +++
 .../core/impl/config/TypedResolverImpl.java     | 565 +++++++++++++++++
 6 files changed, 918 insertions(+), 876 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/deltaspike/blob/5b07e197/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/Config.java
----------------------------------------------------------------------
diff --git 
a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/Config.java
 
b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/Config.java
index 2422c7b..47d2c50 100644
--- 
a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/Config.java
+++ 
b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/Config.java
@@ -1,68 +1,80 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.deltaspike.core.api.config;
-
-import org.apache.deltaspike.core.spi.config.ConfigFilter;
-import org.apache.deltaspike.core.spi.config.ConfigSource;
-
-import java.util.List;
-
-/**
- * The Configuration for an application/ClassLoader.
- */
-public interface Config
-{
-
-    /**
-     * @return all the current ConfigSources for this Config
-     */
-    ConfigSource[] getConfigSources();
-
-    /**
-     * This method can be used for programmatically adding {@link 
ConfigSource}s.
-     * It is not needed for normal 'usage' by end users, but only for 
Extension Developers!
-     *
-     * @param configSourcesToAdd the ConfigSources to add
-     */
-    void addConfigSources(List<ConfigSource> configSourcesToAdd);
-
-    /**
-     * @return the {@link ConfigFilter}s for the current application.
-     */
-    List<ConfigFilter> getConfigFilters();
-
-    /**
-     * Add a {@link ConfigFilter} to the ConfigResolver. This will only affect 
the current WebApp (or more precisely the
-     * current ClassLoader and it's children).
-     *
-     * @param configFilter
-     */
-    void addConfigFilter(ConfigFilter configFilter);
-
-    /**
-     * Filter the configured value.
-     * This can e.g. be used for decryption.
-     * @param key the key of the config property
-     * @param value to be filtered
-     * @param forLog whether the value is intended to be presented to some 
humans somehow.
-     *               If filtered for logging, then secrets might get starred 
out '*****'.
-     * @return the filtered value
-     */
-    String filterConfigValue(String key, String value, boolean forLog);
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.core.api.config;
+
+import org.apache.deltaspike.core.spi.config.ConfigFilter;
+import org.apache.deltaspike.core.spi.config.ConfigSource;
+
+import java.util.List;
+
+/**
+ * The Configuration for an application/ClassLoader.
+ */
+public interface Config
+{
+
+    /**
+     * The entry point to the builder-based optionally typed configuration 
resolution mechanism.
+     *
+     * String is the default type for configuration entries and is not 
considered a 'type' by this resolver. Therefore
+     * an UntypedResolver is returned by this method. To convert the 
configuration value to another type, call
+     * {@link ConfigResolver.UntypedResolver#as(Class)}.
+     *
+     * @param name The property key to resolve
+     * @return A builder for configuration resolution.
+     */
+    ConfigResolver.UntypedResolver<String> resolve(String name);
+
+    /**
+     * @return all the current ConfigSources for this Config
+     */
+    ConfigSource[] getConfigSources();
+
+    /**
+     * This method can be used for programmatically adding {@link 
ConfigSource}s.
+     * It is not needed for normal 'usage' by end users, but only for 
Extension Developers!
+     *
+     * @param configSourcesToAdd the ConfigSources to add
+     */
+    void addConfigSources(List<ConfigSource> configSourcesToAdd);
+
+    /**
+     * @return the {@link ConfigFilter}s for the current application.
+     */
+    List<ConfigFilter> getConfigFilters();
+
+    /**
+     * Add a {@link ConfigFilter} to the ConfigResolver. This will only affect 
the current WebApp (or more precisely the
+     * current ClassLoader and it's children).
+     *
+     * @param configFilter
+     */
+    void addConfigFilter(ConfigFilter configFilter);
+
+    /**
+     * Filter the configured value.
+     * This can e.g. be used for decryption.
+     * @param key the key of the config property
+     * @param value to be filtered
+     * @param forLog whether the value is intended to be presented to some 
humans somehow.
+     *               If filtered for logging, then secrets might get starred 
out '*****'.
+     * @return the filtered value
+     */
+    String filterConfigValue(String key, String value, boolean forLog);
+}

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/5b07e197/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java
----------------------------------------------------------------------
diff --git 
a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java
 
b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java
index fa280ef..8a5dbe9 100644
--- 
a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java
+++ 
b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java
@@ -29,17 +29,13 @@ import java.util.List;
 import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import javax.enterprise.inject.Typed;
 
-import org.apache.deltaspike.core.api.projectstage.ProjectStage;
 import org.apache.deltaspike.core.spi.config.ConfigFilter;
 import org.apache.deltaspike.core.spi.config.ConfigSource;
 import org.apache.deltaspike.core.util.ClassUtils;
-import org.apache.deltaspike.core.util.ExceptionUtils;
-import org.apache.deltaspike.core.util.ProjectStageProducer;
 
 /**
  * The main entry point to the DeltaSpike configuration mechanism.
@@ -155,11 +151,10 @@ public final class ConfigResolver
 
     public static String getPropertyValue(String key, String defaultValue, 
boolean evaluateVariables)
     {
-        ConfigResolverContext configResolverContext = evaluateVariables ?
-                ConfigResolverContext.EVAL_VARIABLES : 
ConfigResolverContext.NONE;
-        String value = getPropertyValue(key, configResolverContext);
-
-        return fallbackToDefaultIfEmpty(key, value, defaultValue, 
configResolverContext);
+        return getConfigProvider().getConfig().resolve(key)
+                .withDefault(defaultValue)
+                .evaluateVariables(evaluateVariables)
+                .getValue();
     }
 
     /**
@@ -172,9 +167,10 @@ public final class ConfigResolver
      */
     public static String getPropertyValue(String key)
     {
-        return getPropertyValue(
-                key, 
-                new ConfigResolverContext().setEvaluateVariables(true));
+        return getConfigProvider().getConfig().resolve(key)
+                .evaluateVariables(true)
+                .withCurrentProjectStage(false)
+                .getValue();
     }
 
     /**
@@ -188,9 +184,10 @@ public final class ConfigResolver
      */
     public static String getPropertyValue(String key, boolean 
evaluateVariables)
     {
-        return getPropertyValue(
-                key, 
-                evaluateVariables ? ConfigResolverContext.EVAL_VARIABLES : 
ConfigResolverContext.NONE);
+        return getConfigProvider().getConfig().resolve(key)
+                .evaluateVariables(evaluateVariables)
+                .withCurrentProjectStage(false)
+                .getValue();
     }
 
     /**
@@ -214,17 +211,10 @@ public final class ConfigResolver
      */
     public static String getProjectStageAwarePropertyValue(String key)
     {
-        ConfigResolverContext configResolverContext = 
ConfigResolverContext.PROJECTSTAGE_EVAL_VARIABLES;
-        
-        ProjectStage ps = getProjectStage();
-
-        String value = getPropertyValue(key + '.' + ps, configResolverContext);
-        if (value == null)
-        {
-            value = getPropertyValue(key, configResolverContext);
-        }
-
-        return value;
+        return getConfigProvider().getConfig().resolve(key)
+                .withCurrentProjectStage(true)
+                .evaluateVariables(true)
+                .getValue();
     }
     /**
      * {@link #getProjectStageAwarePropertyValue(String)} which returns the 
provided default value if no configured
@@ -238,9 +228,11 @@ public final class ConfigResolver
      */
     public static String getProjectStageAwarePropertyValue(String key, String 
defaultValue)
     {
-        String value = getProjectStageAwarePropertyValue(key);
-
-        return fallbackToDefaultIfEmpty(key, value, defaultValue, 
ConfigResolverContext.PROJECTSTAGE_EVAL_VARIABLES);
+        return getConfigProvider().getConfig().resolve(key)
+                .withCurrentProjectStage(true)
+                .withDefault(defaultValue)
+                .evaluateVariables(true)
+                .getValue();
     }
 
     /**
@@ -284,21 +276,11 @@ public final class ConfigResolver
      */
     public static String getPropertyAwarePropertyValue(String key, String 
property)
     {
-        String propertyValue = getProjectStageAwarePropertyValue(property);
-
-        String value = null;
-
-        if (propertyValue != null && propertyValue.length() > 0)
-        {
-            value = getProjectStageAwarePropertyValue(key + '.' + 
propertyValue);
-        }
-
-        if (value == null)
-        {
-            value = getProjectStageAwarePropertyValue(key);
-        }
-
-        return value;
+        return getConfigProvider().getConfig().resolve(key)
+                .withCurrentProjectStage(true)
+                .parameterizedBy(property)
+                .evaluateVariables(true)
+                .getValue();
     }
 
     /**
@@ -318,81 +300,12 @@ public final class ConfigResolver
      */
     public static String getPropertyAwarePropertyValue(String key, String 
property, String defaultValue)
     {
-        String value = getPropertyAwarePropertyValue(key, property);
-
-        return fallbackToDefaultIfEmpty(key, value, defaultValue, 
ConfigResolverContext.PROJECTSTAGE_EVAL_VARIABLES);
-    }
-    
-    private static String getPropertyValue(String key, ConfigResolverContext 
configResolverContext)
-    {
-        if (key == null)
-        {
-            return null;
-        }
-
-        ConfigSource[] appConfigSources = getConfigSources();
-        String value;
-
-        for (ConfigSource configSource : appConfigSources)
-        {
-            value = configSource.getPropertyValue(key);
-
-            if (value != null)
-            {
-                LOG.log(Level.FINE, "found value {0} for key {1} in 
ConfigSource {2}.",
-                        new Object[]{filterConfigValueForLog(key, value), key, 
configSource.getConfigName()});
-
-                if (configResolverContext.isEvaluateVariables())
-                {
-                    value = resolveVariables(value, configResolverContext);
-                }
-
-                return filterConfigValue(key, value);
-            }
-
-            LOG.log(Level.FINER, "NO value found for key {0} in ConfigSource 
{1}.",
-                    new Object[]{key, configSource.getConfigName()});
-        }
-
-        return null;        
-    }
-
-    /**
-     * recursively resolve any ${varName} in the value
-     */
-    private static String resolveVariables(String value, ConfigResolverContext 
configResolverContext)
-    {
-        int startVar = 0;
-        while ((startVar = value.indexOf("${", startVar)) >= 0)
-        {
-            int endVar = value.indexOf("}", startVar);
-            if (endVar <= 0)
-            {
-                break;
-            }
-            String varName = value.substring(startVar + 2, endVar);
-            if (varName.isEmpty())
-            {
-                break;
-            }
-            
-            String variableValue;
-            if (configResolverContext.isProjectStageAware())
-            {
-                variableValue = getProjectStageAwarePropertyValue(varName);
-            }
-            else
-            {
-                variableValue = getPropertyValue(varName, true);
-            }
-            
-            if (variableValue != null)
-            {
-                value = value.replace("${" + varName + "}", variableValue);
-            }
-            startVar++;
-        }
-        return value;
+        return getConfigProvider().getConfig().resolve(key)
+                .withCurrentProjectStage(true)
+                .parameterizedBy(property)
+                .withDefault(defaultValue)
+                .evaluateVariables(true)
+                .getValue();
     }
 
     /**
@@ -438,8 +351,8 @@ public final class ConfigResolver
     public static Map<String, String> getAllProperties()
     {
         // must use a new list because Arrays.asList() is resistant to sorting 
on some JVMs:
-        List<ConfigSource> appConfigSources = sortAscending(new 
ArrayList<ConfigSource>(
-                Arrays.<ConfigSource> asList(getConfigSources())));
+        List<ConfigSource> appConfigSources = sortAscending(new ArrayList<>(
+                Arrays.asList(getConfigSources())));
         Map<String, String> result = new HashMap<String, String>();
 
         for (ConfigSource configSource : appConfigSources)
@@ -453,7 +366,7 @@ public final class ConfigResolver
         return Collections.unmodifiableMap(result);
     }
 
-    public static synchronized ConfigSource[] getConfigSources()
+    public static ConfigSource[] getConfigSources()
     {
         return getConfigProvider().getConfig().getConfigSources();
     }
@@ -474,29 +387,6 @@ public final class ConfigResolver
         return configSources;
     }
 
-    private static ProjectStage getProjectStage()
-    {
-        return ProjectStageProducer.getInstance().getProjectStage();
-    }
-
-    private static <T> T fallbackToDefaultIfEmpty(String key, T value, T 
defaultValue,
-                                                  ConfigResolverContext 
configResolverContext)
-    {
-        if (value == null || (value instanceof String && 
((String)value).isEmpty()))
-        {
-            if (configResolverContext != null && defaultValue instanceof String
-                    && configResolverContext.isEvaluateVariables())
-            {
-                defaultValue = (T) resolveVariables((String) defaultValue, 
configResolverContext);
-            }
-            LOG.log(Level.FINE, "no configured value found for key {0}, using 
default value {1}.",
-                    new Object[]{key, defaultValue});
-
-            return defaultValue;
-        }
-
-        return value;
-    }
 
     /**
      * Filter the configured value.
@@ -711,440 +601,7 @@ public final class ConfigResolver
      */
     public static UntypedResolver<String> resolve(String name)
     {
-        return new PropertyBuilder<String>(name);
-    }
-
-    private static class PropertyBuilder<T> implements UntypedResolver<T>
-    {
-
-        private String keyOriginal;
-
-        private String keyResolved;
-
-        private Type configEntryType = String.class;
-
-        private boolean withDefault = false;
-        private T defaultValue;
-
-        private boolean projectStageAware = true;
-
-        private String propertyParameter;
-
-        private String parameterValue;
-
-        private boolean strictly = false;
-
-        private boolean isList = false;
-
-        private Converter<?> converter;
-
-        private boolean evaluateVariables = false;
-        private boolean logChanges = false;
-
-        private long cacheTimeMs = -1;
-        private volatile long reloadAfter = -1;
-        private T lastValue = null;
-
-
-        private PropertyBuilder()
-        {
-        }
-
-        protected PropertyBuilder(String propertyName)
-        {
-            this.keyOriginal = propertyName;
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public <N> TypedResolver<N> as(Class<N> clazz)
-        {
-            configEntryType = clazz;
-            return (TypedResolver<N>) this;
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public TypedResolver<List<T>> asList()
-        {
-            isList = true;
-            TypedResolver<List<T>> listTypedResolver = 
(TypedResolver<List<T>>) this;
-
-            if (defaultValue == null)
-            {
-                // the default for lists is an empty list instead of null
-                return 
listTypedResolver.withDefault(Collections.<T>emptyList());
-            }
-
-            return listTypedResolver;
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public <N> TypedResolver<N> as(Class<N> clazz, Converter<N> converter)
-        {
-            configEntryType = clazz;
-            this.converter = converter;
-
-            return (TypedResolver<N>) this;
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public <N> TypedResolver<N> as(Type clazz, Converter<N> converter)
-        {
-            configEntryType = clazz;
-            this.converter = converter;
-
-            return (TypedResolver<N>) this;
-        }
-
-        @Override
-        public TypedResolver<T> withDefault(T value)
-        {
-            defaultValue = value;
-            withDefault = true;
-            return this;
-        }
-
-        @Override
-        public TypedResolver<T> withStringDefault(String value)
-        {
-            if (value == null || value.isEmpty())
-            {
-                throw new RuntimeException("Empty String or null supplied as 
string-default value for property "
-                        + keyOriginal);
-            }
-
-            if (isList)
-            {
-                defaultValue = splitAndConvertListValue(value);
-            }
-            else
-            {
-                defaultValue = convert(value);
-            }
-            withDefault = true;
-            return this;
-        }
-
-        @Override
-        public TypedResolver<T> cacheFor(TimeUnit timeUnit, long value)
-        {
-            this.cacheTimeMs = timeUnit.toMillis(value);
-            return this;
-        }
-
-        @Override
-        public TypedResolver<T> parameterizedBy(String propertyName)
-        {
-            this.propertyParameter = propertyName;
-
-            if (propertyParameter != null && !propertyParameter.isEmpty())
-            {
-                String parameterValue = ConfigResolver
-                        .resolve(propertyParameter)
-                        .withCurrentProjectStage(projectStageAware)
-                        .getValue();
-
-                if (parameterValue != null && !parameterValue.isEmpty())
-                {
-                    this.parameterValue = parameterValue;
-                }
-            }
-
-            return this;
-        }
-
-        @Override
-        public TypedResolver<T> withCurrentProjectStage(boolean with)
-        {
-            this.projectStageAware = with;
-            return this;
-        }
-
-        @Override
-        public TypedResolver<T> strictly(boolean strictly)
-        {
-            this.strictly = strictly;
-            return this;
-        }
-
-        @Override
-        public TypedResolver<T> evaluateVariables(boolean evaluateVariables)
-        {
-            this.evaluateVariables = evaluateVariables;
-            return this;
-        }
-
-        @Override
-        public TypedResolver<T> logChanges(boolean logChanges)
-        {
-            this.logChanges = logChanges;
-            return this;
-        }
-
-        @Override
-        public T getValue()
-        {
-            long now = -1;
-            if (cacheTimeMs > 0)
-            {
-                now = System.currentTimeMillis();
-                if (now <= reloadAfter)
-                {
-                    return lastValue;
-                }
-            }
-
-            String valueStr = resolveStringValue();
-            T value;
-            if (isList)
-            {
-                value = splitAndConvertListValue(valueStr);
-            }
-            else
-            {
-                value = convert(valueStr);
-            }
-
-            if (withDefault)
-            {
-                ConfigResolverContext configResolverContext = new 
ConfigResolverContext()
-                        .setEvaluateVariables(evaluateVariables)
-                        .setProjectStageAware(projectStageAware);
-                value = fallbackToDefaultIfEmpty(keyResolved, value, 
defaultValue, configResolverContext);
-                if (isList && String.class.isInstance(value))
-                {
-                    value = splitAndConvertListValue(String.class.cast(value));
-                }
-            }
-
-            if (logChanges && (value != null && !value.equals(lastValue) || 
(value == null && lastValue != null)) )
-            {
-                LOG.log(Level.INFO, "New value {0} for key {1}.",
-                    new Object[]{filterConfigValueForLog(keyOriginal, 
valueStr), keyOriginal});
-            }
-
-            lastValue = value;
-
-            if (cacheTimeMs > 0)
-            {
-                reloadAfter = now + cacheTimeMs;
-            }
-
-            return value;
-        }
-
-        private T splitAndConvertListValue(String valueStr)
-        {
-            if (valueStr == null)
-            {
-                return null;
-            }
-
-            List list = new ArrayList();
-            StringBuilder currentValue = new StringBuilder();
-            int length = valueStr.length();
-            for (int i = 0; i < length; i++)
-            {
-                char c = valueStr.charAt(i);
-                if (c == '\\')
-                {
-                    if (i < length - 1)
-                    {
-                        char nextC = valueStr.charAt(i + 1);
-                        currentValue.append(nextC);
-                        i++;
-                    }
-                }
-                else if (c == ',')
-                {
-                    String trimedVal = currentValue.toString().trim();
-                    if (trimedVal.length() > 0)
-                    {
-                        list.add(convert(trimedVal));
-                    }
-
-                    currentValue.setLength(0);
-                }
-                else
-                {
-                    currentValue.append(c);
-                }
-            }
-
-            String trimedVal = currentValue.toString().trim();
-            if (trimedVal.length() > 0)
-            {
-                list.add(convert(trimedVal));
-            }
-
-            return (T) list;
-        }
-
-        @Override
-        public String getKey()
-        {
-            return keyOriginal;
-        }
-
-        @Override
-        public String getResolvedKey()
-        {
-            return keyResolved;
-        }
-
-        @Override
-        public T getDefaultValue()
-        {
-            return defaultValue;
-        }
-
-        /**
-         * Performs the resolution cascade
-         */
-        private String resolveStringValue()
-        {
-            ProjectStage ps = null;
-            String value = null;
-            keyResolved = keyOriginal;
-            int keySuffices = 0;
-
-            // make the longest key
-            // first, try appending resolved parameter
-            if (propertyParameter != null && !propertyParameter.isEmpty())
-            {
-                if (parameterValue != null && !parameterValue.isEmpty())
-                {
-                    keyResolved += "." + parameterValue;
-                    keySuffices++;
-                }
-                // if parameter value can't be resolved and strictly
-                else if (strictly)
-                {
-                    return null;
-                }
-            }
-
-            // try appending projectstage
-            if (projectStageAware)
-            {
-                ps = getProjectStage();
-                keyResolved += "." + ps;
-                keySuffices++;
-            }
-
-            // make initial resolution of longest key
-            value = getPropertyValue(keyResolved, evaluateVariables);
-
-            // try fallbacks if not strictly
-            if (value == null && !strictly)
-            {
-
-                // by the length of the longest resolved key already tried
-                // breaks are left out intentionally
-                switch (keySuffices)
-                {
-
-                    case 2:
-                        // try base.param
-                        keyResolved = keyOriginal + "." + parameterValue;
-                        value = getPropertyValue(keyResolved, null, 
evaluateVariables);
-
-                        if (value != null)
-                        {
-                            return value;
-                        }
-
-                        // try base.ps
-                        ps = getProjectStage();
-                        keyResolved = keyOriginal + "." + ps;
-                        value = getPropertyValue(keyResolved, null, 
evaluateVariables);
-
-                        if (value != null)
-                        {
-                            return value;
-                        }
-
-                    case 1:
-                        // try base
-                        keyResolved = keyOriginal;
-                        value = getPropertyValue(keyResolved, null, 
evaluateVariables);
-                        return value;
-
-                    default:
-                        // the longest key was the base, no fallback
-                        return null;
-                }
-            }
-
-            return value;
-        }
-
-        /**
-         * If a converter was provided for this builder, it takes precedence 
over the built-in converters.
-         */
-        private T convert(String value)
-        {
-            if (value == null)
-            {
-                return null;
-            }
-
-            Object result = null;
-
-            if (this.converter != null)
-            {
-                try
-                {
-                    result = converter.convert(value);
-                }
-                catch (Exception e)
-                {
-                    throw ExceptionUtils.throwAsRuntimeException(e);
-                }
-            }
-            else if (String.class.equals(configEntryType))
-            {
-                result = value;
-            }
-            else if (Class.class.equals(configEntryType))
-            {
-                result = ClassUtils.tryToLoadClassForName(value);
-            }
-            else if (Boolean.class.equals(configEntryType))
-            {
-                Boolean isTrue = "TRUE".equalsIgnoreCase(value);
-                isTrue |= "1".equalsIgnoreCase(value);
-                isTrue |= "YES".equalsIgnoreCase(value);
-                isTrue |= "Y".equalsIgnoreCase(value);
-                isTrue |= "JA".equalsIgnoreCase(value);
-                isTrue |= "J".equalsIgnoreCase(value);
-                isTrue |= "OUI".equalsIgnoreCase(value);
-
-                result = isTrue;
-            }
-            else if (Integer.class.equals(configEntryType))
-            {
-                result = Integer.parseInt(value);
-            }
-            else if (Long.class.equals(configEntryType))
-            {
-                result = Long.parseLong(value);
-            }
-            else if (Float.class.equals(configEntryType))
-            {
-                result = Float.parseFloat(value);
-            }
-            else if (Double.class.equals(configEntryType))
-            {
-                result = Double.parseDouble(value);
-            }
-
-            return (T) result;
-        }
-
+        return getConfigProvider().getConfig().resolve(name);
     }
 
 

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/5b07e197/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolverContext.java
----------------------------------------------------------------------
diff --git 
a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolverContext.java
 
b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolverContext.java
deleted file mode 100644
index 223932f..0000000
--- 
a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolverContext.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.deltaspike.core.api.config;
-
-class ConfigResolverContext
-{
-    static final ConfigResolverContext NONE = new ConfigResolverContext();
-
-    static final ConfigResolverContext EVAL_VARIABLES = new 
ConfigResolverContext().setEvaluateVariables(true);
-
-    static final ConfigResolverContext PROJECTSTAGE = new 
ConfigResolverContext().setProjectStageAware(true);
-
-    static final ConfigResolverContext PROJECTSTAGE_EVAL_VARIABLES = new 
ConfigResolverContext()
-                                                                            
.setProjectStageAware(true)
-                                                                            
.setEvaluateVariables(true);
-
-    private boolean projectStageAware;
-    private boolean evaluateVariables;
-    private boolean propertyAware;
-
-    public ConfigResolverContext()
-    {
-    }
-    
-    public ConfigResolverContext setEvaluateVariables(final boolean 
evaluateVariables)
-    {
-        this.evaluateVariables = evaluateVariables;
-        return this;
-    }   
-    
-    public boolean isEvaluateVariables()
-    {
-        return evaluateVariables;
-    }
-
-    public ConfigResolverContext setProjectStageAware(final boolean 
projectStageAware)
-    {
-        this.projectStageAware = projectStageAware;
-        return this;
-    }
-    
-    public boolean isProjectStageAware()
-    {
-        return projectStageAware;
-    }
-
-    public ConfigResolverContext setPropertyAware(final boolean propertyAware)
-    {
-        this.propertyAware = propertyAware;
-        return this;
-    }
-    
-    public boolean isPropertyAware()
-    {
-        return propertyAware;
-    }
-}

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/5b07e197/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigImpl.java
----------------------------------------------------------------------
diff --git 
a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigImpl.java
 
b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigImpl.java
index 54c1ca9..6f60dda 100644
--- 
a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigImpl.java
+++ 
b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigImpl.java
@@ -1,156 +1,164 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.deltaspike.core.impl.config;
-
-import org.apache.deltaspike.core.api.config.Config;
-import org.apache.deltaspike.core.spi.config.ConfigFilter;
-import org.apache.deltaspike.core.spi.config.ConfigSource;
-import org.apache.deltaspike.core.spi.config.ConfigSourceProvider;
-import org.apache.deltaspike.core.util.ServiceUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * The internal implementation of the Config interface
- */
-public class ConfigImpl implements Config
-{
-    private static final Logger LOG = 
Logger.getLogger(ConfigImpl.class.getName());
-
-    private final ClassLoader classLoader;
-
-    private ConfigSource[] configSources;
-    private List<ConfigFilter> configFilters;
-
-    public ConfigImpl(ClassLoader classLoader)
-    {
-        this.classLoader = classLoader;
-    }
-
-    /**
-     * Performs all the initialisation of the default
-     * ConfigSources, ConfigFilters, etc
-     */
-    void init()
-    {
-        List<ConfigSource> appConfigSources
-            = ServiceUtils.loadServiceImplementations(ConfigSource.class, 
false, classLoader);
-
-        List<ConfigSourceProvider> configSourceProviderServiceLoader
-            = 
ServiceUtils.loadServiceImplementations(ConfigSourceProvider.class, false, 
classLoader);
-
-        for (ConfigSourceProvider configSourceProvider : 
configSourceProviderServiceLoader)
-        {
-            appConfigSources.addAll(configSourceProvider.getConfigSources());
-        }
-        addConfigSources(appConfigSources);
-
-        if (LOG.isLoggable(Level.FINE))
-        {
-            for (ConfigSource cs : appConfigSources)
-            {
-                LOG.log(Level.FINE, "Adding ordinal {0} ConfigSource {1}",
-                        new Object[]{cs.getOrdinal(), cs.getConfigName()});
-            }
-        }
-
-        List<ConfigFilter> configFilters
-            = ServiceUtils.loadServiceImplementations(ConfigFilter.class, 
false, classLoader);
-        this.configFilters = new CopyOnWriteArrayList<>(configFilters);
-    }
-
-
-    @Override
-    public ConfigSource[] getConfigSources()
-    {
-        return configSources;
-    }
-
-    @Override
-    public void addConfigSources(List<ConfigSource> configSourcesToAdd)
-    {
-        List<ConfigSource> allConfigSources = new ArrayList<>();
-        if (this.configSources != null)
-        {
-            for (ConfigSource configSource : this.configSources)
-            {
-                allConfigSources.add(configSource);
-            }
-        }
-
-        allConfigSources.addAll(configSourcesToAdd);
-
-        this.configSources = sortDescending(allConfigSources);
-    }
-
-    @Override
-    public List<ConfigFilter> getConfigFilters()
-    {
-        return Collections.unmodifiableList(configFilters);
-    }
-
-    @Override
-    public void addConfigFilter(ConfigFilter configFilter)
-    {
-        configFilters.add(configFilter);
-    }
-
-    @Override
-    public String filterConfigValue(String key, String value, boolean forLog)
-    {
-        String filteredValue = value;
-
-        for (ConfigFilter filter : configFilters)
-        {
-            filteredValue = forLog ?
-                    filter.filterValueForLog(key, filteredValue) :
-                    filter.filterValue(key, filteredValue);
-        }
-        return filteredValue;
-    }
-
-    private ConfigSource[] sortDescending(List<ConfigSource> configSources)
-    {
-        Collections.sort(configSources, new Comparator<ConfigSource>()
-        {
-            /**
-             * {@inheritDoc}
-             */
-            @Override
-            public int compare(ConfigSource configSource1, ConfigSource 
configSource2)
-            {
-                int o1 = configSource1.getOrdinal();
-                int o2 = configSource2.getOrdinal();
-                if (o1 == o2)
-                {
-                    return 
configSource1.getConfigName().compareTo(configSource2.getConfigName());
-                }
-                return (o1 > o2) ? -1 : 1;
-            }
-        });
-        return configSources.toArray(new ConfigSource[configSources.size()]);
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.core.impl.config;
+
+import org.apache.deltaspike.core.api.config.Config;
+import org.apache.deltaspike.core.api.config.ConfigResolver;
+import org.apache.deltaspike.core.spi.config.ConfigFilter;
+import org.apache.deltaspike.core.spi.config.ConfigSource;
+import org.apache.deltaspike.core.spi.config.ConfigSourceProvider;
+import org.apache.deltaspike.core.util.ServiceUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The internal implementation of the Config interface
+ */
+public class ConfigImpl implements Config
+{
+    private static final Logger LOG = 
Logger.getLogger(ConfigImpl.class.getName());
+
+    private final ClassLoader classLoader;
+
+    private ConfigSource[] configSources;
+    private List<ConfigFilter> configFilters;
+
+    public ConfigImpl(ClassLoader classLoader)
+    {
+        this.classLoader = classLoader;
+    }
+
+    /**
+     * Performs all the initialisation of the default
+     * ConfigSources, ConfigFilters, etc
+     */
+    void init()
+    {
+        List<ConfigSource> appConfigSources
+            = ServiceUtils.loadServiceImplementations(ConfigSource.class, 
false, classLoader);
+
+        List<ConfigSourceProvider> configSourceProviderServiceLoader
+            = 
ServiceUtils.loadServiceImplementations(ConfigSourceProvider.class, false, 
classLoader);
+
+        for (ConfigSourceProvider configSourceProvider : 
configSourceProviderServiceLoader)
+        {
+            appConfigSources.addAll(configSourceProvider.getConfigSources());
+        }
+        addConfigSources(appConfigSources);
+
+        if (LOG.isLoggable(Level.FINE))
+        {
+            for (ConfigSource cs : appConfigSources)
+            {
+                LOG.log(Level.FINE, "Adding ordinal {0} ConfigSource {1}",
+                        new Object[]{cs.getOrdinal(), cs.getConfigName()});
+            }
+        }
+
+        List<ConfigFilter> configFilters
+            = ServiceUtils.loadServiceImplementations(ConfigFilter.class, 
false, classLoader);
+        this.configFilters = new CopyOnWriteArrayList<>(configFilters);
+    }
+
+
+    @Override
+    public ConfigSource[] getConfigSources()
+    {
+        return configSources;
+    }
+
+    @Override
+    public void addConfigSources(List<ConfigSource> configSourcesToAdd)
+    {
+        List<ConfigSource> allConfigSources = new ArrayList<>();
+        if (this.configSources != null)
+        {
+            for (ConfigSource configSource : this.configSources)
+            {
+                allConfigSources.add(configSource);
+            }
+        }
+
+        allConfigSources.addAll(configSourcesToAdd);
+
+        this.configSources = sortDescending(allConfigSources);
+    }
+
+    @Override
+    public List<ConfigFilter> getConfigFilters()
+    {
+        return Collections.unmodifiableList(configFilters);
+    }
+
+    @Override
+    public void addConfigFilter(ConfigFilter configFilter)
+    {
+        configFilters.add(configFilter);
+    }
+
+    @Override
+    public String filterConfigValue(String key, String value, boolean forLog)
+    {
+        String filteredValue = value;
+
+        for (ConfigFilter filter : configFilters)
+        {
+            filteredValue = forLog ?
+                    filter.filterValueForLog(key, filteredValue) :
+                    filter.filterValue(key, filteredValue);
+        }
+        return filteredValue;
+    }
+
+
+    @Override
+    public ConfigResolver.UntypedResolver<String> resolve(String name)
+    {
+        return new TypedResolverImpl(this, name);
+    }
+
+    private ConfigSource[] sortDescending(List<ConfigSource> configSources)
+    {
+        Collections.sort(configSources, new Comparator<ConfigSource>()
+        {
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public int compare(ConfigSource configSource1, ConfigSource 
configSource2)
+            {
+                int o1 = configSource1.getOrdinal();
+                int o2 = configSource2.getOrdinal();
+                if (o1 == o2)
+                {
+                    return 
configSource1.getConfigName().compareTo(configSource2.getConfigName());
+                }
+                return (o1 > o2) ? -1 : 1;
+            }
+        });
+        return configSources.toArray(new ConfigSource[configSources.size()]);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/5b07e197/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigResolverContext.java
----------------------------------------------------------------------
diff --git 
a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigResolverContext.java
 
b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigResolverContext.java
new file mode 100644
index 0000000..4f3997c
--- /dev/null
+++ 
b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigResolverContext.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.core.impl.config;
+
+class ConfigResolverContext
+{
+    static final ConfigResolverContext NONE = new ConfigResolverContext();
+
+    static final ConfigResolverContext EVAL_VARIABLES = new 
ConfigResolverContext().setEvaluateVariables(true);
+
+    static final ConfigResolverContext PROJECTSTAGE = new 
ConfigResolverContext().setProjectStageAware(true);
+
+    static final ConfigResolverContext PROJECTSTAGE_EVAL_VARIABLES = new 
ConfigResolverContext()
+                                                                            
.setProjectStageAware(true)
+                                                                            
.setEvaluateVariables(true);
+
+    private boolean projectStageAware;
+    private boolean evaluateVariables;
+    private boolean propertyAware;
+
+    public ConfigResolverContext()
+    {
+    }
+    
+    public ConfigResolverContext setEvaluateVariables(final boolean 
evaluateVariables)
+    {
+        this.evaluateVariables = evaluateVariables;
+        return this;
+    }   
+    
+    public boolean isEvaluateVariables()
+    {
+        return evaluateVariables;
+    }
+
+    public ConfigResolverContext setProjectStageAware(final boolean 
projectStageAware)
+    {
+        this.projectStageAware = projectStageAware;
+        return this;
+    }
+    
+    public boolean isProjectStageAware()
+    {
+        return projectStageAware;
+    }
+
+    public ConfigResolverContext setPropertyAware(final boolean propertyAware)
+    {
+        this.propertyAware = propertyAware;
+        return this;
+    }
+    
+    public boolean isPropertyAware()
+    {
+        return propertyAware;
+    }
+}

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/5b07e197/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/TypedResolverImpl.java
----------------------------------------------------------------------
diff --git 
a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/TypedResolverImpl.java
 
b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/TypedResolverImpl.java
new file mode 100644
index 0000000..df36054
--- /dev/null
+++ 
b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/TypedResolverImpl.java
@@ -0,0 +1,565 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.core.impl.config;
+
+import org.apache.deltaspike.core.api.config.ConfigResolver;
+import org.apache.deltaspike.core.api.projectstage.ProjectStage;
+import org.apache.deltaspike.core.spi.config.ConfigSource;
+import org.apache.deltaspike.core.util.ClassUtils;
+import org.apache.deltaspike.core.util.ExceptionUtils;
+import org.apache.deltaspike.core.util.ProjectStageProducer;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+
+public class TypedResolverImpl<T> implements ConfigResolver.UntypedResolver<T>
+{
+    private static final Logger LOG = 
Logger.getLogger(TypedResolverImpl.class.getName());
+
+    private final ConfigImpl config;
+
+    private String keyOriginal;
+
+    private String keyResolved;
+
+    private Type configEntryType = String.class;
+
+    private boolean withDefault = false;
+    private T defaultValue;
+
+    private boolean projectStageAware = true;
+
+    private String propertyParameter;
+
+    private String parameterValue;
+
+    private boolean strictly = false;
+
+    private boolean isList = false;
+
+    private ConfigResolver.Converter<?> converter;
+
+    private boolean evaluateVariables = false;
+    private boolean logChanges = false;
+
+    private long cacheTimeMs = -1;
+    private volatile long reloadAfter = -1;
+    private T lastValue = null;
+
+
+    public TypedResolverImpl(ConfigImpl config, String propertyName)
+    {
+        this.config = config;
+        this.keyOriginal = propertyName;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <N> ConfigResolver.TypedResolver<N> as(Class<N> clazz)
+    {
+        configEntryType = clazz;
+        return (ConfigResolver.TypedResolver<N>) this;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public ConfigResolver.TypedResolver<List<T>> asList()
+    {
+        isList = true;
+        ConfigResolver.TypedResolver<List<T>> listTypedResolver = 
(ConfigResolver.TypedResolver<List<T>>) this;
+
+        if (defaultValue == null)
+        {
+            // the default for lists is an empty list instead of null
+            return listTypedResolver.withDefault(Collections.<T>emptyList());
+        }
+
+        return listTypedResolver;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <N> ConfigResolver.TypedResolver<N> as(Class<N> clazz, 
ConfigResolver.Converter<N> converter)
+    {
+        configEntryType = clazz;
+        this.converter = converter;
+
+        return (ConfigResolver.TypedResolver<N>) this;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <N> ConfigResolver.TypedResolver<N> as(Type clazz, 
ConfigResolver.Converter<N> converter)
+    {
+        configEntryType = clazz;
+        this.converter = converter;
+
+        return (ConfigResolver.TypedResolver<N>) this;
+    }
+
+    @Override
+    public ConfigResolver.TypedResolver<T> withDefault(T value)
+    {
+        defaultValue = value;
+        withDefault = true;
+        return this;
+    }
+
+    @Override
+    public ConfigResolver.TypedResolver<T> withStringDefault(String value)
+    {
+        if (value == null || value.isEmpty())
+        {
+            throw new RuntimeException("Empty String or null supplied as 
string-default value for property "
+                    + keyOriginal);
+        }
+
+        if (isList)
+        {
+            defaultValue = splitAndConvertListValue(value);
+        }
+        else
+        {
+            defaultValue = convert(value);
+        }
+        withDefault = true;
+        return this;
+    }
+
+    @Override
+    public ConfigResolver.TypedResolver<T> cacheFor(TimeUnit timeUnit, long 
value)
+    {
+        this.cacheTimeMs = timeUnit.toMillis(value);
+        return this;
+    }
+
+    @Override
+    public ConfigResolver.TypedResolver<T> parameterizedBy(String propertyName)
+    {
+        this.propertyParameter = propertyName;
+
+        if (propertyParameter != null && !propertyParameter.isEmpty())
+        {
+            String parameterValue = ConfigResolver
+                    .resolve(propertyParameter)
+                    .withCurrentProjectStage(projectStageAware)
+                    .getValue();
+
+            if (parameterValue != null && !parameterValue.isEmpty())
+            {
+                this.parameterValue = parameterValue;
+            }
+        }
+
+        return this;
+    }
+
+    @Override
+    public ConfigResolver.TypedResolver<T> withCurrentProjectStage(boolean 
with)
+    {
+        this.projectStageAware = with;
+        return this;
+    }
+
+    @Override
+    public ConfigResolver.TypedResolver<T> strictly(boolean strictly)
+    {
+        this.strictly = strictly;
+        return this;
+    }
+
+    @Override
+    public ConfigResolver.TypedResolver<T> evaluateVariables(boolean 
evaluateVariables)
+    {
+        this.evaluateVariables = evaluateVariables;
+        return this;
+    }
+
+    @Override
+    public ConfigResolver.TypedResolver<T> logChanges(boolean logChanges)
+    {
+        this.logChanges = logChanges;
+        return this;
+    }
+
+    @Override
+    public T getValue()
+    {
+        long now = -1;
+        if (cacheTimeMs > 0)
+        {
+            now = System.currentTimeMillis();
+            if (now <= reloadAfter)
+            {
+                return lastValue;
+            }
+        }
+
+        String valueStr = resolveStringValue();
+        T value;
+        if (isList)
+        {
+            value = splitAndConvertListValue(valueStr);
+        }
+        else
+        {
+            value = convert(valueStr);
+        }
+
+        if (withDefault)
+        {
+            ConfigResolverContext configResolverContext = new 
ConfigResolverContext()
+                    .setEvaluateVariables(evaluateVariables)
+                    .setProjectStageAware(projectStageAware);
+            value = fallbackToDefaultIfEmpty(keyResolved, value, defaultValue, 
configResolverContext);
+            if (isList && String.class.isInstance(value))
+            {
+                value = splitAndConvertListValue(String.class.cast(value));
+            }
+        }
+
+        if (logChanges && (value != null && !value.equals(lastValue) || (value 
== null && lastValue != null)))
+        {
+            LOG.log(Level.INFO, "New value {0} for key {1}.",
+                    new 
Object[]{ConfigResolver.filterConfigValueForLog(keyOriginal, valueStr), 
keyOriginal});
+        }
+
+        lastValue = value;
+
+        if (cacheTimeMs > 0)
+        {
+            reloadAfter = now + cacheTimeMs;
+        }
+
+        return value;
+    }
+
+    private T splitAndConvertListValue(String valueStr)
+    {
+        if (valueStr == null)
+        {
+            return null;
+        }
+
+        List list = new ArrayList();
+        StringBuilder currentValue = new StringBuilder();
+        int length = valueStr.length();
+        for (int i = 0; i < length; i++)
+        {
+            char c = valueStr.charAt(i);
+            if (c == '\\')
+            {
+                if (i < length - 1)
+                {
+                    char nextC = valueStr.charAt(i + 1);
+                    currentValue.append(nextC);
+                    i++;
+                }
+            }
+            else if (c == ',')
+            {
+                String trimedVal = currentValue.toString().trim();
+                if (trimedVal.length() > 0)
+                {
+                    list.add(convert(trimedVal));
+                }
+
+                currentValue.setLength(0);
+            }
+            else
+            {
+                currentValue.append(c);
+            }
+        }
+
+        String trimedVal = currentValue.toString().trim();
+        if (trimedVal.length() > 0)
+        {
+            list.add(convert(trimedVal));
+        }
+
+        return (T) list;
+    }
+
+    @Override
+    public String getKey()
+    {
+        return keyOriginal;
+    }
+
+    @Override
+    public String getResolvedKey()
+    {
+        return keyResolved;
+    }
+
+    @Override
+    public T getDefaultValue()
+    {
+        return defaultValue;
+    }
+
+    /**
+     * Performs the resolution cascade
+     */
+    private String resolveStringValue()
+    {
+        ProjectStage ps = null;
+        String value = null;
+        keyResolved = keyOriginal;
+        int keySuffices = 0;
+
+        // make the longest key
+        // first, try appending resolved parameter
+        if (propertyParameter != null && !propertyParameter.isEmpty())
+        {
+            if (parameterValue != null && !parameterValue.isEmpty())
+            {
+                keyResolved += "." + parameterValue;
+                keySuffices++;
+            }
+            // if parameter value can't be resolved and strictly
+            else if (strictly)
+            {
+                return null;
+            }
+        }
+
+        // try appending projectstage
+        if (projectStageAware)
+        {
+            ps = getProjectStage();
+            keyResolved += "." + ps;
+            keySuffices++;
+        }
+
+        // make initial resolution of longest key
+        value = getPropertyValue(keyResolved);
+
+        // try fallbacks if not strictly
+        if (value == null && !strictly)
+        {
+
+            // by the length of the longest resolved key already tried
+            // breaks are left out intentionally
+            switch (keySuffices)
+            {
+
+                case 2:
+                    // try base.param
+                    keyResolved = keyOriginal + "." + parameterValue;
+                    value = getPropertyValue(keyResolved);
+
+                    if (value != null)
+                    {
+                        return value;
+                    }
+
+                    // try base.ps
+                    ps = getProjectStage();
+                    keyResolved = keyOriginal + "." + ps;
+                    value = getPropertyValue(keyResolved);
+
+                    if (value != null)
+                    {
+                        return value;
+                    }
+
+                case 1:
+                    // try base
+                    keyResolved = keyOriginal;
+                    value = getPropertyValue(keyResolved);
+                    return value;
+
+                default:
+                    // the longest key was the base, no fallback
+                    return null;
+            }
+        }
+
+        return value;
+    }
+
+    /**
+     * If a converter was provided for this builder, it takes precedence over 
the built-in converters.
+     */
+    private T convert(String value)
+    {
+        if (value == null)
+        {
+            return null;
+        }
+
+        Object result = null;
+
+        if (this.converter != null)
+        {
+            try
+            {
+                result = converter.convert(value);
+            }
+            catch (Exception e)
+            {
+                throw ExceptionUtils.throwAsRuntimeException(e);
+            }
+        }
+        else if (String.class.equals(configEntryType))
+        {
+            result = value;
+        }
+        else if (Class.class.equals(configEntryType))
+        {
+            result = ClassUtils.tryToLoadClassForName(value);
+        }
+        else if (Boolean.class.equals(configEntryType))
+        {
+            Boolean isTrue = "TRUE".equalsIgnoreCase(value);
+            isTrue |= "1".equalsIgnoreCase(value);
+            isTrue |= "YES".equalsIgnoreCase(value);
+            isTrue |= "Y".equalsIgnoreCase(value);
+            isTrue |= "JA".equalsIgnoreCase(value);
+            isTrue |= "J".equalsIgnoreCase(value);
+            isTrue |= "OUI".equalsIgnoreCase(value);
+
+            result = isTrue;
+        }
+        else if (Integer.class.equals(configEntryType))
+        {
+            result = Integer.parseInt(value);
+        }
+        else if (Long.class.equals(configEntryType))
+        {
+            result = Long.parseLong(value);
+        }
+        else if (Float.class.equals(configEntryType))
+        {
+            result = Float.parseFloat(value);
+        }
+        else if (Double.class.equals(configEntryType))
+        {
+            result = Double.parseDouble(value);
+        }
+
+        return (T) result;
+    }
+
+    private <T> T fallbackToDefaultIfEmpty(String key, T value, T defaultValue,
+                                           ConfigResolverContext 
configResolverContext)
+    {
+        if (value == null || (value instanceof String && 
((String)value).isEmpty()))
+        {
+            if (configResolverContext != null && defaultValue instanceof String
+                    && configResolverContext.isEvaluateVariables())
+            {
+                defaultValue = (T) resolveVariables((String) defaultValue);
+            }
+
+            if (LOG.isLoggable(Level.FINE))
+            {
+                LOG.log(Level.FINE, "no configured value found for key {0}, 
using default value {1}.",
+                        new Object[]{key, defaultValue});
+            }
+
+            return defaultValue;
+        }
+
+        return value;
+    }
+
+    /**
+     * recursively resolve any ${varName} in the value
+     */
+    private String resolveVariables(String value)
+    {
+        int startVar = 0;
+        while ((startVar = value.indexOf("${", startVar)) >= 0)
+        {
+            int endVar = value.indexOf("}", startVar);
+            if (endVar <= 0)
+            {
+                break;
+            }
+            String varName = value.substring(startVar + 2, endVar);
+            if (varName.isEmpty())
+            {
+                break;
+            }
+
+            String variableValue = new TypedResolverImpl<String>(this.config, 
varName)
+                    .withCurrentProjectStage(this.projectStageAware)
+                    .evaluateVariables(true)
+                    .getValue();
+
+            if (variableValue != null)
+            {
+                value = value.replace("${" + varName + "}", variableValue);
+            }
+            startVar++;
+        }
+        return value;
+    }
+
+    private ProjectStage getProjectStage()
+    {
+        return ProjectStageProducer.getInstance().getProjectStage();
+    }
+
+    private String getPropertyValue(String key)
+    {
+        String value;
+        for (ConfigSource configSource : config.getConfigSources())
+        {
+            value = configSource.getPropertyValue(key);
+
+            if (value != null)
+            {
+                if (LOG.isLoggable(Level.FINE))
+                {
+                    LOG.log(Level.FINE, "found value {0} for key {1} in 
ConfigSource {2}.",
+                            new Object[]{config.filterConfigValue(key, value, 
true),
+                                key, configSource.getConfigName()});
+                }
+
+                if (this.evaluateVariables)
+                {
+                    value = resolveVariables(value);
+                }
+
+                return config.filterConfigValue(key, value, false);
+            }
+
+            if (LOG.isLoggable(Level.FINE))
+            {
+                LOG.log(Level.FINER, "NO value found for key {0} in 
ConfigSource {1}.",
+                        new Object[]{key, configSource.getConfigName()});
+            }
+        }
+
+        return null;
+    }
+
+
+}

Reply via email to