You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by or...@apache.org on 2023/12/15 02:12:49 UTC

(camel) branch main updated: CAMEL-20225: move time-related information to a separate abstraction

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

orpiske 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 c65a2e6509f CAMEL-20225: move time-related information to a separate abstraction
c65a2e6509f is described below

commit c65a2e6509f4a264cac031e2de013e62c0610fa9
Author: Otavio Rodolfo Piske <an...@gmail.com>
AuthorDate: Tue Dec 12 11:15:51 2023 -0300

    CAMEL-20225: move time-related information to a separate abstraction
---
 .../camel/component/cloudevents/CloudEvent.java    |  2 +-
 .../language/csimple/joor/OriginalSimpleTest.java  |  3 +-
 .../knative/ce/AbstractCloudEventProcessor.java    |  2 +-
 .../src/main/java/org/apache/camel/Clock.java      | 38 +++++++++++++++
 .../src/main/java/org/apache/camel/Exchange.java   |  3 ++
 .../src/main/java/org/apache/camel/Message.java    |  2 +-
 .../main/java/org/apache/camel/PooledExchange.java |  1 +
 .../camel/impl/engine/CamelInternalProcessor.java  |  4 +-
 .../impl/engine/DefaultInflightRepository.java     |  2 +-
 .../camel/impl/engine/PooledExchangeFactory.java   |  7 ++-
 .../engine/PooledProcessorExchangeFactory.java     | 12 ++---
 .../camel/impl/event/ExchangeCompletedEvent.java   | 17 ++-----
 .../component/mock/MockEndpointTimeClauseTest.java |  2 +-
 .../event/EventNotifierExchangeCompletedTest.java  |  2 +-
 .../apache/camel/language/simple/SimpleTest.java   |  3 +-
 .../processor/ExchangeCreatedTimestampTest.java    |  2 +-
 .../apache/camel/support/AbstractExchangeTest.java |  7 +++
 .../management/mbean/ManagedBacklogDebugger.java   |  2 +-
 .../org/apache/camel/support/AbstractExchange.java |  8 +--
 .../org/apache/camel/support/DefaultExchange.java  | 21 ++++++++
 .../camel/support/DefaultPooledExchange.java       | 34 +++++++++++--
 .../org/apache/camel/support/LanguageHelper.java   |  2 +-
 .../org/apache/camel/support/MessageHelper.java    |  8 ++-
 .../org/apache/camel/support/MonotonicClock.java   | 38 +++++++++++++++
 .../org/apache/camel/support/ResetableClock.java   | 57 ++++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_4.adoc     |  2 +
 26 files changed, 228 insertions(+), 53 deletions(-)

diff --git a/components/camel-cloudevents/src/main/java/org/apache/camel/component/cloudevents/CloudEvent.java b/components/camel-cloudevents/src/main/java/org/apache/camel/component/cloudevents/CloudEvent.java
index ba3fc3d430a..9f3d6451e59 100644
--- a/components/camel-cloudevents/src/main/java/org/apache/camel/component/cloudevents/CloudEvent.java
+++ b/components/camel-cloudevents/src/main/java/org/apache/camel/component/cloudevents/CloudEvent.java
@@ -77,7 +77,7 @@ public interface CloudEvent {
      */
     default String getEventTime(Exchange exchange) {
         final ZonedDateTime created
-                = ZonedDateTime.ofInstant(Instant.ofEpochMilli(exchange.getCreated()), ZoneId.systemDefault());
+                = ZonedDateTime.ofInstant(Instant.ofEpochMilli(exchange.getClock().getCreated()), ZoneId.systemDefault());
         return DateTimeFormatter.ISO_INSTANT.format(created);
     }
 
diff --git a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
index 5abd0240c51..e6de82b4906 100644
--- a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
+++ b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
@@ -658,7 +658,8 @@ public class OriginalSimpleTest extends LanguageTestSupport {
 
     @Test
     public void testDateExchangeCreated() {
-        Object out = evaluateExpression("${date:exchangeCreated:hh:mm:ss a}", "" + exchange.getCreated());
+        Object out = evaluateExpression("${date:exchangeCreated:hh:mm:ss a}",
+                String.valueOf(exchange.getClock().getCreated()));
         assertNotNull(out);
     }
 
diff --git a/components/camel-knative/camel-knative-component/src/main/java/org/apache/camel/component/knative/ce/AbstractCloudEventProcessor.java b/components/camel-knative/camel-knative-component/src/main/java/org/apache/camel/component/knative/ce/AbstractCloudEventProcessor.java
index 63813d236c0..3165c2d8911 100644
--- a/components/camel-knative/camel-knative-component/src/main/java/org/apache/camel/component/knative/ce/AbstractCloudEventProcessor.java
+++ b/components/camel-knative/camel-knative-component/src/main/java/org/apache/camel/component/knative/ce/AbstractCloudEventProcessor.java
@@ -123,7 +123,7 @@ abstract class AbstractCloudEventProcessor implements CloudEventProcessor {
             setCloudEventHeader(headers, CloudEvent.CAMEL_CLOUD_EVENT_VERSION, ce::version);
             setCloudEventHeader(headers, CloudEvent.CAMEL_CLOUD_EVENT_TIME, () -> {
                 final ZonedDateTime created
-                        = ZonedDateTime.ofInstant(Instant.ofEpochMilli(exchange.getCreated()), ZoneId.systemDefault());
+                        = ZonedDateTime.ofInstant(Instant.ofEpochMilli(exchange.getClock().getCreated()), ZoneId.systemDefault());
 
                 return DateTimeFormatter.ISO_INSTANT.format(created);
             });
diff --git a/core/camel-api/src/main/java/org/apache/camel/Clock.java b/core/camel-api/src/main/java/org/apache/camel/Clock.java
new file mode 100644
index 00000000000..f19ecd35a07
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/Clock.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+/**
+ * A clock used to track the lifetime of an exchange
+ */
+public interface Clock {
+    /**
+     * The elapsed time since the creation of the exchange
+     *
+     * @return The elapsed time, in milliseconds, since the creation of the exchange
+     */
+    long elapsed();
+
+    /**
+     * The point in time the exchange was created
+     *
+     * @return The point in time, in milliseconds, the exchange was created.
+     * @see    System#currentTimeMillis()
+     */
+    long getCreated();
+}
diff --git a/core/camel-api/src/main/java/org/apache/camel/Exchange.java b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
index 0db1ae54850..f8390711f73 100644
--- a/core/camel-api/src/main/java/org/apache/camel/Exchange.java
+++ b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
@@ -662,6 +662,7 @@ public interface Exchange {
      *
      * @see Message#getMessageTimestamp()
      */
+    @Deprecated
     long getCreated();
 
     /**
@@ -672,4 +673,6 @@ public interface Exchange {
      */
     ExchangeExtension getExchangeExtension();
 
+    Clock getClock();
+
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/Message.java b/core/camel-api/src/main/java/org/apache/camel/Message.java
index b4d4da50eb9..02ab5e15d31 100644
--- a/core/camel-api/src/main/java/org/apache/camel/Message.java
+++ b/core/camel-api/src/main/java/org/apache/camel/Message.java
@@ -64,7 +64,7 @@ public interface Message {
      * components when the consumer is able to extract the timestamp from the source event.
      *
      * @return the timestamp, or <tt>0</tt> if the message has no source timestamp.
-     * @see    Exchange#getCreated()
+     * @see    Exchange#getClock()
      */
     long getMessageTimestamp();
 
diff --git a/core/camel-api/src/main/java/org/apache/camel/PooledExchange.java b/core/camel-api/src/main/java/org/apache/camel/PooledExchange.java
index a1b85aa73ec..ae1c075baba 100644
--- a/core/camel-api/src/main/java/org/apache/camel/PooledExchange.java
+++ b/core/camel-api/src/main/java/org/apache/camel/PooledExchange.java
@@ -52,6 +52,7 @@ public interface PooledExchange extends Exchange {
      * <p/>
      * <b>Important:</b> This API is NOT intended for Camel end users, but used internally by Camel itself.
      */
+    @Deprecated
     void reset(long created);
 
     /**
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
index 48c3b610da9..9a581058ba2 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
@@ -648,7 +648,7 @@ public class CamelInternalProcessor extends DelegateAsyncProcessor implements In
                 if (first) {
                     // use route as pseudo source when first
                     String source = LoggerHelper.getLineNumberLoggerName(routeDefinition);
-                    long created = exchange.getCreated();
+                    final long created = exchange.getClock().getCreated();
                     DefaultBacklogTracerEventMessage pseudoFirst = new DefaultBacklogTracerEventMessage(
                             true, false, backlogTracer.incrementTraceCounter(), created, source, routeId, null, exchangeId,
                             rest, template, messageAsXml, messageAsJSon);
@@ -675,7 +675,7 @@ public class CamelInternalProcessor extends DelegateAsyncProcessor implements In
                     String routeId = routeDefinition != null ? routeDefinition.getRouteId() : null;
                     String exchangeId = exchange.getExchangeId();
                     boolean includeExchangeProperties = backlogTracer.isIncludeExchangeProperties();
-                    long created = exchange.getCreated();
+                    long created = exchange.getClock().getCreated();
                     String messageAsXml = MessageHelper.dumpAsXml(exchange.getIn(), includeExchangeProperties, true, 4,
                             true, backlogTracer.isBodyIncludeStreams(), backlogTracer.isBodyIncludeFiles(),
                             backlogTracer.getBodyMaxChars());
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInflightRepository.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInflightRepository.java
index dce5ccc589f..44483efcb7e 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInflightRepository.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInflightRepository.java
@@ -207,7 +207,7 @@ public class DefaultInflightRepository extends ServiceSupport implements Infligh
     }
 
     private static long getExchangeDuration(Exchange exchange) {
-        return System.currentTimeMillis() - exchange.getCreated();
+        return exchange.getClock().elapsed();
     }
 
     private static final class InflightExchangeEntry implements InflightExchange {
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/PooledExchangeFactory.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/PooledExchangeFactory.java
index 7468e4f9c58..f072adbf0fd 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/PooledExchangeFactory.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/PooledExchangeFactory.java
@@ -22,6 +22,7 @@ import org.apache.camel.Exchange;
 import org.apache.camel.PooledExchange;
 import org.apache.camel.spi.ExchangeFactory;
 import org.apache.camel.support.DefaultPooledExchange;
+import org.apache.camel.support.ResetableClock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -66,8 +67,7 @@ public final class PooledExchangeFactory extends PrototypeExchangeFactory {
         }
 
         // reset exchange for reuse
-        PooledExchange ee = (PooledExchange) exchange;
-        ee.reset(System.currentTimeMillis());
+        ((ResetableClock) exchange.getClock()).reset();
 
         return exchange;
     }
@@ -88,8 +88,7 @@ public final class PooledExchangeFactory extends PrototypeExchangeFactory {
         }
 
         // reset exchange for reuse
-        PooledExchange ee = (PooledExchange) exchange;
-        ee.reset(System.currentTimeMillis());
+        ((ResetableClock) exchange.getClock()).reset();
 
         return exchange;
     }
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/PooledProcessorExchangeFactory.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/PooledProcessorExchangeFactory.java
index 634d2db3cd7..2f5b96f8c3d 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/PooledProcessorExchangeFactory.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/PooledProcessorExchangeFactory.java
@@ -25,6 +25,7 @@ import org.apache.camel.Processor;
 import org.apache.camel.spi.ProcessorExchangeFactory;
 import org.apache.camel.support.DefaultPooledExchange;
 import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.support.ResetableClock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -72,9 +73,7 @@ public class PooledProcessorExchangeFactory extends PrototypeProcessorExchangeFa
         }
 
         // reset exchange for reuse
-        PooledExchange ee = (PooledExchange) answer;
-        ee.reset(System.currentTimeMillis());
-
+        ((ResetableClock) exchange.getClock()).reset();
         ExchangeHelper.copyResults(answer, exchange);
         return answer;
     }
@@ -98,8 +97,7 @@ public class PooledProcessorExchangeFactory extends PrototypeProcessorExchangeFa
         }
 
         // reset exchange for reuse
-        PooledExchange ee = (PooledExchange) answer;
-        ee.reset(System.currentTimeMillis());
+        ((ResetableClock) exchange.getClock()).reset();
 
         ExchangeHelper.copyResults(answer, exchange);
         // do not reuse message id on copy
@@ -129,9 +127,7 @@ public class PooledProcessorExchangeFactory extends PrototypeProcessorExchangeFa
         }
 
         // reset exchange for reuse
-        PooledExchange ee = (PooledExchange) answer;
-        ee.reset(System.currentTimeMillis());
-
+        ((ResetableClock) answer.getClock()).reset();
         return answer;
     }
 
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/ExchangeCompletedEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/ExchangeCompletedEvent.java
index 4c7dcd1ea32..91ab9cf3470 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/ExchangeCompletedEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/ExchangeCompletedEvent.java
@@ -26,21 +26,14 @@ public class ExchangeCompletedEvent extends AbstractExchangeEvent implements Cam
 
     public ExchangeCompletedEvent(Exchange source) {
         super(source);
-        long created = getExchange().getCreated();
-        if (created > 0) {
-            this.timeTaken = System.currentTimeMillis() - created;
-        } else {
-            this.timeTaken = -1;
-        }
+
+        this.timeTaken = getExchange().getClock().elapsed();
     }
 
     @Override
     public String toString() {
-        if (timeTaken > -1) {
-            return getExchange().getExchangeId() + " exchange completed"
-                   + " took: " + TimeUtils.printDuration(timeTaken, true);
-        } else {
-            return getExchange().getExchangeId() + " exchange completed";
-        }
+        return getExchange().getExchangeId() + " exchange completed"
+               + " took: " + TimeUtils.printDuration(timeTaken, true);
+
     }
 }
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/mock/MockEndpointTimeClauseTest.java b/core/camel-core/src/test/java/org/apache/camel/component/mock/MockEndpointTimeClauseTest.java
index 6be1d2b3c79..bb1cb10226e 100644
--- a/core/camel-core/src/test/java/org/apache/camel/component/mock/MockEndpointTimeClauseTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/component/mock/MockEndpointTimeClauseTest.java
@@ -36,7 +36,7 @@ public class MockEndpointTimeClauseTest extends ContextTestSupport {
     @Test
     public void testReceivedTimestamp() throws Exception {
         MockEndpoint mock = getMockEndpoint("mock:result");
-        mock.message(0).predicate(e -> e.getCreated() > 0);
+        mock.message(0).predicate(e -> e.getClock().getCreated() > 0);
         mock.message(0).exchangeProperty(Exchange.RECEIVED_TIMESTAMP).isNotNull();
         mock.message(0).exchangeProperty(Exchange.RECEIVED_TIMESTAMP).isInstanceOf(Date.class);
 
diff --git a/core/camel-core/src/test/java/org/apache/camel/impl/event/EventNotifierExchangeCompletedTest.java b/core/camel-core/src/test/java/org/apache/camel/impl/event/EventNotifierExchangeCompletedTest.java
index e43d1adf263..3cfe101fe43 100644
--- a/core/camel-core/src/test/java/org/apache/camel/impl/event/EventNotifierExchangeCompletedTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/impl/event/EventNotifierExchangeCompletedTest.java
@@ -70,7 +70,7 @@ public class EventNotifierExchangeCompletedTest extends ContextTestSupport {
         assertEquals("direct://start", event.getExchange().getFromEndpoint().getEndpointUri());
 
         // grab the created timestamp
-        long created = event.getExchange().getCreated();
+        long created = event.getExchange().getClock().getCreated();
         assertTrue(created > 0);
 
         // calculate elapsed time
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
index 4e54af27e31..f6d5e1a8586 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
@@ -650,7 +650,8 @@ public class SimpleTest extends LanguageTestSupport {
 
     @Test
     public void testDateExchangeCreated() throws Exception {
-        Object out = evaluateExpression("${date:exchangeCreated:hh:mm:ss a}", ("" + exchange.getCreated()).getClass());
+        Object out
+                = evaluateExpression("${date:exchangeCreated:hh:mm:ss a}", ("" + exchange.getClock().getCreated()).getClass());
         assertNotNull(out);
     }
 
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/ExchangeCreatedTimestampTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/ExchangeCreatedTimestampTest.java
index b671336b19f..b352479fb31 100644
--- a/core/camel-core/src/test/java/org/apache/camel/processor/ExchangeCreatedTimestampTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/ExchangeCreatedTimestampTest.java
@@ -34,7 +34,7 @@ public class ExchangeCreatedTimestampTest extends ContextTestSupport {
 
         assertMockEndpointsSatisfied();
 
-        long created = mock.getReceivedExchanges().get(0).getCreated();
+        long created = mock.getReceivedExchanges().get(0).getClock().getCreated();
         assertTrue(created > 0);
     }
 
diff --git a/core/camel-core/src/test/java/org/apache/camel/support/AbstractExchangeTest.java b/core/camel-core/src/test/java/org/apache/camel/support/AbstractExchangeTest.java
index 2710b25b52f..8be0bc8b500 100644
--- a/core/camel-core/src/test/java/org/apache/camel/support/AbstractExchangeTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/support/AbstractExchangeTest.java
@@ -17,6 +17,7 @@
 package org.apache.camel.support;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.Clock;
 import org.apache.camel.Exchange;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.spi.DataType;
@@ -32,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertSame;
 public class AbstractExchangeTest {
 
     static class CustomAbstractExchange extends AbstractExchange {
+        private final Clock clock = new MonotonicClock();
 
         CustomAbstractExchange(CustomAbstractExchange abstractExchange) {
             super(abstractExchange);
@@ -45,6 +47,11 @@ public class AbstractExchangeTest {
         AbstractExchange newCopy() {
             return new CustomAbstractExchange(this);
         }
+
+        @Override
+        public Clock getClock() {
+            return clock;
+        }
     }
 
     @Test
diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java
index a89d53da607..b07f6ee39ee 100644
--- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java
+++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java
@@ -394,7 +394,7 @@ public class ManagedBacklogDebugger implements ManagedBacklogDebuggerMBean {
                             + "]";
                 }
 
-                long elapsed = TimeUtils.elapsedMillisSince(suspendedExchange.getCreated());
+                long elapsed = TimeUtils.elapsedMillisSince(suspendedExchange.getClock().getCreated());
 
                 messageHistoryBuilder
                         .append("    <messageHistoryEntry")
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java b/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
index 42ff7b76398..df3ed17dcc3 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
@@ -53,7 +53,6 @@ abstract class AbstractExchange implements Exchange {
 
     protected final CamelContext context;
     protected Map<String, Object> properties; // create properties on-demand as we use internal properties mostly
-    protected long created;
     protected Message in;
     protected Message out;
     protected Exception exception;
@@ -81,7 +80,6 @@ abstract class AbstractExchange implements Exchange {
     public AbstractExchange(CamelContext context, ExchangePattern pattern) {
         this.context = context;
         this.pattern = pattern;
-        this.created = System.currentTimeMillis();
 
         internalProperties = new EnumMap<>(ExchangePropertyKey.class);
         privateExtension = new ExtendedExchangeExtension(this);
@@ -90,7 +88,6 @@ abstract class AbstractExchange implements Exchange {
     public AbstractExchange(Exchange parent) {
         this.context = parent.getContext();
         this.pattern = parent.getPattern();
-        this.created = parent.getCreated();
 
         internalProperties = new EnumMap<>(ExchangePropertyKey.class);
 
@@ -103,7 +100,6 @@ abstract class AbstractExchange implements Exchange {
     AbstractExchange(AbstractExchange parent) {
         this.context = parent.getContext();
         this.pattern = parent.getPattern();
-        this.created = parent.getCreated();
 
         this.internalProperties = new EnumMap<>(parent.internalProperties);
 
@@ -140,7 +136,6 @@ abstract class AbstractExchange implements Exchange {
     public AbstractExchange(Endpoint fromEndpoint) {
         this.context = fromEndpoint.getCamelContext();
         this.pattern = fromEndpoint.getExchangePattern();
-        this.created = System.currentTimeMillis();
 
         internalProperties = new EnumMap<>(ExchangePropertyKey.class);
         privateExtension = new ExtendedExchangeExtension(this);
@@ -150,7 +145,6 @@ abstract class AbstractExchange implements Exchange {
     public AbstractExchange(Endpoint fromEndpoint, ExchangePattern pattern) {
         this.context = fromEndpoint.getCamelContext();
         this.pattern = pattern;
-        this.created = System.currentTimeMillis();
 
         internalProperties = new EnumMap<>(ExchangePropertyKey.class);
         privateExtension = new ExtendedExchangeExtension(this);
@@ -159,7 +153,7 @@ abstract class AbstractExchange implements Exchange {
 
     @Override
     public long getCreated() {
-        return created;
+        return getClock().getCreated();
     }
 
     abstract AbstractExchange newCopy();
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/DefaultExchange.java b/core/camel-support/src/main/java/org/apache/camel/support/DefaultExchange.java
index 91cb6c882b0..51bc0669bdb 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/DefaultExchange.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/DefaultExchange.java
@@ -20,6 +20,7 @@ import java.util.EnumMap;
 import java.util.Map;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.Clock;
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
@@ -29,38 +30,58 @@ import org.apache.camel.ExchangePropertyKey;
  * The default and only implementation of {@link Exchange}.
  */
 public final class DefaultExchange extends AbstractExchange {
+    private final Clock timeInfo;
 
     DefaultExchange(CamelContext context, EnumMap<ExchangePropertyKey, Object> internalProperties,
                     Map<String, Object> properties) {
         super(context, internalProperties, properties);
+
+        this.timeInfo = new MonotonicClock();
     }
 
     public DefaultExchange(CamelContext context) {
         super(context);
+
+        this.timeInfo = new MonotonicClock();
     }
 
     public DefaultExchange(CamelContext context, ExchangePattern pattern) {
         super(context, pattern);
+
+        this.timeInfo = new MonotonicClock();
     }
 
     public DefaultExchange(Exchange parent) {
         super(parent);
+
+        this.timeInfo = parent.getClock();
     }
 
     DefaultExchange(AbstractExchange parent) {
         super(parent);
+
+        this.timeInfo = parent.getClock();
     }
 
     public DefaultExchange(Endpoint fromEndpoint) {
         super(fromEndpoint);
+
+        this.timeInfo = new MonotonicClock();
     }
 
     public DefaultExchange(Endpoint fromEndpoint, ExchangePattern pattern) {
         super(fromEndpoint, pattern);
+
+        this.timeInfo = new MonotonicClock();
     }
 
     @Override
     AbstractExchange newCopy() {
         return new DefaultExchange(this);
     }
+
+    @Override
+    public Clock getClock() {
+        return timeInfo;
+    }
 }
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/DefaultPooledExchange.java b/core/camel-support/src/main/java/org/apache/camel/support/DefaultPooledExchange.java
index 19c757c1309..286875aed55 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/DefaultPooledExchange.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/DefaultPooledExchange.java
@@ -19,6 +19,7 @@ package org.apache.camel.support;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.Clock;
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
@@ -29,7 +30,7 @@ import org.apache.camel.PooledExchange;
  * The default and only implementation of {@link PooledExchange}.
  */
 public final class DefaultPooledExchange extends AbstractExchange implements PooledExchange {
-
+    private final ResetableClock clock;
     private OnDoneTask onDone;
     private Class<?> originalInClassType;
     private Message originalOut;
@@ -40,30 +41,46 @@ public final class DefaultPooledExchange extends AbstractExchange implements Poo
         super(context);
         this.originalPattern = getPattern();
         this.properties = new ConcurrentHashMap<>(8);
+
+        this.clock = new ResetableClock();
     }
 
     public DefaultPooledExchange(CamelContext context, ExchangePattern pattern) {
         super(context, pattern);
         this.originalPattern = pattern;
         this.properties = new ConcurrentHashMap<>(8);
+
+        this.clock = new ResetableClock();
     }
 
     public DefaultPooledExchange(Exchange parent) {
         super(parent);
         this.originalPattern = parent.getPattern();
         this.properties = new ConcurrentHashMap<>(8);
+
+        Clock parentClock = parent.getClock();
+
+        if (parentClock instanceof ResetableClock rs) {
+            this.clock = rs;
+        } else {
+            this.clock = new ResetableClock(parent.getClock());
+        }
     }
 
     public DefaultPooledExchange(Endpoint fromEndpoint) {
         super(fromEndpoint);
         this.originalPattern = getPattern();
         this.properties = new ConcurrentHashMap<>(8);
+
+        this.clock = new ResetableClock();
     }
 
     public DefaultPooledExchange(Endpoint fromEndpoint, ExchangePattern pattern) {
         super(fromEndpoint, pattern);
         this.originalPattern = pattern;
         this.properties = new ConcurrentHashMap<>(8);
+
+        this.clock = new ResetableClock();
     }
 
     @Override
@@ -86,8 +103,10 @@ public final class DefaultPooledExchange extends AbstractExchange implements Poo
     }
 
     public void done() {
-        if (created > 0) {
-            this.created = 0; // by setting to 0 we also flag that this exchange is done and needs to be reset to use again
+        if (clock.getCreated() > 0) {
+            // by unsetting (setting to 0) we also flag that this exchange is done and needs to be reset to use again
+            clock.unset();
+
             this.properties.clear();
             internalProperties.clear();
             if (this.safeCopyProperties != null) {
@@ -117,12 +136,14 @@ public final class DefaultPooledExchange extends AbstractExchange implements Poo
             if (onDone != null) {
                 onDone.onDone(this);
             }
+
         }
     }
 
     @Override
+    @Deprecated
     public void reset(long created) {
-        this.created = created;
+        clock.reset();
     }
 
     @Override
@@ -169,4 +190,9 @@ public final class DefaultPooledExchange extends AbstractExchange implements Poo
         }
     }
 
+    @Override
+    public Clock getClock() {
+        return clock;
+    }
+
 }
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/LanguageHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/LanguageHelper.java
index c31913beecd..65c19334c8e 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/LanguageHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/LanguageHelper.java
@@ -263,7 +263,7 @@ public final class LanguageHelper {
      * @return          A Date instance
      */
     public static Date dateFromExchangeCreated(Exchange exchange) {
-        long num = exchange.getCreated();
+        long num = exchange.getClock().getCreated();
         return new Date(num);
     }
 
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
index 7760d70ca8a..4382bb347d4 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
@@ -716,7 +716,7 @@ public final class MessageHelper {
             label = "from[" + URISupport.sanitizeUri(StringHelper.limitLength(exchange.getFromEndpoint().getEndpointUri(), 100))
                     + "]";
         }
-        long elapsed = System.currentTimeMillis() - exchange.getCreated();
+        final long elapsed = exchange.getClock().elapsed();
 
         List<MessageHistory> list = exchange.getProperty(ExchangePropertyKey.MESSAGE_HISTORY, List.class);
         boolean enabled = list != null;
@@ -768,9 +768,8 @@ public final class MessageHelper {
                 // fast
                 label = URISupport.sanitizeUri(StringHelper.limitLength(label, 100));
                 // we do not have elapsed time
-                elapsed = 0;
                 sb.append("\t...\n");
-                sb.append(String.format(goMessageHistoryOutput, loc, routeId + "/" + id, label, elapsed));
+                sb.append(String.format(goMessageHistoryOutput, loc, routeId + "/" + id, label, 0));
                 sb.append("\n");
             }
         } else {
@@ -790,9 +789,8 @@ public final class MessageHelper {
                 // characters in the sanitizeUri method and will be reasonably
                 // fast
                 label = URISupport.sanitizeUri(StringHelper.limitLength(history.getNode().getLabel(), 100));
-                elapsed = history.getElapsed();
 
-                sb.append(String.format(goMessageHistoryOutput, loc, routeId + "/" + id, label, elapsed));
+                sb.append(String.format(goMessageHistoryOutput, loc, routeId + "/" + id, label, history.getElapsed()));
                 sb.append("\n");
             }
         }
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/MonotonicClock.java b/core/camel-support/src/main/java/org/apache/camel/support/MonotonicClock.java
new file mode 100644
index 00000000000..3a3ca3f482b
--- /dev/null
+++ b/core/camel-support/src/main/java/org/apache/camel/support/MonotonicClock.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.camel.Clock;
+
+public class MonotonicClock implements Clock {
+    private final long created;
+
+    MonotonicClock() {
+        this.created = System.currentTimeMillis();
+    }
+
+    @Override
+    public long elapsed() {
+        return System.currentTimeMillis() - created;
+    }
+
+    @Override
+    public long getCreated() {
+        return created;
+    }
+}
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/ResetableClock.java b/core/camel-support/src/main/java/org/apache/camel/support/ResetableClock.java
new file mode 100644
index 00000000000..cabf9c776fc
--- /dev/null
+++ b/core/camel-support/src/main/java/org/apache/camel/support/ResetableClock.java
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.camel.Clock;
+
+public final class ResetableClock implements Clock {
+    private long created;
+
+    ResetableClock(Clock clock) {
+        this.created = clock.getCreated();
+    }
+
+    ResetableClock() {
+        this.created = System.currentTimeMillis();
+    }
+
+    @Override
+    public long elapsed() {
+        return System.currentTimeMillis() - created;
+    }
+
+    @Override
+    public long getCreated() {
+        return created;
+    }
+
+    /**
+     * Reset the clock to the current point in time
+     */
+    public void reset() {
+        this.created = System.currentTimeMillis();
+    }
+
+    /**
+     * Unset the clock (set to zero). This is part of the pooling exchange support, so that the exchange can be marked
+     * as done and reused
+     */
+    void unset() {
+        this.created = 0;
+    }
+}
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc
index e9d33a5b449..8b79b865cd3 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc
@@ -11,6 +11,8 @@ from both 4.0 to 4.1 and 4.1 to 4.2.
 Removed the deprecated constructor from the internal class `org.apache.camel.util.StopWatch`. Users of this class are advised to
 use the default constructor if necessary.
 
+The method `getCreated` is now deprecated. Access to the time-related information from the exchange should be done via `getClock`.
+
 === camel-azure-cosmosdb
 
 The useDefaultIdentity parameter has been removed in favor of the credentialType parameter. Now user should select between SHARED_ACCOUNT_KEY and AZURE_IDENTITY.