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 43eea0b3cabb7e6f1f07c93de42eff4e75881cf1
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Dec 18 07:20:53 2025 +0100

    CAMEL-22431: camel-once - A component for development to trigger only once
---
 .../org/apache/camel/catalog/components/once.json  |  9 +--
 .../component/once/OnceEndpointConfigurer.java     |  3 +
 .../component/once/OnceEndpointUriFactory.java     |  3 +-
 .../org/apache/camel/component/once/once.json      |  9 +--
 .../apache/camel/component/once/OnceComponent.java |  2 +
 .../apache/camel/component/once/OnceConsumer.java  | 83 ++++++++++++++--------
 .../apache/camel/component/once/OnceEndpoint.java  | 15 ++++
 .../endpoint/dsl/OnceEndpointBuilderFactory.java   | 19 +++++
 8 files changed, 103 insertions(+), 40 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 7ed47f77eb6c..690afc34f39c 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
@@ -29,9 +29,10 @@
   },
   "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" },
-    "delay": { "index": 1, "kind": "parameter", "displayName": "Delay", 
"group": "consumer", "label": "", "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." },
-    "bridgeErrorHandler": { "index": 2, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "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  [...]
-    "exceptionHandler": { "index": 3, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By def [...]
-    "exchangePattern": { "index": 4, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." }
+    "body": { "index": 1, "kind": "parameter", "displayName": "Body", "group": 
"consumer", "label": "", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "supportFileReference": true, "description": "The data 
to use as message body. You can externalize the data by using file: or 
classpath: as prefix and specify the location of the file." },
+    "delay": { "index": 2, "kind": "parameter", "displayName": "Delay", 
"group": "consumer", "label": "", "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." },
+    "bridgeErrorHandler": { "index": 3, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "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  [...]
+    "exceptionHandler": { "index": 4, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By def [...]
+    "exchangePattern": { "index": 5, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." }
   }
 }
diff --git 
a/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceEndpointConfigurer.java
 
b/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceEndpointConfigurer.java
index 6a77ea9ceac2..629bf407ee8e 100644
--- 
a/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceEndpointConfigurer.java
+++ 
b/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceEndpointConfigurer.java
@@ -23,6 +23,7 @@ public class OnceEndpointConfigurer extends 
PropertyConfigurerSupport implements
     public boolean configure(CamelContext camelContext, Object obj, String 
name, Object value, boolean ignoreCase) {
         OnceEndpoint target = (OnceEndpoint) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "body": target.setBody(property(camelContext, 
java.lang.String.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;
@@ -37,6 +38,7 @@ public class OnceEndpointConfigurer extends 
PropertyConfigurerSupport implements
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "body": return java.lang.String.class;
         case "bridgeerrorhandler":
         case "bridgeErrorHandler": return boolean.class;
         case "delay": return long.class;
@@ -52,6 +54,7 @@ public class OnceEndpointConfigurer extends 
PropertyConfigurerSupport implements
     public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
         OnceEndpoint target = (OnceEndpoint) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "body": return target.getBody();
         case "bridgeerrorhandler":
         case "bridgeErrorHandler": return target.isBridgeErrorHandler();
         case "delay": return target.getDelay();
diff --git 
a/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceEndpointUriFactory.java
 
b/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceEndpointUriFactory.java
index 20250f4e0912..6d835449c5ce 100644
--- 
a/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceEndpointUriFactory.java
+++ 
b/components/camel-once/src/generated/java/org/apache/camel/component/once/OnceEndpointUriFactory.java
@@ -23,7 +23,8 @@ public class OnceEndpointUriFactory extends 
org.apache.camel.support.component.E
     private static final Set<String> SECRET_PROPERTY_NAMES;
     private static final Map<String, String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(5);
+        Set<String> props = new HashSet<>(6);
+        props.add("body");
         props.add("bridgeErrorHandler");
         props.add("delay");
         props.add("exceptionHandler");
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 7ed47f77eb6c..690afc34f39c 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
@@ -29,9 +29,10 @@
   },
   "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" },
-    "delay": { "index": 1, "kind": "parameter", "displayName": "Delay", 
"group": "consumer", "label": "", "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." },
-    "bridgeErrorHandler": { "index": 2, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "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  [...]
-    "exceptionHandler": { "index": 3, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By def [...]
-    "exchangePattern": { "index": 4, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." }
+    "body": { "index": 1, "kind": "parameter", "displayName": "Body", "group": 
"consumer", "label": "", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "supportFileReference": true, "description": "The data 
to use as message body. You can externalize the data by using file: or 
classpath: as prefix and specify the location of the file." },
+    "delay": { "index": 2, "kind": "parameter", "displayName": "Delay", 
"group": "consumer", "label": "", "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." },
+    "bridgeErrorHandler": { "index": 3, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "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  [...]
+    "exceptionHandler": { "index": 4, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By def [...]
+    "exchangePattern": { "index": 5, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." }
   }
 }
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 5db8d6f49300..bdd0f162b3ad 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
@@ -25,6 +25,8 @@ import org.apache.camel.support.DefaultComponent;
 @Component("once")
 public class OnceComponent extends DefaultComponent {
 
+    // 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);
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 9530aca1555d..96da3b1650b7 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
@@ -16,14 +16,19 @@
  */
 package org.apache.camel.component.once;
 
+import java.io.InputStream;
 import java.util.Timer;
 import java.util.TimerTask;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
 import org.apache.camel.Processor;
 import org.apache.camel.StartupListener;
 import org.apache.camel.support.DefaultConsumer;
+import org.apache.camel.support.LanguageHelper;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.util.StringHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,47 +36,24 @@ public class OnceConsumer extends DefaultConsumer 
implements StartupListener {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(OnceConsumer.class);
 
+    private final CamelContext camelContext;
     private final OnceEndpoint endpoint;
     private final Timer timer;
-    private TimerTask task;
+    private final TimerTask task;
     private volatile boolean scheduled;
 
     public OnceConsumer(OnceEndpoint endpoint, Processor processor) {
         super(endpoint, processor);
+        this.camelContext = endpoint.getCamelContext();
         this.endpoint = endpoint;
         this.timer = new Timer(endpoint.getName());
-    }
-
-    @Override
-    public void doInit() throws Exception {
-        task = new TimerTask() {
-            @Override
-            public void run() {
-                Exchange exchange = createExchange(false);
-                try {
-                    // TODO: options for body/headers to enrich the exchange 
message
-                    getProcessor().process(exchange);
-                } catch (Exception e) {
-                    exchange.setException(e);
-                }
-
-                // handle any thrown exception
-                try {
-                    if (exchange.getException() != null) {
-                        getExceptionHandler().handleException("Error 
processing exchange", exchange, exchange.getException());
-                    }
-                } finally {
-                    releaseExchange(exchange, false);
-                }
-            }
-        };
+        this.task = new OnceTimerTask();
     }
 
     @Override
     protected void doStart() throws Exception {
         super.doStart();
-
-        if (task != null && !scheduled && 
endpoint.getCamelContext().getStatus().isStarted()) {
+        if (!scheduled && endpoint.getCamelContext().getStatus().isStarted()) {
             scheduleTask(task, timer);
         }
     }
@@ -81,15 +63,13 @@ public class OnceConsumer extends DefaultConsumer 
implements StartupListener {
         if (task != null) {
             task.cancel();
         }
-        task = null;
         scheduled = false;
-
         super.doStop();
     }
 
     @Override
     public void onCamelContextStarted(CamelContext context, boolean 
alreadyStarted) throws Exception {
-        if (task != null && !scheduled) {
+        if (!scheduled) {
             scheduleTask(task, timer);
         }
     }
@@ -99,4 +79,45 @@ public class OnceConsumer extends DefaultConsumer implements 
StartupListener {
         timer.schedule(task, endpoint.getDelay());
         scheduled = true;
     }
+
+    private class OnceTimerTask extends TimerTask {
+
+        @Override
+        public void run() {
+            Exchange exchange = createExchange(false);
+            try {
+                String body = resolveData(endpoint.getBody());
+                exchange.getMessage().setBody(body);
+                getProcessor().process(exchange);
+            } catch (Exception e) {
+                exchange.setException(e);
+            }
+
+            // handle any thrown exception
+            try {
+                if (exchange.getException() != null) {
+                    getExceptionHandler().handleException("Error processing 
exchange", exchange, exchange.getException());
+                }
+            } finally {
+                releaseExchange(exchange, false);
+            }
+        }
+    }
+
+    private String resolveData(String data) throws Exception {
+        String answer = data;
+        if (ResourceHelper.hasScheme(data)) {
+            try (InputStream is = 
ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, data)) {
+                answer = 
camelContext.getTypeConverter().mandatoryConvertTo(String.class, is);
+            }
+        }
+        if (answer != null) {
+            answer = camelContext.resolvePropertyPlaceholders(answer);
+        }
+        return answer;
+    }
+
+    private static boolean isSimpleLanguage(String pattern) {
+        return StringHelper.hasStartToken(pattern, "simple");
+    }
 }
diff --git 
a/components/camel-once/src/main/java/org/apache/camel/component/once/OnceEndpoint.java
 
b/components/camel-once/src/main/java/org/apache/camel/component/once/OnceEndpoint.java
index 83cfe258defb..ba1b07bfa729 100644
--- 
a/components/camel-once/src/main/java/org/apache/camel/component/once/OnceEndpoint.java
+++ 
b/components/camel-once/src/main/java/org/apache/camel/component/once/OnceEndpoint.java
@@ -37,6 +37,9 @@ public class OnceEndpoint extends DefaultEndpoint {
     private String name;
     @UriParam(defaultValue = "1000")
     private long delay = 1000;
+    @UriParam
+    @Metadata(supportFileReference = true)
+    private String body;
 
     public OnceEndpoint() {
     }
@@ -91,4 +94,16 @@ public class OnceEndpoint extends DefaultEndpoint {
     public void setDelay(long delay) {
         this.delay = delay;
     }
+
+    public String getBody() {
+        return body;
+    }
+
+    /**
+     * The data to use as message body. You can externalize the data by using 
file: or classpath: as prefix and specify
+     * the location of the file.
+     */
+    public void setBody(String body) {
+        this.body = body;
+    }
 }
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OnceEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OnceEndpointBuilderFactory.java
index ced7b44531ac..d4bac6603f14 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OnceEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OnceEndpointBuilderFactory.java
@@ -44,6 +44,25 @@ public interface OnceEndpointBuilderFactory {
             return (AdvancedOnceEndpointBuilder) this;
         }
 
+        /**
+         * The data to use as message body. You can externalize the data by
+         * using file: or classpath: as prefix and specify the location of the
+         * file.
+         * 
+         * This option can also be loaded from an existing file, by prefixing
+         * with file: or classpath: followed by the location of the file.
+         * 
+         * The option is a: <code>java.lang.String</code> type.
+         * 
+         * Group: consumer
+         * 
+         * @param body the value to set
+         * @return the dsl builder
+         */
+        default OnceEndpointBuilder body(String body) {
+            doSetProperty("body", body);
+            return this;
+        }
         /**
          * The number of milliseconds to wait before triggering. The default
          * value is 1000.

Reply via email to