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

ppalaga pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git

commit 78f03720944ce50481a9b63fe8b70cdb78a5443e
Author: Luca Burgazzoli <[email protected]>
AuthorDate: Tue Jun 8 21:44:52 2021 +0200

    core: sync BaseModel and FastCamelContext with Camel main
---
 .../org/apache/camel/quarkus/core/BaseModel.java   | 192 ++++++++++++++++++++-
 .../camel/quarkus/core/FastCamelContext.java       | 106 +++++++++++-
 integration-tests/kamelet/pom.xml                  |   4 +
 .../component/kamelet/it/KameletResource.java      |   8 +
 .../component/kamelet/it/KameletRoutes.java        |  23 +++
 .../quarkus/component/kamelet/it/KameletTest.java  |  19 ++
 6 files changed, 338 insertions(+), 14 deletions(-)

diff --git 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/BaseModel.java
 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/BaseModel.java
index 4233ddc..410c8d6 100644
--- 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/BaseModel.java
+++ 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/BaseModel.java
@@ -23,13 +23,19 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.StringJoiner;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.Component;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
 import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.FailedToCreateRouteFromTemplateException;
+import org.apache.camel.NoSuchBeanException;
+import org.apache.camel.PropertyBindingException;
 import org.apache.camel.RouteTemplateContext;
 import org.apache.camel.impl.DefaultModelReifierFactory;
 import org.apache.camel.model.DataFormatDefinition;
@@ -45,15 +51,25 @@ import 
org.apache.camel.model.Resilience4jConfigurationDefinition;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.RouteDefinitionHelper;
 import org.apache.camel.model.RouteFilters;
+import org.apache.camel.model.RouteTemplateBeanDefinition;
 import org.apache.camel.model.RouteTemplateDefinition;
 import org.apache.camel.model.RouteTemplateParameterDefinition;
 import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.transformer.TransformerDefinition;
 import org.apache.camel.model.validator.ValidatorDefinition;
+import org.apache.camel.spi.ExchangeFactory;
+import org.apache.camel.spi.Language;
 import org.apache.camel.spi.ModelReifierFactory;
 import org.apache.camel.spi.NodeIdFactory;
+import org.apache.camel.spi.PropertyConfigurer;
+import org.apache.camel.spi.ScriptingLanguage;
+import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.support.ScriptHelper;
+import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.AntPathMatcher;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.function.Suppliers;
 
 public abstract class BaseModel implements Model {
 
@@ -218,11 +234,6 @@ public abstract class BaseModel implements Model {
             throw new IllegalArgumentException("Cannot find RouteTemplate with 
id " + routeTemplateId);
         }
 
-        // apply configurer if any present
-        if (target.getConfigurer() != null) {
-            target.getConfigurer().accept(routeTemplateContext);
-        }
-
         final Map<String, Object> prop = new HashMap<>();
         // include default values first from the template (and validate that 
we have inputs for all required parameters)
         if (target.getTemplateParameters() != null) {
@@ -232,8 +243,8 @@ public abstract class BaseModel implements Model {
                 if (temp.getDefaultValue() != null) {
                     prop.put(temp.getName(), temp.getDefaultValue());
                 } else {
-                    // this is a required parameter do we have that as input
-                    if 
(!routeTemplateContext.getParameters().containsKey(temp.getName())) {
+                    if (temp.isRequired() && 
!routeTemplateContext.getParameters().containsKey(temp.getName())) {
+                        // this is a required parameter which is missing
                         templatesBuilder.add(temp.getName());
                     }
                 }
@@ -241,14 +252,23 @@ public abstract class BaseModel implements Model {
             if (templatesBuilder.length() > 0) {
                 throw new IllegalArgumentException(
                         "Route template " + routeTemplateId + " the following 
mandatory parameters must be provided: "
-                                + templatesBuilder.toString());
+                                + templatesBuilder);
             }
         }
 
-        // then override with user parameters
+        // then override with user parameters part 1
         if (routeTemplateContext.getParameters() != null) {
             prop.putAll(routeTemplateContext.getParameters());
         }
+        // route template context should include default template parameters 
from the target route template
+        // so it has all parameters available
+        if (target.getTemplateParameters() != null) {
+            for (RouteTemplateParameterDefinition temp : 
target.getTemplateParameters()) {
+                if 
(!routeTemplateContext.getParameters().containsKey(temp.getName()) && 
temp.getDefaultValue() != null) {
+                    routeTemplateContext.setParameter(temp.getName(), 
temp.getDefaultValue());
+                }
+            }
+        }
 
         RouteTemplateDefinition.Converter converter = 
RouteTemplateDefinition.Converter.DEFAULT_CONVERTER;
 
@@ -275,6 +295,15 @@ public abstract class BaseModel implements Model {
         def.setTemplateParameters(prop);
         def.setRouteTemplateContext(routeTemplateContext);
 
+        // setup local beans
+        if (target.getTemplateBeans() != null) {
+            addTemplateBeans(routeTemplateContext, target);
+        }
+
+        if (target.getConfigurer() != null) {
+            routeTemplateContext.setConfigurer(target.getConfigurer());
+        }
+
         // assign ids to the routes and validate that the id's are all unique
         String duplicate = RouteDefinitionHelper.validateUniqueIds(def, 
routeDefinitions);
         if (duplicate != null) {
@@ -286,6 +315,96 @@ public abstract class BaseModel implements Model {
         return def.getId();
     }
 
+    private void addTemplateBeans(RouteTemplateContext routeTemplateContext, 
RouteTemplateDefinition target) throws Exception {
+        for (RouteTemplateBeanDefinition b : target.getTemplateBeans()) {
+            final Map<String, Object> props = new HashMap<>();
+            if (b.getProperties() != null) {
+                b.getProperties().forEach(p -> props.put(p.getKey(), 
p.getValue()));
+            }
+            if (b.getBeanSupplier() != null) {
+                if (props.isEmpty()) {
+                    // bean class is optional for supplier
+                    if (b.getBeanClass() != null) {
+                        routeTemplateContext.bind(b.getName(), 
b.getBeanClass(), b.getBeanSupplier());
+                    } else {
+                        routeTemplateContext.bind(b.getName(), 
b.getBeanSupplier());
+                    }
+                }
+            } else if (b.getScript() != null) {
+                final String script = b.getScript().getScript();
+                final Language lan = camelContext.resolveLanguage(b.getType());
+                final Class<?> clazz = b.getBeanType() != null
+                        ? 
camelContext.getClassResolver().resolveMandatoryClass(b.getBeanType())
+                        : b.getBeanClass() != null ? b.getBeanClass() : 
Object.class;
+                final ScriptingLanguage slan = lan instanceof 
ScriptingLanguage ? (ScriptingLanguage) lan : null;
+                if (slan != null) {
+                    // scripting language should be evaluated with route 
template context as binding
+                    // and memorize so the script is only evaluated once and 
the local bean is the same
+                    // if a route template refers to the local bean multiple 
times
+                    routeTemplateContext.bind(b.getName(), clazz, 
Suppliers.memorize(() -> {
+                        Map<String, Object> bindings = new HashMap<>();
+                        // use rtx as the short-hand name, as context would 
imply its CamelContext
+                        bindings.put("rtc", routeTemplateContext);
+                        Object local = slan.evaluate(script, bindings, clazz);
+                        if (!props.isEmpty()) {
+                            setPropertiesOnTarget(camelContext, local, props);
+                        }
+                        return local;
+                    }));
+                } else {
+                    // exchange based languages needs a dummy exchange to be 
evaluated
+                    // and memorize so the script is only evaluated once and 
the local bean is the same
+                    // if a route template refers to the local bean multiple 
times
+                    routeTemplateContext.bind(b.getName(), clazz, 
Suppliers.memorize(() -> {
+                        ExchangeFactory ef = 
camelContext.adapt(ExtendedCamelContext.class).getExchangeFactory();
+                        Exchange dummy = ef.create(false);
+                        try {
+                            String text = 
ScriptHelper.resolveOptionalExternalScript(camelContext, dummy, script);
+                            if (text != null) {
+                                Expression exp = lan.createExpression(text);
+                                Object local = exp.evaluate(dummy, clazz);
+                                if (!props.isEmpty()) {
+                                    setPropertiesOnTarget(camelContext, local, 
props);
+                                }
+                                return local;
+                            } else {
+                                return null;
+                            }
+                        } finally {
+                            ef.release(dummy);
+                        }
+                    }));
+                }
+            } else if (b.getBeanClass() != null || b.getType() != null && 
b.getType().startsWith("#class:")) {
+                Class<?> clazz = b.getBeanClass() != null
+                        ? b.getBeanClass() : 
camelContext.getClassResolver().resolveMandatoryClass(b.getType().substring(7));
+                // we only have the bean class so we use that to create a new 
bean via the injector
+                // and memorize so the bean is only created once and the local 
bean is the same
+                // if a route template refers to the local bean multiple times
+                routeTemplateContext.bind(b.getName(), clazz,
+                        Suppliers.memorize(() -> {
+                            Object local = 
camelContext.getInjector().newInstance(clazz);
+                            if (!props.isEmpty()) {
+                                setPropertiesOnTarget(camelContext, local, 
props);
+                            }
+                            return local;
+                        }));
+            } else if (b.getType() != null && 
b.getType().startsWith("#type:")) {
+                Class<?> clazz = 
camelContext.getClassResolver().resolveMandatoryClass(b.getType().substring(6));
+                Set<?> found = 
getCamelContext().getRegistry().findByType(clazz);
+                if (found == null || found.isEmpty()) {
+                    throw new NoSuchBeanException(null, clazz.getName());
+                } else if (found.size() > 1) {
+                    throw new NoSuchBeanException(
+                            "Found " + found.size() + " beans of type: " + 
clazz + ". Only one bean expected.");
+                } else {
+                    // do not set properties when using #type as it uses an 
existing shared bean
+                    routeTemplateContext.bind(b.getName(), clazz, 
found.iterator().next());
+                }
+            }
+        }
+    }
+
     @Override
     public synchronized List<RestDefinition> getRestDefinitions() {
         return restDefinitions;
@@ -526,4 +645,59 @@ public abstract class BaseModel implements Model {
         return camelContext.isStarted() && !camelContext.isStarting();
     }
 
+    private static void setPropertiesOnTarget(CamelContext context, Object 
target, Map<String, Object> properties) {
+        ObjectHelper.notNull(context, "context");
+        ObjectHelper.notNull(target, "target");
+        ObjectHelper.notNull(properties, "properties");
+
+        if (target instanceof CamelContext) {
+            throw new UnsupportedOperationException("Configuring the Camel 
Context is not supported");
+        }
+
+        PropertyConfigurer configurer = null;
+        if (target instanceof Component) {
+            // the component needs to be initialized to have the configurer 
ready
+            ServiceHelper.initService(target);
+            configurer = ((Component) target).getComponentPropertyConfigurer();
+        }
+
+        if (configurer == null) {
+            // see if there is a configurer for it
+            configurer = context.adapt(ExtendedCamelContext.class)
+                    .getConfigurerResolver()
+                    
.resolvePropertyConfigurer(target.getClass().getSimpleName(), context);
+        }
+
+        try {
+            PropertyBindingSupport.build()
+                    .withMandatory(true)
+                    .withRemoveParameters(false)
+                    .withConfigurer(configurer)
+                    .withIgnoreCase(true)
+                    .withFlattenProperties(true)
+                    .bind(context, target, properties);
+        } catch (PropertyBindingException e) {
+            String key = e.getOptionKey();
+            if (key == null) {
+                String prefix = e.getOptionPrefix();
+                if (prefix != null && !prefix.endsWith(".")) {
+                    prefix = "." + prefix;
+                }
+
+                key = prefix != null
+                        ? prefix + "." + e.getPropertyName()
+                        : e.getPropertyName();
+            }
+
+            // enrich the error with more precise details with option prefix 
and key
+            throw new PropertyBindingException(
+                    e.getTarget(),
+                    e.getPropertyName(),
+                    e.getValue(),
+                    null,
+                    key,
+                    e.getCause());
+        }
+    }
+
 }
diff --git 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastCamelContext.java
 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastCamelContext.java
index 4fafa76..dda025f 100644
--- 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastCamelContext.java
+++ 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/FastCamelContext.java
@@ -19,6 +19,7 @@ package org.apache.camel.quarkus.core;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -30,11 +31,13 @@ import org.apache.camel.CatalogCamelContext;
 import org.apache.camel.Component;
 import org.apache.camel.Endpoint;
 import org.apache.camel.Expression;
+import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.FailedToStartRouteException;
 import org.apache.camel.Predicate;
 import org.apache.camel.Processor;
 import org.apache.camel.Route;
 import org.apache.camel.RouteTemplateContext;
+import org.apache.camel.StartupStep;
 import org.apache.camel.TypeConverter;
 import org.apache.camel.ValueHolder;
 import org.apache.camel.builder.AdviceWith;
@@ -87,6 +90,7 @@ import org.apache.camel.model.Model;
 import org.apache.camel.model.ModelCamelContext;
 import org.apache.camel.model.ModelLifecycleStrategy;
 import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.model.ProcessorDefinitionHelper;
 import org.apache.camel.model.Resilience4jConfigurationDefinition;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.RouteDefinitionHelper;
@@ -100,7 +104,6 @@ import 
org.apache.camel.processor.DefaultAnnotationBasedProcessorFactory;
 import org.apache.camel.processor.DefaultDeferServiceFactory;
 import org.apache.camel.processor.DefaultInternalProcessorFactory;
 import org.apache.camel.processor.DefaultProcessorFactory;
-import org.apache.camel.reifier.RouteReifier;
 import org.apache.camel.reifier.errorhandler.ErrorHandlerReifier;
 import org.apache.camel.reifier.language.ExpressionReifier;
 import org.apache.camel.reifier.transformer.TransformerReifier;
@@ -132,6 +135,7 @@ import org.apache.camel.spi.InterceptEndpointFactory;
 import org.apache.camel.spi.InternalProcessorFactory;
 import org.apache.camel.spi.Language;
 import org.apache.camel.spi.LanguageResolver;
+import org.apache.camel.spi.LocalBeanRepositoryAware;
 import org.apache.camel.spi.ManagementNameStrategy;
 import org.apache.camel.spi.MessageHistoryFactory;
 import org.apache.camel.spi.ModelJAXBContextFactory;
@@ -152,6 +156,7 @@ import org.apache.camel.spi.RouteController;
 import org.apache.camel.spi.RouteFactory;
 import org.apache.camel.spi.RoutesLoader;
 import org.apache.camel.spi.ShutdownStrategy;
+import org.apache.camel.spi.StartupStepRecorder;
 import org.apache.camel.spi.StreamCachingStrategy;
 import org.apache.camel.spi.Tracer;
 import org.apache.camel.spi.Transformer;
@@ -165,12 +170,19 @@ import org.apache.camel.spi.ValidatorRegistry;
 import org.apache.camel.spi.XMLRoutesDefinitionLoader;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.DefaultUuidGenerator;
+import org.apache.camel.support.LocalBeanRegistry;
 import org.apache.camel.support.NormalizedUri;
 import org.apache.camel.support.ResolverHelper;
+import org.apache.camel.support.SimpleUuidGenerator;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class FastCamelContext extends AbstractCamelContext implements 
CatalogCamelContext, ModelCamelContext {
+    private static final Logger LOG = 
LoggerFactory.getLogger(FastCamelContext.class);
+    private static final UuidGenerator UUID = new SimpleUuidGenerator();
+
     private final String version;
     private final ModelToXMLDumper modelDumper;
     private Model model;
@@ -933,8 +945,11 @@ public class FastCamelContext extends AbstractCamelContext 
implements CatalogCam
         model.setModelReifierFactory(modelReifierFactory);
     }
 
-    @Override
     public void startRouteDefinitions(List<RouteDefinition> routeDefinitions) 
throws Exception {
+        if (model == null && isLightweight()) {
+            throw new IllegalStateException("Access to model not supported in 
lightweight mode");
+        }
+
         // indicate we are staring the route using this thread so
         // we are able to query this if needed
         boolean alreadyStartingRoutes = isStartingRoutes();
@@ -943,22 +958,88 @@ public class FastCamelContext extends 
AbstractCamelContext implements CatalogCam
         }
 
         PropertiesComponent pc = 
getCamelContextReference().getPropertiesComponent();
+        // route templates supports binding beans that are local for the 
template only
+        // in this local mode then we need to check for side-effects (see 
further)
+        LocalBeanRepositoryAware localBeans = null;
+        if (getCamelContextReference().getRegistry() instanceof 
LocalBeanRepositoryAware) {
+            localBeans = (LocalBeanRepositoryAware) 
getCamelContextReference().getRegistry();
+        }
         try {
             RouteDefinitionHelper.forceAssignIds(getCamelContextReference(), 
routeDefinitions);
             for (RouteDefinition routeDefinition : routeDefinitions) {
                 // assign ids to the routes and validate that the id's is all 
unique
                 String duplicate = 
RouteDefinitionHelper.validateUniqueIds(routeDefinition, routeDefinitions);
                 if (duplicate != null) {
-                    throw new 
FailedToStartRouteException(routeDefinition.getId(),
+                    throw new FailedToStartRouteException(
+                            routeDefinition.getId(),
                             "duplicate id detected: " + duplicate + ". Please 
correct ids to be unique among all your routes.");
                 }
 
                 // if the route definition was created via a route template 
then we need to prepare its parameters when the route is being created and 
started
                 if (routeDefinition.isTemplate() != null && 
routeDefinition.isTemplate()
                         && routeDefinition.getTemplateParameters() != null) {
+
+                    // apply configurer if any present
+                    if 
(routeDefinition.getRouteTemplateContext().getConfigurer() != null) {
+                        
routeDefinition.getRouteTemplateContext().getConfigurer()
+                                
.accept(routeDefinition.getRouteTemplateContext());
+                    }
+
+                    // copy parameters/bean repository to not cause side-effect
+                    Map<String, Object> params = new 
HashMap<>(routeDefinition.getTemplateParameters());
+                    LocalBeanRegistry bbr = (LocalBeanRegistry) 
routeDefinition.getRouteTemplateContext()
+                            .getLocalBeanRepository();
+                    LocalBeanRegistry bbrCopy = new LocalBeanRegistry();
+
+                    // make all bean in the bean repository use unique keys 
(need to add uuid counter)
+                    // so when the route template is used again to create 
another route, then there is
+                    // no side-effect from previously used values that Camel 
may use in its endpoint
+                    // registry and elsewhere
+                    if (bbr != null && !bbr.isEmpty()) {
+                        for (Map.Entry<String, Object> param : 
params.entrySet()) {
+                            Object value = param.getValue();
+                            if (value instanceof String) {
+                                String oldKey = (String) value;
+                                boolean clash = bbr.keys().stream().anyMatch(k 
-> k.equals(oldKey));
+                                if (clash) {
+                                    String newKey = oldKey + "-" + 
UUID.generateUuid();
+                                    LOG.debug(
+                                            "Route: {} re-assigning local-bean 
id: {} to: {} to ensure ids are globally unique",
+                                            routeDefinition.getId(), oldKey, 
newKey);
+                                    bbrCopy.put(newKey, bbr.remove(oldKey));
+                                    param.setValue(newKey);
+                                }
+                            }
+                        }
+                        // the remainder of the local beans must also have 
their ids made global unique
+                        for (String oldKey : bbr.keySet()) {
+                            String newKey = oldKey + "-" + UUID.generateUuid();
+                            LOG.debug(
+                                    "Route: {} re-assigning local-bean id: {} 
to: {} to ensure ids are globally unique",
+                                    routeDefinition.getId(), oldKey, newKey);
+                            bbrCopy.put(newKey, bbr.get(oldKey));
+                            if (!params.containsKey(oldKey)) {
+                                // if a bean was bound as local bean with a 
key and it was not defined as template parameter
+                                // then store it as if it was a template 
parameter with same key=value which allows us
+                                // to use this local bean in the route without 
any problem such as:
+                                //   to("bean:{{myBean}}")
+                                // and myBean is the local bean id.
+                                params.put(oldKey, newKey);
+                            }
+                        }
+                    }
+
                     Properties prop = new Properties();
-                    prop.putAll(routeDefinition.getTemplateParameters());
+                    prop.putAll(params);
                     pc.setLocalProperties(prop);
+
+                    // we need to shadow the bean registry on the CamelContext 
with the local beans from the route template context
+                    if (localBeans != null && bbrCopy != null) {
+                        localBeans.setLocalBeanRepository(bbrCopy);
+                    }
+
+                    // need to reset auto assigned ids, so there is no clash 
when creating routes
+                    
ProcessorDefinitionHelper.resetAllAutoAssignedNodeIds(routeDefinition);
                 }
 
                 // must ensure route is prepared, before we can start it
@@ -967,14 +1048,29 @@ public class FastCamelContext extends 
AbstractCamelContext implements CatalogCam
                     routeDefinition.markPrepared();
                 }
 
-                Route route = new RouteReifier(getCamelContextReference(), 
routeDefinition).createRoute();
+                StartupStepRecorder recorder = 
getCamelContextReference().adapt(ExtendedCamelContext.class)
+                        .getStartupStepRecorder();
+                StartupStep step = recorder.beginStep(Route.class, 
routeDefinition.getRouteId(), "Create Route");
+                Route route = model.getModelReifierFactory().createRoute(this, 
routeDefinition);
+                recorder.endStep(step);
+
                 RouteService routeService = new RouteService(route);
                 startRouteService(routeService, true);
+
+                // clear local after the route is created via the reifier
+                pc.setLocalProperties(null);
+                if (localBeans != null) {
+                    localBeans.setLocalBeanRepository(null);
+                }
             }
         } finally {
             if (!alreadyStartingRoutes) {
                 setStartingRoutes(false);
             }
+            pc.setLocalProperties(null);
+            if (localBeans != null) {
+                localBeans.setLocalBeanRepository(null);
+            }
         }
     }
 
diff --git a/integration-tests/kamelet/pom.xml 
b/integration-tests/kamelet/pom.xml
index ad4d6b2..ade4967 100644
--- a/integration-tests/kamelet/pom.xml
+++ b/integration-tests/kamelet/pom.xml
@@ -56,6 +56,10 @@
             <artifactId>camel-quarkus-timer</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-bean</artifactId>
+        </dependency>
+        <dependency>
             <groupId>io.quarkus</groupId>
             <artifactId>quarkus-resteasy</artifactId>
         </dependency>
diff --git 
a/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletResource.java
 
b/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletResource.java
index 9484755..bf61c5b 100644
--- 
a/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletResource.java
+++ 
b/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletResource.java
@@ -21,6 +21,7 @@ import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 
@@ -64,4 +65,11 @@ public class KameletResource {
     public String kameletChain(String message) throws Exception {
         return 
fluentProducerTemplate.to("direct:chain").withBody(message).request(String.class);
     }
+
+    @Path("/invoke/{name}")
+    @POST
+    @Produces(MediaType.TEXT_PLAIN)
+    public String invoke(@PathParam("name") String name, String message) 
throws Exception {
+        return fluentProducerTemplate.toF("kamelet:%s", 
name).withBody(message).request(String.class);
+    }
 }
diff --git 
a/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletRoutes.java
 
b/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletRoutes.java
index 84be64c..7062b0a 100644
--- 
a/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletRoutes.java
+++ 
b/integration-tests/kamelet/src/main/java/org/apache/camel/quarkus/component/kamelet/it/KameletRoutes.java
@@ -16,7 +16,11 @@
  */
 package org.apache.camel.quarkus.component.kamelet.it;
 
+import java.util.Locale;
+
+import io.quarkus.runtime.annotations.RegisterForReflection;
 import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
 import org.apache.camel.builder.RouteBuilder;
 
 public class KameletRoutes extends RouteBuilder {
@@ -44,8 +48,27 @@ public class KameletRoutes extends RouteBuilder {
                 .from("kamelet:source")
                 .setBody().simple("{{prefix}} ${body} {{suffix}}");
 
+        routeTemplate("toUpperWithBean")
+                .templateBean("to-upper", new ToUpperProcessor())
+                .from("kamelet:source")
+                .to("bean:{{to-upper}}");
+
+        routeTemplate("toUpperWithClass")
+                .templateBean("to-upper", ToUpperProcessor.class)
+                .from("kamelet:source")
+                .to("bean:{{to-upper}}");
+
         from("direct:chain")
                 .to("kamelet:echo/1?prefix=Camel Quarkus&suffix=Chained")
                 .to("kamelet:echo/2?prefix=Hello&suffix=Route");
     }
+
+    @RegisterForReflection
+    public static class ToUpperProcessor implements Processor {
+        @Override
+        public void process(Exchange exchange) throws Exception {
+            exchange.getMessage().setBody(
+                    
exchange.getMessage().getBody(String.class).toUpperCase(Locale.US));
+        }
+    }
 }
diff --git 
a/integration-tests/kamelet/src/test/java/org/apache/camel/quarkus/component/kamelet/it/KameletTest.java
 
b/integration-tests/kamelet/src/test/java/org/apache/camel/quarkus/component/kamelet/it/KameletTest.java
index 50f494d..77a6607 100644
--- 
a/integration-tests/kamelet/src/test/java/org/apache/camel/quarkus/component/kamelet/it/KameletTest.java
+++ 
b/integration-tests/kamelet/src/test/java/org/apache/camel/quarkus/component/kamelet/it/KameletTest.java
@@ -65,4 +65,23 @@ class KameletTest {
                 .statusCode(200)
                 .body(is("Hello Camel Quarkus Kamelet Chained Route"));
     }
+
+    @Test
+    public void testInvoke() {
+        RestAssured.given()
+                .contentType(ContentType.TEXT)
+                .body("Kamelet")
+                .post("/kamelet/invoke/toUpperWithBean")
+                .then()
+                .statusCode(200)
+                .body(is("KAMELET"));
+
+        RestAssured.given()
+                .contentType(ContentType.TEXT)
+                .body("Kamelet2")
+                .post("/kamelet/invoke/toUpperWithClass")
+                .then()
+                .statusCode(200)
+                .body(is("KAMELET2"));
+    }
 }

Reply via email to