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.
