This is an automated email from the ASF dual-hosted git repository.
jamesnetherton pushed a commit to branch 3.20.x
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/3.20.x by this push:
new c9674a96c6 Improve handling of findSingleByType where multiple beans
exist without any @Default qualifier
c9674a96c6 is described below
commit c9674a96c6d0284c0338b05fae60ae9fba9fecb0
Author: Zheng Feng <[email protected]>
AuthorDate: Tue May 20 14:05:29 2025 +0800
Improve handling of findSingleByType where multiple beans exist without any
@Default qualifier
Fixes #7315
Co-authored-by: James Netherton <[email protected]>
---
.../core/runtime/CamelBeanPrecedenceTest.java | 172 +++++++++++++++++++++
.../quarkus/core/runtime/CamelPrecedeBeanTest.java | 70 ---------
.../camel/quarkus/core/RuntimeBeanRepository.java | 75 +++++----
.../component/jpa/deployment/JpaProcessor.java | 52 +++++++
.../quarkus/component/jpa/it/JpaResource.java | 35 +++++
.../camel/quarkus/component/jpa/it/JpaRoute.java | 2 +
.../jpa/src/main/resources/application.properties | 33 +++-
...DefaultAndExplicitEntityManagerFactoryTest.java | 52 +++++++
...JpaMultipleNamedResourcesWithNoDefaultTest.java | 46 ++++++
.../JpaSingleNamedResourceWithNoDefaultTest.java | 45 ++++++
.../quarkus/component/sql/it/SqlDbInitializer.java | 2 +
.../quarkus/component/sql/it/SqlResource.java | 2 +
.../camel/quarkus/component/sql/it/SqlRoutes.java | 2 +
.../sql/it/datasource/SqlDataSourceResource.java | 64 ++++++++
.../sql/src/main/resources/application.properties | 19 ++-
.../SqlMultipleNamedDatasourceWithDefaultTest.java | 58 +++++++
...qlMultipleNamedDatasourceWithNoDefaultTest.java | 58 +++++++
17 files changed, 682 insertions(+), 105 deletions(-)
diff --git
a/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/runtime/CamelBeanPrecedenceTest.java
b/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/runtime/CamelBeanPrecedenceTest.java
new file mode 100644
index 0000000000..31352e76e9
--- /dev/null
+++
b/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/runtime/CamelBeanPrecedenceTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.quarkus.core.runtime;
+
+import io.quarkus.arc.DefaultBean;
+import io.quarkus.test.QuarkusUnitTest;
+import io.smallrye.common.annotation.Identifier;
+import jakarta.annotation.Priority;
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Inject;
+import org.apache.camel.CamelContext;
+import org.apache.camel.support.CamelContextHelper;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class CamelBeanPrecedenceTest {
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));
+
+ @Inject
+ CamelContext context;
+
+ @Test
+ public void findSingleByTypeForDefaultBeansWithPriority() {
+ BeanA bean = CamelContextHelper.findSingleByType(context, BeanA.class);
+ assertEquals("bar", bean.name());
+ }
+
+ @Test
+ public void findSingleByTypeForDefaultBeansWithSamePriority() {
+ BeanB bean = CamelContextHelper.findSingleByType(context, BeanB.class);
+ assertNull(bean);
+ }
+
+ @Test
+ public void findSingleByTypeForDefaultBeansWithoutPriority() {
+ BeanC bean = CamelContextHelper.findSingleByType(context, BeanC.class);
+ assertNull(bean);
+ }
+
+ @Test
+ public void findSingleByTypeForDefaultBean() {
+ BeanD bean = CamelContextHelper.findSingleByType(context, BeanD.class);
+ assertEquals("foo", bean.name());
+ }
+
+ @Test
+ public void findSingleByTypeWhereIdentifierQualifierAppliedToBean() {
+ BeanE bean = CamelContextHelper.findSingleByType(context, BeanE.class);
+ assertEquals("foo", bean.name());
+ }
+
+ @Test
+ public void findSingleByTypeWhereSingleBeanExists() {
+ BeanF bean = CamelContextHelper.findSingleByType(context, BeanF.class);
+ assertEquals("foo", bean.name());
+ }
+
+ @Test
+ public void findSingleByTypeWhereNoBeanProduced() {
+ BeanG bean = CamelContextHelper.findSingleByType(context, BeanG.class);
+ assertNull(bean);
+ }
+
+ // Default beans with priority
+ @Produces
+ @Priority(100)
+ BeanA createFooBeanA() {
+ return new BeanA("foo");
+ }
+
+ @Produces
+ @Priority(200)
+ BeanA createBarBeanA() {
+ return new BeanA("bar");
+ }
+
+ // Default beans with same priority
+ @Produces
+ @Priority(100)
+ BeanB createFooBeanB() {
+ return new BeanB("foo");
+ }
+
+ @Produces
+ @Priority(100)
+ BeanB createBarBeanB() {
+ return new BeanB("bar");
+ }
+
+ // Multiple default beans without priority
+ @Produces
+ BeanC createFooBeanC() {
+ return new BeanC("foo");
+ }
+
+ @Produces
+ BeanC createBarBeanC() {
+ return new BeanC("bar");
+ }
+
+ // Multiple beans with DefaultBean override
+ @Produces
+ BeanD createFooBeanD() {
+ return new BeanD("foo");
+ }
+
+ @Produces
+ @DefaultBean
+ BeanD createBarBeanD() {
+ return new BeanD("bar");
+ }
+
+ // Multiple beans with @Identifier qualifier
+ @Produces
+ BeanE createFooBeanE() {
+ return new BeanE("foo");
+ }
+
+ @Produces
+ @Identifier("bar")
+ BeanE createBarBeanE() {
+ return new BeanE("bar");
+ }
+
+ // Single bean
+ @Produces
+ BeanF createFooBeanF() {
+ return new BeanF("foo");
+ }
+
+ public record BeanA(String name) {
+ }
+
+ public record BeanB(String name) {
+ }
+
+ public record BeanC(String name) {
+ }
+
+ public record BeanD(String name) {
+ }
+
+ public record BeanE(String name) {
+ }
+
+ public record BeanF(String name) {
+ }
+
+ public record BeanG(String name) {
+ }
+}
diff --git
a/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/runtime/CamelPrecedeBeanTest.java
b/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/runtime/CamelPrecedeBeanTest.java
deleted file mode 100644
index 71620aa058..0000000000
---
a/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/runtime/CamelPrecedeBeanTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.quarkus.core.runtime;
-
-import io.quarkus.test.QuarkusUnitTest;
-import jakarta.annotation.Priority;
-import jakarta.enterprise.inject.Produces;
-import jakarta.inject.Inject;
-import org.apache.camel.CamelContext;
-import org.apache.camel.support.CamelContextHelper;
-import org.jboss.shrinkwrap.api.ShrinkWrap;
-import org.jboss.shrinkwrap.api.spec.JavaArchive;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-public class CamelPrecedeBeanTest {
- @RegisterExtension
- static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
- .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
- .addClasses(MyTestBean.class));
-
- @Inject
- CamelContext context;
-
- @Test
- public void testBeanPrecedence() {
- MyTestBean bean = CamelContextHelper.findSingleByType(context,
MyTestBean.class);
- assertEquals("bar", bean.getName());
- }
-
- @Produces
- @Priority(100)
- MyTestBean createFoo() {
- return new MyTestBean("foo");
- }
-
- @Produces
- @Priority(200)
- MyTestBean createBar() {
- return new MyTestBean("bar");
- }
-
- public static final class MyTestBean {
- private final String name;
-
- public MyTestBean(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
- }
-}
diff --git
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/RuntimeBeanRepository.java
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/RuntimeBeanRepository.java
index 6382cd7b0c..6eb36869b0 100644
---
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/RuntimeBeanRepository.java
+++
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/RuntimeBeanRepository.java
@@ -171,39 +171,58 @@ public final class RuntimeBeanRepository implements
BeanRepository {
public <T> T findSingleByType(Class<T> type) {
ArcContainer container = Arc.container();
Optional<Annotation[]> qualifiers = resolveQualifiersForType(type);
- if (container != null) {
- List<InstanceHandle<T>> handles;
- if (qualifiers.isPresent()) {
- handles = container.listAll(type, qualifiers.get());
- } else {
- handles = container.listAll(type);
- }
+ List<InstanceHandle<T>> handles;
+ if (qualifiers.isPresent()) {
+ handles = container.listAll(type, qualifiers.get());
+ } else {
+ handles = container.listAll(type);
+ }
+
+ if (handles.isEmpty()) {
+ // No matches for the given bean type
+ return null;
+ } else if (handles.size() == 1) {
+ // Only 1 bean exists for the given type so just return it
+ return handles.get(0).get();
+ }
+ // For multiple bean matches determine how many have the @Default
qualifier
+ long defaultBeanCount = handles.stream()
+ .map(InstanceHandle::getBean)
+ .filter(this::isDefaultBean)
+ .count();
+
+ // Determine if all beans for the matching type have the same priority
+ boolean beansHaveSamePriority = handles.stream()
+ .map(InstanceHandle::getBean)
+ .map(InjectableBean::getPriority)
+ .distinct()
+ .count() == 1;
+
+ // Try to resolve the target bean by @Priority and @Default qualifiers
+ if ((defaultBeanCount == 1) || (defaultBeanCount > 1 &&
!beansHaveSamePriority)) {
List<InstanceHandle<T>> sortedHandles = new
ArrayList<>(handles.size());
sortedHandles.addAll(handles);
-
- if (sortedHandles.size() > 1) {
- sortedHandles.sort((bean1, bean2) -> {
- Integer priority2 = bean2.getBean().getPriority();
- Integer priority1 = bean1.getBean().getPriority();
-
- int result = priority2.compareTo(priority1);
- // If the priority is same, the default bean wins
- if (result == 0) {
- if (isDefaultBean(bean1.getBean())) {
- result = -1;
- } else if (isDefaultBean(bean2.getBean())) {
- result = 1;
- }
+ sortedHandles.sort((bean1, bean2) -> {
+ Integer priority2 = bean2.getBean().getPriority();
+ Integer priority1 = bean1.getBean().getPriority();
+
+ int result = priority2.compareTo(priority1);
+ // If the priority is same, the default bean wins
+ if (result == 0) {
+ if (isDefaultBean(bean1.getBean())) {
+ result = -1;
+ } else if (isDefaultBean(bean2.getBean())) {
+ result = 1;
}
- return result;
- });
- }
-
- if (sortedHandles.size() > 0) {
- return sortedHandles.get(0).get();
- }
+ }
+ return result;
+ });
+ return sortedHandles.get(0).get();
}
+
+ // Multiple beans exist for the given type, and we could not determine
which one to use
+ // Users must resolve the conflict by explicitly referencing the bean
via endpoint URI options etc
return null;
}
diff --git
a/extensions/jpa/deployment/src/main/java/org/apache/camel/quarkus/component/jpa/deployment/JpaProcessor.java
b/extensions/jpa/deployment/src/main/java/org/apache/camel/quarkus/component/jpa/deployment/JpaProcessor.java
index 4ec977b19d..9ba2e5ce22 100644
---
a/extensions/jpa/deployment/src/main/java/org/apache/camel/quarkus/component/jpa/deployment/JpaProcessor.java
+++
b/extensions/jpa/deployment/src/main/java/org/apache/camel/quarkus/component/jpa/deployment/JpaProcessor.java
@@ -23,14 +23,20 @@ import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
+import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
+import io.quarkus.gizmo.Gizmo;
import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem;
import jakarta.persistence.EntityManagerFactory;
import org.apache.camel.component.jpa.JpaComponent;
+import org.apache.camel.component.jpa.JpaEndpoint;
import org.apache.camel.quarkus.component.jpa.CamelJpaProducer;
import org.apache.camel.quarkus.component.jpa.CamelJpaRecorder;
import
org.apache.camel.quarkus.core.deployment.spi.CamelBeanQualifierResolverBuildItem;
import org.apache.camel.quarkus.core.deployment.spi.CamelRuntimeBeanBuildItem;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
class JpaProcessor {
@@ -75,4 +81,50 @@ class JpaProcessor {
}
}
}
+
+ // TODO: Remove this and make it possible to override methods in
JpaEndpoint
+ // https://github.com/apache/camel-quarkus/issues/7369
+ @BuildStep
+ BytecodeTransformerBuildItem
transformMethodJpaEndpointCreateEntityManagerFactory() {
+ // Since spring-orm is not on the classpath, transform
JpaEndpoint.createEntityManagerFactory
+ // to avoid trying to create a local LocalEntityManagerFactoryBean and
+ // suppress ClassNotFoundException when the component could not find a
EntityManagerFactory to use.
+ //
+ // For example:
+ //
+ // protected EntityManagerFactory createEntityManagerFactory() {
+ // throw new IllegalStateException();
+ // }
+ return new BytecodeTransformerBuildItem.Builder()
+ .setClassToTransform(JpaEndpoint.class.getName())
+ .setCacheable(true)
+ .setVisitorFunction((className, classVisitor) -> {
+ return new ClassVisitor(Gizmo.ASM_API_VERSION,
classVisitor) {
+ @Override
+ public MethodVisitor visitMethod(int access, String
name, String descriptor, String signature,
+ String[] exceptions) {
+ MethodVisitor visitor = super.visitMethod(access,
name, descriptor, signature, exceptions);
+
+ if (name.equals("createEntityManagerFactory")
+ &&
descriptor.equals("()Ljakarta/persistence/EntityManagerFactory;")) {
+ return new
MethodVisitor(Gizmo.ASM_API_VERSION, visitor) {
+ @Override
+ public void visitCode() {
+ super.visitCode();
+ visitor.visitTypeInsn(Opcodes.NEW,
"java/lang/IllegalStateException");
+ visitor.visitInsn(Opcodes.DUP);
+ visitor.visitLdcInsn(
+ "Cannot create
EntityManagerFactory. Check quarkus.hibernate-orm configuration.");
+
visitor.visitMethodInsn(Opcodes.INVOKESPECIAL,
"java/lang/IllegalStateException",
+ "<init>",
"(Ljava/lang/String;)V", false);
+ visitor.visitInsn(Opcodes.ATHROW);
+ }
+ };
+ }
+ return visitor;
+ }
+ };
+ })
+ .build();
+ }
}
diff --git
a/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaResource.java
b/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaResource.java
index 4dc0779571..762934284c 100644
---
a/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaResource.java
+++
b/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaResource.java
@@ -24,7 +24,12 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import io.quarkus.arc.ClientProxy;
+import io.quarkus.arc.InjectableBean;
+import jakarta.enterprise.inject.Default;
import jakarta.inject.Inject;
+import jakarta.inject.Named;
+import jakarta.persistence.EntityManagerFactory;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
@@ -39,6 +44,7 @@ import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.jpa.JpaConstants;
+import org.apache.camel.component.jpa.JpaEndpoint;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.quarkus.component.jpa.it.model.Fruit;
@@ -134,4 +140,33 @@ public class JpaResource {
mock.reset();
return Response.ok().build();
}
+
+ @Path("/entityManagerFactory")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createJpaEndpoint() {
+ try {
+ String uri = "jpa:" + Fruit.class.getName() + "?nativeQuery=SELECT
* FROM fruits";
+
+ JpaEndpoint endpoint = context.getEndpoint(uri, JpaEndpoint.class);
+ EntityManagerFactory entityManagerFactory =
endpoint.getEntityManagerFactory();
+
+ InjectableBean<?> bean = ((ClientProxy)
entityManagerFactory).arc_bean();
+ Named namedQualifier = (Named) bean.getQualifiers()
+ .stream()
+ .filter(qualifier ->
qualifier.annotationType().equals(Named.class))
+ .findFirst()
+ .orElse(null);
+
+ String beanName = namedQualifier != null ? namedQualifier.value()
: "";
+
+ Map<String, Object> response = Map.of(
+ "name", beanName,
+ "default",
bean.getQualifiers().contains(Default.Literal.INSTANCE));
+
+ return Response.ok(response).build();
+ } catch (Exception e) {
+ return Response.serverError().entity(e.getMessage()).build();
+ }
+ }
}
diff --git
a/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaRoute.java
b/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaRoute.java
index 85b396c3af..a4964d81f2 100644
---
a/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaRoute.java
+++
b/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaRoute.java
@@ -18,6 +18,7 @@ package org.apache.camel.quarkus.component.jpa.it;
import java.util.Collections;
+import io.quarkus.arc.profile.UnlessBuildProfile;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@@ -27,6 +28,7 @@ import org.apache.camel.component.jpa.TransactionStrategy;
import org.apache.camel.processor.idempotent.jpa.JpaMessageIdRepository;
import org.apache.camel.quarkus.component.jpa.it.model.Fruit;
+@UnlessBuildProfile(anyOf = { "single-resource-no-default",
"multi-resource-no-default" })
@ApplicationScoped
public class JpaRoute extends RouteBuilder {
diff --git a/integration-tests/jpa/src/main/resources/application.properties
b/integration-tests/jpa/src/main/resources/application.properties
index 699598208b..b8a35ef880 100644
--- a/integration-tests/jpa/src/main/resources/application.properties
+++ b/integration-tests/jpa/src/main/resources/application.properties
@@ -14,11 +14,32 @@
## See the License for the specific language governing permissions and
## limitations under the License.
## ---------------------------------------------------------------------------
-quarkus.datasource.db-kind=${cq.sqlJdbcKind:h2}
-quarkus.datasource.test.db-kind=${cq.sqlJdbcKind:h2}
-quarkus.datasource.test.jdbc.max-size=8
+jpa.model.packages=org.apache.camel.quarkus.component.jpa.it.model,org.apache.camel.processor.idempotent.jpa
-quarkus.hibernate-orm.test.packages=org.apache.camel.quarkus.component.jpa.it.model,org.apache.camel.processor.idempotent.jpa
-quarkus.hibernate-orm.test.datasource=test
-quarkus.hibernate-orm.test.database.generation=drop-and-create
+%dev,test,prod.quarkus.datasource.db-kind=${cq.sqlJdbcKind:h2}
+
+%dev,test,prod.quarkus.datasource.test.db-kind=${cq.sqlJdbcKind:h2}
+%dev,test,prod.quarkus.datasource.test.jdbc.max-size=8
+
+%dev,test,prod.quarkus.hibernate-orm.test.packages=${jpa.model.packages}
+%dev,test,prod.quarkus.hibernate-orm.test.datasource=test
+%dev,test,prod.quarkus.hibernate-orm.test.database.generation=drop-and-create
+
+# single-resource-no-default profile to test single named DataSource /
EntityManagerFactory beans without defaults
+%single-resource-no-default.quarkus.camel.routes-discovery.exclude-patterns=**/*
+%single-resource-no-default.quarkus.datasource.testA.db-kind=h2
+%single-resource-no-default.quarkus.hibernate-orm.testA.packages=${jpa.model.packages}
+%single-resource-no-default.quarkus.hibernate-orm.testA.datasource=testA
+%single-resource-no-default.quarkus.hibernate-orm.testA.database.generation=drop-and-create
+
+# multi-resource-no-default profile to test multiple named DataSource /
EntityManagerFactory beans without defaults
+%multi-resource-no-default.quarkus.camel.routes-discovery.exclude-patterns=**/*
+%multi-resource-no-default.quarkus.datasource.testA.db-kind=h2
+%multi-resource-no-default.quarkus.hibernate-orm.testA.packages=${jpa.model.packages}
+%multi-resource-no-default.quarkus.hibernate-orm.testA.datasource=testA
+%multi-resource-no-default.quarkus.hibernate-orm.testA.database.generation=drop-and-create
+%multi-resource-no-default.quarkus.datasource.testB.db-kind=h2
+%multi-resource-no-default.quarkus.hibernate-orm.testB.packages=${jpa.model.packages}
+%multi-resource-no-default.quarkus.hibernate-orm.testB.datasource=testB
+%multi-resource-no-default.quarkus.hibernate-orm.testB.database.generation=drop-and-create
diff --git
a/integration-tests/jpa/src/test/java/org/apache/camel/quarkus/component/jpa/it/JpaMultipleNamedResourcesWithNoDefaultAndExplicitEntityManagerFactoryTest.java
b/integration-tests/jpa/src/test/java/org/apache/camel/quarkus/component/jpa/it/JpaMultipleNamedResourcesWithNoDefaultAndExplicitEntityManagerFactoryTest.java
new file mode 100644
index 0000000000..4e801a4388
--- /dev/null
+++
b/integration-tests/jpa/src/test/java/org/apache/camel/quarkus/component/jpa/it/JpaMultipleNamedResourcesWithNoDefaultAndExplicitEntityManagerFactoryTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.quarkus.component.jpa.it;
+
+import java.util.Map;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.Matchers.is;
+
+@QuarkusTest
+@TestProfile(JpaMultipleNamedResourcesWithNoDefaultAndExplicitEntityManagerFactoryTest.class)
+public class
JpaMultipleNamedResourcesWithNoDefaultAndExplicitEntityManagerFactoryTest
implements QuarkusTestProfile {
+ @Test
+ void
multipleNamedResourcesWithoutDefaultAutowiresChosenEntityManagerFactory() {
+ RestAssured.given()
+ .get("/jpa/entityManagerFactory")
+ .then()
+ .statusCode(200)
+ .body(
+ "name", is("testB"),
+ "default", is(false));
+ }
+
+ @Override
+ public String getConfigProfile() {
+ return "multi-resource-no-default";
+ }
+
+ @Override
+ public Map<String, String> getConfigOverrides() {
+ return Map.of("camel.component.jpa.entity-managerFactory", "#testB");
+ }
+}
diff --git
a/integration-tests/jpa/src/test/java/org/apache/camel/quarkus/component/jpa/it/JpaMultipleNamedResourcesWithNoDefaultTest.java
b/integration-tests/jpa/src/test/java/org/apache/camel/quarkus/component/jpa/it/JpaMultipleNamedResourcesWithNoDefaultTest.java
new file mode 100644
index 0000000000..50d1766b72
--- /dev/null
+++
b/integration-tests/jpa/src/test/java/org/apache/camel/quarkus/component/jpa/it/JpaMultipleNamedResourcesWithNoDefaultTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.quarkus.component.jpa.it;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.Matchers.endsWith;
+
+@QuarkusTest
+@TestProfile(JpaMultipleNamedResourcesWithNoDefaultTest.class)
+public class JpaMultipleNamedResourcesWithNoDefaultTest implements
QuarkusTestProfile {
+
+ @Test
+ void multipleNamedResourcesWithoutDefaultNotAutowirable() {
+ // There are multiple named EntityManagerFactory beans but no default
bean
+ // EntityManagerFactory autowiring should fail
+ RestAssured.given()
+ .get("/jpa/entityManagerFactory")
+ .then()
+ .statusCode(500)
+ .body(endsWith("Check quarkus.hibernate-orm configuration."));
+ }
+
+ @Override
+ public String getConfigProfile() {
+ return "multi-resource-no-default";
+ }
+}
diff --git
a/integration-tests/jpa/src/test/java/org/apache/camel/quarkus/component/jpa/it/JpaSingleNamedResourceWithNoDefaultTest.java
b/integration-tests/jpa/src/test/java/org/apache/camel/quarkus/component/jpa/it/JpaSingleNamedResourceWithNoDefaultTest.java
new file mode 100644
index 0000000000..c1ee3d8d36
--- /dev/null
+++
b/integration-tests/jpa/src/test/java/org/apache/camel/quarkus/component/jpa/it/JpaSingleNamedResourceWithNoDefaultTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.quarkus.component.jpa.it;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.Matchers.is;
+
+@QuarkusTest
+@TestProfile(JpaSingleNamedResourceWithNoDefaultTest.class)
+public class JpaSingleNamedResourceWithNoDefaultTest implements
QuarkusTestProfile {
+ @Test
+ void singleNamedResourceWithoutDefaultAutowiresEntityManagerFactory() {
+ RestAssured.given()
+ .get("/jpa/entityManagerFactory")
+ .then()
+ .statusCode(200)
+ .body(
+ "name", is("testA"),
+ "default", is(false));
+ }
+
+ @Override
+ public String getConfigProfile() {
+ return "single-resource-no-default";
+ }
+}
diff --git
a/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlDbInitializer.java
b/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlDbInitializer.java
index fa2b9c24ea..f651f1b6f7 100644
---
a/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlDbInitializer.java
+++
b/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlDbInitializer.java
@@ -25,6 +25,7 @@ import java.sql.SQLException;
import java.sql.Statement;
import io.agroal.api.AgroalDataSource;
+import io.quarkus.arc.profile.UnlessBuildProfile;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
@@ -32,6 +33,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ApplicationScoped
+@UnlessBuildProfile(anyOf = { "multi-ds-no-default", "multi-ds-with-default" })
public class SqlDbInitializer {
private static final Logger LOGGER =
LoggerFactory.getLogger(SqlDbInitializer.class);
diff --git
a/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlResource.java
b/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlResource.java
index 36849c2477..0c6af9c4f0 100644
---
a/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlResource.java
+++
b/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlResource.java
@@ -20,6 +20,7 @@ import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;
+import io.quarkus.arc.profile.UnlessBuildProfile;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@@ -42,6 +43,7 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
@Path("/sql")
@ApplicationScoped
+@UnlessBuildProfile(anyOf = { "multi-ds-no-default", "multi-ds-with-default" })
public class SqlResource {
@ConfigProperty(name = "quarkus.datasource.db-kind")
diff --git
a/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlRoutes.java
b/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlRoutes.java
index 07fa20034b..a1d2b25093 100644
---
a/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlRoutes.java
+++
b/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/SqlRoutes.java
@@ -26,6 +26,7 @@ import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import io.agroal.api.AgroalDataSource;
+import io.quarkus.arc.profile.UnlessBuildProfile;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
@@ -40,6 +41,7 @@ import
org.apache.camel.processor.idempotent.jdbc.JdbcMessageIdRepository;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.springframework.transaction.jta.JtaTransactionManager;
+@UnlessBuildProfile(anyOf = { "multi-ds-no-default", "multi-ds-with-default" })
@ApplicationScoped
public class SqlRoutes extends RouteBuilder {
diff --git
a/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/datasource/SqlDataSourceResource.java
b/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/datasource/SqlDataSourceResource.java
new file mode 100644
index 0000000000..b705c36c0f
--- /dev/null
+++
b/integration-tests/sql/src/main/java/org/apache/camel/quarkus/component/sql/it/datasource/SqlDataSourceResource.java
@@ -0,0 +1,64 @@
+/*
+ * 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.quarkus.component.sql.it.datasource;
+
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import io.quarkus.arc.ClientProxy;
+import io.quarkus.arc.InjectableBean;
+import jakarta.enterprise.inject.Default;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.sql.SqlEndpoint;
+
+@Path("/sql/datasource")
+public class SqlDataSourceResource {
+ @Inject
+ CamelContext camelContext;
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createSqlEndpoint(@QueryParam("dataSourceRef") String
dataSourceRef) {
+ try {
+ String uri = "sql:SELECT * FROM some_table";
+ if (dataSourceRef != null) {
+ uri += "?dataSource=#" + dataSourceRef;
+ }
+
+ SqlEndpoint endpoint = camelContext.getEndpoint(uri,
SqlEndpoint.class);
+ DataSource dataSource = endpoint.getDataSource();
+
+ InjectableBean<?> bean = ((ClientProxy) dataSource).arc_bean();
+ String beanName = bean.getName() == null ? "" : bean.getName();
+ Map<String, Object> response = Map.of(
+ "name", beanName,
+ "default",
bean.getQualifiers().contains(Default.Literal.INSTANCE));
+
+ return Response.ok(response).build();
+ } catch (Exception e) {
+ return Response.serverError().entity(e.getMessage()).build();
+ }
+ }
+}
diff --git a/integration-tests/sql/src/main/resources/application.properties
b/integration-tests/sql/src/main/resources/application.properties
index 146ece032b..a2364e4000 100644
--- a/integration-tests/sql/src/main/resources/application.properties
+++ b/integration-tests/sql/src/main/resources/application.properties
@@ -15,7 +15,24 @@
## limitations under the License.
## ---------------------------------------------------------------------------
-quarkus.datasource.db-kind=${cq.sqlJdbcKind:h2}
+# Default profile configuration
+%dev,test,prod.quarkus.datasource.db-kind=${cq.sqlJdbcKind:h2}
+
+# multi-ds-with-default profile to test multiple named DataSource beans with
the default DataSource
+%multi-ds-with-default.quarkus.camel.routes-discovery.exclude-patterns=**/*
+%multi-ds-with-default.quarkus.datasource.db-kind=h2
+%multi-ds-with-default.quarkus.datasource.devservices.enabled=false
+%multi-ds-with-default.quarkus.datasource.testA.db-kind=h2
+%multi-ds-with-default.quarkus.datasource.testA.devservices.enabled=false
+%multi-ds-with-default.quarkus.datasource.testB.db-kind=h2
+%multi-ds-with-default.quarkus.datasource.testB.devservices.enabled=false
+
+# multi-ds-no-default profile to test multiple named DataSource beans without
a default DataSource
+%multi-ds-no-default.quarkus.camel.routes-discovery.exclude-patterns=**/*
+%multi-ds-no-default.quarkus.datasource.testA.db-kind=h2
+%multi-ds-no-default.quarkus.datasource.testA.devservices.enabled=false
+%multi-ds-no-default.quarkus.datasource.testB.db-kind=h2
+%multi-ds-no-default.quarkus.datasource.testB.devservices.enabled=false
#
# Camel Quarkus SQL
diff --git
a/integration-tests/sql/src/test/java/org/apache/camel/quarkus/component/sql/it/SqlMultipleNamedDatasourceWithDefaultTest.java
b/integration-tests/sql/src/test/java/org/apache/camel/quarkus/component/sql/it/SqlMultipleNamedDatasourceWithDefaultTest.java
new file mode 100644
index 0000000000..929f9bd92e
--- /dev/null
+++
b/integration-tests/sql/src/test/java/org/apache/camel/quarkus/component/sql/it/SqlMultipleNamedDatasourceWithDefaultTest.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.camel.quarkus.component.sql.it;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.Matchers.blankOrNullString;
+import static org.hamcrest.Matchers.is;
+
+@QuarkusTest
+@TestProfile(SqlMultipleNamedDatasourceWithDefaultTest.class)
+public class SqlMultipleNamedDatasourceWithDefaultTest implements
QuarkusTestProfile {
+ @Test
+ void multipleNamedDataSourcesWithDefaultAutowired() {
+ RestAssured.given()
+ .get("/sql/datasource")
+ .then()
+ .statusCode(200)
+ .body(
+ "name", blankOrNullString(),
+ "default", is(true));
+ }
+
+ @Test
+ void multipleNamedDataSourcesWithExplicitLookup() {
+ RestAssured.given()
+ .queryParam("dataSourceRef", "testB")
+ .get("/sql/datasource")
+ .then()
+ .statusCode(200)
+ .body(
+ "name", is("testB"),
+ "default", is(false));
+ }
+
+ @Override
+ public String getConfigProfile() {
+ return "multi-ds-with-default";
+ }
+}
diff --git
a/integration-tests/sql/src/test/java/org/apache/camel/quarkus/component/sql/it/SqlMultipleNamedDatasourceWithNoDefaultTest.java
b/integration-tests/sql/src/test/java/org/apache/camel/quarkus/component/sql/it/SqlMultipleNamedDatasourceWithNoDefaultTest.java
new file mode 100644
index 0000000000..0c82e38a98
--- /dev/null
+++
b/integration-tests/sql/src/test/java/org/apache/camel/quarkus/component/sql/it/SqlMultipleNamedDatasourceWithNoDefaultTest.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.camel.quarkus.component.sql.it;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.is;
+
+@QuarkusTest
+@TestProfile(SqlMultipleNamedDatasourceWithNoDefaultTest.class)
+public class SqlMultipleNamedDatasourceWithNoDefaultTest implements
QuarkusTestProfile {
+ @Test
+ void multipleNamedDataSourcesWithoutDefaultNotAutowirable() {
+ // There are multiple named DataSource beans but no default bean
+ // DataSource autowiring should fail
+ RestAssured.given()
+ .get("/sql/datasource")
+ .then()
+ .statusCode(500)
+ .body(endsWith("DataSource must be configured"));
+ }
+
+ @Test
+ void multipleNamedDataSourcesWithExplicitLookup() {
+ RestAssured.given()
+ .queryParam("dataSourceRef", "testB")
+ .get("/sql/datasource")
+ .then()
+ .statusCode(200)
+ .body(
+ "name", is("testB"),
+ "default", is(false));
+ }
+
+ @Override
+ public String getConfigProfile() {
+ return "multi-ds-no-default";
+ }
+}