FELIX-1146 refactor update tasks to immediately gather configurations
   and properties for asynchronous delivery. This not completely eliminates
   potential dual delivery but limits the probability dramatically
   (see issue for further discussion)
FELIX-1488 adapt to new configuration/factory binding API

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@805668 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
index f6a040c..6d15ae8 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
@@ -593,10 +593,9 @@
             for ( int i = 0; i < configs.length; i++ )
             {
                 final ConfigurationImpl cfg = configs[i];
-                if ( location.equals( cfg.getBundleLocation() ) )
+                if ( location.equals( cfg.getDynamicBundleLocation() ) )
                 {
-                    // reset dynamic binding
-                    cfg.setBundleLocation( null, false );
+                    cfg.setDynamicBundleLocation( null );
                 }
             }
 
@@ -606,10 +605,9 @@
             for ( int i = 0; i < factories.length; i++ )
             {
                 final Factory factory = factories[i];
-                if ( location.equals( factory.getBundleLocation() ) )
+                if ( location.equals( factory.getDynamicBundleLocation() ) )
                 {
-                    // reset dynamic binding
-                    factory.setBundleLocation( null, false );
+                    factory.setDynamicBundleLocation( null );
                 }
             }
         }
@@ -761,9 +759,12 @@
 
     /**
      * Calls the registered configuration plugins on the given configuration
-     * object unless the configuration has just been created and not been
-     * updated yet.
+     * properties from the given configuration object unless the configuration
+     * has just been created and not been updated yet.
      *
+     * @param props The configuraiton properties run through the registered
+     *          ConfigurationPlugin services. This may be <code>null</code>
+     *          in which case this method just immediately returns.
      * @param targetPid The identification of the configuration update used to
      *          select the plugins according to their cm.target service
      *          property
@@ -771,19 +772,13 @@
      *          is to be updated with configuration
      * @param cfg The configuration object whose properties have to be passed
      *          through the plugins
-     * @return The properties from the configuration object passed through the
-     *         plugins or <code>null</code> if the configuration object has
-     *         been newly created and no properties exist yet.
      */
-    private Dictionary callPlugins( final String targetPid, final ServiceReference sr, final ConfigurationImpl cfg )
+    private void callPlugins( final Dictionary props, final String targetPid, final ServiceReference sr,
+        final ConfigurationImpl cfg )
     {
-        // return a deep copy, since the plugins may tamper with the array
-        // and collection elements, which should not modify the internal data
-        Dictionary props = cfg.getProperties( true );
-
         // guard against NPE for new configuration never updated
         if (props == null) {
-            return null;
+            return;
         }
 
         ServiceReference[] plugins = null;
@@ -800,7 +795,7 @@
         // abort early if there are no plugins
         if ( plugins == null || plugins.length == 0 )
         {
-            return props;
+            return;
         }
 
         // sort the plugins by their service.cmRanking
@@ -821,7 +816,8 @@
             }
             catch ( Throwable t )
             {
-                log( LogService.LOG_ERROR, "Unexpected problem calling" + " configuration plugin", t );
+                log( LogService.LOG_ERROR, "Unexpected problem calling configuration plugin " + toString( pluginRef ),
+                    t );
             }
             finally
             {
@@ -830,8 +826,6 @@
             }
             cfg.setAutoProperties( props, false );
         }
-
-        return props;
     }
 
 
@@ -960,51 +954,70 @@
         return null;
     }
 
+
+    static String toString( ServiceReference ref )
+    {
+        String[] ocs = ( String[] ) ref.getProperty( "objectClass" );
+        String oc = "[";
+        for ( int i = 0; i < ocs.length; i++ )
+        {
+            oc += ocs[i];
+            if ( i < ocs.length - 1 )
+                oc += ", ";
+        }
+
+        oc += ", id=" + ref.getProperty( Constants.SERVICE_ID );
+        oc += ", bundle=" + ref.getBundle().getBundleId();
+        oc += "]";
+        return oc;
+    }
+
     // ---------- inner classes ------------------------------------------------
 
     private class ManagedServiceUpdate implements Runnable
     {
-        private String pid;
+        private final String pid;
 
-        private ServiceReference sr;
+        private final ServiceReference sr;
 
-        private ManagedService service;
+        private final ManagedService service;
 
+        private final ConfigurationImpl config;
+
+        private final Dictionary rawProperties;
 
         ManagedServiceUpdate( String pid, ServiceReference sr, ManagedService service )
         {
             this.pid = pid;
             this.sr = sr;
             this.service = service;
+
+            // get or load configuration for the pid
+            ConfigurationImpl config = null;
+            Dictionary rawProperties = null;
+            try
+            {
+                config = getExistingConfiguration( pid );
+                if (config != null) {
+                    rawProperties = config.getProperties( true );
+                }
+            }
+            catch ( IOException ioe )
+            {
+                log( LogService.LOG_ERROR, "Error loading configuration for " + pid, ioe );
+            }
+
+            this.config = config;
+            this.rawProperties = rawProperties;
         }
 
 
         public void run()
         {
-            // get or load configuration for the pid
-            ConfigurationImpl cfg;
-            try
-            {
-                cfg = getExistingConfiguration( pid );
-            }
-            catch ( IOException ioe )
-            {
-                log( LogService.LOG_ERROR, "Error loading configuration for " + pid, ioe );
-                return;
-            }
-
-            // this will be set below to be given to the service
-            Dictionary dictionary;
-
             // check configuration and call plugins if existing
-            if ( cfg != null )
+            Dictionary properties = rawProperties;
+            if ( config != null )
             {
-                if ( cfg.isDelivered() )
-                {
-                    log( LogService.LOG_DEBUG, "Configuration " + pid + " has already been delivered", null );
-                    return;
-                }
-
                 Bundle serviceBundle = sr.getBundle();
                 if ( serviceBundle == null )
                 {
@@ -1018,52 +1031,42 @@
                 // 104.4.1 No update call back for PID already bound to another
                 // bundle location
                 // 104.4.1 assign configuration to bundle if unassigned
-                String bundleLocation = serviceBundle.getLocation();
-                if ( cfg.getBundleLocation() == null )
+                if ( !config.tryBindLocation( serviceBundle.getLocation() ) )
                 {
-                    // dynamically bind to the location of the service if unbound
-                    cfg.setBundleLocation( bundleLocation, false );
-                }
-                else if ( !bundleLocation.equals( cfg.getBundleLocation() ) )
-                {
-                    log( LogService.LOG_ERROR, "Cannot use configuration for " + pid + " requested by bundle "
-                        + serviceBundle.getLocation() + " but belongs to " + cfg.getBundleLocation(), null );
+                    log( LogService.LOG_ERROR, "Cannot use configuration " + pid + " for "
+                        + ConfigurationManager.toString( sr ) + ": Configuration bound to bundle "
+                        + config.getBundleLocation(), null );
                     return;
                 }
 
                 // 104.3 Report an error in the log if more than one service
                 // with the same PID asks for the configuration
-                if ( cfg.getServiceReference() != null && !sr.equals( cfg.getServiceReference() ) )
-                {
-                    log( LogService.LOG_ERROR, "Configuration for " + pid + " has already been used for service "
-                        + cfg.getServiceReference() + " and will now also be given to " + sr, null );
-                }
-                else
+                if ( config.getServiceReference() == null )
                 {
                     // assign the configuration to the service
-                    cfg.setServiceReference( sr );
+                    config.setServiceReference( sr );
+                }
+                else if ( !sr.equals( config.getServiceReference() ) )
+                {
+                    log( LogService.LOG_ERROR, "Configuration for " + pid + " has already been used for service "
+                        + ConfigurationManager.toString( config.getServiceReference() )
+                        + " and will now also be given to " + ConfigurationManager.toString( sr ), null );
                 }
 
                 // prepare the configuration for the service (call plugins)
-                dictionary = callPlugins( pid, sr, cfg );
+                callPlugins( properties, pid, sr, config );
             }
             else
             {
                 // 104.5.3 ManagedService.updated must be called with null
                 // if no configuration is available
-                dictionary = null;
+                properties = null;
             }
 
             // update the service with the configuration
             try
             {
-                service.updated( dictionary );
-
-                // if there is nothing to set, don't
-                if ( cfg != null )
-                {
-                    cfg.setDelivered( true );
-	            }
+                service.updated( properties );
             }
             catch ( ConfigurationException ce )
             {
@@ -1092,34 +1095,89 @@
 
     private class ManagedServiceFactoryUpdate implements Runnable
     {
-        private String factoryPid;
+        private final String factoryPid;
 
-        private ServiceReference sr;
+        private final ServiceReference sr;
 
-        private ManagedServiceFactory service;
+        private final ManagedServiceFactory service;
 
+        private final Factory factory;
+
+        private final Map configs;
 
         ManagedServiceFactoryUpdate( String factoryPid, ServiceReference sr, ManagedServiceFactory service )
         {
             this.factoryPid = factoryPid;
             this.sr = sr;
             this.service = service;
+
+            Factory factory = null;
+            Map configs = null;
+            try
+            {
+                factory = getFactory( factoryPid );
+                if (factory != null) {
+                    configs = new HashMap();
+                    for ( Iterator pi = factory.getPIDs().iterator(); pi.hasNext(); )
+                    {
+                        final String pid = ( String ) pi.next();
+                        ConfigurationImpl cfg;
+                        try
+                        {
+                            cfg = getExistingConfiguration( pid );
+                        }
+                        catch ( IOException ioe )
+                        {
+                            log( LogService.LOG_ERROR, "Error loading configuration for " + pid, ioe );
+                            continue;
+                        }
+
+                        // sanity check on the configuration
+                        if ( cfg == null )
+                        {
+                            log( LogService.LOG_ERROR, "Configuration " + pid + " referred to by factory " + factoryPid
+                                + " does not exist", null );
+                            factory.removePID( pid );
+                            factory.storeSilently();
+                            continue;
+                        }
+                        else if ( cfg.isNew() )
+                        {
+                            // Configuration has just been created but not yet updated
+                            // we currently just ignore it and have the update mechanism
+                            // provide the configuration to the ManagedServiceFactory
+                            // As of FELIX-612 (not storing new factory configurations)
+                            // this should not happen. We keep this for added stability
+                            // but raise the logging level to error.
+                            log( LogService.LOG_ERROR, "Ignoring new configuration pid=" + pid, null );
+                            continue;
+                        }
+                        else if ( !factoryPid.equals( cfg.getFactoryPid() ) )
+                        {
+                            log( LogService.LOG_ERROR, "Configuration " + pid + " referred to by factory " + factoryPid
+                                + " seems to belong to factory " + cfg.getFactoryPid(), null );
+                            factory.removePID( pid );
+                            factory.storeSilently();
+                            continue;
+                        }
+
+                        // get the configuration properties for later
+                        configs.put(cfg, cfg.getProperties( true ));
+                    }
+                }
+            }
+            catch ( IOException ioe )
+            {
+                log( LogService.LOG_ERROR, "Cannot get factory mapping for factory PID " + factoryPid, ioe );
+            }
+
+            this.factory = factory;
+            this.configs = configs;
         }
 
 
         public void run()
         {
-            Factory factory;
-            try
-            {
-                factory = getFactory( factoryPid );
-            }
-            catch ( IOException ioe )
-            {
-                log( LogService.LOG_ERROR, "Cannot get factory mapping for factory PID " + factoryPid, ioe );
-                return;
-            }
-
             Bundle serviceBundle = sr.getBundle();
             if ( serviceBundle == null )
             {
@@ -1128,102 +1186,62 @@
                 return;
             }
 
-            String bundleLocation = serviceBundle.getLocation();
-            if ( factory.getBundleLocation() == null )
-            {
-                // bind to the location of the service if unbound
-                factory.setBundleLocation( bundleLocation, false );
-            }
-            else if ( !bundleLocation.equals( factory.getBundleLocation() ) )
+            final String serviceBundleLocation = serviceBundle.getLocation();
+            if ( !factory.tryBindLocation( serviceBundleLocation ) )
             {
                 // factory PID is bound to another bundle
-                log( LogService.LOG_ERROR, "Cannot use Factory configuration for " + factoryPid
-                    + " requested by bundle " + serviceBundle.getLocation() + " but belongs to "
+                log( LogService.LOG_ERROR, "Cannot use factory configuration " + factoryPid + " for "
+                    + ConfigurationManager.toString( sr ) + ": Configuration bound to bundle "
                     + factory.getBundleLocation(), null );
+
                 return;
             }
 
-            Set pids = factory.getPIDs();
-
-            for ( Iterator pi = pids.iterator(); pi.hasNext(); )
+            for ( Iterator ci=configs.entrySet().iterator(); ci.hasNext(); )
             {
-                String pid = ( String ) pi.next();
-                ConfigurationImpl cfg;
-                try
-                {
-                    cfg = getExistingConfiguration( pid );
-                }
-                catch ( IOException ioe )
-                {
-                    log( LogService.LOG_ERROR, "Error loading configuration for " + pid, ioe );
-                    continue;
-                }
-
-                // sanity check on the configuration
-                if ( cfg == null )
-                {
-                    log( LogService.LOG_ERROR, "Configuration " + pid + " referred to by factory " + factoryPid
-                        + " does not exist", null );
-                    factory.removePID( pid );
-                    factory.storeSilently();
-                    continue;
-                }
-                else if ( cfg.isNew() )
-                {
-                    // Configuration has just been created but not yet updated
-                    // we currently just ignore it and have the update mechanism
-                    // provide the configuration to the ManagedServiceFactory
-                    // As of FELIX-612 (not storing new factory configurations)
-                    // this should not happen. We keep this for added stability
-                    // but raise the logging level to error.
-                    log( LogService.LOG_ERROR, "Ignoring new configuration pid=" + pid, null );
-                    continue;
-                }
-                else if ( !factoryPid.equals( cfg.getFactoryPid() ) )
-                {
-                    log( LogService.LOG_ERROR, "Configuration " + pid + " referred to by factory " + factoryPid
-                        + " seems to belong to factory " + cfg.getFactoryPid(), null );
-                    factory.removePID( pid );
-                    factory.storeSilently();
-                    continue;
-                }
-
-                // do not re-updated unmodified configuration
-                if ( cfg.isDelivered() )
-                {
-                    log( LogService.LOG_DEBUG, "Configuration " + pid + " has already been updated", null );
-                    continue;
-                }
+                final Map.Entry entry = (Map.Entry) ci.next();
+                final ConfigurationImpl cfg = (ConfigurationImpl) entry.getKey();
+                final Dictionary properties = (Dictionary) entry.getValue();
 
                 // check bundle location of configuration
-                if ( cfg.getBundleLocation() == null )
-                {
-                    // dynamically bind to the location of the service if unbound
-                    cfg.setBundleLocation( bundleLocation, false );
-                }
-                else if ( !bundleLocation.equals( cfg.getBundleLocation() ) )
+                if ( !cfg.tryBindLocation( serviceBundleLocation ) )
                 {
                     // configuration is bound to another bundle
-                    log( LogService.LOG_ERROR, "Configuration " + pid + " (factory " + factoryPid
-                        + ") belongs to bundle " + cfg.getBundleLocation() + " but was requested for bundle "
-                        + bundleLocation, null );
+                    log( LogService.LOG_ERROR, "Cannot use configuration " + cfg.getPid() + " (factory " + factoryPid
+                        + ") for " + ConfigurationManager.toString( sr ) + ": Configuration bound to bundle "
+                        + cfg.getBundleLocation(), null );
+
                     continue;
                 }
 
+                // 104.3 Report an error in the log if more than one service
+                // with the same PID asks for the configuration
+                if ( cfg.getServiceReference() == null )
+                {
+                    // assign the configuration to the service
+                    cfg.setServiceReference( sr );
+                }
+                else if ( !sr.equals( cfg.getServiceReference() ) )
+                {
+                    log( LogService.LOG_ERROR, "Configuration for " + cfg.getPid() + " (factory " + factoryPid
+                        + ") has already been used for service "
+                        + ConfigurationManager.toString( cfg.getServiceReference() )
+                        + " and will now also be given to " + ConfigurationManager.toString( sr ), null );
+                }
+
                 // prepare the configuration for the service (call plugins)
                 // call the plugins with cm.target set to the service's factory PID
                 // (clarification in Section 104.9.1 of Compendium 4.2)
-                Dictionary dictionary = callPlugins( factoryPid, sr, cfg );
+                callPlugins( properties, factoryPid, sr, cfg );
 
                 // update the service with the configuration
                 try
                 {
                     // only, if there is non-null configuration data
-                    if ( dictionary != null )
+                    if ( properties  != null )
                     {
-                        log( LogService.LOG_DEBUG, sr + ": Updating configuration pid=" + pid, null );
-                        service.updated( pid, dictionary );
-                        cfg.setDelivered( true );
+                        log( LogService.LOG_DEBUG, sr + ": Updating configuration pid=" + cfg.getPid(), null );
+                        service.updated( cfg.getPid(), properties );
                     }
                 }
                 catch ( ConfigurationException ce )
@@ -1257,12 +1275,15 @@
     private class UpdateConfiguration implements Runnable
     {
 
-        private ConfigurationImpl config;
+        private final ConfigurationImpl config;
+
+        private final Dictionary properties;
 
 
-        UpdateConfiguration( ConfigurationImpl config )
+        UpdateConfiguration( final ConfigurationImpl config )
         {
             this.config = config;
+            this.properties = config.getProperties( true );
         }
 
 
@@ -1270,69 +1291,65 @@
         {
             try
             {
-                if ( config.isDelivered() )
-                {
-                    log( LogService.LOG_DEBUG, "Configuration " + config.getPid() + " has already been updated", null );
-                    return;
-                }
-
                 if ( config.getFactoryPid() == null )
                 {
                     final ServiceReference[] srList = bundleContext.getServiceReferences( ManagedService.class
                         .getName(), "(" + Constants.SERVICE_PID + "=" + config.getPid() + ")" );
-                    if ( srList != null && srList.length > 0 )
+                    if ( srList != null )
                     {
-                        final ServiceReference sr = srList[0];
-                        final ManagedService srv = ( ManagedService ) bundleContext.getService( sr );
+                        // find the primary configuration owner
+                        final ServiceReference ownerRef = getOwner( config, srList );
+                        final String bundleLocation = ownerRef.getBundle().getLocation();
 
-                        // 104.3 Report an error in the log if more than one service
-                        // with the same PID asks for the configuration
-                        if ( srList.length > 1 )
+                        // if the configuration is unbound, bind to owner
+                        if ( config.getBundleLocation() == null )
                         {
-                            for ( int i = 1; i < srList.length; i++ )
+                            config.setDynamicBundleLocation( bundleLocation );
+                        }
+                        final String configBundleLocation = config.getBundleLocation();
+
+                        // provide configuration to all services from the
+                        // correct bundle
+                        for ( int i = 0; i < srList.length; i++ )
+                        {
+
+                            final ServiceReference userRef = srList[i];
+                            final String userRefLocation = userRef.getBundle().getLocation();
+
+                            // only consider the entry if in the same bundle
+                            if ( !userRefLocation.equals( configBundleLocation ) )
+                            {
+                                log( LogService.LOG_ERROR, "Cannot use configuration " + config.getPid() + " for "
+                                    + ConfigurationManager.toString( userRef ) + ": Configuration bound to bundle "
+                                    + configBundleLocation, null );
+
+                                continue;
+                            }
+
+                            // 104.3 Report an error in the log if more than one
+                            // service with the same PID asks for the
+                            // configuration
+                            if ( userRef != ownerRef )
                             {
                                 log( LogService.LOG_ERROR, "Configuration for " + config.getPid()
-                                    + " is used for service " + sr
-                                    + "following services will not receive configuration: " + srList[i], null );
+                                    + " has already been used for service " + ConfigurationManager.toString( ownerRef )
+                                    + " and will now also be given to " + ConfigurationManager.toString( userRef ), null );
                             }
-                        }
 
-                        try
-                        {
-                            // bind the configuration, fail if bound to another
-                            // bundle !!
-                            // check bundle location of configuration
-                            String bundleLocation = sr.getBundle().getLocation();
-                            if ( config.getBundleLocation() == null )
+                            try
                             {
-                                // dynamically bind to the location of the service if unbound
-                                config.setBundleLocation( bundleLocation, false );
+                                final ManagedService srv = ( ManagedService ) bundleContext.getService( userRef );
+                                if ( srv != null )
+                                {
+                                    Dictionary props = new CaseInsensitiveDictionary( properties );
+                                    callPlugins( props, config.getPid(), userRef, config );
+                                    srv.updated( props );
+                                }
                             }
-                            else if ( !bundleLocation.equals( config.getBundleLocation() ) )
+                            finally
                             {
-                                // configuration is bound to another bundle
-                                log( LogService.LOG_ERROR, "Configuration " + config.getPid() + " belongs to bundle "
-                                    + config.getBundleLocation() + " but was requested for bundle " + bundleLocation,
-                                    null );
-                                return;
+                                bundleContext.ungetService( userRef );
                             }
-
-                            // record the delivery of the configuration
-                            if ( config.getServiceReference() == null )
-                            {
-                                config.setServiceReference( sr );
-                            }
-
-                            // prepare the configuration for the service (call plugins)
-                            Dictionary dictionary = callPlugins( config.getPid(), sr, config );
-
-                            // update the ManagedService with the properties
-                            srv.updated( dictionary );
-                            config.setDelivered( true );
-                        }
-                        finally
-                        {
-                            bundleContext.ungetService( sr );
                         }
                     }
                 }
@@ -1342,44 +1359,59 @@
                         .getName(), "(" + Constants.SERVICE_PID + "=" + config.getFactoryPid() + ")" );
                     if ( srList != null && srList.length > 0 )
                     {
-                        final ServiceReference sr = srList[0];
-                        final ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext.getService( sr );
-                        try
+                        // find the primary configuration owner
+                        final ServiceReference ownerRef = getOwner( config, srList );
+                        final String bundleLocation = ownerRef.getBundle().getLocation();
+
+                        // if the configuration is unbound, bind to owner
+                        if ( config.getBundleLocation() == null )
                         {
-                            // bind the configuration, fail if bound to another
-                            // bundle !!
-                            // check bundle location of configuration
-                            String bundleLocation = sr.getBundle().getLocation();
-                            if ( config.getBundleLocation() == null )
-                            {
-                                // dynamically bind to the location of the service if unbound
-                                config.setBundleLocation( bundleLocation, false );
-                            }
-                            else if ( !bundleLocation.equals( config.getBundleLocation() ) )
-                            {
-                                // configuration is bound to another bundle
-                                log( LogService.LOG_ERROR, "Configuration " + config.getPid() + " (factory "
-                                    + config.getFactoryPid() + ") belongs to bundle " + config.getBundleLocation()
-                                    + " but was requested for bundle " + bundleLocation, null );
-                                return;
-                            }
-
-                            // prepare the configuration for the service (call plugins)
-                            // call the plugins with cm.target set to the service's factory PID
-                            // (clarification in Section 104.9.1 of Compendium 4.2)
-                            Dictionary dictionary = callPlugins( config.getFactoryPid(), sr, config );
-
-                            // update the ManagedServiceFactory with the properties
-                            // only, if there is non-null configuration data
-                            if ( dictionary != null )
-                            {
-                                srv.updated( config.getPid(), dictionary );
-                                config.setDelivered( true );
-                            }
+                            config.setDynamicBundleLocation( bundleLocation );
                         }
-                        finally
+
+                        // provide configuration to all services from the
+                        // correct bundle
+                        for ( int i = 0; i < srList.length; i++ )
                         {
-                            bundleContext.ungetService( sr );
+                            final ServiceReference ref = srList[i];
+                            final String refLocation = ref.getBundle().getLocation();
+
+                            // only consider the entry if in the same bundle
+                            if ( !refLocation.equals( config.getBundleLocation() ) )
+                            {
+                                log( LogService.LOG_ERROR, "Cannot use configuration " + config.getPid() + " (factory "
+                                    + config.getFactoryPid() + ") for " + ConfigurationManager.toString( ref )
+                                    + ": Configuration bound to bundle " + config.getBundleLocation(), null );
+
+                                continue;
+                            }
+
+                            // 104.3 Report an error in the log if more than one
+                            // service with the same PID asks for the
+                            // configuration
+                            if ( ref != ownerRef )
+                            {
+                                log( LogService.LOG_ERROR, "Configuration for " + config.getPid() + " (factory "
+                                    + config.getFactoryPid() + ") has already been used for service "
+                                    + ConfigurationManager.toString( ownerRef ) + " and will now also be given to "
+                                    + ConfigurationManager.toString( ref ), null );
+                            }
+
+                            try
+                            {
+                                final ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext
+                                    .getService( ref );
+                                if ( srv != null && properties != null )
+                                {
+                                    Dictionary props = new CaseInsensitiveDictionary( properties );
+                                    callPlugins( props, config.getFactoryPid(), ref, config );
+                                    srv.updated( config.getPid(), props );
+                                }
+                            }
+                            finally
+                            {
+                                bundleContext.ungetService( ref );
+                            }
                         }
                     }
                 }
@@ -1409,6 +1441,29 @@
             }
         }
 
+
+        private ServiceReference getOwner( ConfigurationImpl config, ServiceReference[] srList )
+        {
+            // find the current owner among the references (if any)
+            if ( config.getServiceReference() != null )
+            {
+                for ( int i = 0; i < srList.length; i++ )
+                {
+                    if ( srList[i].equals( config.getServiceReference() ) )
+                    {
+                        return srList[i];
+                    }
+                }
+            }
+
+            // configuration has never been supplied or the binding is stale
+            // just use the first entry in the list as the new owner
+            final ServiceReference ownerRef = srList[0];
+            config.setServiceReference( ownerRef );
+            return ownerRef;
+        }
+
+
         public String toString()
         {
             return "Update: pid=" + config.getPid();
@@ -1435,28 +1490,30 @@
         {
             try
             {
-                if ( config.isDelivered() )
-                {
-                    log( LogService.LOG_DEBUG, "Deletion of configuration " + pid + " has already been delivered", null );
-                    return;
-                }
+                final String configLocation = config.getBundleLocation();
 
                 if ( factoryPid == null )
                 {
                     ServiceReference[] srList = bundleContext.getServiceReferences( ManagedService.class.getName(), "("
                         + Constants.SERVICE_PID + "=" + pid + ")" );
-                    if ( srList != null && srList.length > 0 )
+                    if ( srList != null )
                     {
-                        final ServiceReference sr = srList[0];
-                        final ManagedService srv = ( ManagedService ) bundleContext.getService( sr );
-                        try
+                        for ( int i = 0; i < srList.length; i++ )
                         {
-                            srv.updated( null );
-                            config.setDelivered( true );
-                        }
-                        finally
-                        {
-                            bundleContext.ungetService( sr );
+                            final ServiceReference sr = srList[i];
+                            if ( sr.getBundle().getLocation().equals( configLocation ) )
+                            {
+                                // only if the service is from the bound bundle
+                                final ManagedService srv = ( ManagedService ) bundleContext.getService( sr );
+                                try
+                                {
+                                    srv.updated( null );
+                                }
+                                finally
+                                {
+                                    bundleContext.ungetService( sr );
+                                }
+                            }
                         }
                     }
                 }
@@ -1469,18 +1526,25 @@
 
                     ServiceReference[] srList = bundleContext.getServiceReferences( ManagedServiceFactory.class
                         .getName(), "(" + Constants.SERVICE_PID + "=" + factoryPid + ")" );
-                    if ( srList != null && srList.length > 0 )
+                    if ( srList != null)
                     {
-                        final ServiceReference sr = srList[0];
-                        final ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext.getService( sr );
-                        try
+                        for ( int i = 0; i < srList.length; i++ )
                         {
-                            srv.deleted( pid );
-                            config.setDelivered( true );
-                        }
-                        finally
-                        {
-                            bundleContext.ungetService( sr );
+                            final ServiceReference sr = srList[i];
+                            if ( sr.getBundle().getLocation().equals( configLocation ) )
+                            {
+                                // only if the service is from the bound bundle
+                                final ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext
+                                    .getService( sr );
+                                try
+                                {
+                                    srv.deleted( pid );
+                                }
+                                finally
+                                {
+                                    bundleContext.ungetService( sr );
+                                }
+                            }
                         }
                     }
                 }
@@ -1553,7 +1617,8 @@
                 }
                 catch ( Throwable t )
                 {
-                    log( LogService.LOG_ERROR, "Unexpected problem delivery configuration event to " + srs[i], t );
+                    log( LogService.LOG_ERROR, "Unexpected problem delivery configuration event to "
+                        + ConfigurationManager.toString( srs[i] ), t );
                 }
             }
         }
@@ -1564,48 +1629,20 @@
         }
     }
 
-    private static abstract class AbstractManagedServiceTracker extends ServiceTracker
+    private static class ManagedServiceTracker extends ServiceTracker
     {
-        protected final ConfigurationManager cm;
 
-        AbstractManagedServiceTracker( ConfigurationManager cm, String className )
+        private final ConfigurationManager cm;
+
+
+        ManagedServiceTracker( ConfigurationManager cm )
         {
-            super( cm.bundleContext, className, null );
+            super( cm.bundleContext, ManagedService.class.getName(), null );
             this.cm = cm;
             open();
         }
 
 
-        public void removedService( ServiceReference reference, Object service )
-        {
-            // check whether we can take back the configuration object
-            String[] pids = getServicePid( reference );
-            if ( pids != null )
-            {
-                for ( int i = 0; i < pids.length; i++ )
-                {
-                    ConfigurationImpl cfg = cm.getCachedConfiguration( pids[i] );
-                    if ( cfg != null && reference.equals( cfg.getServiceReference() ) )
-                    {
-                        cfg.setServiceReference( null );
-                        cfg.setDelivered( false );
-                    }
-                }
-            }
-
-            super.removedService( reference, service );
-        }
-    }
-
-    private static class ManagedServiceTracker extends AbstractManagedServiceTracker
-    {
-
-        ManagedServiceTracker(ConfigurationManager cm)
-        {
-            super( cm, ManagedService.class.getName() );
-        }
-
-
         public Object addingService( ServiceReference reference )
         {
             Object serviceObject = super.addingService( reference );
@@ -1622,13 +1659,38 @@
 
             return serviceObject;
         }
+
+
+        public void removedService( ServiceReference reference, Object service )
+        {
+            // check whether we can take back the configuration object
+            String[] pids = getServicePid( reference );
+            if ( pids != null )
+            {
+                for ( int i = 0; i < pids.length; i++ )
+                {
+                    ConfigurationImpl cfg = cm.getCachedConfiguration( pids[i] );
+                    if ( cfg != null && reference.equals( cfg.getServiceReference() ) )
+                    {
+                        cfg.setServiceReference( null );
+                    }
+                }
+            }
+
+            super.removedService( reference, service );
+        }
     }
 
-    private static class ManagedServiceFactoryTracker extends AbstractManagedServiceTracker
+    private static class ManagedServiceFactoryTracker extends ServiceTracker
     {
-        ManagedServiceFactoryTracker(ConfigurationManager cm)
+        private final ConfigurationManager cm;
+
+
+        ManagedServiceFactoryTracker( ConfigurationManager cm )
         {
-            super( cm, ManagedServiceFactory.class.getName() );
+            super( cm.bundleContext, ManagedServiceFactory.class.getName(), null );
+            this.cm = cm;
+            open();
         }
 
 
@@ -1665,9 +1727,9 @@
                         {
                             String pid = ( String ) pi.next();
                             ConfigurationImpl cfg = cm.getCachedConfiguration( pid );
-                            if ( cfg != null )
+                            if ( cfg != null && reference.equals( cfg.getServiceReference() ) )
                             {
-                                cfg.setDelivered( false );
+                                cfg.setServiceReference( null );
                             }
                         }
                     }