This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch backport/25412-to-camel-4.22.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit 7b1c0c65e465d5a9b6508db5d2d45838b0ed32d9 Author: Claus Ibsen <[email protected]> AuthorDate: Tue Aug 11 13:48:39 2026 +0200 CAMEL-24375: parseDuration should handle plain millis value Consolidate duration parsing into TimeUtils.toDuration() so both CamelContextHelper.parseDuration() and DurationConverter.toDuration() delegate to a single implementation that handles plain millis, human- readable (20s, 1m30s), and ISO-8601 (PT20S) formats. This fixes NoTypeConversionAvailableException when parsing duration values during early route initialization before the type converter registry is loaded. Closes #25412 Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../apache/camel/converter/DurationConverter.java | 6 +-- .../CamelContextHelperParseDurationTest.java | 63 ++++++++++++++++++++++ .../apache/camel/support/CamelContextHelper.java | 10 +++- .../main/java/org/apache/camel/util/TimeUtils.java | 6 ++- .../java/org/apache/camel/util/TimeUtilsTest.java | 30 +++++++++++ 5 files changed, 108 insertions(+), 7 deletions(-) diff --git a/core/camel-base/src/main/java/org/apache/camel/converter/DurationConverter.java b/core/camel-base/src/main/java/org/apache/camel/converter/DurationConverter.java index 68e3600338f8..e23470490517 100644 --- a/core/camel-base/src/main/java/org/apache/camel/converter/DurationConverter.java +++ b/core/camel-base/src/main/java/org/apache/camel/converter/DurationConverter.java @@ -45,11 +45,7 @@ public final class DurationConverter { @Converter(order = 3) public static Duration toDuration(String source) { - if (source.startsWith("P") || source.startsWith("-P") || source.startsWith("p") || source.startsWith("-p")) { - return Duration.parse(source); - } else { - return Duration.ofMillis(TimeUtils.toMilliSeconds(source)); - } + return TimeUtils.toDuration(source); } @Converter(order = 4) diff --git a/core/camel-core/src/test/java/org/apache/camel/support/CamelContextHelperParseDurationTest.java b/core/camel-core/src/test/java/org/apache/camel/support/CamelContextHelperParseDurationTest.java new file mode 100644 index 000000000000..eb75b198cf89 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/support/CamelContextHelperParseDurationTest.java @@ -0,0 +1,63 @@ +/* + * 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.support; + +import java.time.Duration; + +import org.apache.camel.ContextTestSupport; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CamelContextHelperParseDurationTest extends ContextTestSupport { + + @Test + void testParseDurationMillis() { + Duration d = CamelContextHelper.parseDuration(context, "20000"); + assertThat(d).isEqualTo(Duration.ofMillis(20000)); + } + + @Test + void testParseDurationHumanReadable() { + Duration d = CamelContextHelper.parseDuration(context, "20s"); + assertThat(d).isEqualTo(Duration.ofSeconds(20)); + } + + @Test + void testParseDurationHumanReadableMinutes() { + Duration d = CamelContextHelper.parseDuration(context, "1m30s"); + assertThat(d).isEqualTo(Duration.ofSeconds(90)); + } + + @Test + void testParseDurationISO8601() { + Duration d = CamelContextHelper.parseDuration(context, "PT20S"); + assertThat(d).isEqualTo(Duration.ofSeconds(20)); + } + + @Test + void testParseDurationISO8601Lowercase() { + Duration d = CamelContextHelper.parseDuration(context, "pt20s"); + assertThat(d).isEqualTo(Duration.ofSeconds(20)); + } + + @Test + void testParseDurationNull() { + Duration d = CamelContextHelper.parseDuration(context, null); + assertThat(d).isNull(); + } +} diff --git a/core/camel-support/src/main/java/org/apache/camel/support/CamelContextHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/CamelContextHelper.java index 7e8b29c80caa..2148b81353a3 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/CamelContextHelper.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/CamelContextHelper.java @@ -461,7 +461,15 @@ public final class CamelContextHelper { * @throws IllegalStateException is thrown if illegal argument or type conversion not possible */ public static Duration parseDuration(CamelContext camelContext, String text) { - return parse(camelContext, Duration.class, text); + if (text == null) { + return null; + } + // ensure we support property placeholders + String s = camelContext.resolvePropertyPlaceholders(text); + if (s == null) { + return null; + } + return TimeUtils.toDuration(s); } /** diff --git a/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java b/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java index 30d345bf906b..41c264f99190 100644 --- a/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java +++ b/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java @@ -173,7 +173,11 @@ public final class TimeUtils { * @param source duration which can be in text format such as 15s */ public static Duration toDuration(String source) { - return Duration.ofMillis(toMilliSeconds(source)); + if (source.startsWith("P") || source.startsWith("-P") || source.startsWith("p") || source.startsWith("-p")) { + return Duration.parse(source); + } else { + return Duration.ofMillis(toMilliSeconds(source)); + } } /** diff --git a/core/camel-util/src/test/java/org/apache/camel/util/TimeUtilsTest.java b/core/camel-util/src/test/java/org/apache/camel/util/TimeUtilsTest.java index f6f5cae97c96..aae6263ea47f 100644 --- a/core/camel-util/src/test/java/org/apache/camel/util/TimeUtilsTest.java +++ b/core/camel-util/src/test/java/org/apache/camel/util/TimeUtilsTest.java @@ -72,6 +72,36 @@ public class TimeUtilsTest { assertEquals(time, time2); } + @Test + public void testToDurationMillis() { + assertEquals(Duration.ofMillis(20000), TimeUtils.toDuration("20000")); + } + + @Test + public void testToDurationHumanReadable() { + assertEquals(Duration.ofSeconds(20), TimeUtils.toDuration("20s")); + } + + @Test + public void testToDurationHumanReadableMinutes() { + assertEquals(Duration.ofSeconds(90), TimeUtils.toDuration("1m30s")); + } + + @Test + public void testToDurationISO8601() { + assertEquals(Duration.ofSeconds(20), TimeUtils.toDuration("PT20S")); + } + + @Test + public void testToDurationISO8601Lowercase() { + assertEquals(Duration.ofSeconds(20), TimeUtils.toDuration("pt20s")); + } + + @Test + public void testToDurationISO8601Negative() { + assertEquals(Duration.ofSeconds(-30), TimeUtils.toDuration("-PT30S")); + } + @Test void testDurationMatchesExpectWithDate() throws InterruptedException { Date startTime = new Date();
