[FELIX-2663] Allow ConfigInstaller to perform placeholder substitution from framework properties
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1027382 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/ConfigInstaller.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/ConfigInstaller.java
index e386d6f..31e63d8 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/ConfigInstaller.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/ConfigInstaller.java
@@ -175,7 +175,7 @@
} else {
p.load(in);
}
- InterpolationHelper.performSubstitution((Map) p);
+ InterpolationHelper.performSubstitution((Map) p, context);
ht.putAll(p);
}
else if ( f.getName().endsWith( ".config" ) )
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java
index aa9818d..c17628b 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java
@@ -187,7 +187,7 @@
public void updated(String pid, Dictionary properties)
{
- InterpolationHelper.performSubstitution(new DictionaryAsMap(properties));
+ InterpolationHelper.performSubstitution(new DictionaryAsMap(properties), context);
DirectoryWatcher watcher = null;
synchronized (watchers)
{
diff --git a/utils/src/main/java/org/apache/felix/utils/properties/InterpolationHelper.java b/utils/src/main/java/org/apache/felix/utils/properties/InterpolationHelper.java
index 36d3c7f..c9c210f 100644
--- a/utils/src/main/java/org/apache/felix/utils/properties/InterpolationHelper.java
+++ b/utils/src/main/java/org/apache/felix/utils/properties/InterpolationHelper.java
@@ -16,6 +16,8 @@
*/
package org.apache.felix.utils.properties;
+import org.osgi.framework.BundleContext;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -55,10 +57,20 @@
*/
public static void performSubstitution(Map<String,String> properties)
{
+ performSubstitution(properties, null);
+ }
+
+ /**
+ * Perform substitution on a property set
+ *
+ * @param properties the property set to perform substitution on
+ */
+ public static void performSubstitution(Map<String,String> properties, BundleContext context)
+ {
for (String name : properties.keySet())
{
String value = properties.get(name);
- properties.put(name, substVars(value, name, null, properties));
+ properties.put(name, substVars(value, name, null, properties, context));
}
}
@@ -79,11 +91,16 @@
* detect cycles.
* @param cycleMap Map of variable references used to detect nested cycles.
* @param configProps Set of configuration properties.
+ * @param context the bundle context to retrieve properties from
* @return The value of the specified string after system property substitution.
* @throws IllegalArgumentException If there was a syntax error in the
* property placeholder syntax or a recursive variable reference.
**/
- public static String substVars(String val, String currentKey, Map<String,String> cycleMap, Map<String,String> configProps)
+ public static String substVars(String val,
+ String currentKey,
+ Map<String,String> cycleMap,
+ Map<String,String> configProps,
+ BundleContext context)
throws IllegalArgumentException
{
if (cycleMap == null)
@@ -147,8 +164,22 @@
String substValue = (String) ((configProps != null) ? configProps.get(variable) : null);
if (substValue == null)
{
- // Ignore unknown property values.
- substValue = variable.length() > 0 ? System.getProperty(variable, "") : "";
+ if (variable.length() <= 0)
+ {
+ substValue = "";
+ }
+ else if (context != null)
+ {
+ substValue = context.getProperty(variable);
+ if (substValue == null)
+ {
+ substValue = "";
+ }
+ }
+ else
+ {
+ substValue = System.getProperty(variable, "");
+ }
}
// Remove the found variable from the cycle map, since
@@ -163,7 +194,7 @@
// Now perform substitution again, since there could still
// be substitutions to make.
- val = substVars(val, currentKey, cycleMap, configProps);
+ val = substVars(val, currentKey, cycleMap, configProps, context);
// Remove escape characters preceding {, } and \
val = unescape(val);
diff --git a/utils/src/test/java/org/apache/felix/utils/properties/InterpolationHelperTest.java b/utils/src/test/java/org/apache/felix/utils/properties/InterpolationHelperTest.java
index 5e4dcc4..f4ea8ea 100644
--- a/utils/src/test/java/org/apache/felix/utils/properties/InterpolationHelperTest.java
+++ b/utils/src/test/java/org/apache/felix/utils/properties/InterpolationHelperTest.java
@@ -23,6 +23,12 @@
public class InterpolationHelperTest extends TestCase {
+ private MockBundleContext context;
+
+ protected void setUp() throws Exception {
+ context = new MockBundleContext();
+ }
+
public void testBasicSubstitution()
{
System.setProperty("value1", "sub_value1");
@@ -34,7 +40,7 @@
for (Enumeration e = props.keys(); e.hasMoreElements();)
{
String name = (String) e.nextElement();
- props.put(name, InterpolationHelper.substVars((String) props.get(name), name, null, props));
+ props.put(name, InterpolationHelper.substVars((String) props.get(name), name, null, props, context));
}
assertEquals("value0", props.get("key0"));
@@ -43,26 +49,53 @@
}
+ public void testBasicSubstitutionWithContext()
+ {
+ System.setProperty("value1", "sub_value1");
+ System.setProperty("value2", "sub_value2");
+ context.setProperty("value3", "context_value1");
+ context.setProperty("value2", "context_value2");
+
+ Hashtable props = new Hashtable();
+ props.put("key0", "value0");
+ props.put("key1", "${value1}");
+ props.put("key2", "${value2}");
+ props.put("key3", "${value3}");
+
+ for (Enumeration e = props.keys(); e.hasMoreElements();)
+ {
+ String name = (String) e.nextElement();
+ props.put(name,
+ InterpolationHelper.substVars((String) props.get(name), name, null, props, context));
+ }
+
+ assertEquals("value0", props.get("key0"));
+ assertEquals("sub_value1", props.get("key1"));
+ assertEquals("context_value2", props.get("key2"));
+ assertEquals("context_value1", props.get("key3"));
+
+ }
+
public void testSubstitutionFailures()
{
- assertEquals("a}", InterpolationHelper.substVars("a}", "b", null, new Hashtable()));
- assertEquals("${a", InterpolationHelper.substVars("${a", "b", null, new Hashtable()));
+ assertEquals("a}", InterpolationHelper.substVars("a}", "b", null, new Hashtable(), context));
+ assertEquals("${a", InterpolationHelper.substVars("${a", "b", null, new Hashtable(), context));
}
public void testEmptyVariable() {
- assertEquals("", InterpolationHelper.substVars("${}", "b", null, new Hashtable()));
+ assertEquals("", InterpolationHelper.substVars("${}", "b", null, new Hashtable(), context));
}
public void testInnerSubst() {
Hashtable props = new Hashtable();
props.put("a", "b");
props.put("b", "c");
- assertEquals("c", InterpolationHelper.substVars("${${a}}", "z", null, props));
+ assertEquals("c", InterpolationHelper.substVars("${${a}}", "z", null, props, context));
}
public void testSubstLoop() {
try {
- InterpolationHelper.substVars("${a}", "a", null, new Hashtable());
+ InterpolationHelper.substVars("${a}", "a", null, new Hashtable(), context);
fail("Should have thrown an exception");
} catch (IllegalArgumentException e) {
// expected
@@ -71,9 +104,9 @@
public void testSubstitutionEscape()
{
- assertEquals("${a}", InterpolationHelper.substVars("$\\{a${#}\\}", "b", null, new Hashtable()));
- assertEquals("${a}", InterpolationHelper.substVars("$\\{a\\}${#}", "b", null, new Hashtable()));
- assertEquals("${a}", InterpolationHelper.substVars("$\\{a\\}", "b", null, new Hashtable()));
+ assertEquals("${a}", InterpolationHelper.substVars("$\\{a${#}\\}", "b", null, new Hashtable(), context));
+ assertEquals("${a}", InterpolationHelper.substVars("$\\{a\\}${#}", "b", null, new Hashtable(), context));
+ assertEquals("${a}", InterpolationHelper.substVars("$\\{a\\}", "b", null, new Hashtable(), context));
}
}
diff --git a/utils/src/test/java/org/apache/felix/utils/properties/MockBundleContext.java b/utils/src/test/java/org/apache/felix/utils/properties/MockBundleContext.java
new file mode 100644
index 0000000..7a69b5e
--- /dev/null
+++ b/utils/src/test/java/org/apache/felix/utils/properties/MockBundleContext.java
@@ -0,0 +1,134 @@
+/*
+ * 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.utils.properties;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.Dictionary;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+public class MockBundleContext implements BundleContext {
+ private Properties properties = new Properties();
+
+ public void setProperty(String name, String value) {
+ this.properties.setProperty(name, value);
+ }
+ public String getProperty(String name) {
+ String value = this.properties.getProperty(name);
+ if (value == null) {
+ value = System.getProperty(name);
+ }
+ return value;
+ }
+
+ public Bundle getBundle() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Bundle installBundle(String s) throws BundleException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Bundle installBundle(String s, InputStream stream) throws BundleException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Bundle getBundle(long l) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Bundle[] getBundles() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addServiceListener(ServiceListener listener, String s) throws InvalidSyntaxException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addServiceListener(ServiceListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void removeServiceListener(ServiceListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addBundleListener(BundleListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void removeBundleListener(BundleListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addFrameworkListener(FrameworkListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void removeFrameworkListener(FrameworkListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ServiceRegistration registerService(String[] strings, Object o, Dictionary dictionary) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ServiceRegistration registerService(String s, Object o, Dictionary dictionary) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ServiceReference[] getServiceReferences(String s, String s1) throws InvalidSyntaxException {
+ throw new UnsupportedOperationException();
+ }
+
+ public ServiceReference[] getAllServiceReferences(String s, String s1) throws InvalidSyntaxException {
+ throw new UnsupportedOperationException();
+ }
+
+ public ServiceReference getServiceReference(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object getService(ServiceReference reference) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean ungetService(ServiceReference reference) {
+ throw new UnsupportedOperationException();
+ }
+
+ public File getDataFile(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Filter createFilter(String s) throws InvalidSyntaxException {
+ throw new UnsupportedOperationException();
+ }
+}