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

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

commit b2f60baf9fade33d83f9e159b345031f8a8119bf
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Jan 26 10:53:18 2025 +0100

    CAMEL-21599: camel-kamelet - Rework error handler for kamelets to be more 
standard Camel. WIP
---
 .../apache/camel/component/kamelet/kamelet.json    |  4 +--
 .../apache/camel/component/kamelet/Kamelet.java    |  9 +++++
 .../camel/component/kamelet/KameletComponent.java  | 26 +++++++-------
 .../camel/component/kamelet/KameletEndpoint.java   | 17 +++++----
 .../camel/component/kamelet/KameletProducer.java   |  1 +
 .../kamelet/KameletNoErrorHandlerTest.java         |  6 +++-
 ...rFalseTest.java => KameletRouteSingleTest.java} | 41 ++++++++++------------
 .../java/org/apache/camel/impl/DefaultModel.java   |  5 +++
 8 files changed, 65 insertions(+), 44 deletions(-)

diff --git 
a/components/camel-kamelet/src/generated/resources/META-INF/org/apache/camel/component/kamelet/kamelet.json
 
b/components/camel-kamelet/src/generated/resources/META-INF/org/apache/camel/component/kamelet/kamelet.json
index 377a4908ac5..1d696ba4356 100644
--- 
a/components/camel-kamelet/src/generated/resources/META-INF/org/apache/camel/component/kamelet/kamelet.json
+++ 
b/components/camel-kamelet/src/generated/resources/META-INF/org/apache/camel/component/kamelet/kamelet.json
@@ -32,7 +32,7 @@
     "lazyStartProducer": { "index": 5, "kind": "property", "displayName": 
"Lazy Start Producer", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether the producer should be started lazy (on the first message). By 
starting lazy you can use this to allow CamelContext and routes to startup in 
situations where a producer may otherwise fail [...]
     "timeout": { "index": 6, "kind": "property", "displayName": "Timeout", 
"group": "producer", "label": "producer", "required": false, "type": "integer", 
"javaType": "long", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 30000, "description": "The timeout value to use if block is 
enabled." },
     "autowiredEnabled": { "index": 7, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
-    "noErrorHandler": { "index": 8, "kind": "property", "displayName": "No 
Error Handler", "group": "advanced", "label": "advanced", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": true, "description": "Kamelets, by 
default, will not do fine-grained error handling, but works in no-error-handler 
mode. This can be turned off, to use old behaviour in earlier versions of 
Camel." },
+    "noErrorHandler": { "index": 8, "kind": "property", "displayName": "No 
Error Handler", "group": "advanced", "label": "advanced", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "description": "Whether kamelets 
should use error handling or not. By default, the Kamelet uses the same error 
handler as from the calling route. This means that if the calling route has 
error handling that performs r [...]
     "routeTemplateLoaderListener": { "index": 9, "kind": "property", 
"displayName": "Route Template Loader Listener", "group": "advanced", "label": 
"advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.RouteTemplateLoaderListener", "deprecated": false, 
"autowired": true, "secret": false, "description": "To plugin a custom listener 
for when the Kamelet component is loading Kamelets from external resources." }
   },
   "properties": {
@@ -46,6 +46,6 @@
     "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produc [...]
     "timeout": { "index": 8, "kind": "parameter", "displayName": "Timeout", 
"group": "producer (advanced)", "label": "producer,advanced", "required": 
false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 30000, "description": "The timeout 
value to use if block is enabled." },
     "location": { "index": 9, "kind": "parameter", "displayName": "Location", 
"group": "advanced", "label": "advanced", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Location of the Kamelet to 
use which can be specified as a resource from file system, classpath etc. The 
location cannot use wildcards, and must refer to a file including extension, 
for example file:\/etc\/ [...]
-    "noErrorHandler": { "index": 10, "kind": "parameter", "displayName": "No 
Error Handler", "group": "advanced", "label": "advanced", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": true, "description": "Kamelets, by 
default, will not do fine-grained error handling, but works in no-error-handler 
mode. This can be turned off, to use old behaviour in earlier versions of 
Camel." }
+    "noErrorHandler": { "index": 10, "kind": "parameter", "displayName": "No 
Error Handler", "group": "advanced", "label": "advanced", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "description": "Whether kamelets 
should use error handling or not. By default, the Kamelet uses the same error 
handler as from the calling route. This means that if the calling route has 
error handling that performs [...]
   }
 }
diff --git 
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
 
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
index da2cdc792b0..c20ee878206 100644
--- 
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
+++ 
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
@@ -24,6 +24,7 @@ import java.util.function.Predicate;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.builder.NoErrorHandlerBuilder;
+import org.apache.camel.model.ModelCamelContext;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.RouteTemplateDefinition;
 import org.apache.camel.model.ToDefinition;
@@ -48,6 +49,7 @@ public final class Kamelet {
     public static final String PARAM_LOCATION = "location";
     public static final String PARAM_UUID = "uuid";
     public static final String DEFAULT_LOCATION = "classpath:kamelets";
+    public static final String PARENT_ROUTE_ID = "parentRouteId";
     public static final String NO_ERROR_HANDLER = "noErrorHandler";
 
     // use a running counter as uuid
@@ -182,6 +184,7 @@ public final class Kamelet {
         final String rid = (String) parameters.get(PARAM_ROUTE_ID);
         final boolean noErrorHandler = (boolean) 
parameters.get(NO_ERROR_HANDLER);
         final String uuid = (String) parameters.get(PARAM_UUID);
+        final String pid = (String) parameters.get(PARENT_ROUTE_ID);
 
         ObjectHelper.notNull(rid, PARAM_ROUTE_ID);
         ObjectHelper.notNull(uuid, PARAM_UUID);
@@ -195,6 +198,12 @@ public final class Kamelet {
         def.setNodePrefixId(uuid);
         if (noErrorHandler) {
             def.setErrorHandlerFactory(new NoErrorHandlerBuilder());
+        } else if (pid != null) {
+            ModelCamelContext mcc = (ModelCamelContext) in.getCamelContext();
+            RouteDefinition parent = mcc.getRouteDefinition(pid);
+            if (parent != null) {
+                
def.setErrorHandlerFactory(parent.getErrorHandlerFactory().cloneBuilder());
+            }
         }
 
         if (def.getInput() == null) {
diff --git 
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
 
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
index 586293c5d64..4c54b5b88c6 100644
--- 
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
+++ 
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
@@ -54,6 +54,7 @@ import static 
org.apache.camel.component.kamelet.Kamelet.PARAM_LOCATION;
 import static org.apache.camel.component.kamelet.Kamelet.PARAM_ROUTE_ID;
 import static org.apache.camel.component.kamelet.Kamelet.PARAM_TEMPLATE_ID;
 import static org.apache.camel.component.kamelet.Kamelet.PARAM_UUID;
+import static org.apache.camel.component.kamelet.Kamelet.PARENT_ROUTE_ID;
 
 /**
  * Materialize route templates
@@ -85,8 +86,8 @@ public class KameletComponent extends DefaultComponent {
     private boolean block = true;
     @Metadata(label = "producer", defaultValue = "30000")
     private long timeout = 30000L;
-    @Metadata(label = "advanced", defaultValue = "true")
-    private boolean noErrorHandler = true;
+    @Metadata(label = "advanced")
+    private boolean noErrorHandler;
 
     @Metadata
     private Map<String, Properties> templateProperties;
@@ -272,8 +273,13 @@ public class KameletComponent extends DefaultComponent {
     }
 
     /**
-     * Kamelets, by default, will not do fine-grained error handling, but 
works in no-error-handler mode. This can be
-     * turned off, to use old behaviour in earlier versions of Camel.
+     * Whether kamelets should use error handling or not. By default, the 
Kamelet uses the same error handler as from
+     * the calling route. This means that if the calling route has error 
handling that performs retries, or routing to a
+     * dead letter channel, then the kamelet route will use this also.
+     * <p>
+     * This can be turned off by setting this option to true. If off then the 
kamelet route is not using error handling,
+     * and any exception thrown will for source kamelets be logged by the 
consumer, and the sink/action kamelets will
+     * fail processing.
      */
     public void setNoErrorHandler(boolean noErrorHandler) {
         this.noErrorHandler = noErrorHandler;
@@ -459,6 +465,10 @@ public class KameletComponent extends DefaultComponent {
 
             LOG.debug("Creating route from template={} and id={}", templateId, 
routeId);
             try {
+                // TODO: Nicer API
+                if (parentRouteId != null) {
+                    endpoint.getKameletProperties().put(PARENT_ROUTE_ID, 
parentRouteId);
+                }
                 String id = context.addRouteFromTemplate(routeId, templateId, 
uuid, endpoint.getKameletProperties());
                 RouteDefinition def = context.getRouteDefinition(id);
 
@@ -475,14 +485,6 @@ public class KameletComponent extends DefaultComponent {
             }
         }
 
-        //        @Override
-        //        public void onEndpointAdd(Endpoint endpoint, Route route) {
-        //            if (endpoint instanceof KameletEndpoint ke) {
-        //                String parentRouteId = 
getCamelContext().getCamelContextExtension().getCreateRoutes();
-        //                track(ke, parentRouteId);
-        //            }
-        //        }
-
         @Override
         public void onContextInitialized(CamelContext context) throws 
VetoCamelContextStartException {
             if (this.initialized.compareAndSet(false, true)) {
diff --git 
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java
 
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java
index 0ba9c80057d..bffb41634ff 100644
--- 
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java
+++ 
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.component.kamelet;
 
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -56,8 +55,8 @@ public class KameletEndpoint extends DefaultEndpoint {
     private long timeout = 30000L;
     @UriParam(label = "producer,advanced", defaultValue = "true")
     private boolean failIfNoConsumers = true;
-    @UriParam(label = "advanced", defaultValue = "true")
-    private boolean noErrorHandler = true;
+    @UriParam(label = "advanced")
+    private boolean noErrorHandler;
 
     public KameletEndpoint(String uri,
                            KameletComponent component,
@@ -80,8 +79,13 @@ public class KameletEndpoint extends DefaultEndpoint {
     }
 
     /**
-     * Kamelets, by default, will not do fine-grained error handling, but 
works in no-error-handler mode. This can be
-     * turned off, to use old behaviour in earlier versions of Camel.
+     * Whether kamelets should use error handling or not. By default, the 
Kamelet uses the same error handler as from
+     * the calling route. This means that if the calling route has error 
handling that performs retries, or routing to a
+     * dead letter channel, then the kamelet route will use this also.
+     *
+     * This can be turned off by setting this option to true. If off then the 
kamelet route is not using error handling,
+     * and any exception thrown will for source kamelets be logged by the 
consumer, and the sink/action kamelets will
+     * fail processing.
      */
     public void setNoErrorHandler(boolean noErrorHandler) {
         this.noErrorHandler = noErrorHandler;
@@ -156,7 +160,8 @@ public class KameletEndpoint extends DefaultEndpoint {
     }
 
     public Map<String, Object> getKameletProperties() {
-        return Collections.unmodifiableMap(kameletProperties);
+        return kameletProperties;
+        //        return Collections.unmodifiableMap(kameletProperties);
     }
 
     /**
diff --git 
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletProducer.java
 
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletProducer.java
index 38d291a8fcd..1d219d524d3 100644
--- 
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletProducer.java
+++ 
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletProducer.java
@@ -141,6 +141,7 @@ final class KameletProducer extends DefaultAsyncProducer 
implements RouteIdAware
 
     @Override
     protected void doInit() throws Exception {
+        super.doInit();
         ManagementStrategy ms = 
getEndpoint().getCamelContext().getManagementStrategy();
         if (ms != null && ms.getManagementAgent() != null) {
             registerKamelets = 
ms.getManagementAgent().getRegisterRoutesCreateByKamelet();
diff --git 
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletNoErrorHandlerTest.java
 
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletNoErrorHandlerTest.java
index ba78c36d58a..6f47af18db4 100644
--- 
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletNoErrorHandlerTest.java
+++ 
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletNoErrorHandlerTest.java
@@ -37,7 +37,11 @@ public class KameletNoErrorHandlerTest extends 
CamelTestSupport {
     protected RoutesBuilder createRouteBuilder() {
         return new RouteBuilder() {
             @Override
-            public void configure() {
+            public void configure() throws Exception {
+
+                // enable no error handler
+                customize(KameletComponent.class, k -> 
k.setNoErrorHandler(true));
+
                 routeTemplate("echo")
                         .templateParameter("prefix")
                         .from("kamelet:source")
diff --git 
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletHttpSinkNoErrorHandlerFalseTest.java
 
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletRouteSingleTest.java
similarity index 55%
rename from 
components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletHttpSinkNoErrorHandlerFalseTest.java
rename to 
components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletRouteSingleTest.java
index 303f94ea1fe..13eea44ca3d 100644
--- 
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletHttpSinkNoErrorHandlerFalseTest.java
+++ 
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletRouteSingleTest.java
@@ -16,47 +16,42 @@
  */
 package org.apache.camel.component.kamelet;
 
-import java.net.UnknownHostException;
+import java.util.UUID;
 
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.test.junit5.CamelTestSupport;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
-import static org.junit.jupiter.api.Assertions.fail;
-
-public class KameletHttpSinkNoErrorHandlerFalseTest extends CamelTestSupport {
+import static org.assertj.core.api.Assertions.assertThat;
 
+public class KameletRouteSingleTest extends CamelTestSupport {
     @Test
-    public void testHttpSink() throws Exception {
-        getMockEndpoint("mock:dead").expectedMessageCount(0);
-
-        try {
-            template.sendBody("direct:start", "Hello World");
-            fail("Should throw exception");
-        } catch (Exception e) {
-            assertInstanceOf(UnknownHostException.class, e.getCause());
-        }
+    public void testSingle() {
+        String body = UUID.randomUUID().toString();
 
-        MockEndpoint.assertIsSatisfied(context);
+        assertThat(
+                
fluentTemplate.toF("direct:single").withBody(body).request(String.class)).isEqualTo("a-"
 + body);
     }
 
+    // **********************************************
+    //
+    // test set-up
+    //
+    // **********************************************
+
     @Override
     protected RoutesBuilder createRouteBuilder() {
         return new RouteBuilder() {
             @Override
             public void configure() {
-                routeTemplate("myhttp")
-                        .templateParameter("url")
+                routeTemplate("echo")
+                        .templateParameter("prefix")
                         .from("kamelet:source")
-                        .removeHeaders("*")
-                        .to("{{url}}");
+                        .setBody().simple("{{prefix}}-${body}");
 
-                from("direct:start").routeId("test")
-                        .errorHandler(deadLetterChannel("mock:dead"))
-                        
.kamelet("myhttp?noErrorHandler=false&url=https://webhook.unknownhost.sitessss/";)
+                from("direct:single").routeId("test")
+                        .to("kamelet:echo?prefix=a")
                         .log("${body}");
             }
         };
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
index 03db1eb3f9a..b233de3b765 100644
--- 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
@@ -429,6 +429,8 @@ public class DefaultModel implements Model {
             RouteTemplateContext routeTemplateContext)
             throws Exception {
 
+        String parentRouteId = (String) 
routeTemplateContext.getParameters().remove("_parentRouteId");
+
         RouteTemplateDefinition target = null;
         for (RouteTemplateDefinition def : routeTemplateDefinitions) {
             if (routeTemplateId.equals(def.getId())) {
@@ -515,6 +517,9 @@ public class DefaultModel implements Model {
             }
         }
 
+        if (parentRouteId != null) {
+            prop.put("parentRouteId", parentRouteId);
+        }
         RouteDefinition def = converter.apply(target, prop);
         if (routeId != null) {
             def.setId(routeId);

Reply via email to