FELIX-1488 Only persist static configuration bindings and keep dynamic
bindings in-memory only (also fixes FELIX-1484)

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@804740 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java
index 0ad02e8..b126eef 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -84,7 +84,7 @@
     {
         configurationAdmin.checkPermission();
         checkDeleted();
-        delegatee.setBundleLocation( bundleLocation );
+        delegatee.setBundleLocation( bundleLocation, true );
     }
 
 
@@ -117,7 +117,7 @@
     public Dictionary getProperties()
     {
         checkDeleted();
-        
+
         // return a deep copy since the spec says, that modification of
         // any value should not modify the internal, stored value
         return delegatee.getProperties( true );
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java
index 61c07c7..a1a6102 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java
@@ -95,7 +95,8 @@
 
         if ( config.getBundleLocation() == null )
         {
-            config.setBundleLocation( this.getBundle().getLocation() );
+            // statically bind the configuration
+            config.setBundleLocation( this.getBundle().getLocation(), true );
         }
         else if ( !config.getBundleLocation().equals( this.getBundle().getLocation() ) )
         {
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 395ebe3..d67b549 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
@@ -92,6 +92,12 @@
     private volatile String bundleLocation;
 
     /**
+     * Flag indicating whether this configuration object is statically or
+     * dynamically bound.
+     */
+    private volatile boolean staticallyBound;
+
+    /**
      * The <code>ServiceReference</code> of the serviceReference which first asked for
      * this configuration. This field is <code>null</code> if the configuration
      * has not been handed to a serviceReference by way of the <code>ManagedService.update(Dictionary)</code>
@@ -140,6 +146,7 @@
         this.pid = ( String ) properties.remove( Constants.SERVICE_PID );
         this.factoryPID = ( String ) properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
         this.bundleLocation = ( String ) properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
+        this.staticallyBound = bundleLocation != null;
 
         // set the properties internally
         configureFromPersistence( properties );
@@ -165,6 +172,7 @@
         this.pid = pid;
         this.factoryPID = factoryPid;
         this.bundleLocation = bundleLocation;
+        this.staticallyBound = bundleLocation != null;
         this.properties = null;
 
         // this is a new configuration object, store immediately unless
@@ -172,7 +180,7 @@
         // case the configuration is only stored when first updated
         if (factoryPid == null) {
             Dictionary props = new Hashtable();
-            setAutoProperties( props, true );
+            setAutoProperties( props, staticallyBound );
             props.put( CONFIGURATION_NEW, Boolean.TRUE );
             persistenceManager.store( pid, props );
         }
@@ -281,20 +289,30 @@
     /* (non-Javadoc)
      * @see org.osgi.service.cm.Configuration#setBundleLocation(java.lang.String)
      */
-    public void setBundleLocation( String bundleLocation )
+    public void setBundleLocation( String bundleLocation, boolean staticBinding )
     {
         if ( !isDeleted() )
         {
-            this.bundleLocation = bundleLocation;
+            if ( staticBinding )
+            {
+                // set binding and mark static and store everything
+                this.bundleLocation = bundleLocation;
+                this.staticallyBound = staticBinding;
 
-            // 104.15.2.8 The bundle location will be set persistently
-            try
-            {
-                store();
+                // 104.15.2.8 The bundle location will be set persistently
+                try
+                {
+                    store();
+                }
+                catch ( IOException ioe )
+                {
+                    configurationManager.log( LogService.LOG_ERROR, "Persisting new bundle location failed", ioe );
+                }
             }
-            catch ( IOException ioe )
+            else if ( !staticallyBound )
             {
-                configurationManager.log( LogService.LOG_ERROR, "Persisting new bundle location failed", ioe );
+                // dynamic binding, no need to store
+                this.bundleLocation = bundleLocation;
             }
         }
     }
@@ -339,7 +357,7 @@
         {
             CaseInsensitiveDictionary newProperties = new CaseInsensitiveDictionary( properties );
 
-            setAutoProperties( newProperties, true );
+            setAutoProperties( newProperties, staticallyBound );
 
             localPersistenceManager.store( pid, newProperties );
 
@@ -422,10 +440,11 @@
         {
             props = new Hashtable();
 
-            // add automatic properties including the bundle location (if set)
-            setAutoProperties( props, true );
+            // add automatic properties including the bundle location (if
+            // statically bound)
+            setAutoProperties( props, staticallyBound );
         }
-        else if ( getBundleLocation() != null )
+        else if ( getBundleLocation() != null && staticallyBound )
         {
             props.put( ConfigurationAdmin.SERVICE_BUNDLELOCATION, getBundleLocation() );
         }
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 1bd31ff..98120b0 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
@@ -135,7 +135,7 @@
     private int pmtCount;
 
     // the cache of Factory instances mapped by their factory PID
-    private Map factories;
+    private final Map factories = new HashMap();
 
     // the cache of Configuration instances mapped by their PID
     // have this always set to prevent NPE on bundle shutdown
@@ -173,7 +173,6 @@
 
         // set up some fields
         this.bundleContext = bundleContext;
-        this.factories = new HashMap();
 
         // configurationlistener support
         configurationListenerTracker = new ServiceTracker( bundleContext, ConfigurationListener.class.getName(), null );
@@ -275,12 +274,18 @@
             logTracker.close();
         }
 
-        // just ensure the configuration cache is cleared, not cleaning
+        // just ensure the configuration cache is empty
         synchronized ( configurations )
         {
             configurations.clear();
         }
 
+        // just ensure the factory cache is empty
+        synchronized ( factories )
+        {
+            factories.clear();
+        }
+
         this.bundleContext = null;
     }
 
@@ -296,11 +301,12 @@
     }
 
 
-    Iterator getCachedConfigurations()
+    ConfigurationImpl[] getCachedConfigurations()
     {
         synchronized ( configurations )
         {
-            return configurations.values().iterator();
+            return ( ConfigurationImpl[] ) configurations.values().toArray(
+                new ConfigurationImpl[configurations.size()] );
         }
     }
 
@@ -330,6 +336,34 @@
     }
 
 
+    Factory getCachedFactory( String factoryPid )
+    {
+        synchronized ( factories )
+        {
+            return ( Factory ) factories.get( factoryPid );
+        }
+    }
+
+
+    Factory[] getCachedFactories()
+    {
+        synchronized ( factories )
+        {
+            return ( Factory[] ) factories.values().toArray( new Factory[factories.size()] );
+        }
+    }
+
+
+    void cacheFactory( Factory factory )
+    {
+        synchronized ( factories )
+        {
+            factories.put( factory.getFactoryPid(), factory );
+        }
+    }
+
+
+
     // ---------- ConfigurationAdminImpl support -------------------------------
 
     /*
@@ -502,57 +536,32 @@
     {
         if ( event.getType() == BundleEvent.UNINSTALLED && handleBundleEvents )
         {
-            String location = event.getBundle().getLocation();
+            final String location = event.getBundle().getLocation();
 
-            try
+            // we only reset dynamic bindings, which are only present in
+            // cached configurations, hence only consider cached configs here
+            final ConfigurationImpl[] configs = getCachedConfigurations();
+            for ( int i = 0; i < configs.length; i++ )
             {
-                PersistenceManager[] pmList = getPersistenceManagers();
-                for ( int i = 0; i < pmList.length; i++ )
+                final ConfigurationImpl cfg = configs[i];
+                if ( location.equals( cfg.getBundleLocation() ) )
                 {
-                    Enumeration configs = pmList[i].getDictionaries();
-                    while ( configs.hasMoreElements() )
-                    {
-                        Dictionary config = ( Dictionary ) configs.nextElement();
-
-                        String pid = ( String ) config.get( Constants.SERVICE_PID );
-                        if ( pid != null )
-                        {
-                            ConfigurationImpl cfg = getCachedConfiguration( pid );
-                            if ( cfg == null )
-                            {
-                                cfg = new ConfigurationImpl( this, pmList[i], config );
-                            }
-
-                            if ( location.equals( cfg.getBundleLocation() ) )
-                            {
-                                cfg.setBundleLocation( null );
-                            }
-                        }
-                        else
-                        {
-
-                            Factory factory = Factory.getFactory( pmList[i], config );
-                            if ( factory != null )
-                            {
-                                Factory cachedFactory = getCachedFactory( factory.getFactoryPid() );
-                                if ( cachedFactory != null )
-                                {
-                                    factory = cachedFactory;
-                                }
-
-                                if ( location.equals( factory.getBundleLocation() ) )
-                                {
-                                    factory.setBundleLocation( null );
-                                }
-                            }
-                        }
-                    }
+                    // reset dynamic binding
+                    cfg.setBundleLocation( null, false );
                 }
-
             }
-            catch ( Exception e )
+
+            // we only reset dynamic bindings, which are only present in
+            // cached factories, hence only consider cached factories here
+            final Factory[] factories = getCachedFactories();
+            for ( int i = 0; i < factories.length; i++ )
             {
-                log( LogService.LOG_WARNING, "Problem unbinding configurations for bundle " + location, e );
+                final Factory factory = factories[i];
+                if ( location.equals( factory.getBundleLocation() ) )
+                {
+                    // reset dynamic binding
+                    factory.setBundleLocation( null, false );
+                }
             }
         }
     }
@@ -619,11 +628,10 @@
         if ( pid != null )
         {
             ManagedServiceUpdate update = new ManagedServiceUpdate( pid, sr, service );
-            updateThread.schedule( update );
+                updateThread.schedule( update );
+            }
         }
 
-    }
-
 
     private void configure( ServiceReference sr, ManagedServiceFactory service )
     {
@@ -631,11 +639,10 @@
         if ( pid != null )
         {
             ManagedServiceFactoryUpdate update = new ManagedServiceFactoryUpdate( pid, sr, service );
-            updateThread.schedule( update );
+                updateThread.schedule( update );
+            }
         }
 
-    }
-
 
     /**
      * Factory method to create a new configuration object. The configuration
@@ -647,13 +654,13 @@
      *            <code>null</code>.
      * @param factoryPid
      *            The factory PID of the new configuration. Not
-     *            <code>null</code> if the new configuration object belongs to
-     *            a factory. The configuration object will not be persisted if
+     *            <code>null</code> if the new configuration object belongs to a
+     *            factory. The configuration object will not be persisted if
      *            this parameter is not <code>null</code>.
      * @param bundleLocation
      *            The bundle location of the bundle to which the configuration
-     *            belongs or <code>null</code> if the configuration is not
-     *            bound yet.
+     *            belongs or <code>null</code> if the configuration is not bound
+     *            yet.
      * @return The new configuration object
      * @throws IOException
      *             May be thrown if an error occurrs persisting the new
@@ -665,24 +672,6 @@
     }
 
 
-    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 = getCachedFactory( factoryPid );
@@ -938,7 +927,8 @@
                 String bundleLocation = serviceBundle.getLocation();
                 if ( cfg.getBundleLocation() == null )
                 {
-                    cfg.setBundleLocation( bundleLocation );
+                    // dynamically bind to the location of the service if unbound
+                    cfg.setBundleLocation( bundleLocation, false );
                 }
                 else if ( !bundleLocation.equals( cfg.getBundleLocation() ) )
                 {
@@ -1048,7 +1038,7 @@
             if ( factory.getBundleLocation() == null )
             {
                 // bind to the location of the service if unbound
-                factory.setBundleLocation( bundleLocation );
+                factory.setBundleLocation( bundleLocation, false );
             }
             else if ( !bundleLocation.equals( factory.getBundleLocation() ) )
             {
@@ -1114,8 +1104,8 @@
                 // check bundle location of configuration
                 if ( cfg.getBundleLocation() == null )
                 {
-                    // bind to the location of the service if unbound
-                    cfg.setBundleLocation( bundleLocation );
+                    // dynamically bind to the location of the service if unbound
+                    cfg.setBundleLocation( bundleLocation, false );
                 }
                 else if ( !bundleLocation.equals( cfg.getBundleLocation() ) )
                 {
@@ -1221,9 +1211,8 @@
                             String bundleLocation = sr.getBundle().getLocation();
                             if ( config.getBundleLocation() == null )
                             {
-                                // bind to the location of the service if
-                                // unbound
-                                config.setBundleLocation( bundleLocation );
+                                // dynamically bind to the location of the service if unbound
+                                config.setBundleLocation( bundleLocation, false );
                             }
                             else if ( !bundleLocation.equals( config.getBundleLocation() ) )
                             {
@@ -1269,9 +1258,8 @@
                             String bundleLocation = sr.getBundle().getLocation();
                             if ( config.getBundleLocation() == null )
                             {
-                                // bind to the location of the service if
-                                // unbound
-                                config.setBundleLocation( bundleLocation );
+                                // dynamically bind to the location of the service if unbound
+                                config.setBundleLocation( bundleLocation, false );
                             }
                             else if ( !bundleLocation.equals( config.getBundleLocation() ) )
                             {
@@ -1493,12 +1481,12 @@
             if ( pid != null )
             {
                 ConfigurationImpl cfg = cm.getCachedConfiguration( pid );
-                if ( cfg != null && reference.equals( cfg.getServiceReference() ) )
-                {
-                    cfg.setServiceReference( null );
-                    cfg.setDelivered( false );
+                    if ( cfg != null && reference.equals( cfg.getServiceReference() ) )
+                    {
+                        cfg.setServiceReference( null );
+                        cfg.setDelivered( false );
+                    }
                 }
-            }
 
             super.removedService( reference, service );
         }
@@ -1556,6 +1544,7 @@
             return serviceObject;
         }
 
+
         public void removedService( ServiceReference reference, Object service )
         {
             // check whether we can take back the configuration objects
@@ -1563,19 +1552,19 @@
             if ( factoryPid != null )
             {
                 Factory factory = cm.getCachedFactory( factoryPid );
-                if ( factory != null )
-                {
-                    for ( Iterator pi = factory.getPIDs().iterator(); pi.hasNext(); )
+                    if ( factory != null )
                     {
-                        String pid = ( String ) pi.next();
-                        ConfigurationImpl cfg = cm.getCachedConfiguration( pid );
-                        if ( cfg != null )
+                        for ( Iterator pi = factory.getPIDs().iterator(); pi.hasNext(); )
                         {
-                            cfg.setDelivered( false );
+                            String pid = ( String ) pi.next();
+                            ConfigurationImpl cfg = cm.getCachedConfiguration( pid );
+                            if ( cfg != null )
+                            {
+                                cfg.setDelivered( false );
+                            }
                         }
                     }
                 }
-            }
 
             super.removedService( reference, service );
         }
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/Factory.java b/configadmin/src/main/java/org/apache/felix/cm/impl/Factory.java
index 958ce9f..366090d 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/Factory.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/Factory.java
@@ -49,6 +49,9 @@
     // the bundle location to which factory PID mapping is bound
     private String bundleLocation;
 
+    // whether the factory is statically bound to a bundle or not
+    private boolean staticallyBound;
+
     // the set of configuration PIDs belonging to this factory
     private Set pids;
 
@@ -89,7 +92,9 @@
     {
         this.persistenceManager = persistenceManager;
         this.factoryPid = factoryPid;
-        pids = new HashSet();
+        this.pids = new HashSet();
+        this.bundleLocation = null;
+        this.staticallyBound = false;
     }
 
 
@@ -98,7 +103,8 @@
         this( persistenceManager, factoryPid );
 
         // set bundle location
-        bundleLocation = ( String ) props.get( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
+        this.bundleLocation = ( String ) props.get( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
+        this.staticallyBound = this.bundleLocation != null;
 
         // set pids
         String[] pidList = ( String[] ) props.get( FACTORY_PID_LIST );
@@ -130,12 +136,20 @@
     }
 
 
-    void setBundleLocation( String bundleLocation )
+    void setBundleLocation( String bundleLocation, boolean staticBinding )
     {
-        this.bundleLocation = bundleLocation;
+        if ( staticBinding )
+        {
+            this.bundleLocation = bundleLocation;
+            this.staticallyBound = true;
 
-        // 104.15.2.8 The bundle location will be set persistently
-        storeSilently();
+            // 104.15.2.8 The bundle location will be set persistently
+            storeSilently();
+        }
+        else if ( !this.staticallyBound )
+        {
+            this.bundleLocation = bundleLocation;
+        }
     }
 
 
@@ -161,7 +175,7 @@
     {
         Hashtable props = new Hashtable();
 
-        if ( bundleLocation != null )
+        if ( bundleLocation != null && staticallyBound )
         {
             props.put( ConfigurationAdmin.SERVICE_BUNDLELOCATION, this.getBundleLocation() );
         }