FELIX-1284 Add support to check whether a dynamic configuration update
with a modified method is possible from the point of view of dependencies.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@798526 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
index d0d2ec2..a889f12 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
@@ -522,14 +522,20 @@
      */
     ServiceReference[] getFrameworkServiceReferences()
     {
+        return getFrameworkServiceReferences( getTarget() );
+    }
+
+
+    private ServiceReference[] getFrameworkServiceReferences( String targetFilter )
+    {
         try
         {
             return m_componentManager.getActivator().getBundleContext().getServiceReferences(
-                m_dependencyMetadata.getInterface(), getTarget() );
+                m_dependencyMetadata.getInterface(), targetFilter );
         }
         catch ( InvalidSyntaxException ise )
         {
-            m_componentManager.log( LogService.LOG_ERROR, "Unexpected problem with filter '" + getTarget() + "'",
+            m_componentManager.log( LogService.LOG_ERROR, "Unexpected problem with filter '" + targetFilter + "'",
                 m_componentManager.getComponentMetadata(), ise );
             return null;
         }
@@ -1300,6 +1306,74 @@
     //------------- Service target filter support -----------------------------
 
     /**
+     * Returns <code>true</code> if the <code>properties</code> can be
+     * dynamically applied to the component to which the dependency manager
+     * belongs.
+     * <p>
+     * This method applies the following heuristics (in the given order):
+     * <ol>
+     * <li>If there is no change in the target filter for this dependency, the
+     * properties can be applied</li>
+     * <li>If the dependency is static and there are changes in the target
+     * filter we cannot dynamically apply the configuration because the filter
+     * may (assume they do for simplicity here) cause the bindings to change.</li>
+     * <li>If there is still at least one service matching the new target filter
+     * we can apply the configuration because the depdency is dynamic.</li>
+     * <li>If there are no more services matching the filter, we can still
+     * apply the configuration if the dependency is optional.</li>
+     * <li>Ultimately, if all other checks do not apply we cannot dynamically
+     * apply.</li>
+     * </ol>
+     */
+    boolean canUpdateDynamically( Dictionary properties )
+    {
+        // 1. no target filter change
+        final String targetFilter = ( String ) properties.get( m_dependencyMetadata.getTargetPropertyName() );
+        if ( ( getTarget() == null && targetFilter == null ) || getTarget().equals( targetFilter ) )
+        {
+            // can update if target filter is not changed, since there is
+            // no change is service binding
+            return true;
+        }
+        // invariant: target filter change
+
+        // 2. if static policy, cannot update dynamically
+        // (for simplicity assuming change in target service binding)
+        if ( m_dependencyMetadata.isStatic() )
+        {
+            // cannot update if services are statically bound and the target
+            // filter is modified, since there is (potentially at least)
+            // a change is service bindings
+            return false;
+        }
+        // invariant: target filter change + dynamic policy
+
+        // 3. check target services matching the new filter
+        ServiceReference[] refs = getFrameworkServiceReferences( targetFilter );
+        if ( refs != null && refs.length > 0 )
+        {
+            // can update since there is at least on service matching the
+            // new target filter and the services may be exchanged dynamically
+            return true;
+        }
+        // invariant: target filter change + dynamic policy + no more matching service
+
+        // 4. check optionality
+        if ( m_dependencyMetadata.isOptional() )
+        {
+            // can update since even if no service matches the new filter, this
+            // makes no difference because the dependency is optional
+            return true;
+        }
+        // invariant: target filter change + dynamic policy + no more matching service + required
+
+        // 5. cannot dynamically update because the target filter results in
+        // no more applicable services which is not acceptable
+        return false;
+    }
+
+
+    /**
      * Sets the target filter from target filter property contained in the
      * properties. The filter is taken from a property whose name is derived
      * from the dependency name and the suffix <code>.target</code> as defined