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

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

commit 001d2319014baed456fa2b6f02c2dff2f05f2759
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Dec 18 09:39:56 2025 +0100

    CAMEL-22431: camel-once - A component for development to trigger only once
---
 .../org/apache/camel/catalog/components/once.json  |  3 +-
 components/camel-once/pom.xml                      |  6 +++
 .../component/once/OnceComponentConfigurer.java    |  3 ++
 .../org/apache/camel/component/once/once.json      |  3 +-
 .../apache/camel/component/once/OnceComponent.java | 22 +++++++++++
 .../apache/camel/component/once/OnceConsumer.java  |  5 ++-
 .../component/once/OnceBodyAndHeaderTest.java      | 45 ++++++++++++++++++++++
 .../org/apache/camel/component/once/OnceTest.java} | 29 ++++++++------
 .../src/test/resources/log4j2.properties           | 29 ++++++++++++++
 9 files changed, 130 insertions(+), 15 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/once.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/once.json
index 909c42b0f30e..3dbbeeaae5fa 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/once.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/once.json
@@ -25,7 +25,8 @@
   },
   "componentProperties": {
     "bridgeErrorHandler": { "index": 0, "kind": "property", "displayName": 
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Allows for bridging the consumer to the Camel routing Error Handler, which 
mean any exceptions (if possible) occurred while the Camel consumer is trying 
to pickup incoming messages, or the like [...]
-    "autowiredEnabled": { "index": 1, "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 [...]
+    "autowiredEnabled": { "index": 1, "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 [...]
+    "delay": { "index": 2, "kind": "property", "displayName": "Delay", 
"group": "advanced", "label": "advanced", "required": false, "type": "integer", 
"javaType": "long", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 1000, "description": "The number of milliseconds to wait before 
triggering. The default value is 1000." }
   },
   "properties": {
     "name": { "index": 0, "kind": "path", "displayName": "Name", "group": 
"consumer", "label": "", "required": true, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "The logical name" },
diff --git a/components/camel-once/pom.xml b/components/camel-once/pom.xml
index 1e658ef0008c..0a5449df1e9f 100644
--- a/components/camel-once/pom.xml
+++ b/components/camel-once/pom.xml
@@ -39,5 +39,11 @@
             <artifactId>camel-support</artifactId>
         </dependency>
 
+        <!-- testing -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git 
a/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceComponentConfigurer.java
 
b/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceComponentConfigurer.java
index dd779699bc77..9ac516e44da7 100644
--- 
a/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceComponentConfigurer.java
+++ 
b/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceComponentConfigurer.java
@@ -27,6 +27,7 @@ public class OnceComponentConfigurer extends 
PropertyConfigurerSupport implement
         case "autowiredEnabled": 
target.setAutowiredEnabled(property(camelContext, boolean.class, value)); 
return true;
         case "bridgeerrorhandler":
         case "bridgeErrorHandler": 
target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); 
return true;
+        case "delay": target.setDelay(property(camelContext, long.class, 
value)); return true;
         default: return false;
         }
     }
@@ -38,6 +39,7 @@ public class OnceComponentConfigurer extends 
PropertyConfigurerSupport implement
         case "autowiredEnabled": return boolean.class;
         case "bridgeerrorhandler":
         case "bridgeErrorHandler": return boolean.class;
+        case "delay": return long.class;
         default: return null;
         }
     }
@@ -50,6 +52,7 @@ public class OnceComponentConfigurer extends 
PropertyConfigurerSupport implement
         case "autowiredEnabled": return target.isAutowiredEnabled();
         case "bridgeerrorhandler":
         case "bridgeErrorHandler": return target.isBridgeErrorHandler();
+        case "delay": return target.getDelay();
         default: return null;
         }
     }
diff --git 
a/components/camel-once/src/generated/resources/META-INF/org/apache/camel/component/once/once.json
 
b/components/camel-once/src/generated/resources/META-INF/org/apache/camel/component/once/once.json
index 909c42b0f30e..3dbbeeaae5fa 100644
--- 
a/components/camel-once/src/generated/resources/META-INF/org/apache/camel/component/once/once.json
+++ 
b/components/camel-once/src/generated/resources/META-INF/org/apache/camel/component/once/once.json
@@ -25,7 +25,8 @@
   },
   "componentProperties": {
     "bridgeErrorHandler": { "index": 0, "kind": "property", "displayName": 
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Allows for bridging the consumer to the Camel routing Error Handler, which 
mean any exceptions (if possible) occurred while the Camel consumer is trying 
to pickup incoming messages, or the like [...]
-    "autowiredEnabled": { "index": 1, "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 [...]
+    "autowiredEnabled": { "index": 1, "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 [...]
+    "delay": { "index": 2, "kind": "property", "displayName": "Delay", 
"group": "advanced", "label": "advanced", "required": false, "type": "integer", 
"javaType": "long", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 1000, "description": "The number of milliseconds to wait before 
triggering. The default value is 1000." }
   },
   "properties": {
     "name": { "index": 0, "kind": "path", "displayName": "Name", "group": 
"consumer", "label": "", "required": true, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "The logical name" },
diff --git 
a/components/camel-once/src/main/java/org/apache/camel/component/once/OnceComponent.java
 
b/components/camel-once/src/main/java/org/apache/camel/component/once/OnceComponent.java
index bdd0f162b3ad..f050fec0eebf 100644
--- 
a/components/camel-once/src/main/java/org/apache/camel/component/once/OnceComponent.java
+++ 
b/components/camel-once/src/main/java/org/apache/camel/component/once/OnceComponent.java
@@ -19,18 +19,40 @@ package org.apache.camel.component.once;
 import java.util.Map;
 
 import org.apache.camel.Endpoint;
+import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.annotations.Component;
 import org.apache.camel.support.DefaultComponent;
+import org.apache.camel.util.PropertiesHelper;
 
 @Component("once")
 public class OnceComponent extends DefaultComponent {
 
+    @Metadata(label = "advanced", defaultValue = "1000")
+    private long delay = 1000;
+
     // TOOD: option to support groovy/simple language etc
 
     @Override
     protected Endpoint createEndpoint(String uri, String remaining, 
Map<String, Object> parameters) throws Exception {
         OnceEndpoint answer = new OnceEndpoint(uri, this, remaining);
+        answer.setDelay(delay);
+        answer.setHeaders(PropertiesHelper.extractProperties(parameters, 
"header."));
+        answer.setVariables(PropertiesHelper.extractProperties(parameters, 
"variable."));
         setProperties(answer, parameters);
         return answer;
     }
+
+    public long getDelay() {
+        return delay;
+    }
+
+    /**
+     * The number of milliseconds to wait before triggering.
+     * <p/>
+     * The default value is 1000.
+     */
+    public void setDelay(long delay) {
+        this.delay = delay;
+    }
+
 }
diff --git 
a/components/camel-once/src/main/java/org/apache/camel/component/once/OnceConsumer.java
 
b/components/camel-once/src/main/java/org/apache/camel/component/once/OnceConsumer.java
index 934c7b1f21f3..be57bce140f9 100644
--- 
a/components/camel-once/src/main/java/org/apache/camel/component/once/OnceConsumer.java
+++ 
b/components/camel-once/src/main/java/org/apache/camel/component/once/OnceConsumer.java
@@ -73,8 +73,9 @@ public class OnceConsumer extends DefaultConsumer implements 
StartupListener {
     }
 
     protected void scheduleTask(TimerTask task, Timer timer) {
-        LOG.debug("Scheduled once after: {} mills for task: {} ", 
endpoint.getDelay(), task);
-        timer.schedule(task, endpoint.getDelay());
+        long delay = Math.max(0, endpoint.getDelay());
+        LOG.debug("Scheduled once after: {} mills for task: {} ", delay, task);
+        timer.schedule(task, delay);
         scheduled = true;
     }
 
diff --git 
a/components/camel-once/src/test/java/org/apache/camel/component/once/OnceBodyAndHeaderTest.java
 
b/components/camel-once/src/test/java/org/apache/camel/component/once/OnceBodyAndHeaderTest.java
new file mode 100644
index 000000000000..a3c8683715df
--- /dev/null
+++ 
b/components/camel-once/src/test/java/org/apache/camel/component/once/OnceBodyAndHeaderTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.component.once;
+
+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;
+
+public class OnceBodyAndHeaderTest extends CamelTestSupport {
+
+    @Test
+    public void testOnce() throws Exception {
+        getMockEndpoint("mock:result").expectedBodiesReceived("world");
+        getMockEndpoint("mock:result").expectedHeaderReceived("foo", "abc");
+        getMockEndpoint("mock:result").expectedHeaderReceived("bar", "123");
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                
from("once:tick?delay=-1&body=world&header.foo=abc&header.bar=123").to("mock:result");
+            }
+        };
+    }
+}
diff --git 
a/components/camel-once/src/main/java/org/apache/camel/component/once/OnceComponent.java
 
b/components/camel-once/src/test/java/org/apache/camel/component/once/OnceTest.java
similarity index 54%
copy from 
components/camel-once/src/main/java/org/apache/camel/component/once/OnceComponent.java
copy to 
components/camel-once/src/test/java/org/apache/camel/component/once/OnceTest.java
index bdd0f162b3ad..296b18f41559 100644
--- 
a/components/camel-once/src/main/java/org/apache/camel/component/once/OnceComponent.java
+++ 
b/components/camel-once/src/test/java/org/apache/camel/component/once/OnceTest.java
@@ -16,21 +16,28 @@
  */
 package org.apache.camel.component.once;
 
-import java.util.Map;
+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 org.apache.camel.Endpoint;
-import org.apache.camel.spi.annotations.Component;
-import org.apache.camel.support.DefaultComponent;
+public class OnceTest extends CamelTestSupport {
 
-@Component("once")
-public class OnceComponent extends DefaultComponent {
+    @Test
+    public void testOnce() throws Exception {
+        getMockEndpoint("mock:result").expectedBodiesReceived("hello");
 
-    // TOOD: option to support groovy/simple language etc
+        MockEndpoint.assertIsSatisfied(context);
+    }
 
     @Override
-    protected Endpoint createEndpoint(String uri, String remaining, 
Map<String, Object> parameters) throws Exception {
-        OnceEndpoint answer = new OnceEndpoint(uri, this, remaining);
-        setProperties(answer, parameters);
-        return answer;
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("once:tick?body=hello").to("mock:result");
+            }
+        };
     }
 }
diff --git a/components/camel-once/src/test/resources/log4j2.properties 
b/components/camel-once/src/test/resources/log4j2.properties
new file mode 100644
index 000000000000..3e730b946a76
--- /dev/null
+++ b/components/camel-once/src/test/resources/log4j2.properties
@@ -0,0 +1,29 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-once-test.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file

Reply via email to