I've investigated this a bit more. There are actually two different
problems:
a) The number of escape characters I need depends on from where I reference
the variable. For every indirection I need to double the number of
backslashes. This also means that all uses of a variable containing escape
characters must be used from the same level of indirection. A bit
complicated but it's due to the fact that all variables are evaluated
dynamically. This means that unescaping can occur several times.
b) FileInstall incorrectly thinks that a configuration property is changed
and therefore overwrites the property with the evaluated value.
I think I've found the reason (and possibly a solution) to b).
In the ConfigInstaller.setConfig() method the properties are read from a
configuration file and propagated as a configuration. Here is an excerpt
from that method:
* final Properties p = new Properties();*
* in.mark(1);*
* boolean isXml = in.read() == '<';*
* in.reset();*
* if (isXml) {*
* p.loadFromXML(in);*
* } else {*
* p.load(in);*
* }*
* InterpolationHelper.performSubstitution((Map) p, context);*
* ht.putAll(p);*
Note that the file is read using Java's standard Properties class. The
unescaping is also done by that class. Then, at the end, the variable
substitution is done as a separate call.
Then look at the ConfigInstaller.configurationEvent() method:
* if (configurationEvent.getType() == ConfigurationEvent.CM_UPDATED)*
* {*
* try*
* {*
* Configuration config =
getConfigurationAdmin().getConfiguration(*
* configurationEvent.getPid(),*
*
configurationEvent.getFactoryPid());*
* Dictionary dict = config.getProperties();*
* String fileName = (String) dict.get(
DirectoryWatcher.FILENAME );*
* File file = fileName != null ? fromConfigKey(fileName) :
null;*
* if( file != null && file.isFile() ) {*
* if( fileName.endsWith( ".cfg" ) )*
* {*
* org.apache.felix.utils.properties.Properties props
= new org.apache.felix.utils.properties.Properties( file, context );*
Note that now the configuration file is read using
org.apache.felix.utils.properties.Properties class. It turns out that they
don't produce identical results. I haven't investigated exactly how they
differ but they do.
A simple test:
1. Create a configuration file with the following content:
a=$\\\\{var}
ab=${a}b
abc=${ab}c
2. Add the following line at the end:
d=foo
3. FileInstall will now incorrectly change the contents of the
configuration file to:
a=$\\\\{var}
ab=${a}b
abc = ${var}bc
d=foo
Now if I change the ConfigInstaller.setConfig() method to the following:
*org.apache.felix.utils.properties.Properties p = new
org.apache.felix.utils.properties.Properties( f, context );*
*InterpolationHelper.performSubstitution((Map) p, context);*
Then FileInstall will not incorrectly change the contents of the
configuration file.
I propose to do this change in order to solve problem b) above. I
appreciate if you have any thoughts on this.
I realize that problem a) is trickier due to the dynamic nature of variable
substitution. I haven't yet determined how I think the escape characters
should be handled but the current situation is not ideal.
/Bengt
2013/11/28 Bengt Rodehav <[email protected]>
> JIRA created:
>
> https://issues.apache.org/jira/browse/FELIX-4332
>
> /Bengt
>
>
> 2013/11/28 Bengt Rodehav <[email protected]>
>
>> I've come up with easily reproducable errors using Karaf 2.3.3:
>>
>> - Install a fresh Karaf 2.3.3
>> - Add the following line to etc/custom.properties:
>> felix.fileinstall.enableConfigSave = true
>>
>> Create a file etc/test.cfg with the following contents:
>>
>> a=$\\{var}
>> ab=${a}b
>> abc=${ab}c
>>
>> I expect this to be evaluated to:
>> a=$\{var}
>> ab=$\{var}b
>> abc=$\{var}bc
>>
>> But if I execute the Karaf command:
>>
>> config:list "(service.pid=test)"
>>
>> I get:
>>
>> ----------------------------------------------------------------
>> Pid: test
>> BundleLocation: null
>> Properties:
>> service.pid = test
>> a = ${var}
>> abc = bc
>> felix.fileinstall.filename =
>> file:/C:/dev/Karaf/apache-karaf-2.3.3/etc/test.cfg
>> ab = b
>>
>> My interpretation of this is that the variable "a" has been correctly
>> evaluated. But, when evalutating the variable "ab" it seems that the
>> variable "a" is evaluated again despite the fact that it has already been
>> evaluated. FileInstall now looks for the value of a variable called "var"
>> which evalutes to an empty string because there is no such variable.
>>
>> The variable "abc" consequently evaluates to "bc" since the variable "ab"
>> has been evaluated to "b".
>>
>> To make it even worse, now change the first row in test.cfg to:
>>
>> a=$\\\\{var}
>>
>> We now get:
>>
>> ----------------------------------------------------------------
>> Pid: test
>> BundleLocation: null
>> Properties:
>> service.pid = test
>> a = $\{var}
>> abc = ${var}bc
>> felix.fileinstall.filename =
>> file:/C:/dev/Karaf/apache-karaf-2.3.3/etc/test.cfg
>> ab = ${var}b
>>
>> Thus we get the same phenomenom. The variable "a" is evaluated
>> differently if it is evaluated on its own or as part of another expression.
>> But, due to having configured FileInstall to write back changes, the
>> contents of the test.cfg is now changed by FileInstall despite the fact
>> that the configuration has not changed at all. The contents of test.cfg is
>> now:
>>
>> a=$\\\\{var}
>> ab=${a}b
>> abc = ${var}bc
>>
>> The "abc" variable has been altered. FileInstall has incorrectly
>> determined that its value has changed.
>>
>> This is clearly a bug. I will create a JIRA.
>>
>> /Bengt
>>
>>
>>
>> 2013/11/26 Bengt Rodehav <[email protected]>
>>
>>> I'm using Apache Karaf 2.3.3 which comes with FileInstall 3.2.6. I have
>>> set the felix.fileinstall.enableConfigSave property to true in order to
>>> have FileInstall write back configuration changes to the file. Normally all
>>> configuration changes are done by editing the configuration file but there
>>> is one property that I change programmatically using ConfigAdmin (an
>>> "enable" property to start/stop my service). I am dependent on that
>>> property being persisted in the configuration file which is why I set the
>>> enableConfigSave property to true.
>>>
>>> When configuring FileInstall to write back configuration changes to the
>>> configuration file, it is important that variables are not substituted for
>>> the evaluated value. This normally works since FileInstall evalutates the
>>> property in the configuration file and compares it with the configuration
>>> admin's value. If they are the same, the value in the configuration file is
>>> kept unchanged.
>>>
>>> However, when using the escape character this is broken. In my case I'm
>>> using Apache Camel underneath. When configuring routes via the config
>>> admin, I sometimes need to set a value to
>>> "${expression-to-be-evaluated-by-camel}". I therefore escape the "{" and
>>> "}" to stop FileInstall from trying to evaluate the expression. Like this:
>>>
>>> $\\{expression-to-be-evaluated-by-camel\\}
>>>
>>> This also normally works but not when I have an indirection. E g when
>>> specifying the following:
>>>
>>> a=$\\{var}
>>> ab=${a}b
>>>
>>> FileInstall will change the configuration file to:
>>>
>>> a=$\\{var}
>>> ab = ${var}b
>>>
>>> Note that the variable "ab" has now been expanded and written back to
>>> the configuration file even if neither of the variables "a" and "ab" have
>>> been changed.
>>>
>>> I think this is because FileInstall does the following:
>>>
>>> 1. Calculates the value of "a" to "$\{var}
>>> 2. Calculates the value of "b" to "${var}b
>>>
>>> Note that every evaluation will perform "unescaping". This means that an
>>> extra "unescaping" will be done for every indirection which fools
>>> FileInstall into thinking that the property has been changed.
>>>
>>> I'm not exactly sure how this should be fixed in FileInstall. One idea
>>> is to never "unescape" already evaluated variables. Actually I think this
>>> is probably what would fix this...
>>>
>>> Does anybody have any ideas about this? Should I create a JIRA?
>>>
>>> /Bengt
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>
>