FELIX-3176 Implement Configuration Admin 4.1 changes:
  - CM_LOCATION_CHANGED event
  - reassign config on location changes
  - support for new configuration Regions
  - Factory (internal mechanism) are not location bound any more
  - Each factory configuration instance is individually bound
  - adapt integration tests
  - temporarily include ConfigurationEvent and ConfigurationPermission
    classes from the OSGi repo for two new constants
  - export cm package at version 1.4 (and reimport [1.4,1.5))

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1186765 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/pom.xml b/configadmin/pom.xml
index e3abe64..fe87aab 100644
--- a/configadmin/pom.xml
+++ b/configadmin/pom.xml
@@ -32,7 +32,7 @@
 
     <name>Apache Felix Configuration Admin Service</name>
     <description>
-        Implementation of the OSGi Configuration Admin Service Specification 1.2
+        Implementation of the OSGi Configuration Admin Service Specification 1.4
     </description>
 
     <scm>
@@ -149,13 +149,14 @@
                         <Export-Package>
                             org.apache.felix.cm;
                             org.apache.felix.cm.file;version=${api-package-version};provide:=true,
-                            org.osgi.service.cm;provide:=true
+                            org.osgi.service.cm;version=1.4;-split-package:=merge-first;provide:=true
                         </Export-Package>
                         <Private-Package>
                             org.apache.felix.cm.impl,
                             org.osgi.util.tracker
                         </Private-Package>
                         <Import-Package>
+                            org.osgi.service.cm;version="[1.4,1.5)",
                             *
                         </Import-Package>
                         <DynamicImport-Package>
@@ -163,7 +164,7 @@
                         </DynamicImport-Package>
 						<Export-Service>
 							org.osgi.service.cm.ConfigurationAdmin;
-								service.description="Configuration Admin Service Specification 1.3 Implementation";
+								service.description="Configuration Admin Service Specification 1.4 Implementation";
 								service.pid="org.osgi.service.cm.ConfigurationAdmin";
 								service.vendor="Apache Software Foundation",
 							org.apache.felix.cm.PersistenceManager;
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java b/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
index 7c118ca..b4076c7 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
@@ -63,28 +63,31 @@
     {
         this();
 
-        Enumeration keys = props.keys();
-        while ( keys.hasMoreElements() )
+        if ( props != null )
         {
-            Object key = keys.nextElement();
-
-            // check the correct syntax of the key
-            checkKey( key );
-
-            // check uniqueness of key
-            String lowerCase = ( ( String ) key ).toLowerCase();
-            if ( internalMap.containsKey( lowerCase ) )
+            Enumeration keys = props.keys();
+            while ( keys.hasMoreElements() )
             {
-                throw new IllegalArgumentException( "Key [" + key + "] already present in different case" );
+                Object key = keys.nextElement();
+
+                // check the correct syntax of the key
+                checkKey( key );
+
+                // check uniqueness of key
+                String lowerCase = ( ( String ) key ).toLowerCase();
+                if ( internalMap.containsKey( lowerCase ) )
+                {
+                    throw new IllegalArgumentException( "Key [" + key + "] already present in different case" );
+                }
+
+                // check the value
+                Object value = props.get( key );
+                value = checkValue( value );
+
+                // add the key/value pair
+                internalMap.put( lowerCase, value );
+                originalKeys.put( lowerCase, key );
             }
-
-            // check the value
-            Object value = props.get( key );
-            value = checkValue( value );
-
-            // add the key/value pair
-            internalMap.put( lowerCase, value );
-            originalKeys.put( lowerCase, key );
         }
     }
 
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 b62b133..396f564 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
@@ -70,7 +70,8 @@
      */
     public String getBundleLocation()
     {
-        configurationAdmin.checkPermission();
+        // CM 1.4 / 104.13.2.4
+        configurationAdmin.checkPermission( delegatee.getBundleLocation() );
         checkDeleted();
         return delegatee.getBundleLocation();
     }
@@ -82,7 +83,9 @@
      */
     public void setBundleLocation( String bundleLocation )
     {
-        configurationAdmin.checkPermission();
+        // CM 1.4 / 104.13.2.4
+        configurationAdmin.checkPermission( delegatee.getBundleLocation() );
+        configurationAdmin.checkPermission( bundleLocation );
         checkDeleted();
         delegatee.setStaticBundleLocation( bundleLocation );
     }
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 d5d4517..73bf15a 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
@@ -79,7 +79,8 @@
      */
     public Configuration createFactoryConfiguration( String factoryPid, String location ) throws IOException
     {
-        this.checkPermission();
+        // CM 1.4 / 104.13.2.3
+        this.checkPermission( location );
 
         return this.wrap( configurationManager.createFactoryConfiguration( factoryPid, location ) );
     }
@@ -98,7 +99,8 @@
         }
         else if ( !config.getBundleLocation().equals( this.getBundle().getLocation() ) )
         {
-            this.checkPermission();
+            // CM 1.4 / 104.13.2.3
+            this.checkPermission( config.getBundleLocation() );
         }
 
         return this.wrap( config );
@@ -110,9 +112,17 @@
      */
     public Configuration getConfiguration( String pid, String location ) throws IOException
     {
-        this.checkPermission();
+        // CM 1.4 / 104.13.2.3
+        this.checkPermission( location );
 
-        return this.wrap( configurationManager.getConfiguration( pid, location ) );
+        ConfigurationImpl config = configurationManager.getConfiguration( pid, location );
+        if ( config.getBundleLocation() != null )
+        {
+            // CM 1.4 / 104.13.2.3
+            this.checkPermission( config.getBundleLocation() );
+        }
+
+        return this.wrap( config );
     }
 
 
@@ -149,11 +159,11 @@
      * Returns <code>true</code> if the current access control context (call
      * stack) has the CONFIGURE permission.
      */
-    boolean hasPermission()
+    boolean hasPermission(String name)
     {
         try
         {
-            checkPermission();
+            checkPermission(name);
             return true;
         }
         catch ( SecurityException se )
@@ -164,20 +174,30 @@
 
 
     /**
-     * Checks whether the current access control context (call stack) has the
-     * <code>CONFIGURE</code> permission and throws a
+     * Checks whether the current access control context (call stack) has
+     * the given permission for the given bundle location and throws a
      * <code>SecurityException</code> if this is not the case.
      *
-     * @throws SecurityException if the access control context does not have the
-     *             <code>CONFIGURE</code> permission.
+     * @param name The bundle location to check for permission. If
+     *   <code>null</code> assumes <code>*</code>.
+     * @param permission The actual permission to check. This must be one
+     *    of the constants defined in the
+     *    <code>ConfigurationPermission</code> class.
+     *
+     * @throws SecurityException if the access control context does not
+     *      have the appropriate permission
      */
-    void checkPermission()
+    void checkPermission( String name )
     {
         // the caller's permission must be checked
         final SecurityManager sm = System.getSecurityManager();
         if ( sm != null )
         {
-            sm.checkPermission( new ConfigurationPermission( "*", ConfigurationPermission.CONFIGURE ) );
+            if (name == null) {
+                name = "*";
+            }
+
+            sm.checkPermission( new ConfigurationPermission( name, ConfigurationPermission.CONFIGURE ) );
         }
     }
 
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationBase.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationBase.java
index 3f4e839..1d9a8b4 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationBase.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationBase.java
@@ -22,7 +22,6 @@
 import java.io.IOException;
 import java.util.Dictionary;
 import org.apache.felix.cm.PersistenceManager;
-import org.osgi.framework.ServiceReference;
 import org.osgi.service.log.LogService;
 
 
@@ -41,30 +40,8 @@
     // the basic ID of this instance
     private final String baseId;
 
-    /**
-     * The statically bound bundle location, which is set explicitly by calling
-     * the Configuration.setBundleLocation(String) method.
-     */
-    private volatile String staticBundleLocation;
-
-    /**
-     * The bundle location from dynamic binding. This value is set as the
-     * configuration or factory is assigned to a ManagedService[Factory].
-     */
-    private volatile String dynamicBundleLocation;
-
-    /**
-     * The <code>ServiceReference</code> of the serviceReference which first
-     * asked for this instance. This field is <code>null</code> if the instance
-     * has not been handed to a serviceReference by way of the
-     * <code>ManagedService.update(Dictionary)</code> or
-     * <code>ManagedServiceFactory.updated(String, Dictionary)</code> method.
-     */
-    private volatile ServiceReference serviceReference;
-
-
     protected ConfigurationBase( final ConfigurationManager configurationManager,
-        final PersistenceManager persistenceManager, final String baseId, final String bundleLocation )
+        final PersistenceManager persistenceManager, final String baseId )
     {
         if ( configurationManager == null )
         {
@@ -79,10 +56,6 @@
         this.configurationManager = configurationManager;
         this.persistenceManager = persistenceManager;
         this.baseId = baseId;
-
-        // set bundle location from persistence and/or check for dynamic binding
-        this.staticBundleLocation = bundleLocation;
-        this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( baseId );
     }
 
 
@@ -104,168 +77,6 @@
     }
 
 
-    /**
-     * Returns the "official" bundle location as visible from the outside
-     * world of code calling into the Configuration.getBundleLocation() method.
-     * <p>
-     * In other words: The {@link #getStaticBundleLocation()} is returned if
-     * not <code>null</code>. Otherwise the {@link #getDynamicBundleLocation()}
-     * is returned (which may also be <code>null</code>).
-     */
-    String getBundleLocation()
-    {
-        if ( staticBundleLocation != null )
-        {
-            return staticBundleLocation;
-        }
-
-        return dynamicBundleLocation;
-    }
-
-
-    /**
-     * Returns the bundle location according to actual configuration binding.
-     * <p>
-     * This may be different from the {@link #getBundleLocation()} result if
-     * the <code>Configuration.setBundleLocation(String)</code> method has been
-     * called after the configuration has been dynamically bound to a bundle.
-     * <p>
-     * In other words: The {@link #getDynamicBundleLocation()} is returned if
-     * not <code>null</code>. Otherwise the {@link #getStaticBundleLocation()}
-     * is returned (which may also be <code>null</code>).
-     */
-    String getBoundBundleLocation()
-    {
-        if ( dynamicBundleLocation != null )
-        {
-            return dynamicBundleLocation;
-        }
-
-        return staticBundleLocation;
-    }
-
-
-    String getDynamicBundleLocation()
-    {
-        return dynamicBundleLocation;
-    }
-
-
-    String getStaticBundleLocation()
-    {
-        return staticBundleLocation;
-    }
-
-
-    void setServiceReference( ServiceReference serviceReference )
-    {
-        this.serviceReference = serviceReference;
-    }
-
-
-    ServiceReference getServiceReference()
-    {
-        return serviceReference;
-    }
-
-
-    void setStaticBundleLocation( final String bundleLocation )
-    {
-        // 104.15.2.8 The bundle location will be set persistently
-        this.staticBundleLocation = bundleLocation;
-        storeSilently();
-
-        // FELIX-1488: If a configuration is bound to a location and a new
-        // location is statically set, the old binding must be removed
-        // by removing the configuration from the targets and the new binding
-        // must be setup by updating the configuration for new targets
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the static bundle location.
-         * The following code is therefore disabled for now
-        if ( ( this instanceof ConfigurationImpl ) && ( bundleLocation != null ) )
-        {
-            // remove configuration from current managed service [factory]
-            if ( getDynamicBundleLocation() != null && !bundleLocation.equals( getDynamicBundleLocation() ) )
-            {
-                getConfigurationManager().revokeConfiguration( ( ConfigurationImpl ) this );
-            }
-
-            // check whether we have to assign the configuration to new targets
-            getConfigurationManager().reassignConfiguration( ( ConfigurationImpl ) this );
-        }
-         *
-         */
-
-        // corrollary to not reassigning configurations: we have to check
-        // whether the dynamic bundle location is different from the static
-        // now. If so, the dynamic bundle location has to be removed.
-        // also set the service reference to null because we assume to not
-        // be bound to a service anymore
-        if ( bundleLocation != null && getDynamicBundleLocation() != null
-            && !bundleLocation.equals( getDynamicBundleLocation() ) )
-        {
-            setDynamicBundleLocation( null );
-            setServiceReference( null );
-        }
-    }
-
-
-    void setDynamicBundleLocation( final String bundleLocation )
-    {
-        this.dynamicBundleLocation = bundleLocation;
-        this.configurationManager.setDynamicBundleLocation( this.getBaseId(), bundleLocation );
-
-        // FELIX-1488: If a dynamically bound configuration is unbound and not
-        // statically bound, it may be rebound to another bundle asking for it
-        // (unless the dynamic unbind happens due to configuration deletion)
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the static bundle location.
-         * The following code is therefore disabled for now
-        if ( bundleLocation == null && getStaticBundleLocation() == null && ( this instanceof ConfigurationImpl ) )
-        {
-            getConfigurationManager().reassignConfiguration( ( ConfigurationImpl ) this );
-        }
-         *
-         */
-    }
-
-
-    /**
-     * Tries to bind this configuration or factory to the given bundle location:
-     * <ul>
-     * <li>If already dynamically bound, <code>true</code> is returned if the
-     * dynamic binding equals the desired binding. Otherwise <code>false</code>
-     * is returned.</li>
-     * <li>If not dynamically bound but statically bound and the static binding
-     * is not equal to the desired binding, <code>false</code> is returned.</li>
-     * <li>Otherwise this configuration or factory is dynamically bound to the
-     * desired location and <code>true</code> is returned.</li>
-     * </ul>
-     *
-     * @param bundleLocation
-     *            The desired bundle location to which this configuration or
-     *            factory should be dynamically bound.
-     * @return <code>true</code> if this configuration or factory is dynamically
-     *         bound to the desired location.
-     */
-    boolean tryBindLocation( final String bundleLocation )
-    {
-        if ( this.dynamicBundleLocation != null )
-        {
-            return this.dynamicBundleLocation.equals( bundleLocation );
-        }
-        else if ( this.staticBundleLocation != null && !this.staticBundleLocation.equals( bundleLocation ) )
-        {
-            return false;
-        }
-
-        setDynamicBundleLocation( bundleLocation );
-        return true;
-    }
-
-
     abstract void store() throws IOException;
 
 
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 2ff0b4d..676c33a 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
@@ -115,12 +115,25 @@
     private static final String CONFIGURATION_NEW = "_felix_.cm.newConfiguration";
 
     /**
-     * The factory serviceReference PID of this configuration or <code>null</code> if this
+     * The factory PID of this configuration or <code>null</code> if this
      * is not a factory configuration.
      */
     private final String factoryPID;
 
     /**
+     * The statically bound bundle location, which is set explicitly by calling
+     * the Configuration.setBundleLocation(String) method or when the
+     * configuration was created with the two-argument method.
+     */
+    private volatile String staticBundleLocation;
+
+    /**
+     * The bundle location from dynamic binding. This value is set as the
+     * configuration or factory is assigned to a ManagedService[Factory].
+     */
+    private volatile String dynamicBundleLocation;
+
+    /**
      * The configuration data of this configuration instance. This is a private
      * copy of the properties of which a copy is made when the
      * {@link #getProperties()} method is called. This field is
@@ -159,13 +172,16 @@
     ConfigurationImpl( ConfigurationManager configurationManager, PersistenceManager persistenceManager,
         Dictionary properties )
     {
-        super( configurationManager, persistenceManager, ( String ) properties.remove( Constants.SERVICE_PID ),
-            ( String ) properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) );
+        super( configurationManager, persistenceManager, ( String ) properties.remove( Constants.SERVICE_PID ) );
 
         this.factoryPID = ( String ) properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
         this.isDeleted = false;
         this.lastUpdatedTime = -1;
 
+        // set bundle location from persistence and/or check for dynamic binding
+        this.staticBundleLocation = ( String ) properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) ;
+        this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( getBaseId() );
+
         // set the properties internally
         configureFromPersistence( properties );
     }
@@ -174,12 +190,16 @@
     ConfigurationImpl( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String pid,
         String factoryPid, String bundleLocation ) throws IOException
     {
-        super( configurationManager, persistenceManager, pid, bundleLocation );
+        super( configurationManager, persistenceManager, pid );
 
         this.factoryPID = factoryPid;
         this.isDeleted = false;
         this.lastUpdatedTime = -1;
 
+        // set bundle location from persistence and/or check for dynamic binding
+        this.staticBundleLocation = bundleLocation;
+        this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( getBaseId() );
+
         // first "update"
         this.properties = null;
         setLastModificationTime();
@@ -218,6 +238,96 @@
     }
 
 
+
+
+    /**
+     * Returns the "official" bundle location as visible from the outside
+     * world of code calling into the Configuration.getBundleLocation() method.
+     * <p>
+     * In other words: The {@link #getStaticBundleLocation()} is returned if
+     * not <code>null</code>. Otherwise the {@link #getDynamicBundleLocation()}
+     * is returned (which may also be <code>null</code>).
+     */
+    String getBundleLocation()
+    {
+        if ( staticBundleLocation != null )
+        {
+            return staticBundleLocation;
+        }
+
+        return dynamicBundleLocation;
+    }
+
+
+    String getDynamicBundleLocation()
+    {
+        return dynamicBundleLocation;
+    }
+
+
+    String getStaticBundleLocation()
+    {
+        return staticBundleLocation;
+    }
+
+
+    void setStaticBundleLocation( final String bundleLocation )
+    {
+        // CM 1.4; needed for bundle location change at the end
+        final String oldBundleLocation = getBundleLocation();
+
+        // 104.15.2.8 The bundle location will be set persistently
+        this.staticBundleLocation = bundleLocation;
+        storeSilently();
+
+        // check whether the dynamic bundle location is different from the
+        // static now. If so, the dynamic bundle location has to be
+        // removed.
+        if ( bundleLocation != null && getDynamicBundleLocation() != null
+            && !bundleLocation.equals( getDynamicBundleLocation() ) )
+        {
+            setDynamicBundleLocation( null, false );
+        }
+
+        // CM 1.4
+        this.getConfigurationManager().locationChanged( this, oldBundleLocation );
+    }
+
+
+    void setDynamicBundleLocation( final String bundleLocation, final boolean dispatchConfiguration )
+    {
+        // CM 1.4; needed for bundle location change at the end
+        final String oldBundleLocation = getBundleLocation();
+
+        this.dynamicBundleLocation = bundleLocation;
+        this.getConfigurationManager().setDynamicBundleLocation( this.getBaseId(), bundleLocation );
+
+        // CM 1.4
+        if ( dispatchConfiguration )
+        {
+            this.getConfigurationManager().locationChanged( this, oldBundleLocation );
+
+        }
+    }
+
+
+    /**
+     * Dynamically binds this configuration to the given location unless
+     * the configuration is already bound (statically or dynamically). In
+     * the case of this configuration to be dynamically bound a
+     * <code>CM_LOCATION_CHANGED</code> event is dispatched.
+     */
+    boolean tryBindLocation( final String bundleLocation )
+    {
+        if ( this.getBundleLocation() == null )
+        {
+            setDynamicBundleLocation( bundleLocation, true );
+        }
+
+        return true;
+    }
+
+
     /**
      * Returns an optionally deep copy of the properties of this configuration
      * instance.
@@ -451,7 +561,10 @@
     {
         synchronized ( this )
         {
-            this.lastUpdatedTime = lastModificationTime;
+            if ( this.lastUpdatedTime < lastModificationTime )
+            {
+                this.lastUpdatedTime = lastModificationTime;
+            }
         }
     }
 
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 b94e322..f734da1 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
@@ -95,9 +95,6 @@
     // configurations
     private static Random numberGenerator;
 
-    // comparator used to keep the ordered persistence manager map
-    private static final Comparator cmRankComp = new RankingComparator( true, ConfigurationPlugin.CM_RANKING );
-
     // the BundleContext of the Configuration Admin Service bundle
     private BundleContext bundleContext;
 
@@ -418,45 +415,16 @@
     }
 
 
-    // ---------- ConfigurationAdminImpl support
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see org.osgi.service.cm.ConfigurationAdmin#createFactoryConfiguration(java.lang.String)
-     */
     ConfigurationImpl createFactoryConfiguration( ConfigurationAdminImpl configurationAdmin, String factoryPid )
         throws IOException
     {
-        // check Persmission if factory is bound to another bundle
-        Factory factory = getFactory( factoryPid );
-        if ( factory.getBundleLocation() != null
-            && !factory.getBundleLocation().equals( configurationAdmin.getBundle().getLocation() ) )
-        {
-            configurationAdmin.checkPermission();
-        }
-
-        // create the configuration
-        String pid = createPid( factoryPid );
-        ConfigurationImpl config = createConfiguration( pid, factoryPid, configurationAdmin.getBundle().getLocation() );
-
-        return config;
+        return createFactoryConfiguration( factoryPid, configurationAdmin.getBundle().getLocation() );
     }
 
 
-    /*
-     * (non-Javadoc)
-     *
-     * @see org.osgi.service.cm.ConfigurationAdmin#createFactoryConfiguration(java.lang.String,
-     *      java.lang.String)
-     */
     ConfigurationImpl createFactoryConfiguration( String factoryPid, String location ) throws IOException
     {
-        // create the configuration
-        String pid = createPid( factoryPid );
-        ConfigurationImpl config = createConfiguration( pid, factoryPid, location );
-
-        return config;
+        return createConfiguration( createPid( factoryPid ), factoryPid, location );
     }
 
 
@@ -509,9 +477,6 @@
             filter = bundleContext.createFilter( filterString );
         }
 
-        boolean unprivileged = configurationAdmin != null && !configurationAdmin.hasPermission();
-        String location = unprivileged ? configurationAdmin.getBundle().getLocation() : null;
-
         List configList = new ArrayList();
 
         PersistenceManager[] pmList = getPersistenceManagers();
@@ -529,14 +494,11 @@
                     continue;
                 }
 
-                // ignore this config if not privileged and not bound to bundle
-                if ( unprivileged )
+                // CM 1.4 / 104.13.2.3 Permission required
+                if ( !configurationAdmin.hasPermission( ( String ) config
+                    .get( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) ) )
                 {
-                    Object boundLocation = config.get( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
-                    if ( !location.equals( boundLocation ) )
-                    {
-                        continue;
-                    }
+                    continue;
                 }
 
                 // check filter
@@ -590,6 +552,16 @@
     }
 
 
+    void locationChanged( ConfigurationImpl config, String oldLocation )
+    {
+        fireConfigurationEvent( ConfigurationEvent.CM_LOCATION_CHANGED, config.getPid(), config.getFactoryPid() );
+        if ( oldLocation != null )
+        {
+            updateThread.schedule( new LocationChanged( config, oldLocation ) );
+        }
+    }
+
+
     void fireConfigurationEvent( int type, String pid, String factoryPid )
     {
         FireConfigurationEvent event = new FireConfigurationEvent( type, pid, factoryPid );
@@ -620,19 +592,7 @@
                 final ConfigurationImpl cfg = configs[i];
                 if ( location.equals( cfg.getDynamicBundleLocation() ) )
                 {
-                    cfg.setDynamicBundleLocation( null );
-                }
-            }
-
-            // 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++ )
-            {
-                final Factory factory = factories[i];
-                if ( location.equals( factory.getDynamicBundleLocation() ) )
-                {
-                    factory.setDynamicBundleLocation( null );
+                    cfg.setDynamicBundleLocation( null, true );
                 }
             }
         }
@@ -658,18 +618,15 @@
             else
             {
                 // sort the references according to the cmRanking property
-                SortedSet pms = new TreeSet( new RankingComparator( false ) );
-                for ( int i = 0; i < refs.length; i++ )
+                if ( refs.length > 1 )
                 {
-                    pms.add( refs[i] );
+                    Arrays.sort( refs, RankingComparator.SRV_RANKING );
                 }
 
                 // create the service array from the sorted set of referenecs
-                int pmIndex = 0;
-                for ( Iterator pi = pms.iterator(); pi.hasNext(); pmIndex++ )
+                for ( int i = 0; i < refs.length; i++ )
                 {
-                    ServiceReference ref = ( ServiceReference ) pi.next();
-                    Object service = persistenceManagerTracker.getService( ref );
+                    Object service = persistenceManagerTracker.getService( refs[i] );
                     if ( service != null )
                     {
                         pmList.add( new CachingPersistenceManagerProxy( ( PersistenceManager ) service ) );
@@ -832,32 +789,34 @@
         }
 
         // sort the plugins by their service.cmRanking
-        SortedSet pluginSet = new TreeSet( cmRankComp );
-        for ( int i = 0; i < plugins.length; i++ )
+        if ( plugins.length > 1 )
         {
-            pluginSet.add( plugins[i] );
+            Arrays.sort( plugins, RankingComparator.CM_RANKING );
         }
 
         // call the plugins in order
-        for ( Iterator pi = pluginSet.iterator(); pi.hasNext(); )
+        for ( int i = 0; i < plugins.length; i++ )
         {
-            ServiceReference pluginRef = ( ServiceReference ) pi.next();
+            ServiceReference pluginRef = plugins[i];
             ConfigurationPlugin plugin = ( ConfigurationPlugin ) bundleContext.getService( pluginRef );
-            try
+            if ( plugin != null )
             {
-                plugin.modifyConfiguration( sr, props );
+                try
+                {
+                    plugin.modifyConfiguration( sr, props );
+                }
+                catch ( Throwable t )
+                {
+                    log( LogService.LOG_ERROR, "Unexpected problem calling configuration plugin "
+                        + toString( pluginRef ), t );
+                }
+                finally
+                {
+                    // ensure ungetting the plugin
+                    bundleContext.ungetService( pluginRef );
+                }
+                cfg.setAutoProperties( props, false );
             }
-            catch ( Throwable t )
-            {
-                log( LogService.LOG_ERROR, "Unexpected problem calling configuration plugin " + toString( pluginRef ),
-                    t );
-            }
-            finally
-            {
-                // ensure ungetting the plugin
-                bundleContext.ungetService( pluginRef );
-            }
-            cfg.setAutoProperties( props, false );
         }
     }
 
@@ -1056,8 +1015,225 @@
         }
     }
 
-    // ---------- inner classes ------------------------------------------------
 
+    /**
+     * Checks whether the bundle is allowed to receive the configuration
+     * with the given location binding.
+     * <p>
+     * This method implements the logic defined CM 1.4 / 104.4.1:
+     * <ul>
+     * <li>If the location is <code>null</code> (the configuration is not
+     * bound yet), assume the bundle is allowed</li>
+     * <li>If the location is a single location (no leading "?"), require
+     * the bundle's location to match</li>
+     * <li>If the location is a multi-location (leading "?"), assume the
+     * bundle is allowed if there is no security manager. If there is a
+     * security manager, check whether the bundle has "target" permission
+     * on this location.</li>
+     * </ul>
+     */
+    boolean canReceive( final Bundle bundle, final String location )
+    {
+        if ( location == null )
+        {
+            return true;
+        }
+        else if ( location.startsWith( "?" ) )
+        {
+            // multi-location
+            if ( System.getSecurityManager() != null )
+            {
+                return bundle.hasPermission( new ConfigurationPermission( location, ConfigurationPermission.TARGET ) );
+            }
+            return true;
+        }
+        else
+        {
+            // single location, must match
+            return location.equals( bundle.getLocation() );
+        }
+    }
+
+    // ---------- inner classes
+
+    private ServiceHelper createServiceHelper( ConfigurationImpl config )
+    {
+        if ( config.getFactoryPid() == null )
+        {
+            return new ManagedServiceHelper( config );
+        }
+        return new ManagedServiceFactoryHelper( config );
+    }
+
+    private abstract class ServiceHelper
+    {
+        protected final ConfigurationImpl config;
+
+        private final Dictionary properties;
+
+        protected ServiceHelper( ConfigurationImpl config )
+        {
+            this.config = config;
+            this.properties = config.getProperties( true );
+        }
+
+        final ServiceReference[] getServices( )
+        {
+            try
+            {
+                ServiceReference[] refs = doGetServices();
+                if ( refs != null && refs.length > 1 )
+                {
+                    Arrays.sort( refs, RankingComparator.SRV_RANKING );
+                }
+                return refs;
+            }
+            catch ( InvalidSyntaxException ise )
+            {
+                log( LogService.LOG_ERROR, "Service selection filter is invalid to update " + config, ise );
+            }
+            return null;
+        }
+
+
+        protected abstract ServiceReference[] doGetServices() throws InvalidSyntaxException;
+
+
+        abstract void provide( ServiceReference service );
+
+
+        abstract void remove( ServiceReference service );
+
+
+        protected Dictionary getProperties( String targetPid, ServiceReference service )
+        {
+            Dictionary props = new CaseInsensitiveDictionary( this.properties );
+            callPlugins( props, targetPid, service, config );
+            return props;
+        }
+    }
+
+    private class ManagedServiceHelper extends ServiceHelper
+    {
+
+        protected ManagedServiceHelper( ConfigurationImpl config )
+        {
+            super( config );
+        }
+
+
+        public ServiceReference[] doGetServices() throws InvalidSyntaxException
+        {
+            return bundleContext.getServiceReferences( ManagedService.class.getName(), "(" + Constants.SERVICE_PID
+                + "=" + config.getPid() + ")" );
+        }
+
+
+        public void provide( ServiceReference service )
+        {
+            ManagedService srv = ( ManagedService ) bundleContext.getService( service );
+            if ( srv != null )
+            {
+                try
+                {
+                    Dictionary props = getProperties( this.config.getPid(), service );
+                    srv.updated( props );
+                }
+                catch ( Throwable t )
+                {
+                    handleCallBackError( t, service, config );
+                }
+                finally
+                {
+                    bundleContext.ungetService( service );
+                }
+            }
+        }
+
+
+        public void remove( ServiceReference service )
+        {
+            ManagedService srv = ( ManagedService ) bundleContext.getService( service );
+            try
+            {
+                srv.updated( null );
+            }
+            catch ( Throwable t )
+            {
+                handleCallBackError( t, service, config );
+            }
+            finally
+            {
+                bundleContext.ungetService( service );
+            }
+        }
+
+    }
+
+    private class ManagedServiceFactoryHelper extends ServiceHelper
+    {
+
+        protected ManagedServiceFactoryHelper( ConfigurationImpl config )
+        {
+            super( config );
+            // TODO Auto-generated constructor stub
+        }
+
+
+        public ServiceReference[] doGetServices() throws InvalidSyntaxException
+        {
+            return bundleContext.getServiceReferences( ManagedServiceFactory.class.getName(), "("
+                + Constants.SERVICE_PID + "=" + config.getFactoryPid() + ")" );
+        }
+
+
+        public void provide( ServiceReference service )
+        {
+            ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext.getService( service );
+            if ( srv != null )
+            {
+                try
+                {
+                    Dictionary props = getProperties( this.config.getFactoryPid(), service );
+                    srv.updated( config.getPid(), props );
+                }
+                catch ( Throwable t )
+                {
+                    handleCallBackError( t, service, config );
+                }
+                finally
+                {
+                    bundleContext.ungetService( service );
+                }
+            }
+        }
+
+
+        public void remove( ServiceReference service )
+        {
+            ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext.getService( service );
+            try
+            {
+                srv.deleted( config.getPid() );
+            }
+            catch ( Throwable t )
+            {
+                handleCallBackError( t, service, config );
+            }
+            finally
+            {
+                bundleContext.ungetService( service );
+            }
+        }
+
+    }
+
+    /**
+     * The <code>ManagedServiceUpdate</code> updates a freshly registered
+     * <code>ManagedService</code> with a specific configuration. If a
+     * ManagedService is registered with multiple PIDs an instance of this
+     * class is used for each registered PID.
+     */
     private class ManagedServiceUpdate implements Runnable
     {
         private final String pid;
@@ -1110,8 +1286,7 @@
             // only update configuration if lastModificationTime is less than
             // lastUpdateTime
             Dictionary properties = rawProperties;
-            if ( properties != null && config != null && lastModificationTime <= config.getLastUpdatedTime()
-                && sr.equals( config.getServiceReference() ) )
+            if ( properties != null && config != null && lastModificationTime < config.getLastUpdatedTime() )
             {
                 if ( isLogEnabled( LogService.LOG_DEBUG ) )
                 {
@@ -1127,7 +1302,7 @@
             {
                 if ( isLogEnabled( LogService.LOG_DEBUG ) )
                 {
-                    log( LogService.LOG_DEBUG, "Updating configuration " + config.getPid() + " to modification #"
+                    log( LogService.LOG_DEBUG, "Updating configuration " + pid + " to modification #"
                         + config.getLastModificationTime(), null );
                 }
 
@@ -1142,32 +1317,17 @@
                     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
-                // bundle location
-                // 104.4.1 assign configuration to bundle if unassigned
-                if ( !config.tryBindLocation( serviceBundle.getLocation() ) )
+                // CM 1.4 / 104.13.2.2
+                if ( !canReceive( serviceBundle, config.getBundleLocation() ) )
                 {
-                    log( LogService.LOG_ERROR, "Cannot use configuration " + pid + " for "
-                        + ConfigurationManager.toString( sr ) + ": Configuration bound to bundle "
-                        + config.getBundleLocation(), null );
+                    log( LogService.LOG_ERROR,
+                        "Cannot use configuration " + pid + " for " + ConfigurationManager.toString( sr )
+                            + ": No visibility to configuration bound to " + 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 ( config.getServiceReference() == null )
-                {
-                    // assign the configuration to the service
-                    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 );
-                }
+                // 104.4.2 Dynamic Binding
+                config.tryBindLocation( serviceBundle.getLocation() );
 
                 // prepare the configuration for the service (call plugins)
                 callPlugins( properties, pid, sr, config );
@@ -1207,6 +1367,13 @@
         }
     }
 
+    /**
+     * The <code>ManagedServiceFactoryUpdate</code> updates a freshly
+     * registered <code>ManagedServiceFactory</code> with a specific
+     * configuration. If a ManagedServiceFactory is registered with
+     * multiple PIDs an instance of this class is used for each registered
+     * PID.
+     */
     private class ManagedServiceFactoryUpdate implements Runnable
     {
         private final String factoryPid;
@@ -1215,8 +1382,6 @@
 
         private final ManagedServiceFactory service;
 
-        private final Factory factory;
-
         private final Map configs;
 
         private final Map stamps;
@@ -1293,7 +1458,6 @@
                 log( LogService.LOG_ERROR, "Cannot get factory mapping for factory PID " + factoryPid, ioe );
             }
 
-            this.factory = factory;
             this.configs = configs;
             this.stamps = stamps;
         }
@@ -1312,17 +1476,6 @@
                 return;
             }
 
-            final String serviceBundleLocation = serviceBundle.getLocation();
-            if ( !factory.tryBindLocation( serviceBundleLocation ) )
-            {
-                // factory PID is bound to another bundle
-                log( LogService.LOG_ERROR, "Cannot use factory configuration " + factoryPid + " for "
-                    + ConfigurationManager.toString( sr ) + ": Configuration bound to bundle "
-                    + factory.getBundleLocation(), null );
-
-                return;
-            }
-
             for ( Iterator ci=configs.entrySet().iterator(); ci.hasNext(); )
             {
                 final Map.Entry entry = (Map.Entry) ci.next();
@@ -1330,7 +1483,7 @@
                 final Dictionary properties = (Dictionary) entry.getValue();
                 final long lastModificationTime = ( ( Long ) stamps.get( cfg ) ).longValue();
 
-                if ( lastModificationTime <= cfg.getLastUpdatedTime() && sr.equals( cfg.getServiceReference() ) )
+                if ( lastModificationTime <= cfg.getLastUpdatedTime() )
                 {
                     if ( isLogEnabled( LogService.LOG_DEBUG ) )
                     {
@@ -1338,7 +1491,7 @@
                             + cfg.getLastModificationTime() + " has already been updated to update #"
                             + cfg.getLastUpdatedTime() + ", nothing to be done anymore.", null );
                     }
-                    return;
+                    continue;
                 }
 
                 if ( isLogEnabled( LogService.LOG_DEBUG ) )
@@ -1347,31 +1500,17 @@
                         + cfg.getLastModificationTime(), null );
                 }
 
-                // check bundle location of configuration
-                if ( !cfg.tryBindLocation( serviceBundleLocation ) )
+                // CM 1.4 / 104.13.2.1
+                if ( !canReceive( serviceBundle, cfg.getBundleLocation() ) )
                 {
-                    // configuration is bound to another bundle
-                    log( LogService.LOG_ERROR, "Cannot use configuration " + cfg.getPid() + " (factory " + factoryPid
-                        + ") for " + ConfigurationManager.toString( sr ) + ": Configuration bound to bundle "
-                        + cfg.getBundleLocation(), null );
-
+                    log( LogService.LOG_ERROR,
+                        "Cannot use configuration " + cfg.getPid() + " for " + ConfigurationManager.toString( sr )
+                            + ": No visibility to configuration bound to " + 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 );
-                }
+                // 104.4.2 Dynamic Binding
+                cfg.tryBindLocation( serviceBundle.getLocation() );
 
                 // prepare the configuration for the service (call plugins)
                 // call the plugins with cm.target set to the service's factory PID
@@ -1414,11 +1553,18 @@
         }
     }
 
+
+    /**
+     * The <code>UpdateConfiguration</code> is used to update
+     * <code>ManagedService[Factory]</code> services with the configuration
+     * they are subscribed to. This may cause the configuration to be
+     * supplied to multiple services.
+     */
     private class UpdateConfiguration implements Runnable
     {
 
         private final ConfigurationImpl config;
-        private final Dictionary properties;
+        private final ServiceHelper helper;
         private final long lastModificationTime;
 
 
@@ -1427,13 +1573,7 @@
             this.config = config;
             synchronized ( config )
             {
-                Dictionary props = config.getProperties( true );
-                if ( props == null )
-                {
-                    props = new Hashtable();
-                }
-
-                this.properties = props;
+                this.helper = createServiceHelper( config );
                 this.lastModificationTime = config.getLastModificationTime();
             }
         }
@@ -1441,227 +1581,62 @@
 
         public void run()
         {
-            try
+            if ( lastModificationTime <= config.getLastUpdatedTime() )
             {
-                // only update configuration if lastModificationTime is
-                // less than lastUpdateTime
-                // (this must be inside the try-catch-finally to ensure
-                // the event is sent regardless of ManagedService[Factory]
-                // update)
-                if ( lastModificationTime <= config.getLastUpdatedTime() )
-                {
-                    if ( isLogEnabled( LogService.LOG_DEBUG ) )
-                    {
-                        log( LogService.LOG_DEBUG, "Configuration " + config.getPid() + " at modification #"
-                            + config.getLastModificationTime() + " has already been updated to update #"
-                            + config.getLastUpdatedTime() + ", nothing to be done anymore.", null );
-                    }
-                    return;
-                }
-
                 if ( isLogEnabled( LogService.LOG_DEBUG ) )
                 {
-                    log( LogService.LOG_DEBUG, "Updating configuration " + config.getPid() + " to modification #"
+                    log( LogService.LOG_DEBUG,
+                        "Configuration " + config.getPid() + " at modification #" + config.getLastModificationTime()
+                            + " has already been updated to update #" + config.getLastUpdatedTime()
+                            + ", nothing to be done anymore.", null );
+                }
+                return;
+            }
+
+            if ( isLogEnabled( LogService.LOG_DEBUG ) )
+            {
+                log(
+                    LogService.LOG_DEBUG,
+                    "Updating configuration " + config.getPid() + " to modification #"
                         + config.getLastModificationTime(), null );
-                }
-
-                if ( config.getFactoryPid() == null )
-                {
-                    final ServiceReference[] srList = bundleContext.getServiceReferences( ManagedService.class
-                        .getName(), "(" + Constants.SERVICE_PID + "=" + config.getPid() + ")" );
-                    if ( srList != null )
-                    {
-                        // find the primary configuration owner
-                        final ServiceReference ownerRef = getOwner( config, srList );
-                        final String bundleLocation = ( ownerRef != null ) ? ownerRef.getBundle().getLocation()
-                            : config.getBoundBundleLocation();
-
-                        // if the configuration is unbound, bind to owner
-                        if ( config.getBundleLocation() == null )
-                        {
-                            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()
-                                    + " has already been used for service " + ConfigurationManager.toString( ownerRef )
-                                    + " and will now also be given to " + ConfigurationManager.toString( userRef ), null );
-                            }
-
-                            try
-                            {
-                                final ManagedService srv = ( ManagedService ) bundleContext.getService( userRef );
-                                if ( srv != null )
-                                {
-                                    Dictionary props = new CaseInsensitiveDictionary( properties );
-                                    callPlugins( props, config.getPid(), userRef, config );
-                                    srv.updated( props );
-                                }
-                            }
-                            catch ( Throwable t )
-                            {
-                                handleCallBackError( t, userRef, config );
-                            }
-                            finally
-                            {
-                                bundleContext.ungetService( userRef );
-                            }
-
-                            // update the lastUpdatedTime
-                            config.setLastUpdatedTime( lastModificationTime );
-
-                            if ( isLogEnabled( LogService.LOG_DEBUG ) )
-                            {
-                                log( LogService.LOG_DEBUG, "Updated configuration " + config.getPid() + " to update #"
-                                    + config.getLastUpdatedTime(), null );
-                            }
-                        }
-                    }
-                }
-                else
-                {
-                    ServiceReference[] srList = bundleContext.getServiceReferences( ManagedServiceFactory.class
-                        .getName(), "(" + Constants.SERVICE_PID + "=" + config.getFactoryPid() + ")" );
-                    if ( srList != null && srList.length > 0 )
-                    {
-                        // find the primary configuration owner
-                        final ServiceReference ownerRef = getOwner( config, srList );
-                        final String bundleLocation = ( ownerRef != null ) ? ownerRef.getBundle().getLocation()
-                            : config.getBoundBundleLocation();
-
-                        // if the configuration is unbound, bind to owner
-                        if ( config.getBundleLocation() == null )
-                        {
-                            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 ref = srList[i];
-                            final String refLocation = ref.getBundle().getLocation();
-
-                            // only consider the entry if in the same bundle
-                            if ( !refLocation.equals( configBundleLocation ) )
-                            {
-                                log( LogService.LOG_ERROR, "Cannot use configuration " + config.getPid() + " (factory "
-                                    + config.getFactoryPid() + ") for " + ConfigurationManager.toString( ref )
-                                    + ": 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 ( 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 )
-                                {
-                                    Dictionary props = new CaseInsensitiveDictionary( properties );
-                                    callPlugins( props, config.getFactoryPid(), ref, config );
-                                    srv.updated( config.getPid(), props );
-                                }
-                            }
-                            catch ( Throwable t )
-                            {
-                                handleCallBackError( t, ref, config );
-                            }
-                            finally
-                            {
-                                bundleContext.ungetService( ref );
-                            }
-
-                            // update the lastUpdatedTime
-                            config.setLastUpdatedTime( lastModificationTime );
-
-                            if ( isLogEnabled( LogService.LOG_DEBUG ) )
-                            {
-                                log( LogService.LOG_DEBUG, "Updated configuration " + config.getPid() + " to update #"
-                                    + config.getLastUpdatedTime(), null );
-                            }
-                        }
-                    }
-                }
             }
-            catch ( InvalidSyntaxException ise )
-            {
-                log( LogService.LOG_ERROR, "Service selection filter is invalid to update " + config, ise );
-            }
-        }
 
-
-        private ServiceReference getOwner( ConfigurationImpl config, ServiceReference[] srList )
-        {
-            // find the current owner among the references (if any)
-            if ( config.getServiceReference() != null )
+            final ServiceReference[] srList = helper.getServices();
+            if ( srList != null )
             {
+                // optionally bind dynamically to the first service
+                config.tryBindLocation( srList[0].getBundle().getLocation() );
+
+                final String configBundleLocation = config.getBundleLocation();
+
+                // provide configuration to all services from the
+                // correct bundle
                 for ( int i = 0; i < srList.length; i++ )
                 {
-                    if ( srList[i].equals( config.getServiceReference() ) )
+                    final ServiceReference ref = srList[i];
+
+                    // CM 1.4 / 104.13.2.2
+                    if ( !canReceive( ref.getBundle(), configBundleLocation ) )
                     {
-                        return srList[i];
+                        log( LogService.LOG_ERROR, "Cannot use configuration " + config.getPid() + " for "
+                            + ConfigurationManager.toString( ref ) + ": No visibility to configuration bound to "
+                            + configBundleLocation, null );
+                        continue;
+                    }
+
+                    helper.provide( ref );
+
+                    if ( isLogEnabled( LogService.LOG_DEBUG ) )
+                    {
+                        log( LogService.LOG_DEBUG,
+                            "Updated configuration " + config.getPid() + " to update #" + config.getLastUpdatedTime(),
+                            null );
                     }
                 }
-            }
-            // configuration has never been supplied or the binding is stale
 
-            // if the configuration is location bound, find a service reference
-            // from the same bundle
-            final String configLocation = config.getBoundBundleLocation();
-            if (configLocation != null) {
-                for ( int i = 0; i < srList.length; i++ )
-                {
-                    if ( configLocation.equals(srList[i].getBundle().getLocation() ) )
-                    {
-                        return srList[i];
-                    }
-                }
-                // no service from the same bundle found, thus we cannot
-                // find a new owner !!
-                return null;
+                // update the lastUpdatedTime
+                config.setLastUpdatedTime( lastModificationTime );
             }
-            // configuration is not location bound (yet)
-
-            // just use the first entry in the list as the new owner
-            final ServiceReference ownerRef = srList[0];
-            config.setServiceReference( ownerRef );
-            return ownerRef;
         }
 
 
@@ -1671,6 +1646,12 @@
         }
     }
 
+
+    /**
+     * The <code>DeleteConfiguration</code> class is used to inform
+     * <code>ManagedService[Factory]</code> services of a configuration
+     * being deleted.
+     */
     private class DeleteConfiguration implements Runnable
     {
 
@@ -1686,7 +1667,7 @@
              * final and cannot be reset.
              */
             this.config = config;
-            this.configLocation = config.getBoundBundleLocation();
+            this.configLocation = config.getBundleLocation();
         }
 
 
@@ -1694,84 +1675,34 @@
         {
             final String pid = config.getPid();
             final String factoryPid = config.getFactoryPid();
+            final ServiceHelper helper = createServiceHelper( config );
 
-            try
+            ServiceReference[] srList = helper.getServices( );
+            if ( srList != null )
             {
-                if ( factoryPid == null )
+                for ( int i = 0; i < srList.length; i++ )
                 {
-                    ServiceReference[] srList = bundleContext.getServiceReferences( ManagedService.class.getName(), "("
-                        + Constants.SERVICE_PID + "=" + pid + ")" );
-                    if ( srList != null )
+                    final ServiceReference sr = srList[i];
+                    if ( canReceive( sr.getBundle(), configLocation ) )
                     {
-                        for ( int i = 0; i < srList.length; i++ )
-                        {
-                            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 );
-                                }
-                                catch ( Throwable t )
-                                {
-                                    handleCallBackError( t, sr, config );
-                                }
-                                finally
-                                {
-                                   bundleContext.ungetService( sr );
-                                }
-                            }
-                        }
-                    }
-                }
-                else
-                {
-                    // remove the pid from the factory
-                    try
-                    {
-                        Factory factory = getFactory( factoryPid );
-                        factory.removePID( pid );
-                        factory.store();
-                    }
-                    catch ( IOException ioe )
-                    {
-                        log( LogService.LOG_ERROR, "Failed removing " + pid + " from the factory " + factoryPid, ioe );
-                    }
-
-                    ServiceReference[] srList = bundleContext.getServiceReferences( ManagedServiceFactory.class
-                        .getName(), "(" + Constants.SERVICE_PID + "=" + factoryPid + ")" );
-                    if ( srList != null)
-                    {
-                        for ( int i = 0; i < srList.length; i++ )
-                        {
-                            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 );
-                                }
-                                catch ( Throwable t )
-                                {
-                                    handleCallBackError( t, sr, config );
-                                }
-                                finally
-                                {
-                                    bundleContext.ungetService( sr );
-                                }
-                            }
-                        }
+                        helper.remove( sr );
                     }
                 }
             }
-            catch ( InvalidSyntaxException ise )
+
+            if ( factoryPid != null )
             {
-                log( LogService.LOG_ERROR, "Service selection filter is invalid to update " + config, ise );
+                // remove the pid from the factory
+                try
+                {
+                    Factory factory = getFactory( factoryPid );
+                    factory.removePID( pid );
+                    factory.store();
+                }
+                catch ( IOException ioe )
+                {
+                    log( LogService.LOG_ERROR, "Failed removing " + pid + " from the factory " + factoryPid, ioe );
+                }
             }
         }
 
@@ -1781,6 +1712,56 @@
         }
     }
 
+    private class LocationChanged implements Runnable
+    {
+        private final ConfigurationImpl config;
+        private final String oldLocation;
+
+
+        LocationChanged( ConfigurationImpl config, String oldLocation )
+        {
+            this.config = config;
+            this.oldLocation = oldLocation;
+        }
+
+
+        public void run()
+        {
+            ServiceHelper helper = createServiceHelper( this.config );
+            ServiceReference[] srList = helper.getServices( );
+            if ( srList != null )
+            {
+                // make sure the config is dynamically bound to the first
+                // service if it has been unbound causing this update
+                config.tryBindLocation( srList[0].getBundle().getLocation() );
+
+                for ( int i = 0; i < srList.length; i++ )
+                {
+                    final ServiceReference sr = srList[i];
+                    final boolean wasVisible = canReceive( sr.getBundle(), oldLocation );
+                    final boolean isVisible = canReceive( sr.getBundle(), config.getBundleLocation() );
+                    if ( wasVisible && !isVisible )
+                    {
+                        // call deleted method
+                        helper.remove( sr );
+                    }
+                    else if ( !wasVisible && isVisible )
+                    {
+                        // call updated method
+                        helper.provide( sr );
+                    }
+                }
+            }
+        }
+
+
+        public String toString()
+        {
+            return "Location Changed (pid=" + config.getPid() + "): " + oldLocation + " ==> "
+                + config.getBundleLocation();
+        }
+    }
+
     private class FireConfigurationEvent implements Runnable
     {
         private final int type;
@@ -1837,6 +1818,8 @@
                     return "CM_DELETED";
                 case ConfigurationEvent.CM_UPDATED:
                     return "CM_UPDATED";
+                case ConfigurationEvent.CM_LOCATION_CHANGED:
+                    return "CM_LOCATION_CHANGED";
                 default:
                     return "<UNKNOWN(" + type + ")>";
             }
@@ -1907,26 +1890,6 @@
 
             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 ServiceTracker
@@ -1958,34 +1921,7 @@
 
             return serviceObject;
         }
-
-
-        public void removedService( ServiceReference reference, Object service )
-        {
-            // check whether we can take back the configuration objects
-            String[] factoryPids = getServicePid( reference );
-            if ( factoryPids != null )
-            {
-                for ( int i = 0; i < factoryPids.length; i++ )
-                {
-                    Factory factory = cm.getCachedFactory( factoryPids[i] );
-                    if ( factory != null )
-                    {
-                        for ( Iterator pi = factory.getPIDs().iterator(); pi.hasNext(); )
-                        {
-                            String pid = ( String ) pi.next();
-                            ConfigurationImpl cfg = cm.getCachedConfiguration( pid );
-                            if ( cfg != null && reference.equals( cfg.getServiceReference() ) )
-                            {
-                                cfg.setServiceReference( null );
-                            }
-                        }
-                    }
-                }
-            }
-
-            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 48589f1..5122816 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
@@ -26,7 +26,6 @@
 import java.util.Set;
 
 import org.apache.felix.cm.PersistenceManager;
-import org.osgi.service.cm.ConfigurationAdmin;
 
 
 /**
@@ -66,14 +65,14 @@
 
     Factory( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String factoryPid )
     {
-        super(configurationManager, persistenceManager, factoryPid, null);
+        super( configurationManager, persistenceManager, factoryPid );
     }
 
 
-    Factory( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String factoryPid, Dictionary props )
+    Factory( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String factoryPid,
+        Dictionary props )
     {
-        super( configurationManager, persistenceManager, factoryPid, ( String ) props
-            .get( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) );
+        super( configurationManager, persistenceManager, factoryPid );
 
         // set pids
         String[] pidList = ( String[] ) props.get( FACTORY_PID_LIST );
@@ -115,8 +114,6 @@
     {
         Hashtable props = new Hashtable();
 
-        replaceProperty( props, ConfigurationAdmin.SERVICE_BUNDLELOCATION, getStaticBundleLocation() );
-
         if ( !pids.isEmpty() )
         {
             props.put( FACTORY_PID_LIST, pids.toArray( new String[pids.size()] ) );
diff --git a/configadmin/src/main/java/org/osgi/service/cm/ConfigurationEvent.java b/configadmin/src/main/java/org/osgi/service/cm/ConfigurationEvent.java
new file mode 100644
index 0000000..c215ad4
--- /dev/null
+++ b/configadmin/src/main/java/org/osgi/service/cm/ConfigurationEvent.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) OSGi Alliance (2004, 2011). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.osgi.service.cm;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A Configuration Event.
+ *
+ * <p>
+ * {@code ConfigurationEvent} objects are delivered to all registered
+ * {@code ConfigurationListener} service objects. ConfigurationEvents must be
+ * asynchronously delivered in chronological order with respect to each
+ * listener.
+ *
+ * <p>
+ * A type code is used to identify the type of event. The following event types
+ * are defined:
+ * <ul>
+ * <li>{@link #CM_UPDATED}</li>
+ * <li>{@link #CM_DELETED}</li>
+ * <li>{@link #CM_LOCATION_CHANGED}</li>
+ * </ul>
+ * Additional event types may be defined in the future.
+ *
+ * <p>
+ * Security Considerations. {@code ConfigurationEvent} objects do not provide
+ * {@code Configuration} objects, so no sensitive configuration information is
+ * available from the event. If the listener wants to locate the
+ * {@code Configuration} object for the specified pid, it must use
+ * {@code ConfigurationAdmin}.
+ *
+ * @see ConfigurationListener
+ * @Immutable
+ * @version $Id: 1493c281dfd9387837e6399b9b815cdc8dc43453 $
+ * @since 1.2
+ */
+public class ConfigurationEvent {
+    /**
+     * A {@code Configuration} has been updated.
+     *
+     * <p>
+     * This {@code ConfigurationEvent} type that indicates that a
+     * {@code Configuration} object has been updated with new properties.
+     *
+     * An event is fired when a call to {@link Configuration#update(Dictionary)}
+     * successfully changes a configuration.
+     */
+    public static final int         CM_UPDATED  = 1;
+    /**
+     * A {@code Configuration} has been deleted.
+     *
+     * <p>
+     * This {@code ConfigurationEvent} type that indicates that a
+     * {@code Configuration} object has been deleted.
+     *
+     * An event is fired when a call to {@link Configuration#delete()}
+     * successfully deletes a configuration.
+     */
+    public static final int         CM_DELETED  = 2;
+
+    /**
+     * The location of a {@code Configuration} has been changed.
+     *
+     * <p>
+     * This {@code ConfigurationEvent} type that indicates that the location of
+     * a {@code Configuration} object has been changed.
+     *
+     * An event is fired when a call to
+     * {@link Configuration#setBundleLocation(String)} successfully changes the
+     * location.
+     *
+     * @since 1.4
+     */
+    public static final int         CM_LOCATION_CHANGED = 3;
+    /**
+     * Type of this event.
+     *
+     * @see #getType()
+     */
+    private final int               type;
+    /**
+     * The factory pid associated with this event.
+     */
+    private final String            factoryPid;
+    /**
+     * The pid associated with this event.
+     */
+    private final String            pid;
+    /**
+     * The ConfigurationAdmin service which created this event.
+     */
+    private final ServiceReference  reference;
+
+    /**
+     * Constructs a {@code ConfigurationEvent} object from the given
+     * {@code ServiceReference} object, event type, and pids.
+     *
+     * @param reference The {@code ServiceReference} object of the Configuration
+     *        Admin service that created this event.
+     * @param type The event type. See {@link #getType()}.
+     * @param factoryPid The factory pid of the associated configuration if the
+     *        target of the configuration is a ManagedServiceFactory. Otherwise
+     *        {@code null} if the target of the configuration is a
+     *        ManagedService.
+     * @param pid The pid of the associated configuration.
+     */
+    public ConfigurationEvent(ServiceReference reference,
+            int type,
+            String factoryPid, String pid) {
+        this.reference = reference;
+        this.type = type;
+        this.factoryPid = factoryPid;
+        this.pid = pid;
+        if ((reference == null) || (pid == null)) {
+            throw new NullPointerException("reference and pid must not be null");
+        }
+    }
+
+    /**
+     * Returns the factory pid of the associated configuration.
+     *
+     * @return Returns the factory pid of the associated configuration if the
+     *         target of the configuration is a ManagedServiceFactory. Otherwise
+     *         {@code null} if the target of the configuration is a
+     *         ManagedService.
+     */
+    public String getFactoryPid() {
+        return factoryPid;
+    }
+
+    /**
+     * Returns the pid of the associated configuration.
+     *
+     * @return Returns the pid of the associated configuration.
+     */
+    public String getPid() {
+        return pid;
+    }
+
+    /**
+     * Return the type of this event.
+     * <p>
+     * The type values are:
+     * <ul>
+     * <li>{@link #CM_UPDATED}</li>
+     * <li>{@link #CM_DELETED}</li>
+     * <li>{@link #CM_LOCATION_CHANGED}</li>
+     * </ul>
+     *
+     * @return The type of this event.
+     */
+    public int getType() {
+        return type;
+    }
+
+    /**
+     * Return the {@code ServiceReference} object of the Configuration
+     * Admin service that created this event.
+     *
+     * @return The {@code ServiceReference} object for the Configuration
+     *         Admin service that created this event.
+     */
+    public ServiceReference getReference() {
+        return reference;
+    }
+}
diff --git a/configadmin/src/main/java/org/osgi/service/cm/ConfigurationPermission.java b/configadmin/src/main/java/org/osgi/service/cm/ConfigurationPermission.java
new file mode 100644
index 0000000..fb3eb4e
--- /dev/null
+++ b/configadmin/src/main/java/org/osgi/service/cm/ConfigurationPermission.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) OSGi Alliance (2004, 2011). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.service.cm;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Indicates a bundle's authority to configure bundles or be updated by
+ * Configuration Admin.
+ *
+ * @ThreadSafe
+ * @version $Id: 0d700c494f2dc2bbe05165bf5c79fe185c9f0a4a $
+ * @since 1.2
+ */
+
+public final class ConfigurationPermission extends BasicPermission {
+    static final long               serialVersionUID    = 5716868734811965383L;
+    /**
+     * Provides permission to create new configurations for other bundles as
+     * well as manipulate them. The action string {@value #CONFIGURE}.
+     */
+    public final static String      CONFIGURE           = "configure";
+
+    /**
+     * The permission to be updated, that is, act as a Managed Service or
+     * Managed Service Factory. The action string {@value #TARGET}.
+     *
+     * @since 1.4
+     */
+    public final static String      TARGET              = "target";
+
+    private final static int        ACTION_CONFIGURE    = 0x00000001;
+    private final static int        ACTION_TARGET       = 0x00000002;
+    private final static int        ACTION_ALL          = ACTION_CONFIGURE
+                                                                | ACTION_TARGET;
+    final static int                ACTION_NONE         = 0;
+
+    /**
+     * The actions mask.
+     */
+    transient int                   action_mask;
+
+    /**
+     * The actions in canonical form.
+     *
+     * @serial
+     */
+    private volatile String         actions             = null;
+
+    /**
+     * Parsed name if it includes wildcards: "*"
+     */
+    private transient List substrings;
+
+    /**
+     * Create a new ConfigurationPermission.
+     *
+     * @param name Name of the permission. Wildcards ({@code '*'}) are allowed
+     *        in the name. During {@link #implies(Permission)}, the name is
+     *        matched to the requested permission using the substring matching
+     *        rules used by {@link Filter}s.
+     * @param actions Comma separated list of {@link #CONFIGURE},
+     *        {@link #TARGET}.
+     */
+
+    public ConfigurationPermission(String name, String actions) {
+        this(name, parseActions(actions));
+    }
+
+    /**
+     * Package private constructor used by ConfigurationPermissionCollection.
+     *
+     * @param name location string
+     * @param mask action mask
+     */
+    ConfigurationPermission(String name, int mask) {
+        super(name);
+        setTransients(mask);
+    }
+
+    /**
+     * Called by constructors and when deserialized.
+     *
+     * @param mask action mask
+     */
+    private void setTransients(int mask) {
+        if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+            throw new IllegalArgumentException("invalid action string");
+        }
+        action_mask = mask;
+        substrings = parseSubstring(getName());
+    }
+
+    /**
+     * Parse action string into action mask.
+     *
+     * @param actions Action string.
+     * @return action mask.
+     */
+    private static int parseActions(String actions) {
+        boolean seencomma = false;
+
+        int mask = ACTION_NONE;
+
+        if (actions == null) {
+            return mask;
+        }
+
+        char[] a = actions.toCharArray();
+
+        int i = a.length - 1;
+        if (i < 0)
+            return mask;
+
+        while (i != -1) {
+            char c;
+
+            // skip whitespace
+            while ((i != -1)
+                    && ((c = a[i]) == ' ' || c == '\r' || c == '\n'
+                            || c == '\f' || c == '\t'))
+                i--;
+
+            // check for the known strings
+            int matchlen;
+
+            if (i >= 5 && (a[i - 5] == 't' || a[i - 5] == 'T')
+                    && (a[i - 4] == 'a' || a[i - 4] == 'A')
+                    && (a[i - 3] == 'r' || a[i - 3] == 'R')
+                    && (a[i - 2] == 'g' || a[i - 2] == 'G')
+                    && (a[i - 1] == 'e' || a[i - 1] == 'E')
+                    && (a[i] == 't' || a[i] == 'T')) {
+                matchlen = 6;
+                mask |= ACTION_TARGET;
+
+            }
+            else
+                if (i >= 8 && (a[i - 8] == 'c' || a[i - 8] == 'C')
+                        && (a[i - 7] == 'o' || a[i - 7] == 'O')
+                        && (a[i - 6] == 'n' || a[i - 6] == 'N')
+                        && (a[i - 5] == 'f' || a[i - 5] == 'F')
+                        && (a[i - 4] == 'i' || a[i - 4] == 'I')
+                        && (a[i - 3] == 'g' || a[i - 3] == 'G')
+                        && (a[i - 2] == 'u' || a[i - 2] == 'U')
+                        && (a[i - 1] == 'r' || a[i - 1] == 'R')
+                        && (a[i] == 'e' || a[i] == 'E')) {
+                    matchlen = 9;
+                    mask |= ACTION_CONFIGURE;
+
+                }
+                else {
+                    // parse error
+                    throw new IllegalArgumentException("invalid actions: "
+                            + actions);
+                }
+
+            // make sure we didn't just match the tail of a word
+            // like "ackbarftarget". Also, skip to the comma.
+            seencomma = false;
+            while (i >= matchlen && !seencomma) {
+                switch (a[i - matchlen]) {
+                    case ',' :
+                        seencomma = true;
+                        /* FALLTHROUGH */
+                    case ' ' :
+                    case '\r' :
+                    case '\n' :
+                    case '\f' :
+                    case '\t' :
+                        break;
+                    default :
+                        throw new IllegalArgumentException(
+                                "invalid permission: " + actions);
+                }
+                i--;
+            }
+
+            // point i at the location of the comma minus one (or -1).
+            i -= matchlen;
+        }
+
+        if (seencomma) {
+            throw new IllegalArgumentException("invalid actions: " + actions);
+        }
+
+        return mask;
+    }
+
+    /**
+     * Parse the name for wildcard processing.
+     *
+     * @param name The name of the permission.
+     * @return {@code null} is the name has no wildcards or a
+     *         {@code List<String>} where element is a substring to match or
+     *         null for {@code '*'}.
+     */
+    private static List parseSubstring(String name) {
+        if (name.indexOf('*') < 0) {
+            return null;
+        }
+        char[] chars = name.toCharArray();
+        StringBuffer sb = new StringBuffer(chars.length);
+
+        List sub = new ArrayList(10);
+
+        for (int pos = 0; pos < chars.length; pos++) {
+            char c = chars[pos];
+
+            switch (c) {
+                case '*' : {
+                    if (sb.length() > 0) {
+                        sub.add(sb.toString());
+                    }
+                    sb.setLength(0);
+                    sub.add(null);
+                    break;
+                }
+
+                case '\\' : {
+                    pos++;
+                    if (pos < chars.length) {
+                        c = chars[pos];
+                    }
+                    /* fall through into default */
+                }
+
+                default : {
+                    sb.append(c);
+                    break;
+                }
+            }
+        }
+        if (sb.length() > 0) {
+            sub.add(sb.toString());
+        }
+
+        int size = sub.size();
+
+        if (size == 0) {
+            return null;
+        }
+
+        if (size == 1) {
+            if (sub.get(0) != null) {
+                return null;
+            }
+        }
+        return sub;
+    }
+
+    /**
+     * Determines if a {@code ConfigurationPermission} object "implies" the
+     * specified permission.
+     *
+     * @param p The target permission to check.
+     * @return {@code true} if the specified permission is implied by this
+     *         object; {@code false} otherwise.
+     */
+
+    public boolean implies(Permission p) {
+        if (!(p instanceof ConfigurationPermission)) {
+            return false;
+        }
+        ConfigurationPermission requested = (ConfigurationPermission) p;
+        return implies0(requested, ACTION_NONE);
+    }
+
+    /**
+     * Internal implies method. Used by the implies and the permission
+     * collection implies methods.
+     *
+     * @param requested The requested ConfigurationPermission which has already
+     *        be validated as a proper argument.
+     * @param effective The effective actions with which to start.
+     * @return {@code true} if the specified permission is implied by this
+     *         object; {@code false} otherwise.
+     */
+    boolean implies0(ConfigurationPermission requested, int effective) {
+        /* check actions first - much faster */
+        effective |= action_mask;
+        final int desired = requested.action_mask;
+        if ((effective & desired) != desired) {
+            return false;
+        }
+        String requestedName = requested.getName();
+        if (substrings == null) {
+            return getName().equals(requestedName);
+        }
+        for (int i = 0, pos = 0, size = substrings.size(); i < size; i++) {
+            String substr = (String) substrings.get(i);
+
+            if (i + 1 < size) /* if this is not that last substr */{
+                if (substr == null) /* * */{
+                    String substr2 = (String) substrings.get(i + 1);
+
+                    if (substr2 == null) /* ** */
+                        continue; /* ignore first star */
+                    /* xxx */
+                    int index = requestedName.indexOf(substr2, pos);
+                    if (index == -1) {
+                        return false;
+                    }
+
+                    pos = index + substr2.length();
+                    if (i + 2 < size) // if there are more
+                        // substrings, increment
+                        // over the string we just
+                        // matched; otherwise need
+                        // to do the last substr
+                        // check
+                        i++;
+                }
+                else /* xxx */{
+                    int len = substr.length();
+                    if (requestedName.regionMatches(pos, substr, 0, len)) {
+                        pos += len;
+                    }
+                    else {
+                        return false;
+                    }
+                }
+            }
+            else /* last substr */{
+                if (substr == null) /* * */{
+                    return true;
+                }
+                /* xxx */
+                return requestedName.endsWith(substr);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determines the equality of two {@code ConfigurationPermission} objects.
+     * <p>
+     * Two {@code ConfigurationPermission} objects are equal.
+     *
+     * @param obj The object being compared for equality with this object.
+     * @return {@code true} if {@code obj} is equivalent to this
+     *         {@code ConfigurationPermission}; {@code false} otherwise.
+     */
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (!(obj instanceof ConfigurationPermission)) {
+            return false;
+        }
+
+        ConfigurationPermission cp = (ConfigurationPermission) obj;
+
+        return (action_mask == cp.action_mask)
+                && getName().equals(cp.getName());
+    }
+
+    /**
+     * Returns the hash code value for this object.
+     *
+     * @return Hash code value for this object.
+     */
+
+    public int hashCode() {
+        int h = 31 * 17 + getName().hashCode();
+        h = 31 * h + getActions().hashCode();
+        return h;
+    }
+
+    /**
+     * Returns the canonical string representation of the
+     * {@code ConfigurationPermission} actions.
+     *
+     * <p>
+     * Always returns present {@code ConfigurationPermission} actions in the
+     * following order: {@value #CONFIGURE}, {@value #TARGET}
+     *
+     * @return Canonical string representation of the
+     *         {@code ConfigurationPermission} actions.
+     */
+    public String getActions() {
+        String result = actions;
+        if (result == null) {
+            StringBuffer sb = new StringBuffer();
+            boolean comma = false;
+
+            int mask = action_mask;
+            if ((mask & ACTION_CONFIGURE) == ACTION_CONFIGURE) {
+                sb.append(CONFIGURE);
+                comma = true;
+            }
+
+            if ((mask & ACTION_TARGET) == ACTION_TARGET) {
+                if (comma)
+                    sb.append(',');
+                sb.append(TARGET);
+            }
+
+            actions = result = sb.toString();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a new {@code PermissionCollection} object suitable for storing
+     * {@code ConfigurationPermission}s.
+     *
+     * @return A new {@code PermissionCollection} object.
+     */
+    public PermissionCollection newPermissionCollection() {
+        return new ConfigurationPermissionCollection();
+    }
+
+    /**
+     * WriteObject is called to save the state of this permission object to a
+     * stream. The actions are serialized, and the superclass takes care of the
+     * name.
+     */
+    private synchronized void writeObject(java.io.ObjectOutputStream s)
+            throws IOException {
+        // Write out the actions. The superclass takes care of the name
+        // call getActions to make sure actions field is initialized
+        if (actions == null)
+            getActions();
+        s.defaultWriteObject();
+    }
+
+    /**
+     * readObject is called to restore the state of this permission from a
+     * stream.
+     */
+    private synchronized void readObject(java.io.ObjectInputStream s)
+            throws IOException, ClassNotFoundException {
+        // Read in the data, then initialize the transients
+        s.defaultReadObject();
+        setTransients(parseActions(actions));
+    }
+}
+
+/**
+ * Stores a set of {@code ConfigurationPermission} permissions.
+ *
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+final class ConfigurationPermissionCollection extends PermissionCollection {
+    static final long                               serialVersionUID    = -6917638867081695839L;
+    /**
+     * Collection of permissions.
+     *
+     * @serial
+     * @GuardedBy this
+     */
+    private Map    permissions;
+
+    /**
+     * Boolean saying if "*" is in the collection.
+     *
+     * @serial
+     * @GuardedBy this
+     */
+    private boolean                                 all_allowed;
+
+    /**
+     * Creates an empty {@code ConfigurationPermissionCollection} object.
+     *
+     */
+    public ConfigurationPermissionCollection() {
+        permissions = new HashMap();
+        all_allowed = false;
+    }
+
+    /**
+     * Adds the specified permission to the
+     * {@code ConfigurationPermissionCollection}. The key for the hash is the
+     * interface name of the service.
+     *
+     * @param permission The {@code Permission} object to add.
+     *
+     * @exception IllegalArgumentException If the permission is not an
+     *            {@code ConfigurationPermission}.
+     *
+     * @exception SecurityException If this ConfigurationPermissionCollection
+     *            object has been marked read-only.
+     */
+
+    public void add(Permission permission) {
+        if (!(permission instanceof ConfigurationPermission)) {
+            throw new IllegalArgumentException("invalid permission: "
+                    + permission);
+        }
+
+        if (isReadOnly())
+            throw new SecurityException("attempt to add a Permission to a "
+                    + "readonly PermissionCollection");
+
+        final ConfigurationPermission cp = (ConfigurationPermission) permission;
+        final String name = cp.getName();
+        synchronized (this) {
+            Map pc = permissions;
+            final ConfigurationPermission existing = (ConfigurationPermission) pc.get(name);
+            if (existing != null) {
+                final int oldMask = existing.action_mask;
+                final int newMask = cp.action_mask;
+                if (oldMask != newMask) {
+                    pc.put(name, new ConfigurationPermission(name, oldMask
+                            | newMask));
+                }
+            }
+            else {
+                pc.put(name, cp);
+            }
+
+            if (!all_allowed) {
+                if (name.equals("*")) {
+                    all_allowed = true;
+                }
+            }
+        }
+    }
+
+    /**
+     * Determines if the specified permissions implies the permissions expressed
+     * in {@code permission}.
+     *
+     * @param permission The Permission object to compare with this
+     *        {@code ConfigurationPermission} object.
+     * @return {@code true} if {@code permission} is a proper subset of a
+     *         permission in the set; {@code false} otherwise.
+     */
+    public boolean implies(Permission permission) {
+        if (!(permission instanceof ConfigurationPermission)) {
+            return false;
+        }
+        final ConfigurationPermission requested = (ConfigurationPermission) permission;
+        int effective = ConfigurationPermission.ACTION_NONE;
+
+        Collection perms;
+        synchronized (this) {
+            Map pc = permissions;
+            /* short circuit if the "*" Permission was added */
+            if (all_allowed) {
+                ConfigurationPermission cp = (ConfigurationPermission) pc.get("*");
+                if (cp != null) {
+                    effective |= cp.action_mask;
+                    final int desired = requested.action_mask;
+                    if ((effective & desired) == desired) {
+                        return true;
+                    }
+                }
+            }
+            perms = pc.values();
+        }
+        /* iterate one by one over permissions */
+        for (Iterator permI = perms.iterator(); permI.hasNext(); ) {
+            ConfigurationPermission perm = (ConfigurationPermission) permI.next();
+            if (perm.implies0(requested, effective)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns an enumeration of all {@code ConfigurationPermission} objects in
+     * the container.
+     *
+     * @return Enumeration of all {@code ConfigurationPermission} objects.
+     */
+    public synchronized Enumeration elements() {
+        List all = new ArrayList(permissions.values());
+        return Collections.enumeration(all);
+    }
+
+    /* serialization logic */
+    private static final ObjectStreamField[]    serialPersistentFields  = {
+            new ObjectStreamField("hasElement", Boolean.TYPE),
+            new ObjectStreamField("permissions", HashMap.class),
+            new ObjectStreamField("all_allowed", Boolean.TYPE)          };
+
+    private synchronized void writeObject(ObjectOutputStream out)
+            throws IOException {
+        ObjectOutputStream.PutField pfields = out.putFields();
+        pfields.put("hasElement", false);
+        pfields.put("permissions", permissions);
+        pfields.put("all_allowed", all_allowed);
+        out.writeFields();
+    }
+
+    private synchronized void readObject(java.io.ObjectInputStream in)
+            throws IOException, ClassNotFoundException {
+        ObjectInputStream.GetField gfields = in.readFields();
+        boolean hasElement = gfields.get("hasElement", false);
+        if (hasElement) { // old format
+            permissions = new HashMap();
+            permissions.put("*", new ConfigurationPermission("*",
+                    ConfigurationPermission.CONFIGURE));
+            all_allowed = true;
+        }
+        else {
+            permissions = (HashMap) gfields
+                    .get("permissions",
+                            new HashMap());
+            all_allowed = gfields.get("all_allowed", false);
+        }
+    }
+}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java
index ccb8f6f..9fb9c42 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java
@@ -40,6 +40,12 @@
 public class ConfigurationBaseTest extends ConfigurationTestBase
 {
 
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
+
     @Test
     public void test_basic_configuration_configure_then_start() throws BundleException, IOException
     {
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java
index 159c588..63f0c70 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java
@@ -39,6 +39,13 @@
 public class ConfigurationBindingTest extends ConfigurationTestBase
 {
 
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
+
+
      @Test
     public void test_configuration_unbound_on_uninstall() throws BundleException
     {
@@ -445,6 +452,7 @@
 
         // remove the static binding and assert still bound
         config.setBundleLocation( null );
+        delay();
         TestCase.assertEquals( location, config.getBundleLocation() );
 
         // uninstall bundle and assert configuration unbound
@@ -580,33 +588,13 @@
         // ==> configuration is bound to locationB
         TestCase.assertEquals( locationB, config.getBundleLocation() );
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration removed from service ms1
-            TestCase.assertNull( testerA1.props );
-            TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
+        // ==> configuration removed from service ms1
+        TestCase.assertNull( testerA1.props );
+        TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
 
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.props );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration remains for service ms1
-            TestCase.assertNotNull( testerA1.props );
-            TestCase.assertEquals( 1, testerA1.numManagedServiceUpdatedCalls );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.props );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
-        }
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.props );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
     }
 
 
@@ -650,33 +638,13 @@
         // ==> configuration is bound to locationB
         TestCase.assertEquals( locationB, config.getBundleLocation() );
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration removed from service ms1
-            TestCase.assertNull( testerA1.props );
-            TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
+        // ==> configuration removed from service ms1
+        TestCase.assertNull( testerA1.props );
+        TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
 
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.props );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration remains for service ms1
-            TestCase.assertNotNull( testerA1.props );
-            TestCase.assertEquals( 1, testerA1.numManagedServiceUpdatedCalls );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.props );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
-        }
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.props );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
 
         // 6. Update configuration now
         config.update();
@@ -684,7 +652,7 @@
 
         // ==> configuration supplied to the service ms2
         TestCase.assertNotNull( testerB1.props );
-        TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
+        TestCase.assertEquals( 2, testerB1.numManagedServiceUpdatedCalls );
     }
 
 
@@ -726,35 +694,14 @@
         // ==> configuration is bound to locationB
         TestCase.assertEquals( locationB, config.getBundleLocation() );
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration removed from service ms1
-            TestCase.assertNull( testerA1.configs.get( pid ));
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
+        // ==> configuration removed from service ms1
+        TestCase.assertNull( testerA1.configs.get( pid ));
+        TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
 
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration not removed from service ms1
-            TestCase.assertNotNull( testerA1.configs.get( pid ));
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
-            TestCase.assertEquals( 0, testerA1.numManagedServiceFactoryDeleteCalls );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.configs.get( pid ) );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
 
         // 6. Update configuration now
         config.update();
@@ -762,7 +709,7 @@
 
         // ==> configuration supplied to the service ms2
         TestCase.assertNotNull( testerB1.configs.get( pid ) );
-        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( 2, testerB1.numManagedServiceFactoryUpdatedCalls );
    }
 
 
@@ -808,35 +755,14 @@
         // ==> configuration is bound to locationB
         TestCase.assertEquals( locationB, config.getBundleLocation() );
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration removed from service ms1
-            TestCase.assertNull( testerA1.configs.get( pid ));
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
+        // ==> configuration removed from service ms1
+        TestCase.assertNull( testerA1.configs.get( pid ));
+        TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
 
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration not removed from service ms1
-            TestCase.assertNotNull( testerA1.configs.get( pid ));
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
-            TestCase.assertEquals( 0, testerA1.numManagedServiceFactoryDeleteCalls );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.configs.get( pid ) );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
 
         // 6. Update configuration now
         config.update();
@@ -844,7 +770,7 @@
 
         // ==> configuration supplied to the service ms2
         TestCase.assertNotNull( testerB1.configs.get( pid ) );
-        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( 2, testerB1.numManagedServiceFactoryUpdatedCalls );
     }
 
 
@@ -887,31 +813,12 @@
         bundleA.uninstall();
         delay();
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration is bound to locationB
-            TestCase.assertEquals( locationB, config.getBundleLocation() );
+        // ==> configuration is bound to locationB
+        TestCase.assertEquals( locationB, config.getBundleLocation() );
 
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.props );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration is unbound
-            TestCase.assertNull( config.getBundleLocation() );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.props );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
-        }
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.props );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
 
         // 6. Update configuration now
         config.update();
@@ -919,7 +826,7 @@
 
         // ==> configuration supplied to the service ms2
         TestCase.assertNotNull( testerB1.props );
-        TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
+        TestCase.assertEquals( 2, testerB1.numManagedServiceUpdatedCalls );
     }
 
 
@@ -962,31 +869,12 @@
         bundleA.uninstall();
         delay();
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration is bound to locationB
-            TestCase.assertEquals( locationB, config.getBundleLocation() );
+        // ==> configuration is bound to locationB
+        TestCase.assertEquals( locationB, config.getBundleLocation() );
 
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration is unbound
-            TestCase.assertNull( config.getBundleLocation() );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.configs.get( pid ) );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
 
         // 6. Update configuration now
         config.update();
@@ -994,6 +882,6 @@
 
         // ==> configuration supplied to the service ms2
         TestCase.assertNotNull( testerB1.configs.get( pid ) );
-        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( 2, testerB1.numManagedServiceFactoryUpdatedCalls );
     }
 }
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java
index e012e24..f76e9af 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java
@@ -29,7 +29,9 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.Dictionary;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Set;
 
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
@@ -60,24 +62,6 @@
 public abstract class ConfigurationTestBase
 {
 
-    /**
-     * There is currently an open issue in the specification in whether a
-     * call to Configuration.setBundleLocation() might trigger a configuration
-     * update or not.
-     * We have test cases in our integration test suite for both cases. To
-     * enable the respective tests set this field accordingly:
-     * <dl>
-     * <dt>false</dt>
-     * <dd>Expect configuration to <b>NOT</b> be redispatched. That is existing
-     * configurations are kept and other services are not updated</dd>
-     * <dt>true</dt>
-     * <dd>Expect configuration to be redispatched. That is existing configuration
-     * is revoked (update(null) or delete calls) and new matching services are
-     * updated.</dd>
-     * </dl>
-     */
-    public static final boolean REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION = false;
-
     // the name of the system property providing the bundle file to be installed and tested
     protected static final String BUNDLE_JAR_SYS_PROP = "project.bundle.file";
 
@@ -99,6 +83,8 @@
 
     protected ServiceTracker configAdminTracker;
 
+    private Set<String> configurations = new HashSet<String>();
+
     protected static final String PROP_NAME = "theValue";
     protected static final Dictionary<String, String> theConfig;
 
@@ -121,6 +107,7 @@
         }
 
         final Option[] base = options(
+            /* CoreOptions.allFrameworks(), */
             provision(
                 CoreOptions.bundle( bundleFile.toURI().toString() ),
                 mavenBundle( "org.ops4j.pax.swissbox", "pax-swissbox-tinybundles", "1.0.0" )
@@ -148,6 +135,11 @@
             bundle.uninstall();
         }
 
+        for ( String pid : configurations )
+        {
+            deleteConfig( pid );
+        }
+
         configAdminTracker.close();
         configAdminTracker = null;
     }
@@ -299,7 +291,7 @@
         final ConfigurationAdmin ca = getConfigurationAdmin();
         try
         {
-            final Configuration config = ca.createFactoryConfiguration( factoryPid, null );
+            final Configuration config = ca.createFactoryConfiguration( factoryPid, location );
             if ( withProps )
             {
                 config.update( theConfig );
@@ -345,6 +337,7 @@
         final ConfigurationAdmin ca = getConfigurationAdmin();
         try
         {
+            configurations.remove( pid );
             final Configuration config = ca.getConfiguration( pid );
             config.delete();
         }
@@ -366,6 +359,7 @@
             {
                 for ( Configuration configuration : configs )
                 {
+                    configurations.remove( configuration.getPid() );
                     configuration.delete();
                 }
             }
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java
index 1ce490e..55d9eb0 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java
@@ -39,6 +39,11 @@
 @RunWith(JUnit4TestRunner.class)
 public class MultiServiceFactoryPIDTest extends ConfigurationTestBase
 {
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
 
     @Test
     public void test_two_services_same_pid_in_same_bundle_configure_before_registration() throws BundleException
@@ -61,7 +66,7 @@
 
         // assert activater has configuration (two calls, one per pid)
         TestCase.assertNotNull( "Expect Properties after Service Registration", tester.configs.get( pid ) );
-        TestCase.assertEquals( "Expect a single update call", 2, tester.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceFactoryUpdatedCalls );
 
         TestCase.assertEquals( bundle.getLocation(), config.getBundleLocation() );
 
@@ -165,23 +170,10 @@
 
             delay();
 
-            /*
-             * According to BJ Hargrave configuration is not re-dispatched
-             * due to setting the bundle location.
-             * <p>
-             * Therefore, we have two sets one with re-dispatch expectation and
-             * one without re-dispatch expectation.
-             */
-            if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-            {
-                // expect configuration reassigned
-                TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
-            }
-            else
-            {
-                // expected configuration unbound
-                TestCase.assertNull( config.getBundleLocation() );
-            }
+            // expect configuration reassigned
+            TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
+            TestCase.assertNotNull( "Expect Properties after Configuration Redispatch", tester2.configs.get( pid ) );
+            TestCase.assertEquals( "Expect update call after Configuration Redispatch", 1, tester2.numManagedServiceFactoryUpdatedCalls );
 
             // remove the configuration for good
             deleteConfig( pid );
@@ -230,78 +222,27 @@
             delay();
 
             TestCase.assertEquals( factoryPid, config.getFactoryPid() );
-            TestCase.assertNotNull( config.getBundleLocation() );
 
-            if ( bundle.getLocation().equals( config.getBundleLocation() ) )
-            {
-                // configuration assigned to the first bundle
-                TestCase.assertNotNull( "Expect Properties after Service Registration", tester.configs.get( pid ) );
-                TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceFactoryUpdatedCalls );
+            TestCase.assertEquals(
+                "Configuration must be bound to second bundle because the service has higher ranking",
+                bundle.getLocation(), config.getBundleLocation() );
 
-                TestCase.assertTrue( "Expect Properties after Service Registration", tester2.configs.isEmpty() );
-                TestCase.assertEquals( "Expect a single update call", 0, tester2.numManagedServiceFactoryUpdatedCalls );
+            // configuration assigned to the first bundle
+            TestCase.assertNotNull( "Expect Properties after Service Registration", tester.configs.get( pid ) );
+            TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceFactoryUpdatedCalls );
 
-                bundle.uninstall();
-                bundle = null;
+            TestCase.assertTrue( "Expect Properties after Service Registration", tester2.configs.isEmpty() );
+            TestCase.assertEquals( "Expect a single update call", 0, tester2.numManagedServiceFactoryUpdatedCalls );
 
-                delay();
+            bundle.uninstall();
+            bundle = null;
 
-                /*
-                 * According to BJ Hargrave configuration is not re-dispatched
-                 * due to setting the bundle location.
-                 * <p>
-                 * Therefore, we have two sets one with re-dispatch expectation and
-                 * one without re-dispatch expectation.
-                 */
-                if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-                {
-                    // expect configuration reassigned
-                    TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
-                }
-                else
-                {
-                    // expected configuration unbound
-                    TestCase.assertNull( config.getBundleLocation() );
-                }
-            }
-            else if ( bundle2.getLocation().equals( config.getBundleLocation() ) )
-            {
-                // configuration assigned to the second bundle
-                // assert activater has configuration (two calls, one per pid)
-                TestCase.assertNotNull( "Expect Properties after Service Registration", tester2.configs.get( pid ) );
-                TestCase.assertEquals( "Expect a single update call", 1, tester2.numManagedServiceFactoryUpdatedCalls );
+            delay();
 
-                TestCase.assertTrue( "Expect Properties after Service Registration", tester.configs.isEmpty() );
-                TestCase.assertEquals( "Expect a single update call", 0, tester.numManagedServiceFactoryUpdatedCalls );
-
-                bundle2.uninstall();
-                bundle2 = null;
-
-                delay();
-
-                /*
-                 * According to BJ Hargrave configuration is not re-dispatched
-                 * due to setting the bundle location.
-                 * <p>
-                 * Therefore, we have two sets one with re-dispatch expectation and
-                 * one without re-dispatch expectation.
-                 */
-                if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-                {
-                    // expect configuration reassigned
-                    TestCase.assertEquals( bundle.getLocation(), config.getBundleLocation() );
-                }
-                else
-                {
-                    // expected configuration unbound
-                    TestCase.assertNull( config.getBundleLocation() );
-                }
-            }
-            else
-            {
-                // configuration assigned to some other bundle ... fail
-                TestCase.fail( "Configuration assigned to unexpected bundle " + config.getBundleLocation() );
-            }
+            // expect configuration reassigned
+            TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
+            TestCase.assertNotNull( "Expect Properties after Configuration Redispatch", tester2.configs.get( pid ) );
+            TestCase.assertEquals( "Expect a single update call after Configuration Redispatch", 1, tester2.numManagedServiceFactoryUpdatedCalls );
 
             // remove the configuration for good
             deleteConfig( pid );
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java
index 9ff808a..f4eedda 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java
@@ -39,6 +39,11 @@
 @RunWith(JUnit4TestRunner.class)
 public class MultiServicePIDTest extends ConfigurationTestBase
 {
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
 
     @Test
     public void test_two_services_same_pid_in_same_bundle_configure_before_registration() throws BundleException
@@ -150,7 +155,7 @@
 
             // expect first activator to have received properties
 
-            // assert first bundle has configuration (two calls, one per srv)
+            // assert first bundle has configuration (one calls, one per srv)
             TestCase.assertNotNull( "Expect Properties after Service Registration", tester.props );
             TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceUpdatedCalls );
 
@@ -166,23 +171,16 @@
 
             delay();
 
-            /*
-             * According to BJ Hargrave configuration is not re-dispatched
-             * due to setting the bundle location.
-             * <p>
-             * Therefore, we have two sets one with re-dispatch expectation and
-             * one without re-dispatch expectation.
-             */
-            if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-            {
-                // expect configuration reassigned
-                TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
-            }
-            else
-            {
-                // expected configuration unbound
-                TestCase.assertNull( config.getBundleLocation() );
-            }
+            // after uninstallation, the configuration is redispatched
+            // due to the dynamic binding being removed
+
+            // expect configuration reassigned
+            TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
+
+            // assert second bundle now has the configuration
+            TestCase.assertNotNull( "Expect Properties after Configuration redispatch", tester2.props );
+            TestCase.assertEquals( "Expect a single update call after Configuration redispatch", 1,
+                tester2.numManagedServiceUpdatedCalls );
 
             // remove the configuration for good
             deleteConfig( pid );
@@ -231,78 +229,33 @@
 
             final Configuration config = getConfiguration( pid );
             TestCase.assertEquals( pid, config.getPid() );
-            TestCase.assertNotNull( config.getBundleLocation() );
 
-            if ( bundle.getLocation().equals( config.getBundleLocation() ) )
-            {
-                // configuration assigned to the first bundle
-                TestCase.assertNotNull( "Expect Properties after Service Registration", tester.props );
-                TestCase.assertEquals( "Expect a single update call", 2, tester.numManagedServiceUpdatedCalls );
+            TestCase.assertEquals(
+                "Configuration must be bound to first bundle because the service has higher ranking",
+                bundle.getLocation(), config.getBundleLocation() );
 
-                TestCase.assertNull( "Expect Properties after Service Registration", tester2.props );
-                TestCase.assertEquals( "Expect a single update call", 1, tester2.numManagedServiceUpdatedCalls );
+            // configuration assigned to the first bundle
+            TestCase.assertNotNull( "Expect Properties after Service Registration", tester.props );
+            TestCase.assertEquals( "Expect a single update call", 2, tester.numManagedServiceUpdatedCalls );
 
-                bundle.uninstall();
-                bundle = null;
+            TestCase.assertNull( "Expect Properties after Service Registration", tester2.props );
+            TestCase.assertEquals( "Expect a single update call", 1, tester2.numManagedServiceUpdatedCalls );
 
-                delay();
+            bundle.uninstall();
+            bundle = null;
 
-                /*
-                 * According to BJ Hargrave configuration is not re-dispatched
-                 * due to setting the bundle location.
-                 * <p>
-                 * Therefore, we have two sets one with re-dispatch expectation and
-                 * one without re-dispatch expectation.
-                 */
-                if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-                {
-                    // expect configuration reassigned
-                    TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
-                }
-                else
-                {
-                    // expected configuration unbound
-                    TestCase.assertNull( config.getBundleLocation() );
-                }
-            }
-            else if ( bundle2.getLocation().equals( config.getBundleLocation() ) )
-            {
-                // configuration assigned to the second bundle
-                // assert activater has configuration (two calls, one per pid)
-                TestCase.assertNotNull( "Expect Properties after Service Registration", tester2.props );
-                TestCase.assertEquals( "Expect a single update call", 2, tester2.numManagedServiceUpdatedCalls );
+            delay();
 
-                TestCase.assertNull( "Expect Properties after Service Registration", tester.props );
-                TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceUpdatedCalls );
+            // after uninstallation, the configuration is redispatched
+            // due to the dynamic binding being removed
 
-                bundle2.uninstall();
-                bundle2 = null;
+            // expect configuration reassigned
+            TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
 
-                delay();
-
-                /*
-                 * According to BJ Hargrave configuration is not re-dispatched
-                 * due to setting the bundle location.
-                 * <p>
-                 * Therefore, we have two sets one with re-dispatch expectation and
-                 * one without re-dispatch expectation.
-                 */
-                if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-                {
-                    // expect configuration reassigned
-                    TestCase.assertEquals( bundle.getLocation(), config.getBundleLocation() );
-                }
-                else
-                {
-                    // expected configuration unbound
-                    TestCase.assertNull( config.getBundleLocation() );
-                }
-            }
-            else
-            {
-                // configuration assigned to some other bundle ... fail
-                TestCase.fail( "Configuration assigned to unexpected bundle " + config.getBundleLocation() );
-            }
+            // assert second bundle now has the configuration
+            TestCase.assertNotNull( "Expect Properties after Configuration redispatch", tester2.props );
+            TestCase.assertEquals( "Expect a single update call after Configuration redispatch", 2,
+                tester2.numManagedServiceUpdatedCalls );
 
             // remove the configuration for good
             deleteConfig( pid );