Hi Pierre,
Sorry for the late reply ...
Pierre De Rop schrieb:
> I am experimenting the following: I need to use ConfigAdmin (trunk) for
> configuring my components. I also uses SCR (trunk).
> Because we have our own specific configuration database, I first
> populate CM during startup like this:
>
> 1) pre-load all configurations from our own database
>
> 2) and for each PID loaded from our own database, when then populate CM
> like this:
>
> populateCM(String PID, Dictionary c) {
> Configuration config = _cm.getConfiguration(PID, null);
> if (config.getBundleLocation() != null)
> {
> config.setBundleLocation(null);
> }
> config.update(c);
> }
>
> 3) Now, because we have our own persistence manager, I don't want to let
> CM store all configuration in the default CM PersistenceManager.
> So, I have noticed that CM has a nice feature -> we can provide our own
> PersistenceManager.
That's what I would have proposed reading #1 and #2 above ;-)
>
> 4) So, before SCR is started, I provide in the OSGi Registry my own
> PersistentManager: For now: this persistent manager is simple and just
> stores in
> a map all CM configuration, like this:
>
> -----------------------------------------------------------------------------------------
>
> public class MapPersistenceManager implements PersistenceManager
> {
> private ConcurrentHashMap<String, Dictionary> _cmstore = new
> ConcurrentHashMap<String, Dictionary>();
>
> public void delete(String pid) throws IOException
> {
> _cmstore.remove(pid);
> }
>
> public boolean exists(String pid)
> {
> return _cmstore.containsKey(pid);
> }
>
> public Enumeration getDictionaries() throws IOException
> {
> return Collections.enumeration(_cmstore.values());
> }
>
> public Dictionary load(String pid) throws IOException
> {
> Dictionary d = _cmstore.get(pid);
> if (d == null) {
> d = new Hashtable();
> }
> return d;
> }
>
> public void store(String pid, Dictionary dic) throws IOException
> {
> _cmstore.put(pid, dic);
> }
> }
> -----------------------------------------------------------------------------------------
>
>
> Notice that initially, I used a Map and I synchronized all my methods,
> but I then got the following exception:
>
> java.util.ConcurrentModificationException
> at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
> at java.util.HashMap$ValueIterator.next(HashMap.java:822)
> at java.util.Collections$1.nextElement(Collections.java:3389)
> at
> org.apache.felix.cm.impl.ConfigurationManager.listConfigurations(ConfigurationManager.java:518)
>
> at
> org.apache.felix.cm.impl.ConfigurationAdminImpl.listConfigurations(ConfigurationAdminImpl.java:124)
>
> at
> org.apache.felix.scr.impl.config.ConfigurationComponentRegistry.findConfigurations(ConfigurationComponentRegistry.java:248)
>
> at
> org.apache.felix.scr.impl.config.ConfigurationComponentRegistry.findFactoryConfigurations(ConfigurationComponentRegistry.java:240)
>
> at
> org.apache.felix.scr.impl.config.ConfigurationComponentRegistry.createComponentHolder(ConfigurationComponentRegistry.java:107)
>
> at
> org.apache.felix.scr.impl.BundleComponentActivator.loadDescriptor(BundleComponentActivator.java:243)
>
> at
> org.apache.felix.scr.impl.BundleComponentActivator.initialize(BundleComponentActivator.java:146)
>
> at
> org.apache.felix.scr.impl.BundleComponentActivator.<init>(BundleComponentActivator.java:110)
>
> at
> org.apache.felix.scr.impl.Activator.loadComponents(Activator.java:233)
> at
> org.apache.felix.scr.impl.Activator.bundleChanged(Activator.java:169)
> at
> org.apache.felix.framework.util.EventDispatcher.invokeBundleListenerCallback(EventDispatcher.java:800)
>
> at
> org.apache.felix.framework.util.EventDispatcher.fireEventImmediately(EventDispatcher.java:728)
>
> at
> org.apache.felix.framework.util.EventDispatcher.fireBundleEvent(EventDispatcher.java:610)
>
> at org.apache.felix.framework.Felix.fireBundleEvent(Felix.java:3576)
> at org.apache.felix.framework.Felix.startBundle(Felix.java:1650)
> at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:915)
> at
> com.alcatel.as.service.bundleinstaller.impl.BundleInstallerImpl$DeployedBundle.start(BundleInstallerImpl.java:1133)
>
> at
> com.alcatel.as.service.bundleinstaller.impl.BundleInstallerImpl.startBundles(BundleInstallerImpl.java:776)
>
> at
> com.alcatel.as.service.bundleinstaller.impl.BundleInstallerImpl.doScan(BundleInstallerImpl.java:664)
>
> at
> com.alcatel.as.service.bundleinstaller.impl.BundleInstallerImpl$1.run(BundleInstallerImpl.java:164)
>
>
> So, I guess here that CM may concurrently invoke both getDictionaries()
> and store(String pid, Dictionary dic) methods, and while enumerating the
> Enum returned by the getDictionaries() method, I then get the exception
> above ? In any case, I replaced replaced my Map with a ConcurrentHashMap
> and I don't have the exception anymore.
Probably yes - particularly in a situation where SCR is trying to find
configuration while another part is storing configuration.
>
> 5) Now, with the ConcurrentHashMap version, I start my Felix. I have a
> demo appli which is something like the well known Dictionary OSGi demo:
> I have a SpellChecker and an EnglishDictionary.
>
> The EnglishDictionary SCR descriptor is defined like this:
> -------------------------------------------------------------------------------------------------------------------------------------------
>
> <?xml version="1.0" encoding="utf-8"?>
> <components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" >
> <scr:component name="englishdico" modified="updated"
> configuration-policy="require" immediate="true">
> <service>
> <provide interface="com.alcatel_lucent.dictionary.DictionaryService"/>
> </service>
> <property name="Language" value="en"/>
> <implementation
> class="com.alcatel_lucent.dictionary.english.EnglishDictionary" />
> </scr:component>
> </components>
> -------------------------------------------------------------------------------------------------------------------------------------------
>
>
> As you see, my EnglishDictionary component has a
> configuration-policy="require", meaning that the PID "englishdico"
> must be available before activating the englishdico component.
>
> 6) But, during startup, I don't know if it's a race condition issue or
> not, but the englishdico component is not activated and I have the
> following log:
>
> 2009-11-27 08:44:01,305 SCR Component Actor INFO osgi config/ca -
> [englishdico] Missing required configuration, cannot activate
>
> what I don't understand is that I have this log, but before the log, I
> see that the ConfigurationComponentRegistry.configurationEvent() method
> has been called with the proper "englishdico" PID
> So, I have modified the
> ConfigurationComponentRegistry.configurationEvent method in order to add
> more logs:
>
> -------------------------------------------------------------------------------------------------------------------------------------------
>
> public void configurationEvent( ConfigurationEvent event )
> {
> final String pid = event.getPid();
> final String factoryPid = event.getFactoryPid();
> Activator.log( LogService.LOG_DEBUG, null, "Received
> Configuration Event pid=" + event.getPid() + "/type=" + event.getType(),
> null );
>
> final ComponentHolder cm;
> if ( factoryPid == null )
> {
> cm = getComponent( pid );
> }
> else
> {
> cm = getComponent( factoryPid );
> }
>
> if (cm == null) {
> Activator.log( LogService.LOG_WARNING, null, "Component Holder
> is null for pid= " + event.getPid(),
> null );
> return;
> }
> if (cm.getComponentMetadata().isConfigurationIgnored()) {
> Activator.log( LogService.LOG_WARNING, null, "Component Holder
> ignores configuration for pid= " + event.getPid(),
> null );
> return;
> }
> -------------------------------------------------------------------------------------------------------------------------------------------
>
>
> 7) So, I start Felix, and then I have the following logs (SCR seems to
> catch the "englishdico" PID but it deos nothing, because the
> getComponent(pid) method returned null:
>
> 2009-11-27 08:44:00,630 ProxyAppReporter-FastCacheListener DEBUG osgi
> config/ca - [138] Scheduling task Update: pid=englishdico
> 2009-11-27 08:44:00,630 Configuration Updater (Update: pid=englishdico)
> DEBUG osgi config/ca - [138] Running task Update: pid=englishdico
> 2009-11-27 08:44:00,630 Configuration Updater (Update: pid=englishdico)
> DEBUG osgi config/ca - [138] Updating configuration englishdico to
> modification #2
> 2009-11-27 08:44:00,631 Configuration Updater (Update: pid=englishdico)
> DEBUG osgi config/ca - [138] Scheduling task Fire ConfigurationEvent:
> pid=englishdico
> 2009-11-27 08:44:00,631 Configuration Updater (Fire ConfigurationEvent:
> pid=englishdico) DEBUG osgi config/ca - [138] Running task Fire
> ConfigurationEvent: pid=englishdico
> 2009-11-27 08:44:00,631 Configuration Updater (Fire ConfigurationEvent:
> pid=englishdico) DEBUG osgi config/ca - Received Configuration Event
> pid=englishdico/type=1
> 2009-11-27 08:44:00,631 Configuration Updater (Fire ConfigurationEvent:
> pid=englishdico) WARN osgi config/ca - Component Holder is null for
> pid= englishdico
>
> 8) Therefore, next, I see that my "englishdico" is not activated:
>
> 2009-11-27 08:44:00,632 ProxyAppReporter-FastCacheListener DEBUG osgi
> config/ca - [138] UpdateConfiguration(englishdico) scheduled
> 2009-11-27 08:44:01,301 ProxyAppReporter-FastCacheListener DEBUG osgi
> config/ca - [englishdico] Component englishdico created: DS=1,
> implementation=com.alcatel_lucent.dictionary.english.EnglishDictionary,
> immediate=true, default-enabled=true, factory=null,
> configuration-policy=require, activate=activate, deactivate=deactivate,
> modified=updated
> 2009-11-27 08:44:01,301 ProxyAppReporter-FastCacheListener DEBUG osgi
> config/ca - [englishdico] Component englishdico Services:
> servicefactory=false,
> services=[com.alcatel_lucent.dictionary.DictionaryService]
> 2009-11-27 08:44:01,302 ProxyAppReporter-FastCacheListener DEBUG osgi
> config/ca - [englishdico] Component englishdico Properties: {Language=en}
> 2009-11-27 08:44:01,304 ProxyAppReporter-FastCacheListener DEBUG osgi
> config/ca - [englishdico] State transition : Disabled -> Enabling
> 2009-11-27 08:44:01,304 ProxyAppReporter-FastCacheListener DEBUG osgi
> config/ca - [englishdico] State transition : Enabling -> Unsatisfied
> 2009-11-27 08:44:01,304 ProxyAppReporter-FastCacheListener DEBUG osgi
> config/ca - [englishdico] Component enabled
> 2009-11-27 08:44:01,305 ProxyAppReporter-FastCacheListener DEBUG osgi
> config/ca - Adding task [Enable Component: englishdico (10)] as #1 in
> the queue
> 2009-11-27 08:44:01,305 SCR Component Actor INFO osgi config/ca -
> Running task: Enable Component: englishdico (10)
> 2009-11-27 08:44:01,305 SCR Component Actor DEBUG osgi config/ca -
> [englishdico] State transition : Unsatisfied -> Activating
> 2009-11-27 08:44:01,305 SCR Component Actor DEBUG osgi config/ca -
> [englishdico] Activating component
> 2009-11-27 08:44:01,305 SCR Component Actor INFO osgi config/ca -
> [englishdico] Missing required configuration, cannot activate
>
> And my englishdico is not activated because the PID is missing for my
> englishdico component, but this is not the case (see step 7)
>
> 9) If I somehow re-update the "englishdico" by invoking my method
> populateCM("englishdico", dictionary) ... then my englishdico is finally
> properly activated:
>
> 2009-11-27 08:44:28,439 ProxyAppReporter-FastCacheListener DEBUG osgi
> config/ca - [138] Scheduling task Update: pid=englishdico
> 2009-11-27 08:44:28,440 Configuration Updater (Update: pid=englishdico)
> DEBUG osgi config/ca - [138] Running task Update: pid=englishdico
> 2009-11-27 08:44:28,440 Configuration Updater (Update: pid=englishdico)
> DEBUG osgi config/ca - [138] Updating configuration englishdico to
> modification #3
> 2009-11-27 08:44:28,440 Configuration Updater (Update: pid=englishdico)
> DEBUG osgi config/ca - [138] Scheduling task Fire ConfigurationEvent:
> pid=englishdico
> 2009-11-27 08:44:28,440 Configuration Updater (Fire ConfigurationEvent:
> pid=englishdico) DEBUG osgi config/ca - [138] Running task Fire
> ConfigurationEvent: pid=englishdico
> 2009-11-27 08:44:28,440 Configuration Updater (Fire ConfigurationEvent:
> pid=englishdico) DEBUG osgi config/ca - Received Configuration Event
> pid=englishdico/type=1
> 2009-11-27 08:44:28,442 Configuration Updater (Fire ConfigurationEvent:
> pid=englishdico) DEBUG osgi config/ca - [englishdico] State transition :
> Unsatisfied -> Activating
> 2009-11-27 08:44:28,443 Configuration Updater (Fire ConfigurationEvent:
> pid=englishdico) DEBUG osgi config/ca - [englishdico] Activating component
> 2009-11-27 08:44:28,446 Configuration Updater (Fire ConfigurationEvent:
> pid=englishdico) DEBUG osgi config/ca - [englishdico] getting activate:
> activate
> 2009-11-27 08:44:28,446 Configuration Updater (Fire ConfigurationEvent:
> pid=englishdico) DEBUG osgi config/ca - [englishdico] invoking activate:
> activate
>
> So, my question is:
>
> 1) Is there a problem with my "MapPersistenceManager"
> implementation: Am I doing something very bad here ?
> 2) Or is there a race condition in SCR ?
The best thing to crosscheck these questions is to use the default
persistence manager for a test. If this works, chances are that there is
a race condition inside your MapPersistenceManager (maybe even related
to the use of the ConcurrentHashMap ..)
Regards
Felix
>
> thanks for your help;
> /pierre
>
>