FELIX-3481 Start using targeted PIDs:
  - generify factory and configuration caches
  - try to find targeted [factory] configuration(s)
  - Replace string pid and factoryPid fields in ConfigurationImpl and Factory
    by TargetedPID typed fields

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1357149 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java
index f1e95bc..263a6e4 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
@@ -52,7 +52,7 @@
     public String getPid()
     {
         checkDeleted();
-        return delegatee.getPid();
+        return delegatee.getPidString();
     }
 
 
@@ -62,7 +62,7 @@
     public String getFactoryPid()
     {
         checkDeleted();
-        return delegatee.getFactoryPid();
+        return delegatee.getFactoryPidString();
     }
 
 
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 6891060..79e6f0c 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,6 +22,7 @@
 import java.io.IOException;
 import java.util.Dictionary;
 import org.apache.felix.cm.PersistenceManager;
+import org.apache.felix.cm.impl.helper.TargetedPID;
 import org.osgi.service.log.LogService;
 
 
@@ -38,7 +39,7 @@
     private final PersistenceManager persistenceManager;
 
     // the basic ID of this instance
-    private final String baseId;
+    private final TargetedPID baseId;
 
     protected ConfigurationBase( final ConfigurationManager configurationManager,
         final PersistenceManager persistenceManager, final String baseId )
@@ -55,7 +56,7 @@
 
         this.configurationManager = configurationManager;
         this.persistenceManager = persistenceManager;
-        this.baseId = baseId;
+        this.baseId = new TargetedPID( baseId );
     }
 
 
@@ -71,7 +72,7 @@
     }
 
 
-    String getBaseId()
+    TargetedPID getBaseId()
     {
         return baseId;
     }
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 90aaf25..b721821 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
@@ -24,6 +24,7 @@
 import java.util.Hashtable;
 
 import org.apache.felix.cm.PersistenceManager;
+import org.apache.felix.cm.impl.helper.TargetedPID;
 import org.osgi.framework.Constants;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
@@ -99,7 +100,7 @@
      * The factory PID of this configuration or <code>null</code> if this
      * is not a factory configuration.
      */
-    private final String factoryPID;
+    private final TargetedPID factoryPID;
 
     /**
      * The statically bound bundle location, which is set explicitly by calling
@@ -145,12 +146,13 @@
     {
         super( configurationManager, persistenceManager, ( String ) properties.remove( Constants.SERVICE_PID ) );
 
-        this.factoryPID = ( String ) properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
+        final String factoryPid = ( String ) properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
+        this.factoryPID = ( factoryPid == null ) ? null : new TargetedPID( factoryPid );
         this.isDeleted = false;
 
         // set bundle location from persistence and/or check for dynamic binding
         this.staticBundleLocation = ( String ) properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) ;
-        this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( getBaseId() );
+        this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( getBaseId().toString() );
 
         // set the properties internally
         configureFromPersistence( properties );
@@ -162,12 +164,12 @@
     {
         super( configurationManager, persistenceManager, pid );
 
-        this.factoryPID = factoryPid;
+        this.factoryPID = ( factoryPid == null ) ? null : new TargetedPID( factoryPid );
         this.isDeleted = false;
 
         // set bundle location from persistence and/or check for dynamic binding
         this.staticBundleLocation = bundleLocation;
-        this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( getBaseId() );
+        this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( getBaseId().toString() );
 
         // first "update"
         this.properties = null;
@@ -186,24 +188,34 @@
     public void delete() throws IOException
     {
         this.isDeleted = true;
-        getPersistenceManager().delete( this.getPid() );
-        getConfigurationManager().setDynamicBundleLocation( this.getPid(), null );
+        getPersistenceManager().delete( this.getPidString() );
+        getConfigurationManager().setDynamicBundleLocation( this.getPidString(), null );
         getConfigurationManager().deleted( this );
     }
 
 
-    public String getPid()
+    public String getPidString()
+    {
+        return getBaseId().toString();
+    }
+
+
+    public TargetedPID getPid()
     {
         return getBaseId();
     }
 
 
-    public String getFactoryPid()
+    public String getFactoryPidString()
     {
-        return factoryPID;
+        return (factoryPID == null) ? null : factoryPID.toString();
     }
 
 
+    public TargetedPID getFactoryPid()
+    {
+        return factoryPID;
+    }
 
 
     /**
@@ -266,7 +278,7 @@
         final String oldBundleLocation = getBundleLocation();
 
         this.dynamicBundleLocation = bundleLocation;
-        this.getConfigurationManager().setDynamicBundleLocation( this.getBaseId(), bundleLocation );
+        this.getConfigurationManager().setDynamicBundleLocation( this.getPidString(), bundleLocation );
 
         // CM 1.4
         if ( dispatchConfiguration )
@@ -288,7 +300,7 @@
         if ( this.getBundleLocation() == null )
         {
             getConfigurationManager().log( LogService.LOG_DEBUG, "Dynamically binding config {0} to {1}", new Object[]
-                { getPid(), bundleLocation } );
+                { getPidString(), bundleLocation } );
             setDynamicBundleLocation( bundleLocation, true );
         }
 
@@ -335,15 +347,15 @@
         if ( localPersistenceManager != null )
         {
             // read configuration from persistence (again)
-            if ( localPersistenceManager.exists( getPid() ) )
+            if ( localPersistenceManager.exists( getPidString() ) )
             {
-                Dictionary properties = localPersistenceManager.load( getPid() );
+                Dictionary properties = localPersistenceManager.load( getPidString() );
 
                 // ensure serviceReference pid
                 String servicePid = ( String ) properties.get( Constants.SERVICE_PID );
-                if ( servicePid != null && !getPid().equals( servicePid ) )
+                if ( servicePid != null && !getPidString().equals( servicePid ) )
                 {
-                    throw new IOException( "PID of configuration file does match requested PID; expected " + getPid()
+                    throw new IOException( "PID of configuration file does match requested PID; expected " + getPidString()
                         + ", got " + servicePid );
                 }
 
@@ -367,34 +379,15 @@
             CaseInsensitiveDictionary newProperties = new CaseInsensitiveDictionary( properties );
 
             getConfigurationManager().log( LogService.LOG_DEBUG, "Updating config {0} with {1}", new Object[]
-                { getPid(), newProperties } );
+                { getPidString(), newProperties } );
 
             setAutoProperties( newProperties, true );
 
             // persist new configuration
-            localPersistenceManager.store( getPid(), newProperties );
+            localPersistenceManager.store( getPidString(), newProperties );
 
             // if this is a factory configuration, update the factory with
-            String factoryPid = getFactoryPid();
-            if ( factoryPid != null )
-            {
-                Factory factory = getConfigurationManager().getFactory( factoryPid );
-                if ( factory.addPID( getPid() ) )
-                {
-                    // only write back if the pid was not already registered
-                    // with the factory
-                    try
-                    {
-                        factory.store();
-                    }
-                    catch ( IOException ioe )
-                    {
-                        getConfigurationManager().log( LogService.LOG_ERROR,
-                            "Failure storing factory {0} with new configuration {1}", new Object[]
-                                { factoryPid, getPid(), ioe } );
-                    }
-                }
-            }
+            updateFactory();
 
             // finally assign the configuration for use
             configure( newProperties );
@@ -416,7 +409,7 @@
 
         if ( obj instanceof Configuration )
         {
-            return getPid().equals( ( ( Configuration ) obj ).getPid() );
+            return getPidString().equals( ( ( Configuration ) obj ).getPid() );
         }
 
         return false;
@@ -425,13 +418,13 @@
 
     public int hashCode()
     {
-        return getPid().hashCode();
+        return getPidString().hashCode();
     }
 
 
     public String toString()
     {
-        return "Configuration PID=" + getPid() + ", factoryPID=" + factoryPID + ", bundleLocation=" + getBundleLocation();
+        return "Configuration PID=" + getPidString() + ", factoryPID=" + factoryPID + ", bundleLocation=" + getBundleLocation();
     }
 
 
@@ -451,19 +444,56 @@
      */
     void ensureFactoryConfigPersisted() throws IOException
     {
-        if ( this.factoryPID != null && isNew() && !getPersistenceManager().exists( getPid() ) )
+        if ( this.factoryPID != null && isNew() && !getPersistenceManager().exists( getPidString() ) )
         {
             storeNewConfiguration();
         }
     }
 
 
+    /**
+     * Persists a new (freshly created) configuration with a marker for
+     * it to be a new configuration.
+     *
+     * @throws IOException If an error occurrs storing the configuraiton
+     */
     private void storeNewConfiguration() throws IOException
     {
         Dictionary props = new Hashtable();
         setAutoProperties( props, true );
         props.put( CONFIGURATION_NEW, Boolean.TRUE );
-        getPersistenceManager().store( getPid(), props );
+        getPersistenceManager().store( getPidString(), props );
+    }
+
+
+    /**
+     * Makes sure the configuration is added to the {@link Factory} (and
+     * the factory be stored if updated) if this is a factory
+     * configuration.
+     *
+     * @throws IOException If an error occurrs storing the {@link Factory}
+     */
+    private void updateFactory() throws IOException {
+        String factoryPid = getFactoryPidString();
+        if ( factoryPid != null )
+        {
+            Factory factory = getConfigurationManager().getOrCreateFactory( factoryPid );
+            if ( factory.addPID( getPidString() ) )
+            {
+                // only write back if the pid was not already registered
+                // with the factory
+                try
+                {
+                    factory.store();
+                }
+                catch ( IOException ioe )
+                {
+                    getConfigurationManager().log( LogService.LOG_ERROR,
+                        "Failure storing factory {0} with new configuration {1}", new Object[]
+                            { factoryPid, getPidString(), ioe } );
+                }
+            }
+        }
     }
 
 
@@ -489,7 +519,7 @@
         }
 
         // only store now, if this is not a new configuration
-        getPersistenceManager().store( getPid(), props );
+        getPersistenceManager().store( getPidString(), props );
     }
 
 
@@ -576,8 +606,8 @@
     void setAutoProperties( Dictionary properties, boolean withBundleLocation )
     {
         // set pid and factory pid in the properties
-        replaceProperty( properties, Constants.SERVICE_PID, getPid() );
-        replaceProperty( properties, ConfigurationAdmin.SERVICE_FACTORYPID, factoryPID );
+        replaceProperty( properties, Constants.SERVICE_PID, getPidString() );
+        replaceProperty( properties, ConfigurationAdmin.SERVICE_FACTORYPID, getFactoryPidString() );
 
         // bundle location is not set here
         if ( withBundleLocation )
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 b36b014..fd5ca09 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
@@ -29,6 +29,7 @@
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
@@ -163,11 +164,11 @@
     private int pmtCount;
 
     // the cache of Factory instances mapped by their factory PID
-    private final Map factories = new HashMap();
+    private final HashMap<String, Factory> factories = new HashMap<String, Factory>();
 
     // the cache of Configuration instances mapped by their PID
     // have this always set to prevent NPE on bundle shutdown
-    private final Map configurations = new HashMap();
+    private final HashMap<String, ConfigurationImpl> configurations = new HashMap<String, ConfigurationImpl>();
 
     /**
      * The map of dynamic configuration bindings. This maps the
@@ -273,14 +274,14 @@
         props.put( Constants.SERVICE_VENDOR, "Apache Software Foundation" );
         configurationAdminRegistration = bundleContext.registerService( ConfigurationAdmin.class.getName(), caf, props );
 
+        // start handling ManagedService[Factory] services
+        managedServiceTracker = new ManagedServiceTracker(this);
+        managedServiceFactoryTracker = new ManagedServiceFactoryTracker(this);
+
         // start processing the event queues only after registering the service
         // see FELIX-2813 for details
         this.updateThread.start();
         this.eventThread.start();
-
-        // start handling ManagedService[Factory] services
-        managedServiceTracker = new ManagedServiceTracker(this);
-        managedServiceFactoryTracker = new ManagedServiceFactoryTracker(this);
     }
 
 
@@ -290,6 +291,10 @@
         // stop handling bundle events immediately
         handleBundleEvents = false;
 
+        // stop handling ManagedService[Factory] services
+        managedServiceFactoryTracker.close();
+        managedServiceTracker.close();
+
         // stop queue processing before unregistering the service
         // see FELIX-2813 for details
         if ( updateThread != null )
@@ -315,10 +320,6 @@
         // unregistration the manager is still alive and can react
         isActive = false;
 
-        // stop handling ManagedService[Factory] services
-        managedServiceFactoryTracker.close();
-        managedServiceTracker.close();
-
         // don't care for PersistenceManagers any more
         persistenceManagerTracker.close();
 
@@ -370,7 +371,7 @@
     {
         synchronized ( configurations )
         {
-            return ( ConfigurationImpl ) configurations.get( pid );
+            return configurations.get( pid );
         }
     }
 
@@ -379,7 +380,7 @@
     {
         synchronized ( configurations )
         {
-            return ( ConfigurationImpl[] ) configurations.values().toArray(
+            return configurations.values().toArray(
                 new ConfigurationImpl[configurations.size()] );
         }
     }
@@ -389,13 +390,14 @@
     {
         synchronized ( configurations )
         {
-            Object existing = configurations.get( configuration.getPid() );
+            final String pid = configuration.getPidString();
+            final Object existing = configurations.get( pid );
             if ( existing != null )
             {
                 return ( ConfigurationImpl ) existing;
             }
 
-            configurations.put( configuration.getPid(), configuration );
+            configurations.put( pid, configuration );
             return configuration;
         }
     }
@@ -405,7 +407,7 @@
     {
         synchronized ( configurations )
         {
-            configurations.remove( configuration.getPid() );
+            configurations.remove( configuration.getPidString() );
         }
     }
 
@@ -414,7 +416,7 @@
     {
         synchronized ( factories )
         {
-            return ( Factory ) factories.get( factoryPid );
+            return factories.get( factoryPid );
         }
     }
 
@@ -423,7 +425,7 @@
     {
         synchronized ( factories )
         {
-            return ( Factory[] ) factories.values().toArray( new Factory[factories.size()] );
+            return factories.values().toArray( new Factory[factories.size()] );
         }
     }
 
@@ -432,7 +434,7 @@
     {
         synchronized ( factories )
         {
-            factories.put( factory.getFactoryPid(), factory );
+            factories.put( factory.getFactoryPidString(), factory );
         }
     }
 
@@ -472,6 +474,49 @@
         return cacheConfiguration( createConfiguration( createPid( factoryPid ), factoryPid, location ) );
     }
 
+    /**
+     * Returns a targeted configuration for the given service PID and
+     * the reference target service.
+     *
+     * @param rawPid The raw service PID to get targeted configuration for.
+     * @param target The target <code>ServiceReference</code> to get
+     *      configuration for.
+     * @return The best matching targeted configuration or <code>null</code>
+     *      if there is no configuration at all.
+     * @throwss IOException if an error occurrs reading configurations
+     *      from persistence.
+     */
+    ConfigurationImpl getTargetedConfiguration( final String rawPid, final ServiceReference target ) throws IOException
+    {
+        final Bundle serviceBundle = target.getBundle();
+        if ( serviceBundle != null )
+        {
+            // for pre-1.5 API compatibility
+            final StringBuffer targetedPid = new StringBuffer( rawPid );
+            int i = 3;
+            String[] names = new String[4];
+            names[i--] = targetedPid.toString();
+            targetedPid.append( '|' ).append( serviceBundle.getSymbolicName() );
+            names[i--] = targetedPid.toString();
+            targetedPid.append( '|' ).append( TargetedPID.getBundleVersion( serviceBundle ) );
+            names[i--] = targetedPid.toString();
+            targetedPid.append( '|' ).append( serviceBundle.getLocation() );
+            names[i--] = targetedPid.toString();
+
+            for ( String candidate : names )
+            {
+                ConfigurationImpl config = getConfiguration( candidate );
+                if ( config != null )
+                {
+                    return config;
+                }
+            }
+        }
+
+        // service already unregistered, nothing to do really
+        return null;
+    }
+
 
     /**
      * Returns the {@link ConfigurationImpl} with the given PID if
@@ -626,7 +671,7 @@
     {
         // remove the configuration from the cache
         removeConfiguration( config );
-        fireConfigurationEvent( ConfigurationEvent.CM_DELETED, config.getPid(), config.getFactoryPid() );
+        fireConfigurationEvent( ConfigurationEvent.CM_DELETED, config.getPidString(), config.getFactoryPidString() );
         updateThread.schedule( new DeleteConfiguration( config ) );
         log( LogService.LOG_DEBUG, "DeleteConfiguration({0}) scheduled", new Object[]
             { config.getPid() } );
@@ -637,7 +682,7 @@
     {
         if ( fireEvent )
         {
-            fireConfigurationEvent( ConfigurationEvent.CM_UPDATED, config.getPid(), config.getFactoryPid() );
+            fireConfigurationEvent( ConfigurationEvent.CM_UPDATED, config.getPidString(), config.getFactoryPidString() );
         }
         updateThread.schedule( new UpdateConfiguration( config ) );
         log( LogService.LOG_DEBUG, "UpdateConfiguration({0}) scheduled", new Object[]
@@ -647,7 +692,7 @@
 
     void locationChanged( ConfigurationImpl config, String oldLocation )
     {
-        fireConfigurationEvent( ConfigurationEvent.CM_LOCATION_CHANGED, config.getPid(), config.getFactoryPid() );
+        fireConfigurationEvent( ConfigurationEvent.CM_LOCATION_CHANGED, config.getPidString(), config.getFactoryPidString() );
         if ( oldLocation != null && !config.isNew() )
         {
             updateThread.schedule( new LocationChanged( config, oldLocation ) );
@@ -854,14 +899,100 @@
     }
 
 
+    /**
+     * Returns a list of {@link Factory} instances according to the
+     * Configuration Admin 1.5 specification for targeted PIDs (Section
+     * 104.3.2)
+     *
+     * @param rawFactoryPid The raw factory PID without any targetting.
+     * @param target The <code>ServiceReference</code> of the service to
+     *      be supplied with targeted configuration.
+     * @return A list of {@link Factory} instances as listed above. This
+     *      list will always at least include an instance for the
+     *      <code>rawFactoryPid</code>. Other instances are only included
+     *      if existing.
+     * @throws IOException If an error occurrs reading any of the
+     *      {@link Factory} instances from persistence
+     */
+    List<Factory> getTargetedFactories( final String rawFactoryPid, final ServiceReference target ) throws IOException
+    {
+        LinkedList<Factory> factories = new LinkedList<Factory>();
+
+        final Bundle serviceBundle = target.getBundle();
+        if ( serviceBundle != null )
+        {
+            // for pre-1.5 API compatibility
+            final StringBuffer targetedPid = new StringBuffer( rawFactoryPid );
+            factories.add( getOrCreateFactory( targetedPid.toString() ) );
+
+            targetedPid.append( '|' ).append( serviceBundle.getSymbolicName() );
+            Factory f = getFactory( targetedPid.toString() );
+            if ( f != null )
+            {
+                factories.add( 0, f );
+            }
+
+            targetedPid.append( '|' ).append( TargetedPID.getBundleVersion( serviceBundle ) );
+            f = getFactory( targetedPid.toString() );
+            if ( f != null )
+            {
+                factories.add( 0, f );
+            }
+
+            targetedPid.append( '|' ).append( serviceBundle.getLocation() );
+            f = getFactory( targetedPid.toString() );
+            if ( f != null )
+            {
+                factories.add( 0, f );
+            }
+        }
+
+        return factories;
+    }
+
+
+    /**
+     * Gets the factory with the exact identifier from the cached or from
+     * the persistence managers. If no factory exists already one is
+     * created and cached.
+     *
+     * @param factoryPid The PID of the {@link Factory} to return
+     * @return The existing or newly created {@link Factory}
+     * @throws IOException If an error occurrs reading the factory from
+     *      a {@link PersistenceManager}
+     */
+    Factory getOrCreateFactory( String factoryPid ) throws IOException
+    {
+        Factory factory = getFactory( factoryPid );
+        if ( factory != null )
+        {
+            return factory;
+        }
+
+        return createFactory( factoryPid );
+    }
+
+
+    /**
+     * Gets the factory with the exact identifier from the cached or from
+     * the persistence managers. If no factory exists <code>null</code>
+     * is returned.
+     *
+     * @param factoryPid The PID of the {@link Factory} to return
+     * @return The existing {@link Factory} or <code>null</code>
+     * @throws IOException If an error occurrs reading the factory from
+     *      a {@link PersistenceManager}
+     */
     Factory getFactory( String factoryPid ) throws IOException
     {
+        // check for cached factory
         Factory factory = getCachedFactory( factoryPid );
         if ( factory != null )
         {
             return factory;
         }
 
+        // try to load factory from persistence
         PersistenceManager[] pmList = getPersistenceManagers();
         for ( int i = 0; i < pmList.length; i++ )
         {
@@ -873,11 +1004,14 @@
             }
         }
 
-        // if getting here, there is no configuration yet, optionally create new
-        return createFactory( factoryPid );
+        // no existing factory
+        return null;
     }
 
 
+    /**
+     * Creates a new factory with the given <code>factoryPid</code>.
+     */
     Factory createFactory( String factoryPid )
     {
         Factory factory = new Factory( this, getPersistenceManagers()[0], factoryPid );
@@ -1195,7 +1329,7 @@
             long revision = -1;
             try
             {
-                config = getConfiguration( pid );
+                config = getTargetedConfiguration( pid, sr );
                 if ( config != null )
                 {
                     synchronized ( config )
@@ -1224,8 +1358,8 @@
             // check configuration and call plugins if existing
             if ( config != null )
             {
-                log( LogService.LOG_DEBUG, "Updating configuration {0} to revision #{1}", new Object[]
-                    { pid, new Long( revision ) } );
+                log( LogService.LOG_DEBUG, "Updating service {0} to with configuration {1}@{2}", new Object[]
+                    { pid, this.config.getPid(), new Long( revision ) } );
 
                 Bundle serviceBundle = sr.getBundle();
                 if ( serviceBundle == null )
@@ -1296,13 +1430,13 @@
             this.factoryPid = factoryPid;
             this.sr = sr;
 
-            Factory factory = null;
+            List<Factory> factories = null;
             Map configs = null;
             Map revisions = null;
             try
             {
-                factory = getFactory( factoryPid );
-                if (factory != null) {
+                factories = getTargetedFactories( factoryPid, sr );
+                for (Factory factory : factories) {
                     configs = new HashMap();
                     revisions = new HashMap();
                     for ( Iterator pi = factory.getPIDs().iterator(); pi.hasNext(); )
@@ -1342,6 +1476,12 @@
                                 { pid } );
                             continue;
                         }
+                        /*
+                         * this code would catch targeted factory PIDs;
+                         * since this is not expected any way, we can
+                         * leave this out
+                         */
+                        /*
                         else if ( !factoryPid.equals( cfg.getFactoryPid() ) )
                         {
                             log( LogService.LOG_ERROR,
@@ -1352,6 +1492,7 @@
                             factory.storeSilently();
                             continue;
                         }
+                        */
 
                         // get the configuration properties for later
                         synchronized ( cfg )
@@ -1401,8 +1542,8 @@
                     final Dictionary properties = ( Dictionary ) entry.getValue();
                     final long revision = ( ( Long ) revisions.get( cfg ) ).longValue();
 
-                    log( LogService.LOG_DEBUG, "Updating configuration {0} to revision #{1}", new Object[]
-                        { cfg.getPid(), new Long( revision ) } );
+                    log( LogService.LOG_DEBUG, "Updating service {0} with configuration {1}/{2}@{3}", new Object[]
+                        { this.factoryPid, cfg.getFactoryPid(), cfg.getPid(), new Long( revision ) } );
 
                     // CM 1.4 / 104.13.2.1
                     if ( !canReceive( serviceBundle, cfg.getBundleLocation() ) )
@@ -1435,31 +1576,37 @@
         }
     }
 
-    private abstract class ConfigurationProvider<T> implements Runnable {
+    private abstract class ConfigurationProvider<T> implements Runnable
+    {
 
         protected final ConfigurationImpl config;
         protected final long revision;
         protected final Dictionary<String, ?> properties;
         protected final BaseTracker<T> helper;
 
-        protected ConfigurationProvider(final ConfigurationImpl config) {
-            this.config = config;
-            this.helper = ( BaseTracker<T> ) ( ( config.getFactoryPid() == null ) ? managedServiceTracker : managedServiceFactoryTracker );
+
+        protected ConfigurationProvider( final ConfigurationImpl config )
+        {
             synchronized ( config )
             {
+                this.config = config;
                 this.revision = config.getRevision();
                 this.properties = config.getProperties( true );
             }
+            this.helper = ( BaseTracker<T> ) ( ( config.getFactoryPid() == null ) ? managedServiceTracker
+                : managedServiceFactoryTracker );
         }
 
-        protected TargetedPID getTargetedServicePid() {
-            final String factoryPid = this.config.getFactoryPid();
-            if (factoryPid == null) {
-                return new TargetedPID( this.config.getPid() );
+
+        protected TargetedPID getTargetedServicePid()
+        {
+            final TargetedPID factoryPid = this.config.getFactoryPid();
+            if ( factoryPid != null )
+            {
+                return factoryPid;
             }
-            return new TargetedPID( factoryPid );
+            return this.config.getPid();
         }
-
     }
 
     /**
@@ -1558,9 +1705,6 @@
 
         public void run()
         {
-            final String pid = config.getPid();
-            final String factoryPid = config.getFactoryPid();
-
             List<ServiceReference<?>> srList = this.helper.getServices( getTargetedServicePid() );
             if ( !srList.isEmpty() )
             {
@@ -1589,12 +1733,14 @@
                 }
             }
 
+            final TargetedPID factoryPid = config.getFactoryPid();
             if ( factoryPid != null )
             {
                 // remove the pid from the factory
+                final String pid = config.getPidString();
                 try
                 {
-                    Factory factory = getFactory( factoryPid );
+                    Factory factory = getOrCreateFactory( factoryPid.toString() );
                     factory.removePID( pid );
                     factory.store();
                 }
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 5122816..b3564ac 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,6 +26,7 @@
 import java.util.Set;
 
 import org.apache.felix.cm.PersistenceManager;
+import org.apache.felix.cm.impl.helper.TargetedPID;
 
 
 /**
@@ -86,12 +87,18 @@
     }
 
 
-    String getFactoryPid()
+    TargetedPID getFactoryPid()
     {
         return getBaseId();
     }
 
 
+    String getFactoryPidString()
+    {
+        return getFactoryPid().toString();
+    }
+
+
     Set getPIDs()
     {
         return new HashSet( pids );
@@ -119,7 +126,7 @@
             props.put( FACTORY_PID_LIST, pids.toArray( new String[pids.size()] ) );
         }
 
-        String id = factoryPidToIdentifier( this.getFactoryPid() );
+        String id = factoryPidToIdentifier( this.getFactoryPid().toString() );
         if ( props.isEmpty() )
         {
             getPersistenceManager().delete( id );
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/helper/ManagedServiceFactoryTracker.java b/configadmin/src/main/java/org/apache/felix/cm/impl/helper/ManagedServiceFactoryTracker.java
index 58eed84..6910eaf 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/helper/ManagedServiceFactoryTracker.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/helper/ManagedServiceFactoryTracker.java
@@ -42,8 +42,8 @@
         {
             try
             {
-                Dictionary props = getProperties( rawProps, config.getFactoryPid(), reference );
-                service.updated( config.getPid(), props );
+                Dictionary props = getProperties( rawProps, config.getFactoryPidString(), reference );
+                service.updated( config.getPidString(), props );
             }
             catch ( Throwable t )
             {
@@ -65,7 +65,7 @@
         {
             try
             {
-                service.deleted( config.getPid() );
+                service.deleted( config.getPidString() );
             }
             catch ( Throwable t )
             {
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/helper/ManagedServiceTracker.java b/configadmin/src/main/java/org/apache/felix/cm/impl/helper/ManagedServiceTracker.java
index 56f81a2..b5e1f29 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/helper/ManagedServiceTracker.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/helper/ManagedServiceTracker.java
@@ -61,7 +61,7 @@
             {
                 if ( properties != null )
                 {
-                    properties = getProperties( properties, config.getPid(), service );
+                    properties = getProperties( properties, config.getPidString(), service );
                 }
 
                 srv.updated( properties );
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/helper/TargetedPID.java b/configadmin/src/main/java/org/apache/felix/cm/impl/helper/TargetedPID.java
index 784ef8f..d41f045 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/helper/TargetedPID.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/helper/TargetedPID.java
@@ -45,6 +45,26 @@
     private final String location;
 
 
+    /**
+     * Returns the bundle's version as required for targeted PIDs: If the
+     * bundle has a version the string representation of the version
+     * string converted to a Version object is returned. Otherwise the
+     * string representation of <code>Version.emptyVersion</code> is
+     * returned.
+     * <p>
+     * To remain compatible with pre-R4.2 (Framework API < 1.5) we cannot
+     * use the <code>Bundle.getVersion()</code> method.
+     *
+     * @param bundle The bundle whose version is to be returned.
+     */
+    public static String getBundleVersion( final Bundle bundle )
+    {
+        Object vHeader = bundle.getHeaders().get( Constants.BUNDLE_VERSION );
+        Version version = ( vHeader == null ) ? Version.emptyVersion : new Version( vHeader.toString() );
+        return version.toString();
+    }
+
+
     public TargetedPID( final String rawPid )
     {
         this.rawPid = rawPid;
@@ -137,9 +157,8 @@
         }
 
         // bundle version does not match
-        Object v = serviceBundle.getHeaders().get( Constants.BUNDLE_VERSION );
-        Version s = ( v == null ) ? Version.emptyVersion : new Version( v.toString() );
-        if ( !this.version.equals( s.toString() ) )
+
+        if ( !this.version.equals( getBundleVersion( serviceBundle ) ) )
         {
             return false;
         }
@@ -282,4 +301,10 @@
         // not the same class or different raw PID
         return false;
     }
+
+    @Override
+    public String toString()
+    {
+        return this.rawPid;
+    }
 }