Repository: camel
Updated Branches:
  refs/heads/camel-2.18.x 01e2ae4db -> 04d0b6391


CAMEL-11225: Deadlock in component creation


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/ffab9916
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/ffab9916
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/ffab9916

Branch: refs/heads/camel-2.18.x
Commit: ffab991659a5983d4654638b9ce1623e8c5d91ed
Parents: 01e2ae4
Author: lburgazzoli <lburgazz...@gmail.com>
Authored: Tue May 2 15:44:48 2017 +0200
Committer: lburgazzoli <lburgazz...@gmail.com>
Committed: Tue May 9 11:44:09 2017 +0200

----------------------------------------------------------------------
 .../apache/camel/impl/DefaultCamelContext.java  | 52 ++++++++++++++++--
 .../spring/CircularComponentCreationTest.java   | 57 ++++++++++++++++++++
 .../spring/CircularComponentInjectionTest.java  | 40 ++++++++++++++
 .../CircularComponentCreationComplexTest.xml    | 35 ++++++++++++
 .../CircularComponentCreationSimpleTest.xml     | 35 ++++++++++++
 .../CircularComponentInjectionTest-context.xml  | 35 ++++++++++++
 6 files changed, 251 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/ffab9916/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java 
b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
index e8cb84c..3a206a3 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
@@ -26,6 +26,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
@@ -274,6 +275,7 @@ public class DefaultCamelContext extends ServiceSupport 
implements ModelCamelCon
     private final StopWatch stopWatch = new StopWatch(false);
     private Date startDate;
     private ModelJAXBContextFactory modelJAXBContextFactory;
+    private final ThreadLocal<Set<String>> componentsInCreation = 
ThreadLocal.withInitial(HashSet::new);
 
     /**
      * Creates the {@link CamelContext} using {@link JndiRegistry} as registry,
@@ -408,8 +410,19 @@ public class DefaultCamelContext extends ServiceSupport 
implements ModelCamelCon
     }
 
     public Component getComponent(String name, boolean autoCreateComponents, 
boolean autoStart) {
-        // atomic operation to get/create a component. Avoid global locks.
-        return components.computeIfAbsent(name, comp -> initComponent(name, 
autoCreateComponents, autoStart));
+        // Check if the named component is already being created, that would 
mean
+        // that the initComponent has triggered a new getComponent
+        if (componentsInCreation.get().contains(name)) {
+            throw new IllegalStateException("Circular dependency detected, the 
component " + name + " is already being created");
+        }
+
+        try {
+            // atomic operation to get/create a component. Avoid global locks.
+            return components.computeIfAbsent(name, comp -> 
initComponent(name, autoCreateComponents, autoStart));
+        } finally {
+            // cremove the reference to the component being created
+            componentsInCreation.get().remove(name);
+        }
     }
     
     /**
@@ -422,12 +435,45 @@ public class DefaultCamelContext extends ServiceSupport 
implements ModelCamelCon
                 if (log.isDebugEnabled()) {
                     log.debug("Using ComponentResolver: {} to resolve 
component with name: {}", getComponentResolver(), name);
                 }
+
+                // Mark the component as being created so we can detect 
circular
+                // requests.
+                //
+                // In spring apps, the component resolver may trigger a new 
getComponent
+                // because of the underlying bean factory and as the endpoints 
are
+                // registered as singleton, the spring factory creates the bean
+                // and then check the type so the getComponent is always 
triggered.
+                //
+                // Simple circular dependency:
+                //
+                //   <camelContext id="camel" 
xmlns="http://camel.apache.org/schema/spring";>
+                //     <route>
+                //       <from id="twitter" 
uri="twitter://timeline/home?type=polling"/>
+                //       <log message="Got ${body}"/>
+                //     </route>
+                //   </camelContext>
+                //
+                // Complex circular dependency:
+                //
+                //   <camelContext id="camel" 
xmlns="http://camel.apache.org/schema/spring";>
+                //     <route>
+                //       <from id="log" uri="seda:test"/>
+                //       <to id="seda" uri="log:test"/>
+                //     </route>
+                //   </camelContext>
+                //
+                // This would freeze the app (lock or infinite loop).
+                //
+                // See https://issues.apache.org/jira/browse/CAMEL-11225
+                componentsInCreation.get().add(name);
+
                 component = getComponentResolver().resolveComponent(name, 
this);
                 if (component != null) {
                     component.setCamelContext(this);
                     postInitComponent(name, component);
                     if (autoStart && (isStarted() || isStarting())) {
-                        // If the component is looked up after the context is 
started, lets start it up.
+                        // If the component is looked up after the context is 
started,
+                        // lets start it up.
                         if (component instanceof Service) {
                             startService((Service)component);
                         }

http://git-wip-us.apache.org/repos/asf/camel/blob/ffab9916/components/camel-spring/src/test/java/org/apache/camel/spring/CircularComponentCreationTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-spring/src/test/java/org/apache/camel/spring/CircularComponentCreationTest.java
 
b/components/camel-spring/src/test/java/org/apache/camel/spring/CircularComponentCreationTest.java
new file mode 100644
index 0000000..05f50ed
--- /dev/null
+++ 
b/components/camel-spring/src/test/java/org/apache/camel/spring/CircularComponentCreationTest.java
@@ -0,0 +1,57 @@
+/**
+ * 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.camel.spring;
+
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.FailedToCreateRouteException;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.util.IOHelper;
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class CircularComponentCreationTest {
+    @Test
+    public void testSimple() {
+        
doTest("org/apache/camel/spring/CircularComponentCreationSimpleTest.xml");
+    }
+
+    @Test
+    public void testComplex() {
+        
doTest("org/apache/camel/spring/CircularComponentCreationComplexTest.xml");
+    }
+
+    // *******************************
+    // Test implementation
+    // *******************************
+
+    private void doTest(String path) {
+        AbstractXmlApplicationContext applicationContext = null;
+        CamelContext camelContext = null;
+        try {
+            applicationContext = new ClassPathXmlApplicationContext(path);
+            camelContext = new SpringCamelContext(applicationContext);
+        } catch (Exception e) {
+            Assert.assertTrue(e instanceof RuntimeCamelException);
+            Assert.assertTrue(e.getCause() instanceof 
FailedToCreateRouteException);
+        } finally {
+            IOHelper.close(applicationContext);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/ffab9916/components/camel-spring/src/test/java/org/apache/camel/spring/CircularComponentInjectionTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-spring/src/test/java/org/apache/camel/spring/CircularComponentInjectionTest.java
 
b/components/camel-spring/src/test/java/org/apache/camel/spring/CircularComponentInjectionTest.java
new file mode 100644
index 0000000..92500b7
--- /dev/null
+++ 
b/components/camel-spring/src/test/java/org/apache/camel/spring/CircularComponentInjectionTest.java
@@ -0,0 +1,40 @@
+/**
+ * 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.camel.spring;
+
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.EndpointInject;
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+
+@ContextConfiguration
+public class CircularComponentInjectionTest extends SpringRunWithTestSupport {
+    @EndpointInject(ref = "seda")
+    protected Endpoint sedaEndpoint;
+    @EndpointInject(ref = "log")
+    protected Endpoint logEndpoint;
+
+    @DirtiesContext
+    @Test
+    public void test() {
+        Assert.assertNotNull(sedaEndpoint);
+        Assert.assertNotNull(logEndpoint);
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/ffab9916/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentCreationComplexTest.xml
----------------------------------------------------------------------
diff --git 
a/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentCreationComplexTest.xml
 
b/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentCreationComplexTest.xml
new file mode 100644
index 0000000..4b72c4c
--- /dev/null
+++ 
b/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentCreationComplexTest.xml
@@ -0,0 +1,35 @@
+<?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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="
+         http://www.springframework.org/schema/beans
+         http://www.springframework.org/schema/beans/spring-beans.xsd
+         http://camel.apache.org/schema/spring
+         http://camel.apache.org/schema/spring/camel-spring.xsd";>
+
+  <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring";>
+    <route>
+      <from id="log" uri="seda:test"/>
+      <to id="seda" uri="log:test"/>
+    </route>
+  </camelContext>
+
+</beans>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/ffab9916/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentCreationSimpleTest.xml
----------------------------------------------------------------------
diff --git 
a/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentCreationSimpleTest.xml
 
b/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentCreationSimpleTest.xml
new file mode 100644
index 0000000..927519e
--- /dev/null
+++ 
b/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentCreationSimpleTest.xml
@@ -0,0 +1,35 @@
+<?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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="
+         http://www.springframework.org/schema/beans
+         http://www.springframework.org/schema/beans/spring-beans.xsd
+         http://camel.apache.org/schema/spring
+         http://camel.apache.org/schema/spring/camel-spring.xsd";>
+
+  <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring";>
+    <route>
+      <from id="twitter" uri="twitter://timeline/home?type=polling"/>
+      <log message="Got ${body}"/>
+    </route>
+  </camelContext>
+
+</beans>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/ffab9916/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentInjectionTest-context.xml
----------------------------------------------------------------------
diff --git 
a/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentInjectionTest-context.xml
 
b/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentInjectionTest-context.xml
new file mode 100644
index 0000000..4b72c4c
--- /dev/null
+++ 
b/components/camel-spring/src/test/resources/org/apache/camel/spring/CircularComponentInjectionTest-context.xml
@@ -0,0 +1,35 @@
+<?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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="
+         http://www.springframework.org/schema/beans
+         http://www.springframework.org/schema/beans/spring-beans.xsd
+         http://camel.apache.org/schema/spring
+         http://camel.apache.org/schema/spring/camel-spring.xsd";>
+
+  <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring";>
+    <route>
+      <from id="log" uri="seda:test"/>
+      <to id="seda" uri="log:test"/>
+    </route>
+  </camelContext>
+
+</beans>
\ No newline at end of file

Reply via email to