[ 
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)

Reply via email to