Add example to demo file, bindy & sftp extensions
diff --git a/docs/modules/ROOT/attachments/examples.json b/docs/modules/ROOT/attachments/examples.json
index 439e2cc..86d7a7e 100644
--- a/docs/modules/ROOT/attachments/examples.json
+++ b/docs/modules/ROOT/attachments/examples.json
@@ -10,6 +10,11 @@
     "link": "https://github.com/apache/camel-quarkus-examples/tree/master/timer-log-main"
   },
   {
+    "title": "File consumer with Bindy \u0026 FTP",
+    "description": "Shows how to consume CSV files, marshal \u0026 unmarshal the data and send it onwards via FTP",
+    "link": "https://github.com/apache/camel-quarkus-examples/tree/master/file-bindy-ftp"
+  },
+  {
     "title": "HTTP with vanilla JAX-RS or with Camel `platform-http` component",
     "description": "Shows how to create HTTP endpoints using either the RESTEasy",
     "link": "https://github.com/apache/camel-quarkus-examples/tree/master/http-log"
diff --git a/file-bindy-ftp/README.adoc b/file-bindy-ftp/README.adoc
new file mode 100644
index 0000000..e334e22
--- /dev/null
+++ b/file-bindy-ftp/README.adoc
@@ -0,0 +1,167 @@
+= File consumer with Bindy & FTP: A Camel Quarkus example
+:cq-example-description: An example that shows how to consume CSV files, marshal & unmarshal the data and send it onwards via FTP
+
+{cq-description}
+
+TIP: Check the https://camel.apache.org/camel-quarkus/latest/first-steps.html[Camel Quarkus User guide] for prerequisites
+and other general information.
+
+
+== Prerequisites
+
+The example application requires a running FTP server. For simplicity, you can start one with Docker.
+
+[source,shell]
+----
+docker run -ti --rm -p 2222:2222 \
+    -e PASSWORD_ACCESS=true \
+    -e USER_NAME=ftpuser \
+    -e USER_PASSWORD=ftppassword \
+    -e DOCKER_MODS=linuxserver/mods:openssh-server-openssh-client \
+    linuxserver/openssh-server
+----
+
+If you prefer to use a different server, then the properties prefixed with `ftp.` in `src/main/resources/application.properties` can be adjusted for this purpose.
+
+== Start in Development mode
+
+Ensure the aforementioned FTP server is running and do the following to run the application in development mode.
+
+[source,shell]
+----
+$ mvn clean compile quarkus:dev
+----
+
+The above command compiles the project, starts the application and lets the Quarkus tooling watch for changes in your
+workspace. Any modifications in your project will automatically take effect in the running application.
+
+TIP: Please refer to the Development mode section of
+https://camel.apache.org/camel-quarkus/latest/first-steps.html#_development_mode[Camel Quarkus User guide] for more details.
+
+You should start to see some log messages appearing on the console.
+
+Every 10 seconds the timer component triggers the generation of some random 'books' data and creates a CSV file in a temporary directory with 100 entries.
+
+[source,shell]
+----
+[route1] (Camel (camel-1) thread #3 - timer://generateBooks) Generating randomized books CSV data
+----
+
+Next the CSV file is read by a file consumer and Bindy is used to marshal the individual data rows into `Book` objects.
+
+[source,shell]
+----
+[route2] (Camel (camel-1) thread #1 - file:///tmp/books) Reading books CSV data from 89A0EE24CB03A69-0000000000000000
+----
+
+Next the collection of `Book` objects is split into individual items and is aggregated based on the `genre` property.
+
+[source,shell]
+----
+[route3] (Camel (camel-1) thread #0 - AggregateTimeoutChecker) Processed 34 books for genre 'Action'
+[route3] (Camel (camel-1) thread #0 - AggregateTimeoutChecker) Processed 31 books for genre 'Crime'
+[route3] (Camel (camel-1) thread #0 - AggregateTimeoutChecker) Processed 35 books for genre 'Horror'
+----
+
+Finally, the aggregated book collections are unmarshalled back to CSV format and uploaded to the test FTP server.
+
+[source,shell]
+----
+[route4] (Camel (camel-1) thread #2 - seda://processed) Uploaded books-Action-89A0EE24CB03A69-0000000000000069.csv
+[route4] (Camel (camel-1) thread #2 - seda://processed) Uploaded books-Crime-89A0EE24CB03A69-0000000000000069.csv
+[route4] (Camel (camel-1) thread #2 - seda://processed) Uploaded books-Horror-89A0EE24CB03A69-0000000000000069.csv
+----
+
+=== Package and run the application
+
+Once you are done with developing you may want to package and run the application.
+
+TIP: Find more details about the JVM mode and Native mode in the Package and run section of
+https://camel.apache.org/camel-quarkus/latest/first-steps.html#_package_and_run_the_application[Camel Quarkus User guide]
+
+==== JVM mode
+
+[source,shell]
+----
+$ mvn clean package
+$ java -jar target/quarkus-app/quarkus-run.jar
+----
+
+==== Native mode
+
+IMPORTANT: Native mode requires having GraalVM and other tools installed. Please check the Prerequisites section
+of https://camel.apache.org/camel-quarkus/latest/first-steps.html#_prerequisites[Camel Quarkus User guide].
+
+To prepare a native executable using GraalVM, run the following command:
+
+[source,shell]
+----
+$ mvn clean package -Pnative
+$ ./target/*-runner
+----
+
+==== Deploying to Kubernetes
+
+You can build a container image for the application like this. Refer to the https://quarkus.io/guides/deploying-to-kubernetes[Quarkus Kubernetes guide] for options around customizing image names, registries etc.
+
+[source,shell]
+----
+$ mvn clean package -DskipTests -Dquarkus.container-image.build=true
+----
+
+If you are using a local development cluster like Kind or k3s, you can use host the container image on your local host. Or, with minikube, use the Docker daemon from the cluster virtual machine `eval $(minikube docker-env)`. Otherwise, you'll need to push the image to a registry of your choosing.
+
+Next apply the necessary resources to the cluster.
+
+[source,shell]
+----
+$ kubectl apply -f target/kubernetes/kubernetes.yml
+
+secret/ftp-credentials created
+service/camel-quarkus-examples-file-bindy-ftp created
+service/ftp-server created
+deployment.apps/camel-quarkus-examples-file-bindy-ftp created
+deployment.apps/ssh-server-deployment created
+----
+
+TIP: You can build & deploy in one single step by doing `mvn clean package -DskipTests -Dquarkus.kubernetes.deploy=true`
+
+Check pods are running.
+
+[source,shell]
+----
+$ kubectl get pods
+
+NAME                                                     READY     STATUS    RESTARTS   AGE
+camel-quarkus-examples-file-bindy-ftp-5d48f4d85c-sjl8k   1/1       Running   0          21s
+ssh-server-deployment-5c667bccfc-52xfz                   1/1       Running   0          21s
+----
+
+Tail the application logs.
+
+[source,shell]
+----
+$ kubectl logs -f camel-quarkus-examples-file-bindy-ftp-5d48f4d85c-sjl8k
+----
+
+To clean up do.
+
+[source,shell]
+----
+$ kubectl delete all -l app.kubernetes.io/name=camel-quarkus-examples-file-bindy-ftp
+----
+
+==== Deploying to OpenShift
+
+To start a Source To Image (S2I) build and deploy the application.
+
+[source,shell]
+----
+$ mvn clean package -DskipTests -Dquarkus.kubernetes.deploy=true -Dopenshift
+----
+
+You can check the pod status and tail logs using the commands mentioned above in the Kubernetes section. Use the `oc` binary instead of `kubectl` if preferred.
+
+== Feedback
+
+Please report bugs and propose improvements via https://github.com/apache/camel-quarkus/issues[GitHub issues of Camel Quarkus] project.
diff --git a/file-bindy-ftp/pom.xml b/file-bindy-ftp/pom.xml
new file mode 100644
index 0000000..cd0fcb6
--- /dev/null
+++ b/file-bindy-ftp/pom.xml
@@ -0,0 +1,331 @@
+<?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/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-quarkus-examples-file-bindy-ftp</artifactId>
+    <groupId>org.apache.camel.quarkus.examples</groupId>
+    <version>1.6.0-SNAPSHOT</version>
+
+    <name>Camel Quarkus :: Examples :: File Bindy FTP</name>
+    <description>Camel Quarkus Example :: File Bindy FTP</description>
+
+    <properties>
+        <camel-quarkus.version>1.7.0</camel-quarkus.version>
+        <quarkus.version>1.12.0.Final</quarkus.version>
+
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.testTarget>${maven.compiler.target}</maven.compiler.testTarget>
+        <maven.compiler.testSource>${maven.compiler.source}</maven.compiler.testSource>
+
+        <formatter-maven-plugin.version>2.11.0</formatter-maven-plugin.version>
+        <impsort-maven-plugin.version>1.3.2</impsort-maven-plugin.version>
+        <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
+        <maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
+        <maven-resources-plugin.version>3.1.0</maven-resources-plugin.version>
+        <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
+        <mycila-license.version>3.0</mycila-license.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- Import BOM -->
+            <dependency>
+                <groupId>org.apache.camel.quarkus</groupId>
+                <artifactId>camel-quarkus-bom</artifactId>
+                <version>${camel-quarkus.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel.quarkus</groupId>
+                <artifactId>camel-quarkus-bom-test</artifactId>
+                <version>${camel-quarkus.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-bean</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-bindy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-direct</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-file</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-ftp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-log</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-microprofile-health</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-seda</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-timer</artifactId>
+        </dependency>
+
+        <!-- Test -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-integration-testcontainers-support</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+
+                <plugin>
+                    <groupId>net.revelc.code.formatter</groupId>
+                    <artifactId>formatter-maven-plugin</artifactId>
+                    <version>${formatter-maven-plugin.version}</version>
+                    <configuration>
+                        <configFile>${maven.multiModuleProjectDirectory}/eclipse-formatter-config.xml</configFile>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>net.revelc.code</groupId>
+                    <artifactId>impsort-maven-plugin</artifactId>
+                    <version>${impsort-maven-plugin.version}</version>
+                    <configuration>
+                        <groups>java.,javax.,org.w3c.,org.xml.,junit.</groups>
+                        <removeUnused>true</removeUnused>
+                        <staticAfter>true</staticAfter>
+                        <staticGroups>java.,javax.,org.w3c.,org.xml.,junit.</staticGroups>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>${maven-compiler-plugin.version}</version>
+                    <configuration>
+                        <showDeprecation>true</showDeprecation>
+                        <showWarnings>true</showWarnings>
+                        <compilerArgs>
+                            <arg>-Xlint:unchecked</arg>
+                        </compilerArgs>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                    <configuration>
+                        <failIfNoTests>false</failIfNoTests>
+                        <systemProperties>
+                            <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                        </systemProperties>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>io.quarkus</groupId>
+                    <artifactId>quarkus-maven-plugin</artifactId>
+                    <version>${quarkus.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-failsafe-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>${maven-jar-plugin.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>com.mycila</groupId>
+                    <artifactId>license-maven-plugin</artifactId>
+                    <version>${mycila-license.version}</version>
+                    <configuration>
+                        <failIfUnknown>true</failIfUnknown>
+                        <header>${maven.multiModuleProjectDirectory}/header.txt</header>
+                        <excludes>
+                            <exclude>**/*.adoc</exclude>
+                            <exclude>**/*.txt</exclude>
+                            <exclude>**/LICENSE.txt</exclude>
+                            <exclude>**/LICENSE</exclude>
+                            <exclude>**/NOTICE.txt</exclude>
+                            <exclude>**/NOTICE</exclude>
+                            <exclude>**/README</exclude>
+                            <exclude>**/pom.xml.versionsBackup</exclude>
+                        </excludes>
+                        <mapping>
+                            <java>SLASHSTAR_STYLE</java>
+                            <properties>CAMEL_PROPERTIES_STYLE</properties>
+                            <kt>SLASHSTAR_STYLE</kt>
+                        </mapping>
+                        <headerDefinitions>
+                            <headerDefinition>${maven.multiModuleProjectDirectory}/license-properties-headerdefinition.xml</headerDefinition>
+                        </headerDefinitions>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+            <plugin>
+                <groupId>io.quarkus</groupId>
+                <artifactId>quarkus-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>build</id>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>net.revelc.code.formatter</groupId>
+                <artifactId>formatter-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>format</id>
+                        <goals>
+                            <goal>format</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>net.revelc.code</groupId>
+                <artifactId>impsort-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>sort-imports</id>
+                        <goals>
+                            <goal>sort</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <properties>
+                <quarkus.package.type>native</quarkus.package.type>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>kubernetes</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+                <property>
+                    <name>kubernetes</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>io.quarkus</groupId>
+                    <artifactId>quarkus-kubernetes</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>io.quarkus</groupId>
+                    <artifactId>quarkus-container-image-jib</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+        <profile>
+            <id>openshift</id>
+            <activation>
+                <property>
+                    <name>openshift</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>io.quarkus</groupId>
+                    <artifactId>quarkus-openshift</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/file-bindy-ftp/src/main/java/org/apache/camel/example/Book.java b/file-bindy-ftp/src/main/java/org/apache/camel/example/Book.java
new file mode 100644
index 0000000..1e631ff
--- /dev/null
+++ b/file-bindy-ftp/src/main/java/org/apache/camel/example/Book.java
@@ -0,0 +1,87 @@
+/*
+ * 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.example;
+
+import java.util.Objects;
+
+import io.quarkus.runtime.annotations.RegisterForReflection;
+import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
+import org.apache.camel.dataformat.bindy.annotation.DataField;
+
+@RegisterForReflection
+@CsvRecord(separator = ",")
+public class Book {
+
+    @DataField(pos = 1)
+    private int id;
+
+    @DataField(pos = 2)
+    private String author;
+
+    @DataField(pos = 3)
+    private String title;
+
+    @DataField(pos = 4)
+    private String genre;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getAuthor() {
+        return author;
+    }
+
+    public void setAuthor(String author) {
+        this.author = author;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getGenre() {
+        return genre;
+    }
+
+    public void setGenre(String genre) {
+        this.genre = genre;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        Book book = (Book) o;
+        return Objects.equals(author, book.author) && Objects.equals(title, book.title) && Objects.equals(genre, book.genre);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(author, title, genre);
+    }
+}
diff --git a/file-bindy-ftp/src/main/java/org/apache/camel/example/BookGenerator.java b/file-bindy-ftp/src/main/java/org/apache/camel/example/BookGenerator.java
new file mode 100644
index 0000000..74b8cdf
--- /dev/null
+++ b/file-bindy-ftp/src/main/java/org/apache/camel/example/BookGenerator.java
@@ -0,0 +1,86 @@
+/*
+ * 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.example;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Named;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+
+/**
+ * Processor to generate {@Book}s with a random set of data.
+ */
+@ApplicationScoped
+@Named
+public class BookGenerator implements Processor {
+
+    private static final String[] BOOK_GENRES = { "Action", "Crime", "Horror" };
+
+    private static final String[] BOOK_DESCRIPTION = {
+            "Awesome",
+            "Amazing",
+            "Fantastic",
+            "Incredible",
+            "Tremendous",
+    };
+
+    private static final String[] FIRST_NAMES = {
+            "Fyodor",
+            "Jane",
+            "Leo",
+            "Oscar",
+            "William",
+    };
+
+    private static final String[] LAST_NAMES = {
+            "Austen",
+            "Dostoevsky",
+            "Shakespeare",
+            "Tolstoy",
+            "Wilde",
+    };
+
+    @Override
+    public void process(Exchange exchange) throws Exception {
+        Random random = new Random();
+        List<Book> books = new ArrayList<>();
+
+        for (int i = 0; i < 100; i++) {
+            String genre = BOOK_GENRES[random.nextInt(BOOK_GENRES.length)];
+            String description = BOOK_DESCRIPTION[random.nextInt(BOOK_DESCRIPTION.length)];
+            String title = String.format("The %s book of %s #%d", description, genre, i);
+
+            String firstName = FIRST_NAMES[random.nextInt(FIRST_NAMES.length)];
+            String lastName = LAST_NAMES[random.nextInt(LAST_NAMES.length)];
+            String author = String.format("%s %s", firstName, lastName);
+
+            Book book = new Book();
+            book.setId(i);
+            book.setAuthor(author);
+            book.setTitle(title);
+            book.setGenre(genre);
+            books.add(book);
+        }
+
+        exchange.getMessage().setBody(books);
+    }
+}
diff --git a/file-bindy-ftp/src/main/java/org/apache/camel/example/Routes.java b/file-bindy-ftp/src/main/java/org/apache/camel/example/Routes.java
new file mode 100644
index 0000000..48e93e2
--- /dev/null
+++ b/file-bindy-ftp/src/main/java/org/apache/camel/example/Routes.java
@@ -0,0 +1,59 @@
+/*
+ * 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.example;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.dataformat.BindyType;
+import org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy;
+
+public class Routes extends RouteBuilder {
+
+    @Override
+    public void configure() throws Exception {
+        // Generate some book objects with random data
+        from("timer:generateBooks?period={{timer.period}}&delay={{timer.delay}}")
+                .log("Generating randomized books CSV data")
+                .process("bookGenerator")
+                // Marshal each book to CSV format
+                .marshal().bindy(BindyType.Csv, Book.class)
+                // Write CSV data to file
+                .to("file:{{csv.location}}");
+
+        // Consume book CSV files
+        from("file:{{csv.location}}?delay=1000")
+                .log("Reading books CSV data from ${header.CamelFileName}")
+                .unmarshal().bindy(BindyType.Csv, Book.class)
+                .split(body())
+                .to("direct:aggregateBooks");
+
+        // Aggregate books based on their genre
+        from("direct:aggregateBooks")
+                .setHeader("BookGenre", simple("${body.genre}"))
+                .aggregate(simple("${body.genre}"), new GroupedBodyAggregationStrategy()).completionInterval(5000)
+                .log("Processed ${header.CamelAggregatedSize} books for genre '${header.BookGenre}'")
+                .to("seda:processed");
+
+        from("seda:processed")
+                // Marshal books back to CSV format
+                .marshal().bindy(BindyType.Csv, Book.class)
+                .setHeader(Exchange.FILE_NAME, simple("books-${header.BookGenre}-${exchangeId}.csv"))
+                // Send aggregated book genre CSV files to an FTP host
+                .to("sftp://{{ftp.username}}@{{ftp.host}}:{{ftp.port}}/uploads/books?password={{ftp.password}}")
+                .log("Uploaded ${header.CamelFileName}");
+    }
+}
diff --git a/file-bindy-ftp/src/main/kubernetes/kubernetes.yml b/file-bindy-ftp/src/main/kubernetes/kubernetes.yml
new file mode 100644
index 0000000..7d6d3dc
--- /dev/null
+++ b/file-bindy-ftp/src/main/kubernetes/kubernetes.yml
@@ -0,0 +1,83 @@
+#
+# 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.
+#
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: ssh-server-deployment
+  labels:
+    app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+    app.kubernetes.io/version: 1.6.0-SNAPSHOT
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+      app.kubernetes.io/version: 1.6.0-SNAPSHOT
+  template:
+    metadata:
+      labels:
+        app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+        app.kubernetes.io/version: 1.6.0-SNAPSHOT
+    spec:
+      containers:
+        - name: openssh-server
+          # Use a simple SFTP server implementation based on Apache Mina SSHD. Purely for testing only, NOT for production use
+          image: quay.io/jamesnetherton/sftp-server:0.1.0
+          ports:
+            - containerPort: 2222
+          env:
+            - name: FTP_USER
+              valueFrom:
+                secretKeyRef:
+                  name: ftp-credentials
+                  key: FTP_USER
+            - name: FTP_PASSWORD
+              valueFrom:
+                secretKeyRef:
+                  name: ftp-credentials
+                  key: FTP_PASSWORD
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+    app.kubernetes.io/version: 1.6.0-SNAPSHOT
+  name: ftp-server
+spec:
+  ports:
+    - name: sftp
+      port: 2222
+      targetPort: 2222
+  selector:
+    app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+    app.kubernetes.io/version: 1.6.0-SNAPSHOT
+  type: ClusterIP
+---
+apiVersion: v1
+data:
+  FTP_PASSWORD: ZnRwcGFzc3dvcmQ=
+  FTP_USER: ZnRwdXNlcg==
+kind: Secret
+metadata:
+  labels:
+    app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+    app.kubernetes.io/version: 1.6.0-SNAPSHOT
+  name: ftp-credentials
+type: Opaque
+---
diff --git a/file-bindy-ftp/src/main/kubernetes/openshift.yml b/file-bindy-ftp/src/main/kubernetes/openshift.yml
new file mode 100644
index 0000000..7d6d3dc
--- /dev/null
+++ b/file-bindy-ftp/src/main/kubernetes/openshift.yml
@@ -0,0 +1,83 @@
+#
+# 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.
+#
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: ssh-server-deployment
+  labels:
+    app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+    app.kubernetes.io/version: 1.6.0-SNAPSHOT
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+      app.kubernetes.io/version: 1.6.0-SNAPSHOT
+  template:
+    metadata:
+      labels:
+        app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+        app.kubernetes.io/version: 1.6.0-SNAPSHOT
+    spec:
+      containers:
+        - name: openssh-server
+          # Use a simple SFTP server implementation based on Apache Mina SSHD. Purely for testing only, NOT for production use
+          image: quay.io/jamesnetherton/sftp-server:0.1.0
+          ports:
+            - containerPort: 2222
+          env:
+            - name: FTP_USER
+              valueFrom:
+                secretKeyRef:
+                  name: ftp-credentials
+                  key: FTP_USER
+            - name: FTP_PASSWORD
+              valueFrom:
+                secretKeyRef:
+                  name: ftp-credentials
+                  key: FTP_PASSWORD
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+    app.kubernetes.io/version: 1.6.0-SNAPSHOT
+  name: ftp-server
+spec:
+  ports:
+    - name: sftp
+      port: 2222
+      targetPort: 2222
+  selector:
+    app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+    app.kubernetes.io/version: 1.6.0-SNAPSHOT
+  type: ClusterIP
+---
+apiVersion: v1
+data:
+  FTP_PASSWORD: ZnRwcGFzc3dvcmQ=
+  FTP_USER: ZnRwdXNlcg==
+kind: Secret
+metadata:
+  labels:
+    app.kubernetes.io/name: camel-quarkus-examples-file-bindy-ftp
+    app.kubernetes.io/version: 1.6.0-SNAPSHOT
+  name: ftp-credentials
+type: Opaque
+---
diff --git a/file-bindy-ftp/src/main/resources/application.properties b/file-bindy-ftp/src/main/resources/application.properties
new file mode 100644
index 0000000..7776de3
--- /dev/null
+++ b/file-bindy-ftp/src/main/resources/application.properties
@@ -0,0 +1,59 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+quarkus.banner.enabled = false
+
+# Uncomment if your application image is to be pushed to an external registry
+#quarkus.container-image.registry=my.docker-registry.net
+
+# How often should the books CSV be generated
+timer.period = 10000
+timer.delay = 10000
+
+# Location of where to store the book CSV files
+csv.location = {{sys:java.io.tmpdir}}/books
+
+# FTP server location
+ftp.host = {{env:FTP_SERVER_SERVICE_HOST:localhost}}
+ftp.port = {{env:FTP_SERVER_SERVICE_PORT:2222}}
+ftp.username = {{env:FTP_USER:ftpuser}}
+ftp.password = {{env:FTP_PASSWORD:ftppassword}}
+
+# Kubernetes
+
+# Add the FTP server credentials secret to the application Pod
+quarkus.kubernetes.env.secrets=ftp-credentials
+
+quarkus.kubernetes.image-pull-policy=IfNotPresent
+
+# Uncomment to set resource limits
+#quarkus.kubernetes.resources.requests.memory=64Mi
+#quarkus.kubernetes.resources.requests.cpu=250m
+#quarkus.kubernetes.resources.limits.memory=512Mi
+#quarkus.kubernetes.resources.limits.cpu=1000m
+
+# OpenShift
+quarkus.openshift.image-pull-policy=IfNotPresent
+
+# Add the FTP server credentials secret to the application Pod
+quarkus.openshift.env.secrets=ftp-credentials
+
+# Uncomment to set resource limits
+#quarkus.openshift.resources.requests.memory=64Mi
+#quarkus.openshift.resources.requests.cpu=250m
+#quarkus.openshift.resources.limits.memory=512Mi
+#quarkus.openshift.resources.limits.cpu=1000m
diff --git a/file-bindy-ftp/src/test/java/org/apache/camel/example/FileToFtpIT.java b/file-bindy-ftp/src/test/java/org/apache/camel/example/FileToFtpIT.java
new file mode 100644
index 0000000..14eacbd
--- /dev/null
+++ b/file-bindy-ftp/src/test/java/org/apache/camel/example/FileToFtpIT.java
@@ -0,0 +1,24 @@
+/*
+ * 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.example;
+
+import io.quarkus.test.junit.NativeImageTest;
+
+@NativeImageTest
+class FileToFtpIT extends FileToFtpTest {
+
+}
diff --git a/file-bindy-ftp/src/test/java/org/apache/camel/example/FileToFtpTest.java b/file-bindy-ftp/src/test/java/org/apache/camel/example/FileToFtpTest.java
new file mode 100644
index 0000000..cdf8b59
--- /dev/null
+++ b/file-bindy-ftp/src/test/java/org/apache/camel/example/FileToFtpTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.example;
+
+import java.util.concurrent.TimeUnit;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import org.junit.jupiter.api.Test;
+
+import static org.awaitility.Awaitility.await;
+
+@QuarkusTest
+@QuarkusTestResource(FtpTestResource.class)
+public class FileToFtpTest {
+
+    @Test
+    public void testFileToFtp() throws JSchException {
+        JSch jsch = new JSch();
+        jsch.setKnownHosts(System.getProperty("user.home") + "/.ssh/known_hosts");
+
+        Session session = jsch.getSession("ftpuser", System.getProperty("ftp.host"));
+        session.setPort(Integer.parseInt(System.getProperty("ftp.port")));
+        session.setPassword("ftppassword");
+        session.setConfig("StrictHostKeyChecking", "no");
+        session.connect(5000);
+        Channel sftp = null;
+        try {
+            sftp = session.openChannel("sftp");
+            sftp.connect(5000);
+
+            ChannelSftp channelSftp = (ChannelSftp) sftp;
+
+            await().atMost(10L, TimeUnit.SECONDS).pollDelay(500, TimeUnit.MILLISECONDS).until(() -> {
+                try {
+                    return channelSftp.ls("uploads/books").size() >= 3;
+                } catch (Exception e) {
+                    return false;
+                }
+            });
+        } finally {
+            if (sftp != null) {
+                sftp.disconnect();
+            }
+        }
+    }
+}
diff --git a/file-bindy-ftp/src/test/java/org/apache/camel/example/FtpTestResource.java b/file-bindy-ftp/src/test/java/org/apache/camel/example/FtpTestResource.java
new file mode 100644
index 0000000..64584d9
--- /dev/null
+++ b/file-bindy-ftp/src/test/java/org/apache/camel/example/FtpTestResource.java
@@ -0,0 +1,71 @@
+/*
+ * 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.example;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.apache.camel.util.CollectionHelper;
+import org.apache.commons.io.FileUtils;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+public class FtpTestResource implements QuarkusTestResourceLifecycleManager {
+
+    private static final int FTP_PORT = 2222;
+    private static final String SSH_IMAGE = "quay.io/jamesnetherton/sftp-server:0.1.0";
+
+    private GenericContainer container;
+
+    @Override
+    public Map<String, String> start() {
+        Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir"), "books");
+        try {
+            FileUtils.deleteDirectory(tmpDir.toFile());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        try {
+            container = new GenericContainer(SSH_IMAGE)
+                    .withExposedPorts(FTP_PORT)
+                    .withEnv("PASSWORD_ACCESS", "true")
+                    .withEnv("FTP_USER", "ftpuser")
+                    .withEnv("FTP_PASSWORD", "ftppassword")
+                    .waitingFor(Wait.forListeningPort());
+
+            container.start();
+
+            return CollectionHelper.mapOf(
+                    "ftp.host", container.getContainerIpAddress(),
+                    "ftp.port", container.getMappedPort(FTP_PORT).toString(),
+                    "timer.delay", "100");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void stop() {
+        if (container != null) {
+            container.stop();
+        }
+    }
+}