FELIX-4297 Fix deadlock when binding one reference creates another, with a test

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1536996 13f79535-47bb-0310-9956-ffa450edef68
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 a8c895e..dacad1b 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
@@ -1063,8 +1063,10 @@
         }
         catch ( ClassNotFoundException e )
         {
-            log( LogService.LOG_ERROR, "Could not load implementation object class", e );
-            throw new IllegalStateException("Could not load implementation object class");
+            log( LogService.LOG_ERROR, "Could not load implementation object class {0}", 
+                    new Object[] {getComponentMetadata().getImplementationClassName()}, e );
+            throw new IllegalStateException("Could not load implementation object class "
+                    + getComponentMetadata().getImplementationClassName());
         }
         m_componentMethods.initComponentMethods( m_componentMetadata, implementationObjectClass );
 
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 32ddf2c..99a87cf 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
@@ -1534,7 +1534,6 @@
         // null. This is valid for both immediate and delayed components
         if ( componentInstance != null )
         {
-            info.waitForOpen( m_componentManager, getName(), "invokeBindMethod" );
             synchronized ( m_tracker.tracked() )
             {
                 if (info.outOfRange( trackingCount ) )
@@ -1543,6 +1542,7 @@
                     return true;
                 }
             }
+            //edgeInfo open has been set, so binding has started.
             return doInvokeBindMethod( componentInstance, refPair );
 
         }
@@ -1585,7 +1585,6 @@
         // null. This is valid for both immediate and delayed components
         if ( componentInstance != null )
         {
-            info.waitForOpen( m_componentManager, getName(), "invokeUpdatedMethod" );
             synchronized ( m_tracker.tracked() )
             {
                 if (info.outOfRange( trackingCount ) )
@@ -1594,6 +1593,7 @@
                     return;
                 }
             }
+            info.waitForOpen( m_componentManager, getName(), "invokeUpdatedMethod" );
             if ( !getServiceObject( m_bindMethods.getUpdated(), refPair ))
             {
                 m_componentManager.log( LogService.LOG_WARNING,
@@ -1637,14 +1637,18 @@
         // null. This is valid for both immediate and delayed components
         if ( componentInstance != null )
         {
-            info.waitForOpen( m_componentManager, getName(), "invokeUnbindMethod" );
-            boolean outOfRange;
             synchronized ( m_tracker.tracked() )
             {
                 if (info.beforeRange( trackingCount ))
                 {
+                    //never bound
                     return;
                 }
+            }
+            info.waitForOpen( m_componentManager, getName(), "invokeUnbindMethod" );
+            boolean outOfRange;
+            synchronized ( m_tracker.tracked() )
+            {
                 outOfRange = info.afterRange( trackingCount );
             }
             if ( outOfRange )
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/EdgeInfo.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/EdgeInfo.java
index 66a2a62..0d33fb7 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/EdgeInfo.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/EdgeInfo.java
@@ -130,19 +130,25 @@
         closeLatch.countDown();
     }
 
+    /**
+     * Returns whether the tracking count is before the open count or after the close count (if set)
+     * This must be called from within a block synchronized on m_tracker.tracked().
+     * Setting open occurs in a synchronized block as well, to the tracker's current tracking count.
+     * Therefore if this outOfRange call finds open == -1 then open will be set to a tracking count 
+     * at least as high as the argument tracking count.
+     * @param trackingCount tracking count from tracker to compare with range
+     * @return true if open not set, tracking count before open, or close set and tracking count after close.
+     */
     public boolean outOfRange( int trackingCount )
     {
-        return (open != -1 && trackingCount < open)
-            || (close != -1 && trackingCount > close);
+        return open == -1 
+                || trackingCount < open
+                || (close != -1 && trackingCount > close);
     }
     
     public boolean beforeRange( int trackingCount )
     {
-        if (open == -1) 
-        {
-            throw new IllegalStateException("beforeRange called before open range set");
-        }
-        return trackingCount < open;
+        return open == -1 || trackingCount < open;
     }
     
     public boolean afterRange( int trackingCount )
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/CircularFactoryTest.java b/scr/src/test/java/org/apache/felix/scr/integration/CircularFactoryTest.java
new file mode 100644
index 0000000..de2875e
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/CircularFactoryTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.felix.scr.integration;
+
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.scr.Component;
+import org.apache.felix.scr.integration.components.circularFactory.FactoryClient;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.ServiceReference;
+
+@RunWith(JUnit4TestRunner.class)
+public class CircularFactoryTest extends ComponentTestBase
+{
+
+    static
+    {
+        // uncomment to enable debugging of this test class
+//        paxRunnerVmOption = DEBUG_VM_OPTION;
+
+        descriptorFile = "/integration_test_circularFactory.xml";
+        COMPONENT_PACKAGE = COMPONENT_PACKAGE + ".circularFactory";
+   }
+    
+    @Test
+    public void testCircularFactory() throws Exception
+    {
+        ServiceReference<FactoryClient> sr = bundle.getBundleContext().getServiceReference( FactoryClient.class );
+        FactoryClient fc = bundle.getBundleContext().getService( sr );
+
+        for ( String message: log.foundWarnings() )
+        {
+            TestCase.fail( "unexpected warning or error logged: " + message );
+        }
+
+    }
+
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/ComponentTestBase.java b/scr/src/test/java/org/apache/felix/scr/integration/ComponentTestBase.java
index 72c6f05..d22005f 100644
--- a/scr/src/test/java/org/apache/felix/scr/integration/ComponentTestBase.java
+++ b/scr/src/test/java/org/apache/felix/scr/integration/ComponentTestBase.java
@@ -138,6 +138,7 @@
         builder.setHeader("Export-Package", "org.apache.felix.scr.integration.components," +
                                             "org.apache.felix.scr.integration.components.activatesignature," +
                                             "org.apache.felix.scr.integration.components.circular," +
+                                            "org.apache.felix.scr.integration.components.circularFactory," +
                                             "org.apache.felix.scr.integration.components.concurrency," +
                                             "org.apache.felix.scr.integration.components.felix3680," +
                                             "org.apache.felix.scr.integration.components.felix3680_2");
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/circularFactory/FactoryClient.java b/scr/src/test/java/org/apache/felix/scr/integration/components/circularFactory/FactoryClient.java
new file mode 100644
index 0000000..b57db0b
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/circularFactory/FactoryClient.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.felix.scr.integration.components.circularFactory;
+
+import java.util.Hashtable;
+
+import org.osgi.service.component.ComponentFactory;
+
+public class FactoryClient
+{
+    
+    
+    protected void setFactory(ComponentFactory cf)
+    {
+        cf.newInstance(new Hashtable<String, Object>());
+        
+    }
+    
+    protected void unsetFactory(ComponentFactory cf)
+    {
+        
+    }
+    
+    protected void setFactoryInstance(FactoryInstance fi)
+    {
+        
+    }
+    
+    protected void unsetFactoryInstance(FactoryInstance fi)
+    {
+        
+    }
+
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/circularFactory/FactoryInstance.java b/scr/src/test/java/org/apache/felix/scr/integration/components/circularFactory/FactoryInstance.java
new file mode 100644
index 0000000..70fec40
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/circularFactory/FactoryInstance.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.felix.scr.integration.components.circularFactory;
+
+public class FactoryInstance
+{
+
+}
diff --git a/scr/src/test/resources/integration_test_circularFactory.xml b/scr/src/test/resources/integration_test_circularFactory.xml
new file mode 100644
index 0000000..02e6a63
--- /dev/null
+++ b/scr/src/test/resources/integration_test_circularFactory.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+-->
+<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+
+	<scr:component name="client" enabled="true"
+		configuration-policy="ignore">
+		<implementation
+			class="org.apache.felix.scr.integration.components.circularFactory.FactoryClient" />
+		<service>
+			<provide
+				interface="org.apache.felix.scr.integration.components.circularFactory.FactoryClient" />
+		</service>
+		<reference name="Factory"
+			interface="org.osgi.service.component.ComponentFactory" 
+			cardinality="0..1"
+			policy="dynamic" 
+			bind="setFactory" 
+			unbind="unsetFactory" />
+		<reference name="FactoryInstance"
+			interface="org.apache.felix.scr.integration.components.circularFactory.FactoryInstance"
+			cardinality="0..n" 
+			policy="dynamic" 
+			bind="setFactoryInstance" 
+			unbind="unsetFactoryInstance" />
+	</scr:component>
+	
+	<scr:component name="Factory" enabled="true"
+		configuration-policy="ignore" factory="factory">
+		<implementation
+			class="org.apache.felix.scr.integration.components.circularFactory.FactoryInstance" />
+		<service>
+			<provide
+				interface="org.apache.felix.scr.integration.components.circularFactory.FactoryInstance" />
+		</service>
+	</scr:component>
+
+
+</components>