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; + } + + +}