You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by da...@apache.org on 2021/08/03 08:29:01 UTC

[felix-dev] branch master updated: FELIX-6441 Make it possible to run the Configuration Interpolation independently of Configuration Admin

This is an automated email from the ASF dual-hosted git repository.

davidb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git


The following commit(s) were added to refs/heads/master by this push:
     new bbadfa2  FELIX-6441 Make it possible to run the Configuration Interpolation independently of Configuration Admin
     new 67ab32b  Merge pull request #83 from bosschaert/FELIX-6441-sq
bbadfa2 is described below

commit bbadfa28bd856c6bfe3e8183514b4fed2bd78284
Author: David Bosschaert <da...@apache.org>
AuthorDate: Fri Jul 30 15:24:41 2021 +0100

    FELIX-6441 Make it possible to run the Configuration Interpolation independently of Configuration Admin
    
    The new StandaloneInterpolator class provides the entry-point for using
    the interpolator outside of a Configuration Admin environment.
---
 configadmin-plugins/interpolation/pom.xml          |   2 +-
 .../plugin/interpolation/Activator.java            |   2 +-
 .../InterpolationConfigurationPlugin.java          |  11 +-
 .../interpolation/StandaloneInterpolator.java      |  80 +++++++++++++
 .../InterpolationConfigurationPluginTest.java      |  12 +-
 .../interpolation/StandaloneInterpolatorTest.java  | 124 +++++++++++++++++++++
 .../interpolation/src/test/resources/res1/my.db    |   1 +
 7 files changed, 218 insertions(+), 14 deletions(-)

diff --git a/configadmin-plugins/interpolation/pom.xml b/configadmin-plugins/interpolation/pom.xml
index 10563be..203a7ba 100644
--- a/configadmin-plugins/interpolation/pom.xml
+++ b/configadmin-plugins/interpolation/pom.xml
@@ -28,7 +28,7 @@
 
     <artifactId>org.apache.felix.configadmin.plugin.interpolation</artifactId>
     <packaging>jar</packaging>
-    <version>1.1.5-SNAPSHOT</version>
+    <version>1.2.0-SNAPSHOT</version>
 
     <name>Apache Felix Configuration Admin Values Interpolation Plugin</name>
     <description>
diff --git a/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Activator.java b/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Activator.java
index c538c7d..c3fcbe9 100644
--- a/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Activator.java
+++ b/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Activator.java
@@ -51,7 +51,7 @@ public class Activator implements BundleActivator {
         }
         String encoding = context.getProperty(ENCODING_PROPERTY);
 
-        ConfigurationPlugin plugin = new InterpolationConfigurationPlugin(context, directory, encoding);
+        ConfigurationPlugin plugin = new InterpolationConfigurationPlugin(context::getProperty, directory, encoding);
         Dictionary<String, Object> props = new Hashtable<>();
         props.put(ConfigurationPlugin.CM_RANKING, PLUGIN_RANKING);
         props.put("config.plugin.id", PLUGIN_ID);
diff --git a/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java b/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java
index 4974848..2dae521 100644
--- a/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java
+++ b/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java
@@ -30,10 +30,9 @@ import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
+import java.util.function.Function;
 import java.util.stream.Stream;
 
-import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.cm.ConfigurationPlugin;
@@ -95,13 +94,13 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
         TYPE_MAP.put("char[]", char[].class);
     }
 
-    private final BundleContext context;
+    private final Function<String, String> propertiesProvider;
     private final List<File> directory;
 
     private final Charset encodingCharset;
 
-    InterpolationConfigurationPlugin(BundleContext bc, String dir, String fileEncoding) {
-        context = bc;
+    InterpolationConfigurationPlugin(Function<String, String> pp, String dir, String fileEncoding) {
+        propertiesProvider = pp;
         if (dir != null) {
             directory = Stream.of(dir.split("\\s*,\\s*")).map(File::new).collect(toList());
             getLog().info("Configured directory for secrets: {}", dir);
@@ -189,7 +188,7 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
     }
 
     String getVariableFromProperty(final String name) {
-        return context.getProperty(name);
+        return propertiesProvider.apply(name);
     }
 
     String getVariableFromFile(final String key, final String name, final Object pid) {
diff --git a/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/StandaloneInterpolator.java b/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/StandaloneInterpolator.java
new file mode 100644
index 0000000..7facda1
--- /dev/null
+++ b/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/StandaloneInterpolator.java
@@ -0,0 +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.felix.configadmin.plugin.interpolation;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.osgi.framework.Constants;
+
+/**
+ * Entrypoint into the interpolator independent of the OSGi API, so it can be used from outside of an
+ * OSGi Configuration Admin environment.
+ */
+public class StandaloneInterpolator {
+    final InterpolationConfigurationPlugin plugin;
+
+    /**
+     * Constructor.
+     *
+     * @param frameworkProperties Properties to use for framework property substitutions.
+     * @param secretsLocations The directories where secrets files can be found. The platform default
+     * encoding will be used for these files.
+     */
+    public StandaloneInterpolator(Map<String,String> frameworkProperties, File ... secretsLocations) {
+        this(frameworkProperties, null, secretsLocations);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param frameworkProperties Properties to use for framework property substitutions.
+     * @param encoding The file encoding to be used for the files in the secrets locations.
+     * @param secretsLocations The directories where secrets files can be found.
+     */
+    public StandaloneInterpolator(Map<String,String> frameworkProperties, String encoding, File ... secretsLocations) {
+        String locations = Arrays.asList(secretsLocations).stream()
+                .map(File::toString)
+                .collect(Collectors.joining(","));
+        plugin = new InterpolationConfigurationPlugin(frameworkProperties::get, locations, encoding);
+    }
+
+    /**
+     * Perform configuration interpolations.
+     *
+     * @param pid The PID of the configuration.
+     * @param dict The dictionary containing the configuration properties. The dictionary will be updated
+     * by the interpolation substitutions.
+     */
+    public void interpolate(String pid, Dictionary<String, Object> dict) {
+        boolean pidAdded = false;
+        try {
+            if (dict.get(Constants.SERVICE_PID) == null) {
+                dict.put(Constants.SERVICE_PID, pid);
+                pidAdded = true;
+            }
+
+            plugin.modifyConfiguration(null, dict);
+        } finally {
+            if (pidAdded)
+                dict.remove(Constants.SERVICE_PID);
+        }
+    }
+}
diff --git a/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java b/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java
index 888f0a5..8a402e8 100644
--- a/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java
+++ b/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java
@@ -69,7 +69,7 @@ public class InterpolationConfigurationPluginTest {
         BundleContext bc = Mockito.mock(BundleContext.class);
         Mockito.when(bc.getProperty("foo.bar")).thenReturn("hello there");
 
-        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc, null, null);
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc::getProperty, null, null);
 
         String envUser = System.getenv("USER");
         String userVar;
@@ -171,7 +171,7 @@ public class InterpolationConfigurationPluginTest {
     public void testReplacementInStringArray() throws IOException {
         BundleContext bc = Mockito.mock(BundleContext.class);
         Mockito.when(bc.getProperty("foo.bar")).thenReturn("hello there");
-        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc, null, null);
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc::getProperty, null, null);
 
         Dictionary<String, Object> dict = new Hashtable<>();
         dict.put("array", new String[] { "1", "$[prop:foo.bar]", "3" });
@@ -191,7 +191,7 @@ public class InterpolationConfigurationPluginTest {
         Mockito.when(bc.getProperty("foo.bar")).thenReturn("hello there");
         String rf = getClass().getResource("/other/testfile.txt").getFile();
         File file = new File(rf);
-        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc,
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc::getProperty,
                 file.getParent() + "," + file.getParentFile().getParent(), null);
 
         assertEquals("xxhello thereyyhello therezz",
@@ -206,7 +206,7 @@ public class InterpolationConfigurationPluginTest {
         BundleContext bc = Mockito.mock(BundleContext.class);
         String rf = getClass().getResource("/other/testfile.txt").getFile();
         File file = new File(rf);
-        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc,
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc::getProperty,
                 file.getParent() + "," + file.getParentFile().getParent(), null);
 
         assertEquals("xxhello thereyyhello therezz",
@@ -221,7 +221,7 @@ public class InterpolationConfigurationPluginTest {
         BundleContext bc = Mockito.mock(BundleContext.class);
         Mockito.when(bc.getProperty("foo.bar")).thenReturn("hello there");
         Mockito.when(bc.getProperty("key")).thenReturn("foo.bar");
-        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc, null, null);
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc::getProperty, null, null);
 
         assertEquals("hello there", plugin.replace("akey", "$[prop:$[prop:key]]", "apid"));
     }
@@ -240,7 +240,7 @@ public class InterpolationConfigurationPluginTest {
     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);
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc::getProperty, null, null);
 
         assertArrayEquals(new Integer[] { 2000, 3000 },
                 (Integer[]) plugin.replace("key", "$[prop:foo;type=Integer[];delimiter=,]", "somepid"));
diff --git a/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/StandaloneInterpolatorTest.java b/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/StandaloneInterpolatorTest.java
new file mode 100644
index 0000000..be250b3
--- /dev/null
+++ b/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/StandaloneInterpolatorTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.felix.configadmin.plugin.interpolation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Test;
+import org.osgi.framework.Constants;
+
+public class StandaloneInterpolatorTest {
+    @Test
+    public void testFrameworkPropertyInterpolation() {
+        Map<String, String> fprops = new HashMap<>();
+        fprops.put("my.prop", "12345");
+        fprops.put("my.other.prop", "ABCDE");
+
+        StandaloneInterpolator interpolator = new StandaloneInterpolator(fprops);
+
+        Dictionary<String, Object> dict = new Hashtable<>();
+        dict.put("foo", "bar");
+        dict.put("nonsubst", "$[yeah:yeah]");
+        dict.put("prop", "$[prop:my.prop]");
+        interpolator.interpolate("org.foo.bar", dict);
+
+        assertEquals(3, Collections.list(dict.keys()).size());
+        assertEquals("bar", dict.get("foo"));
+        assertEquals("$[yeah:yeah]", dict.get("nonsubst"));
+        assertEquals("12345", dict.get("prop"));
+    }
+
+    @Test
+    public void testEnvVarInterpolation() {
+        String envUser = System.getenv("USER");
+        String userVar;
+        if (envUser == null) {
+            envUser = System.getenv("USERNAME"); // maybe we're on Windows
+            userVar = "USERNAME";
+        } else {
+            userVar = "USER";
+        }
+
+        StandaloneInterpolator interpolator = new StandaloneInterpolator(Collections.emptyMap());
+
+        Dictionary<String, Object> dict = new Hashtable<>();
+        dict.put("someuser", "$[env:" + userVar + "]");
+        dict.put(Constants.SERVICE_PID, "org.foo.bar");
+        interpolator.interpolate("org.foo.bar", dict);
+
+        assertEquals(envUser, dict.get("someuser"));
+    }
+
+    @Test
+    public void testSecretInterpolation() {
+        URL resUrl = getClass().getResource("/res1");
+        File res1Dir = new File(resUrl.getFile());
+        File res0Dir = new File(res1Dir.getParentFile(), "res0");
+
+        StandaloneInterpolator interpolator = new StandaloneInterpolator(Collections.emptyMap(), res0Dir, res1Dir);
+
+        Dictionary<String, Object> dict = new Hashtable<>();
+        dict.put("name", "$[secret:my.db]");
+        dict.put("pass", "$[secret:my.pwd]");
+        interpolator.interpolate("my.pid", dict);
+
+        assertEquals(2, dict.size());
+        assertEquals("tiger", dict.get("name"));
+        assertEquals("$[secret:my.pwd]", dict.get("pass"));
+    }
+
+    @Test
+    public void testSecretInterpolationEncoding() {
+        URL resUrl = getClass().getResource("/res1");
+        File res1Dir = new File(resUrl.getFile());
+        File res0Dir = new File(res1Dir.getParentFile(), "res0");
+
+        StandaloneInterpolator interpolator = new StandaloneInterpolator(Collections.emptyMap(), "UTF-8", res1Dir, res0Dir);
+
+        Dictionary<String, Object> dict = new Hashtable<>();
+        dict.put("name", "$[secret:my.db]");
+        dict.put("pass", "$[secret:my.pwd]");
+        interpolator.interpolate("my.pid", dict);
+
+        assertEquals("tiger", dict.get("name"));
+    }
+
+    @Test
+    public void testSecretInterpolationEncoding2() {
+        URL resUrl = getClass().getResource("/res1");
+        File res1Dir = new File(resUrl.getFile());
+
+        StandaloneInterpolator interpolator = new StandaloneInterpolator(Collections.emptyMap(), "UTF-16", res1Dir);
+
+        Dictionary<String, Object> dict = new Hashtable<>();
+        dict.put("name", "$[secret:my.db]");
+        dict.put("pass", "$[secret:my.pwd]");
+        interpolator.interpolate("my.pid", dict);
+
+        assertNotEquals("tiger", dict.get("name"));
+        assertNotEquals("$[secret:my.db]", dict.get("name"));
+    }
+}
diff --git a/configadmin-plugins/interpolation/src/test/resources/res1/my.db b/configadmin-plugins/interpolation/src/test/resources/res1/my.db
new file mode 100644
index 0000000..5a2c44b
--- /dev/null
+++ b/configadmin-plugins/interpolation/src/test/resources/res1/my.db
@@ -0,0 +1 @@
+tiger
\ No newline at end of file