FELIX-3524: added support for new OSGi 4.3 configuration-pid attribute.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1345185 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java b/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java
index 6ab57b6..8aadf20 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java
@@ -22,8 +22,11 @@
 import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.felix.scr.Component;
 import org.apache.felix.scr.ScrService;
@@ -77,6 +80,25 @@
     private final Map m_componentHoldersByName;
 
     /**
+     * The map of known components indexed by component configuration pid. The values are
+     * Sets of the {@link ComponentHolder} interface. Normally, the configuration pid
+     * is the component name, but since DS 1.2 (OSGi 4.3), a component may specify a specific 
+     * pid, and it is possible that different components refer to the same pid. That's why 
+     * the values of this map are Sets of ComponentHolders, allowing to lookup all components
+     * which are using a given configuration pid.
+     * This map is used when the ConfigurationSupport detects that a CM pid is updated. When 
+     * a PID is updated, the ConfigurationSupport listener class invokes the 
+     * {@link #getComponentHoldersByPid(String)} method which returns an iterator over all 
+     * components that are using the given pid for configuration.
+     * <p>
+     *
+     * @see #registerComponentHolder(String, ComponentHolder)
+     * @see #unregisterComponentHolder(String)
+     * @see ConfigurationSupport#configurationEvent(org.osgi.service.cm.ConfigurationEvent)
+     */
+    private final Map m_componentHoldersByPid;
+
+    /**
      * Map of components by component ID. This map indexed by the component
      * ID number (<code>java.lang.Long</code>) contains the actual
      * {@link AbstractComponentManager} instances existing in the system.
@@ -107,6 +129,7 @@
     {
         m_bundleContext = context;
         m_componentHoldersByName = new HashMap();
+        m_componentHoldersByPid = new HashMap();
         m_componentsById = new HashMap();
         m_componentCounter = -1;
 
@@ -355,8 +378,24 @@
 
             m_componentHoldersByName.put( name, component );
         }
-    }
 
+        synchronized (m_componentHoldersByPid)
+        {
+            // See if the component declares a specific configuration pid (112.4.4 configuration-pid)
+            String configurationPid = component.getComponentMetadata().getConfigurationPid();
+
+            // Since several components may refer to the same configuration pid, we have to
+            // store the component holder in a Set, in order to be able to lookup every
+            // components from a given pid.            
+            Set set = (Set) m_componentHoldersByPid.get(configurationPid);
+            if (set == null)
+            {
+                set = new HashSet();
+                m_componentHoldersByPid.put(configurationPid, set);
+            }
+            set.add(component);
+        }
+    }
 
     /**
      * Returns the component registered under the given name or <code>null</code>
@@ -379,6 +418,27 @@
         return null;
     }
 
+    /**
+     * Returns the list of ComponentHolder instances whose configuration pids are matching 
+     * the given pid.
+     * @param pid the pid candidate
+     * @return a iterator of ComponentHolder, or an empty iterator if no ComponentHolders 
+     * are found
+     */
+    public final Iterator getComponentHoldersByPid(String pid)
+    {
+        Set componentHoldersUsingPid = new HashSet();
+        synchronized (m_componentHoldersByPid)
+        {
+            Set set = (Set) m_componentHoldersByPid.get(pid);
+            // only return the entry if non-null and not a reservation
+            if (set != null)
+            {
+                componentHoldersUsingPid.addAll(set);
+            }
+        }
+        return componentHoldersUsingPid.iterator();
+    }
 
     /**
      * Returns an array of all values currently stored in the component holders
@@ -403,13 +463,29 @@
      */
     final void unregisterComponentHolder( String name )
     {
+        Object component;
         synchronized ( m_componentHoldersByName )
         {
-            m_componentHoldersByName.remove( name );
+            component = m_componentHoldersByName.remove(name);
+        }
+        
+        if (component instanceof ComponentHolder) {
+            synchronized (m_componentHoldersByPid)
+            {
+                String configurationPid = ((ComponentHolder) component).getComponentMetadata().getConfigurationPid();
+                Set componentsForPid = (Set) m_componentHoldersByPid.get(configurationPid);
+                if (componentsForPid != null)
+                {
+                    componentsForPid.remove(component);
+                    if (componentsForPid.size() == 0) 
+                    {
+                        m_componentHoldersByPid.remove(configurationPid);
+                    }
+                }
+            }
         }
     }
 
-
     //---------- base configuration support
 
     /**
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurationSupport.java b/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurationSupport.java
index f158643..d60fb52 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurationSupport.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurationSupport.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.util.Dictionary;
 import java.util.Hashtable;
+import java.util.Iterator;
 
 import org.apache.felix.scr.impl.Activator;
 import org.apache.felix.scr.impl.BundleComponentActivator;
@@ -76,7 +77,7 @@
         {
             final BundleContext bundleContext = holder.getActivator().getBundleContext();
             final String bundleLocation = bundleContext.getBundle().getLocation();
-            final String name = holder.getComponentMetadata().getName();
+            final String confPid = holder.getComponentMetadata().getConfigurationPid();
 
             final ServiceReference caRef = bundleContext.getServiceReference(ComponentRegistry.CONFIGURATION_ADMIN);
             if (caRef != null)
@@ -86,7 +87,7 @@
                 {
                     try
                     {
-                        final Configuration[] factory = findFactoryConfigurations(ca, name);
+                        final Configuration[] factory = findFactoryConfigurations(ca, confPid);
                         if (factory != null)
                         {
                             for (int i = 0; i < factory.length; i++)
@@ -99,11 +100,11 @@
                         else
                         {
                             // check for configuration and configure the holder
-                            final Configuration singleton = findSingletonConfiguration(ca, name);
+                            final Configuration singleton = findSingletonConfiguration(ca, confPid);
                             if (singleton != null)
                             {
-                                final Dictionary props = getConfiguration(ca, name, bundleLocation);
-                                holder.configurationUpdated(name, props);
+                                final Dictionary props = getConfiguration(ca, confPid, bundleLocation);
+                                holder.configurationUpdated(confPid, props);
                             }
                         }
                     }
@@ -164,24 +165,29 @@
         final String pid = event.getPid();
         final String factoryPid = event.getFactoryPid();
 
-        final ComponentHolder cm;
+        // iterate over all components which must be configured with this pid
+        // (since DS 1.2, components may specify a specific configuration PID (112.4.4 configuration-pid)
+        Iterator it; 
+              
         if (factoryPid == null)
         {
-            cm = this.m_registry.getComponentHolder(pid);
+            it = this.m_registry.getComponentHoldersByPid(pid);
         }
         else
         {
-            cm = this.m_registry.getComponentHolder(factoryPid);
+            it = this.m_registry.getComponentHoldersByPid(factoryPid);
         }
 
         Activator.log(LogService.LOG_DEBUG, null, "configurationEvent: Handling "
-            + ((event.getType() == ConfigurationEvent.CM_DELETED) ? "DELETE" : "UPDATE") + " of Configuration PID="
-            + pid, null);
+                + ((event.getType() == ConfigurationEvent.CM_DELETED) ? "DELETE" : "UPDATE")
+                + " of Configuration PID=" + pid, null);
 
-        if (cm != null && !cm.getComponentMetadata().isConfigurationIgnored())
+        while (it.hasNext())
         {
-            switch (event.getType())
+            final ComponentHolder cm = (ComponentHolder) it.next();
+            if (!cm.getComponentMetadata().isConfigurationIgnored())
             {
+                switch (event.getType()) {
                 case ConfigurationEvent.CM_DELETED:
                     cm.configurationDeleted(pid);
                     break;
@@ -235,6 +241,7 @@
                 default:
                     Activator.log(LogService.LOG_WARNING, null, "Unknown ConfigurationEvent type " + event.getType(),
                         null);
+                }
             }
         }
     }
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/config/ImmediateComponentHolder.java b/scr/src/main/java/org/apache/felix/scr/impl/config/ImmediateComponentHolder.java
index a627314..398d73d 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/config/ImmediateComponentHolder.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/config/ImmediateComponentHolder.java
@@ -26,6 +26,7 @@
 
 import org.apache.felix.scr.Component;
 import org.apache.felix.scr.impl.BundleComponentActivator;
+import org.apache.felix.scr.impl.manager.ComponentFactoryImpl;
 import org.apache.felix.scr.impl.manager.DelayedComponentManager;
 import org.apache.felix.scr.impl.manager.ImmediateComponentManager;
 import org.apache.felix.scr.impl.manager.ServiceFactoryComponentManager;
@@ -185,7 +186,7 @@
             return;
         }
 
-        if ( pid.equals( getComponentMetadata().getName() ) )
+        if ( pid.equals( getComponentMetadata().getConfigurationPid() ) )
         {
             // singleton configuration deleted
             m_singleComponent.obtainStateLock();
@@ -269,7 +270,7 @@
             return;
         }
 
-        if ( pid.equals( getComponentMetadata().getName() ) )
+        if ( pid.equals( getComponentMetadata().getConfigurationPid() ) )
         {
             m_singleComponent.obtainStateLock();
             try
@@ -444,6 +445,39 @@
         }
     }
 
+    /**
+     * Compares this {@code ImmediateComponentHolder} object to another object.
+     * 
+     * <p>
+     * A ImmediateComponentHolder is considered to be <b>equal to </b> another 
+     * ImmediateComponentHolder if the component names are equal(using 
+     * {@code String.equals}).
+     * 
+     * @param object The {@code ImmediateComponentHolder} object to be compared.
+     * @return {@code true} if {@code object} is a
+     *         {@code ImmediateComponentHolder} and is equal to this object;
+     *         {@code false} otherwise.
+     */
+   public boolean equals(Object object)
+    {
+        if (!(object instanceof ImmediateComponentHolder))
+        {
+            return false;
+        }
+
+        ImmediateComponentHolder other = (ImmediateComponentHolder) object;
+        return getComponentMetadata().getName().equals(other.getComponentMetadata().getName());
+    }
+    
+   /**
+    * Returns a hash code value for the object.
+    * 
+    * @return An integer which is a hash code value for this object.
+    */
+   public int hashCode()
+   {
+       return getComponentMetadata().getName().hashCode();
+   }
 
     //---------- internal
 
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java
index 2724a4d..524fb27 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java
@@ -124,13 +124,13 @@
         {
             log(
                 LogService.LOG_DEBUG,
-                "Component {0} created: DS={1}, implementation={2}, immediate={3}, default-enabled={4}, factory={5}, configuration-policy={6}, activate={7}, deactivate={8}, modified={9}",
+                "Component {0} created: DS={1}, implementation={2}, immediate={3}, default-enabled={4}, factory={5}, configuration-policy={6}, activate={7}, deactivate={8}, modified={9} configuration-pid={10}",
                 new Object[]
                     { metadata.getName(), new Integer( metadata.getNamespaceCode() ),
                         metadata.getImplementationClassName(), Boolean.valueOf( metadata.isImmediate() ),
                         Boolean.valueOf( metadata.isEnabled() ), metadata.getFactoryIdentifier(),
                         metadata.getConfigurationPolicy(), metadata.getActivate(), metadata.getDeactivate(),
-                        metadata.getModified() }, null );
+                        metadata.getModified(), metadata.getConfigurationPid() }, null );
 
             if ( metadata.getServiceMetadata() != null )
             {
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java
index c7c5fad..1a7df82 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java
@@ -140,6 +140,38 @@
         return instance;
     }
 
+    /**
+     * Compares this {@code ComponentFactoryImpl} object to another object.
+     * 
+     * <p>
+     * A component factory impl is considered to be <b>equal to </b> another component
+     * factory impl if the component names are equal(using {@code String.equals}).
+     * 
+     * @param object The {@code ComponentFactoryImpl} object to be compared.
+     * @return {@code true} if {@code object} is a
+     *         {@code ComponentFactoryImpl} and is equal to this object;
+     *         {@code false} otherwise.
+     */
+   public boolean equals(Object object)
+    {
+        if (!(object instanceof ComponentFactoryImpl))
+        {
+            return false;
+        }
+
+        ComponentFactoryImpl other = (ComponentFactoryImpl) object;
+        return getComponentMetadata().getName().equals(other.getComponentMetadata().getName());
+    }
+    
+   /**
+    * Returns a hash code value for the object.
+    * 
+    * @return An integer which is a hash code value for this object.
+    */
+   public int hashCode()
+   {
+       return getComponentMetadata().getName().hashCode();
+   }
 
     /**
      * The component factory does not have a component to create.
@@ -264,7 +296,7 @@
 
     public void configurationDeleted( String pid )
     {
-        if ( pid.equals( getComponentMetadata().getName() ) )
+        if ( pid.equals( getComponentMetadata().getConfigurationPid() ) )
         {
             // deleting configuration of a component factory is like
             // providing an empty configuration
@@ -299,7 +331,7 @@
 
     public void configurationUpdated( String pid, Dictionary configuration )
     {
-        if ( pid.equals( getComponentMetadata().getName() ) )
+        if ( pid.equals( getComponentMetadata().getConfigurationPid() ) )
         {
             m_configuration = configuration;
         }
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java b/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
index 3b524a7..2469c9d 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
@@ -94,6 +94,9 @@
     // 112.4.3 configuration-policy (since DS 1.1)
     private String m_configurationPolicy = null;
 
+    // 112.4.4 configuration-pid (since DS 1.2)
+    private String m_configurationPid;
+
     // Associated properties (0..*)
     private Dictionary m_properties = new Hashtable();
 
@@ -129,6 +132,19 @@
     /////////////////////////////////////////// SETTERS //////////////////////////////////////
 
     /**
+     * Setter for the configuration-pid component (since DS 1.2)
+     * @param configurationPid
+     */
+    public void setConfigirationPid(String configurationPid)
+    {
+        if ( m_validated )
+        {
+            return;
+        }
+        m_configurationPid = configurationPid;
+    }
+
+    /**
      * Setter for the name
      *
      * @param name
@@ -418,6 +434,20 @@
         return getImplementationClassName();
     }
 
+    /**
+     * Returns the configuration pid for the component. The pid is the one specified in the
+     * component's configuration-pid DS 1.2 attribute, if specified. Else the component name is used
+     * as the pid by default.
+     */
+    public String getConfigurationPid()
+    {
+        if (m_configurationPid != null) 
+        {
+            return m_configurationPid;
+        }
+        return getName();
+    }
+
 
     /**
      * Returns the value of the enabled flag
@@ -714,6 +744,12 @@
             throw validationFailure( "modified method declaration requires DS 1.1 or later namespace " );
         }
 
+        // 112.4.4 configuration-pid can be specified since DS 1.2
+        if ( m_configurationPid != null && m_namespaceCode < XmlHandler.DS_VERSION_1_2 )
+        {
+            throw validationFailure( "configuration-pid attribute requires DS 1.2 or later namespace " );
+        }
+
         // Next check if the properties are valid (and extract property values)
         Iterator propertyIterator = m_propertyMetaData.iterator();
         while ( propertyIterator.hasNext() )