[
https://issues.apache.org/jira/browse/KARAF-6074?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17728285#comment-17728285
]
Grzegorz Grzybek commented on KARAF-6074:
-----------------------------------------
The {{~}} is something new in Config Admin spec 1.6 (CMPN R7) and is related to
new methods in {{org.osgi.service.cm.ConfigurationAdmin}} interface:
{code:java}
org.osgi.service.cm.ConfigurationAdmin.getFactoryConfiguration(String
factoryPid, String name, String location)
{code}.
Javadoc says:
{quote}
The PID for this Configuration object is generated from the provided factory
PID and the name by starting with the factory PID appending a tilde ('~'
\u007E), and then appending the name.
{quote}
And this is exactly what
{{org.apache.felix.cm.impl.ConfigurationAdminImpl#getFactoryConfiguration(java.lang.String,
java.lang.String)}} is doing (felix.configadmin 1.9.26:
{code:java}
final String pid = factoryPid + '~' + name;
{code}
FactoryPid itself should _not_ contain a dash, although the spec doesn't say
anything (?) about it. At least I didn't find anything.
felix.fileinstall's
{{org.apache.felix.fileinstall.internal.ConfigInstaller#parsePid()}} has:
{code:java}
String pid = path.substring(0, path.lastIndexOf('.'));
int n = pid.indexOf('-');
if (n > 0)
{
String factoryPid = pid.substring(n + 1);
pid = pid.substring(0, n);
return new String[]
{
pid, factoryPid
};
}
else
{
return new String[]
{
pid, null
};
}
{code}
So for:
{code:xml}
<config name="org.apache.karaf.example.config-abc">
{code}
the related configuration file should be
{{etc/org.apache.karaf.example.config-abc.cfg}} and felix.fileinstall would
create:
{code:java}
new String[] { "org.apache.karaf.example.config", "abc" }
{code}
then
{{org.apache.felix.fileinstall.internal.ConfigInstaller#getConfiguration()}}
would detect ({{pid[1] != null}}) that this new method from cm 1.6 should be
called with these parameters
{code:java}
org.osgi.service.cm.ConfigurationAdmin#getFactoryConfiguration("org.apache.karaf.example.config",
"abc", "?")
{code}
Then pid would be {{org.apache.karaf.example.config~abc}} and if the config is
new, this method would be called with these parameters:
{code:java}
org.apache.felix.cm.impl.ConfigurationManager#createFactoryConfiguration("org.apache.karaf.example.config~abc",
"org.apache.karaf.example.config", "?")
{code}
When config.xml file is dropped to {{deploy/}}, the first relevant method
called is
{{org.apache.karaf.features.internal.service.FeatureConfigInstaller#installFeatureConfigs()}}
- no configadmin or fileinstall is involved yet.
* ConfigId#pid is set to {{org.apache.karaf.example.config-abc}}
* n == 31 because of {{pid.contains("~") ? pid.indexOf('~') : pid.indexOf('-')}}
* ConfigId#isFactoryPid is set to true
* ConfigId#factoryPid is set to {{org.apache.karaf.example.config}}
* ConfigId#name stays null, because there's no {{~}} and this may be a problem
cfgFile is set to
{{/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg}}
(_full_ pid)
Because {{ConfigId#name}} is null,
{{org.osgi.service.cm.ConfigurationAdmin#createFactoryConfiguration(factoryPid,
location)}} is called instead of
{{org.osgi.service.cm.ConfigurationAdmin#getFactoryConfiguration(factoryPid,
name, location)}}.
org.apache.felix.cm.impl.ConfigurationManager#createPid() returns random PID
based on factory PID. In my case:
{{org.apache.karaf.example.config.11346460-6c07-4dcb-b1e9-8b6e71bda0b9}}
in {{org.apache.felix.cm.impl.ConfigurationImpl#ConfigurationImpl()}}:
{noformat}
this.basePID: {org.apache.felix.cm.impl.helper.TargetedPID@8022}
"org.apache.karaf.example.config.11346460-6c07-4dcb-b1e9-8b6e71bda0b9"
this.factoryPID = {org.apache.felix.cm.impl.helper.TargetedPID@8023}
"org.apache.karaf.example.config"
{noformat}
{{org.apache.felix.cm.impl.ConfigurationImpl#storeNewConfiguration()}} is not
called (because it's factory pid).
Karaf's FeatureConfigInstaller continues to operate on:
{noformat}
cfg = {org.apache.felix.cm.impl.ConfigurationAdapter@8033} "Configuration
PID=org.apache.karaf.example.config.11346460-6c07-4dcb-b1e9-8b6e71bda0b9,
factoryPID=org.apache.karaf.example.config, bundleLocation=null"
configurationAdmin: org.apache.felix.cm.impl.ConfigurationAdminImpl =
{org.apache.felix.cm.impl.ConfigurationAdminImpl@7907}
delegatee: org.apache.felix.cm.impl.ConfigurationImpl =
{org.apache.felix.cm.impl.ConfigurationImpl@8021} "Configuration
PID=org.apache.karaf.example.config.11346460-6c07-4dcb-b1e9-8b6e71bda0b9,
factoryPID=org.apache.karaf.example.config, bundleLocation=null"
baseId: org.apache.felix.cm.impl.helper.TargetedPID =
{org.apache.felix.cm.impl.helper.TargetedPID@8022}
"org.apache.karaf.example.config.11346460-6c07-4dcb-b1e9-8b6e71bda0b9"
configurationManager: org.apache.felix.cm.impl.ConfigurationManager =
{org.apache.felix.cm.impl.ConfigurationManager@8017}
dynamicBundleLocation: java.lang.String = null
factoryPID: org.apache.felix.cm.impl.helper.TargetedPID =
{org.apache.felix.cm.impl.helper.TargetedPID@8023}
"org.apache.karaf.example.config"
isDeleted: boolean = false
locked: boolean = false
persistenceManager: org.apache.felix.cm.PersistenceManager =
{org.apache.felix.cm.impl.persistence.CachingPersistenceManagerProxy@8020}
properties: org.apache.felix.cm.impl.CaseInsensitiveDictionary = null
revision: long = 1
staticBundleLocation: java.lang.String = null
{noformat}
Karaf adds {{org.apache.karaf.features.configKey}} property.
{{org.osgi.service.cm.Configuration#update()}} is called passing:
{noformat}
cfgProps = {java.util.Hashtable@8007} size = 3
{@7984} "org.apache.karaf.features.configKey" -> {@7977}
"org.apache.karaf.example.config-abc"
{@8013} "key2" -> {@8014} "value2"
{@8015} "key1" -> {@8016} "value1"
{noformat}
Before {{org.apache.felix.cm.PersistenceManager#store()}} is called, this is
the content of the dictionary:
{noformat}
newProperties = {org.apache.felix.cm.impl.CaseInsensitiveDictionary@8066}
"{:org.apache.felix.configadmin.revision:=1, key1=value1, key2=value2,
org.apache.karaf.features.configKey=org.apache.karaf.example.config-abc,
service.factoryPid=org.apache.karaf.example.config,
service.pid=org.apache.karaf.example.config.11346460-6c07-4dcb-b1e9-8b6e71bda0b9}"
internalMap: java.util.SortedMap = {java.util.TreeMap@8095} size = 6
{@8104} ":org.apache.felix.configadmin.revision:" -> {java.lang.Long@8105} 1
{@8015} "key1" -> {@8016} "value1"
{@8013} "key2" -> {@8014} "value2"
{@7984} "org.apache.karaf.features.configKey" -> {@7977}
"org.apache.karaf.example.config-abc"
{@8106} "service.factoryPid" -> {@7980} "org.apache.karaf.example.config"
{@8107} "service.pid" -> {@8019}
"org.apache.karaf.example.config.11346460-6c07-4dcb-b1e9-8b6e71bda0b9"
{noformat}
config file is calculated:
{{/data/servers/apache-karaf-4.4.4-SNAPSHOT/data/cache/bundle12/data/config/org/apache/karaf/example/config/11346460-6c07-4dcb-b1e9-8b6e71bda0b9.config}}
After PM writes it, it contains:
{noformat}
:org.apache.felix.configadmin.revision:=L"1"
key1="value1"
key2="value2"
org.apache.karaf.features.configKey="org.apache.karaf.example.config-abc"
service.factoryPid="org.apache.karaf.example.config"
service.pid="org.apache.karaf.example.config.11346460-6c07-4dcb-b1e9-8b6e71bda0b9"
{noformat}
After houskeeping by configadmin (no fileinstall involved yet),
{{org.apache.felix.cm.impl.ConfigurationImpl#properties}} contains:
{noformat}
this.properties = {org.apache.felix.cm.impl.CaseInsensitiveDictionary@8066}
"{key1=value1, key2=value2,
org.apache.karaf.features.configKey=org.apache.karaf.example.config-abc}"
internalMap: java.util.SortedMap = {java.util.TreeMap@8095} size = 3
{@8015} "key1" -> {@8016} "value1"
{@8013} "key2" -> {@8014} "value2"
{@7984} "org.apache.karaf.features.configKey" -> {@7977}
"org.apache.karaf.example.config-abc"
{noformat}
{{org.osgi.service.cm.ConfigurationEvent#CM_UPDATED}} is sent. Fileinstall is
one of the async listeners:
{noformat}
listeners: org.osgi.service.cm.ConfigurationListener[] =
{org.osgi.service.cm.ConfigurationListener[4]@8276}
0 = {org.apache.felix.fileinstall.internal.ConfigInstaller@8279}
1 = {org.apache.karaf.config.command.completers.ConfigurationCompleter@8280}
2 =
{org.apache.karaf.shell.impl.console.osgi.secured.SecuredSessionFactoryImpl@8281}
3 = {org.apache.karaf.config.core.impl.JsonConfigInstaller@8282}
{noformat}
_(messed up with my breakpoints, so ids has changed below)_
{{org.apache.felix.fileinstall.internal.ConfigInstaller#doConfigurationEvent()}}
is called with
{noformat}
configurationEvent = {org.osgi.service.cm.ConfigurationEvent@8194}
factoryPid: java.lang.String = {@7984} "org.apache.karaf.example.config"
pid: java.lang.String = {@7983}
"org.apache.karaf.example.config.6cc22302-2bbd-4d07-bf62-67ea80fcb294"
reference: org.osgi.framework.ServiceReference =
{org.apache.felix.framework.ServiceRegistrationImpl$ServiceReferenceImpl@8168}
"[org.osgi.service.cm.ConfigurationAdmin]"
type: int = 1
{noformat}
because there's no {{felix.fileinstall.filename}} property, saving is skipped.
{{org.apache.felix.cm.impl.ConfigurationManager.UpdateConfiguration}} is
scheduled, but there are no {{org.osgi.service.cm.ManagedServiceFactory}}
registered.
Karaf enters this stage (storage is {{KARAF_HOME/etc}}):
{code:java}
if (storage != null && configCfgStore) {
properties.put(FILEINSTALL_FILE_NAME,
cfgFile.getAbsoluteFile().toURI().toString());
cfgProps.put(FILEINSTALL_FILE_NAME,
cfgFile.getAbsoluteFile().toURI().toString());
}
{code}
At this stage,
/data/servers/apache-karaf-4.4.4-SNAPSHOT/data/cache/bundle12/data/config/org/apache/karaf/example/config/6cc22302-2bbd-4d07-bf62-67ea80fcb294.config
is already there.
cfgFile is set to be
{{/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg}}
tmpFile is created:
{{/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg15916354998619434439.tmp}}
it's saved to contain:
{noformat}
key1=value1
key2=value2
org.apache.karaf.features.configKey = org.apache.karaf.example.config-abc
felix.fileinstall.filename =
file:/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg
{noformat}
and finally renamed to cfgFile.
{{org.apache.felix.fileinstall.internal.ConfigInstaller#setConfig}} is called
after detecting
{{/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg}}.
Karaf features thread stops dealing with the deployed file.
fileinstall parses the name into pid table:
{noformat}
pid[0] = {@8515} "org.apache.karaf.example.config"
pid[1] = {@8516} "abc"
{noformat}
fileinstalls searches for existing configs with
{{(felix.fileinstall.filename=file:/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg)}}
filter, but there's none (there's only a file written by Karaf with this
property).
fileinstall calls this new cm1.6 method:
{code:java}
org.apache.felix.cm.impl.ConfigurationAdminImpl#getFactoryConfiguration("org.apache.karaf.example.config",
"abc", "?")
{code}
{noformat}
factoryPid: java.lang.String = {@8515} "org.apache.karaf.example.config"
location: java.lang.String = {@8550} "?"
name: java.lang.String = {@8516} "abc"
{noformat}
it's already super confusing - fileinstall passes "abc" as name parameter, but
uses variable called {{factoryPid}}...
felix.configadmin constructs pid variable equal to
{{org.apache.karaf.example.config~abc}} (with tilde). Of course
{{org.apache.felix.cm.impl.ConfigurationManager#getConfiguration("org.apache.karaf.example.config~abc")}}
returns null, because there's only
{noformat}
{@7983} org.apache.karaf.example.config.6cc22302-2bbd-4d07-bf62-67ea80fcb294 ->
{org.apache.felix.cm.impl.ConfigurationImpl@7974} "Configuration
PID=org.apache.karaf.example.config.6cc22302-2bbd-4d07-bf62-67ea80fcb294,
factoryPID=org.apache.karaf.example.config, bundleLocation=null"
{noformat}
so
{{org.apache.felix.cm.impl.ConfigurationManager#createFactoryConfiguration("org.apache.karaf.example.config~abc",
"org.apache.karaf.example.config", "?")}} is called. But mind that Karaf
already called this method without a name, but using
{{org.apache.karaf.example.config}} factoryPid.
fileinstall calls:
{code:java}
org.apache.felix.cm.impl.ConfigurationAdapter#updateIfDifferent(props, )
{code}
props are:
{noformat}
properties = {java.util.Hashtable@8506} size = 4
{@8753} "org.apache.karaf.features.configKey" -> {@8754}
"org.apache.karaf.example.config-abc"
{@8755} "key2" -> {@8756} "value2"
{@8757} "key1" -> {@8758} "value1"
{@8759} "felix.fileinstall.filename" -> {@8760}
"file:/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg"
{noformat}
finally the persistence manager of configadmin stores these properties for
{{org.apache.karaf.example.config~abc}} PID:
{noformat}
props = {org.apache.felix.cm.impl.CaseInsensitiveDictionary@8780}
"{:org.apache.felix.configadmin.revision:=1,
felix.fileinstall.filename=file:/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg,
key1=value1, key2=value2,
org.apache.karaf.features.configKey=org.apache.karaf.example.config-abc,
service.bundleLocation=?, service.factoryPid=org.apache.karaf.example.config,
service.pid=org.apache.karaf.example.config~abc}"
internalMap: java.util.SortedMap = {java.util.TreeMap@8809} size = 8
{@8820} ":org.apache.felix.configadmin.revision:" -> {java.lang.Long@8821} 1
{@8759} "felix.fileinstall.filename" -> {@8760}
"file:/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg"
{@8757} "key1" -> {@8758} "value1"
{@8755} "key2" -> {@8756} "value2"
{@8753} "org.apache.karaf.features.configKey" -> {@8754}
"org.apache.karaf.example.config-abc"
{@8822} "service.bundleLocation" -> {@8550} "?"
{@8823} "service.factoryPid" -> {@8515} "org.apache.karaf.example.config"
{@8824} "service.pid" -> {@8552} "org.apache.karaf.example.config~abc"
{noformat}
The file is
{{/data/servers/apache-karaf-4.4.4-SNAPSHOT/data/cache/bundle12/data/config/org/apache/karaf/example/config%007eabc.config}}
and it contains:
{noformat}
:org.apache.felix.configadmin.revision:=L"1"
felix.fileinstall.filename="file:/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg"
key1="value1"
key2="value2"
org.apache.karaf.features.configKey="org.apache.karaf.example.config-abc"
service.bundleLocation="?"
service.factoryPid="org.apache.karaf.example.config"
service.pid="org.apache.karaf.example.config~abc"
{noformat}
and we have two configs:
{noformat}
karaf@root()> config:list '(service.factoryPid=org.apache.karaf.example.config)'
----------------------------------------------------------------
Pid:
org.apache.karaf.example.config.6cc22302-2bbd-4d07-bf62-67ea80fcb294
FactoryPid: org.apache.karaf.example.config
BundleLocation: null
Properties:
key1 = value1
key2 = value2
org.apache.karaf.features.configKey = org.apache.karaf.example.config-abc
service.factoryPid = org.apache.karaf.example.config
service.pid =
org.apache.karaf.example.config.6cc22302-2bbd-4d07-bf62-67ea80fcb294
----------------------------------------------------------------
Pid: org.apache.karaf.example.config~abc
FactoryPid: org.apache.karaf.example.config
BundleLocation: ?
Properties:
felix.fileinstall.filename =
file:/data/servers/apache-karaf-4.4.4-SNAPSHOT/etc/org.apache.karaf.example.config-abc.cfg
key1 = value1
key2 = value2
org.apache.karaf.features.configKey = org.apache.karaf.example.config-abc
service.factoryPid = org.apache.karaf.example.config
service.pid = org.apache.karaf.example.config~abc
{noformat}
> Race condition between the FeaturesService and FeatureDeploymentListener
> ------------------------------------------------------------------------
>
> Key: KARAF-6074
> URL: https://issues.apache.org/jira/browse/KARAF-6074
> Project: Karaf
> Issue Type: Bug
> Components: karaf
> Affects Versions: 4.2.2, 4.2.4
> Environment: Karaf 4.2.2 Windows 7 and Equinox
> Reporter: J. Brébec
> Priority: Critical
>
> On the first start of a custom Karaf container (4.2.2), the logs shows a log
> of NPE in FeatureDeploymentListener.
> After some analysis of this Exception, it's look like a race condition
> between the FeaturesService and the FeatureDeploymentListener :
> # The FeaturesService starts and launch a provisioning in another thread
> # a FeatureDeploymentListener is registered, and call bundleChanged for
> every bundle installed (the startup bundles)
> # It calls FeaturesService.state.requirements and get an empty map
> # Updating the root regions in the requirements map fails with an NPE
> # Some times later, the deployment task launched at step 1 is started, and
> FeaturesService.saveState() is called : state.requirements."root region" is
> initialised
> The step 5 is executed in another thread, so it can happens before step 2
> (and it works) or after, causing this NPE
> Moreover, some methods in FeaturesService assume that
> state.requirements."root region" exists. There are probably others npe here.
>
> {code:java}
> 14:37:55.668 ERROR [activator-1-thread-2] Unable to update deployed features
> for bundle: org.eclipse.osgi - 3.12.100.v20180210-1608
> java.lang.NullPointerException: null
> at
> org.apache.karaf.deployer.features.FeatureDeploymentListener.bundleChanged(FeatureDeploymentListener.java:247)
> [25:org.apache.karaf.deployer.features:4.2.2]
> at
> org.apache.karaf.deployer.features.FeatureDeploymentListener.init(FeatureDeploymentListener.java:95)
> [25:org.apache.karaf.deployer.features:4.2.2]
> at
> org.apache.karaf.deployer.features.osgi.Activator.doStart(Activator.java:52)
> [25:org.apache.karaf.deployer.features:4.2.2]
> at org.apache.karaf.util.tracker.BaseActivator.run(BaseActivator.java:292)
> [25:org.apache.karaf.deployer.features:4.2.2]
> at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
> [?:?]
> at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:?]
> at
> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
> [?:?]
> at
> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
> [?:?]{code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)