You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ro...@apache.org on 2022/04/07 21:56:06 UTC
[felix-dev] branch master updated: FELIX-6518 - Enable interpolation plugin to self reference properties
This is an automated email from the ASF dual-hosted git repository.
rotty3000 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 c233f6922d FELIX-6518 - Enable interpolation plugin to self reference properties
c233f6922d is described below
commit c233f6922db4adb758bd65529e41fab3ecffd5c8
Author: Raymond Augé <ro...@apache.org>
AuthorDate: Wed Apr 6 18:37:26 2022 -0400
FELIX-6518 - Enable interpolation plugin to self reference properties
Signed-off-by: Raymond Augé <ro...@apache.org>
---
configadmin-plugins/interpolation/README.md | 13 +++++-
.../InterpolationConfigurationPlugin.java | 50 ++++++++++++++++++--
.../InterpolationConfigurationPluginTest.java | 53 ++++++++++++++++++----
3 files changed, 99 insertions(+), 17 deletions(-)
diff --git a/configadmin-plugins/interpolation/README.md b/configadmin-plugins/interpolation/README.md
index c71165b78e..620b349174 100644
--- a/configadmin-plugins/interpolation/README.md
+++ b/configadmin-plugins/interpolation/README.md
@@ -11,8 +11,9 @@ Supported sources:
* Environment variables
* Framework properties
* System properties
+* Other properties within the same configuration (self references)
-## Usage with Secret Files
+## Interpolating Secret Files
Usually secrets (for example when provided by Kubernetes) will surface as files at a certain mount point, e.g.:
@@ -51,7 +52,7 @@ com.my.userinfo:
"greeting": "Hello $[env:USER]!"
```
-## Interpolating properties
+## Interpolating Framework/System properties
Properties can also be interpolated in the configuration. The properties values are
obtained through the `BundleContext.getProperty(key)` API, which will return the framework
@@ -60,6 +61,14 @@ with this key is returned.
Property values are obtained through the `$[prop:my.property]` syntax.
+## Interpolating configuration properties (self references)
+
+Interpolated values can also come from other values in the configuration itself.
+
+Configuration values are obtained through the `$[conf:my.property]` syntax.
+
+*Note:* Cycles are prevented and logged.
+
## Default Values
It is possible to specify a default value as part of the placeholder, for example:
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 e8d456a0ca..f2e7b67372 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
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
@@ -47,6 +48,8 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
private static final String TYPE_SECRET = "secret";
+ private static final String TYPE_CONF = "conf";
+
private static final String DIRECTIVE_TYPE = "type";
/** Delimiter for splitting up a single value into an array. */
@@ -126,7 +129,7 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
String key = keys.nextElement();
Object val = properties.get(key);
if (val instanceof String) {
- Object newVal = getNewValue(key, (String) val, pid);
+ Object newVal = getNewValue(key, (String) val, pid, properties);
if (newVal != null && !newVal.equals(val)) {
properties.put(key, newVal);
getLog().info("Replaced value of configuration property '{}' for PID {}", key, pid);
@@ -135,7 +138,7 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
String[] array = (String[]) val;
String[] newArray = null;
for (int i = 0; i < array.length; i++) {
- Object newVal = getNewValue(key, array[i], pid);
+ Object newVal = getNewValue(key, array[i], pid, properties);
if (newVal != null && !newVal.equals(array[i])) {
if (newArray == null) {
newArray = new String[array.length];
@@ -152,15 +155,15 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
}
}
- private Object getNewValue(final String key, final String value, final Object pid) {
- final Object result = replace(key, value, pid);
+ private Object getNewValue(final String key, final String value, final Object pid, final Dictionary<String, Object> properties) {
+ final Object result = replace(key, value, pid, properties);
if (value.equals(result)) {
return null;
}
return result;
}
- Object replace(final String key, final String value, final Object pid) {
+ Object replace(final String key, final String value, final Object pid, final Dictionary<String, Object> properties) {
final Object result = Interpolator.replace(value, (type, name, dir) -> {
String v = null;
if (TYPE_ENV.equals(type)) {
@@ -171,6 +174,9 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
} else if (TYPE_SECRET.equals(type)) {
v = getVariableFromFile(key, name, pid);
+
+ } else if (TYPE_CONF.equals(type)) {
+ v = getVariableFromConfiguration(name, properties);
}
if (v == null) {
v = dir.get(DIRECTIVE_DEFAULT);
@@ -183,6 +189,40 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
return result;
}
+ String getVariableFromConfiguration(final String name, final Dictionary<String, Object> properties) {
+ Object val = properties.get(name);
+ String result;
+ if (val.getClass().isArray()) {
+ if (val instanceof int[]) {
+ result = Arrays.toString((int[])val);
+ } else if (val instanceof long[]) {
+ result = Arrays.toString((long[])val);
+ } else if (val instanceof float[]) {
+ result = Arrays.toString((float[])val);
+ } else if (val instanceof double[]) {
+ result = Arrays.toString((double[])val);
+ } else if (val instanceof byte[]) {
+ result = Arrays.toString((byte[])val);
+ } else if (val instanceof short[]) {
+ result = Arrays.toString((short[])val);
+ } else if (val instanceof boolean[]) {
+ result = Arrays.toString((boolean[])val);
+ } else if (val instanceof char[]) {
+ result = Arrays.toString((char[])val);
+ } else {
+ result =Arrays.toString((Object[])val);
+ }
+ } else {
+ result = String.valueOf(val);
+ }
+ // prevent circular references
+ if (result.startsWith("$[conf:")) {
+ getLog().warn("There is a cycle in '{}' for PID {}, returning {}", name, properties.get(Constants.SERVICE_PID), name);
+ return name;
+ }
+ return result;
+ }
+
String getVariableFromEnvironment(final String name) {
return System.getenv(name);
}
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 e078fc1a98..4fdeab038d 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
@@ -30,6 +30,39 @@ import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
public class InterpolationConfigurationPluginTest {
+ @Test
+ public void testSelfReferenceConfiguration() throws Exception {
+ BundleContext bc = Mockito.mock(BundleContext.class);
+ Mockito.when(bc.getProperty("foo.bar")).thenReturn("hello there");
+
+ InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc::getProperty, null, null);
+
+ Dictionary<String, Object> dict = new Hashtable<>();
+ dict.put("someprop", "$[prop:foo.bar]");
+ dict.put("nope", "$[blah:blah]");
+ dict.put("self.reference", "$[conf:someprop] foo");
+ dict.put("long.array", new long[] {3L, 250000L});
+ dict.put("self.long.array", "$[conf:long.array]");
+ dict.put("string.array", new String[] {"foo", "bar"});
+ dict.put("self.string.array", "$[conf:string.array]");
+ dict.put("char.array", new char[] {'c', '香'});
+ dict.put("self.char.array", "$[conf:char.array]");
+ dict.put("self.circular.reference", "$[conf:self.circular.reference]");
+ dict.put("self.circular.reference.a", "$[conf:self.circular.reference.b]");
+ dict.put("self.circular.reference.b", "$[conf:self.circular.reference.a]");
+
+ plugin.modifyConfiguration(null, dict);
+ assertEquals("hello there", dict.get("someprop"));
+ assertEquals("$[blah:blah]", dict.get("nope"));
+ assertEquals("hello there foo", dict.get("self.reference"));
+ assertEquals("[3, 250000]", dict.get("self.long.array"));
+ assertEquals("[foo, bar]", dict.get("self.string.array"));
+ assertEquals("[c, 香]", dict.get("self.char.array"));
+ assertEquals("self.circular.reference", dict.get("self.circular.reference"));
+ assertEquals("self.circular.reference.a", dict.get("self.circular.reference.a"));
+ assertEquals("self.circular.reference.a", dict.get("self.circular.reference.b"));
+ }
+
@Test
public void testModifyConfiguration() throws Exception {
String envUser = System.getenv("USER");
@@ -119,9 +152,9 @@ public class InterpolationConfigurationPluginTest {
InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(null,
file.getParent() + "," + file.getParentFile().getParent(), null);
- assertEquals("xxla la layy", plugin.replace("akey", "xx$[secret:testfile.txt]yy", "apid"));
+ assertEquals("xxla la layy", plugin.replace("akey", "xx$[secret:testfile.txt]yy", "apid", new Hashtable<>()));
String doesNotReplace = "xx$[" + rf + "]yy";
- assertEquals(doesNotReplace, plugin.replace("akey", doesNotReplace, "apid"));
+ assertEquals(doesNotReplace, plugin.replace("akey", doesNotReplace, "apid", new Hashtable<>()));
}
@Test
@@ -131,7 +164,7 @@ public class InterpolationConfigurationPluginTest {
InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(null,
file.getParent() + "," + file.getParentFile().getParent(), null);
- assertEquals("foo", plugin.replace("akey", "foo", "apid"));
+ assertEquals("foo", plugin.replace("akey", "foo", "apid", new Hashtable<>()));
}
@Test
@@ -208,10 +241,10 @@ public class InterpolationConfigurationPluginTest {
file.getParent() + "," + file.getParentFile().getParent(), null);
assertEquals("xxhello thereyyhello therezz",
- plugin.replace("akey", "xx$[prop:foo.bar]yy$[prop:foo.bar]zz", "apid"));
+ plugin.replace("akey", "xx$[prop:foo.bar]yy$[prop:foo.bar]zz", "apid", new Hashtable<>()));
assertEquals("xxla la layyhello therezz",
- plugin.replace("akey", "xx$[secret:testfile.txt]yy$[prop:foo.bar]zz", "apid"));
+ plugin.replace("akey", "xx$[secret:testfile.txt]yy$[prop:foo.bar]zz", "apid", new Hashtable<>()));
}
@Test
@@ -223,10 +256,10 @@ public class InterpolationConfigurationPluginTest {
file.getParent() + "," + file.getParentFile().getParent(), null);
assertEquals("xxhello thereyyhello therezz",
- plugin.replace("akey", "xx$[secret:foo.bar]yy$[secret:foo.bar]zz", "apid"));
+ plugin.replace("akey", "xx$[secret:foo.bar]yy$[secret:foo.bar]zz", "apid", new Hashtable<>()));
assertEquals("xxla la layyhello therezz",
- plugin.replace("akey", "xx$[secret:testfile.txt]yy$[secret:foo.bar]zz", "apid"));
+ plugin.replace("akey", "xx$[secret:testfile.txt]yy$[secret:foo.bar]zz", "apid", new Hashtable<>()));
}
@Test
@@ -236,7 +269,7 @@ public class InterpolationConfigurationPluginTest {
Mockito.when(bc.getProperty("key")).thenReturn("foo.bar");
InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc::getProperty, null, null);
- assertEquals("hello there", plugin.replace("akey", "$[prop:$[prop:key]]", "apid"));
+ assertEquals("hello there", plugin.replace("akey", "$[prop:$[prop:key]]", "apid", new Hashtable<>()));
}
@Test
@@ -256,9 +289,9 @@ public class InterpolationConfigurationPluginTest {
InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc::getProperty, null, null);
assertArrayEquals(new Integer[] { 2000, 3000 },
- (Integer[]) plugin.replace("key", "$[prop:foo;type=Integer[];delimiter=,]", "somepid"));
+ (Integer[]) plugin.replace("key", "$[prop:foo;type=Integer[];delimiter=,]", "somepid", new Hashtable<>()));
assertArrayEquals(new Integer[] { 1, 2 },
- (Integer[]) plugin.replace("key", "$[prop:bar;type=Integer[];delimiter=,;default=1,2]", "somepid"));
+ (Integer[]) plugin.replace("key", "$[prop:bar;type=Integer[];delimiter=,;default=1,2]", "somepid", new Hashtable<>()));
}
@Test