FELIX-3645: Tests which reproduces the "Could not obtain lock in 5000 milliseconds" Exception.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1379968 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/ComponentConcurrencyTest.java b/scr/src/test/java/org/apache/felix/scr/integration/ComponentConcurrencyTest.java
new file mode 100644
index 0000000..90667eb
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/ComponentConcurrencyTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Date;
+import java.util.Hashtable;
+
+import javax.inject.Inject;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.scr.Component;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogEntry;
+import org.osgi.service.log.LogListener;
+import org.osgi.service.log.LogReaderService;
+import org.osgi.service.log.LogService;
+
+@RunWith(JUnit4TestRunner.class)
+public class ComponentConcurrencyTest extends ComponentTestBase
+{
+    static
+    {
+        // uncomment to enable debugging of this test class
+        //  paxRunnerVmOption = DEBUG_VM_OPTION;
+        descriptorFile = "/integration_test_component_concurrency.xml";
+    }
+
+    @Inject
+    protected BundleContext bundleContext;
+
+    protected static void delay(int secs)
+    {
+        try
+        {
+            Thread.sleep(secs * 1000);
+        }
+        catch (InterruptedException ie)
+        {
+        }
+    }
+
+    // Used to ignore logs displayed by the framework from stdout.
+    // (the log service will log it because it listen to fwk error 
+    // events ...).
+    static class NullStdout extends PrintStream
+    {
+        NullStdout()
+        {
+            super(new OutputStream()
+            {
+                @Override
+                public void write(int b) throws IOException
+                {
+                }
+            });
+        }
+    }
+
+    public static class Log implements LogListener
+    {
+        private volatile boolean _foundWarnings;
+        private final static PrintStream _out =
+                new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.err), 128));
+
+        public void logged(LogEntry entry)
+        {
+            if (entry.getLevel() <= 2)
+            {
+                _foundWarnings = true;
+            }
+            StringWriter sw = new StringWriter();
+            sw.append("D=");
+            sw.append(new Date(entry.getTime()).toString());
+            sw.append(", T=" + Thread.currentThread().getName());
+            sw.append(": ");
+            sw.append(entry.getMessage());
+            if (entry.getException() != null)
+            {
+                sw.append(System.getProperty("line.separator"));
+                PrintWriter pw = new PrintWriter(sw);
+                entry.getException().printStackTrace(pw);
+            }
+            _out.println(sw.toString());
+        }
+
+        boolean foundWarnings()
+        {
+            return _foundWarnings;
+        }
+    }
+
+    @Test
+    public void test_concurrent_component_activation_using_componentFactories()
+    {
+        final PrintStream out = System.out;
+        final PrintStream err = System.err;
+        System.setOut(new NullStdout());
+        System.setErr(new NullStdout());
+        
+        try
+        {
+            Log log = new Log();
+            ServiceReference sr = bundleContext.getServiceReference(LogReaderService.class.getName());
+            TestCase.assertNotNull(sr);
+            LogReaderService logReader = (LogReaderService) bundleContext.getService(sr);
+            TestCase.assertNotNull(logReader);
+            logReader.addLogListener(log);
+
+            final Component AFactory =
+                    findComponentByName("org.apache.felix.scr.integration.components.concurrency.AFactory");
+            TestCase.assertNotNull(AFactory);
+            AFactory.enable();
+
+            final Component CFactory =
+                    findComponentByName("org.apache.felix.scr.integration.components.concurrency.CFactory");
+            TestCase.assertNotNull(CFactory);
+            CFactory.enable();
+
+            delay(30);
+            TestCase.assertFalse(log.foundWarnings());
+        }
+
+        finally
+        {
+            System.setOut(out);
+            System.setErr(err);
+        }
+    }
+}
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 d13a9ee..9facebd 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
@@ -104,7 +104,7 @@
 
     @ProbeBuilder
     public TestProbeBuilder extendProbe(TestProbeBuilder builder) {
-        builder.setHeader("Export-Package", "org.apache.felix.scr.integration.components,org.apache.felix.scr.integration.components.activatesignature,org.apache.felix.scr.integration.components.circular");
+        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.concurrency");
         builder.setHeader("Import-Package", "org.apache.felix.scr,org.apache.felix.scr.component;mandatory:=\"status\"; status=\"provisional\"");
         builder.setHeader("Bundle-ManifestVersion", "2");
         return builder;
@@ -125,7 +125,8 @@
             provision(
                 CoreOptions.bundle( bundleFile.toURI().toString() ),
                 mavenBundle( "org.ops4j.pax.tinybundles", "tinybundles", "1.0.0" ),
-                mavenBundle( "org.apache.felix", "org.apache.felix.configadmin", "1.0.10" )
+                mavenBundle( "org.apache.felix", "org.apache.felix.configadmin", "1.0.10" ),
+                mavenBundle( "org.apache.felix", "org.apache.felix.log", "1.0.1" )
              ),
              junitBundles(),
              systemProperty( "ds.factory.enabled" ).value( Boolean.toString( NONSTANDARD_COMPONENT_FACTORY_BEHAVIOR ) )
@@ -358,7 +359,7 @@
                 .set(Constants.BUNDLE_SYMBOLICNAME, "simplecomponent")
                 .set(Constants.BUNDLE_VERSION, "0.0.11")
                 .set(Constants.IMPORT_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,org.apache.felix.scr.integration.components.activatesignature,org.apache.felix.scr.integration.components.circular,org.apache.felix.scr.integration.components.concurrency")
                 .set("Service-Component", "OSGI-INF/components.xml")
             .build(withBnd());
 
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/A.java b/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/A.java
new file mode 100644
index 0000000..c582a31
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/A.java
@@ -0,0 +1,22 @@
+/*
+ * 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.concurrency;
+
+public class A
+{
+    void bindB(B b)
+    {
+    }
+
+    void start()
+    {
+    }
+}
\ No newline at end of file
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/AFactory.java b/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/AFactory.java
new file mode 100644
index 0000000..2eb7ff5
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/AFactory.java
@@ -0,0 +1,60 @@
+/*
+ * 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.concurrency;
+
+import org.osgi.service.component.ComponentFactory;
+import org.osgi.service.component.ComponentInstance;
+
+public class AFactory implements Runnable {
+    private ComponentFactory _aFactory;
+    private Thread[] _threads = new Thread[1];
+    
+    public void bindAFactory(ComponentFactory aFactory) {
+      _aFactory = aFactory;
+    }
+    
+    void activate() {
+      System.out.println("AFactory started");
+      for (int i = 0; i < _threads.length; i++) {
+        _threads[i] = new Thread(this);
+        _threads[i].start();
+      }
+    }
+    
+    void deactivate() {
+      System.out.println("AFactory stopped");
+      for (int i = 0; i < _threads.length; i++) {
+        _threads[i].interrupt();
+//        try {
+//          _threads[i].join();
+//        } catch (InterruptedException e) {
+//        }
+      }
+    }
+    
+    public void run() {
+      while (true) {
+        try {
+          //System.out.println("Creating A");
+          ComponentInstance ci = _aFactory.newInstance(null);
+          ci.dispose();
+          if (Thread.currentThread().isInterrupted()) {
+            return;
+          }
+        } catch (Throwable t) {
+          if (!(t instanceof InterruptedException)) {
+            //System.out.println("AFactory thread exiting: got exception: " + t.toString());
+          }
+          return;
+        }
+      }
+    }
+  }
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/B.java b/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/B.java
new file mode 100644
index 0000000..4b0833e
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/B.java
@@ -0,0 +1,18 @@
+/*
+ * 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.concurrency;
+
+public class B
+{
+    void bindC(C c)
+    {
+    }
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/C.java b/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/C.java
new file mode 100644
index 0000000..1a11206
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/C.java
@@ -0,0 +1,16 @@
+/*
+ * 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.concurrency;
+
+public class C
+{
+
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/CFactory.java b/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/CFactory.java
new file mode 100644
index 0000000..98a8a1d
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/concurrency/CFactory.java
@@ -0,0 +1,59 @@
+/*
+ * 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.concurrency;
+
+import org.osgi.service.component.ComponentFactory;
+import org.osgi.service.component.ComponentInstance;
+
+public class CFactory implements Runnable {
+    private ComponentFactory _cFactory;
+    private Thread[] _threads = new Thread[1];
+    
+    public void bindCFactory(ComponentFactory cFactory) {
+      _cFactory = cFactory;
+    }
+    
+    void activate() {
+      System.out.println("CFactory started");
+      for (int i = 0; i < _threads.length; i++) {
+        _threads[i] = new Thread(this);
+        _threads[i].start();
+      }
+    }
+    
+    void deactivate() {
+      System.out.println("CFactory stopped");
+      for (int i = 0; i < _threads.length; i++) {
+        _threads[i].interrupt();
+        try {
+          _threads[i].join();
+        } catch (InterruptedException e) {
+        }
+      }
+    }
+    
+    public void run() {
+      while (true) {
+        try {
+          ComponentInstance ci = _cFactory.newInstance(null);
+          ci.dispose();
+          if (Thread.currentThread().isInterrupted()) {
+            return;
+          }
+        } catch (Throwable t) {
+          if (!(t instanceof InterruptedException)) {
+            //System.out.println("CFactory thread exiting: got exception: " + t.toString());
+          }
+          return;
+        }
+      }
+    }
+  }
diff --git a/scr/src/test/resources/integration_test_component_concurrency.xml b/scr/src/test/resources/integration_test_component_concurrency.xml
new file mode 100644
index 0000000..aeb7c7f
--- /dev/null
+++ b/scr/src/test/resources/integration_test_component_concurrency.xml
@@ -0,0 +1,64 @@
+<?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='org.apache.felix.scr.integration.components.concurrency.A'
+		factory='AFactory' activate='start'>
+		<implementation
+			class='org.apache.felix.scr.integration.components.concurrency.A' />
+		<reference name='b'
+			interface='org.apache.felix.scr.integration.components.concurrency.B'
+			bind='bindB' cardinality='0..n' policy='dynamic' />
+	</scr:component>
+
+	<scr:component
+		name='org.apache.felix.scr.integration.components.concurrency.B'>
+		<implementation
+			class='org.apache.felix.scr.integration.components.concurrency.B' />
+		<service>
+			<provide interface='org.apache.felix.scr.integration.components.concurrency.B' />
+		</service>
+		<reference name='c'
+			interface='org.apache.felix.scr.integration.components.concurrency.C'
+			bind='bindC' />
+	</scr:component>
+
+	<scr:component
+		name='org.apache.felix.scr.integration.components.concurrency.C'
+		factory='CFactory'>
+		<implementation
+			class='org.apache.felix.scr.integration.components.concurrency.C' />
+		<service>
+			<provide interface='org.apache.felix.scr.integration.components.concurrency.C' />
+		</service>
+	</scr:component>
+
+	<scr:component enabled='false'
+		name='org.apache.felix.scr.integration.components.concurrency.AFactory'
+		activate='activate' deactivate='deactivate'>
+		<implementation
+			class='org.apache.felix.scr.integration.components.concurrency.AFactory' />
+		<reference name='aFactory'
+			interface='org.osgi.service.component.ComponentFactory' bind='bindAFactory'
+			target='(component.factory=AFactory)' />
+	</scr:component>
+
+	<scr:component enabled='false'
+		name='org.apache.felix.scr.integration.components.concurrency.CFactory'
+		activate='activate' deactivate='deactivate'>
+		<implementation
+			class='org.apache.felix.scr.integration.components.concurrency.CFactory' />
+		<reference name='cFactory'
+			interface='org.osgi.service.component.ComponentFactory' bind='bindCFactory'
+			target='(component.factory=CFactory)' />
+	</scr:component>
+</components>