You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2017/02/05 18:46:59 UTC

logging-log4j2 git commit: [LOG4J2-1809]: Add global configuration environment SPI

Repository: logging-log4j2
Updated Branches:
  refs/heads/LOG4J2-1431 bd587cf4e -> a9bcf176c


[LOG4J2-1809]: Add global configuration environment SPI

This adds a PropertySource service provider interface for providing
global configuration properties. This also refactors support in
LOG4J2-1431 to use these PropertySource providers.


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/a9bcf176
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/a9bcf176
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/a9bcf176

Branch: refs/heads/LOG4J2-1431
Commit: a9bcf176cc154fadae2a464c9743788809cf8ae6
Parents: bd587cf
Author: Matt Sicker <bo...@gmail.com>
Authored: Sun Feb 5 12:45:19 2017 -0600
Committer: Matt Sicker <bo...@gmail.com>
Committed: Sun Feb 5 12:45:19 2017 -0600

----------------------------------------------------------------------
 .../log4j/util/EnvironmentPropertySource.java   |  55 +++++++++
 .../log4j/util/PropertiesPropertySource.java    |  54 ++++++++
 .../logging/log4j/util/PropertiesUtil.java      | 123 ++++---------------
 .../log4j/util/PropertyFilePropertySource.java  |  52 ++++++++
 .../logging/log4j/util/PropertySource.java      | 115 +++++++++++++++++
 .../util/SystemPropertiesPropertySource.java    |  48 ++++++++
 ...org.apache.logging.log4j.util.PropertySource |  16 +++
 7 files changed, 367 insertions(+), 96 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java
new file mode 100644
index 0000000..d413d61
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java
@@ -0,0 +1,55 @@
+/*
+ * 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.logging.log4j.util;
+
+import java.util.Map;
+
+/**
+ * PropertySource implementation that uses environment variables as a source. All environment variables must begin
+ * with {@code LOG4J_} so as not to conflict with other variables. Normalized environment variables follow a scheme
+ * like this: {@code log4j2.fooBarProperty} would normalize to {@code LOG4J_FOO_BAR_PROPERTY}.
+ *
+ * @since 2.9
+ */
+public class EnvironmentPropertySource implements PropertySource {
+    @Override
+    public int getPriority() {
+        return -100;
+    }
+
+    @Override
+    public void forEach(final BiConsumer<String, String> action) {
+        for (final Map.Entry<String, String> entry : System.getenv().entrySet()) {
+            final String key = entry.getKey();
+            if (key.startsWith("LOG4J_")) {
+                action.accept(key.substring(6), entry.getValue());
+            }
+        }
+    }
+
+    @Override
+    public CharSequence getNormalForm(final Iterable<? extends CharSequence> tokens) {
+        final StringBuilder sb = new StringBuilder("LOG4J");
+        for (final CharSequence token : tokens) {
+            sb.append('_');
+            for (int i = 0; i < token.length(); i++) {
+                sb.append(Character.toUpperCase(token.charAt(i)));
+            }
+        }
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java
new file mode 100644
index 0000000..7622509
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java
@@ -0,0 +1,54 @@
+/*
+ * 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.logging.log4j.util;
+
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * PropertySource backed by a {@link Properties} instance. Normalized property names follow a scheme like this:
+ * {@code Log4jContextSelector} would normalize to {@code log4j2.contextSelector}.
+ *
+ * @since 2.9
+ */
+public class PropertiesPropertySource implements PropertySource {
+
+    private static final String PREFIX = "log4j2.";
+
+    private final Properties properties;
+
+    public PropertiesPropertySource(final Properties properties) {
+        this.properties = properties;
+    }
+
+    @Override
+    public int getPriority() {
+        return 0;
+    }
+
+    @Override
+    public void forEach(final BiConsumer<String, String> action) {
+        for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
+            action.accept(((String) entry.getKey()), ((String) entry.getValue()));
+        }
+    }
+
+    @Override
+    public CharSequence getNormalForm(final Iterable<? extends CharSequence> tokens) {
+        return PREFIX + Util.joinAsCamelCase(tokens);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
index c57d1f9..1b26b38 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
@@ -18,15 +18,16 @@ package org.apache.logging.log4j.util;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URL;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * <em>Consider this class private.</em>
@@ -47,7 +48,7 @@ public final class PropertiesUtil {
      * @param props the Properties to use by default
      */
     public PropertiesUtil(final Properties props) {
-        this.environment = new Environment(props);
+        this.environment = new Environment(new PropertiesPropertySource(props));
     }
 
     /**
@@ -57,15 +58,7 @@ public final class PropertiesUtil {
      * @param propertiesFileName the location of properties file to load
      */
     public PropertiesUtil(final String propertiesFileName) {
-        final Properties properties = new Properties();
-        for (final URL url : LoaderUtil.findResources(propertiesFileName)) {
-            try (final InputStream in = url.openStream()) {
-                properties.load(in);
-            } catch (final IOException ioe) {
-                LowLevelLogUtil.logException("Unable to read " + url.toString(), ioe);
-            }
-        }
-        this.environment = new Environment(properties);
+        this.environment = new Environment(new PropertyFilePropertySource(propertiesFileName));
     }
 
     /**
@@ -256,93 +249,31 @@ public final class PropertiesUtil {
      */
     private static class Environment {
 
-        private static final String NORMALIZED_PREFIX = "log4j2.";
-        private static final String PREFIXES = "(?:[Ll]og4j2?|org\\.apache\\.logging\\.log4j\\.)?";
-        private static final Pattern PROPERTY_TOKENIZER = Pattern.compile(PREFIXES + "([A-Z]*[a-z0-9]+)[-._/]?");
-
-        private final Map<String, String> literal = new ConcurrentHashMap<>();
-        private final Map<String, String> normalized = new ConcurrentHashMap<>();
+        private final Map<CharSequence, String> literal = new ConcurrentHashMap<>();
+        private final Map<CharSequence, String> normalized = new ConcurrentHashMap<>();
         private final Map<List<CharSequence>, String> tokenized = new ConcurrentHashMap<>();
 
-        private Environment(final Properties props) {
-            for (final Map.Entry<String, String> entry : System.getenv().entrySet()) {
-                final String envKey = entry.getKey();
-                final String value = entry.getValue();
-                if (envKey.startsWith("LOG4J_")) {
-                    final String key = envKey.substring(6);
-                    literal.put(key, value);
-                    final List<CharSequence> tokens = tokenize(envKey);
-                    if (!tokens.isEmpty()) {
-                        tokenized.put(tokens, value);
-                        normalized.put("LOG4J" + joinAsAllCapsUnderscored(tokens), value);
-                    }
-                }
-            }
-            for (final Map.Entry<Object, Object> entry : props.entrySet()) {
-                final String key = (String) entry.getKey();
-                final String value = (String) entry.getValue();
-                literal.put(key, value);
-                final List<CharSequence> tokens = tokenize(key);
-                if (tokens.isEmpty()) {
-                    normalized.put(NORMALIZED_PREFIX + key, value);
-                } else {
-                    tokenized.put(tokens, value);
-                    normalized.put(NORMALIZED_PREFIX + joinAsCamelCase(tokens), value);
-                }
-
-            }
-            final Properties systemProperties = getSystemProperties();
-            for (Map.Entry<Object, Object> entry : systemProperties.entrySet()) {
-                final String key = (String) entry.getKey();
-                final String value = (String) entry.getValue();
-                literal.put(key, value);
-                // TODO: should this include tokenized lookups? could get screwy
-                final List<CharSequence> tokens = tokenize(key);
-                if (tokens.isEmpty()) {
-                    normalized.put(NORMALIZED_PREFIX + key, value);
-                } else {
-                    tokenized.put(tokens, value);
-                    normalized.put(NORMALIZED_PREFIX + joinAsCamelCase(tokens), value);
-                }
-            }
-        }
-
-        private static CharSequence joinAsAllCapsUnderscored(final List<CharSequence> tokens) {
-            final StringBuilder sb = new StringBuilder();
-            for (final CharSequence token : tokens) {
-                sb.append('_');
-                for (int i = 0; i < token.length(); i++) {
-                    sb.append(Character.toUpperCase(token.charAt(i)));
-                }
+        private Environment(final PropertySource propertySource) {
+            final Set<PropertySource> sources = new TreeSet<>(new PropertySource.Comparator());
+            sources.add(propertySource);
+            for (final PropertySource source : ServiceLoader.load(PropertySource.class)) {
+                sources.add(source);
             }
-            return sb.toString();
-        }
-
-        private static CharSequence joinAsCamelCase(final List<CharSequence> tokens) {
-            final StringBuilder sb = new StringBuilder();
-            boolean first = true;
-            for (final CharSequence token : tokens) {
-                if (first) {
-                    sb.append(token);
-                } else {
-                    sb.append(Character.toUpperCase(token.charAt(0)));
-                    if (token.length() > 1) {
-                        sb.append(token.subSequence(1, token.length()));
+            for (final PropertySource source : sources) {
+                source.forEach(new BiConsumer<String, String>() {
+                    @Override
+                    public void accept(final String key, final String value) {
+                        literal.put(key, value);
+                        final List<CharSequence> tokens = PropertySource.Util.tokenize(key);
+                        if (tokens.isEmpty()) {
+                            normalized.put(source.getNormalForm(Collections.singleton(key)), value);
+                        } else {
+                            normalized.put(source.getNormalForm(tokens), value);
+                            tokenized.put(tokens, value);
+                        }
                     }
-                }
-                first = false;
-            }
-            return sb.toString();
-        }
-
-        private static List<CharSequence> tokenize(final CharSequence value) {
-            // TODO: cache?
-            List<CharSequence> tokens = new ArrayList<>();
-            final Matcher matcher = PROPERTY_TOKENIZER.matcher(value);
-            while (matcher.find()) {
-                tokens.add(matcher.group(1).toLowerCase());
+                });
             }
-            return tokens;
         }
 
         private String get(final String key) {
@@ -361,7 +292,7 @@ public final class PropertiesUtil {
             if (prop != null) {
                 return prop;
             }
-            return tokenized.get(tokenize(key));
+            return tokenized.get(PropertySource.Util.tokenize(key));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java
new file mode 100644
index 0000000..2920460
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java
@@ -0,0 +1,52 @@
+/*
+ * 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.logging.log4j.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Properties;
+
+/**
+ * PropertySource backed by a properties file. Follows the same conventions as {@link PropertiesPropertySource}.
+ *
+ * @since 2.9
+ */
+public class PropertyFilePropertySource extends PropertiesPropertySource {
+
+    public PropertyFilePropertySource(final String fileName) {
+        super(loadPropertiesFile(fileName));
+    }
+
+    private static Properties loadPropertiesFile(final String fileName) {
+        final Properties props = new Properties();
+        for (final URL url : LoaderUtil.findResources(fileName)) {
+            try (final InputStream in = url.openStream()) {
+                props.load(in);
+            } catch (IOException e) {
+                LowLevelLogUtil.logException("Unable to read " + url, e);
+            }
+        }
+        return props;
+    }
+
+    @Override
+    public int getPriority() {
+        return 0;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java
new file mode 100644
index 0000000..4dd9cf3
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertySource.java
@@ -0,0 +1,115 @@
+package org.apache.logging.log4j.util;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A source for global configuration properties.
+ *
+ * @since 2.9
+ */
+public interface PropertySource {
+
+    /**
+     * Returns the order in which this PropertySource has priority. A higher value means that the source will be
+     * applied later so as to take precedence over other property sources.
+     *
+     * @return priority value
+     */
+    int getPriority();
+
+    /**
+     * Iterates over all properties and performs an action for each key/value pair.
+     *
+     * @param action action to perform on each key/value pair
+     */
+    void forEach(BiConsumer<String, String> action);
+
+    /**
+     * Converts a list of property name tokens into a normal form. For example, a list of tokens such as
+     * "foo", "bar", "baz", might be normalized into the property name "log4j2.fooBarBaz".
+     *
+     * @param tokens list of property name tokens
+     * @return a normalized property name using the given tokens
+     */
+    CharSequence getNormalForm(Iterable<? extends CharSequence> tokens);
+
+    /**
+     * Comparator for ordering PropertySource instances by priority.
+     *
+     * @since 2.9
+     */
+    class Comparator implements java.util.Comparator<PropertySource>, Serializable {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public int compare(final PropertySource o1, final PropertySource o2) {
+            return Integer.compare(Objects.requireNonNull(o1).getPriority(), Objects.requireNonNull(o2).getPriority());
+        }
+    }
+
+    /**
+     * Utility methods useful for PropertySource implementations.
+     *
+     * @since 2.9
+     */
+    final class Util {
+        private static final String PREFIXES = "(?:[Ll]og4j2?|org\\.apache\\.logging\\.log4j\\.)?";
+        private static final Pattern PROPERTY_TOKENIZER = Pattern.compile(PREFIXES + "([A-Z]*[a-z0-9]+)[-._/]?");
+        private static final Map<CharSequence, List<CharSequence>> CACHE = new ConcurrentHashMap<>();
+
+        /**
+         * Converts a property name string into a list of tokens. This will strip a prefix of {@code log4j},
+         * {@code log4j2}, {@code Log4j}, or {@code org.apache.logging.log4j}, along with separators of
+         * dash {@code -}, dot {@code .}, underscore {@code _}, and slash {@code /}. Tokens can also be separated
+         * by camel case conventions without needing a separator character in between.
+         *
+         * @param value property name
+         * @return the property broken into lower case tokens
+         */
+        public static List<CharSequence> tokenize(final CharSequence value) {
+            if (CACHE.containsKey(value)) {
+                return CACHE.get(value);
+            }
+            List<CharSequence> tokens = new ArrayList<>();
+            final Matcher matcher = PROPERTY_TOKENIZER.matcher(value);
+            while (matcher.find()) {
+                tokens.add(matcher.group(1).toLowerCase());
+            }
+            CACHE.put(value, tokens);
+            return tokens;
+        }
+
+        /**
+         * Joins a list of strings using camelCaseConventions.
+         *
+         * @param tokens tokens to convert
+         * @return tokensAsCamelCase
+         */
+        public static CharSequence joinAsCamelCase(final Iterable<? extends CharSequence> tokens) {
+            final StringBuilder sb = new StringBuilder();
+            boolean first = true;
+            for (final CharSequence token : tokens) {
+                if (first) {
+                    sb.append(token);
+                } else {
+                    sb.append(Character.toUpperCase(token.charAt(0)));
+                    if (token.length() > 1) {
+                        sb.append(token.subSequence(1, token.length()));
+                    }
+                }
+                first = false;
+            }
+            return sb.toString();
+        }
+
+        private Util() {
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java
new file mode 100644
index 0000000..8733f23
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java
@@ -0,0 +1,48 @@
+/*
+ * 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.logging.log4j.util;
+
+import java.util.Map;
+
+/**
+ * PropertySource backed by the current system properties. Other than having a higher priority over normal properties,
+ * this follows the same rules as {@link PropertiesPropertySource}.
+ *
+ * @since 2.9
+ */
+public class SystemPropertiesPropertySource implements PropertySource {
+
+    private static final String PREFIX = "log4j2.";
+
+    @Override
+    public int getPriority() {
+        return 100;
+    }
+
+    @Override
+    public void forEach(final BiConsumer<String, String> action) {
+        for (final Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
+            action.accept(((String) entry.getKey()), ((String) entry.getValue()));
+        }
+    }
+
+    @Override
+    public CharSequence getNormalForm(final Iterable<? extends CharSequence> tokens) {
+        return PREFIX + Util.joinAsCamelCase(tokens);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a9bcf176/log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource b/log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
new file mode 100644
index 0000000..39c959c
--- /dev/null
+++ b/log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
@@ -0,0 +1,16 @@
+# 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.
+org.apache.logging.log4j.util.EnvironmentPropertySource
+org.apache.logging.log4j.util.SystemPropertiesPropertySource
\ No newline at end of file