You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cz...@apache.org on 2020/02/27 11:12:00 UTC

svn commit: r1874568 - in /felix/trunk/configadmin-plugins/interpolation: ./ src/main/java/org/apache/felix/configadmin/plugin/interpolation/ src/test/java/org/apache/felix/configadmin/plugin/interpolation/

Author: cziegeler
Date: Thu Feb 27 11:11:59 2020
New Revision: 1874568

URL: http://svn.apache.org/viewvc?rev=1874568&view=rev
Log:
FELIX-6225 : Support splitting up value into array

Modified:
    felix/trunk/configadmin-plugins/interpolation/README.md
    felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java
    felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java

Modified: felix/trunk/configadmin-plugins/interpolation/README.md
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin-plugins/interpolation/README.md?rev=1874568&r1=1874567&r2=1874568&view=diff
==============================================================================
--- felix/trunk/configadmin-plugins/interpolation/README.md (original)
+++ felix/trunk/configadmin-plugins/interpolation/README.md Thu Feb 27 11:11:59 2020
@@ -7,9 +7,9 @@ An OSGi Configuration Admin Plugin that
 * Framework properties
 * System properties
 
-## Usage with Kubernetes secrets
+## Usage with Secret Files
 
-The Kubernetes secrets will surface as file at a certain mountpoint, e.g.:
+Usually secrets (for example when provided by Kubernetes) will surface as files at a certain mount point, e.g.:
 
 ```
 $ ls /mnt/mysecrets
@@ -55,12 +55,51 @@ with this key is returned.
 
 Property values are obtained through the `$[prop:my.property]` syntax.
 
+## Default Values
+
+It is possible to specify a default value as part of the placeholder, for example:
+
+```
+"port" : "$[env:PORT;default=8080]"
+```
+
+Without a default, the placeholder is left in the value if no value can be found. With a default, the default is used instead.
+
+## Type Support
+
+A placeholder can contain additional information like the type the value should be converted to.
+
+
+```
+"port" : "$[env:PORT;type=Integer]"
+```
+
+In the above example, an Integer object with the port will be put into the Configuration instead of a String value.
+
+### Supported Scalar and Primitive Types
+
+The following types are supported: String, Integer, int, Long, long, Float, float, Double, double, Byte, byte, Short, short, Character, char, Boolean, boolean.
+
+### Supported Array Types
+
+The following array types are supported: String[], Integer[], int[], Long[], long[], Float[], float[], Double[], double[], Byte[], byte[], Short[], short[], Character[], char[], Boolean[], boolean[].
+
+A provided value (including the default value) can be split up into a string array before conversion by configuring a delimiter as part of the placeholder:
+
+```
+"ports" : "$[env:PORT;type=Integer[];delimiter=,;default=8080,8081]"
+```
+
 ## Configuration of the plugin
 
+The plugin (and Configuration Admin) can be controlled by various properties. These properties are
+framework properties and can be provided on initialization of the OSGi framework or as system properties
+as framework properties default to system properties.
+
 ### Consistent processing
 
-It is recommended to configure the ConfigAdmin to only start processing once this plugin is active. In case of
-the Felix ConfigAdmin implementation, this can be achieved by using the following property:
+It is recommended to configure the Configuration Admin to only start processing once this plugin is active. In case of
+the Apache Felix ConfigAdmin implementation, this can be achieved by using the following property:
 
 * `felix.cm.config.plugins`: `org.apache.felix.configadmin.plugin.interpolation`
 
@@ -73,6 +112,11 @@ This is done through the following prope
 
 * `org.apache.felix.configadmin.plugin.interpolation.secretsdir`: specify the directory where the files used for the file-based interpolation, such as Kubernetes secrets, are mounted.
 
-The property can be provided as an OSGi Framework property or alternatively as a Java System Property. 
-
 If the property is not present, the plugin will function, but without being able to replace values based on secrets.
+
+### File Encoding
+
+When reading files, for example secrets, the platform default encoding is used. The following property can be used to to control the reading:
+
+* `org.apache.felix.configadmin.plugin.interpolation.file.encoding` : specify the encoding to be used.
+

Modified: felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java?rev=1874568&r1=1874567&r2=1874568&view=diff
==============================================================================
--- felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java (original)
+++ felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java Thu Feb 27 11:11:59 2020
@@ -20,9 +20,12 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.osgi.framework.BundleContext;
@@ -42,6 +45,9 @@ class InterpolationConfigurationPlugin i
 
     private static final String DIRECTIVE_TYPE = "type";
 
+    /** Delimiter for splitting up a single value into an array. */
+    private static final String DIRECTIVE_DELIMITER = "delimiter";
+
     private static final String DIRECTIVE_DEFAULT = "default";
 
     private static final Map<String, Class<?>> TYPE_MAP = new HashMap<>();
@@ -166,7 +172,7 @@ class InterpolationConfigurationPlugin i
                 v = dir.get(DIRECTIVE_DEFAULT);
             }
             if (v != null && dir.containsKey(DIRECTIVE_TYPE)) {
-                return convertType(dir.get(DIRECTIVE_TYPE), v);
+                return convertType(dir.get(DIRECTIVE_TYPE), v, dir.get(DIRECTIVE_DELIMITER));
             }
             return v;
         });
@@ -216,17 +222,87 @@ class InterpolationConfigurationPlugin i
         return new String(bytes, this.encodingCharset).trim();
     }
 
-    private Object convertType(String type, String s) {
+    /**
+     * Convert the value to the given type
+     *
+     * @param type      The type (optional)
+     * @param value     The value
+     * @param delimiter The delimiter for array types (optional)
+     * @return The converted value
+     */
+    Object convertType(final String type, final String value, final String delimiter) {
         if (type == null) {
-            return s;
+            return value;
         }
 
         Class<?> cls = TYPE_MAP.get(type);
         if (cls != null) {
-            return Converters.standardConverter().convert(s).to(cls);
+            if ((cls.isArray() || Collection.class.isAssignableFrom(cls)) && delimiter != null) {
+                final String[] array = split(value, delimiter);
+                return Converters.standardConverter().convert(array).to(cls);
+            }
+            return Converters.standardConverter().convert(value).to(cls);
         }
 
         getLog().warn("Cannot convert to type: " + type);
-        return s;
+        return value;
+    }
+
+    /**
+     * Split a string into an array of strings. A backslash can be used to escape
+     * the delimiter (avoiding splitting), unless that backslash is preceded by
+     * another backslash, in which case the two backslashes are replaced by a single
+     * one and the value is split after the backslash.
+     * 
+     * @param value     The value to split
+     * @param delimiter The delimiter
+     * @return The resulting array
+     */
+    String[] split(String value, final String delimiter) {
+        List<String> result = null;
+        int start = -1;
+        while (start < value.length()) {
+            start = value.indexOf(delimiter, start + 1);
+            if (start == -1) {
+                // no delimiter found -> end
+                start = value.length();
+                if (result != null) {
+                    result.add(value);
+                }
+            } else {
+
+                boolean split = true;
+                if (start > 1 && value.charAt(start - 1) == Interpolator.ESCAPE) {
+                    if (start == 1 || value.charAt(start - 2) != Interpolator.ESCAPE) {
+                        split = false;
+                    } else if (value.charAt(start - 2) == Interpolator.ESCAPE) {
+                        value = value.substring(0, start - 2).concat(value.substring(start - 1));
+                        start--;
+                    }
+                }
+
+                if (split) {
+                    if (result == null) {
+                        result = new ArrayList<String>();
+                    }
+                    result.add(value.substring(0, start));
+                    value = value.substring(start + delimiter.length());
+                    start = -1;
+                } else {
+                    if (start == 1) {
+                        value = value.substring(1);
+                    } else {
+                        value = value.substring(0, start - 1).concat(value.substring(start));
+                        start--;
+                    }
+                }
+            }
+
+        }
+
+        if (result == null) {
+            return new String[] { value };
+        }
+        return result.toArray(new String[result.size()]);
     }
 }

Modified: felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java?rev=1874568&r1=1874567&r2=1874568&view=diff
==============================================================================
--- felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java (original)
+++ felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java Thu Feb 27 11:11:59 2020
@@ -16,6 +16,7 @@
  */
 package org.apache.felix.configadmin.plugin.interpolation;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
 import java.io.File;
@@ -197,4 +198,26 @@ public class InterpolationConfigurationP
 
         assertEquals("hello there", plugin.replace("akey", "$[prop:$[prop:key]]", "apid"));
     }
+
+    @Test
+    public void testArraySplit() throws Exception {
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(null, null, null);
+
+        assertArrayEquals(new String[] { "a,b,c" }, plugin.split("a,b,c", "."));
+        assertArrayEquals(new String[] { "a", "b", "c" }, plugin.split("a,b,c", ","));
+        assertArrayEquals(new String[] { "a,b", "c" }, plugin.split("a\\,b,c", ","));
+        assertArrayEquals(new String[] { "a\\", "b", "c" }, plugin.split("a\\\\,b,c", ","));
+    }
+
+    @Test
+    public void testArrayTypeConversion() throws Exception {
+        BundleContext bc = Mockito.mock(BundleContext.class);
+        Mockito.when(bc.getProperty("foo")).thenReturn("2000,3000");
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc, null, null);
+
+        assertArrayEquals(new Integer[] { 2000, 3000 },
+                (Integer[]) plugin.replace("key", "$[prop:foo;type=Integer[];delimiter=,]", "somepid"));
+        assertArrayEquals(new Integer[] { 1, 2 },
+                (Integer[]) plugin.replace("key", "$[prop:bar;type=Integer[];delimiter=,;default=1,2]", "somepid"));
+    }
 }