This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.22.x by this push:
new 806835b6cf60 CAMEL-24410: Fix camel-jbang export redeliveryDelay
Duration parsing (4.22.x backport)
806835b6cf60 is described below
commit 806835b6cf60aa78979917fee66f272fdae6d0c6
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Aug 25 13:19:53 2026 +0200
CAMEL-24410: Fix camel-jbang export redeliveryDelay Duration parsing
(4.22.x backport)
Backport of #25635 to the camel-4.22.x maintenance branch.
camel export failed when YAML routes use Duration-typed options (e.g. quoted
redeliveryDelay: "500"), while camel run worked. During export,
ExportTypeConverter
is registered with TypeConverterExists.Override and replaced the default
String-to-Duration converter but did not handle Duration, causing
NoTypeConversionAvailableException. This adds Duration parsing via
TimeUtils.toDuration.
Cherry-picked cleanly from main (62c0150).
Closes #25661
Co-authored-by: Omar Atie <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
.../camel/dsl/jbang/core/commands/ExportTest.java | 16 ++++
.../route-configuration-redelivery-delay.yaml | 34 ++++++++
.../camel/main/download/ExportTypeConverter.java | 5 ++
.../main/download/ExportTypeConverterTest.java | 96 ++++++++++++++++++++++
4 files changed, 151 insertions(+)
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
index 5cd3ba749d61..d26c54931dff 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
@@ -102,6 +102,22 @@ class ExportTest {
Assertions.assertNotNull(model.getProperties().getProperty("project.build.outputTimestamp"));
}
+ @ParameterizedTest
+ @MethodSource("runtimeProvider")
+ void shouldExportRouteConfigurationWithStringRedeliveryDelay(RuntimeType
rt) throws Exception {
+ LOG.info("shouldExportRouteConfigurationWithStringRedeliveryDelay {}",
rt);
+ Export command = createCommand(rt,
+ new String[] {
"src/test/resources/route-configuration-redelivery-delay.yaml" },
+ "--gav=examples:route:1.0.0", "--dir=" + workingDir,
"--quiet");
+ int exit = command.doCall();
+
+ assertThat(exit).isZero();
+ Model model = readMavenModel();
+ assertThat(model.getGroupId()).isEqualTo("examples");
+ assertThat(model.getArtifactId()).isEqualTo("route");
+ assertThat(model.getVersion()).isEqualTo("1.0.0");
+ }
+
@ParameterizedTest
@MethodSource("runtimeProvider")
public void shouldExportDifferentVersion(RuntimeType rt) throws Exception {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/route-configuration-redelivery-delay.yaml
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/route-configuration-redelivery-delay.yaml
new file mode 100644
index 000000000000..811a9809b897
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/route-configuration-redelivery-delay.yaml
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+- routeConfiguration:
+ errorHandler:
+ id: errorHandlerf23c
+ deadLetterChannel:
+ id: deadLetterChannelda61
+ deadLetterUri: direct:defaultDLQ
+ redeliveryPolicy:
+ id: redeliveryPolicybe2a
+ maximumRedeliveries: 0
+ redeliveryDelay: "500"
+- route:
+ id: testRoute
+ from:
+ uri: direct:start
+ steps:
+ - to:
+ uri: mock:result
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/ExportTypeConverter.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/ExportTypeConverter.java
index 1ec5c51cff7e..ae086b797b3b 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/ExportTypeConverter.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/ExportTypeConverter.java
@@ -16,11 +16,14 @@
*/
package org.apache.camel.main.download;
+import java.time.Duration;
+
import org.apache.camel.Exchange;
import org.apache.camel.TypeConversionException;
import org.apache.camel.converter.ObjectConverter;
import org.apache.camel.support.TypeConverterSupport;
import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.TimeUtils;
/**
* During export then we can be more flexible and allow missing property
placeholders to resolve to a hardcoded value,
@@ -47,6 +50,8 @@ public class ExportTypeConverter extends TypeConverterSupport
{
return (T) ObjectConverter.toByte(s);
} else if (type == String.class) {
return (T) s;
+ } else if (type == Duration.class) {
+ return (T) TimeUtils.toDuration(s);
}
return null;
}
diff --git
a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/ExportTypeConverterTest.java
b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/ExportTypeConverterTest.java
new file mode 100644
index 000000000000..c4d061846ecb
--- /dev/null
+++
b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/ExportTypeConverterTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.download;
+
+import java.time.Duration;
+
+import org.apache.camel.NoTypeConversionAvailableException;
+import org.apache.camel.TypeConverterExists;
+import org.apache.camel.impl.engine.SimpleCamelContext;
+import org.apache.camel.spi.TypeConverterRegistry;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class ExportTypeConverterTest {
+
+ private SimpleCamelContext context;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ context = new SimpleCamelContext();
+ TypeConverterRegistry registry = context.getTypeConverterRegistry();
+ registry.setTypeConverterExists(TypeConverterExists.Override);
+ registry.addTypeConverter(Duration.class, String.class, new
ExportTypeConverter());
+ context.start();
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ if (context != null) {
+ context.stop();
+ }
+ }
+
+ @Test
+ void shouldConvertStringToDurationWhenExportConverterOverridesDefault()
throws Exception {
+ Duration duration =
context.getTypeConverter().mandatoryConvertTo(Duration.class, "500");
+
+ assertEquals(Duration.ofMillis(500), duration);
+ }
+
+ @Test
+ void
shouldConvertDurationTextWithUnitWhenExportConverterOverridesDefault() throws
Exception {
+ Duration duration =
context.getTypeConverter().mandatoryConvertTo(Duration.class, "2s");
+
+ assertEquals(Duration.ofSeconds(2), duration);
+ }
+
+ @Test
+ void shouldFailMandatoryConversionWithoutDurationSupport() {
+ TypeConverterRegistry registry = context.getTypeConverterRegistry();
+ registry.addTypeConverter(Duration.class, String.class, new
ExportTypeConverterWithoutDuration());
+
+ assertThrows(NoTypeConversionAvailableException.class,
+ () ->
context.getTypeConverter().mandatoryConvertTo(Duration.class, "500"));
+ }
+
+ @Test
+ void shouldConvertViaExportTypeConverterDirectly() {
+ ExportTypeConverter converter = new ExportTypeConverter();
+
+ Duration duration = converter.convertTo(Duration.class, null, "500ms");
+
+ assertEquals(Duration.ofMillis(500), duration);
+ }
+
+ /**
+ * Mimics ExportTypeConverter before Duration support was added.
+ */
+ private static final class ExportTypeConverterWithoutDuration extends
ExportTypeConverter {
+ @Override
+ public <T> T convertTo(Class<T> type, org.apache.camel.Exchange
exchange, Object value) {
+ if (type == Duration.class) {
+ return null;
+ }
+ return super.convertTo(type, exchange, value);
+ }
+ }
+}