This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch file-confirm in repository https://gitbox.apache.org/repos/asf/camel.git
commit 614fc359ebf627e686ad556a63f5296cfa3a43d2 Author: Claus Ibsen <[email protected]> AuthorDate: Sun Jun 16 11:03:57 2024 +0200 CAMEL-19010: camel-file - Idempontent=true should be in eager mode and call confirm/remove. --- .../component/file/FileEndpointConfigurer.java | 6 ++ .../component/file/FileEndpointUriFactory.java | 3 +- .../org/apache/camel/component/file/file.json | 81 ++++++++-------- .../camel/component/file/GenericFileConsumer.java | 16 ++-- .../camel/component/file/GenericFileEndpoint.java | 23 +++++ .../component/file/GenericFileOnCompletion.java | 22 ++++- .../file/FileConsumerIdempotentConfirmTest.java | 102 +++++++++++++++++++++ .../ROOT/pages/camel-4x-upgrade-guide-4_7.adoc | 5 + 8 files changed, 208 insertions(+), 50 deletions(-) diff --git a/components/camel-file/src/generated/java/org/apache/camel/component/file/FileEndpointConfigurer.java b/components/camel-file/src/generated/java/org/apache/camel/component/file/FileEndpointConfigurer.java index f7031d64cfc..af0193c8941 100644 --- a/components/camel-file/src/generated/java/org/apache/camel/component/file/FileEndpointConfigurer.java +++ b/components/camel-file/src/generated/java/org/apache/camel/component/file/FileEndpointConfigurer.java @@ -88,6 +88,8 @@ public class FileEndpointConfigurer extends PropertyConfigurerSupport implements case "forceWrites": target.setForceWrites(property(camelContext, boolean.class, value)); return true; case "greedy": target.setGreedy(property(camelContext, boolean.class, value)); return true; case "idempotent": target.setIdempotent(property(camelContext, java.lang.Boolean.class, value)); return true; + case "idempotenteager": + case "idempotentEager": target.setIdempotentEager(property(camelContext, java.lang.Boolean.class, value)); return true; case "idempotentkey": case "idempotentKey": target.setIdempotentKey(property(camelContext, java.lang.String.class, value)); return true; case "idempotentrepository": @@ -270,6 +272,8 @@ public class FileEndpointConfigurer extends PropertyConfigurerSupport implements case "forceWrites": return boolean.class; case "greedy": return boolean.class; case "idempotent": return java.lang.Boolean.class; + case "idempotenteager": + case "idempotentEager": return java.lang.Boolean.class; case "idempotentkey": case "idempotentKey": return java.lang.String.class; case "idempotentrepository": @@ -453,6 +457,8 @@ public class FileEndpointConfigurer extends PropertyConfigurerSupport implements case "forceWrites": return target.isForceWrites(); case "greedy": return target.isGreedy(); case "idempotent": return target.getIdempotent(); + case "idempotenteager": + case "idempotentEager": return target.getIdempotentEager(); case "idempotentkey": case "idempotentKey": return target.getIdempotentKey(); case "idempotentrepository": diff --git a/components/camel-file/src/generated/java/org/apache/camel/component/file/FileEndpointUriFactory.java b/components/camel-file/src/generated/java/org/apache/camel/component/file/FileEndpointUriFactory.java index 628f1705d2b..8392bf61f30 100644 --- a/components/camel-file/src/generated/java/org/apache/camel/component/file/FileEndpointUriFactory.java +++ b/components/camel-file/src/generated/java/org/apache/camel/component/file/FileEndpointUriFactory.java @@ -23,7 +23,7 @@ public class FileEndpointUriFactory extends org.apache.camel.support.component.E private static final Set<String> SECRET_PROPERTY_NAMES; private static final Set<String> MULTI_VALUE_PREFIXES; static { - Set<String> props = new HashSet<>(97); + Set<String> props = new HashSet<>(98); props.add("allowNullBody"); props.add("antExclude"); props.add("antFilterCaseSensitive"); @@ -62,6 +62,7 @@ public class FileEndpointUriFactory extends org.apache.camel.support.component.E props.add("forceWrites"); props.add("greedy"); props.add("idempotent"); + props.add("idempotentEager"); props.add("idempotentKey"); props.add("idempotentRepository"); props.add("inProgressRepository"); diff --git a/components/camel-file/src/generated/resources/META-INF/org/apache/camel/component/file/file.json b/components/camel-file/src/generated/resources/META-INF/org/apache/camel/component/file/file.json index d92732e7ffe..982c327b86c 100644 --- a/components/camel-file/src/generated/resources/META-INF/org/apache/camel/component/file/file.json +++ b/components/camel-file/src/generated/resources/META-INF/org/apache/camel/component/file/file.json @@ -105,45 +105,46 @@ "filterDirectory": { "index": 54, "kind": "parameter", "displayName": "Filter Directory", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Filters the directory based on Simple language. For example to filter on current date, you can use a simple date pattern such as ${date:now:yyyMMdd}" }, "filterFile": { "index": 55, "kind": "parameter", "displayName": "Filter File", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Filters the file based on Simple language. For example to filter on file size, you can use ${file:size} 5000" }, "idempotent": { "index": 56, "kind": "parameter", "displayName": "Idempotent", "group": "filter", "label": "consumer,filter", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "false", "description": "Option to use the Idempotent Consumer EIP pattern to let Camel skip already processed files. Will by default use a memory based LRUCache that holds 1000 entries. If noop=true then idempotent w [...] - "idempotentKey": { "index": 57, "kind": "parameter", "displayName": "Idempotent Key", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom idempotent key. By default the absolute path of the file is used. You can use the File Language, for example to use the file name and file size, you can do: idempotentKey=${file:name}-${file:size}" }, - "idempotentRepository": { "index": 58, "kind": "parameter", "displayName": "Idempotent Repository", "group": "filter", "label": "consumer,filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.IdempotentRepository", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable repository org.apache.camel.spi.IdempotentRepository which by default use MemoryIdempotentRepository if none is specified and idempotent is true." }, - "include": { "index": 59, "kind": "parameter", "displayName": "Include", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Is used to include files, if filename matches the regex pattern (matching is case in-sensitive). Notice if you use symbols such as plus sign and others you would need to configure this using the RAW() syntax if configuring th [...] - "includeExt": { "index": 60, "kind": "parameter", "displayName": "Include Ext", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Is used to include files matching file extension name (case insensitive). For example to include txt files, then use includeExt=txt. Multiple extensions can be separated by comma, for example to include txt and xml fil [...] - "maxDepth": { "index": 61, "kind": "parameter", "displayName": "Max Depth", "group": "filter", "label": "consumer,filter", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 2147483647, "description": "The maximum depth to traverse when recursively processing a directory." }, - "maxMessagesPerPoll": { "index": 62, "kind": "parameter", "displayName": "Max Messages Per Poll", "group": "filter", "label": "consumer,filter", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "To define a maximum messages to gather per poll. By default no maximum is set. Can be used to set a limit of e.g. 1000 to avoid when starting up the server that there are thousands of files. Set a value of 0 or n [...] - "minDepth": { "index": 63, "kind": "parameter", "displayName": "Min Depth", "group": "filter", "label": "consumer,filter", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The minimum depth to start processing when recursively processing a directory. Using minDepth=1 means the base directory. Using minDepth=2 means the first sub directory." }, - "move": { "index": 64, "kind": "parameter", "displayName": "Move", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Expression (such as Simple Language) used to dynamically set the filename when moving it after processing. To move files into a .done subdirectory just enter .done." }, - "exclusiveReadLockStrategy": { "index": 65, "kind": "parameter", "displayName": "Exclusive Read Lock Strategy", "group": "lock", "label": "consumer,lock", "required": false, "type": "object", "javaType": "org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy<java.io.File>", "deprecated": false, "autowired": false, "secret": false, "description": "Pluggable read-lock as a org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy implementation." }, - "readLock": { "index": 66, "kind": "parameter", "displayName": "Read Lock", "group": "lock", "label": "consumer,lock", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "none", "markerFile", "fileLock", "rename", "changed", "idempotent", "idempotent-changed", "idempotent-rename" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "none", "description": "Used by consumer, to only poll the files if it has exclusive read-lock on the [...] - "readLockCheckInterval": { "index": 67, "kind": "parameter", "displayName": "Read Lock Check Interval", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "Interval in millis for the read-lock, if supported by the read lock. This interval is used for sleeping between attempts to acquire the read lock. For example when using the changed read [...] - "readLockDeleteOrphanLockFiles": { "index": 68, "kind": "parameter", "displayName": "Read Lock Delete Orphan Lock Files", "group": "lock", "label": "consumer,lock", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether or not read lock with marker files should upon startup delete any orphan read lock files, which may have been left on the file system, if Camel was not proper [...] - "readLockIdempotentReleaseAsync": { "index": 69, "kind": "parameter", "displayName": "Read Lock Idempotent Release Async", "group": "lock", "label": "consumer,lock", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the delayed release task should be synchronous or asynchronous. See more details at the readLockIdempotentReleaseDelay option." }, - "readLockIdempotentReleaseAsyncPoolSize": { "index": 70, "kind": "parameter", "displayName": "Read Lock Idempotent Release Async Pool Size", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The number of threads in the scheduled thread pool when using asynchronous release tasks. Using a default of 1 core threads should be sufficient in almost all use-cases, onl [...] - "readLockIdempotentReleaseDelay": { "index": 71, "kind": "parameter", "displayName": "Read Lock Idempotent Release Delay", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "Whether to delay the release task for a period of millis. This can be used to delay the release tasks to expand the window when a file is regarded as read-locked, in an active\/active cluster [...] - "readLockIdempotentReleaseExecutorService": { "index": 72, "kind": "parameter", "displayName": "Read Lock Idempotent Release Executor Service", "group": "lock", "label": "consumer,lock", "required": false, "type": "object", "javaType": "java.util.concurrent.ScheduledExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom and shared thread pool for asynchronous release tasks. See more details at the readLockIdempotentReleaseDelay opt [...] - "readLockLoggingLevel": { "index": 73, "kind": "parameter", "displayName": "Read Lock Logging Level", "group": "lock", "label": "consumer,lock", "required": false, "type": "object", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "DEBUG", "description": "Logging level used when a read lock could not be acquired. By default a DEBUG is logged. You can cha [...] - "readLockMarkerFile": { "index": 74, "kind": "parameter", "displayName": "Read Lock Marker File", "group": "lock", "label": "consumer,lock", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to use marker file with the changed, rename, or exclusive read lock types. By default a marker file is used as well to guard against other processes picking up the same files. This b [...] - "readLockMinAge": { "index": 75, "kind": "parameter", "displayName": "Read Lock Min Age", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0, "description": "This option is applied only for readLock=changed. It allows to specify a minimum age the file must be before attempting to acquire the read lock. For example use readLockMinAge=300s to require the file is [...] - "readLockMinLength": { "index": 76, "kind": "parameter", "displayName": "Read Lock Min Length", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1, "description": "This option is applied only for readLock=changed. It allows you to configure a minimum file length. By default Camel expects the file to contain data, and thus the default value is 1. You can set th [...] - "readLockRemoveOnCommit": { "index": 77, "kind": "parameter", "displayName": "Read Lock Remove On Commit", "group": "lock", "label": "consumer,lock", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "This option is applied only for readLock=idempotent. It allows to specify whether to remove the file name entry from the idempotent repository when processing the file is succeeded [...] - "readLockRemoveOnRollback": { "index": 78, "kind": "parameter", "displayName": "Read Lock Remove On Rollback", "group": "lock", "label": "consumer,lock", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "This option is applied only for readLock=idempotent. It allows to specify whether to remove the file name entry from the idempotent repository when processing the file failed an [...] - "readLockTimeout": { "index": 79, "kind": "parameter", "displayName": "Read Lock Timeout", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 10000, "description": "Optional timeout in millis for the read-lock, if supported by the read-lock. If the read-lock could not be granted and the timeout triggered, then Camel will skip the file. At next poll Camel, will t [...] - "backoffErrorThreshold": { "index": 80, "kind": "parameter", "displayName": "Backoff Error Threshold", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The number of subsequent error polls (failed due some error) that should happen before the backoffMultipler should kick-in." }, - "backoffIdleThreshold": { "index": 81, "kind": "parameter", "displayName": "Backoff Idle Threshold", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The number of subsequent idle polls that should happen before the backoffMultipler should kick-in." }, - "backoffMultiplier": { "index": 82, "kind": "parameter", "displayName": "Backoff Multiplier", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "To let the scheduled polling consumer backoff if there has been a number of subsequent idles\/errors in a row. The multiplier is then the number of polls that will be skipped before the next actual attempt is h [...] - "delay": { "index": 83, "kind": "parameter", "displayName": "Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 500, "description": "Milliseconds before the next poll." }, - "greedy": { "index": 84, "kind": "parameter", "displayName": "Greedy", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If greedy is enabled, then the ScheduledPollConsumer will run immediately again, if the previous run polled 1 or more messages." }, - "initialDelay": { "index": 85, "kind": "parameter", "displayName": "Initial Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "Milliseconds before the first poll starts." }, - "repeatCount": { "index": 86, "kind": "parameter", "displayName": "Repeat Count", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0, "description": "Specifies a maximum limit of number of fires. So if you set it to 1, the scheduler will only fire once. If you set it to 5, it will only fire five times. A value of zero or negative means fire forever." }, - "runLoggingLevel": { "index": 87, "kind": "parameter", "displayName": "Run Logging Level", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "TRACE", "description": "The consumer logs a start\/complete log line when it polls. This option allows you to configure the l [...] - "scheduledExecutorService": { "index": 88, "kind": "parameter", "displayName": "Scheduled Executor Service", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.util.concurrent.ScheduledExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "Allows for configuring a custom\/shared thread pool to use for the consumer. By default each consumer has its own single threaded thread pool." }, - "scheduler": { "index": 89, "kind": "parameter", "displayName": "Scheduler", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "none", "description": "To use a cron scheduler from either camel-spring or camel-quartz component. Use value spring or quartz for built in scheduler" }, - "schedulerProperties": { "index": 90, "kind": "parameter", "displayName": "Scheduler Properties", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": "scheduler.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "To configure additional properties when using a custom scheduler or any of the Quartz, Spring based scheduler." }, - "startScheduler": { "index": 91, "kind": "parameter", "displayName": "Start Scheduler", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the scheduler should be auto started." }, - "timeUnit": { "index": 92, "kind": "parameter", "displayName": "Time Unit", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.util.concurrent.TimeUnit", "enum": [ "NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", "DAYS" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "MILLISECONDS", "description": "Time unit for initialDelay and delay options." }, - "useFixedDelay": { "index": 93, "kind": "parameter", "displayName": "Use Fixed Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in JDK for details." }, - "shuffle": { "index": 94, "kind": "parameter", "displayName": "Shuffle", "group": "sort", "label": "consumer,sort", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To shuffle the list of files (sort in random order)" }, - "sortBy": { "index": 95, "kind": "parameter", "displayName": "Sort By", "group": "sort", "label": "consumer,sort", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Built-in sort by using the File Language. Supports nested sorts, so you can have a sort by file name and as a 2nd group sort by modified date." }, - "sorter": { "index": 96, "kind": "parameter", "displayName": "Sorter", "group": "sort", "label": "consumer,sort", "required": false, "type": "object", "javaType": "java.util.Comparator<org.apache.camel.component.file.GenericFile<java.io.File>>", "deprecated": false, "autowired": false, "secret": false, "description": "Pluggable sorter as a java.util.Comparator class." } + "idempotentEager": { "index": 57, "kind": "parameter", "displayName": "Idempotent Eager", "group": "filter", "label": "consumer,filter", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "false", "description": "Option to use the Idempotent Consumer EIP pattern to let Camel skip already processed files. Will by default use a memory based LRUCache that holds 1000 entri [...] + "idempotentKey": { "index": 58, "kind": "parameter", "displayName": "Idempotent Key", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom idempotent key. By default the absolute path of the file is used. You can use the File Language, for example to use the file name and file size, you can do: idempotentKey=${file:name}-${file:size}" }, + "idempotentRepository": { "index": 59, "kind": "parameter", "displayName": "Idempotent Repository", "group": "filter", "label": "consumer,filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.IdempotentRepository", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable repository org.apache.camel.spi.IdempotentRepository which by default use MemoryIdempotentRepository if none is specified and idempotent is true." }, + "include": { "index": 60, "kind": "parameter", "displayName": "Include", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Is used to include files, if filename matches the regex pattern (matching is case in-sensitive). Notice if you use symbols such as plus sign and others you would need to configure this using the RAW() syntax if configuring th [...] + "includeExt": { "index": 61, "kind": "parameter", "displayName": "Include Ext", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Is used to include files matching file extension name (case insensitive). For example to include txt files, then use includeExt=txt. Multiple extensions can be separated by comma, for example to include txt and xml fil [...] + "maxDepth": { "index": 62, "kind": "parameter", "displayName": "Max Depth", "group": "filter", "label": "consumer,filter", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 2147483647, "description": "The maximum depth to traverse when recursively processing a directory." }, + "maxMessagesPerPoll": { "index": 63, "kind": "parameter", "displayName": "Max Messages Per Poll", "group": "filter", "label": "consumer,filter", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "To define a maximum messages to gather per poll. By default no maximum is set. Can be used to set a limit of e.g. 1000 to avoid when starting up the server that there are thousands of files. Set a value of 0 or n [...] + "minDepth": { "index": 64, "kind": "parameter", "displayName": "Min Depth", "group": "filter", "label": "consumer,filter", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The minimum depth to start processing when recursively processing a directory. Using minDepth=1 means the base directory. Using minDepth=2 means the first sub directory." }, + "move": { "index": 65, "kind": "parameter", "displayName": "Move", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Expression (such as Simple Language) used to dynamically set the filename when moving it after processing. To move files into a .done subdirectory just enter .done." }, + "exclusiveReadLockStrategy": { "index": 66, "kind": "parameter", "displayName": "Exclusive Read Lock Strategy", "group": "lock", "label": "consumer,lock", "required": false, "type": "object", "javaType": "org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy<java.io.File>", "deprecated": false, "autowired": false, "secret": false, "description": "Pluggable read-lock as a org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy implementation." }, + "readLock": { "index": 67, "kind": "parameter", "displayName": "Read Lock", "group": "lock", "label": "consumer,lock", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "none", "markerFile", "fileLock", "rename", "changed", "idempotent", "idempotent-changed", "idempotent-rename" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "none", "description": "Used by consumer, to only poll the files if it has exclusive read-lock on the [...] + "readLockCheckInterval": { "index": 68, "kind": "parameter", "displayName": "Read Lock Check Interval", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "Interval in millis for the read-lock, if supported by the read lock. This interval is used for sleeping between attempts to acquire the read lock. For example when using the changed read [...] + "readLockDeleteOrphanLockFiles": { "index": 69, "kind": "parameter", "displayName": "Read Lock Delete Orphan Lock Files", "group": "lock", "label": "consumer,lock", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether or not read lock with marker files should upon startup delete any orphan read lock files, which may have been left on the file system, if Camel was not proper [...] + "readLockIdempotentReleaseAsync": { "index": 70, "kind": "parameter", "displayName": "Read Lock Idempotent Release Async", "group": "lock", "label": "consumer,lock", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the delayed release task should be synchronous or asynchronous. See more details at the readLockIdempotentReleaseDelay option." }, + "readLockIdempotentReleaseAsyncPoolSize": { "index": 71, "kind": "parameter", "displayName": "Read Lock Idempotent Release Async Pool Size", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The number of threads in the scheduled thread pool when using asynchronous release tasks. Using a default of 1 core threads should be sufficient in almost all use-cases, onl [...] + "readLockIdempotentReleaseDelay": { "index": 72, "kind": "parameter", "displayName": "Read Lock Idempotent Release Delay", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "Whether to delay the release task for a period of millis. This can be used to delay the release tasks to expand the window when a file is regarded as read-locked, in an active\/active cluster [...] + "readLockIdempotentReleaseExecutorService": { "index": 73, "kind": "parameter", "displayName": "Read Lock Idempotent Release Executor Service", "group": "lock", "label": "consumer,lock", "required": false, "type": "object", "javaType": "java.util.concurrent.ScheduledExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom and shared thread pool for asynchronous release tasks. See more details at the readLockIdempotentReleaseDelay opt [...] + "readLockLoggingLevel": { "index": 74, "kind": "parameter", "displayName": "Read Lock Logging Level", "group": "lock", "label": "consumer,lock", "required": false, "type": "object", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "DEBUG", "description": "Logging level used when a read lock could not be acquired. By default a DEBUG is logged. You can cha [...] + "readLockMarkerFile": { "index": 75, "kind": "parameter", "displayName": "Read Lock Marker File", "group": "lock", "label": "consumer,lock", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to use marker file with the changed, rename, or exclusive read lock types. By default a marker file is used as well to guard against other processes picking up the same files. This b [...] + "readLockMinAge": { "index": 76, "kind": "parameter", "displayName": "Read Lock Min Age", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0, "description": "This option is applied only for readLock=changed. It allows to specify a minimum age the file must be before attempting to acquire the read lock. For example use readLockMinAge=300s to require the file is [...] + "readLockMinLength": { "index": 77, "kind": "parameter", "displayName": "Read Lock Min Length", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1, "description": "This option is applied only for readLock=changed. It allows you to configure a minimum file length. By default Camel expects the file to contain data, and thus the default value is 1. You can set th [...] + "readLockRemoveOnCommit": { "index": 78, "kind": "parameter", "displayName": "Read Lock Remove On Commit", "group": "lock", "label": "consumer,lock", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "This option is applied only for readLock=idempotent. It allows to specify whether to remove the file name entry from the idempotent repository when processing the file is succeeded [...] + "readLockRemoveOnRollback": { "index": 79, "kind": "parameter", "displayName": "Read Lock Remove On Rollback", "group": "lock", "label": "consumer,lock", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "This option is applied only for readLock=idempotent. It allows to specify whether to remove the file name entry from the idempotent repository when processing the file failed an [...] + "readLockTimeout": { "index": 80, "kind": "parameter", "displayName": "Read Lock Timeout", "group": "lock", "label": "consumer,lock", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 10000, "description": "Optional timeout in millis for the read-lock, if supported by the read-lock. If the read-lock could not be granted and the timeout triggered, then Camel will skip the file. At next poll Camel, will t [...] + "backoffErrorThreshold": { "index": 81, "kind": "parameter", "displayName": "Backoff Error Threshold", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The number of subsequent error polls (failed due some error) that should happen before the backoffMultipler should kick-in." }, + "backoffIdleThreshold": { "index": 82, "kind": "parameter", "displayName": "Backoff Idle Threshold", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The number of subsequent idle polls that should happen before the backoffMultipler should kick-in." }, + "backoffMultiplier": { "index": 83, "kind": "parameter", "displayName": "Backoff Multiplier", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "To let the scheduled polling consumer backoff if there has been a number of subsequent idles\/errors in a row. The multiplier is then the number of polls that will be skipped before the next actual attempt is h [...] + "delay": { "index": 84, "kind": "parameter", "displayName": "Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 500, "description": "Milliseconds before the next poll." }, + "greedy": { "index": 85, "kind": "parameter", "displayName": "Greedy", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If greedy is enabled, then the ScheduledPollConsumer will run immediately again, if the previous run polled 1 or more messages." }, + "initialDelay": { "index": 86, "kind": "parameter", "displayName": "Initial Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "Milliseconds before the first poll starts." }, + "repeatCount": { "index": 87, "kind": "parameter", "displayName": "Repeat Count", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0, "description": "Specifies a maximum limit of number of fires. So if you set it to 1, the scheduler will only fire once. If you set it to 5, it will only fire five times. A value of zero or negative means fire forever." }, + "runLoggingLevel": { "index": 88, "kind": "parameter", "displayName": "Run Logging Level", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "TRACE", "description": "The consumer logs a start\/complete log line when it polls. This option allows you to configure the l [...] + "scheduledExecutorService": { "index": 89, "kind": "parameter", "displayName": "Scheduled Executor Service", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.util.concurrent.ScheduledExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "Allows for configuring a custom\/shared thread pool to use for the consumer. By default each consumer has its own single threaded thread pool." }, + "scheduler": { "index": 90, "kind": "parameter", "displayName": "Scheduler", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "none", "description": "To use a cron scheduler from either camel-spring or camel-quartz component. Use value spring or quartz for built in scheduler" }, + "schedulerProperties": { "index": 91, "kind": "parameter", "displayName": "Scheduler Properties", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": "scheduler.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "To configure additional properties when using a custom scheduler or any of the Quartz, Spring based scheduler." }, + "startScheduler": { "index": 92, "kind": "parameter", "displayName": "Start Scheduler", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the scheduler should be auto started." }, + "timeUnit": { "index": 93, "kind": "parameter", "displayName": "Time Unit", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.util.concurrent.TimeUnit", "enum": [ "NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", "DAYS" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "MILLISECONDS", "description": "Time unit for initialDelay and delay options." }, + "useFixedDelay": { "index": 94, "kind": "parameter", "displayName": "Use Fixed Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in JDK for details." }, + "shuffle": { "index": 95, "kind": "parameter", "displayName": "Shuffle", "group": "sort", "label": "consumer,sort", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To shuffle the list of files (sort in random order)" }, + "sortBy": { "index": 96, "kind": "parameter", "displayName": "Sort By", "group": "sort", "label": "consumer,sort", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Built-in sort by using the File Language. Supports nested sorts, so you can have a sort by file name and as a 2nd group sort by modified date." }, + "sorter": { "index": 97, "kind": "parameter", "displayName": "Sorter", "group": "sort", "label": "consumer,sort", "required": false, "type": "object", "javaType": "java.util.Comparator<org.apache.camel.component.file.GenericFile<java.io.File>>", "deprecated": false, "autowired": false, "secret": false, "description": "Pluggable sorter as a java.util.Comparator class." } } } diff --git a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileConsumer.java b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileConsumer.java index 2ffb5adad41..0bd9a45a9c9 100644 --- a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileConsumer.java +++ b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileConsumer.java @@ -612,6 +612,7 @@ public abstract class GenericFileConsumer<T> extends ScheduledBatchPollingConsum } private boolean notUnique(GenericFile<T> file) { + boolean answer = false; // use absolute file path as default key, but evaluate if an // expression key was configured String key = file.getAbsoluteFilePath(); @@ -620,13 +621,16 @@ public abstract class GenericFileConsumer<T> extends ScheduledBatchPollingConsum key = endpoint.getIdempotentKey().evaluate(dummy, String.class); LOG.trace("Evaluated idempotentKey: {} for file: {}", key, file); } - if (key != null && endpoint.getIdempotentRepository().contains(key)) { - LOG.trace( - "This consumer is idempotent and the file has been consumed before matching idempotentKey: {}. Will skip this file: {}", - key, file); - return true; + if (key != null) { + answer = endpoint.isIdempotentEager() + ? !endpoint.getIdempotentRepository().add(key) : endpoint.getIdempotentRepository().contains(key); + if (answer) { + LOG.trace( + "This consumer is idempotent and the file has been consumed before matching idempotentKey: {}. Will skip this file: {}", + key, file); + } } - return false; + return answer; } /** diff --git a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java index 0b14f1fe5e1..40679ff25ad 100644 --- a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java +++ b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java @@ -40,6 +40,7 @@ import org.apache.camel.spi.BrowsableEndpoint; import org.apache.camel.spi.ExceptionHandler; import org.apache.camel.spi.IdempotentRepository; import org.apache.camel.spi.Language; +import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriParam; import org.apache.camel.support.ScheduledPollEndpoint; import org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository; @@ -276,6 +277,13 @@ public abstract class GenericFileEndpoint<T> extends ScheduledPollEndpoint imple + "LRUCache that holds 1000 entries. If noop=true then idempotent will be enabled as well to avoid " + "consuming the same files over and over again.") protected Boolean idempotent; + @UriParam(label = "consumer,filter", defaultValue = "false", description = "Option to use the Idempotent " + + "Consumer EIP pattern to let Camel skip already processed files. Will by default use a memory based " + + "LRUCache that holds 1000 entries. If noop=true then idempotent will be enabled as well to avoid " + + "consuming the same files over and over again.") + @Metadata(label = "consumer,filter", javaType = "java.lang.Boolean", defaultValue = "true", + description = "Sets whether to eagerly add the filename to the idempotent repository or wait until the exchange is complete.") + private Boolean idempotentEager = Boolean.TRUE; @UriParam(label = "consumer,filter", javaType = "java.lang.String", description = "To use a custom idempotent " + "key. By default the absolute path of the file is used. You can use the File Language, for example to " + "use the file name and file size, you can do: idempotentKey=${file:name}-${file:size}") @@ -865,6 +873,10 @@ public abstract class GenericFileEndpoint<T> extends ScheduledPollEndpoint imple return idempotent != null ? idempotent : false; } + public boolean isIdempotentEager() { + return idempotentEager != null ? idempotentEager : false; + } + public String getCharset() { return charset; } @@ -898,6 +910,17 @@ public abstract class GenericFileEndpoint<T> extends ScheduledPollEndpoint imple this.idempotent = idempotent; } + public Boolean getIdempotentEager() { + return idempotentEager; + } + + /** + * Sets whether to eagerly add the key to the idempotent repository or wait until the exchange is complete. + */ + public void setIdempotentEager(Boolean idempotentEager) { + this.idempotentEager = idempotentEager; + } + public Expression getIdempotentKey() { return idempotentKey; } diff --git a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileOnCompletion.java b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileOnCompletion.java index c90a04925af..626bfb98abe 100644 --- a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileOnCompletion.java +++ b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileOnCompletion.java @@ -112,7 +112,6 @@ public class GenericFileOnCompletion<T> implements Synchronization { protected void processStrategyCommit( GenericFileProcessStrategy<T> processStrategy, Exchange exchange, GenericFile<T> file) { if (Boolean.TRUE.equals(endpoint.isIdempotent())) { - // use absolute file path as default key, but evaluate if an // expression key was configured String key = absoluteFileName; @@ -120,10 +119,14 @@ public class GenericFileOnCompletion<T> implements Synchronization { Exchange dummy = endpoint.createExchange(file); key = endpoint.getIdempotentKey().evaluate(dummy, String.class); } - // only add to idempotent repository if we could process the file if (key != null) { - endpoint.getIdempotentRepository().add(key); + // eager = confirm, non-eager = add + if (endpoint.isIdempotentEager()) { + endpoint.getIdempotentRepository().confirm(key); + } else { + endpoint.getIdempotentRepository().add(key); + } } } @@ -151,6 +154,19 @@ public class GenericFileOnCompletion<T> implements Synchronization { LOG.warn("Rollback file strategy: {} for file: {}", processStrategy, file); } + if (Boolean.TRUE.equals(endpoint.isIdempotent())) { + // use absolute file path as default key, but evaluate if an + // expression key was configured + String key = absoluteFileName; + if (endpoint.getIdempotentKey() != null) { + Exchange dummy = endpoint.createExchange(file); + key = endpoint.getIdempotentKey().evaluate(dummy, String.class); + } + if (key != null) { + endpoint.getIdempotentRepository().remove(key); + } + } + // only delete done file if moveFailed option is enabled, as otherwise // on rollback, // we should leave the done file so we can retry diff --git a/core/camel-core/src/test/java/org/apache/camel/component/file/FileConsumerIdempotentConfirmTest.java b/core/camel-core/src/test/java/org/apache/camel/component/file/FileConsumerIdempotentConfirmTest.java new file mode 100644 index 00000000000..b500453a80c --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/component/file/FileConsumerIdempotentConfirmTest.java @@ -0,0 +1,102 @@ +/* + * 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.file; + +import java.nio.file.Files; +import java.util.concurrent.TimeUnit; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * Unit test for the idempotent=true option. + */ +public class FileConsumerIdempotentConfirmTest extends ContextTestSupport { + + private final MyRepo myRepo = new MyRepo(); + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + context.getRegistry().bind("myRepo", myRepo); + + from(fileUri("?idempotent=true&move=done/${file:name}&initialDelay=0&delay=10&idempotentRepository=#myRepo")) + .convertBodyTo(String.class).to("mock:result"); + } + }; + } + + @Test + public void testIdempotentConfirm() throws Exception { + assertNull(myRepo.getKey()); + + template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "report.txt"); + + // consume the file the first time + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedBodiesReceived("Hello World"); + + assertMockEndpointsSatisfied(); + + oneExchangeDone.matchesWaitTime(); + + // reset mock and set new expectations + mock.reset(); + mock.expectedMessageCount(0); + + // move file back + Files.move(testFile("done/report.txt"), testFile("report.txt")); + + // should NOT consume the file again, let a bit time pass to let the + // consumer try to consume it but it should not + Awaitility.await().pollDelay(100, TimeUnit.MILLISECONDS).untilAsserted(() -> assertMockEndpointsSatisfied()); + + FileEndpoint fe = context.getEndpoint(fileUri(), FileEndpoint.class); + assertNotNull(fe); + + MemoryIdempotentRepository repo = (MemoryIdempotentRepository) fe.getInProgressRepository(); + assertEquals(0, repo.getCacheSize(), "Should be no in-progress files"); + + Awaitility.await().pollDelay(100, TimeUnit.MILLISECONDS).untilAsserted(() -> assertNotNull(myRepo.getKey())); + } + + private static final class MyRepo extends MemoryIdempotentRepository { + + private String key; + + @Override + public boolean confirm(String key) { + this.key = key; + return super.confirm(key); + } + + public String getKey() { + return key; + } + } + +} diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_7.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_7.adoc index 38443a1cfcc..35945ba5b5f 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_7.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_7.adoc @@ -84,6 +84,11 @@ To: </route> ---- +=== camel-file + +When using `idempontent=true` then the file consumer will now run in eager mode, and `add` the file to the repository +before processing, and call `confirm` when done. Setting `idempontentEager=false` will use the old behaviour. + === camel-jbang The `generate` commands has been moved into separate plugin which you need to install first to be able to use.
