FELIX-516 Record whether a configuration change (update, delete) has been delivered
to a ManagedService[Factory] to prevent multiple delivery. See issue description
for a situation where this might occurr.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@635540 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java
index 9b26f90..d0632b7 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java
@@ -106,6 +106,17 @@
      */
     private CaseInsensitiveDictionary properties;
 
+    /**
+     * Flag indicating that this configuration object has been delivered to the
+     * owning ManagedService[Factory] after the last update or after being
+     * deleted. This flag is set by the {@link #delete()}, {@link #update()}
+     * and {@link #update(Dictionary)} method and must be checked when this
+     * instance is about to be delivered and reset after the configuration has
+     * been delivered.
+     * @see #setDelivered(boolean)
+     * @see #isDelivered()
+     */
+    private boolean delivered;
 
     ConfigurationImpl( ConfigurationManager configurationManager, PersistenceManager persistenceManager,
         Dictionary properties )
@@ -140,7 +151,30 @@
     }
 
 
-    /* (non-Javadoc)
+    /**
+     * Sets whether the last change (update, delete) to this instance should be
+     * assumed as being delivered to the owning ManagedService[Factory] or not.
+     */
+    void setDelivered( boolean delivered )
+    {
+        this.delivered = delivered;
+    }
+
+
+    /**
+     * Returns whether the last change (update, delete) to this instance should
+     * be assumed as being delivered to the owning ManagedService[Factory] or
+     * not.
+     */
+    boolean isDelivered()
+    {
+        return delivered;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
      * @see org.osgi.service.cm.Configuration#delete()
      */
     public void delete() throws IOException
@@ -150,6 +184,9 @@
             persistenceManager.delete( pid );
             persistenceManager = null;
 
+            // ensure configuration is being delivered
+            setDelivered( false );
+            
             configurationManager.deleted( this );
         }
     }
@@ -244,6 +281,9 @@
 
             configureFromPersistence( properties );
 
+            // ensure configuration is being delivered
+            setDelivered( false );
+            
             configurationManager.updated( this );
         }
     }
@@ -264,6 +304,9 @@
 
             configure( newProperties );
 
+            // ensure configuration is being delivered
+            setDelivered( false );
+            
             configurationManager.updated( this );
         }
     }
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 d42b728..a2fc2f8 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
@@ -491,7 +491,7 @@
                             Factory factory = Factory.getFactory( pmList[i], config );
                             if ( factory != null )
                             {
-                                Factory cachedFactory = ( Factory ) factories.get( factory.getFactoryPid() );
+                                Factory cachedFactory = getCachedFactory( factory.getFactoryPid() );
                                 if ( cachedFactory != null )
                                 {
                                     factory = cachedFactory;
@@ -591,9 +591,27 @@
     }
 
 
+    Factory getCachedFactory( String factoryPid )
+    {
+        synchronized ( factories )
+        {
+            return ( Factory ) factories.get( factoryPid );
+        }
+    }
+
+
+    void cacheFactory( Factory factory )
+    {
+        synchronized ( factories )
+        {
+            factories.put( factory.getFactoryPid(), factory );
+        }
+    }
+
+
     Factory getFactory( String factoryPid ) throws IOException
     {
-        Factory factory = ( Factory ) factories.get( factoryPid );
+        Factory factory = getCachedFactory( factoryPid );
         if ( factory != null )
         {
             return factory;
@@ -605,7 +623,7 @@
             if ( Factory.exists( pmList[i], factoryPid ) )
             {
                 factory = Factory.load( pmList[i], factoryPid );
-                factories.put( factoryPid, factory );
+                cacheFactory( factory );
                 return factory;
             }
         }
@@ -618,7 +636,7 @@
     Factory createFactory( String factoryPid )
     {
         Factory factory = new Factory( getPersistenceManagers()[0], factoryPid );
-        factories.put( factoryPid, factory );
+        cacheFactory( factory );
         return factory;
     }
 
@@ -816,6 +834,12 @@
             if ( cfg != null && !cfg.isNew() )
             {
 
+                if ( cfg.isDelivered() )
+                {
+                    log( LogService.LOG_DEBUG, "Configuration " + pid + " has already been delivered", null );
+                    return;
+                }
+                
                 // 104.3 Ignore duplicate PIDs from other bundles and report
                 // them to the log
                 // 104.4.1 No update call back for PID already bound to another
@@ -861,6 +885,12 @@
             try
             {
                 service.updated( dictionary );
+
+                // if there is nothing to set, don't
+                if ( cfg != null )
+                {
+                    cfg.setDelivered( true );
+                }
             }
             catch ( ConfigurationException ce )
             {
@@ -974,6 +1004,13 @@
                     continue;
                 }
 
+                // do not re-updated unmodified configuration
+                if ( cfg.isDelivered() )
+                {
+                    log( LogService.LOG_DEBUG, "Configuration " + pid + " has already been updated", null );
+                    continue;
+                }
+                
                 // check bundle location of configuration
                 if ( cfg.getBundleLocation() == null )
                 {
@@ -1000,6 +1037,7 @@
                     {
                         log( LogService.LOG_DEBUG, sr + ": Updating configuration pid=" + pid, null );
                         service.updated( pid, dictionary );
+                        cfg.setDelivered( true );
                     }
                 }
                 catch ( ConfigurationException ce )
@@ -1044,6 +1082,12 @@
 
         public void run()
         {
+            if ( config.isDelivered() )
+            {
+                log( LogService.LOG_DEBUG, "Configuration " + config.getPid() + " has already been updated", null );
+                return;
+            }
+            
             try
             {
                 if ( config.getFactoryPid() == null )
@@ -1079,6 +1123,7 @@
 
                             // update the ManagedService with the properties
                             srv.updated( dictionary );
+                            config.setDelivered( true );
                         }
                         finally
                         {
@@ -1123,6 +1168,7 @@
                             if ( dictionary != null )
                             {
                                 srv.updated( config.getPid(), dictionary );
+                                config.setDelivered( true );
                             }
                         }
                         finally
@@ -1162,12 +1208,14 @@
     private class DeleteConfiguration implements Runnable
     {
 
+        private ConfigurationImpl config;
         private String pid;
         private String factoryPid;
 
 
         DeleteConfiguration( ConfigurationImpl config )
         {
+            this.config = config;
             this.pid = config.getPid();
             this.factoryPid = config.getFactoryPid();
         }
@@ -1175,6 +1223,12 @@
 
         public void run()
         {
+            if ( config.isDelivered() )
+            {
+                log( LogService.LOG_DEBUG, "Deletion of configuration " + pid + " has already been delivered", null );
+                return;
+            }
+            
             try
             {
                 if ( factoryPid == null )
@@ -1187,6 +1241,7 @@
                         try
                         {
                             srv.updated( null );
+                            config.setDelivered( true );
                         }
                         finally
                         {
@@ -1209,6 +1264,7 @@
                         try
                         {
                             srv.deleted( pid );
+                            config.setDelivered( true );
                         }
                         finally
                         {
@@ -1314,6 +1370,7 @@
                 if ( cfg != null && reference.equals( cfg.getServiceReference() ) )
                 {
                     cfg.setServiceReference( null );
+                    cfg.setDelivered( false );
                 }
             }
 
@@ -1372,5 +1429,30 @@
 
             return serviceObject;
         }
+        
+        public void removedService( ServiceReference reference, Object service )
+        {
+            // check whether we can take back the configuration objects
+            String factoryPid = ( String ) reference.getProperty( Constants.SERVICE_PID );
+            if ( factoryPid != null )
+            {
+                Factory factory = cm.getCachedFactory( factoryPid );
+                if ( factory != null )
+                {
+                    for ( Iterator pi = factory.getPIDs().iterator(); pi.hasNext(); )
+                    {
+                        String pid = ( String ) pi.next();
+                        ConfigurationImpl cfg = cm.getCachedConfiguration( pid );
+                        if ( cfg != null )
+                        {
+                            cfg.setDelivered( false );
+                        }
+                    }
+                }
+            }
+
+            super.removedService( reference, service );
+        }
+
     }
 }