FELIX-3557 resolve-circular-dependencies-later implementation

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1398942 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java b/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
index 9f1d6d0..1be5427 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
@@ -35,11 +35,13 @@
 import org.apache.felix.scr.impl.config.ScrConfiguration;
 import org.apache.felix.scr.impl.helper.Logger;
 import org.apache.felix.scr.impl.manager.AbstractComponentManager;
+import org.apache.felix.scr.impl.manager.DependencyManager;
 import org.apache.felix.scr.impl.metadata.ComponentMetadata;
 import org.apache.felix.scr.impl.metadata.XmlHandler;
 import org.apache.felix.scr.impl.parser.KXml2SAXParser;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.component.ComponentException;
 import org.osgi.service.log.LogService;
 import org.osgi.util.tracker.ServiceTracker;
@@ -597,4 +599,13 @@
         }
     }
 
+    public void missingServicePresent( ServiceReference serviceReference )
+    {
+        m_componentRegistry.missingServicePresent( serviceReference, m_componentActor );
+    }
+
+    public void registerMissingDependency( DependencyManager dependencyManager, ServiceReference serviceReference )
+    {
+        m_componentRegistry.registerMissingDependency(dependencyManager, serviceReference);
+    }
 }
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 deb4e99..0e2ab5c 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
@@ -38,6 +38,7 @@
 import org.apache.felix.scr.impl.manager.AbstractComponentManager;
 import org.apache.felix.scr.impl.manager.ComponentFactoryImpl;
 import org.apache.felix.scr.impl.manager.ConfigurationComponentFactoryImpl;
+import org.apache.felix.scr.impl.manager.DependencyManager;
 import org.apache.felix.scr.impl.metadata.ComponentMetadata;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -47,6 +48,7 @@
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.ComponentConstants;
 import org.osgi.service.component.ComponentException;
 
 
@@ -128,6 +130,8 @@
     // the ConfigurationAdmin service
     private ConfigurationSupport configurationSupport;
 
+    private final Map m_missingDependencies = new HashMap( );
+
     protected ComponentRegistry( BundleContext context )
     {
         m_bundleContext = context;
@@ -648,4 +652,41 @@
             this.configurationSupport = null;
         }
     }
+
+    public void missingServicePresent( final ServiceReference serviceReference, ComponentActorThread actor )
+    {
+        final List dependencyManagers = ( List ) m_missingDependencies.remove( serviceReference );
+        if ( dependencyManagers != null )
+        {
+            actor.schedule( new Runnable()
+            {
+
+                public void run()
+                {
+                    for ( Iterator i = dependencyManagers.iterator(); i.hasNext(); )
+                    {
+                        DependencyManager dm = ( DependencyManager ) i.next();
+                        dm.invokeBindMethodLate( serviceReference );
+                    }
+                }
+            } );
+        }
+    }
+
+    public synchronized void registerMissingDependency( DependencyManager dependencyManager, ServiceReference serviceReference )
+    {
+        //check that the service reference is from scr
+        if ( serviceReference.getProperty( ComponentConstants.COMPONENT_NAME ) == null || serviceReference.getProperty( ComponentConstants.COMPONENT_ID ) == null )
+        {
+            return;
+        }
+        List dependencyManagers = ( List ) m_missingDependencies.get( serviceReference );
+        if ( dependencyManagers == null )
+        {
+            dependencyManagers = new ArrayList();
+            m_missingDependencies.put( serviceReference, dependencyManagers );
+        }
+        dependencyManagers.add( dependencyManager );
+    }
+
 }
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 d15fa77..27f74d4 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
@@ -1059,7 +1059,7 @@
             return true;
         }
 
-        Map result = new HashMap(); //<ServiceReference, Object[]>
+        Map result = new HashMap(); //<ServiceReference, RefPair>
         // assume success to begin with: if the dependency is optional,
         // we don't care, whether we can bind a service. Otherwise, we
         // require at least one service to be bound, thus we require
@@ -1083,6 +1083,10 @@
                         // of course, we have success if the service is bound
                         success = true;
                     }
+                    else
+                    {
+                        m_componentManager.getActivator().registerMissingDependency(this, refs[index]);
+                    }
                 }
             }
         }
@@ -1100,6 +1104,10 @@
                     // of course, we have success if the service is bound
                     success = true;
                 }
+                else if ( isOptional() )
+                {
+                    m_componentManager.getActivator().registerMissingDependency(this, ref);
+                }
             }
         }
 
@@ -1249,6 +1257,33 @@
         }
     }
 
+    public void invokeBindMethodLate( final ServiceReference ref )
+    {
+        if ( !isSatisfied() )
+        {
+            return;
+        }
+        if ( !isMultiple() )
+        {
+            ServiceReference[] refs = getFrameworkServiceReferences();
+            if ( refs == null )
+            {
+                return; // should not happen, we have one!
+            }
+            // find the service with the highest ranking
+            for ( int i = 1; i < refs.length; i++ )
+            {
+                ServiceReference test = refs[i];
+                if ( test.compareTo( ref ) > 0 )
+                {
+                    return; //another ref is better
+                }
+            }
+        }
+        //TODO static and dynamic reluctant
+        m_componentManager.invokeBindMethod( this, ref );
+    }
+
     /**
      * Calls the bind method. In case there is an exception while calling the
      * bind method, the service is not considered to be bound to the instance
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java
index 1795564..41b1c24 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java
@@ -78,7 +78,6 @@
     // this is null, if none exist or none are provided
     private Dictionary m_configurationProperties;
 
-
     /**
      * The constructor receives both the activator and the metadata
      *
@@ -132,6 +131,9 @@
             m_componentContext = tmpContext;
             m_implementationObject = tmpComponent;
             log( LogService.LOG_DEBUG, "Set implementation object for component {0}", new Object[] { getName() },  null );
+
+            //notify that component was successfully created so any optional circular dependencies can be retried
+            getActivator().missingServicePresent(getServiceReference());
         }
         return true;
     }
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/CircularReferenceTest.java b/scr/src/test/java/org/apache/felix/scr/integration/CircularReferenceTest.java
index 38e4d8d..b548fc1 100644
--- a/scr/src/test/java/org/apache/felix/scr/integration/CircularReferenceTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/integration/CircularReferenceTest.java
@@ -86,7 +86,7 @@
         TestCase.assertNotNull( componentB );
         TestCase.assertEquals( Component.STATE_ACTIVE, componentB.getState() );
         B b = ( B ) componentB.getComponentInstance().getInstance();
-        assertEquals( 0, b.getAs().size() );
+        assertEquals( 1, b.getAs().size() );
     }
     /**
      * A > 1.1 > B > 0..n > A Both should start (B first), and B should have an A reference.
@@ -130,11 +130,12 @@
         Object service = bundleContext.getService( serviceReference );
         assertNotNull( service );
 
+        delay();
 
         A a = ( A ) componentA.getComponentInstance().getInstance();
         assertEquals( 1, a.getBs().size() );
         B b = ( B ) componentB.getComponentInstance().getInstance();
-        assertEquals( 0, b.getAs().size() );
+        assertEquals( 1, b.getAs().size() );
     }
     /**
      * A > 1.1 > B > 0..n > A Both should start, but B should not have an A reference.
@@ -164,11 +165,11 @@
         Object serviceA = bundleContext.getService( serviceReferenceA );
         assertNotNull( serviceA );
 
-
+        delay();
         A a = ( A ) componentA.getComponentInstance().getInstance();
         assertEquals( 1, a.getBs().size() );
         B b = ( B ) componentB.getComponentInstance().getInstance();
-        assertEquals( 0, b.getAs().size() );
+        assertEquals( 1, b.getAs().size() );
 
 
         //disabling (removing the A service registration) and re-enabling will
@@ -207,7 +208,7 @@
         TestCase.assertNotNull( componentB );
         TestCase.assertEquals( Component.STATE_ACTIVE, componentB.getState() );
         B b = ( B ) componentB.getComponentInstance().getInstance();
-        assertEquals( 0, b.getAs().size() );
+        assertEquals( 1, b.getAs().size() );
     }
     /**
      * A > 1.1 > B > 0..1 > A Both should start (B first), and B should have an A reference.