You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/10/26 10:28:34 UTC

[camel] 03/04: CAMEL-20042: camel-spring - Autowire @Primary beans supported

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch primary-spring
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 658a3790542bffa63c45a029987f9d988c70f0c9
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Oct 26 12:20:02 2023 +0200

    CAMEL-20042: camel-spring - Autowire @Primary beans supported
---
 .../org/apache/camel/spring/primary/Customer.java  | 23 ++++++
 .../apache/camel/spring/primary/CustomerImpl.java  | 31 ++++++++
 .../camel/spring/primary/FindSingleByTypeTest.java | 83 ++++++++++++++++++++++
 .../org/apache/camel/spring/primary/MyService.java | 36 ++++++++++
 .../apache/camel/spring/primary/findBySingle.xml   | 38 ++++++++++
 .../spi/ApplicationContextBeanRepository.java      | 13 ----
 .../java/org/apache/camel/spi/BeanRepository.java  |  9 ++-
 .../org/apache/camel/support/DefaultRegistry.java  | 26 +++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_2.adoc     | 15 +++-
 9 files changed, 255 insertions(+), 19 deletions(-)

diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/Customer.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/Customer.java
new file mode 100644
index 00000000000..283f848bbbd
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/Customer.java
@@ -0,0 +1,23 @@
+/*
+ * 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.primary;
+
+public interface Customer {
+
+    String name();
+
+}
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/CustomerImpl.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/CustomerImpl.java
new file mode 100644
index 00000000000..bbe788beb8c
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/CustomerImpl.java
@@ -0,0 +1,31 @@
+/*
+ * 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.primary;
+
+public class CustomerImpl implements Customer {
+
+    private final String name;
+
+    public CustomerImpl(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String name() {
+        return name;
+    }
+}
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/FindSingleByTypeTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/FindSingleByTypeTest.java
new file mode 100644
index 00000000000..d72feb88fb0
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/FindSingleByTypeTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.primary;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.NoSuchBeanTypeException;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.spi.UuidGenerator;
+import org.apache.camel.util.IOHelper;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class FindSingleByTypeTest extends ContextTestSupport {
+
+    private AbstractApplicationContext applicationContext;
+
+    @Override
+    @BeforeEach
+    public void setUp() throws Exception {
+        super.setUp();
+        if (context != null) {
+            context.stop();
+        }
+        applicationContext = new ClassPathXmlApplicationContext("org/apache/camel/spring/primary/findBySingle.xml");
+        context = applicationContext.getBean("myCamel", ModelCamelContext.class);
+    }
+
+    @Override
+    @AfterEach
+    public void tearDown() throws Exception {
+        // we're done so let's properly close the application context
+        IOHelper.close(applicationContext);
+
+        super.tearDown();
+    }
+
+    @Test
+    public void testFindSingle() {
+        // should find primary
+        Customer c = context.getRegistry().findSingleByType(Customer.class);
+
+        Assertions.assertNotNull(c);
+        Assertions.assertEquals("Donald", c.name());
+
+        // should not find anything
+        Object o = context.getRegistry().findSingleByType(UuidGenerator.class);
+        Assertions.assertNull(o);
+    }
+
+    @Test
+    public void testFindSingleMandatory() {
+        // should find primary
+        Customer c = context.getRegistry().mandatoryFindSingleByType(Customer.class);
+        Assertions.assertEquals("Donald", c.name());
+
+        // should not find anything
+        try {
+            context.getRegistry().mandatoryFindSingleByType(UuidGenerator.class);
+            Assertions.fail("Should throw exception");
+        } catch (NoSuchBeanTypeException e) {
+            // expected
+        }
+    }
+
+}
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/MyService.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/MyService.java
new file mode 100644
index 00000000000..a458b2cca4b
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/MyService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.primary;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+@Component
+public class MyService {
+
+    @Bean
+    @Primary
+    public Customer goodCustomer() {
+        return new CustomerImpl("Donald");
+    }
+
+    @Bean
+    public Customer badCustomer() {
+        return new CustomerImpl("Jack");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/primary/findBySingle.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/primary/findBySingle.xml
new file mode 100644
index 00000000000..199b59ae39f
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/primary/findBySingle.xml
@@ -0,0 +1,38 @@
+<?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"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://camel.apache.org/schema/spring
+       http://camel.apache.org/schema/spring/camel-spring.xsd
+	http://www.springframework.org/schema/beans
+           http://www.springframework.org/schema/beans/spring-beans.xsd
+           http://www.springframework.org/schema/context
+           http://www.springframework.org/schema/context/spring-context.xsd">
+
+  <context:component-scan base-package="org.apache.camel.spring.primary"/>
+
+  <!-- empty -->
+  <camelContext id="myCamel" xmlns="http://camel.apache.org/schema/spring">
+    <jmxAgent id="jmx" disabled="true"/>
+  </camelContext>
+
+
+</beans>
diff --git a/components/camel-spring/src/main/java/org/apache/camel/spring/spi/ApplicationContextBeanRepository.java b/components/camel-spring/src/main/java/org/apache/camel/spring/spi/ApplicationContextBeanRepository.java
index f659bbcecfc..7cfd6923c15 100644
--- a/components/camel-spring/src/main/java/org/apache/camel/spring/spi/ApplicationContextBeanRepository.java
+++ b/components/camel-spring/src/main/java/org/apache/camel/spring/spi/ApplicationContextBeanRepository.java
@@ -21,12 +21,10 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.camel.NoSuchBeanException;
-import org.apache.camel.NoSuchBeanTypeException;
 import org.apache.camel.spi.BeanRepository;
 import org.springframework.beans.factory.BeanFactoryUtils;
 import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
-import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
 import org.springframework.beans.factory.config.NamedBeanHolder;
 import org.springframework.context.ApplicationContext;
 
@@ -96,15 +94,4 @@ public class ApplicationContextBeanRepository implements BeanRepository {
         }
     }
 
-    @Override
-    public <T> T mandatoryFindSingleByType(Class<T> type) {
-        try {
-            NamedBeanHolder<T> holder = applicationContext.getAutowireCapableBeanFactory().resolveNamedBean(type);
-            return holder.getBeanInstance();
-        } catch (NoUniqueBeanDefinitionException e) {
-            throw new NoSuchBeanTypeException(type, e.getNumberOfBeansFound());
-        } catch (NoSuchBeanDefinitionException e) {
-            throw new NoSuchBeanTypeException(type);
-        }
-    }
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/BeanRepository.java b/core/camel-api/src/main/java/org/apache/camel/spi/BeanRepository.java
index fb875d2812f..ecb9ab93675 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/BeanRepository.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/BeanRepository.java
@@ -88,12 +88,11 @@ public interface BeanRepository {
      *              found.
      */
     default <T> T mandatoryFindSingleByType(Class<T> type) {
-        Set<T> set = findByType(type);
-        if (set.size() == 1) {
-            return set.iterator().next();
-        } else {
-            throw new NoSuchBeanTypeException(type, set.size());
+        T answer = findSingleByType(type);
+        if (answer == null) {
+            throw new NoSuchBeanTypeException(type);
         }
+        return answer;
     }
 
     /**
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java b/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
index 0cdca505fae..dd52d5a6b07 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
@@ -342,6 +342,32 @@ public class DefaultRegistry extends ServiceSupport implements Registry, LocalBe
         return answer;
     }
 
+    @Override
+    public <T> T findSingleByType(Class<T> type) {
+        T found = null;
+
+        // local repository takes precedence
+        BeanRepository local = localRepositoryEnabled ? localRepository.get() : null;
+        if (local != null) {
+            found = local.findSingleByType(type);
+        }
+
+        if (found == null && repositories != null) {
+            for (BeanRepository r : repositories) {
+                found = r.findSingleByType(type);
+            }
+        }
+
+        if (found == null) {
+            found = supplierRegistry.findSingleByType(type);
+        }
+        if (found == null) {
+            found = fallbackRegistry.findSingleByType(type);
+        }
+
+        return found;
+    }
+
     @Override
     protected void doStop() throws Exception {
         super.doStop();
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_2.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_2.adoc
index dca28e4a9a3..40457010611 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_2.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_2.adoc
@@ -39,8 +39,21 @@ expected to function correctly if using Java 21.
 
 A new release of Apache Ignite is expected that will start to support Java 21.
 
-=== camel-spring-boot
+=== camel-spring & camel-spring-boot
+
+==== Autowiring Primary beans
+
+Camel will now take into account `@Primary` beans from Spring when autowiring by type.
+For example a JDBC `DataSource` in the SQL component will now use the `@Primary` data source
+when multiple data sources are defined.
+
+Previously Camel would not autowire if there are 2 or more beans for a given type.
+
+NOTE: This is a change in behaviour, that can affect your applications when upgrading.
+
+==== Properties
 
 The `initialProperties` and `overrideProperties` on Camel `PropertiesComponent` will now
 take precedence over Spring Boot properties. This can be used for testing purpose,
 to allow overriding properties when using `CamelTestSupport` for unit testing.
+