This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 68ea079 Camel 17571 (#7200)
68ea079 is described below
commit 68ea0794002beadbc63482455039004286ef2b7d
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Mar 14 15:51:17 2022 +0100
Camel 17571 (#7200)
* CAMEL-17571: camel-dsl - Allow to register custom annotation processors
that can do custom logic after a DSL has compiled source into Java object.
* CAMEL-17571: camel-jbang - Support for spring @Component/@Service
annotations in custom beans
* CAMEL-17571: camel-dsl - Allow to register custom annotation processors
that can do custom logic after a DSL has compiled source into Java object.
* CAMEL-17571: camel-dsl - Allow to register custom annotation processors
that can do custom logic after a DSL has compiled source into Java object.
* CAMEL-17571: camel-jbang - Support for spring @Autowired/@Value
annotations in custom beans
* CAMEL-17571: camel-jbang - Support for spring @Autowired/@Value
annotations in custom beans
* CAMEL-17571: camel-jbang - Support for spring @Bean annotations in custom
beans
* CAMEL-17571: camel-jbang - Support for spring @Bean annotations in custom
beans
* CAMEL-17571: camel-dsl - Allow to register custom annotation processors
that can do custom logic after a DSL has compiled source into Java object.
* CAMEL-17571: camel-dsl - Allow to register custom annotation processors
that can do custom logic after a DSL has compiled source into Java object.
* camel-kamelet-main - Cleanup code
* CAMEL-17571: camel-jbang - Support for quarkus/cdi annotations in custom
beans
* CAMEL-17571: camel-jbang - Support for quarkus/cdi annotations in custom
beans
* CAMEL-17571: camel-jbang - Support for quarkus/cdi annotations in custom
beans
* CAMEL-17571: camel-jbang - docs
* CAMEL-17571: camel-dsl - Allow to register custom annotation processors
that can do custom logic after a DSL has compiled source into Java object.
* CAMEL-17571: camel-dsl - Allow to register custom annotation processors
that can do custom logic after a DSL has compiled source into Java object.
* CAMEL-17571: camel-dsl - Allow to register custom annotation processors
that can do custom logic after a DSL has compiled source into Java object.
* CAMEL-17571: Polished
* CAMEL-17571: Review
* Update docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
Co-authored-by: Nicolas Filotto <[email protected]>
Co-authored-by: Nicolas Filotto <[email protected]>
---
camel-dependencies/pom.xml | 1 +
.../apache/camel/spi/CamelBeanPostProcessor.java | 9 +
.../camel/spi/CamelBeanPostProcessorInjector.java | 46 ++++
.../impl/engine/CamelPostProcessorHelper.java | 90 ++++++-
.../impl/engine/DefaultCamelBeanPostProcessor.java | 102 ++------
.../impl/CamelBeanPostProcessorInjectorTest.java | 101 ++++++++
.../modules/ROOT/pages/camel-jbang.adoc | 34 +++
.../camel/dsl/support/CompilePostProcessor.java | 44 ++++
.../dsl/support/RouteBuilderLoaderSupport.java | 36 ++-
.../dsl/java/joor/JavaRoutesBuilderLoader.java | 57 +---
dsl/camel-kamelet-main/pom.xml | 32 +++
.../camel/main/AnnotationDependencyInjection.java | 286 +++++++++++++++++++++
.../camel/main/DependencyDownloaderKamelet.java | 111 +++++++-
.../java/org/apache/camel/main/KameletMain.java | 3 +
.../org/apache/camel/main/KameletYamlRoutes.java | 130 ----------
parent/pom.xml | 1 +
16 files changed, 819 insertions(+), 264 deletions(-)
diff --git a/camel-dependencies/pom.xml b/camel-dependencies/pom.xml
index b676108..fcb98e3 100644
--- a/camel-dependencies/pom.xml
+++ b/camel-dependencies/pom.xml
@@ -287,6 +287,7 @@
<jackson2-version>2.13.2</jackson2-version>
<jain-sip-ri-bundle-version>1.2.154_2</jain-sip-ri-bundle-version>
<jakarta-api-version>2.1.5</jakarta-api-version>
+ <jakarta-cdi-api-version>2.0.2</jakarta-cdi-api-version>
<jakarta-jaxb-version>2.3.3</jakarta-jaxb-version>
<jakarta-mail-version>1.6.6</jakarta-mail-version>
<jakarta.el-version>3.0.3</jakarta.el-version>
diff --git
a/core/camel-api/src/main/java/org/apache/camel/spi/CamelBeanPostProcessor.java
b/core/camel-api/src/main/java/org/apache/camel/spi/CamelBeanPostProcessor.java
index 0308f54..1fff611 100644
---
a/core/camel-api/src/main/java/org/apache/camel/spi/CamelBeanPostProcessor.java
+++
b/core/camel-api/src/main/java/org/apache/camel/spi/CamelBeanPostProcessor.java
@@ -83,4 +83,13 @@ public interface CamelBeanPostProcessor {
return false;
}
+ /**
+ * Adds a custom bean post injector
+ *
+ * @param injector the custom injector
+ */
+ default void
addCamelBeanPostProjectInjector(CamelBeanPostProcessorInjector injector) {
+ // noop
+ }
+
}
diff --git
a/core/camel-api/src/main/java/org/apache/camel/spi/CamelBeanPostProcessorInjector.java
b/core/camel-api/src/main/java/org/apache/camel/spi/CamelBeanPostProcessorInjector.java
new file mode 100644
index 0000000..6c70ab2
--- /dev/null
+++
b/core/camel-api/src/main/java/org/apache/camel/spi/CamelBeanPostProcessorInjector.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.spi;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * Used for custom injection when doing {@link CamelBeanPostProcessor} bean
post-processing. Can be used to support
+ * 3rd-party annotations for dependency injections.
+ */
+public interface CamelBeanPostProcessorInjector {
+
+ /**
+ * Field injection
+ *
+ * @param field the field
+ * @param bean the bean instance where the field is present
+ * @param beanName optional bean id of the bean
+ */
+ void onFieldInject(Field field, Object bean, String beanName);
+
+ /**
+ * Method injection
+ *
+ * @param method the method
+ * @param bean the bean instance where the method is present
+ * @param beanName optional bean id of the bean
+ */
+ void onMethodInject(Method method, Object bean, String beanName);
+
+}
diff --git
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelPostProcessorHelper.java
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelPostProcessorHelper.java
index 04b4f95..533fb49 100644
---
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelPostProcessorHelper.java
+++
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelPostProcessorHelper.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.impl.engine;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Locale;
@@ -23,6 +24,8 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
+import org.apache.camel.BeanConfigInject;
+import org.apache.camel.BeanInject;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.Consume;
@@ -38,12 +41,15 @@ import org.apache.camel.NoSuchBeanException;
import org.apache.camel.PollingConsumer;
import org.apache.camel.Producer;
import org.apache.camel.ProducerTemplate;
+import org.apache.camel.PropertyInject;
import org.apache.camel.ProxyInstantiationException;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.Service;
+import org.apache.camel.TypeConverter;
import org.apache.camel.spi.BeanProxyFactory;
import org.apache.camel.spi.PropertiesComponent;
import org.apache.camel.spi.PropertyConfigurer;
+import org.apache.camel.spi.Registry;
import org.apache.camel.support.CamelContextHelper;
import org.apache.camel.support.PropertyBindingSupport;
import org.apache.camel.support.service.ServiceHelper;
@@ -51,8 +57,10 @@ import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.apache.camel.support.ObjectHelper.invokeMethod;
+
/**
- * A helper class for Camel based injector or bean post processing hooks.
+ * A helper class for Camel based injector or bean post-processing hooks.
*/
public class CamelPostProcessorHelper implements CamelContextAware {
@@ -402,6 +410,86 @@ public class CamelPostProcessorHelper implements
CamelContextAware {
return bean;
}
+ public Object getInjectionBeanMethodValue(
+ CamelContext context,
+ Method method, Object bean, String beanName) {
+ Class<?> returnType = method.getReturnType();
+ if (returnType == Void.TYPE) {
+ throw new IllegalArgumentException(
+ "@BindToRegistry on class: " + method.getDeclaringClass()
+ + " method: " +
method.getName() + " with void return type is not allowed");
+ }
+
+ Object value;
+ Object[] parameters = bindToRegistryParameterMapping(context, method);
+ if (parameters != null) {
+ value = invokeMethod(method, bean, parameters);
+ } else {
+ value = invokeMethod(method, bean);
+ }
+ return value;
+ }
+
+ private Object[] bindToRegistryParameterMapping(CamelContext context,
Method method) {
+ if (method.getParameterCount() == 0) {
+ return null;
+ }
+
+ // map each parameter if possible
+ Object[] parameters = new Object[method.getParameterCount()];
+ for (int i = 0; i < method.getParameterCount(); i++) {
+ Class<?> type = method.getParameterTypes()[i];
+ if (type.isAssignableFrom(CamelContext.class)) {
+ parameters[i] = context;
+ } else if (type.isAssignableFrom(Registry.class)) {
+ parameters[i] = context.getRegistry();
+ } else if (type.isAssignableFrom(TypeConverter.class)) {
+ parameters[i] = context.getTypeConverter();
+ } else {
+ // we also support @BeanInject and @PropertyInject annotations
+ Annotation[] anns = method.getParameterAnnotations()[i];
+ if (anns.length == 1) {
+ // we dont assume there are multiple annotations on the
same parameter so grab first
+ Annotation ann = anns[0];
+ if (ann.annotationType() == PropertyInject.class) {
+ PropertyInject pi = (PropertyInject) ann;
+ Object result = getInjectionPropertyValue(type,
pi.value(), pi.defaultValue(),
+ null, null, null);
+ parameters[i] = result;
+ } else if (ann.annotationType() == BeanConfigInject.class)
{
+ BeanConfigInject pi = (BeanConfigInject) ann;
+ Object result = getInjectionBeanConfigValue(type,
pi.value());
+ parameters[i] = result;
+ } else if (ann.annotationType() == BeanInject.class) {
+ BeanInject bi = (BeanInject) ann;
+ Object result = getInjectionBeanValue(type,
bi.value());
+ parameters[i] = result;
+ }
+ } else {
+ // okay attempt to default to singleton instances from the
registry
+ Set<?> instances = context.getRegistry().findByType(type);
+ if (instances.size() == 1) {
+ parameters[i] = instances.iterator().next();
+ } else if (instances.size() > 1) {
+ // there are multiple instances of the same type, so
barf
+ throw new IllegalArgumentException(
+ "Multiple beans of the same type: " + type
+ + " exists in the
Camel registry. Specify the bean name on @BeanInject to bind to a single bean,
at the method: "
+ + method);
+ }
+ }
+ }
+
+ // each parameter must be mapped
+ if (parameters[i] == null) {
+ int pos = i + 1;
+ throw new IllegalArgumentException("@BindToProperty cannot
bind parameter #" + pos + " on method: " + method);
+ }
+ }
+
+ return parameters;
+ }
+
/**
* Factory method to create a {@link org.apache.camel.ProducerTemplate} to
be injected into a POJO
*/
diff --git
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
index 3a74646..6293b86 100644
---
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
+++
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
@@ -16,13 +16,11 @@
*/
package org.apache.camel.impl.engine;
-import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
-import java.util.Set;
import java.util.function.Function;
import org.apache.camel.BeanConfigInject;
@@ -35,9 +33,8 @@ import org.apache.camel.EndpointInject;
import org.apache.camel.ExtendedCamelContext;
import org.apache.camel.Produce;
import org.apache.camel.PropertyInject;
-import org.apache.camel.TypeConverter;
import org.apache.camel.spi.CamelBeanPostProcessor;
-import org.apache.camel.spi.Registry;
+import org.apache.camel.spi.CamelBeanPostProcessorInjector;
import org.apache.camel.support.DefaultEndpoint;
import org.apache.camel.util.ReflectionHelper;
import org.slf4j.Logger;
@@ -65,6 +62,7 @@ import static org.apache.camel.util.ObjectHelper.isEmpty;
public class DefaultCamelBeanPostProcessor implements CamelBeanPostProcessor,
CamelContextAware {
protected static final Logger LOG =
LoggerFactory.getLogger(DefaultCamelBeanPostProcessor.class);
+ protected final List<CamelBeanPostProcessorInjector>
beanPostProcessorInjectors = new ArrayList<>();
protected CamelPostProcessorHelper camelPostProcessorHelper;
protected CamelContext camelContext;
protected boolean enabled = true;
@@ -106,6 +104,11 @@ public class DefaultCamelBeanPostProcessor implements
CamelBeanPostProcessor, Ca
}
@Override
+ public void addCamelBeanPostProjectInjector(CamelBeanPostProcessorInjector
injector) {
+ this.beanPostProcessorInjectors.add(injector);
+ }
+
+ @Override
public Object postProcessBeforeInitialization(Object bean, String
beanName) throws Exception {
LOG.trace("Camel bean processing before initialization for bean: {}",
beanName);
@@ -268,6 +271,11 @@ public class DefaultCamelBeanPostProcessor implements
CamelBeanPostProcessor, Ca
String uri = produce.value().isEmpty() ? produce.uri() :
produce.value();
injectField(field, uri, produce.property(), bean, beanName,
produce.binding());
}
+
+ // custom bean injector on the field
+ for (CamelBeanPostProcessorInjector injector :
beanPostProcessorInjectors) {
+ injector.onFieldInject(field, bean, beanName);
+ }
});
}
@@ -323,6 +331,11 @@ public class DefaultCamelBeanPostProcessor implements
CamelBeanPostProcessor, Ca
setterInjection(method, bean, beanName);
getPostProcessorHelper().consumerInjection(method, bean, beanName);
+
+ // custom bean injector on the method
+ for (CamelBeanPostProcessorInjector injector :
beanPostProcessorInjectors) {
+ injector.onMethodInject(method, bean, beanName);
+ }
});
}
@@ -498,12 +511,11 @@ public class DefaultCamelBeanPostProcessor implements
CamelBeanPostProcessor, Ca
}
Object value = ReflectionHelper.getField(field, bean);
- // use dependency injection factory to perform the task of binding the
bean to registry
if (value != null) {
-
if (unbindEnabled) {
getOrLookupCamelContext().getRegistry().unbind(name);
}
+ // use dependency injection factory to perform the task of binding
the bean to registry
Runnable task =
getOrLookupCamelContext().adapt(ExtendedCamelContext.class)
.getDependencyInjectionAnnotationFactory()
.createBindToRegistryFactory(name, value, beanName,
beanPostProcess);
@@ -515,26 +527,14 @@ public class DefaultCamelBeanPostProcessor implements
CamelBeanPostProcessor, Ca
if (isEmpty(name)) {
name = method.getName();
}
- Class<?> returnType = method.getReturnType();
- if (returnType == null || returnType == Void.TYPE) {
- throw new IllegalArgumentException(
- "@BindToRegistry on class: " + method.getDeclaringClass()
- + " method: " +
method.getName() + " with void return type is not allowed");
- }
+ Object value = getPostProcessorHelper()
+ .getInjectionBeanMethodValue(getOrLookupCamelContext(),
method, bean, beanName);
- Object value;
- Object[] parameters = bindToRegistryParameterMapping(method);
- if (parameters != null) {
- value = invokeMethod(method, bean, parameters);
- } else {
- value = invokeMethod(method, bean);
- }
- // use dependency injection factory to perform the task of binding the
bean to registry
if (value != null) {
-
if (unbindEnabled) {
getOrLookupCamelContext().getRegistry().unbind(name);
}
+ // use dependency injection factory to perform the task of binding
the bean to registry
Runnable task =
getOrLookupCamelContext().adapt(ExtendedCamelContext.class)
.getDependencyInjectionAnnotationFactory()
.createBindToRegistryFactory(name, value, beanName,
beanPostProcess);
@@ -542,66 +542,6 @@ public class DefaultCamelBeanPostProcessor implements
CamelBeanPostProcessor, Ca
}
}
- private Object[] bindToRegistryParameterMapping(Method method) {
- if (method.getParameterCount() == 0) {
- return null;
- }
-
- // map each parameter if possible
- Object[] parameters = new Object[method.getParameterCount()];
- for (int i = 0; i < method.getParameterCount(); i++) {
- Class<?> type = method.getParameterTypes()[i];
- if (type.isAssignableFrom(CamelContext.class)) {
- parameters[i] = getOrLookupCamelContext();
- } else if (type.isAssignableFrom(Registry.class)) {
- parameters[i] = getOrLookupCamelContext().getRegistry();
- } else if (type.isAssignableFrom(TypeConverter.class)) {
- parameters[i] = getOrLookupCamelContext().getTypeConverter();
- } else {
- // we also support @BeanInject and @PropertyInject annotations
- Annotation[] anns = method.getParameterAnnotations()[i];
- if (anns.length == 1) {
- // we dont assume there are multiple annotations on the
same parameter so grab first
- Annotation ann = anns[0];
- if (ann.annotationType() == PropertyInject.class) {
- PropertyInject pi = (PropertyInject) ann;
- Object result =
getPostProcessorHelper().getInjectionPropertyValue(type, pi.value(),
pi.defaultValue(),
- null, null, null);
- parameters[i] = result;
- } else if (ann.annotationType() == BeanConfigInject.class)
{
- BeanConfigInject pi = (BeanConfigInject) ann;
- Object result =
getPostProcessorHelper().getInjectionBeanConfigValue(type, pi.value());
- parameters[i] = result;
- } else if (ann.annotationType() == BeanInject.class) {
- BeanInject bi = (BeanInject) ann;
- Object result =
getPostProcessorHelper().getInjectionBeanValue(type, bi.value());
- parameters[i] = result;
- }
- } else {
- // okay attempt to default to singleton instances from the
registry
- Set<?> instances =
getOrLookupCamelContext().getRegistry().findByType(type);
- if (instances.size() == 1) {
- parameters[i] = instances.iterator().next();
- } else if (instances.size() > 1) {
- // there are multiple instances of the same type, so
barf
- throw new IllegalArgumentException(
- "Multiple beans of the same type: " + type
- + " exists in the
Camel registry. Specify the bean name on @BeanInject to bind to a single bean,
at the method: "
- + method);
- }
- }
- }
-
- // each parameter must be mapped
- if (parameters[i] == null) {
- int pos = i + 1;
- throw new IllegalArgumentException("@BindToProperty cannot
bind parameter #" + pos + " on method: " + method);
- }
- }
-
- return parameters;
- }
-
private static boolean isComplexUserType(Class type) {
// lets consider all non java, as complex types
return type != null && !type.isPrimitive() &&
!type.getName().startsWith("java.");
diff --git
a/core/camel-core/src/test/java/org/apache/camel/impl/CamelBeanPostProcessorInjectorTest.java
b/core/camel-core/src/test/java/org/apache/camel/impl/CamelBeanPostProcessorInjectorTest.java
new file mode 100644
index 0000000..dc9793a
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/impl/CamelBeanPostProcessorInjectorTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.impl;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.PropertyInject;
+import org.apache.camel.impl.engine.CamelPostProcessorHelper;
+import org.apache.camel.spi.CamelBeanPostProcessor;
+import org.apache.camel.spi.CamelBeanPostProcessorInjector;
+import org.apache.camel.spi.CamelLogger;
+import org.apache.camel.support.ObjectHelper;
+import org.apache.camel.util.ReflectionHelper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class CamelBeanPostProcessorInjectorTest extends ContextTestSupport {
+
+ private CamelBeanPostProcessor postProcessor;
+ private CamelPostProcessorHelper helper;
+
+ @Override
+ @BeforeEach
+ public void setUp() throws Exception {
+ super.setUp();
+ postProcessor =
context.adapt(ExtendedCamelContext.class).getBeanPostProcessor();
+ postProcessor.addCamelBeanPostProjectInjector(new MyInjector());
+ helper = new CamelPostProcessorHelper(context);
+ }
+
+ private class MyInjector implements CamelBeanPostProcessorInjector {
+
+ @Override
+ public void onFieldInject(Field field, Object bean, String beanName) {
+ if (field.getName().equals("foo")) {
+ ReflectionHelper.setField(field, bean, "changed-foo");
+ }
+ }
+
+ @Override
+ public void onMethodInject(Method method, Object bean, String
beanName) {
+ if (method.getName().equals("createLogger")) {
+ Object out = ObjectHelper.invokeMethod(method, bean,
"changed-bar");
+ context.getRegistry().bind(method.getName(), out);
+ }
+ }
+ }
+
+ public class MyService {
+
+ @PropertyInject(value = "myName", defaultValue = "Donald Duck")
+ private String name;
+ @PropertyInject(value = "myFoo", defaultValue = "myDefault")
+ private String foo;
+
+ public String getName() {
+ return name;
+ }
+
+ public String getFoo() {
+ return foo;
+ }
+
+ public CamelLogger createLogger(String name) {
+ return new CamelLogger(name);
+ }
+ }
+
+ @Test
+ public void testBeanPostInjector() throws Exception {
+ MyService service = new MyService();
+
+ postProcessor.postProcessBeforeInitialization(service, "service");
+ postProcessor.postProcessAfterInitialization(service, "service");
+
+ Assertions.assertEquals("Donald Duck", service.getName());
+ Assertions.assertEquals("changed-foo", service.getFoo());
+
+ CamelLogger logger = (CamelLogger)
context.getRegistry().lookupByName("createLogger");
+ Assertions.assertNotNull(logger);
+ }
+
+}
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index 4a58944..ba8a54f 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -238,6 +238,40 @@ curl http://localhost:8080/hello
Hello World%
----
+=== Dependency Injection in Java classes
+
+When running Camel applications with camel-jbang, then the runtime is
`camel-main` based. This means
+there is no Spring Boot, or Quarkus available. However, we have added support
for using annotation
+based dependency injection in Java classes.
+
+==== Using Camel dependency injection
+
+You can use the following Camel annotations (they work on all runtimes):
+
+- `@BindToRegistry` on class level to create an instance of the class and
register in the xref:registry.adoc[Registry].
+- `@BeanInject` to dependency inject a bean on a class field.
+- `@PropertyInject` to inject a xref:using-propertyplaceholder.adoc[property
placeholder]. Such as a property defined in `application.properties`.
+- `@BindToRegistry` on a method to create a bean by invoking the method.
+- `@Converter` on class level to auto-register the
xref:type-converter.adoc[type converters] from the class.
+
+==== Using Spring Boot dependency injection
+
+You can use the following Spring Boot annotations:
+
+- `@Component` or `@Service` on class level to create an instance of the class
and register in the xref:registry.adoc[Registry].
+- `@Autowired` to dependency inject a bean on a class field. `@Qualifier` can
be used to specify the bean id.
+- `@Value` to inject a xref:using-propertyplaceholder.adoc[property
placeholder]. Such as a property defined in `application.properties`.
+- `@Bean` on a method to create a bean by invoking the method.
+
+==== Using Quarkus injection
+
+You can use the following Quarkus annotations:
+
+- `@ApplicationScoped` or `@Singleton` on class level to create an instance of
the class and register in the xref:registry.adoc[Registry]. `@Named` can be
used to specify the bean id.
+- `@Inject` to dependency inject an bean on a class field. `@Named` can be
used to specify the bean id.
+- `@ConfigProperty` to inject a xref:using-propertyplaceholder.adoc[property
placeholder]. Such as a property defined in `application.properties`.
+- `@Produces` on a method to create a bean by invoking the method. `@Named`
can be used to specify the bean id.
+
=== Debugging
You can debug both camel@apache/camel and your integration scripts by making
use of the `--debug` flag provided by JBang:
diff --git
a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/CompilePostProcessor.java
b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/CompilePostProcessor.java
new file mode 100644
index 0000000..021a97f
--- /dev/null
+++
b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/CompilePostProcessor.java
@@ -0,0 +1,44 @@
+/*
+ * 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.dsl.support;
+
+import org.apache.camel.CamelContext;
+
+/**
+ * Allows to plugin custom post processors that are processed after the DSL
has loaded the source and compiled into a
+ * Java object.
+ * <p/>
+ * This is used to detect and handle {@link org.apache.camel.BindToRegistry}
and {@link org.apache.camel.Converter}
+ * classes.
+ */
+public interface CompilePostProcessor {
+
+ /**
+ * Invoked after the class has been compiled
+ *
+ * @param camelContext the camel context
+ * @param name the name of the resource/class
+ * @param clazz the class
+ * @param instance the object created as instance of the class (if
any)
+ * @throws Exception is thrown if error during post-processing
+ */
+ void postCompile(
+ CamelContext camelContext, String name,
+ Class<?> clazz, Object instance)
+ throws Exception;
+
+}
diff --git
a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
index 11fbc8f..c961867 100644
---
a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
+++
b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
@@ -16,6 +16,10 @@
*/
package org.apache.camel.dsl.support;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
import org.apache.camel.CamelContextAware;
import org.apache.camel.ExtendedCamelContext;
import org.apache.camel.RoutesBuilder;
@@ -33,7 +37,7 @@ import org.apache.camel.support.RoutesBuilderLoaderSupport;
*/
public abstract class RouteBuilderLoaderSupport extends
RoutesBuilderLoaderSupport {
private final String extension;
-
+ private final List<CompilePostProcessor> compilePostProcessors = new
ArrayList<>();
private StartupStepRecorder recorder;
protected RouteBuilderLoaderSupport(String extension) {
@@ -46,6 +50,21 @@ public abstract class RouteBuilderLoaderSupport extends
RoutesBuilderLoaderSuppo
return extension;
}
+ /**
+ * Gets the registered {@link CompilePostProcessor}.
+ */
+ public List<CompilePostProcessor> getCompilePostProcessors() {
+ return compilePostProcessors;
+ }
+
+ /**
+ * Add a custom {@link CompilePostProcessor} to handle specific
post-processing after compiling the source into a
+ * Java object.
+ */
+ public void addCompilePostProcessor(CompilePostProcessor preProcessor) {
+ this.compilePostProcessors.add(preProcessor);
+ }
+
@Override
protected void doBuild() throws Exception {
super.doBuild();
@@ -56,6 +75,21 @@ public abstract class RouteBuilderLoaderSupport extends
RoutesBuilderLoaderSuppo
}
@Override
+ protected void doStart() throws Exception {
+ super.doStart();
+
+ if (getCamelContext() != null) {
+ // discover optional compile post-processors to be used
+ Set<CompilePostProcessor> pres =
getCamelContext().getRegistry().findByType(CompilePostProcessor.class);
+ if (pres != null && !pres.isEmpty()) {
+ for (CompilePostProcessor pre : pres) {
+ addCompilePostProcessor(pre);
+ }
+ }
+ }
+ }
+
+ @Override
public RoutesBuilder loadRoutesBuilder(Resource resource) throws Exception
{
final RouteBuilder builder = doLoadRouteBuilder(resource);
if (builder != null) {
diff --git
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
index dd149db..c29e54d 100644
---
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
+++
b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
@@ -21,24 +21,15 @@ import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.apache.camel.BindToRegistry;
-import org.apache.camel.CamelConfiguration;
-import org.apache.camel.Configuration;
-import org.apache.camel.Converter;
-import org.apache.camel.ExtendedCamelContext;
-import org.apache.camel.LoggingLevel;
-import org.apache.camel.TypeConverterExists;
import org.apache.camel.api.management.ManagedResource;
import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.dsl.support.CompilePostProcessor;
import org.apache.camel.dsl.support.RouteBuilderLoaderSupport;
-import org.apache.camel.spi.CamelBeanPostProcessor;
import org.apache.camel.spi.Resource;
-import org.apache.camel.spi.TypeConverterRegistry;
import org.apache.camel.spi.annotations.RoutesLoader;
import org.apache.camel.support.ResourceHelper;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
-import org.apache.camel.util.ObjectHelper;
import org.joor.Reflect;
@ManagedResource(description = "Managed JavaRoutesBuilderLoader")
@@ -63,51 +54,16 @@ public class JavaRoutesBuilderLoader extends
RouteBuilderLoaderSupport {
Reflect ref = Reflect.compile(name, content).create();
Class<?> clazz = ref.type();
+ Object obj = ref.get();
- if (clazz.getAnnotation(Converter.class) != null) {
- TypeConverterRegistry tcr =
getCamelContext().getTypeConverterRegistry();
- TypeConverterExists exists = tcr.getTypeConverterExists();
- LoggingLevel level = tcr.getTypeConverterExistsLoggingLevel();
- // force type converter to override as we could be re-loading
- tcr.setTypeConverterExists(TypeConverterExists.Override);
- tcr.setTypeConverterExistsLoggingLevel(LoggingLevel.OFF);
- try {
- tcr.addTypeConverters(clazz);
- } finally {
- tcr.setTypeConverterExists(exists);
- tcr.setTypeConverterExistsLoggingLevel(level);
- }
- return null;
+ // support custom annotation scanning post compilation
+ // such as to register custom beans, type converters, etc.
+ for (CompilePostProcessor pre : getCompilePostProcessors()) {
+ pre.postCompile(getCamelContext(), name, clazz, obj);
}
- Object obj = ref.get();
if (obj instanceof RouteBuilder) {
return (RouteBuilder) obj;
- } else if (obj != null) {
- BindToRegistry bir =
obj.getClass().getAnnotation(BindToRegistry.class);
- Configuration cfg =
obj.getClass().getAnnotation(Configuration.class);
- if (bir != null || cfg != null || obj instanceof
CamelConfiguration) {
- CamelBeanPostProcessor bpp =
getCamelContext().adapt(ExtendedCamelContext.class).getBeanPostProcessor();
- if (bir != null && ObjectHelper.isNotEmpty(bir.value())) {
- name = bir.value();
- } else if (cfg != null &&
ObjectHelper.isNotEmpty(cfg.value())) {
- name = cfg.value();
- }
- // to support hot reloading of beans then we need to
enable unbind mode in bean post processor
- bpp.setUnbindEnabled(true);
- try {
- // this class is a bean service which needs to be post
processed and registered which happens
- // automatic by the bean post processor
- bpp.postProcessBeforeInitialization(obj, name);
- bpp.postProcessAfterInitialization(obj, name);
- } finally {
- bpp.setUnbindEnabled(false);
- }
- if (obj instanceof CamelConfiguration) {
- ((CamelConfiguration)
obj).configure(getCamelContext());
- }
- return null;
- }
}
return null;
}
@@ -127,4 +83,5 @@ public class JavaRoutesBuilderLoader extends
RouteBuilderLoaderSupport {
? matcher.group(1) + "." + name
: name;
}
+
}
diff --git a/dsl/camel-kamelet-main/pom.xml b/dsl/camel-kamelet-main/pom.xml
index c1b4f9f..9ae5ade 100644
--- a/dsl/camel-kamelet-main/pom.xml
+++ b/dsl/camel-kamelet-main/pom.xml
@@ -105,6 +105,38 @@
<artifactId>camel-catalog-console</artifactId>
</dependency>
+ <!-- optional spring annotation support -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <version>${spring-version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-expression</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <!-- optional quarkus annotation support -->
+ <dependency>
+ <groupId>jakarta.enterprise</groupId>
+ <artifactId>jakarta.enterprise.cdi-api</artifactId>
+ <version>${jakarta-cdi-api-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.microprofile.config</groupId>
+ <artifactId>microprofile-config-api</artifactId>
+ <version>${microprofile-config-version}</version>
+ </dependency>
+
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-timer</artifactId>
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/AnnotationDependencyInjection.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/AnnotationDependencyInjection.java
new file mode 100644
index 0000000..6689408
--- /dev/null
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/AnnotationDependencyInjection.java
@@ -0,0 +1,286 @@
+/*
+ * 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.main;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Produces;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.CamelConfiguration;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Configuration;
+import org.apache.camel.Converter;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.LoggingLevel;
+import org.apache.camel.NoSuchBeanException;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.TypeConverterExists;
+import org.apache.camel.dsl.support.CompilePostProcessor;
+import org.apache.camel.impl.engine.CamelPostProcessorHelper;
+import org.apache.camel.spi.CamelBeanPostProcessor;
+import org.apache.camel.spi.CamelBeanPostProcessorInjector;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.spi.TypeConverterRegistry;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ReflectionHelper;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+
+/**
+ * To enable camel/spring/quarkus based annotations for dependency injection
when loading DSLs.
+ */
+public final class AnnotationDependencyInjection {
+
+ private AnnotationDependencyInjection() {
+ }
+
+ public static void initAnnotationBasedDependencyInjection(CamelContext
context) {
+ Registry registry = context.getRegistry();
+ CamelBeanPostProcessor cbbp =
context.adapt(ExtendedCamelContext.class).getBeanPostProcessor();
+
+ // camel / common
+ registry.bind("CamelTypeConverterCompilePostProcessor", new
TypeConverterCompilePostProcessor());
+ registry.bind("CamelBindToRegistryCompilePostProcessor", new
BindToRegistryCompilePostProcessor());
+ // spring
+ registry.bind("SpringAnnotationCompilePostProcessor", new
SpringAnnotationCompilePostProcessor());
+ cbbp.addCamelBeanPostProjectInjector(new
SpringBeanPostProcessorInjector(context));
+ // quarkus
+ registry.bind("QuarkusAnnotationCompilePostProcessor", new
QuarkusAnnotationCompilePostProcessor());
+ cbbp.addCamelBeanPostProjectInjector(new
QuarkusBeanPostProcessorInjector(context));
+ }
+
+ private static class TypeConverterCompilePostProcessor implements
CompilePostProcessor {
+
+ @Override
+ public void postCompile(CamelContext camelContext, String name,
Class<?> clazz, Object instance) throws Exception {
+ if (clazz.isAnnotationPresent(Converter.class)) {
+ TypeConverterRegistry tcr =
camelContext.getTypeConverterRegistry();
+ TypeConverterExists exists = tcr.getTypeConverterExists();
+ LoggingLevel level = tcr.getTypeConverterExistsLoggingLevel();
+ // force type converter to override as we could be re-loading
+ tcr.setTypeConverterExists(TypeConverterExists.Override);
+ tcr.setTypeConverterExistsLoggingLevel(LoggingLevel.OFF);
+ try {
+ tcr.addTypeConverters(clazz);
+ } finally {
+ tcr.setTypeConverterExists(exists);
+ tcr.setTypeConverterExistsLoggingLevel(level);
+ }
+ }
+ }
+
+ }
+
+ private static class BindToRegistryCompilePostProcessor implements
CompilePostProcessor {
+
+ @Override
+ public void postCompile(CamelContext camelContext, String name,
Class<?> clazz, Object instance) throws Exception {
+ BindToRegistry bir =
instance.getClass().getAnnotation(BindToRegistry.class);
+ Configuration cfg =
instance.getClass().getAnnotation(Configuration.class);
+ if (bir != null || cfg != null || instance instanceof
CamelConfiguration) {
+ CamelBeanPostProcessor bpp =
camelContext.adapt(ExtendedCamelContext.class).getBeanPostProcessor();
+ if (bir != null && ObjectHelper.isNotEmpty(bir.value())) {
+ name = bir.value();
+ } else if (cfg != null &&
ObjectHelper.isNotEmpty(cfg.value())) {
+ name = cfg.value();
+ }
+ // to support hot reloading of beans then we need to enable
unbind mode in bean post processor
+ bpp.setUnbindEnabled(true);
+ try {
+ // this class uses camels own annotations so the bind to
registry happens
+ // automatic by the bean post processor
+ bpp.postProcessBeforeInitialization(instance, name);
+ bpp.postProcessAfterInitialization(instance, name);
+ } finally {
+ bpp.setUnbindEnabled(false);
+ }
+ if (instance instanceof CamelConfiguration) {
+ ((CamelConfiguration) instance).configure(camelContext);
+ }
+ }
+ }
+
+ }
+
+ private static class SpringAnnotationCompilePostProcessor implements
CompilePostProcessor {
+
+ @Override
+ public void postCompile(CamelContext camelContext, String name,
Class<?> clazz, Object instance) throws Exception {
+ // @Component and @Service are the same
+ Component comp = clazz.getAnnotation(Component.class);
+ Service service = clazz.getAnnotation(Service.class);
+ if (comp != null || service != null) {
+ CamelBeanPostProcessor bpp =
camelContext.adapt(ExtendedCamelContext.class).getBeanPostProcessor();
+ if (comp != null && ObjectHelper.isNotEmpty(comp.value())) {
+ name = comp.value();
+ } else if (service != null &&
ObjectHelper.isNotEmpty(service.value())) {
+ name = service.value();
+ }
+ bindBean(camelContext, name, instance, true);
+ }
+ }
+ }
+
+ private static class SpringBeanPostProcessorInjector implements
CamelBeanPostProcessorInjector {
+
+ private final CamelContext context;
+ private final CamelPostProcessorHelper helper;
+
+ public SpringBeanPostProcessorInjector(CamelContext context) {
+ this.context = context;
+ this.helper = new CamelPostProcessorHelper(context);
+ }
+
+ @Override
+ public void onFieldInject(Field field, Object bean, String beanName) {
+ Autowired autowired = field.getAnnotation(Autowired.class);
+ if (autowired != null) {
+ String name = null;
+ Qualifier qualifier = field.getAnnotation(Qualifier.class);
+ if (qualifier != null) {
+ name = qualifier.value();
+ }
+
+ try {
+ ReflectionHelper.setField(field, bean,
+ helper.getInjectionBeanValue(field.getType(),
name));
+ } catch (NoSuchBeanException e) {
+ if (autowired.required()) {
+ throw e;
+ }
+ // not required so ignore
+ }
+ }
+ Value value = field.getAnnotation(Value.class);
+ if (value != null) {
+ ReflectionHelper.setField(field, bean,
+ helper.getInjectionPropertyValue(field.getType(),
value.value(), null, null, bean, beanName));
+ }
+ }
+
+ @Override
+ public void onMethodInject(Method method, Object bean, String
beanName) {
+ Bean bi = method.getAnnotation(Bean.class);
+ if (bi != null) {
+ Object instance = helper.getInjectionBeanMethodValue(context,
method, bean, beanName);
+ if (instance != null) {
+ String name = method.getName();
+ if (bi.name().length > 0) {
+ name = bi.name()[0];
+ }
+ bindBean(context, name, instance, false);
+ }
+ }
+ }
+ }
+
+ private static class QuarkusAnnotationCompilePostProcessor implements
CompilePostProcessor {
+
+ @Override
+ public void postCompile(CamelContext camelContext, String name,
Class<?> clazz, Object instance) throws Exception {
+ // @ApplicationScoped and @Singleton are considered the same
+ ApplicationScoped as =
clazz.getAnnotation(ApplicationScoped.class);
+ Singleton ss = clazz.getAnnotation(Singleton.class);
+ if (as != null || ss != null) {
+ Named named = clazz.getAnnotation(Named.class);
+ if (named != null) {
+ name = named.value();
+ }
+ bindBean(camelContext, name, instance, true);
+ }
+ }
+ }
+
+ private static class QuarkusBeanPostProcessorInjector implements
CamelBeanPostProcessorInjector {
+
+ private final CamelContext context;
+ private final CamelPostProcessorHelper helper;
+
+ public QuarkusBeanPostProcessorInjector(CamelContext context) {
+ this.context = context;
+ this.helper = new CamelPostProcessorHelper(context);
+ }
+
+ @Override
+ public void onFieldInject(Field field, Object bean, String beanName) {
+ Inject inject = field.getAnnotation(Inject.class);
+ if (inject != null) {
+ String name = null;
+ Named named = field.getAnnotation(Named.class);
+ if (named != null) {
+ name = named.value();
+ }
+
+ ReflectionHelper.setField(field, bean,
+ helper.getInjectionBeanValue(field.getType(), name));
+ }
+ ConfigProperty cp = field.getAnnotation(ConfigProperty.class);
+ if (cp != null) {
+ ReflectionHelper.setField(field, bean,
+ helper.getInjectionPropertyValue(field.getType(),
cp.name(), cp.defaultValue(), null, bean, beanName));
+ }
+ }
+
+ @Override
+ public void onMethodInject(Method method, Object bean, String
beanName) {
+ Produces produces = method.getAnnotation(Produces.class);
+ Named bi = method.getAnnotation(Named.class);
+ if (produces != null || bi != null) {
+ Object instance = helper.getInjectionBeanMethodValue(context,
method, bean, beanName);
+ if (instance != null) {
+ String name = method.getName();
+ if (bi != null && !bi.value().isBlank()) {
+ name = bi.value();
+ }
+ bindBean(context, name, instance, false);
+ }
+ }
+ }
+ }
+
+ private static void bindBean(CamelContext context, String name, Object
instance, boolean postProcess) {
+ // to support hot reloading of beans then we need to enable unbind
mode in bean post processor
+ Registry registry = context.getRegistry();
+ CamelBeanPostProcessor bpp =
context.adapt(ExtendedCamelContext.class).getBeanPostProcessor();
+ bpp.setUnbindEnabled(true);
+ try {
+ // re-bind the bean to the registry
+ registry.unbind(name);
+ registry.bind(name, instance);
+ if (postProcess) {
+ bpp.postProcessBeforeInitialization(instance, name);
+ bpp.postProcessAfterInitialization(instance, name);
+ }
+ } catch (Exception e) {
+ throw RuntimeCamelException.wrapRuntimeException(e);
+ } finally {
+ bpp.setUnbindEnabled(false);
+ }
+ }
+
+}
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/DependencyDownloaderKamelet.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/DependencyDownloaderKamelet.java
index f4155e3..269088b 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/DependencyDownloaderKamelet.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/DependencyDownloaderKamelet.java
@@ -16,21 +16,36 @@
*/
package org.apache.camel.main;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.kamelet.KameletComponent;
+import org.apache.camel.dsl.yaml.YamlRoutesBuilderLoaderSupport;
import org.apache.camel.spi.Resource;
import org.apache.camel.spi.RouteTemplateLoaderListener;
import org.apache.camel.support.service.ServiceHelper;
import org.apache.camel.support.service.ServiceSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snakeyaml.engine.v2.nodes.Node;
+import org.snakeyaml.engine.v2.nodes.NodeType;
+import org.snakeyaml.engine.v2.nodes.ScalarNode;
+import org.snakeyaml.engine.v2.nodes.SequenceNode;
+
+import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.nodeAt;
/**
* To automatic downloaded dependencies that Kamelets requires.
*/
final class DependencyDownloaderKamelet extends ServiceSupport implements
CamelContextAware, RouteTemplateLoaderListener {
- private final KameletYamlRoutes downloader = new KameletYamlRoutes("yaml");
+ private final KameletDependencyDownloader downloader = new
KameletDependencyDownloader("yaml");
private CamelContext camelContext;
@Override
@@ -77,4 +92,98 @@ final class DependencyDownloaderKamelet extends
ServiceSupport implements CamelC
}
}
}
+
+ /**
+ * To automatic downloaded dependencies that Kamelets requires.
+ */
+ private static class KameletDependencyDownloader extends
YamlRoutesBuilderLoaderSupport implements CamelContextAware {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(KameletDependencyDownloader.class);
+ private CamelContext camelContext;
+ private final Set<String> downloaded = new HashSet<>();
+
+ public KameletDependencyDownloader(String extension) {
+ super(extension);
+ }
+
+ @Override
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ protected RouteBuilder builder(Node node, Resource resource) {
+ final List<String> dependencies = new ArrayList<>();
+
+ Node deps = nodeAt(node, "/spec/dependencies");
+ if (deps != null && deps.getNodeType() == NodeType.SEQUENCE) {
+ SequenceNode sn = (SequenceNode) deps;
+ for (Node child : sn.getValue()) {
+ if (child.getNodeType() == NodeType.SCALAR) {
+ ScalarNode scn = (ScalarNode) child;
+ String dep = scn.getValue();
+ if (dep != null) {
+ LOG.trace("Kamelet dependency: {}", dep);
+ dependencies.add(dep);
+ }
+ }
+ }
+ }
+
+ downloadDependencies(dependencies);
+
+ // need to fool and return an empty route builder
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ // noop
+ }
+ };
+ }
+
+ private void downloadDependencies(List<String> dependencies) {
+ final List<String> gavs = new ArrayList<>();
+ for (String dep : dependencies) {
+ String gav = dep;
+ if (dep.startsWith("camel:")) {
+ // it's a known camel component
+ gav = "org.apache.camel:camel-" + dep.substring(6) + ":" +
camelContext.getVersion();
+ }
+ if (isValidGav(gav)) {
+ gavs.add(gav);
+ }
+ }
+
+ if (!gavs.isEmpty()) {
+ for (String gav : gavs) {
+ MavenGav mg = MavenGav.parseGav(camelContext, gav);
+ DownloaderHelper.downloadDependency(camelContext,
mg.getGroupId(), mg.getArtifactId(), mg.getVersion());
+ downloaded.add(gav);
+ }
+ }
+ }
+
+ private boolean isValidGav(String gav) {
+ if (downloaded.contains(gav)) {
+ // already downloaded
+ return false;
+ }
+
+ // skip camel-core and camel-kamelet as they are already included
+ if (gav.contains("org.apache.camel:camel-core") ||
gav.contains("org.apache.camel:camel-kamelet:")) {
+ return false;
+ }
+
+ MavenGav mg = MavenGav.parseGav(camelContext, gav);
+ boolean exists = DownloaderHelper.alreadyOnClasspath(camelContext,
mg.getArtifactId(), mg.getVersion());
+ // valid if not already on classpath
+ return !exists;
+ }
+
+ }
}
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
index 665c872..48a7167 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
@@ -31,6 +31,7 @@ import
org.apache.camel.startup.jfr.FlightRecorderStartupStepRecorder;
* A Main class for booting up Camel with Kamelet in standalone mode.
*/
public class KameletMain extends MainCommandLineSupport {
+
public static final String DEFAULT_KAMELETS_LOCATION =
"classpath:/kamelets,github:apache:camel-kamelets/kamelets";
private static ClassLoader kameletClassLoader;
@@ -163,6 +164,8 @@ public class KameletMain extends MainCommandLineSupport {
answer.setRegistry(registry);
// load camel component and custom health-checks
answer.setLoadHealthChecks(true);
+ // annotation based dependency injection for camel/spring/quarkus
annotations in DSLs and Java beans
+
AnnotationDependencyInjection.initAnnotationBasedDependencyInjection(answer);
// embed HTTP server if port is specified
Object port =
getInitialProperties().get("camel.jbang.platform-http.port");
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletYamlRoutes.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletYamlRoutes.java
deleted file mode 100644
index a57422c..0000000
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletYamlRoutes.java
+++ /dev/null
@@ -1,130 +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.main;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.CamelContextAware;
-import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.dsl.yaml.YamlRoutesBuilderLoaderSupport;
-import org.apache.camel.spi.Resource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.snakeyaml.engine.v2.nodes.Node;
-import org.snakeyaml.engine.v2.nodes.NodeType;
-import org.snakeyaml.engine.v2.nodes.ScalarNode;
-import org.snakeyaml.engine.v2.nodes.SequenceNode;
-
-import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.nodeAt;
-
-/**
- * Reuse the YAML DSL support for parsing Kamelets
- */
-class KameletYamlRoutes extends YamlRoutesBuilderLoaderSupport implements
CamelContextAware {
-
- private static final Logger LOG =
LoggerFactory.getLogger(KameletYamlRoutes.class);
- private CamelContext camelContext;
- private final Set<String> downloaded = new HashSet<>();
-
- public KameletYamlRoutes(String extension) {
- super(extension);
- }
-
- @Override
- public CamelContext getCamelContext() {
- return camelContext;
- }
-
- @Override
- public void setCamelContext(CamelContext camelContext) {
- this.camelContext = camelContext;
- }
-
- @Override
- protected RouteBuilder builder(Node node, Resource resource) {
- final List<String> dependencies = new ArrayList<>();
-
- Node deps = nodeAt(node, "/spec/dependencies");
- if (deps != null && deps.getNodeType() == NodeType.SEQUENCE) {
- SequenceNode sn = (SequenceNode) deps;
- for (Node child : sn.getValue()) {
- if (child.getNodeType() == NodeType.SCALAR) {
- ScalarNode scn = (ScalarNode) child;
- String dep = scn.getValue();
- if (dep != null) {
- LOG.trace("Kamelet dependency: {}", dep);
- dependencies.add(dep);
- }
- }
- }
- }
-
- downloadDependencies(dependencies);
-
- // need to fool and return an empty route builder
- return new RouteBuilder() {
- @Override
- public void configure() throws Exception {
- // noop
- }
- };
- }
-
- private void downloadDependencies(List<String> dependencies) {
- final List<String> gavs = new ArrayList<>();
- for (String dep : dependencies) {
- String gav = dep;
- if (dep.startsWith("camel:")) {
- // it's a known camel component
- gav = "org.apache.camel:camel-" + dep.substring(6) + ":" +
camelContext.getVersion();
- }
- if (isValidGav(gav)) {
- gavs.add(gav);
- }
- }
-
- if (!gavs.isEmpty()) {
- for (String gav : gavs) {
- MavenGav mg = MavenGav.parseGav(camelContext, gav);
- DownloaderHelper.downloadDependency(camelContext,
mg.getGroupId(), mg.getArtifactId(), mg.getVersion());
- downloaded.add(gav);
- }
- }
- }
-
- private boolean isValidGav(String gav) {
- if (downloaded.contains(gav)) {
- // already downloaded
- return false;
- }
-
- // skip camel-core and camel-kamelet as they are already included
- if (gav.contains("org.apache.camel:camel-core") ||
gav.contains("org.apache.camel:camel-kamelet:")) {
- return false;
- }
-
- MavenGav mg = MavenGav.parseGav(camelContext, gav);
- boolean exists = DownloaderHelper.alreadyOnClasspath(camelContext,
mg.getArtifactId(), mg.getVersion());
- // valid if not already on classpath
- return !exists;
- }
-
-}
diff --git a/parent/pom.xml b/parent/pom.xml
index d7f50a1..927c622 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -288,6 +288,7 @@
<javax-annotation-api-version>1.3.2</javax-annotation-api-version>
<jakarta-mail-version>1.6.6</jakarta-mail-version>
<javax-servlet-api-version>3.1.0</javax-servlet-api-version>
+ <jakarta-cdi-api-version>2.0.2</jakarta-cdi-api-version>
<jakarta-api-version>2.1.5</jakarta-api-version>
<jakarta.el-version>3.0.3</jakarta.el-version>
<jakarta-jaxb-version>2.3.3</jakarta-jaxb-version>