You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2023/06/28 10:54:29 UTC

[camel] 01/06: Camel Azure Files component 3.x (#10460)

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

acosentino pushed a commit to branch CAMEL-19279-4.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit aa55c55d263954c1d37f3f7ae8eccf20b9120c49
Author: pekuz <Pe...@Eurofins.com>
AuthorDate: Wed Jun 28 11:57:27 2023 +0200

    Camel Azure Files component 3.x (#10460)
    
    * CAMEL-19279: the first blending outcome (generate has problems)
    
    * CAMEL-19279: retrying push with reconfigured upstream
    
    * CAMEL-19279: wrong Java version mystery resolved
    
    * CAMEL-19279: workaround for PrepareComponentMojo probes (they expect manually created under generated/)
    
    * CAMEL-19279: a failed attempt to add endpoint unit tests
    
    context fails to instantiate component
    
    * CAMEL-19279: component/marker file name and content matters
    
    component/azure-file was created manually with content below:
    
    # Generated by camel build tools - do NOT edit this file!
    class=org.apache.camel.component.file.azure.FilesComponent
    
    * CAMEL-19279: sas token embedded in endpoint URI is too processed and becomes invalid, try some counter-measures
    
    * CAMEL-19279: more robust share extraction
    
    * CAMEL-19279: otherwise Failed to create Consumer for endpoint: azure-files://camelazurefiles.file.core.windows.net/transfer
    
    * CAMEL-19279: preserve sas token params so Azure Files recognizes the token
    
    * CAMEL-19279:  rebuild with contrib profile
    
    * CAMEL-19279: list share without crashing
    
        from("azure-files://camelazurefiles.file.core.windows.net/transfer?sv=2022-11-02&ss=f&srt=sco&sp=rwdlc&se=2023-05-28T22:50:04Z&st=2023-05-24T14:50:04Z&spr=https&sig=gj%2BUKSiCWSHmcubvGhyJhatkP8hkbXkrmV%2B%2BZme%2BCxI%3D")
    
    * CAMEL-19279: a little cleanup
    
    * CAMEL-19279: properties cleanup
    
    * CAMEL-19279: removing non-implemented properties
    
    * CAMEL-19279: last modified is null, not sure why but least do not crash
    
    * CAMEL-19279: doc updated
    
    * CAMEL-19279: removing FTP-specific code
    
    * CAMEL-19279: share or starting dir as the root?
    
    * CAMEL-19279: basic to("azure-files://...) does not crash
    
    * CAMEL-19279: &move param implemented
    
    * CAMEL-19279: consumeWithDelete basic example passes
    
    * CAMEL-19279: TODO for &recursive in combination with &move
    
    * CAMEL-19279: ls includes last modified
    
    * CAMEL-19279: &recursive tests pass, correcting doc respectively
    
    * CAMEL-19279: path handling clarifications and simplifications
    
    * CAMEL-19279: path stepping is better now
    
    * CAMEL-19279: minor cleanup
    
    * CAMEL-19279: localWorkDirectory example does not fail
    
    * CAMEL-19279: timeoutExample does not block
    
    * CAMEL-19279: removing the separator option
    
    * CAMEL-19279: delete with timeouts, the file length hint to producer is possible
    
    * CAMEL-19279:  minor cleanup
    
    * CAMEL-19279:: config cleanup, reduce noops
    
    * CAMEL-19279: consumeStreamExample does not fail, timeout for lwd download
    
    * CAMEL-19279: code cleanup, mainly consumer
    
    * CAMEL-19279: slight consumer optimizations
    
    * CAMEL-19279: cleaner trace log
    
    * CAMEL-19279: cleanup
    
    * CAMEL-19279: meta-data timeout covers more operations
    
    * CAMEL-19279: upload 1 GiB example, clarifications
    
    * CAMEL-19279: doc updates
    
    * CAMEL-19279: no need for bufferSize option
    
    * CAMEL-19279: doc for filter examples
    
    * CAMEL-19279: deep link to comp doc
    
    * a typo
    
    * doc typos corrected
    
    * CAMEL-19279: lesser need to type/read windows
    
    * CAMEL-19279: by observation, the configuration hiding helps to discover @UriPath
    
    * CAMEL-19279: example with FTP-like defaults seems reliable
    
    * CAMEL-19279: more SAS params (despite Azure portal has no UI for them)
    
    * CAMEL-19279: minor cleanup
    
    * CAMEL-19279: more SAS params (missing regen)
    
    * CAMEL-19279: a small cleanup around URIs
    
    * CAMEL-19279: hide moveExisting till we are sure it works
    
    * CAMEL-19279: upload timeout is TODO marked
    
    * CAMEL-19279: preparing for a fallback to the account key
    
    * CAMEL-19279: sharedKey auth is possible
    
    * CAMEL-19279: doc refinement, example short URI with sharedKey
    
    * CAMEL-19279: oh poor standards who respects you
    
    * CAMEL-19279: basic auth is  irrelevant
    
    * CAMEL-19279: basic auth is irrelevant (missing regen)
    
    * CAMEL-19279: better structure, naming, run-time
    
    * CAMEL-19279: reduce generics,  (revert to) only dedicated pollSubDir restores to parent
    
    * CAMEL-19279: moving first conf element from endpoint to its configuration
    
    * CAMEL-19279: remove unneeded generics
    
    * CAMEL-19279: more config elements to config class
    
    * CAMEL-19279: make token directly nested conf element
    
    * CAMEL-19279: /share without subDir detection fixed
    
    * internal comment -> user doc
    
    * CAMEL-19279: endpoint path field always starts with /, relative path correctly calculated
    
    * CAMEL-19279: endpoint cleanup, Azure files does not reuse a client over several shares/endpoints
    
    * CAMEL-19279: inlining and removing non-implemented
    
    * CAMEL-19279: clean up
    
    * cleanup
    
    * CAMEL-19279: polling consumer simplified (using multi-steps)
    
    * CAMEL-19279: consumer doc mentions body type options
    
    * CAMEL-19279: producer doc aligned with consumer
    
    * CAMEL-19279: better leverage multi-step capability of underlying lib
    
    * CAMEL-19279: doc FTP heritage improvements
    
    * CAMEL-19279: traces consistency improved
    
    * CAMEL-19279: simpler build dir impl that does not rely on cwd
    
    * CAMEL-19279: storing many little files improved
    
    * CAMEL-19279: manage expectations
    
    * CAMEL-19279: mark as a preview in pom too
    
    * CAMEL-19279: minor cleanup
    
    * CAMEL-19279: doc punctuation feedback
---
 components/camel-azure/camel-azure-files/pom.xml   |  77 +++
 components/camel-azure/camel-azure-files/readme.md | 143 ++++
 .../file/azure/FilesComponentConfigurer.java       |  61 ++
 .../file/azure/FilesEndpointConfigurer.java        | 553 +++++++++++++++
 .../file/azure/FilesEndpointUriFactory.java        | 185 ++++++
 .../services/org/apache/camel/component.properties |   7 +
 .../org/apache/camel/component/azure-files         |   2 +
 .../apache/camel/configurer/azure-files-component  |   2 +
 .../apache/camel/configurer/azure-files-endpoint   |   2 +
 .../apache/camel/urifactory/azure-files-endpoint   |   2 +
 .../camel/component/file/azure/azure-files.json    | 142 ++++
 .../src/main/docs/azure-files-component.adoc       | 314 +++++++++
 .../camel/component/file/azure/FilesComponent.java |  62 ++
 .../component/file/azure/FilesConfiguration.java   | 105 +++
 .../camel/component/file/azure/FilesConsumer.java  | 254 +++++++
 .../camel/component/file/azure/FilesEndpoint.java  | 184 +++++
 .../camel/component/file/azure/FilesHeaders.java   |  53 ++
 .../component/file/azure/FilesOperations.java      | 740 +++++++++++++++++++++
 .../camel/component/file/azure/FilesPath.java      | 136 ++++
 .../camel/component/file/azure/FilesProducer.java  |  23 +
 .../camel/component/file/azure/FilesToken.java     | 200 ++++++
 .../component/file/azure/FilesURIStrings.java      |  48 ++
 .../component/file/azure/NormalizedOperations.java |  43 ++
 .../FilesChangedExclusiveReadLockStrategy.java     | 156 +++++
 .../strategy/FilesExclusiveReadLockCheck.java      | 140 ++++
 .../strategy/FilesProcessStrategyFactory.java      | 125 ++++
 .../file/azure/strategy/StrategyUtil.java          |  57 ++
 .../file/azure/FilesConfigurationTests.java        | 127 ++++
 .../component/file/azure/FilesEndpointTests.java   |  27 +
 .../camel/component/file/azure/FilesPathTests.java |  36 +
 .../component/file/azure/FilesURIStringsTests.java |  22 +
 parent/pom.xml                                     |   5 +
 32 files changed, 4033 insertions(+)

diff --git a/components/camel-azure/camel-azure-files/pom.xml b/components/camel-azure/camel-azure-files/pom.xml
new file mode 100644
index 00000000000..d1a64aba4a4
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>  
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-azure-parent</artifactId>
+        <version>3.21.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-azure-files</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Camel :: Azure Files</name>
+    <description>Camel Azure Files Component (Preview)</description>
+
+    <properties>
+        <minimalJavaBuildVersion>11</minimalJavaBuildVersion>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-ftp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.azure</groupId>
+            <artifactId>azure-storage-file-share</artifactId>
+            <version>12.18.0</version> <!-- https://mvnrepository.com/artifact/com.azure/azure-storage-file-share -->
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <version>${mockito-version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>${commons-lang3-version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <version>${awaitility-version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/components/camel-azure/camel-azure-files/readme.md b/components/camel-azure/camel-azure-files/readme.md
new file mode 100644
index 00000000000..20b28d9ba42
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/readme.md
@@ -0,0 +1,143 @@
+# Camel Azure Files Component
+
+This component allows to access Azure Files over public Internet.
+It reasonably mimics the (S)FTP(S) components.
+It uses Azure Java SDK.
+
+## State
+
+In development since 2023-05, so far experimental.
+
+## Component Documentation
+
+The source of [the component doc fragment](src/main/docs/azure-files-component.adoc)
+is somehow usable as is. 
+
+The missing fragments can be deducted by `@UriParam` in java source,
+the File component doc (many options are inherited), etc.  
+
+## History
+
+Formulated requirement in https://issues.apache.org/jira/browse/CAMEL-19279.
+
+Forked Git as https://github.com/pekuz/camel/tree/camel-3.x/components/camel-azure/camel-azure-files
+and started blending camel-ftp, camel-azure-storage-blob
+with intent to support Azure Files. 
+
+FTPFile is replaced by ShareFileItem.
+
+Adapted connection management in the operations class. Azure Java SDK
+manages connections internally. It might be possible to integrate
+down at the connection provider level but we are not targeting it upfront
+because FTP protocol uses another session and connection(s) management
+concept than HTTP-based protocols. 
+
+Initially I had hoped, I could reuse some classes as-is but in
+reality a copy-and-paste has been needed because of FTPFile use
+(despite impl needs only: file name, last modified and length). 
+
+A set of problems with mvn generate:
+ 
+  - CAMEL-19379 unclear error message (Resolved & closed)
+  - CAMEL-19385 [Windows] generate Error loading other model. Reason: FirstVersion is not specified.
+
+While cloning the fork in Github Desktop, I had selected "I want to contribute mode",
+and it updated upstream to fork's upstream i.e. https://github.com/apache/camel.git
+it's likely at cause of subsequent problems so changing the upstream back
+to https://github.com/pekuz/camel.git
+
+The endpoint path interpretation is specialized
+to non-uniform /share[/directory]. 
+
+Endpoint parameters cleanup, removed those FTP-specific: 
+
+  - ftpClient
+  - ftpClientConfig
+  - passive
+  - passiveMode
+  - stepwise
+  - binary
+  - charset
+  - account
+  - siteCommand
+  - chmod
+  - fastExistsCheck
+  - handleDirectoryParserAbsoluteResult
+  - separator
+  - sendNoop
+  - bufferSize
+  
+or Azure Files irrelevant:
+  
+  - username
+  - password
+  
+or not implemented:
+
+  - transferLoggingLevel
+  - transferLoggingIntervalSeconds
+  - transferLoggingVerbose
+  - soTimeout
+  - useList
+  - ignoreFileNotFoundOrPermissionError
+  - moveExisting
+
+Basic `to("azure-files://...")` does not crash. 
+
+The component is tested with 1 GiB upload
+(it took 2h47m47s over 1 Mbit/s uplink)
+and download.
+
+## Deps
+
+At first I want to use the Azure Files component with Camel 3.16+
+hence Java 11 is selected as a base dependency.
+
+https://camel.apache.org/manual/what-are-the-dependencies.html
+
+Java 17, and Camel 4 port, might come later after Java 17 and its tools
+chain is approved by our security team.
+
+### Eclipse
+
+Eclipse 2023-03 detects Java version to be used for executing maven from 
+the `requireJavaVersion` element of the `maven-enforcer-plugin` configuration. 
+          
+https://github.com/eclipse-m2e/m2e-core/blob/master/RELEASE_NOTES.md#220
+
+Investigating the component pom's parents chain, I have found at the top-most parent pom:
+
+    <groupId>org.apache</groupId>
+    <artifactId>apache</artifactId>
+    <version>29</version>
+
+    <properties>    
+      <minimalJavaBuildVersion>1.8</minimalJavaBuildVersion>
+
+    <execution>
+      <id>enforce-java-version</id>
+      <goals>
+        <goal>enforce</goal>
+      </goals>
+      <configuration>
+        <rules>
+          <requireJavaVersion>
+            <version>${minimalJavaBuildVersion}</version>
+          </requireJavaVersion>
+        </rules>
+      </configuration>
+    </execution>
+
+consequently it is better to specify in the component pom:
+
+    <properties>
+        <minimalJavaBuildVersion>11</minimalJavaBuildVersion>
+        
+to get a warning if eclipse m2e selected a lower JDK version:
+
+    [INFO] --- enforcer:3.0.0:enforce (enforce-java-version) @ camel-azure-files ---
+    [WARNING] Rule 0: org.apache.maven.plugins.enforcer.RequireJavaVersion failed with message:
+    Detected JDK Version: 1.8.0-271 is not in the allowed range 11.
+    
+Reported as CAMEL-19384.
+
diff --git a/components/camel-azure/camel-azure-files/src/generated/java/org/apache/camel/component/file/azure/FilesComponentConfigurer.java b/components/camel-azure/camel-azure-files/src/generated/java/org/apache/camel/component/file/azure/FilesComponentConfigurer.java
new file mode 100644
index 00000000000..1e4f1d77b5f
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/generated/java/org/apache/camel/component/file/azure/FilesComponentConfigurer.java
@@ -0,0 +1,61 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.file.azure;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@SuppressWarnings("unchecked")
+public class FilesComponentConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        FilesComponent target = (FilesComponent) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true;
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); return true;
+        case "lazystartproducer":
+        case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": return boolean.class;
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": return boolean.class;
+        case "lazystartproducer":
+        case "lazyStartProducer": return boolean.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        FilesComponent target = (FilesComponent) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": return target.isAutowiredEnabled();
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": return target.isBridgeErrorHandler();
+        case "lazystartproducer":
+        case "lazyStartProducer": return target.isLazyStartProducer();
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-azure/camel-azure-files/src/generated/java/org/apache/camel/component/file/azure/FilesEndpointConfigurer.java b/components/camel-azure/camel-azure-files/src/generated/java/org/apache/camel/component/file/azure/FilesEndpointConfigurer.java
new file mode 100644
index 00000000000..c2cda8375e6
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/generated/java/org/apache/camel/component/file/azure/FilesEndpointConfigurer.java
@@ -0,0 +1,553 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.file.azure;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@SuppressWarnings("unchecked")
+public class FilesEndpointConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        FilesEndpoint target = (FilesEndpoint) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "allownullbody":
+        case "allowNullBody": target.setAllowNullBody(property(camelContext, boolean.class, value)); return true;
+        case "antexclude":
+        case "antExclude": target.setAntExclude(property(camelContext, java.lang.String.class, value)); return true;
+        case "antfiltercasesensitive":
+        case "antFilterCaseSensitive": target.setAntFilterCaseSensitive(property(camelContext, boolean.class, value)); return true;
+        case "antinclude":
+        case "antInclude": target.setAntInclude(property(camelContext, java.lang.String.class, value)); return true;
+        case "autocreate":
+        case "autoCreate": target.setAutoCreate(property(camelContext, boolean.class, value)); return true;
+        case "backofferrorthreshold":
+        case "backoffErrorThreshold": target.setBackoffErrorThreshold(property(camelContext, int.class, value)); return true;
+        case "backoffidlethreshold":
+        case "backoffIdleThreshold": target.setBackoffIdleThreshold(property(camelContext, int.class, value)); return true;
+        case "backoffmultiplier":
+        case "backoffMultiplier": target.setBackoffMultiplier(property(camelContext, int.class, value)); return true;
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); return true;
+        case "connecttimeout":
+        case "connectTimeout": target.getConfiguration().setConnectTimeout(property(camelContext, int.class, value)); return true;
+        case "delay": target.setDelay(property(camelContext, long.class, value)); return true;
+        case "delete": target.setDelete(property(camelContext, boolean.class, value)); return true;
+        case "disconnect": target.setDisconnect(property(camelContext, boolean.class, value)); return true;
+        case "disconnectonbatchcomplete":
+        case "disconnectOnBatchComplete": target.setDisconnectOnBatchComplete(property(camelContext, boolean.class, value)); return true;
+        case "donefilename":
+        case "doneFileName": target.setDoneFileName(property(camelContext, java.lang.String.class, value)); return true;
+        case "download": target.setDownload(property(camelContext, boolean.class, value)); return true;
+        case "eagerdeletetargetfile":
+        case "eagerDeleteTargetFile": target.setEagerDeleteTargetFile(property(camelContext, boolean.class, value)); return true;
+        case "eagermaxmessagesperpoll":
+        case "eagerMaxMessagesPerPoll": target.setEagerMaxMessagesPerPoll(property(camelContext, boolean.class, value)); return true;
+        case "exceptionhandler":
+        case "exceptionHandler": target.setExceptionHandler(property(camelContext, org.apache.camel.spi.ExceptionHandler.class, value)); return true;
+        case "exchangepattern":
+        case "exchangePattern": target.setExchangePattern(property(camelContext, org.apache.camel.ExchangePattern.class, value)); return true;
+        case "exclude": target.setExclude(property(camelContext, java.lang.String.class, value)); return true;
+        case "excludeext":
+        case "excludeExt": target.setExcludeExt(property(camelContext, java.lang.String.class, value)); return true;
+        case "exclusivereadlockstrategy":
+        case "exclusiveReadLockStrategy": target.setExclusiveReadLockStrategy(property(camelContext, org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy.class, value)); return true;
+        case "fileexist":
+        case "fileExist": target.setFileExist(property(camelContext, org.apache.camel.component.file.GenericFileExist.class, value)); return true;
+        case "filename":
+        case "fileName": target.setFileName(property(camelContext, java.lang.String.class, value)); return true;
+        case "filter": target.setFilter(property(camelContext, org.apache.camel.component.file.GenericFileFilter.class, value)); return true;
+        case "filterdirectory":
+        case "filterDirectory": target.setFilterDirectory(property(camelContext, java.lang.String.class, value)); return true;
+        case "filterfile":
+        case "filterFile": target.setFilterFile(property(camelContext, java.lang.String.class, value)); return true;
+        case "flatten": target.setFlatten(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 "idempotentkey":
+        case "idempotentKey": target.setIdempotentKey(property(camelContext, java.lang.String.class, value)); return true;
+        case "idempotentrepository":
+        case "idempotentRepository": target.setIdempotentRepository(property(camelContext, org.apache.camel.spi.IdempotentRepository.class, value)); return true;
+        case "inprogressrepository":
+        case "inProgressRepository": target.setInProgressRepository(property(camelContext, org.apache.camel.spi.IdempotentRepository.class, value)); return true;
+        case "include": target.setInclude(property(camelContext, java.lang.String.class, value)); return true;
+        case "includeext":
+        case "includeExt": target.setIncludeExt(property(camelContext, java.lang.String.class, value)); return true;
+        case "initialdelay":
+        case "initialDelay": target.setInitialDelay(property(camelContext, long.class, value)); return true;
+        case "jailstartingdirectory":
+        case "jailStartingDirectory": target.setJailStartingDirectory(property(camelContext, boolean.class, value)); return true;
+        case "keeplastmodified":
+        case "keepLastModified": target.setKeepLastModified(property(camelContext, boolean.class, value)); return true;
+        case "lazystartproducer":
+        case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        case "localworkdirectory":
+        case "localWorkDirectory": target.setLocalWorkDirectory(property(camelContext, java.lang.String.class, value)); return true;
+        case "maxdepth":
+        case "maxDepth": target.setMaxDepth(property(camelContext, int.class, value)); return true;
+        case "maxmessagesperpoll":
+        case "maxMessagesPerPoll": target.setMaxMessagesPerPoll(property(camelContext, int.class, value)); return true;
+        case "maximumreconnectattempts":
+        case "maximumReconnectAttempts": target.setMaximumReconnectAttempts(property(camelContext, int.class, value)); return true;
+        case "mindepth":
+        case "minDepth": target.setMinDepth(property(camelContext, int.class, value)); return true;
+        case "move": target.setMove(property(camelContext, java.lang.String.class, value)); return true;
+        case "moveexistingfilestrategy":
+        case "moveExistingFileStrategy": target.setMoveExistingFileStrategy(property(camelContext, org.apache.camel.component.file.strategy.FileMoveExistingStrategy.class, value)); return true;
+        case "movefailed":
+        case "moveFailed": target.setMoveFailed(property(camelContext, java.lang.String.class, value)); return true;
+        case "noop": target.setNoop(property(camelContext, boolean.class, value)); return true;
+        case "oncompletionexceptionhandler":
+        case "onCompletionExceptionHandler": target.setOnCompletionExceptionHandler(property(camelContext, org.apache.camel.spi.ExceptionHandler.class, value)); return true;
+        case "pollstrategy":
+        case "pollStrategy": target.setPollStrategy(property(camelContext, org.apache.camel.spi.PollingConsumerPollStrategy.class, value)); return true;
+        case "premove":
+        case "preMove": target.setPreMove(property(camelContext, java.lang.String.class, value)); return true;
+        case "presort":
+        case "preSort": target.setPreSort(property(camelContext, boolean.class, value)); return true;
+        case "processstrategy":
+        case "processStrategy": target.setProcessStrategy(property(camelContext, org.apache.camel.component.file.GenericFileProcessStrategy.class, value)); return true;
+        case "readlock":
+        case "readLock": target.setReadLock(property(camelContext, java.lang.String.class, value)); return true;
+        case "readlockcheckinterval":
+        case "readLockCheckInterval": target.setReadLockCheckInterval(property(camelContext, long.class, value)); return true;
+        case "readlockdeleteorphanlockfiles":
+        case "readLockDeleteOrphanLockFiles": target.setReadLockDeleteOrphanLockFiles(property(camelContext, boolean.class, value)); return true;
+        case "readlocklogginglevel":
+        case "readLockLoggingLevel": target.setReadLockLoggingLevel(property(camelContext, org.apache.camel.LoggingLevel.class, value)); return true;
+        case "readlockmarkerfile":
+        case "readLockMarkerFile": target.setReadLockMarkerFile(property(camelContext, boolean.class, value)); return true;
+        case "readlockminage":
+        case "readLockMinAge": target.setReadLockMinAge(property(camelContext, long.class, value)); return true;
+        case "readlockminlength":
+        case "readLockMinLength": target.setReadLockMinLength(property(camelContext, long.class, value)); return true;
+        case "readlockremoveoncommit":
+        case "readLockRemoveOnCommit": target.setReadLockRemoveOnCommit(property(camelContext, boolean.class, value)); return true;
+        case "readlockremoveonrollback":
+        case "readLockRemoveOnRollback": target.setReadLockRemoveOnRollback(property(camelContext, boolean.class, value)); return true;
+        case "readlocktimeout":
+        case "readLockTimeout": target.setReadLockTimeout(property(camelContext, long.class, value)); return true;
+        case "reconnectdelay":
+        case "reconnectDelay": target.setReconnectDelay(property(camelContext, java.time.Duration.class, value).toMillis()); return true;
+        case "recursive": target.setRecursive(property(camelContext, boolean.class, value)); return true;
+        case "repeatcount":
+        case "repeatCount": target.setRepeatCount(property(camelContext, long.class, value)); return true;
+        case "resumedownload":
+        case "resumeDownload": target.setResumeDownload(property(camelContext, boolean.class, value)); return true;
+        case "runlogginglevel":
+        case "runLoggingLevel": target.setRunLoggingLevel(property(camelContext, org.apache.camel.LoggingLevel.class, value)); return true;
+        case "scheduledexecutorservice":
+        case "scheduledExecutorService": target.setScheduledExecutorService(property(camelContext, java.util.concurrent.ScheduledExecutorService.class, value)); return true;
+        case "scheduler": target.setScheduler(property(camelContext, java.lang.Object.class, value)); return true;
+        case "schedulerproperties":
+        case "schedulerProperties": target.setSchedulerProperties(property(camelContext, java.util.Map.class, value)); return true;
+        case "sdd": target.getToken().setSdd(property(camelContext, java.lang.String.class, value)); return true;
+        case "se": target.getToken().setSe(property(camelContext, java.lang.String.class, value)); return true;
+        case "sendemptymessagewhenidle":
+        case "sendEmptyMessageWhenIdle": target.setSendEmptyMessageWhenIdle(property(camelContext, boolean.class, value)); return true;
+        case "sharedkey":
+        case "sharedKey": target.getConfiguration().setSharedKey(property(camelContext, java.lang.String.class, value)); return true;
+        case "shuffle": target.setShuffle(property(camelContext, boolean.class, value)); return true;
+        case "si": target.getToken().setSi(property(camelContext, java.lang.String.class, value)); return true;
+        case "sig": target.getToken().setSig(property(camelContext, java.lang.String.class, value)); return true;
+        case "sip": target.getToken().setSip(property(camelContext, java.lang.String.class, value)); return true;
+        case "sortby":
+        case "sortBy": target.setSortBy(property(camelContext, java.lang.String.class, value)); return true;
+        case "sorter": target.setSorter(property(camelContext, java.util.Comparator.class, value)); return true;
+        case "sp": target.getToken().setSp(property(camelContext, java.lang.String.class, value)); return true;
+        case "spr": target.getToken().setSpr(property(camelContext, java.lang.String.class, value)); return true;
+        case "sr": target.getToken().setSr(property(camelContext, java.lang.String.class, value)); return true;
+        case "srt": target.getToken().setSrt(property(camelContext, java.lang.String.class, value)); return true;
+        case "ss": target.getToken().setSs(property(camelContext, java.lang.String.class, value)); return true;
+        case "st": target.getToken().setSt(property(camelContext, java.lang.String.class, value)); return true;
+        case "startscheduler":
+        case "startScheduler": target.setStartScheduler(property(camelContext, boolean.class, value)); return true;
+        case "streamdownload":
+        case "streamDownload": target.getConfiguration().setStreamDownload(property(camelContext, boolean.class, value)); return true;
+        case "sv": target.getToken().setSv(property(camelContext, java.lang.String.class, value)); return true;
+        case "tempfilename":
+        case "tempFileName": target.setTempFileName(property(camelContext, java.lang.String.class, value)); return true;
+        case "tempprefix":
+        case "tempPrefix": target.setTempPrefix(property(camelContext, java.lang.String.class, value)); return true;
+        case "throwexceptiononconnectfailed":
+        case "throwExceptionOnConnectFailed": target.getConfiguration().setThrowExceptionOnConnectFailed(property(camelContext, boolean.class, value)); return true;
+        case "timeunit":
+        case "timeUnit": target.setTimeUnit(property(camelContext, java.util.concurrent.TimeUnit.class, value)); return true;
+        case "timeout": target.getConfiguration().setTimeout(property(camelContext, int.class, value)); return true;
+        case "usefixeddelay":
+        case "useFixedDelay": target.setUseFixedDelay(property(camelContext, boolean.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "allownullbody":
+        case "allowNullBody": return boolean.class;
+        case "antexclude":
+        case "antExclude": return java.lang.String.class;
+        case "antfiltercasesensitive":
+        case "antFilterCaseSensitive": return boolean.class;
+        case "antinclude":
+        case "antInclude": return java.lang.String.class;
+        case "autocreate":
+        case "autoCreate": return boolean.class;
+        case "backofferrorthreshold":
+        case "backoffErrorThreshold": return int.class;
+        case "backoffidlethreshold":
+        case "backoffIdleThreshold": return int.class;
+        case "backoffmultiplier":
+        case "backoffMultiplier": return int.class;
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": return boolean.class;
+        case "connecttimeout":
+        case "connectTimeout": return int.class;
+        case "delay": return long.class;
+        case "delete": return boolean.class;
+        case "disconnect": return boolean.class;
+        case "disconnectonbatchcomplete":
+        case "disconnectOnBatchComplete": return boolean.class;
+        case "donefilename":
+        case "doneFileName": return java.lang.String.class;
+        case "download": return boolean.class;
+        case "eagerdeletetargetfile":
+        case "eagerDeleteTargetFile": return boolean.class;
+        case "eagermaxmessagesperpoll":
+        case "eagerMaxMessagesPerPoll": return boolean.class;
+        case "exceptionhandler":
+        case "exceptionHandler": return org.apache.camel.spi.ExceptionHandler.class;
+        case "exchangepattern":
+        case "exchangePattern": return org.apache.camel.ExchangePattern.class;
+        case "exclude": return java.lang.String.class;
+        case "excludeext":
+        case "excludeExt": return java.lang.String.class;
+        case "exclusivereadlockstrategy":
+        case "exclusiveReadLockStrategy": return org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy.class;
+        case "fileexist":
+        case "fileExist": return org.apache.camel.component.file.GenericFileExist.class;
+        case "filename":
+        case "fileName": return java.lang.String.class;
+        case "filter": return org.apache.camel.component.file.GenericFileFilter.class;
+        case "filterdirectory":
+        case "filterDirectory": return java.lang.String.class;
+        case "filterfile":
+        case "filterFile": return java.lang.String.class;
+        case "flatten": return boolean.class;
+        case "greedy": return boolean.class;
+        case "idempotent": return java.lang.Boolean.class;
+        case "idempotentkey":
+        case "idempotentKey": return java.lang.String.class;
+        case "idempotentrepository":
+        case "idempotentRepository": return org.apache.camel.spi.IdempotentRepository.class;
+        case "inprogressrepository":
+        case "inProgressRepository": return org.apache.camel.spi.IdempotentRepository.class;
+        case "include": return java.lang.String.class;
+        case "includeext":
+        case "includeExt": return java.lang.String.class;
+        case "initialdelay":
+        case "initialDelay": return long.class;
+        case "jailstartingdirectory":
+        case "jailStartingDirectory": return boolean.class;
+        case "keeplastmodified":
+        case "keepLastModified": return boolean.class;
+        case "lazystartproducer":
+        case "lazyStartProducer": return boolean.class;
+        case "localworkdirectory":
+        case "localWorkDirectory": return java.lang.String.class;
+        case "maxdepth":
+        case "maxDepth": return int.class;
+        case "maxmessagesperpoll":
+        case "maxMessagesPerPoll": return int.class;
+        case "maximumreconnectattempts":
+        case "maximumReconnectAttempts": return int.class;
+        case "mindepth":
+        case "minDepth": return int.class;
+        case "move": return java.lang.String.class;
+        case "moveexistingfilestrategy":
+        case "moveExistingFileStrategy": return org.apache.camel.component.file.strategy.FileMoveExistingStrategy.class;
+        case "movefailed":
+        case "moveFailed": return java.lang.String.class;
+        case "noop": return boolean.class;
+        case "oncompletionexceptionhandler":
+        case "onCompletionExceptionHandler": return org.apache.camel.spi.ExceptionHandler.class;
+        case "pollstrategy":
+        case "pollStrategy": return org.apache.camel.spi.PollingConsumerPollStrategy.class;
+        case "premove":
+        case "preMove": return java.lang.String.class;
+        case "presort":
+        case "preSort": return boolean.class;
+        case "processstrategy":
+        case "processStrategy": return org.apache.camel.component.file.GenericFileProcessStrategy.class;
+        case "readlock":
+        case "readLock": return java.lang.String.class;
+        case "readlockcheckinterval":
+        case "readLockCheckInterval": return long.class;
+        case "readlockdeleteorphanlockfiles":
+        case "readLockDeleteOrphanLockFiles": return boolean.class;
+        case "readlocklogginglevel":
+        case "readLockLoggingLevel": return org.apache.camel.LoggingLevel.class;
+        case "readlockmarkerfile":
+        case "readLockMarkerFile": return boolean.class;
+        case "readlockminage":
+        case "readLockMinAge": return long.class;
+        case "readlockminlength":
+        case "readLockMinLength": return long.class;
+        case "readlockremoveoncommit":
+        case "readLockRemoveOnCommit": return boolean.class;
+        case "readlockremoveonrollback":
+        case "readLockRemoveOnRollback": return boolean.class;
+        case "readlocktimeout":
+        case "readLockTimeout": return long.class;
+        case "reconnectdelay":
+        case "reconnectDelay": return long.class;
+        case "recursive": return boolean.class;
+        case "repeatcount":
+        case "repeatCount": return long.class;
+        case "resumedownload":
+        case "resumeDownload": return boolean.class;
+        case "runlogginglevel":
+        case "runLoggingLevel": return org.apache.camel.LoggingLevel.class;
+        case "scheduledexecutorservice":
+        case "scheduledExecutorService": return java.util.concurrent.ScheduledExecutorService.class;
+        case "scheduler": return java.lang.Object.class;
+        case "schedulerproperties":
+        case "schedulerProperties": return java.util.Map.class;
+        case "sdd": return java.lang.String.class;
+        case "se": return java.lang.String.class;
+        case "sendemptymessagewhenidle":
+        case "sendEmptyMessageWhenIdle": return boolean.class;
+        case "sharedkey":
+        case "sharedKey": return java.lang.String.class;
+        case "shuffle": return boolean.class;
+        case "si": return java.lang.String.class;
+        case "sig": return java.lang.String.class;
+        case "sip": return java.lang.String.class;
+        case "sortby":
+        case "sortBy": return java.lang.String.class;
+        case "sorter": return java.util.Comparator.class;
+        case "sp": return java.lang.String.class;
+        case "spr": return java.lang.String.class;
+        case "sr": return java.lang.String.class;
+        case "srt": return java.lang.String.class;
+        case "ss": return java.lang.String.class;
+        case "st": return java.lang.String.class;
+        case "startscheduler":
+        case "startScheduler": return boolean.class;
+        case "streamdownload":
+        case "streamDownload": return boolean.class;
+        case "sv": return java.lang.String.class;
+        case "tempfilename":
+        case "tempFileName": return java.lang.String.class;
+        case "tempprefix":
+        case "tempPrefix": return java.lang.String.class;
+        case "throwexceptiononconnectfailed":
+        case "throwExceptionOnConnectFailed": return boolean.class;
+        case "timeunit":
+        case "timeUnit": return java.util.concurrent.TimeUnit.class;
+        case "timeout": return int.class;
+        case "usefixeddelay":
+        case "useFixedDelay": return boolean.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        FilesEndpoint target = (FilesEndpoint) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "allownullbody":
+        case "allowNullBody": return target.isAllowNullBody();
+        case "antexclude":
+        case "antExclude": return target.getAntExclude();
+        case "antfiltercasesensitive":
+        case "antFilterCaseSensitive": return target.isAntFilterCaseSensitive();
+        case "antinclude":
+        case "antInclude": return target.getAntInclude();
+        case "autocreate":
+        case "autoCreate": return target.isAutoCreate();
+        case "backofferrorthreshold":
+        case "backoffErrorThreshold": return target.getBackoffErrorThreshold();
+        case "backoffidlethreshold":
+        case "backoffIdleThreshold": return target.getBackoffIdleThreshold();
+        case "backoffmultiplier":
+        case "backoffMultiplier": return target.getBackoffMultiplier();
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": return target.isBridgeErrorHandler();
+        case "connecttimeout":
+        case "connectTimeout": return target.getConfiguration().getConnectTimeout();
+        case "delay": return target.getDelay();
+        case "delete": return target.isDelete();
+        case "disconnect": return target.isDisconnect();
+        case "disconnectonbatchcomplete":
+        case "disconnectOnBatchComplete": return target.isDisconnectOnBatchComplete();
+        case "donefilename":
+        case "doneFileName": return target.getDoneFileName();
+        case "download": return target.isDownload();
+        case "eagerdeletetargetfile":
+        case "eagerDeleteTargetFile": return target.isEagerDeleteTargetFile();
+        case "eagermaxmessagesperpoll":
+        case "eagerMaxMessagesPerPoll": return target.isEagerMaxMessagesPerPoll();
+        case "exceptionhandler":
+        case "exceptionHandler": return target.getExceptionHandler();
+        case "exchangepattern":
+        case "exchangePattern": return target.getExchangePattern();
+        case "exclude": return target.getExclude();
+        case "excludeext":
+        case "excludeExt": return target.getExcludeExt();
+        case "exclusivereadlockstrategy":
+        case "exclusiveReadLockStrategy": return target.getExclusiveReadLockStrategy();
+        case "fileexist":
+        case "fileExist": return target.getFileExist();
+        case "filename":
+        case "fileName": return target.getFileName();
+        case "filter": return target.getFilter();
+        case "filterdirectory":
+        case "filterDirectory": return target.getFilterDirectory();
+        case "filterfile":
+        case "filterFile": return target.getFilterFile();
+        case "flatten": return target.isFlatten();
+        case "greedy": return target.isGreedy();
+        case "idempotent": return target.getIdempotent();
+        case "idempotentkey":
+        case "idempotentKey": return target.getIdempotentKey();
+        case "idempotentrepository":
+        case "idempotentRepository": return target.getIdempotentRepository();
+        case "inprogressrepository":
+        case "inProgressRepository": return target.getInProgressRepository();
+        case "include": return target.getInclude();
+        case "includeext":
+        case "includeExt": return target.getIncludeExt();
+        case "initialdelay":
+        case "initialDelay": return target.getInitialDelay();
+        case "jailstartingdirectory":
+        case "jailStartingDirectory": return target.isJailStartingDirectory();
+        case "keeplastmodified":
+        case "keepLastModified": return target.isKeepLastModified();
+        case "lazystartproducer":
+        case "lazyStartProducer": return target.isLazyStartProducer();
+        case "localworkdirectory":
+        case "localWorkDirectory": return target.getLocalWorkDirectory();
+        case "maxdepth":
+        case "maxDepth": return target.getMaxDepth();
+        case "maxmessagesperpoll":
+        case "maxMessagesPerPoll": return target.getMaxMessagesPerPoll();
+        case "maximumreconnectattempts":
+        case "maximumReconnectAttempts": return target.getMaximumReconnectAttempts();
+        case "mindepth":
+        case "minDepth": return target.getMinDepth();
+        case "move": return target.getMove();
+        case "moveexistingfilestrategy":
+        case "moveExistingFileStrategy": return target.getMoveExistingFileStrategy();
+        case "movefailed":
+        case "moveFailed": return target.getMoveFailed();
+        case "noop": return target.isNoop();
+        case "oncompletionexceptionhandler":
+        case "onCompletionExceptionHandler": return target.getOnCompletionExceptionHandler();
+        case "pollstrategy":
+        case "pollStrategy": return target.getPollStrategy();
+        case "premove":
+        case "preMove": return target.getPreMove();
+        case "presort":
+        case "preSort": return target.isPreSort();
+        case "processstrategy":
+        case "processStrategy": return target.getProcessStrategy();
+        case "readlock":
+        case "readLock": return target.getReadLock();
+        case "readlockcheckinterval":
+        case "readLockCheckInterval": return target.getReadLockCheckInterval();
+        case "readlockdeleteorphanlockfiles":
+        case "readLockDeleteOrphanLockFiles": return target.isReadLockDeleteOrphanLockFiles();
+        case "readlocklogginglevel":
+        case "readLockLoggingLevel": return target.getReadLockLoggingLevel();
+        case "readlockmarkerfile":
+        case "readLockMarkerFile": return target.isReadLockMarkerFile();
+        case "readlockminage":
+        case "readLockMinAge": return target.getReadLockMinAge();
+        case "readlockminlength":
+        case "readLockMinLength": return target.getReadLockMinLength();
+        case "readlockremoveoncommit":
+        case "readLockRemoveOnCommit": return target.isReadLockRemoveOnCommit();
+        case "readlockremoveonrollback":
+        case "readLockRemoveOnRollback": return target.isReadLockRemoveOnRollback();
+        case "readlocktimeout":
+        case "readLockTimeout": return target.getReadLockTimeout();
+        case "reconnectdelay":
+        case "reconnectDelay": return target.getReconnectDelay();
+        case "recursive": return target.isRecursive();
+        case "repeatcount":
+        case "repeatCount": return target.getRepeatCount();
+        case "resumedownload":
+        case "resumeDownload": return target.isResumeDownload();
+        case "runlogginglevel":
+        case "runLoggingLevel": return target.getRunLoggingLevel();
+        case "scheduledexecutorservice":
+        case "scheduledExecutorService": return target.getScheduledExecutorService();
+        case "scheduler": return target.getScheduler();
+        case "schedulerproperties":
+        case "schedulerProperties": return target.getSchedulerProperties();
+        case "sdd": return target.getToken().getSdd();
+        case "se": return target.getToken().getSe();
+        case "sendemptymessagewhenidle":
+        case "sendEmptyMessageWhenIdle": return target.isSendEmptyMessageWhenIdle();
+        case "sharedkey":
+        case "sharedKey": return target.getConfiguration().getSharedKey();
+        case "shuffle": return target.isShuffle();
+        case "si": return target.getToken().getSi();
+        case "sig": return target.getToken().getSig();
+        case "sip": return target.getToken().getSip();
+        case "sortby":
+        case "sortBy": return target.getSortBy();
+        case "sorter": return target.getSorter();
+        case "sp": return target.getToken().getSp();
+        case "spr": return target.getToken().getSpr();
+        case "sr": return target.getToken().getSr();
+        case "srt": return target.getToken().getSrt();
+        case "ss": return target.getToken().getSs();
+        case "st": return target.getToken().getSt();
+        case "startscheduler":
+        case "startScheduler": return target.isStartScheduler();
+        case "streamdownload":
+        case "streamDownload": return target.getConfiguration().isStreamDownload();
+        case "sv": return target.getToken().getSv();
+        case "tempfilename":
+        case "tempFileName": return target.getTempFileName();
+        case "tempprefix":
+        case "tempPrefix": return target.getTempPrefix();
+        case "throwexceptiononconnectfailed":
+        case "throwExceptionOnConnectFailed": return target.getConfiguration().isThrowExceptionOnConnectFailed();
+        case "timeunit":
+        case "timeUnit": return target.getTimeUnit();
+        case "timeout": return target.getConfiguration().getTimeout();
+        case "usefixeddelay":
+        case "useFixedDelay": return target.isUseFixedDelay();
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getCollectionValueType(Object target, String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "exclusivereadlockstrategy":
+        case "exclusiveReadLockStrategy": return com.azure.storage.file.share.models.ShareFileItem.class;
+        case "filter": return com.azure.storage.file.share.models.ShareFileItem.class;
+        case "processstrategy":
+        case "processStrategy": return com.azure.storage.file.share.models.ShareFileItem.class;
+        case "schedulerproperties":
+        case "schedulerProperties": return java.lang.Object.class;
+        case "sorter": return org.apache.camel.component.file.GenericFile.class;
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-azure/camel-azure-files/src/generated/java/org/apache/camel/component/file/azure/FilesEndpointUriFactory.java b/components/camel-azure/camel-azure-files/src/generated/java/org/apache/camel/component/file/azure/FilesEndpointUriFactory.java
new file mode 100644
index 00000000000..f9bf8fd91a4
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/generated/java/org/apache/camel/component/file/azure/FilesEndpointUriFactory.java
@@ -0,0 +1,185 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.file.azure;
+
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.spi.EndpointUriFactory;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+public class FilesEndpointUriFactory extends org.apache.camel.support.component.EndpointUriFactorySupport implements EndpointUriFactory {
+
+    private static final String BASE = "://account[.host]/share[/dir]";
+
+    private static final Set<String> PROPERTY_NAMES;
+    private static final Set<String> SECRET_PROPERTY_NAMES;
+    private static final Set<String> MULTI_VALUE_PREFIXES;
+    static {
+        Set<String> props = new HashSet<>(100);
+        props.add("allowNullBody");
+        props.add("antExclude");
+        props.add("antFilterCaseSensitive");
+        props.add("antInclude");
+        props.add("autoCreate");
+        props.add("backoffErrorThreshold");
+        props.add("backoffIdleThreshold");
+        props.add("backoffMultiplier");
+        props.add("bridgeErrorHandler");
+        props.add("connectTimeout");
+        props.add("delay");
+        props.add("delete");
+        props.add("directoryName");
+        props.add("disconnect");
+        props.add("disconnectOnBatchComplete");
+        props.add("doneFileName");
+        props.add("download");
+        props.add("eagerDeleteTargetFile");
+        props.add("eagerMaxMessagesPerPoll");
+        props.add("exceptionHandler");
+        props.add("exchangePattern");
+        props.add("exclude");
+        props.add("excludeExt");
+        props.add("exclusiveReadLockStrategy");
+        props.add("fileExist");
+        props.add("fileName");
+        props.add("filter");
+        props.add("filterDirectory");
+        props.add("filterFile");
+        props.add("flatten");
+        props.add("greedy");
+        props.add("host");
+        props.add("idempotent");
+        props.add("idempotentKey");
+        props.add("idempotentRepository");
+        props.add("inProgressRepository");
+        props.add("include");
+        props.add("includeExt");
+        props.add("initialDelay");
+        props.add("jailStartingDirectory");
+        props.add("keepLastModified");
+        props.add("lazyStartProducer");
+        props.add("localWorkDirectory");
+        props.add("maxDepth");
+        props.add("maxMessagesPerPoll");
+        props.add("maximumReconnectAttempts");
+        props.add("minDepth");
+        props.add("move");
+        props.add("moveExistingFileStrategy");
+        props.add("moveFailed");
+        props.add("noop");
+        props.add("onCompletionExceptionHandler");
+        props.add("pollStrategy");
+        props.add("port");
+        props.add("preMove");
+        props.add("preSort");
+        props.add("processStrategy");
+        props.add("readLock");
+        props.add("readLockCheckInterval");
+        props.add("readLockDeleteOrphanLockFiles");
+        props.add("readLockLoggingLevel");
+        props.add("readLockMarkerFile");
+        props.add("readLockMinAge");
+        props.add("readLockMinLength");
+        props.add("readLockRemoveOnCommit");
+        props.add("readLockRemoveOnRollback");
+        props.add("readLockTimeout");
+        props.add("reconnectDelay");
+        props.add("recursive");
+        props.add("repeatCount");
+        props.add("resumeDownload");
+        props.add("runLoggingLevel");
+        props.add("scheduledExecutorService");
+        props.add("scheduler");
+        props.add("schedulerProperties");
+        props.add("sdd");
+        props.add("se");
+        props.add("sendEmptyMessageWhenIdle");
+        props.add("sharedKey");
+        props.add("shuffle");
+        props.add("si");
+        props.add("sig");
+        props.add("sip");
+        props.add("sortBy");
+        props.add("sorter");
+        props.add("sp");
+        props.add("spr");
+        props.add("sr");
+        props.add("srt");
+        props.add("ss");
+        props.add("st");
+        props.add("startScheduler");
+        props.add("streamDownload");
+        props.add("sv");
+        props.add("tempFileName");
+        props.add("tempPrefix");
+        props.add("throwExceptionOnConnectFailed");
+        props.add("timeUnit");
+        props.add("timeout");
+        props.add("useFixedDelay");
+        PROPERTY_NAMES = Collections.unmodifiableSet(props);
+        Set<String> secretProps = new HashSet<>(13);
+        secretProps.add("sdd");
+        secretProps.add("se");
+        secretProps.add("sharedKey");
+        secretProps.add("si");
+        secretProps.add("sig");
+        secretProps.add("sip");
+        secretProps.add("sp");
+        secretProps.add("spr");
+        secretProps.add("sr");
+        secretProps.add("srt");
+        secretProps.add("ss");
+        secretProps.add("st");
+        secretProps.add("sv");
+        SECRET_PROPERTY_NAMES = Collections.unmodifiableSet(secretProps);
+        Set<String> prefixes = new HashSet<>(1);
+        prefixes.add("scheduler.");
+        MULTI_VALUE_PREFIXES = Collections.unmodifiableSet(prefixes);
+    }
+
+    @Override
+    public boolean isEnabled(String scheme) {
+        return "azure-files".equals(scheme);
+    }
+
+    @Override
+    public String buildUri(String scheme, Map<String, Object> properties, boolean encode) throws URISyntaxException {
+        String syntax = scheme + BASE;
+        String uri = syntax;
+
+        Map<String, Object> copy = new HashMap<>(properties);
+
+        uri = buildPathParameter(syntax, uri, "host", null, true, copy);
+        uri = buildPathParameter(syntax, uri, "port", null, false, copy);
+        uri = buildPathParameter(syntax, uri, "directoryName", null, false, copy);
+        uri = buildQueryParameters(uri, copy, encode);
+        return uri;
+    }
+
+    @Override
+    public Set<String> propertyNames() {
+        return PROPERTY_NAMES;
+    }
+
+    @Override
+    public Set<String> secretPropertyNames() {
+        return SECRET_PROPERTY_NAMES;
+    }
+
+    @Override
+    public Set<String> multiValuePrefixes() {
+        return MULTI_VALUE_PREFIXES;
+    }
+
+    @Override
+    public boolean isLenientProperties() {
+        return false;
+    }
+}
+
diff --git a/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/component.properties b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/component.properties
new file mode 100644
index 00000000000..233a27d7d20
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/component.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+components=azure-files
+groupId=org.apache.camel
+artifactId=camel-azure-files
+version=3.21.0-SNAPSHOT
+projectName=Camel :: Azure Files
+projectDescription=Camel Azure Files Component (Preview)
diff --git a/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/component/azure-files b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/component/azure-files
new file mode 100644
index 00000000000..0b45e46a9c9
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/component/azure-files
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.file.azure.FilesComponent
diff --git a/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-files-component b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-files-component
new file mode 100644
index 00000000000..f0ecfc414a2
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-files-component
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.file.azure.FilesComponentConfigurer
diff --git a/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-files-endpoint b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-files-endpoint
new file mode 100644
index 00000000000..d40d0080715
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-files-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.file.azure.FilesEndpointConfigurer
diff --git a/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/urifactory/azure-files-endpoint b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/urifactory/azure-files-endpoint
new file mode 100644
index 00000000000..0a6ca7faae4
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/services/org/apache/camel/urifactory/azure-files-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.file.azure.FilesEndpointUriFactory
diff --git a/components/camel-azure/camel-azure-files/src/generated/resources/org/apache/camel/component/file/azure/azure-files.json b/components/camel-azure/camel-azure-files/src/generated/resources/org/apache/camel/component/file/azure/azure-files.json
new file mode 100644
index 00000000000..8ed1db2fe4e
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/generated/resources/org/apache/camel/component/file/azure/azure-files.json
@@ -0,0 +1,142 @@
+{
+  "component": {
+    "kind": "component",
+    "name": "azure-files",
+    "title": "Azure Files",
+    "description": "Camel Azure Files Component (Preview)",
+    "deprecated": false,
+    "deprecationNote": "",
+    "firstVersion": "3.21.0",
+    "label": "cloud,file",
+    "javaType": "org.apache.camel.component.file.azure.FilesComponent",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-azure-files",
+    "version": "3.21.0-SNAPSHOT",
+    "scheme": "azure-files",
+    "extendsScheme": "file",
+    "syntax": "azure-files:\/\/account[.host]\/share[\/dir]",
+    "async": false,
+    "api": false,
+    "consumerOnly": false,
+    "producerOnly": false,
+    "lenientProperties": false
+  },
+  "componentProperties": {
+    "bridgeErrorHandler": { "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now be processed as a me [...]
+    "lazyStartProducer": { "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during star [...]
+    "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
+  },
+  "headers": {
+    "CamelFileLength": { "kind": "header", "displayName": "", "group": "both", "label": "both", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "A long value containing the file size. For producer, known length helps if the body converts to InputStream more efficiently than to bytes array.", "constantName": "org.apache.camel.component.file.azure.FilesHeaders#FILE_LENGTH" },
+    "CamelFileLastModified": { "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "A Long value containing the last modified timestamp of the file.", "constantName": "org.apache.camel.component.file.azure.FilesHeaders#FILE_LAST_MODIFIED" },
+    "CamelFileName": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Specifies the output file name (relative to the endpoint directory) to be used for the output message when sending to the endpoint. If this is not present and no expression either, then a generated message ID is used as the filename instead.", "constantName": "o [...]
+    "CamelFileNameOnly": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Only the file name (the name with no leading paths).", "constantName": "org.apache.camel.component.file.azure.FilesHeaders#FILE_NAME_ONLY" },
+    "CamelFileParent": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The parent path.", "constantName": "org.apache.camel.component.file.azure.FilesHeaders#FILE_PARENT" },
+    "CamelRemoteFileInputStream": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.io.InputStream", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The remote file input stream.", "constantName": "org.apache.camel.component.file.azure.FilesHeaders#REMOTE_FILE_INPUT_STREAM" },
+    "CamelFileLocalWorkPath": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Path to the local work file, if local work directory is used.", "constantName": "org.apache.camel.component.file.azure.FilesHeaders#FILE_LOCAL_WORK_PATH" },
+    "CamelFileHost": { "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The remote hostname.", "constantName": "org.apache.camel.component.file.azure.FilesHeaders#FILE_HOST" }
+  },
+  "properties": {
+    "host": { "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.azure.FilesConfiguration", "configurationField": "configuration", "description": "Hostname of the FTP server" },
+    "port": { "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.azure.FilesConfiguration", "configurationField": "configuration", "description": "Port of the FTP server" },
+    "directoryName": { "kind": "path", "displayName": "Directory Name", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.azure.FilesConfiguration", "configurationField": "configuration", "description": "The starting directory" },
+    "disconnect": { "kind": "parameter", "displayName": "Disconnect", "group": "common", "label": "common", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether or not to disconnect from remote FTP server right after use. Disconnect will only disconnect the current connection to the FTP server. If you have a consumer which you want to stop, then you need to stop the consumer\/ [...]
+    "doneFileName": { "kind": "parameter", "displayName": "Done File Name", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Producer: If provided, then Camel will write a 2nd done file when the original file has been written. The done file will be empty. This option configures what file name to use. Either you can specify a fixed name. Or you can use dynamic plac [...]
+    "fileName": { "kind": "parameter", "displayName": "File Name", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Use Expression such as File Language to dynamically set the filename. For consumers, it's used as a filename filter. For producers, it's used to evaluate the filename to write. If an expression is set, it take precedence over the CamelFileName header [...]
+    "delete": { "kind": "parameter", "displayName": "Delete", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If true, the file will be deleted after it is processed successfully." },
+    "moveFailed": { "kind": "parameter", "displayName": "Move Failed", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the move failure expression based on Simple language. For example, to move files into a .error subdirectory use: .error. Note: When moving the files to the fail location Camel will handle the error and will not pick up the file again." },
+    "noop": { "kind": "parameter", "displayName": "Noop", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If true, the file is not moved or deleted in any way. This option is good for readonly data, or for ETL type requirements. If noop=true, Camel will set idempotent=true as well, to avoid consuming the same files over and over again." },
+    "preMove": { "kind": "parameter", "displayName": "Pre Move", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Expression (such as File Language) used to dynamically set the filename when moving it before processing. For example to move in-progress files into the order directory set this value to order." },
+    "preSort": { "kind": "parameter", "displayName": "Pre Sort", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "When pre-sort is enabled then the consumer will sort the file and directory names during polling, that was retrieved from the file system. You may want to do this in case you need to operate on the files in a sorted order. The  [...]
+    "recursive": { "kind": "parameter", "displayName": "Recursive", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If a directory, will look for files in all the sub-directories as well." },
+    "resumeDownload": { "kind": "parameter", "displayName": "Resume Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Configures whether resume download is enabled. In addition the options localWorkDirectory must be configured so downloaded files are stored in a local directory, which is required to support resuming of downloads." },
+    "sendEmptyMessageWhenIdle": { "kind": "parameter", "displayName": "Send Empty Message When Idle", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If the polling consumer did not poll any files, you can enable this option to send an empty message (no body) instead." },
+    "streamDownload": { "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.azure.FilesConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, th [...]
+    "bridgeErrorHandler": { "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 occurred while the consumer is trying to pickup incoming messages, or the likes, will now [...]
+    "download": { "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file su [...]
+    "exceptionHandler": { "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 default the con [...]
+    "exchangePattern": { "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut", "InOptionalOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "inProgressRepository": { "kind": "parameter", "displayName": "In Progress Repository", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.IdempotentRepository", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable in-progress repository org.apache.camel.spi.IdempotentRepository. The in-progress repository is used to account the current in progress files being consumed.  [...]
+    "localWorkDirectory": { "kind": "parameter", "displayName": "Local Work Directory", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "When consuming, a local work directory can be used to store the remote file content directly in local files, to avoid loading the content into memory. This is beneficial, if you consume a very big re [...]
+    "onCompletionExceptionHandler": { "kind": "parameter", "displayName": "On Completion Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.ExceptionHandler to handle any thrown exceptions that happens during the file on completion process where the consumer d [...]
+    "pollStrategy": { "kind": "parameter", "displayName": "Poll Strategy", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.PollingConsumerPollStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable org.apache.camel.PollingConsumerPollingStrategy allowing you to provide your custom implementation to control error handling usually occurred during the poll operation  [...]
+    "processStrategy": { "kind": "parameter", "displayName": "Process Strategy", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.file.GenericFileProcessStrategy<com.azure.storage.file.share.models.ShareFileItem>", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable org.apache.camel.component.file.GenericFileProcessStrategy allowing you to implement your own readLoc [...]
+    "fileExist": { "kind": "parameter", "displayName": "File Exist", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "org.apache.camel.component.file.GenericFileExist", "enum": [ "Override", "Append", "Fail", "Ignore", "Move", "TryRename" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "Override", "description": "What to do if a file already exists with the same name. Override, which is the default, replaces the exis [...]
+    "flatten": { "kind": "parameter", "displayName": "Flatten", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Flatten is used to flatten the file name path to strip any leading paths, so it's just the file name. This allows you to consume recursively into sub-directories, but when you eg write the files to another directory they will be [...]
+    "jailStartingDirectory": { "kind": "parameter", "displayName": "Jail Starting Directory", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Used for jailing (restricting) writing files to the starting directory (and sub) only. This is enabled by default to not allow Camel to write files to outside directories (to be more secured out of t [...]
+    "tempFileName": { "kind": "parameter", "displayName": "Temp File Name", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The same as tempPrefix option but offering a more fine grained control on the naming of the temporary filename as it uses the File Language. The location for tempFilename is relative to the final file location in the option 'fileNa [...]
+    "tempPrefix": { "kind": "parameter", "displayName": "Temp Prefix", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option is used to write the file using a temporary name and then, after the write is complete, rename it to the real name. Can be used to identify files being written and also avoid consumers (not using exclusive read locks) readin [...]
+    "allowNullBody": { "kind": "parameter", "displayName": "Allow Null Body", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Used to specify if a null body is allowed during file writing. If set to true then an empty file will be created, when set to false, and attempting to send a null body to the file component, a G [...]
+    "disconnectOnBatchComplete": { "kind": "parameter", "displayName": "Disconnect On Batch Complete", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether or not to disconnect from remote FTP server right after a Batch upload is complete. disconnectOnBatchComplete will only disconnect the current connection to the  [...]
+    "eagerDeleteTargetFile": { "kind": "parameter", "displayName": "Eager Delete Target File", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether or not to eagerly delete any existing target file. This option only applies when you use fileExists=Override and the tempFileName option as well. You can use this to disa [...]
+    "keepLastModified": { "kind": "parameter", "displayName": "Keep Last Modified", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Will keep the last modified timestamp from the source file (if any). Will use the FileConstants.FILE_LAST_MODIFIED header to located the timestamp. This header can contain either a java.ut [...]
+    "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may other [...]
+    "moveExistingFileStrategy": { "kind": "parameter", "displayName": "Move Existing File Strategy", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.file.strategy.FileMoveExistingStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Strategy (Custom Strategy) used to move file with special naming token to use when fileExist=Move is configured. By default, there is an  [...]
+    "autoCreate": { "kind": "parameter", "displayName": "Auto Create", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Automatically create missing directories in the file's pathname. For the file consumer, that means creating the starting directory. For the file producer, it means the directory the files should be written to." },
+    "connectTimeout": { "kind": "parameter", "displayName": "Connect Timeout", "group": "advanced", "label": "advanced", "required": false, "type": "duration", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "10000", "configurationClass": "org.apache.camel.component.file.azure.FilesConfiguration", "configurationField": "configuration", "description": "Sets the connect timeout for waiting for a connection to be established Used by both FTPClien [...]
+    "maximumReconnectAttempts": { "kind": "parameter", "displayName": "Maximum Reconnect Attempts", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "Specifies the maximum reconnect attempts Camel performs when it tries to connect to the remote FTP server. Use 0 to disable this behavior." },
+    "reconnectDelay": { "kind": "parameter", "displayName": "Reconnect Delay", "group": "advanced", "label": "advanced", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "1000", "description": "Delay in millis Camel will wait before performing a reconnect attempt." },
+    "throwExceptionOnConnectFailed": { "kind": "parameter", "displayName": "Throw Exception On Connect Failed", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.azure.FilesConfiguration", "configurationField": "configuration", "description": "Should an exception be thrown if connection failed (exhaust [...]
+    "timeout": { "kind": "parameter", "displayName": "Timeout", "group": "advanced", "label": "advanced", "required": false, "type": "duration", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "30000", "configurationClass": "org.apache.camel.component.file.azure.FilesConfiguration", "configurationField": "configuration", "description": "Sets the data timeout for waiting for reply Used only by FTPClient" },
+    "sdd": { "kind": "parameter", "displayName": "Sdd", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of service SAS token" },
+    "se": { "kind": "parameter", "displayName": "Se", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of SAS token" },
+    "sharedKey": { "kind": "parameter", "displayName": "Shared Key", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesConfiguration", "configurationField": "configuration", "description": "Shared key (storage account key)" },
+    "si": { "kind": "parameter", "displayName": "Si", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of service SAS token" },
+    "sig": { "kind": "parameter", "displayName": "Sig", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of SAS token" },
+    "sip": { "kind": "parameter", "displayName": "Sip", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of SAS token" },
+    "sp": { "kind": "parameter", "displayName": "Sp", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of SAS token" },
+    "spr": { "kind": "parameter", "displayName": "Spr", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of SAS token" },
+    "sr": { "kind": "parameter", "displayName": "Sr", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of service SAS token" },
+    "srt": { "kind": "parameter", "displayName": "Srt", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of SAS token" },
+    "ss": { "kind": "parameter", "displayName": "Ss", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of account SAS token" },
+    "st": { "kind": "parameter", "displayName": "St", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of SAS token" },
+    "sv": { "kind": "parameter", "displayName": "Sv", "group": "both", "label": "both", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.file.azure.FilesToken", "configurationField": "token", "description": "part of SAS token" },
+    "antExclude": { "kind": "parameter", "displayName": "Ant Exclude", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Ant style filter exclusion. If both antInclude and antExclude are used, antExclude takes precedence over antInclude. Multiple exclusions may be specified in comma-delimited format." },
+    "antFilterCaseSensitive": { "kind": "parameter", "displayName": "Ant Filter Case Sensitive", "group": "filter", "label": "consumer,filter", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Sets case sensitive flag on ant filter." },
+    "antInclude": { "kind": "parameter", "displayName": "Ant Include", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Ant style filter inclusion. Multiple inclusions may be specified in comma-delimited format." },
+    "eagerMaxMessagesPerPoll": { "kind": "parameter", "displayName": "Eager Max Messages Per Poll", "group": "filter", "label": "consumer,filter", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Allows for controlling whether the limit from maxMessagesPerPoll is eager or not. If eager then the limit is during the scanning of files. Where as false would scan all files, and then per [...]
+    "exclude": { "kind": "parameter", "displayName": "Exclude", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Is used to exclude 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 this as an endp [...]
+    "excludeExt": { "kind": "parameter", "displayName": "Exclude Ext", "group": "filter", "label": "consumer,filter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Is used to exclude files matching file extension name (case insensitive). For example to exclude bak files, then use excludeExt=bak. Multiple extensions can be separated by comma, for example to exclude bak and dat files, use exclu [...]
+    "filter": { "kind": "parameter", "displayName": "Filter", "group": "filter", "label": "consumer,filter", "required": false, "type": "object", "javaType": "org.apache.camel.component.file.GenericFileFilter<com.azure.storage.file.share.models.ShareFileItem>", "deprecated": false, "autowired": false, "secret": false, "description": "Pluggable filter as a org.apache.camel.component.file.GenericFileFilter class. Will skip files if filter returns false in its accept() method." },
+    "filterDirectory": { "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": { "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": { "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 will be enable [...]
+    "idempotentKey": { "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": { "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": { "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 this as an endp [...]
+    "includeExt": { "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 files, use inclu [...]
+    "maxDepth": { "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": { "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 negative to di [...]
+    "minDepth": { "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": { "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": { "kind": "parameter", "displayName": "Exclusive Read Lock Strategy", "group": "lock", "label": "consumer,lock", "required": false, "type": "object", "javaType": "org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy<com.azure.storage.file.share.models.ShareFileItem>", "deprecated": false, "autowired": false, "secret": false, "description": "Pluggable read-lock as a org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy implem [...]
+    "readLock": { "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 file (i.e. th [...]
+    "readLockCheckInterval": { "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 lock, you can [...]
+    "readLockDeleteOrphanLockFiles": { "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 properly shutdown ( [...]
+    "readLockLoggingLevel": { "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 change this leve [...]
+    "readLockMarkerFile": { "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 behavior can b [...]
+    "readLockMinAge": { "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 at last 5 mi [...]
+    "readLockMinLength": { "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 this option to  [...]
+    "readLockRemoveOnCommit": { "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 and a commit [...]
+    "readLockRemoveOnRollback": { "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 and a rollback  [...]
+    "readLockTimeout": { "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 try the file a [...]
+    "backoffErrorThreshold": { "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": { "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": { "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 happening agai [...]
+    "delay": { "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": { "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": { "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": { "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": { "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 logging level  [...]
+    "scheduledExecutorService": { "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": { "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": { "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": { "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": { "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": { "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": { "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": { "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": { "kind": "parameter", "displayName": "Sorter", "group": "sort", "label": "consumer,sort", "required": false, "type": "object", "javaType": "java.util.Comparator<org.apache.camel.component.file.GenericFile<com.azure.storage.file.share.models.ShareFileItem>>", "deprecated": false, "autowired": false, "secret": false, "description": "Pluggable sorter as a java.util.Comparator class." }
+  }
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/docs/azure-files-component.adoc b/components/camel-azure/camel-azure-files/src/main/docs/azure-files-component.adoc
new file mode 100644
index 00000000000..ea446bd8f08
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/docs/azure-files-component.adoc
@@ -0,0 +1,314 @@
+= Azure Files Component
+:doctitle: Azure Files
+:shortname: azure-files
+:artifactid: camel-azure-files
+:description: Camel Azure Files Component (Preview)
+:since: 3.21
+:supportlevel: Preview
+:component-header: Both producer and consumer are supported
+//Manually maintained attributes
+:camel-spring-boot-name: azure-files
+
+*Since Camel {since}*
+
+*{component-header}*
+
+This component provides access to Azure Files.
+
+[CAUTION]
+====
+A preview component so anything can change in a next release
+or it could be even dropped. At the same time it is consolidated
+enough, sparingly documented, a few users reported it was working
+in their environment, and it is ready for wider feedback. 
+====
+
+When consuming from remote files server make sure you read the section titled _Consuming Files_
+further below for details related to consuming files.
+
+Maven users will need to add the following dependency to their `pom.xml`
+for this component:
+
+[source,xml]
+----
+<dependency>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-azure-files</artifactId>
+    <version>x.y.z</version>
+    <!-- use the same version as your Camel core version -->
+</dependency>
+----
+
+== Endpoint URI Format
+
+----
+azure-files://account[.file.core.windows.net][:port]/share[/directory]
+----
+
+Where *directory* represents the underlying directory. The directory
+is a relative path and does not include the share name. The relative path
+can contain nested folders, such as `inbox/spam`. It defaults to
+the share root directory.
+
+The `autoCreate` option is supported for the directory,
+when consumer or producer starts, there's an additional operation
+performed to create the directory configured for the endpoint. The default
+value for `autoCreate` is `true`. On contrary, the share must exist, it
+is not automatically created.
+
+If no *port* number is provided, Camel will provide default values
+according to the protocol (https 443).
+
+You can append query options to the URI in the following format
+`?option=value&option2=value&...`.
+
+The SAS token or shared key based auth is supported, the options
+for selected one must be present.
+
+// component-configure options: START
+
+// component-configure options: END
+
+// component options: START
+include::partial$component-configure-options.adoc[]
+include::partial$component-endpoint-options.adoc[]
+// component options: END
+
+// endpoint options: START
+
+// endpoint options: END
+// component headers: START
+include::partial$component-endpoint-headers.adoc[]
+// component headers: END
+
+
+=== Endpoint URI Examples
+
+----
+azure-files://camelazurefiles.file.core.windows.net/samples?sv=2022-11-02&ss=f&srt=sco&sp=rwdlc&se=2023-06-18T22:29:13Z&st=2023-06-05T14:29:13Z&spr=https&sig=MPsMh8zci0v3To7IT9SKdaFGZV8ezno63m9C8s9bdVQ%3D
+----
+
+----
+azure-files://camelazurefiles/samples/inbox/spam?sharedKey=FAKE502UyuBD...3Z%2BASt9dCmJg%3D%3D&delete=true
+----
+
+== Paths
+
+The path separator is `/`. The absolute paths start with the path separator.
+The absolute paths do not include the share name and they are relative
+to the share root rather than to the endpoint starting directory.
+ 
+*NOTE:* At some places, namely logs of used libraries, OS-specific path separator
+appears, and the relative paths are relative to the share root (rather than
+to the current working directory or to the endpoint starting directory)
+so interpret them with a grain of salt.
+
+== Concurrency
+
+This component does not support concurrency on its endpoints.
+
+== More Information
+
+This component mimics the FTP component.
+So, there are more samples and details on the FTP
+component page.
+
+This component uses the Azure Java SDK libraries for the actual work.
+
+== Consuming Files
+
+The remote consumer will by default leave the consumed
+files untouched on the remote cloud files server. You have to configure it
+explicitly if you want it to delete the files or move them to another
+location. For example you can use `delete=true` to delete the files, or
+use `move=.done` to move the files into `.done` sub directory.
+
+In Camel, the `.`-prefixed folders are excluded from
+recursive polling.
+
+The regular File consumer is different as it will by
+default move files to a `.camel` sub directory. The reason Camel does
+*not* do this by default for the remote consumer is that it may lack
+permissions by default to be able to move or delete files.
+
+=== Body Type Options
+
+For each matching file, the consumer sends to the Camel exchange
+a message with a selected body type:
+
+  - `byte[]` by default
+  - `java.io.InputStream` if `streamDownload=true` is configured
+  - `java.io.File` if `localWorkDirectory` is configured
+
+The body type configuration should be tuned to fit available resources,
+performance targets, route processors, caching, resuming, etc.
+
+=== Limitations
+
+The option *readLock* can be used to force Camel *not* to consume files
+that is currently in the progress of being written. However, this option
+is turned off by default, as it requires that the user has write access.
+See the options table at File2 for more details about
+read locks. +
+ There are other solutions to avoid consuming files that are currently
+being written; for instance, you can write to a temporary
+destination and move the file after it has been written.
+
+For the `readLock=changed`, it relies only on the last modified,
+furthermore a precision finer than 5 seconds might be problematic.
+
+When moving files using `move` or `preMove` option, the files are
+restricted to the share. That prevents consumer from moving files
+outside of the endpoint share.
+
+=== Exchange Properties
+
+The consumer sets the following exchange properties
+
+[width="100%",cols="50%,50%",options="header",]
+|=======================================================================
+|Header |Description
+
+|`CamelBatchIndex` |Current index out of total number of files being consumed in this batch.
+
+|`CamelBatchSize` |Total number of files being consumed in this batch.
+
+|`CamelBatchComplete` |True if there are no more files in this batch.
+|=======================================================================
+
+== Producing Files
+
+The Files producer is optimized for two body types:
+
+  - `java.io.InputStream` if `CamelFileLength` header is set
+  - `byte[]`
+
+In either case the remote file size is allocated
+and then rewritten with body content. Any inconsistency between
+declared file length and stream length results in a corrupted
+remote file.
+
+=== Limitations
+
+The underlying Azure Files service does not allow to grow files. The file
+length must be known at its creation time, consequently:
+
+  - `CamelFileLength` header has an important
+    meaning even for producers.
+  - No append mode is supported.
+
+
+== About Timeouts
+
+You can use the `connectTimeout` option to set
+a timeout in millis to connect or disconnect. 
+
+The `timeout` option only applies as the data timeout in millis.
+
+The meta-data operations timeout is minimum of:
+`readLockCheckInterval`, `timeout` and 20_000 millis.
+
+For now the files upload has no timeout. During the upload,
+the underlying library could log timeout warnings. They are
+recoverable and upload could continue.   
+
+== Using Local Work Directory
+
+Camel supports consuming from remote files servers and downloading the
+files directly into a local work directory. This avoids reading the
+entire remote file content into memory as it is streamed directly into
+the local file using `FileOutputStream`.
+
+Camel will store to a local file with the same name as the remote file,
+though with `.inprogress` as extension while the file is being
+downloaded. Afterwards, the file is renamed to remove the `.inprogress`
+suffix. And finally, when the Exchange is complete
+the local file is deleted.
+
+So if you want to download files from a remote files server and store it
+as local files then you need to route to a file endpoint such as:
+
+[source,java]
+----
+from("azure-files://...&localWorkDirectory=/tmp").to("file://inbox");
+----
+
+[TIP]
+====
+The route above is ultra efficient as it avoids reading the entire file content into memory.
+It will download the remote file directly to a local file stream.
+The `java.io.File` handle is then used as the Exchange body. The file producer leverages this fact and can work directly on the work file `java.io.File` handle and perform a `java.io.File.rename` to the target filename.
+As Camel knows it's a local work file, it can optimize and use a rename instead of a file copy, as the work file is meant to be deleted anyway.
+====
+
+== Custom Filtering
+
+Camel supports pluggable filtering strategies. This strategy it to use
+the build in `org.apache.camel.component.file.GenericFileFilter` in
+Java. You can then configure the endpoint with such a filter to skip
+certain filters before being processed.
+
+In the sample we have built our own filter that only accepts files
+starting with report in the filename.
+
+And then we can configure our route using the *filter* attribute to
+reference our filter (using `#` notation) that we have defined in the
+spring XML file:
+
+The accept(file) file argument has properties:
+
+  - endpoint path: the share name such as `/samples`
+  - relative path: a path to the file such as `subdir/a file`
+  - directory: `true` if a directory
+  - file length: if not a directory then a length of the file in bytes
+
+
+== Filtering using ANT path matcher
+
+The ANT path matcher is a filter that is shipped out-of-the-box in the
+*camel-spring* jar. So you need to depend on *camel-spring* if you are
+using Maven. +
+ The reason is that we leverage Spring's
+http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/util/AntPathMatcher.html[AntPathMatcher]
+to do the actual matching.
+
+The file paths are matched with the following rules:
+
+* `?` matches one character
+* `*` matches zero or more characters
+* `**` matches zero or more directories in a path
+
+The sample below demonstrates how to use it:
+
+[source,java]
+----
+from("azure-files://...&antInclude=**/*.txt").to("...");
+----
+
+== Using a Proxy
+
+Consult the https://learn.microsoft.com/en-us/azure/developer/java/sdk/proxying[underlying library]
+documentation.
+
+
+== Consuming a single file using a fixed name
+
+Unlike FTP component that features special combination of options:
+  
+  - `useList=false`
+  - `fileName=myFileName.txt`
+  - `ignoreFileNotFoundOrPermissionError=true`
+
+to optimize _the single file using a fixed name_ use case,
+it is necessary to fallback to regular filters (i.e. the list
+permission is needed). 
+
+
+== Debug logging
+
+This component has log level *TRACE* that can be helpful if you have
+problems.
+
+
+
+include::spring-boot:partial$starter.adoc[]
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesComponent.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesComponent.java
new file mode 100644
index 00000000000..c19a2868b5f
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesComponent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.azure;
+
+import java.util.Map;
+
+import com.azure.storage.file.share.models.ShareFileItem;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.component.file.GenericFileExist;
+import org.apache.camel.component.file.remote.RemoteFileComponent;
+import org.apache.camel.spi.annotations.Component;
+
+/**
+ * Camel Azure Files component.
+ */
+@Component(FilesComponent.SCHEME)
+public class FilesComponent extends RemoteFileComponent<ShareFileItem> {
+
+    public static final String SCHEME = "azure-files";
+
+    public FilesComponent() {
+    }
+
+    public FilesComponent(CamelContext context) {
+        super(context);
+    }
+
+    @Override
+    protected FilesEndpoint buildFileEndpoint(
+            String uri, String remaining,
+            Map<String, Object> parameters)
+            throws Exception {
+        var config = new FilesConfiguration(FilesURIStrings.getBaseURI(uri));
+        return new FilesEndpoint(uri, this, config);
+    }
+
+    @Override
+    protected void afterPropertiesSet(GenericFileEndpoint<ShareFileItem> endpoint) throws Exception {
+        if (endpoint.getFileExist() == GenericFileExist.Append) {
+            throw new IllegalArgumentException(
+                    "Appending to remote files is not supported.");
+        } else if (endpoint.getFileExist() == GenericFileExist.Move) {
+            throw new IllegalArgumentException(
+                    "Moving of existing remote files is not implemented.");
+        }
+    }
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesConfiguration.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesConfiguration.java
new file mode 100644
index 00000000000..000eda2ecd7
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesConfiguration.java
@@ -0,0 +1,105 @@
+/*
+ * 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.azure;
+
+import java.net.URI;
+
+import org.apache.camel.component.file.remote.RemoteFileConfiguration;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+
+@UriParams
+public class FilesConfiguration extends RemoteFileConfiguration {
+
+    public static final int DEFAULT_HTTPS_PORT = 443;
+    public static final String DEFAULT_INTERNET_DOMAIN = "file.core.windows.net";
+
+    @UriParam(label = "both", description = "Shared key (storage account key)", secret = true)
+    private String sharedKey;
+
+    private String account;
+    private String share;
+
+    public FilesConfiguration() {
+        setProtocol(FilesComponent.SCHEME);
+    }
+
+    public FilesConfiguration(URI uri) {
+        super(uri);
+        setSendNoop(false);
+        setBinary(true);
+        setPassiveMode(true);
+    }
+
+    @Override
+    protected void setDefaultPort() {
+        setPort(DEFAULT_HTTPS_PORT);
+    }
+
+    @Override
+    public void setDirectory(String path) {
+        // split URI path to share and starting directory
+        if (path == null || path.isBlank() || path.contains(FilesPath.PATH_SEPARATOR + "" + FilesPath.PATH_SEPARATOR)
+                || path.equals(FilesPath.SHARE_ROOT)) {
+            throw new IllegalArgumentException("Illegal endpoint URI path (expected share[/dir]): " + path);
+        }
+        var dir = FilesPath.trimTrailingSeparator(path);
+        dir = FilesPath.trimLeadingSeparator(dir);
+        var separator = dir.indexOf(FilesPath.PATH_SEPARATOR);
+        if (separator == -1) {
+            share = dir;
+            dir = FilesPath.SHARE_ROOT;
+        } else {
+            share = dir.substring(0, separator);
+            dir = dir.substring(separator);
+        }
+        super.setDirectory(dir);
+    }
+
+    public String getShare() {
+        return share;
+    }
+
+    @Override
+    public String remoteServerInformation() {
+        return getProtocol() + "://" + getAccount();
+    }
+
+    /**
+     * Files service account or &lt;account>.file.core.windows.net hostname.
+     */
+    @Override
+    public void setHost(String accountOrHostname) {
+        var dot = accountOrHostname.indexOf('.');
+        var hasDot = dot >= 0;
+        account = hasDot ? accountOrHostname.substring(0, dot) : accountOrHostname;
+        super.setHost(hasDot ? accountOrHostname : account + '.' + DEFAULT_INTERNET_DOMAIN);
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public String getSharedKey() {
+        return sharedKey;
+    }
+
+    public void setSharedKey(String sharedKey) {
+        this.sharedKey = sharedKey;
+    }
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesConsumer.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesConsumer.java
new file mode 100644
index 00000000000..0ade389b101
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesConsumer.java
@@ -0,0 +1,254 @@
+/*
+ * 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.azure;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import com.azure.storage.file.share.models.ShareFileItem;
+import org.apache.camel.Message;
+import org.apache.camel.Processor;
+import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.GenericFileOperationFailedException;
+import org.apache.camel.component.file.GenericFileProcessStrategy;
+import org.apache.camel.component.file.remote.RemoteFile;
+import org.apache.camel.component.file.remote.RemoteFileConfiguration;
+import org.apache.camel.component.file.remote.RemoteFileConsumer;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.URISupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ManagedResource(description = "Camel Azure Files consumer")
+public class FilesConsumer extends RemoteFileConsumer<ShareFileItem> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(FilesConsumer.class);
+
+    protected String endpointPath;
+
+    private transient String toString;
+
+    public FilesConsumer(FilesEndpoint endpoint, Processor processor,
+                         FilesOperations fileOperations,
+                         GenericFileProcessStrategy processStrategy) {
+        super(endpoint, processor, fileOperations, processStrategy);
+        this.endpointPath = endpoint.getConfiguration().getDirectory();
+    }
+
+    @Override
+    protected FilesOperations getOperations() {
+        return (FilesOperations) super.getOperations();
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        // turn off scheduler first, so autoCreate is handled before scheduler
+        // starts
+        boolean startScheduler = isStartScheduler();
+        setStartScheduler(false);
+        try {
+            super.doStart();
+            if (endpoint.isAutoCreate() && hasStartingDirectory()) {
+                LOG.debug("Auto creating directory: {}", endpointPath);
+                try {
+                    connectIfNecessary();
+                    operations.buildDirectory(endpointPath, true);
+                } catch (GenericFileOperationFailedException e) {
+                    // log a WARN as we want to start the consumer.
+                    LOG.warn("Error auto creating directory: " + endpointPath + " due " + e.getMessage()
+                             + ". This exception is ignored.",
+                            e);
+                }
+            }
+        } finally {
+            if (startScheduler) {
+                setStartScheduler(true);
+                startScheduler();
+            }
+        }
+    }
+
+    @Override
+    protected boolean pollDirectory(
+            String path, List<GenericFile<ShareFileItem>> fileList,
+            int depth) {
+        LOG.trace("pollDirectory({},,{})", path, depth);
+
+        return doPollDirectory(FilesPath.trimTrailingSeparator(path), null, fileList, depth);
+    }
+
+    @Override
+    protected boolean doPollDirectory(
+            String path, String dirName,
+            List<GenericFile<ShareFileItem>> fileList, int depth) {
+        LOG.trace("doPollDirectory({},{},,{})", path, dirName, depth);
+
+        var listedFileItems = listFileItems(path);
+
+        if (listedFileItems == null || listedFileItems.length == 0) {
+            LOG.trace("No files found in directory: {}", path);
+            return true;
+        }
+
+        LOG.trace("Found {} files in directory: {}", listedFileItems.length, path);
+
+        if (getEndpoint().isPreSort()) {
+            Arrays.sort(listedFileItems, Comparator.comparing(ShareFileItem::getName));
+        }
+
+        for (var fileItem : listedFileItems) {
+            if (handleFileItem(path, fileList, depth + 1, listedFileItems, fileItem)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private boolean handleFileItem(
+            String path, List<GenericFile<ShareFileItem>> polledFiles,
+            int depth, ShareFileItem[] listedFileItems, ShareFileItem fileItem) {
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("Item[name={}, dir={}]", fileItem.getName(), fileItem.isDirectory());
+        }
+
+        // check if we can continue polling in files
+        if (!canPollMoreFiles(polledFiles)) {
+            return true;
+        }
+
+        if (fileItem.isDirectory()) {
+            if (handleDirectory(path, polledFiles, depth, listedFileItems, fileItem)) {
+                return true;
+            }
+        } else {
+            handleFile(path, polledFiles, depth, listedFileItems, fileItem);
+        }
+        return false;
+    }
+
+    private boolean handleDirectory(
+            String path, List<GenericFile<ShareFileItem>> polledFiles,
+            int depth, ShareFileItem[] listedFileItems, ShareFileItem dir) {
+
+        if (endpoint.isRecursive() && depth < endpoint.getMaxDepth()) {
+            var remote = asRemoteFile(path, dir);
+            if (isValidFile(remote, true, listedFileItems)) {
+                String dirName = dir.getName();
+                String dirPath = FilesPath.concat(path, dirName);
+                boolean canPollMore = doSafePollSubDirectory(dirPath, dirName, polledFiles, depth);
+                if (!canPollMore) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void handleFile(
+            String path, List<GenericFile<ShareFileItem>> polledFiles, int depth,
+            ShareFileItem[] listedFileItems, ShareFileItem file) {
+        if (depth >= endpoint.getMinDepth()) {
+            var remote = asRemoteFile(path, file);
+            if (isValidFile(remote, false, listedFileItems)) {
+                polledFiles.add(remote);
+            }
+        }
+    }
+
+    private ShareFileItem[] listFileItems(String dir) {
+        try {
+            return operations.listFiles(dir);
+        } catch (GenericFileOperationFailedException e) {
+            if (ignoreCannotRetrieveFile(null, null, e)) {
+                LOG.debug(
+                        "Cannot list files in directory {} due directory does not exists or file permission error.",
+                        dir);
+                return null;
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    @Override
+    protected boolean isMatched(
+            GenericFile<ShareFileItem> file, String doneFileName,
+            ShareFileItem[] files) {
+        String onlyName = FileUtil.stripPath(doneFileName);
+
+        for (ShareFileItem f : files) {
+            if (f.getName().equals(onlyName)) {
+                return true;
+            }
+        }
+
+        LOG.trace("Done file: {} does not exist", doneFileName);
+        return false;
+    }
+
+    private RemoteFile<ShareFileItem> asRemoteFile(String path, ShareFileItem file) {
+        RemoteFile<ShareFileItem> answer = new RemoteFile<>();
+
+        answer.setEndpointPath(endpointPath);
+        answer.setFile(file);
+        answer.setFileNameOnly(file.getName());
+        if (file.isDirectory() == false) {
+            answer.setFileLength(file.getFileSize());
+            answer.setLastModified(lastModified(file));
+        }
+        answer.setDirectory(file.isDirectory());
+        answer.setHostname(((RemoteFileConfiguration) endpoint.getConfiguration()).getHost());
+        answer.setAbsolute(FilesPath.isAbsolute(path));
+
+        String pseudoAbsoluteFileName = FilesPath.concat(path, file.getName());
+        answer.setAbsoluteFilePath(pseudoAbsoluteFileName);
+
+        String relativePath = StringHelper.after(pseudoAbsoluteFileName, endpointPath);
+        relativePath = FilesPath.ensureRelative(relativePath);
+        answer.setRelativeFilePath(relativePath);
+        answer.setFileName(relativePath);
+
+        return answer;
+    }
+
+    @Override
+    protected void updateFileHeaders(GenericFile<ShareFileItem> file, Message message) {
+        message.setHeader(FilesHeaders.FILE_LENGTH, file.getFileLength());
+        message.setHeader(FilesHeaders.FILE_LAST_MODIFIED, file.getLastModified());
+    }
+
+    @Override
+    public String toString() {
+        if (toString == null) {
+            toString = "FilesConsumer[" + URISupport.sanitizeUri(getEndpoint().getEndpointUri()) + "]";
+        }
+        return toString;
+    }
+
+    private static long lastModified(ShareFileItem file) {
+        var raw = file.getProperties().getLastModified();
+        if (raw == null) {
+            // if ls without metadata
+            return 0;
+        }
+        return raw.toInstant().toEpochMilli();
+    }
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesEndpoint.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesEndpoint.java
new file mode 100644
index 00000000000..53dedf411c9
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesEndpoint.java
@@ -0,0 +1,184 @@
+/*
+ * 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.azure;
+
+import java.time.Duration;
+
+import com.azure.storage.file.share.models.ShareFileItem;
+import org.apache.camel.Category;
+import org.apache.camel.FailedToCreateConsumerException;
+import org.apache.camel.FailedToCreateProducerException;
+import org.apache.camel.Processor;
+import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.component.file.GenericFileConfiguration;
+import org.apache.camel.component.file.GenericFileProcessStrategy;
+import org.apache.camel.component.file.azure.strategy.FilesProcessStrategyFactory;
+import org.apache.camel.component.file.remote.RemoteFileConsumer;
+import org.apache.camel.component.file.remote.RemoteFileEndpoint;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.util.ObjectHelper;
+
+// , extendsScheme = "file"   in FTPS but AzureBlob does not have it
+@UriEndpoint(firstVersion = "3.21.0", scheme = FilesComponent.SCHEME, extendsScheme = "file", title = "Azure Files",
+             syntax = FilesComponent.SCHEME + "://account[.host]/share[/dir]", category = {
+                     Category.CLOUD, Category.FILE },
+             headersClass = FilesHeaders.class)
+@Metadata(excludeProperties = "appendChars,readLockIdempotentReleaseAsync,readLockIdempotentReleaseAsyncPoolSize,"
+                              + "readLockIdempotentReleaseDelay,readLockIdempotentReleaseExecutorService,"
+                              + "directoryMustExist,extendedAttributes,probeContentType,startingDirectoryMustExist,"
+                              + "startingDirectoryMustHaveAccess,chmodDirectory,forceWrites,copyAndDeleteOnRenameFail,"
+                              + "renameUsingCopy,synchronous,passive,passiveMode,stepwise,useList,binary,charset,password,"
+                              + "siteCommand,fastExistsCheck,soTimeout,separator,sendNoop,ignoreFileNotFoundOrPermissionError,"
+                              + "bufferSize,moveExisting,username")
+@ManagedResource(description = "Camel Azure Files endpoint")
+public class FilesEndpoint extends RemoteFileEndpoint<ShareFileItem> {
+
+    // without hiding configuration field from type GenericFileEndpoint<ShareFileItem>
+    // camel-package-maven-plugin: Missing @UriPath on endpoint 
+    @UriParam
+    protected FilesConfiguration configuration;
+
+    @UriParam
+    protected FilesToken token = new FilesToken();
+
+    @UriParam(label = "consumer")
+    protected boolean resumeDownload;
+
+    public FilesEndpoint() {
+    }
+
+    public FilesEndpoint(String uri, FilesComponent component,
+                         FilesConfiguration configuration) {
+        super(uri, component, configuration);
+        setConfiguration(configuration);
+    }
+
+    @Override
+    public String getScheme() {
+        // TODO or name of component bean?
+        return FilesComponent.SCHEME;
+    }
+
+    @Override
+    public RemoteFileConsumer<ShareFileItem> createConsumer(Processor processor) throws Exception {
+        if (isResumeDownload() && ObjectHelper.isEmpty(getLocalWorkDirectory())) {
+            throw new IllegalArgumentException(
+                    "The option localWorkDirectory must be configured when resumeDownload=true");
+        }
+        return super.createConsumer(processor);
+    }
+
+    @Override
+    protected FilesConsumer buildConsumer(Processor processor) {
+        try {
+            return new FilesConsumer(
+                    this, processor, createRemoteFileOperations(),
+                    processStrategy != null ? processStrategy : createGenericFileStrategy());
+        } catch (Exception e) {
+            throw new FailedToCreateConsumerException(this, e);
+        }
+    }
+
+    @Override
+    protected FilesProducer buildProducer() {
+        try {
+            return new FilesProducer(this, createRemoteFileOperations());
+        } catch (Exception e) {
+            throw new FailedToCreateProducerException(this, e);
+        }
+    }
+
+    @Override
+    protected GenericFileProcessStrategy<ShareFileItem> createGenericFileStrategy() {
+        return new FilesProcessStrategyFactory().createGenericFileProcessStrategy(getCamelContext(),
+                getParamsAsMap());
+    }
+
+    @Override
+    public FilesOperations createRemoteFileOperations() {
+        return new FilesOperations(this);
+    }
+
+    public FilesToken getToken() {
+        return token;
+    }
+
+    public void setToken(FilesToken token) {
+        this.token = token;
+    }
+
+    @Override
+    public FilesConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    @Override
+    public void setConfiguration(GenericFileConfiguration configuration) {
+        if (configuration == null || !(configuration instanceof FilesConfiguration)) {
+            throw new IllegalArgumentException("FilesConfiguration expected.");
+        }
+        super.setConfiguration(configuration);
+        this.configuration = (FilesConfiguration) configuration;
+    }
+
+    public boolean isResumeDownload() {
+        return resumeDownload;
+    }
+
+    /**
+     * Configures whether resume download is enabled. In addition the options <tt>localWorkDirectory</tt> must be
+     * configured so downloaded files are stored in a local directory, which is required to support resuming of
+     * downloads.
+     */
+    public void setResumeDownload(boolean resumeDownload) {
+        this.resumeDownload = resumeDownload;
+    }
+
+    @Override
+    public char getFileSeparator() {
+        return FilesPath.PATH_SEPARATOR;
+    }
+
+    @Override
+    public String getCharset() {
+        // unlike FTP, always binary
+        return null;
+    }
+
+    Duration getMetadataTimeout() {
+        var t1 = getConfiguration().getTimeout();
+        var t2 = getReadLockCheckInterval();
+        if (t2 > 0 && t2 < t1) {
+            return Duration.ofMillis(t2);
+        }
+        if (t1 > 0) {
+            return Duration.ofMillis(t1);
+        }
+        return Duration.ofSeconds(20);
+    }
+
+    Duration getDataTimeout() {
+        var t1 = getConfiguration().getTimeout();
+        if (t1 > 0) {
+            return Duration.ofMillis(t1);
+        }
+        return Duration.ofDays(10); // block
+    }
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesHeaders.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesHeaders.java
new file mode 100644
index 00000000000..885c1bea9a9
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesHeaders.java
@@ -0,0 +1,53 @@
+/*
+ * 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.azure;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.file.remote.RemoteFileComponent;
+import org.apache.camel.spi.Metadata;
+
+public final class FilesHeaders {
+
+    @Metadata(label = "both", description = "A `long` value containing the file size. For producer,"
+                                            + " known length helps if the body converts to InputStream"
+                                            + " more efficiently than to bytes array.",
+              javaType = "long")
+    public static final String FILE_LENGTH = Exchange.FILE_LENGTH;
+    @Metadata(label = "consumer", description = "A `Long` value containing the last modified timestamp of the file.",
+              javaType = "long")
+    public static final String FILE_LAST_MODIFIED = Exchange.FILE_LAST_MODIFIED;
+    @Metadata(description = "Specifies the output file name (relative to the endpoint directory) to\n" +
+                            "be used for the output message when sending to the endpoint. If this is\n" +
+                            "not present and no expression either, then a generated message ID is\n" +
+                            "used as the filename instead.",
+              javaType = "String")
+    public static final String FILE_NAME = Exchange.FILE_NAME;
+    @Metadata(description = "Only the file name (the name with no leading paths).", javaType = "String")
+    public static final String FILE_NAME_ONLY = Exchange.FILE_NAME_ONLY;
+    @Metadata(description = "The parent path.", javaType = "String")
+    public static final String FILE_PARENT = Exchange.FILE_PARENT;
+    @Metadata(description = "The remote file input stream.", javaType = "java.io.InputStream")
+    public static final String REMOTE_FILE_INPUT_STREAM = RemoteFileComponent.REMOTE_FILE_INPUT_STREAM;
+    @Metadata(description = "Path to the local work file, if local work directory is used.", javaType = "String")
+    public static final String FILE_LOCAL_WORK_PATH = Exchange.FILE_LOCAL_WORK_PATH;
+
+    @Metadata(description = "The remote hostname.", javaType = "String")
+    public static final String FILE_HOST = "CamelFileHost";
+
+    private FilesHeaders() {
+    }
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesOperations.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesOperations.java
new file mode 100644
index 00000000000..a40bed35da6
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesOperations.java
@@ -0,0 +1,740 @@
+/*
+ * 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.azure;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.time.Duration;
+import java.util.EmptyStackException;
+import java.util.Stack;
+
+import com.azure.core.util.Context;
+import com.azure.storage.common.StorageSharedKeyCredential;
+import com.azure.storage.file.share.ShareDirectoryClient;
+import com.azure.storage.file.share.ShareFileClient;
+import com.azure.storage.file.share.ShareServiceClient;
+import com.azure.storage.file.share.ShareServiceClientBuilder;
+import com.azure.storage.file.share.models.ShareFileItem;
+import com.azure.storage.file.share.models.ShareFileRange;
+import com.azure.storage.file.share.models.ShareStorageException;
+import com.azure.storage.file.share.options.ShareDirectoryCreateOptions;
+import com.azure.storage.file.share.options.ShareFileRenameOptions;
+import com.azure.storage.file.share.options.ShareListFilesAndDirectoriesOptions;
+import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.component.file.FileComponent;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.component.file.GenericFileExist;
+import org.apache.camel.component.file.GenericFileOperationFailedException;
+import org.apache.camel.component.file.remote.RemoteFile;
+import org.apache.camel.component.file.remote.RemoteFileConfiguration;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.TimeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Operations with locally tracked cwd state.
+ * <p>
+ * The state limits thread safety.
+ */
+public class FilesOperations extends NormalizedOperations {
+
+    // TODO the underlying lib supports multi-step navigation, could we eliminate
+    // cwd state?
+
+    public static final String HTTPS = "https";
+
+    static final int HTTP_OK = 200;
+    static final int HTTP_CREATED = 201;
+    static final int HTTP_ACCEPTED = 202;
+    static final int HTTP_NOT_FOUND = 404;
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final FilesEndpoint endpoint;
+    private final FilesConfiguration configuration;
+    private final FilesToken token;
+    private ShareServiceClient client;
+    private ShareDirectoryClient root;
+    private Stack<ShareDirectoryClient> dirStack = new Stack<>();
+
+    FilesOperations(FilesEndpoint endpoint) {
+        super(endpoint.getConfiguration());
+        this.endpoint = endpoint;
+        configuration = endpoint.getConfiguration();
+        token = endpoint.getToken();
+    }
+
+    @Override
+    public void setEndpoint(GenericFileEndpoint<ShareFileItem> endpoint) {
+        if (this.endpoint != endpoint) {
+            throw new IllegalStateException("The endpoint is final: " + endpoint);
+        }
+    }
+
+    @Override
+    public GenericFile<ShareFileItem> newGenericFile() {
+        log.trace("newGenericFile()");
+        return new RemoteFile<>();
+    }
+
+    @Override
+    public boolean connect(RemoteFileConfiguration config, Exchange exchange)
+            throws GenericFileOperationFailedException {
+        log.trace("connect()");
+        root = getClient().getShareClient(configuration.getShare()).getRootDirectoryClient();
+        // TODO what about (starting) directory as the root?
+        dirStack.push(root);
+        // TODO translate runtime exception to Camel one?
+        return existsRemote(root);
+    }
+
+    @Override
+    public boolean isConnected() {
+        log.trace("isConnected()");
+        return root != null;
+    }
+
+    @Override
+    public void disconnect() {
+        log.trace("disconnect()");
+    }
+
+    @Override
+    public void forceDisconnect() throws GenericFileOperationFailedException {
+        log.debug("forceDisconnect()");
+        var ms = configuration.getConnectTimeout();
+        root.forceCloseAllHandles(true, Duration.ofMillis(ms), Context.NONE);
+        root = null;
+        dirStack = new Stack<>();
+    }
+
+    @Override
+    public boolean deleteFile(String name) throws GenericFileOperationFailedException {
+        log.trace("deleteFile({})", name);
+
+        reconnectIfNecessary(null);
+
+        var backup = backup();
+        try {
+            changeCurrentDirectory(FileUtil.onlyPath(name));
+            return deleteRemote(cwd(), FileUtil.stripPath(name));
+        } finally {
+            restore(backup);
+        }
+    }
+
+    private boolean deleteRemote(ShareDirectoryClient dirClient, String fileName) {
+        log.trace("{}> rm {}", dirClient.getDirectoryPath(), fileName);
+        var status = dirClient
+                .deleteFileIfExistsWithResponse(fileName, endpoint.getMetadataTimeout(), Context.NONE).getStatusCode();
+        // doc: If Response's status code is 202, the file was successfully deleted. If status code is 404, the file does not exist.
+        return status == HTTP_NOT_FOUND || status == HTTP_ACCEPTED;
+    }
+
+    @SuppressWarnings("unchecked")
+    void restore(Object backup) {
+        dirStack = (Stack<ShareDirectoryClient>) backup;
+    }
+
+    Object backup() {
+        return dirStack.clone();
+    }
+
+    private ShareDirectoryClient cwd() {
+        var cwd = dirStack.peek();
+        return cwd;
+    }
+
+    @Override
+    public boolean renameFile(String from, String to) throws GenericFileOperationFailedException {
+        // by observation both paths are absolute paths on the share
+        log.trace("renameFile({}, {})", from, to);
+
+        try {
+            return renameRemote(getFileClient(FilesPath.ensureRelative(from)), FilesPath.ensureRelative(to));
+        } catch (RuntimeException e) {
+            throw new GenericFileOperationFailedException("Cannot rename: " + from + " to: " + to, e);
+        }
+    }
+
+    private boolean renameRemote(ShareFileClient fileClient, String shareRelativeTo) {
+        // TODO replace existing?
+        var options = new ShareFileRenameOptions(shareRelativeTo);
+        var renamed = fileClient.renameWithResponse(options, endpoint.getMetadataTimeout(), Context.NONE).getValue();
+        return existsRemote(renamed);
+    }
+
+    @Override
+    public boolean buildDirectory(String directory) throws GenericFileOperationFailedException {
+
+        boolean success = existsDirectory(directory);
+
+        if (!success) {
+            success = buildDirectoryStepByStep(directory);
+        }
+
+        return success;
+    }
+
+    private boolean buildDirectoryStepByStep(String dir) {
+        if (FilesPath.isRoot(dir)) {
+            return true;
+        }
+        var steps = FilesPath.split(dir);
+        var stepClient = root;
+        for (var step : steps) {
+            stepClient = buildDirectoryRemote(stepClient, step);
+            if (!existsRemote(stepClient)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private ShareDirectoryClient buildDirectoryRemote(ShareDirectoryClient dirClient, String name) {
+        log.trace("{}> mkdir {}", dirClient.getDirectoryPath(), name);
+        var options = new ShareDirectoryCreateOptions();
+        return dirClient
+                .createSubdirectoryIfNotExistsWithResponse(name, options, endpoint.getMetadataTimeout(), Context.NONE)
+                .getValue();
+    }
+
+    @Override
+    public boolean retrieveFile(String name, Exchange exchange, long size) throws GenericFileOperationFailedException {
+        log.trace("retrieveFile({})", name);
+        boolean answer;
+        var backup = backup();
+        try {
+            if (endpoint.getLocalWorkDirectory() != null) {
+                answer = retrieveFileToFileInLocalWorkDirectory(name, exchange, endpoint.isResumeDownload());
+            } else {
+                answer = retrieveFileToBody(name, exchange);
+            }
+        } finally {
+            restore(backup);
+        }
+
+        return answer;
+    }
+
+    @Override
+    public void releaseRetrievedFileResources(Exchange exchange) throws GenericFileOperationFailedException {
+        log.trace("releaseRetrievedFileResources({})", exchange.getExchangeId());
+        var is = exchange.getIn().getHeader(FilesHeaders.REMOTE_FILE_INPUT_STREAM, InputStream.class);
+
+        if (is != null) {
+            IOHelper.close(is);
+        }
+    }
+
+    @SuppressWarnings({ "unchecked", "resource" })
+    private boolean retrieveFileToBody(String name, Exchange exchange) throws GenericFileOperationFailedException {
+        boolean success = false;
+        GenericFile<ShareFileItem> target = (GenericFile<ShareFileItem>) exchange
+                .getProperty(FileComponent.FILE_EXCHANGE_FILE);
+        org.apache.camel.util.ObjectHelper.notNull(target,
+                "Exchange should have the " + FileComponent.FILE_EXCHANGE_FILE + " set");
+
+        String path = FileUtil.onlyPath(name);
+        if (path != null) {
+            changeCurrentDirectory(path);
+        }
+        // remote name is now only the file name as we just changed
+        // directory
+        String remoteName = FileUtil.stripPath(name);
+
+        if (configuration.isStreamDownload()) {
+            log.trace("Prepared {} for download as opened input stream.", remoteName);
+            InputStream is = cwd().getFileClient(remoteName).openInputStream();
+            target.setBody(is);
+            exchange.getIn().setHeader(FilesHeaders.REMOTE_FILE_INPUT_STREAM, is);
+            success = true;
+        } else {
+            log.trace("Downloading {} to byte[] body.", remoteName);
+            var os = new ByteArrayOutputStream();
+            ShareFileRange range = new ShareFileRange(0);
+            var ret = cwd().getFileClient(remoteName).downloadWithResponse(os, range, null, endpoint.getDataTimeout(),
+                    Context.NONE);
+            success = ret.getStatusCode() == HTTP_OK;
+            IOHelper.close(os);
+            target.setBody(os.toByteArray());
+        }
+
+        return success;
+    }
+
+    @SuppressWarnings("unchecked")
+    private boolean retrieveFileToFileInLocalWorkDirectory(String name, Exchange exchange, boolean resumeDownload)
+            throws GenericFileOperationFailedException {
+        File inProgress;
+        File local = new File(FileUtil.normalizePath(endpoint.getLocalWorkDirectory()));
+        OutputStream os;
+        long existingSize = -1;
+
+        try {
+            // use relative filename in local work directory
+            GenericFile<ShareFileItem> target = (GenericFile<ShareFileItem>) exchange
+                    .getProperty(FileComponent.FILE_EXCHANGE_FILE);
+            org.apache.camel.util.ObjectHelper.notNull(target,
+                    "Exchange should have the " + FileComponent.FILE_EXCHANGE_FILE + " set");
+            String relativeName = target.getRelativeFilePath();
+
+            inProgress = new File(local, relativeName + ".inprogress");
+            local = new File(local, relativeName);
+
+            // create directory to local work file
+            boolean result = local.mkdirs();
+            if (!result) {
+                log.warn(
+                        "Failed to create local directory {} while retrieving file in local work directory. Directory may already exist or have been created externally",
+                        local);
+            }
+
+            // delete any local file (as its the temp file that is in the
+            // in-progress download)
+            if (local.exists()) {
+                if (!FileUtil.deleteFile(local)) {
+                    throw new GenericFileOperationFailedException("Cannot delete existing local work file: " + local);
+                }
+            }
+
+            // if a previous file exists then store its current size as its a
+            // partial download
+            boolean exists = inProgress.exists();
+            if (exists) {
+                existingSize = inProgress.length();
+            }
+
+            // if we do not resume download, then delete any existing temp file
+            // and create a new to use for in-progress download
+            if (!resumeDownload) {
+                if (exists && !FileUtil.deleteFile(inProgress)) {
+                    throw new GenericFileOperationFailedException(
+                            "Cannot delete existing local work file: " + inProgress);
+                }
+                if (!inProgress.createNewFile()) {
+                    throw new GenericFileOperationFailedException("Cannot create new local work file: " + inProgress);
+                }
+            }
+
+            // store content as a file in the local work directory in the temp
+            // handle
+            boolean append = resumeDownload && existingSize > 0;
+            os = new FileOutputStream(inProgress, append);
+
+            // set header with the path to the local work file
+            exchange.getIn().setHeader(FilesHeaders.FILE_LOCAL_WORK_PATH, local.getPath());
+
+        } catch (Exception e) {
+            throw new GenericFileOperationFailedException("Cannot create new local work file: " + local, e);
+        }
+
+        boolean result;
+        try {
+            GenericFile<ShareFileItem> target = (GenericFile<ShareFileItem>) exchange
+                    .getProperty(FileComponent.FILE_EXCHANGE_FILE);
+            // store the java.io.File handle as the body
+            target.setBody(local);
+
+            String path = FileUtil.onlyPath(name);
+            if (path != null) {
+                changeCurrentDirectory(path);
+            }
+            String remoteName = FileUtil.stripPath(name);
+
+            ShareFileRange range = new ShareFileRange(0);
+            // the file exists so lets try to resume the download
+            if (resumeDownload && existingSize > 0) {
+                log.trace("Client restartOffset: {}", existingSize);
+                log.debug("Resuming download of file: {} at position: {}", remoteName, existingSize);
+                range = new ShareFileRange(existingSize);
+            }
+            log.trace("Downloading {} to local work directory.", remoteName);
+            var ret = cwd().getFileClient(remoteName).downloadWithResponse(os, range, null, endpoint.getDataTimeout(),
+                    Context.NONE);
+            result = ret.getStatusCode() == HTTP_OK;
+
+        } catch (RuntimeException e) {
+            log.trace("Error occurred during retrieving file: {} to local directory.", name);
+            // if we do not attempt to resume download, then attempt to delete
+            // the temporary file
+            if (!resumeDownload) {
+                log.trace("Deleting local work file: {}", name);
+                // failed to retrieve the file so we need to close streams and
+                // delete in progress file
+                // must close stream before deleting file
+                IOHelper.close(os, "retrieve: " + name, log);
+                boolean deleted = FileUtil.deleteFile(inProgress);
+                if (!deleted) {
+                    log.warn(
+                            "Error occurred during retrieving file: {} to local directory. Cannot delete local work file: {}",
+                            inProgress, name);
+                }
+            }
+            throw new GenericFileOperationFailedException(e.getMessage(), e);
+        } finally {
+            // need to close the stream before rename it
+            IOHelper.close(os, "retrieve: " + name, log);
+        }
+
+        log.debug("Retrieve file to local work file result: {}", result);
+
+        if (result) {
+            log.trace("Renaming local in progress file from: {} to: {}", inProgress, local);
+            // operation went okay so rename temp to local after we have
+            // retrieved the data
+            try {
+                if (!FileUtil.renameFile(inProgress, local, false)) {
+                    throw new GenericFileOperationFailedException(
+                            "Cannot rename local work file from: " + inProgress + " to: " + local);
+                }
+            } catch (IOException e) {
+                throw new GenericFileOperationFailedException(
+                        "Cannot rename local work file from: " + inProgress + " to: " + local, e);
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public boolean storeFile(String name, Exchange exchange, long size) throws GenericFileOperationFailedException {
+        log.trace("storeFile({},,{})", name, size);
+
+        name = configuration.normalizePath(name);
+
+        boolean answer;
+        String path = FileUtil.onlyPath(name);
+        String targetName = name;
+        var backup = backup();
+
+        try {
+            if (path != null) {
+                changeCurrentDirectory(path);
+                targetName = FileUtil.stripPath(name);
+            }
+            answer = storeFile(name, targetName, exchange);
+        } catch (GenericFileOperationFailedException e) {
+            throw e;
+        } finally {
+            restore(backup);
+        }
+
+        return answer;
+    }
+
+    private boolean storeFile(String name, String targetName, Exchange exchange)
+            throws GenericFileOperationFailedException {
+        boolean existFile = false;
+        // if an existing file already exists what should we do?
+        if (endpoint.getFileExist() == GenericFileExist.Ignore || endpoint.getFileExist() == GenericFileExist.Fail) {
+            existFile = existsFile(targetName);
+            if (existFile && endpoint.getFileExist() == GenericFileExist.Ignore) {
+                // ignore but indicate that the file was written
+                log.trace("An existing file already exists: {}. Ignore and do not override it.", name);
+                return true;
+            } else if (existFile && endpoint.getFileExist() == GenericFileExist.Fail) {
+                throw new GenericFileOperationFailedException(
+                        "File already exist: " + name + ". Cannot write new file.");
+            }
+        }
+
+        InputStream is = null;
+        int length = 0;
+        if (exchange.getIn().getBody() == null) {
+            // Do an explicit test for a null body and decide what to do
+            if (endpoint.isAllowNullBody()) {
+                log.trace("Writing empty file.");
+                is = new ByteArrayInputStream(new byte[] {});
+            } else {
+                throw new GenericFileOperationFailedException("Cannot write null body to file: " + name);
+            }
+        }
+
+        try {
+            if (is == null) {
+                var knownLength = exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
+                if (knownLength != null) {
+                    is = exchange.getIn().getMandatoryBody(InputStream.class);
+                    length = knownLength.intValue();
+                } else {
+                    log.warn("No file length header, so converting body to byte[].  It might be memory intensive.");
+                    var bytes = exchange.getIn().getMandatoryBody(byte[].class);
+                    length = bytes.length;
+                    is = new ByteArrayInputStream(bytes);
+                }
+            }
+
+            final StopWatch watch = new StopWatch();
+            boolean answer = false;
+            log.debug("About to store file: {} length: {} using stream: {}", targetName, length, is);
+            if (existFile && endpoint.getFileExist() == GenericFileExist.Append) {
+                assert false; // rejected by options validation
+            } else {
+                var cwd = cwd();
+                var file = cwd.getFileClient(targetName);
+                if (deleteRemote(cwd, targetName) && createRemote(file, length)) {
+                    storeRemote(file, is);
+                    answer = true;
+                }
+            }
+            if (log.isDebugEnabled()) {
+                long time = watch.taken();
+                log.debug("Took {} ({} millis) to store: {} and files client returned: {}",
+                        TimeUtils.printDuration(time, true), time, targetName, answer);
+            }
+
+            return answer;
+
+        } catch (InvalidPayloadException | IOException e) {
+            throw new GenericFileOperationFailedException("Cannot store file: " + name, e);
+        } finally {
+            IOHelper.close(is, "store: " + name, log);
+        }
+    }
+
+    private void storeRemote(ShareFileClient file, InputStream is) throws IOException {
+        log.trace("> put {}", file.getFilePath());
+        // NOTE: here >4MiB is possible (unlike the upload limitation)
+        try (var os = file.getFileOutputStream()) {
+            // TODO add data timeout?
+            // TODO err if the is is shorter than allocated file length?
+            is.transferTo(os);
+            os.flush();
+        }
+    }
+
+    private boolean createRemote(ShareFileClient fileClient, int length) {
+        var code = fileClient.createWithResponse(length, null, null, null, null,
+                endpoint.getMetadataTimeout(), Context.NONE).getStatusCode();
+        return code == HTTP_CREATED || code == HTTP_OK;
+    }
+
+    @Override
+    public boolean existsFile(String name) throws GenericFileOperationFailedException {
+        log.trace("existsFile({})", name);
+        return existsRemote(getFileClient(name));
+    }
+
+    private boolean existsDirectory(String path) {
+        return existsRemote(getDirClient(path));
+    }
+
+    private boolean existsRemote(ShareDirectoryClient dirClient) {
+        try {
+            return Boolean.TRUE.equals(
+                    dirClient.existsWithResponse(endpoint.getMetadataTimeout(), Context.NONE).getValue());
+        } catch (ShareStorageException ex) {
+            // observed "Status code 404, ParentNotFound" for deep checks
+            if (ex.getStatusCode() == HTTP_NOT_FOUND) {
+                return false;
+            }
+            throw ex;
+        }
+    }
+
+    private boolean existsRemote(ShareFileClient fileClient) {
+        try {
+            return Boolean.TRUE
+                    .equals(fileClient.existsWithResponse(endpoint.getMetadataTimeout(), Context.NONE).getValue());
+        } catch (ShareStorageException ex) {
+            // observed "Status code 404, ParentNotFound" for deep checks
+            if (ex.getStatusCode() == HTTP_NOT_FOUND) {
+                return false;
+            }
+            throw ex;
+        }
+    }
+
+    /**
+     * @return a relative path, from the share root, of the current directory
+     */
+    @Override
+    public String getCurrentDirectory() throws GenericFileOperationFailedException {
+        String answer = cwd().getDirectoryPath();
+        log.trace("getCurrentDirectory(): {}", answer);
+        return answer;
+    }
+
+    @Override
+    public void changeCurrentDirectory(String path) throws GenericFileOperationFailedException {
+        log.trace("changeCurrentDirectory({})", path);
+        if (FilesPath.isEmpty(path) || path.equals(FilesPath.CWD)
+                || path.equals(FilesPath.SHARE_ROOT + cwd().getDirectoryPath())) {
+            return;
+        }
+
+        var dirs = FilesPath.splitToSteps(path, true);
+        for (String dir : dirs) {
+            trivialCd(dir);
+        }
+    }
+
+    private void trivialCd(String pathStep) {
+        if (FilesPath.isEmptyStep(pathStep)) {
+            return;
+        }
+
+        var cwd = cwd();
+        log.trace("{}> cd {}", cwd.getDirectoryPath(), pathStep);
+        boolean success;
+        try {
+            if (FilesPath.SHARE_ROOT.equals(pathStep)) {
+                changeToRoot();
+                success = true;
+            } else if (FilesPath.PARENT.equals(pathStep)) {
+                changeToParentDirectory();
+                success = true;
+            } else {
+                var subDir = cwd.getSubdirectoryClient(pathStep);
+                success = existsRemote(subDir);
+                if (success) {
+                    dirStack.push(subDir);
+                }
+            }
+        } catch (RuntimeException e) {
+            throw new GenericFileOperationFailedException(e.getMessage(), e);
+        }
+        if (!success) {
+            throw new GenericFileOperationFailedException("Cannot cd(" + pathStep + ").");
+        }
+    }
+
+    @Override
+    public void changeToParentDirectory() throws GenericFileOperationFailedException {
+        log.trace("changeToParentDirectory()");
+        try {
+            dirStack.pop();
+        } catch (EmptyStackException e) {
+            throw new GenericFileOperationFailedException("Root dir does not have parent.", e);
+        }
+    }
+
+    void changeToRoot() {
+        if (!isConnected()) {
+            throw new GenericFileOperationFailedException("Cannot cd to the share root: not connected");
+        }
+        dirStack.empty();
+        dirStack.push(root);
+    }
+
+    @Override
+    public ShareFileItem[] listFiles() throws GenericFileOperationFailedException {
+        log.trace("listFiles()");
+        return listRemote(cwd());
+    }
+
+    @Override
+    public ShareFileItem[] listFiles(String path) throws GenericFileOperationFailedException {
+        log.trace("listFiles({})", path);
+        return listRemote(getDirClient(path));
+    }
+
+    private ShareDirectoryClient getDirClient(String path) {
+
+        assert FilesPath.isAbsolute(path);
+
+        if (FilesPath.isRoot(path)) {
+            return root;
+        }
+        var dir = FilesPath.ensureRelative(path);
+        return root.getSubdirectoryClient(dir);
+    }
+
+    private ShareFileClient getFileClient(String path) {
+
+        assert FilesPath.isAbsolute(path);
+        assert !FilesPath.isRoot(path);
+
+        return root.getFileClient(FilesPath.ensureRelative(path));
+    }
+
+    private ShareFileItem[] listRemote(ShareDirectoryClient dir) {
+        log.trace("{}> ls -a", dir.getDirectoryPath());
+        try {
+            var withTS = new ShareListFilesAndDirectoriesOptions().setIncludeTimestamps(true);
+            return dir.listFilesAndDirectories(withTS, endpoint.getMetadataTimeout(), Context.NONE).stream()
+                    .toArray(ShareFileItem[]::new);
+        } catch (RuntimeException e) {
+            throw new GenericFileOperationFailedException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public boolean sendNoop() throws GenericFileOperationFailedException {
+        log.trace("sendNoOp()");
+        return existsRemote(root);
+    }
+
+    @Override
+    public boolean sendSiteCommand(String command) throws GenericFileOperationFailedException {
+        log.trace("sendSiteCommand({})", command);
+        return true;
+    }
+
+    public ShareServiceClient getClient() {
+        if (client == null) {
+            client = createClient();
+        }
+        return client;
+    }
+
+    private ShareServiceClient createClient() {
+
+        var builder = new ShareServiceClientBuilder().endpoint(HTTPS + "://" + configuration.getHost());
+        var sharedKey = configuration.getSharedKey();
+        if (token.isInvalid()) {
+            if (sharedKey != null) {
+                log.warn("The configured SAS token is not valid, using the shared key fallback.");
+                var keyB64 = FilesURIStrings.reconstructBase64EncodedValue(sharedKey);
+                builder.credential(new StorageSharedKeyCredential(configuration.getAccount(), keyB64));
+            } else {
+                log.error("A valid SAS token or shared key must be configured.");
+            }
+            // TODO Azure AD
+            // https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-requests-to-azure-storage
+        } else {
+            builder = builder.sasToken(token.toURIQuery());
+        }
+        return builder.buildClient();
+    }
+
+    void reconnectIfNecessary(Exchange exchange) throws GenericFileOperationFailedException {
+        boolean reconnectRequired;
+        try {
+            reconnectRequired = !isConnected();
+        } catch (GenericFileOperationFailedException e) {
+            log.trace("Going to reconnect because of: ", e);
+            reconnectRequired = true;
+        }
+        if (reconnectRequired) {
+            log.trace("Probing if the file service is connectible ...");
+            connect(configuration, exchange);
+        }
+    }
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesPath.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesPath.java
new file mode 100644
index 00000000000..9cd32b1e309
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesPath.java
@@ -0,0 +1,136 @@
+package org.apache.camel.component.file.azure;
+
+/**
+ * The path separator is {@code /}.
+ * <p>
+ * The absolute paths start with the path separator, they do not include the share name and they are relative to the
+ * share root rather than to the endpoint starting directory.
+ */
+public class FilesPath {
+
+    public static final String PARENT = "..";
+
+    public static final String CWD = ".";
+
+    public static final String SHARE_ROOT = "/";
+
+    public static final char PATH_SEPARATOR = '/';
+
+    private FilesPath() {
+        // for now, non-constructible
+    }
+
+    public static boolean isEmpty(String path) {
+        return path == null || path.isBlank();
+    }
+
+    public static boolean isEmptyStep(String path) {
+        // TODO blank step like " " could be valid, but Windows trims trailing spaces
+        return isEmpty(path) || path.equals(CWD);
+    }
+
+    public static boolean isRoot(String path) {
+        // no ambiguity such as / or \
+        return path != null && path.equals(SHARE_ROOT);
+    }
+
+    public static boolean isAbsolute(String path) {
+        // no ambiguity such as / or \
+        return path != null && path.startsWith(SHARE_ROOT);
+    }
+
+    public static String ensureRelative(String path) {
+        if (path.startsWith(SHARE_ROOT)) {
+            return path.substring(1);
+        }
+        return path;
+    }
+
+    public static String concat(String dir, String subPath) {
+        if (isEmptyStep(dir)) {
+            return subPath;
+        }
+        if (isEmptyStep(subPath)) {
+            return dir;
+        }
+        return hasTrailingSeparator(dir) ? dir + subPath : dir + PATH_SEPARATOR + subPath;
+    }
+
+    public static String trimTrailingSeparator(String path) {
+        if (path == null) {
+            return null;
+        }
+        if (hasTrailingSeparator(path)) {
+            return path.substring(0, path.length() - 1);
+        }
+        return path;
+    }
+
+    public static boolean hasTrailingSeparator(String path) {
+        return path.charAt(path.length() - 1) == PATH_SEPARATOR;
+    }
+
+    public static String trimParentPath(String path) {
+        if (path == null) {
+            return null;
+        }
+
+        var lastSeparator = path.lastIndexOf(PATH_SEPARATOR);
+        if (lastSeparator != -1) {
+            return path.substring(lastSeparator + 1);
+        }
+        return path;
+    }
+
+    public static String extractParentPath(String path) {
+        if (path == null) {
+            return null;
+        }
+
+        var lastSeparator = path.lastIndexOf(PATH_SEPARATOR);
+        if (lastSeparator == 0) {
+            return SHARE_ROOT;
+        } else if (lastSeparator > 0) {
+            return path.substring(0, lastSeparator);
+        }
+        return null;
+    }
+
+    public static String[] split(String path) {
+        return splitToSteps(path, false);
+    }
+
+    public static String[] splitToSteps(String path, boolean preserveRootAsStep) {
+        if (path == null) {
+            return null;
+        }
+
+        if (preserveRootAsStep && path.equals(SHARE_ROOT)) {
+            return new String[] { SHARE_ROOT };
+        }
+
+        var includeRoot = preserveRootAsStep && path.startsWith(SHARE_ROOT);
+        if (!(includeRoot)) {
+            path = ensureRelative(path);
+        }
+
+        // no ambiguity such as "/|\\\\"
+        var pathSteps = path.split("" + PATH_SEPARATOR);
+
+        if (includeRoot) {
+            pathSteps[0] = SHARE_ROOT; // replace leading ""
+        }
+        return pathSteps;
+    }
+
+    public static String trimLeadingSeparator(String path) {
+        if (path == null) {
+            return null;
+        }
+        if (isAbsolute(path)) {
+            return path.substring(1);
+        }
+        return path;
+    }
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesProducer.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesProducer.java
new file mode 100644
index 00000000000..d1d3b507454
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesProducer.java
@@ -0,0 +1,23 @@
+package org.apache.camel.component.file.azure;
+
+import com.azure.storage.file.share.models.ShareFileItem;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.file.remote.RemoteFileProducer;
+
+public class FilesProducer extends RemoteFileProducer<ShareFileItem> {
+
+    FilesProducer(FilesEndpoint endpoint, FilesOperations operations) {
+        super(endpoint, operations);
+    }
+
+    @Override
+    public void preWriteCheck(Exchange exchange) throws Exception {
+        // noop
+    }
+
+    @Override
+    public void start() {
+        super.start();
+        ((FilesOperations) operations).reconnectIfNecessary(null);
+    }
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesToken.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesToken.java
new file mode 100644
index 00000000000..23627708933
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesToken.java
@@ -0,0 +1,200 @@
+package org.apache.camel.component.file.azure;
+
+import java.net.URISyntaxException;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+
+/**
+ * Azure Files account or service SAS token.
+ * <p>
+ * The user delegation SAS token is not specified for Azure Files.
+ * 
+ * @see com.azure.storage.common.implementation.Constants.UrlConstants
+ * @see <a href="https://learn.microsoft.com/en-us/rest/api/storageservices/create-account-sas">Azure account SAS</a>
+ * @see <a href="https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas">Azure service SAS</a>
+ */
+@UriParams
+final class FilesToken {
+
+    @UriParam(label = "both", description = "part of SAS token", secret = true)
+    protected String sv;
+    @UriParam(label = "both", description = "part of account SAS token", secret = true)
+    protected String ss;
+    @UriParam(label = "both", description = "part of SAS token", secret = true)
+    protected String srt;
+    @UriParam(label = "both", description = "part of SAS token", secret = true)
+    protected String sp;
+    @UriParam(label = "both", description = "part of SAS token", secret = true)
+    protected String se;
+    @UriParam(label = "both", description = "part of SAS token", secret = true)
+    protected String st;
+    @UriParam(label = "both", description = "part of SAS token", secret = true)
+    protected String spr;
+    @UriParam(label = "both", description = "part of SAS token", secret = true)
+    protected String sig;
+
+    @UriParam(label = "both", description = "part of service SAS token", secret = true)
+    private String si;
+    @UriParam(label = "both", description = "part of service SAS token", secret = true)
+    private String sr;
+    @UriParam(label = "both", description = "part of service SAS token", secret = true)
+    private String sdd;
+    @UriParam(label = "both", description = "part of SAS token", secret = true)
+    private String sip;
+
+    public void setSv(String sv) {
+        this.sv = sv;
+    }
+
+    public void setSs(String ss) {
+        this.ss = ss;
+    }
+
+    public void setSrt(String srt) {
+        this.srt = srt;
+    }
+
+    public void setSp(String sp) {
+        this.sp = sp;
+    }
+
+    public void setSe(String se) {
+        this.se = se;
+    }
+
+    public void setSt(String st) {
+        this.st = st;
+    }
+
+    public void setSpr(String spr) {
+        this.spr = spr;
+    }
+
+    public void setSig(String sig) {
+        this.sig = sig;
+    }
+
+    public void setSi(String si) {
+        this.si = si;
+    }
+
+    public void setSr(String sr) {
+        this.sr = sr;
+    }
+
+    public void setSdd(String sdd) {
+        this.sdd = sdd;
+    }
+
+    public void setSip(String sip) {
+        this.sip = sip;
+    }
+
+    boolean isInvalid() {
+        return sig == null || sv == null || se == null || !(isAccountTokenForFilesService() || isFilesServiceToken());
+    }
+
+    private boolean isAccountTokenForFilesService() {
+        return ss != null && ss.contains("f");
+    }
+
+    private boolean isFilesServiceToken() {
+        return sr != null && (sr.contains("f") || sr.contains("s"));
+    }
+
+    // sv=2021-12-02&ss=f&srt=o&sp=rwdlc&se=2023-05-05T19:27:05Z&st=2023-04-28T11:27:05Z&spr=https&sig=TCU0PcBjrxRbKOW%2FLA7HrPLISin6FXLNkRtLvmxkvhY%3D"
+    // params in Azure order
+    public String toURIQuery() {
+        try {
+            return Stream
+                    .of(e("sv", sv), e("ss", ss), e("srt", srt), e("sp", sp), e("se", se), e("st", st),
+                            e("spr", spr), e("sig", sig), e("si", si), e("sr", sr), e("sdd", sdd), e("sip", sip))
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.joining("&"));
+        } catch (URISyntaxException e) {
+            return null;
+        }
+    }
+
+    private String e(String param, String value) throws URISyntaxException {
+        if (value == null || value.isBlank()) {
+            return null;
+        }
+        return param + "=" + FilesURIStrings.encodeTokenValue(value);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(sdd, se, si, sig, sip, sp, spr, sr, srt, ss, st, sv);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        FilesToken other = (FilesToken) obj;
+        return Objects.equals(sdd, other.sdd) && Objects.equals(se, other.se)
+                && Objects.equals(si, other.si) && Objects.equals(sig, other.sig)
+                && Objects.equals(sip, other.sip) && Objects.equals(sp, other.sp)
+                && Objects.equals(spr, other.spr) && Objects.equals(sr, other.sr)
+                && Objects.equals(srt, other.srt) && Objects.equals(ss, other.ss)
+                && Objects.equals(st, other.st) && Objects.equals(sv, other.sv);
+    }
+
+    String getSv() {
+        return sv;
+    }
+
+    String getSs() {
+        return ss;
+    }
+
+    String getSrt() {
+        return srt;
+    }
+
+    String getSp() {
+        return sp;
+    }
+
+    String getSe() {
+        return se;
+    }
+
+    String getSt() {
+        return st;
+    }
+
+    String getSpr() {
+        return spr;
+    }
+
+    String getSig() {
+        return sig;
+    }
+
+    String getSi() {
+        return si;
+    }
+
+    String getSr() {
+        return sr;
+    }
+
+    String getSdd() {
+        return sdd;
+    }
+
+    String getSip() {
+        return sip;
+    }
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesURIStrings.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesURIStrings.java
new file mode 100644
index 00000000000..aeab897e143
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesURIStrings.java
@@ -0,0 +1,48 @@
+package org.apache.camel.component.file.azure;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+
+import org.apache.camel.util.URISupport;
+
+/**
+ * Helper for Camel endpoint URI strings.
+ * <p>
+ * In Camel, URIs are most commonly passed as strings and they are flexible e.g. they could contain expressions that
+ * prior evaluation could violate URI RFC, ...
+ */
+final class FilesURIStrings {
+
+    public static final char QUERY_SEPARATOR = '?';
+
+    /**
+     * Get the base uri part before the options as they can be non URI valid such as the expression using $ chars and
+     * the URI constructor will regard $ as an illegal character and we don't want to enforce end users to to escape the
+     * $ for the expression (file language)
+     */
+    static URI getBaseURI(String uri) throws URISyntaxException {
+        String baseUri = uri;
+        if (uri.indexOf(QUERY_SEPARATOR) != -1) {
+            baseUri = uri.substring(0, uri.indexOf(QUERY_SEPARATOR));
+        }
+        return new URI(baseUri);
+    }
+
+    static String reconstructBase64EncodedValue(String value) {
+        // base64 allows + and =, URI encoded as %2B and %3D
+        // Camel URI configurers decode both + and %2B to a space
+        return value.replace(" ", "+");
+    }
+
+    /**
+     * Uses encoding style expected by the files service: it preserves time separator ':' and encodes base64 plus '+',
+     * slash '/' and padding '='.
+     */
+    static String encodeTokenValue(String value) throws URISyntaxException {
+        return URISupport.createQueryString(Collections.singletonMap("x", value)).substring(2)
+                .replace("+", "%2B") // sig is base64
+                .replace("%3A", ":"); // se has time separator
+    }
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/NormalizedOperations.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/NormalizedOperations.java
new file mode 100644
index 00000000000..156bea13e91
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/NormalizedOperations.java
@@ -0,0 +1,43 @@
+package org.apache.camel.component.file.azure;
+
+import com.azure.storage.file.share.models.ShareFileItem;
+import org.apache.camel.component.file.GenericFileOperationFailedException;
+import org.apache.camel.component.file.remote.RemoteFileConfiguration;
+import org.apache.camel.component.file.remote.RemoteFileOperations;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Cleanup the remote contract a bit, get rid of OS-specific path separators, etc.
+ */
+abstract class NormalizedOperations implements RemoteFileOperations<ShareFileItem> {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final RemoteFileConfiguration configuration;
+
+    protected NormalizedOperations(RemoteFileConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    @Override
+    public final boolean buildDirectory(String directory, boolean absolute) throws GenericFileOperationFailedException {
+
+        log.trace("buildDirectory({},{})", directory, absolute);
+
+        // by observation OS-specific path separator can appear
+
+        directory = configuration.normalizePath(directory);
+
+        if (absolute) {
+            return buildDirectory(directory);
+        } else {
+            // wishful thinking, we inherited a wide contract
+            // but our needs are narrower. We will see....
+            throw new IllegalArgumentException("Relative path: " + directory);
+        }
+    }
+
+    /** Normalized form of {@link #buildDirectory(String, boolean)}. */
+    abstract protected boolean buildDirectory(String path);
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/FilesChangedExclusiveReadLockStrategy.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/FilesChangedExclusiveReadLockStrategy.java
new file mode 100644
index 00000000000..b67479900a6
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/FilesChangedExclusiveReadLockStrategy.java
@@ -0,0 +1,156 @@
+/*
+ * 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.azure.strategy;
+
+import java.time.Duration;
+
+import com.azure.storage.file.share.models.ShareFileItem;
+import org.apache.camel.Exchange;
+import org.apache.camel.LoggingLevel;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy;
+import org.apache.camel.component.file.GenericFileOperations;
+import org.apache.camel.spi.CamelLogger;
+import org.apache.camel.support.task.BlockingTask;
+import org.apache.camel.support.task.Tasks;
+import org.apache.camel.support.task.budget.Budgets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FilesChangedExclusiveReadLockStrategy implements GenericFileExclusiveReadLockStrategy<ShareFileItem> {
+    private static final Logger LOG = LoggerFactory.getLogger(FilesChangedExclusiveReadLockStrategy.class);
+    private long timeout;
+    private long checkInterval = 5000;
+    private LoggingLevel readLockLoggingLevel = LoggingLevel.WARN;
+    private long minLength = 1;
+    private long minAge;
+    private boolean fastExistsCheck;
+
+    @Override
+    public void prepareOnStartup(
+            GenericFileOperations<ShareFileItem> tGenericFileOperations,
+            GenericFileEndpoint<ShareFileItem> tGenericFileEndpoint)
+            throws Exception {
+        // noop
+    }
+
+    @Override
+    public boolean acquireExclusiveReadLock(
+            GenericFileOperations<ShareFileItem> operations, GenericFile<ShareFileItem> file, Exchange exchange)
+            throws Exception {
+        LOG.trace("Waiting for exclusive read lock to file: {}", file);
+
+        BlockingTask task = Tasks.foregroundTask()
+                .withBudget(Budgets.iterationTimeBudget()
+                        .withMaxDuration(Duration.ofMillis(timeout))
+                        .withInterval(Duration.ofMillis(checkInterval))
+                        .build())
+                .withName("ftp-acquire-exclusive-read-lock")
+                .build();
+
+        FilesExclusiveReadLockCheck exclusiveReadLockCheck
+                = new FilesExclusiveReadLockCheck(fastExistsCheck, minAge, minLength);
+
+        if (!task.run(() -> exclusiveReadLockCheck.tryAcquireExclusiveReadLock(operations, file))) {
+            CamelLogger.log(LOG, readLockLoggingLevel,
+                    "Cannot acquire read lock within " + timeout + " millis. Will skip the file: " + file);
+
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void releaseExclusiveReadLockOnAbort(
+            GenericFileOperations<ShareFileItem> operations, GenericFile<ShareFileItem> file, Exchange exchange)
+            throws Exception {
+        // noop
+    }
+
+    @Override
+    public void releaseExclusiveReadLockOnRollback(
+            GenericFileOperations<ShareFileItem> operations, GenericFile<ShareFileItem> file, Exchange exchange)
+            throws Exception {
+        // noop
+    }
+
+    @Override
+    public void releaseExclusiveReadLockOnCommit(
+            GenericFileOperations<ShareFileItem> operations, GenericFile<ShareFileItem> file, Exchange exchange)
+            throws Exception {
+        // noop
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+
+    @Override
+    public void setTimeout(long timeout) {
+        this.timeout = timeout;
+    }
+
+    public long getCheckInterval() {
+        return checkInterval;
+    }
+
+    @Override
+    public void setCheckInterval(long checkInterval) {
+        this.checkInterval = checkInterval;
+    }
+
+    @Override
+    public void setReadLockLoggingLevel(LoggingLevel readLockLoggingLevel) {
+        this.readLockLoggingLevel = readLockLoggingLevel;
+    }
+
+    @Override
+    public void setMarkerFiler(boolean markerFiler) {
+        // noop - not supported by ftp
+    }
+
+    @Override
+    public void setDeleteOrphanLockFiles(boolean deleteOrphanLockFiles) {
+        // noop - not supported by ftp
+    }
+
+    public long getMinLength() {
+        return minLength;
+    }
+
+    public void setMinLength(long minLength) {
+        this.minLength = minLength;
+    }
+
+    public long getMinAge() {
+        return minAge;
+    }
+
+    public void setMinAge(long minAge) {
+        this.minAge = minAge;
+    }
+
+    public boolean isFastExistsCheck() {
+        return fastExistsCheck;
+    }
+
+    public void setFastExistsCheck(boolean fastExistsCheck) {
+        this.fastExistsCheck = fastExistsCheck;
+    }
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/FilesExclusiveReadLockCheck.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/FilesExclusiveReadLockCheck.java
new file mode 100644
index 00000000000..a9dd321ecbf
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/FilesExclusiveReadLockCheck.java
@@ -0,0 +1,140 @@
+/*
+ * 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.azure.strategy;
+
+import java.util.Date;
+
+import com.azure.storage.file.share.models.ShareFileItem;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.component.file.GenericFileOperations;
+import org.apache.camel.util.StopWatch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FilesExclusiveReadLockCheck {
+    // based on misnamed ExclusiveReadLockCheck, in reality it's FTP specific
+    private static final Logger LOG = LoggerFactory.getLogger(FilesExclusiveReadLockCheck.class);
+    private final boolean fastExistsCheck;
+    private final long startTime;
+    private final long minAge;
+    private final long minLength;
+    private final StopWatch watch;
+
+    private long lastModified;
+    private long length;
+
+    public FilesExclusiveReadLockCheck(boolean fastExistsCheck, long minAge, long minLength) {
+        this.fastExistsCheck = fastExistsCheck;
+        this.startTime = new Date().getTime();
+        this.minAge = minAge;
+        this.minLength = minLength;
+        this.watch = new StopWatch();
+
+        this.lastModified = Long.MIN_VALUE;
+        this.length = Long.MIN_VALUE;
+    }
+
+    public boolean tryAcquireExclusiveReadLock(
+            GenericFileOperations<ShareFileItem> operations, GenericFile<ShareFileItem> file) {
+        long newLastModified = 0;
+        long newLength = 0;
+
+        ShareFileItem[] files = getFiles(operations, file);
+
+        LOG.trace("List files {} found {} files", file.getAbsoluteFilePath(), files.length);
+        for (ShareFileItem f : files) {
+            boolean match;
+            if (fastExistsCheck) {
+                // uses the absolute file path as well
+                match = f.getName().equals(file.getAbsoluteFilePath()) || f.getName().equals(file.getFileNameOnly());
+            } else {
+                match = f.getName().equals(file.getFileNameOnly());
+            }
+            if (match) {
+                newLength = f.getFileSize();
+                newLastModified = lastModified(f);
+            }
+        }
+
+        LOG.trace("Previous last modified: {}, new last modified: {}", lastModified, newLastModified);
+        LOG.trace("Previous length: {}, new length: {}", length, newLength);
+        long newOlderThan = startTime + watch.taken() - minAge;
+        LOG.trace("New older than threshold: {}", newOlderThan);
+
+        if (isReadLockAcquired(lastModified, length, newLastModified, newLength, newOlderThan)) {
+            LOG.trace("Read lock acquired.");
+            return true;
+        }
+
+        lastModified = newLastModified;
+        length = newLength;
+        return false;
+    }
+
+    private ShareFileItem[] getFiles(GenericFileOperations<ShareFileItem> operations, GenericFile<ShareFileItem> file) {
+        ShareFileItem[] files;
+        if (fastExistsCheck) {
+            // use the absolute file path to only pickup the file we want to
+            // check, this avoids expensive
+            // list operations if we have a lot of files in the directory
+            files = getFilesFast(operations, file);
+        } else {
+            files = getFilesByFilter(operations, file);
+        }
+        return files;
+    }
+
+    private ShareFileItem[] getFilesByFilter(GenericFileOperations<ShareFileItem> operations, GenericFile<ShareFileItem> file) {
+        // fast option not enabled, so list the directory and filter the
+        // file name
+        String path = file.getParent();
+        if (path.equals("/") || path.equals("\\")) {
+            // special for root (= home) directory
+            LOG.trace(
+                    "Using full directory listing in home directory to update file information. Consider enabling fastExistsCheck option.");
+            return operations.listFiles();
+        } else {
+            LOG.trace(
+                    "Using full directory listing to update file information for {}. Consider enabling fastExistsCheck option.",
+                    path);
+            return operations.listFiles(path);
+        }
+    }
+
+    private ShareFileItem[] getFilesFast(GenericFileOperations<ShareFileItem> operations, GenericFile<ShareFileItem> file) {
+        String path = file.getAbsoluteFilePath();
+        if (path.equals("/") || path.equals("\\")) {
+            // special for root (= home) directory
+            LOG.trace("Using fast exists to update file information in home directory");
+            return operations.listFiles();
+        } else {
+            LOG.trace("Using fast exists to update file information for {}", path);
+            return operations.listFiles(path);
+        }
+    }
+
+    private boolean isReadLockAcquired(
+            long lastModified, long length, long newLastModified, long newLength, long newOlderThan) {
+        return newLength >= minLength && (minAge == 0 && newLastModified == lastModified && newLength == length
+                || minAge != 0 && newLastModified < newOlderThan);
+    }
+
+    private static long lastModified(ShareFileItem file) {
+        return file.getProperties().getLastModified().toInstant().toEpochMilli();
+    }
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/FilesProcessStrategyFactory.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/FilesProcessStrategyFactory.java
new file mode 100644
index 00000000000..0b8f83fadb6
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/FilesProcessStrategyFactory.java
@@ -0,0 +1,125 @@
+/*
+ * 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.azure.strategy;
+
+import java.util.Map;
+
+import com.azure.storage.file.share.models.ShareFileItem;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Expression;
+import org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy;
+import org.apache.camel.component.file.GenericFileProcessStrategy;
+import org.apache.camel.component.file.GenericFileProcessStrategyFactory;
+import org.apache.camel.component.file.strategy.GenericFileDeleteProcessStrategy;
+import org.apache.camel.component.file.strategy.GenericFileExpressionRenamer;
+import org.apache.camel.component.file.strategy.GenericFileNoOpProcessStrategy;
+import org.apache.camel.component.file.strategy.GenericFileRenameExclusiveReadLockStrategy;
+import org.apache.camel.component.file.strategy.GenericFileRenameProcessStrategy;
+import org.apache.camel.util.ObjectHelper;
+
+public final class FilesProcessStrategyFactory implements GenericFileProcessStrategyFactory<ShareFileItem> {
+
+    @Override
+    public GenericFileProcessStrategy<ShareFileItem> createGenericFileProcessStrategy(
+            CamelContext context, Map<String, Object> params) {
+
+        // We assume a value is present only if its value not null for String
+        // and 'true' for boolean
+        Expression moveExpression = (Expression) params.get("move");
+        Expression moveFailedExpression = (Expression) params.get("moveFailed");
+        Expression preMoveExpression = (Expression) params.get("preMove");
+        boolean isNoop = params.get("noop") != null;
+        boolean isDelete = params.get("delete") != null;
+        boolean isMove = moveExpression != null || preMoveExpression != null || moveFailedExpression != null;
+
+        if (isDelete) {
+            GenericFileDeleteProcessStrategy<ShareFileItem> strategy = new GenericFileDeleteProcessStrategy<>();
+            strategy.setExclusiveReadLockStrategy(getExclusiveReadLockStrategy(params));
+            if (preMoveExpression != null) {
+                GenericFileExpressionRenamer<ShareFileItem> renamer = new GenericFileExpressionRenamer<>();
+                renamer.setExpression(preMoveExpression);
+                strategy.setBeginRenamer(renamer);
+            }
+            if (moveFailedExpression != null) {
+                GenericFileExpressionRenamer<ShareFileItem> renamer = new GenericFileExpressionRenamer<>();
+                renamer.setExpression(moveFailedExpression);
+                strategy.setFailureRenamer(renamer);
+            }
+            return strategy;
+        } else if (isMove || isNoop) {
+            GenericFileRenameProcessStrategy<ShareFileItem> strategy = new GenericFileRenameProcessStrategy<>();
+            strategy.setExclusiveReadLockStrategy(getExclusiveReadLockStrategy(params));
+            if (!isNoop && moveExpression != null) {
+                // move on commit is only possible if not noop
+                GenericFileExpressionRenamer<ShareFileItem> renamer = new GenericFileExpressionRenamer<>();
+                renamer.setExpression(moveExpression);
+                strategy.setCommitRenamer(renamer);
+            }
+            // both move and noop supports pre move
+            if (moveFailedExpression != null) {
+                GenericFileExpressionRenamer<ShareFileItem> renamer = new GenericFileExpressionRenamer<>();
+                renamer.setExpression(moveFailedExpression);
+                strategy.setFailureRenamer(renamer);
+            }
+            // both move and noop supports pre move
+            if (preMoveExpression != null) {
+                GenericFileExpressionRenamer<ShareFileItem> renamer = new GenericFileExpressionRenamer<>();
+                renamer.setExpression(preMoveExpression);
+                strategy.setBeginRenamer(renamer);
+            }
+            return strategy;
+        } else {
+            // default strategy will do nothing
+            GenericFileNoOpProcessStrategy<ShareFileItem> strategy = new GenericFileNoOpProcessStrategy<>();
+            strategy.setExclusiveReadLockStrategy(getExclusiveReadLockStrategy(params));
+            return strategy;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static GenericFileExclusiveReadLockStrategy<ShareFileItem> getExclusiveReadLockStrategy(
+            Map<String, Object> params) {
+        GenericFileExclusiveReadLockStrategy<ShareFileItem> strategy
+                = (GenericFileExclusiveReadLockStrategy<ShareFileItem>) params.get("exclusiveReadLockStrategy");
+        if (strategy != null) {
+            return strategy;
+        }
+
+        // no explicit strategy set then fallback to readLock option
+        String readLock = (String) params.get("readLock");
+        if (ObjectHelper.isNotEmpty(readLock)) {
+            if ("none".equals(readLock) || "false".equals(readLock)) {
+                return null;
+            } else if ("rename".equals(readLock)) {
+                GenericFileRenameExclusiveReadLockStrategy<ShareFileItem> readLockStrategy
+                        = new GenericFileRenameExclusiveReadLockStrategy<>();
+
+                StrategyUtil.setup(readLockStrategy, params);
+
+                return readLockStrategy;
+            } else if ("changed".equals(readLock)) {
+                FilesChangedExclusiveReadLockStrategy readLockStrategy = new FilesChangedExclusiveReadLockStrategy();
+
+                StrategyUtil.setup(readLockStrategy, params);
+
+                return readLockStrategy;
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/StrategyUtil.java b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/StrategyUtil.java
new file mode 100644
index 00000000000..d326562757f
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/strategy/StrategyUtil.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.camel.component.file.azure.strategy;
+
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.apache.camel.LoggingLevel;
+import org.apache.camel.component.file.strategy.GenericFileRenameExclusiveReadLockStrategy;
+
+final class StrategyUtil {
+
+    private StrategyUtil() {
+
+    }
+
+    static <T> void ifNotEmpty(Map<String, Object> params, String key, Class<T> clazz, Consumer<T> consumer) {
+        Object o = params.get(key);
+
+        if (o != null) {
+            consumer.accept(clazz.cast(o));
+        }
+    }
+
+    static void setup(GenericFileRenameExclusiveReadLockStrategy<?> readLockStrategy, Map<String, Object> params) {
+        ifNotEmpty(params, "readLockTimeout", Long.class, readLockStrategy::setTimeout);
+        ifNotEmpty(params, "readLockCheckInterval", Long.class, readLockStrategy::setCheckInterval);
+        ifNotEmpty(params, "readLockMarkerFile", Boolean.class, readLockStrategy::setMarkerFiler);
+        ifNotEmpty(params, "readLockLoggingLevel", LoggingLevel.class, readLockStrategy::setReadLockLoggingLevel);
+    }
+
+    static void setup(FilesChangedExclusiveReadLockStrategy readLockStrategy, Map<String, Object> params) {
+        ifNotEmpty(params, "readLockTimeout", Long.class, readLockStrategy::setTimeout);
+        ifNotEmpty(params, "readLockCheckInterval", Long.class, readLockStrategy::setCheckInterval);
+        ifNotEmpty(params, "readLockMinLength", Long.class, readLockStrategy::setMinLength);
+        ifNotEmpty(params, "readLockMinAge", Long.class, readLockStrategy::setMinAge);
+        ifNotEmpty(params, "fastExistsCheck", Boolean.class, readLockStrategy::setFastExistsCheck);
+        ifNotEmpty(params, "readLockMarkerFile", Boolean.class, readLockStrategy::setMarkerFiler);
+        ifNotEmpty(params, "readLockLoggingLevel", LoggingLevel.class, readLockStrategy::setReadLockLoggingLevel);
+    }
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesConfigurationTests.java b/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesConfigurationTests.java
new file mode 100644
index 00000000000..98fd801c7ba
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesConfigurationTests.java
@@ -0,0 +1,127 @@
+package org.apache.camel.component.file.azure;
+
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class FilesConfigurationTests extends CamelTestSupport {
+
+    @Test
+    void accountForAccountHostURIShouldBeExtracted() {
+        var remoteConf = context
+                .getEndpoint("azure-files://account.file.core.windows.net/share", FilesEndpoint.class)
+                .getConfiguration();
+        assertEquals("account", remoteConf.getAccount());
+    }
+
+    @Test
+    void accountForAccountOnlyURIShouldBeExtracted() {
+        var remoteConf = context.getEndpoint("azure-files://account/share", FilesEndpoint.class).getConfiguration();
+        assertEquals("account", remoteConf.getAccount());
+    }
+
+    @Test
+    void hostForAccountHostURIShouldBeExtracted() {
+        var remoteConf = context
+                .getEndpoint("azure-files://account.file.core.windows.net/share", FilesEndpoint.class)
+                .getConfiguration();
+        assertEquals("account.file.core.windows.net", remoteConf.getHost());
+    }
+
+    @Test
+    void hostForAccountURIShouldDefaultTo_file_core_windows_netSuffix() {
+        var remoteConf = context.getEndpoint("azure-files://account/share", FilesEndpoint.class).getConfiguration();
+        assertEquals("account.file.core.windows.net", remoteConf.getHost());
+    }
+
+    @Test
+    void shareForValidURIShouldBeExtracted() {
+        var remoteConf = context.getEndpoint("azure-files://account/share?", FilesEndpoint.class).getConfiguration();
+        assertEquals("share", remoteConf.getShare());
+    }
+
+    @Test
+    void shareForValidURIShouldBeExtracted2() {
+        var remoteConf = context.getEndpoint("azure-files://account/share/", FilesEndpoint.class).getConfiguration();
+        assertEquals("share", remoteConf.getShare());
+    }
+
+    @Test
+    void shareForDoubleSlashURIPathShouldBeExtracted() {
+        // relaxed handling, it could be rejected if we wanted to be strict
+        var remoteConf = context.getEndpoint("azure-files://account//share/", FilesEndpoint.class).getConfiguration();
+        assertEquals("share", remoteConf.getShare());
+    }
+
+    @Test
+    void shareForValidURIShouldBeExtracted3() {
+        var remoteConf = context.getEndpoint("azure-files://account/share/?", FilesEndpoint.class).getConfiguration();
+        assertEquals("share", remoteConf.getShare());
+    }
+
+    @Test
+    void shareForValidURIShouldBeExtracted4() {
+        var remoteConf = context.getEndpoint("azure-files://account/share/path", FilesEndpoint.class).getConfiguration();
+        assertEquals("share", remoteConf.getShare());
+    }
+
+    @Test
+    void directoryForValidShareURIShouldBeExtracted() {
+        var remoteConf = context.getEndpoint("azure-files://account/share?", FilesEndpoint.class).getConfiguration();
+        assertEquals("/", remoteConf.getDirectory());
+        assertEquals("/", remoteConf.getDirectoryName());
+    }
+
+    @Test
+    void dirForValidShareURIShouldStartWithSlash() {
+        var remoteConf = context.getEndpoint("azure-files://account/share/?", FilesEndpoint.class).getConfiguration();
+        assertEquals("/", remoteConf.getDirectory());
+        assertEquals("/", remoteConf.getDirectoryName());
+    }
+
+    @Test
+    void endpointURIPathWithDoubleSlashShouldThrowRuntimeException() {
+        // it could be rejected or relaxed, we do not mind, rejecting is slightly is slightly cleaner
+        // by observation ResolveEndpointFailedException but let's not rely on too concrete subtype
+        assertThrows(RuntimeException.class,
+                () -> context.getEndpoint("azure-files://account/share//?", FilesEndpoint.class).getConfiguration());
+    }
+
+    @Test
+    void endpointURIPathWithSlashOnlyShouldThrowRuntimeException() {
+        assertThrows(RuntimeException.class,
+                () -> context.getEndpoint("azure-files://account/?", FilesEndpoint.class).getConfiguration());
+    }
+
+    @Test
+    void endpointURIPathWithDoubleSlashOnlyShouldThrowRuntimeException() {
+        assertThrows(RuntimeException.class,
+                () -> context.getEndpoint("azure-files://account//?", FilesEndpoint.class).getConfiguration());
+    }
+
+    @Test
+    void dirForValidDirectoryNameURIShouldBeExtracted() {
+        var remoteConf = context.getEndpoint("azure-files://account/share/dir", FilesEndpoint.class).getConfiguration();
+        assertEquals("/dir", remoteConf.getDirectory());
+        assertEquals("/dir", remoteConf.getDirectoryName());
+    }
+
+    @Test
+    void dirForValidDirectoryNameURIShouldBeExtracted2() {
+        var remoteConf = context.getEndpoint("azure-files://account/share/dir?", FilesEndpoint.class).getConfiguration();
+        ;
+        assertEquals("/dir", remoteConf.getDirectory());
+        assertEquals("/dir", remoteConf.getDirectoryName());
+    }
+
+    @Test
+    void dirForValidDirectoryNameURIShouldBeExtracted3() {
+        var remoteConf = context.getEndpoint("azure-files://account/share/dir/?", FilesEndpoint.class).getConfiguration();
+        ;
+        assertEquals("/dir", remoteConf.getDirectory());
+        assertEquals("/dir", remoteConf.getDirectoryName());
+    }
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesEndpointTests.java b/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesEndpointTests.java
new file mode 100644
index 00000000000..46d1bae7516
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesEndpointTests.java
@@ -0,0 +1,27 @@
+package org.apache.camel.component.file.azure;
+
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class FilesEndpointTests extends CamelTestSupport {
+
+    @Test
+    void sasTokenForCopyPastedURIShouldBePreserved() {
+        var plainToken
+                = "sv=2022-11-02&ss=f&srt=sco&sp=rwdlc&se=2023-05-28T22:50:04Z&st=2023-05-24T14:50:04Z&spr=https&sig=gj%2BUKSiCWSHmcubvGhyJhatkP8hkbXkrmV%2B%2BZme%2BCxI%3D";
+        // context while resolving endpoint calls SAS setters with decoded values
+        // by observation Camel decoded sig=gj UKSiCWSHmcubvGhyJhatkP8hkbXkrmV  Zme CxI=
+        // using URISupport sig=gj+UKSiCWSHmcubvGhyJhatkP8hkbXkrmV++Zme+CxI=
+        //  leads to "Signature size is invalid" response from server
+        // likely need to post-process replacing + by %2B 
+        // Camel also sorted params before calling setters
+        var endpoint = context.getEndpoint(
+                "azure-files://account/share?" + plainToken, FilesEndpoint.class);
+        assertEquals(
+                plainToken,
+                endpoint.getToken().toURIQuery());
+    }
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesPathTests.java b/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesPathTests.java
new file mode 100644
index 00000000000..a063652604b
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesPathTests.java
@@ -0,0 +1,36 @@
+package org.apache.camel.component.file.azure;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+@SuppressWarnings("static-method")
+public class FilesPathTests {
+
+    @Test
+    void splitAbsolutePreservingRootShouldReturnRootAndSteps() {
+        assertArrayEquals(new String[] { "/", "1", "2" }, FilesPath.splitToSteps("/1/2", true));
+    }
+
+    @Test
+    void splitAbsoluteWithoutPreservingRootShouldReturnStepsOnly() {
+        assertArrayEquals(new String[] { "1", "2" }, FilesPath.splitToSteps("/1/2", false));
+    }
+
+    @Test
+    void splitRelativePreservingRootShouldReturnStepsOnly() {
+        assertArrayEquals(new String[] { "1", "2" }, FilesPath.splitToSteps("1/2", true));
+    }
+
+    @Test
+    void splitRootPreservingRootShouldReturnRoot() {
+        assertArrayEquals(new String[] { "/" }, FilesPath.splitToSteps("/", true));
+    }
+
+    @Test
+    void splitWithoutSeparatorShouldReturnInput() {
+        // by observation, Camel devs were uncertain what is returned ...
+        assertArrayEquals(new String[] { "a path" }, FilesPath.split("a path"));
+    }
+
+}
diff --git a/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesURIStringsTests.java b/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesURIStringsTests.java
new file mode 100644
index 00000000000..bb7e7c0c940
--- /dev/null
+++ b/components/camel-azure/camel-azure-files/src/test/java/org/apache/camel/component/file/azure/FilesURIStringsTests.java
@@ -0,0 +1,22 @@
+package org.apache.camel.component.file.azure;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@SuppressWarnings("static-method")
+public class FilesURIStringsTests {
+
+    @Test
+    void encodeTokenValueShouldEncodeBase64PlusSlashAndPadding() throws Exception {
+        // e.g. for the sig base64 param on SAS token the encoding style must encode '+', '/', '='
+        assertEquals("%2B%2Fa%3D", FilesURIStrings.encodeTokenValue("+/a="));
+    }
+
+    @Test
+    void encodeTokenValueShouldPreserveTimeSeparator() throws Exception {
+        // e.g. for the se param on SAS token the encoding style must preserve ':'
+        assertEquals("11:55:01", FilesURIStrings.encodeTokenValue("11:55:01"));
+    }
+
+}
diff --git a/parent/pom.xml b/parent/pom.xml
index ee7cc003f79..7244cce1a5b 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -851,6 +851,11 @@
                 <artifactId>camel-azure-eventhubs</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-azure-files</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>org.apache.camel</groupId>
                 <artifactId>camel-azure-key-vault</artifactId>