You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by dm...@apache.org on 2019/01/22 16:37:46 UTC

[camel] branch master updated (723870b -> b870eeb)

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

dmvolod pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git.


    from 723870b  camel3 - controlbus component should use public camel-api
     new ba75f65  [CAMEL-13026] Add 'CsvMarshallerFactory' and corresponding tests
     new f0a0d1b  [CAMEL-13026] Using 'ObjectHelper.notNull' as suggested
     new a32d44e  Adapt documentation and add possibility to set the marshaller factory in 'camel-core'
     new b870eeb  CAMEL-13026: Polished Closes #2705

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../camel/model/dataformat/CsvDataFormat.java      |  25 ++++
 .../camel-csv/src/main/docs/csv-dataformat.adoc    |   4 +-
 .../apache/camel/dataformat/csv/CsvDataFormat.java |  25 +++-
 .../apache/camel/dataformat/csv/CsvMarshaller.java |  24 +++-
 ...ordConverter.java => CsvMarshallerFactory.java} |  29 ++--
 .../camel/dataformat/csv/CsvMarshalHeaderTest.java | 109 +++++++++++++++
 ...MarshalHeaderWithCustomMarshallFactoryTest.java | 154 +++++++++++++++++++++
 .../modules/ROOT/pages/csv-dataformat.adoc         |   4 +-
 .../csv/springboot/CsvDataFormatConfiguration.java |  12 ++
 9 files changed, 366 insertions(+), 20 deletions(-)
 copy components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/{CsvRecordConverter.java => CsvMarshallerFactory.java} (55%)
 create mode 100644 components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderTest.java
 create mode 100644 components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderWithCustomMarshallFactoryTest.java


[camel] 03/04: Adapt documentation and add possibility to set the marshaller factory in 'camel-core'

Posted by dm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dmvolod pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git

commit a32d44eaae3a908306dc6a4d2cdf64744b5b440a
Author: Christian Ribeaud <ch...@novartis.com>
AuthorDate: Mon Jan 21 10:13:30 2019 +0100

    Adapt documentation and add possibility to set the marshaller factory in 'camel-core'
---
 .../camel/model/dataformat/CsvDataFormat.java      | 24 ++++++++++++++++++++++
 .../camel-csv/src/main/docs/csv-dataformat.adoc    |  4 +++-
 .../modules/ROOT/pages/csv-dataformat.adoc         |  2 ++
 .../csv/springboot/CsvDataFormatConfiguration.java | 12 +++++++++++
 4 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java b/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java
index ccc10ea..36e7406 100644
--- a/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java
+++ b/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java
@@ -85,6 +85,8 @@ public class CsvDataFormat extends DataFormatDefinition {
     private Boolean trim;
     @XmlAttribute
     private Boolean trailingDelimiter;
+    @XmlAttribute
+    private String marshallerFactoryRef;
 
     // Unmarshall options
     @XmlAttribute
@@ -197,6 +199,10 @@ public class CsvDataFormat extends DataFormatDefinition {
             Object recordConverter = CamelContextHelper.mandatoryLookup(camelContext, recordConverterRef);
             setProperty(camelContext, dataFormat, "recordConverter", recordConverter);
         }
+        if (ObjectHelper.isNotEmpty(marshallerFactoryRef)) {
+            Object marshallerFactory = CamelContextHelper.mandatoryLookup(camelContext, marshallerFactoryRef.trim());
+            setProperty(camelContext, dataFormat, "marshallerFactory", marshallerFactory);
+        }
     }
 
     private static Character singleChar(String value, String attributeName) {
@@ -206,6 +212,24 @@ public class CsvDataFormat extends DataFormatDefinition {
         return value.charAt(0);
     }
 
+    /**
+     * Sets the <code>CsvMarshallerFactory</code> reference.
+     *
+     * @param marshallerFactoryRef the <code>CsvMarshallerFactory</code> reference. Could be <code>null</code>.
+     */
+    public void setMarshallerFactoryRef(String marshallerFactoryRef) {
+        this.marshallerFactoryRef = marshallerFactoryRef;
+    }
+
+    /**
+     * Returns the <code>CsvMarshallerFactory</code> reference.
+     *
+     * @return the <code>CsvMarshallerFactory</code> or <code>null</code> if none has been specified.
+     */
+    public String getMarshallerFactoryRef() {
+        return marshallerFactoryRef;
+    }
+
     public String getFormatRef() {
         return formatRef;
     }
diff --git a/components/camel-csv/src/main/docs/csv-dataformat.adoc b/components/camel-csv/src/main/docs/csv-dataformat.adoc
index 7979999..3a1253d 100644
--- a/components/camel-csv/src/main/docs/csv-dataformat.adoc
+++ b/components/camel-csv/src/main/docs/csv-dataformat.adoc
@@ -12,7 +12,7 @@ exported/imported by Excel.
 ### Options
 
 // dataformat options: START
-The CSV dataformat supports 28 options, which are listed below.
+The CSV dataformat supports 29 options, which are listed below.
 
 
 
@@ -42,6 +42,7 @@ The CSV dataformat supports 28 options, which are listed below.
 | ignoreHeaderCase | false | Boolean | Sets whether or not to ignore case when accessing header names.
 | trim | false | Boolean | Sets whether or not to trim leading and trailing blanks.
 | trailingDelimiter | false | Boolean | Sets whether or not to add a trailing delimiter.
+| marshallerFactoryRef |  | String | Sets the `CsvMarshallerFactory` reference.
 | lazyLoad | false | Boolean | Whether the unmarshalling should produce an iterator that reads the lines on the fly or if all the lines must be read at one.
 | useMaps | false | Boolean | Whether the unmarshalling should produce maps (HashMap)for the lines values instead of lists. It requires to have header (either defined or collected).
 | useOrderedMaps | false | Boolean | Whether the unmarshalling should produce ordered maps (LinkedHashMap) for the lines values instead of lists. It requires to have header (either defined or collected).
@@ -89,6 +90,7 @@ The component supports 29 options, which are listed below.
 | *camel.dataformat.csv.trim* | Sets whether or not to trim leading and trailing blanks. | false | Boolean
 | *camel.dataformat.csv.use-maps* | Whether the unmarshalling should produce maps (HashMap)for the lines values instead of lists. It requires to have header (either defined or collected). | false | Boolean
 | *camel.dataformat.csv.use-ordered-maps* | Whether the unmarshalling should produce ordered maps (LinkedHashMap) for the lines values instead of lists. It requires to have header (either defined or collected). | false | Boolean
+| *camel.dataformat.csv.marshaller-factory-ref* | Refers to a custom `CsvMarshallerFactory` to lookup from the registry to use. |  | String
 |===
 // spring-boot-auto-configure options: END
 ND
diff --git a/docs/components/modules/ROOT/pages/csv-dataformat.adoc b/docs/components/modules/ROOT/pages/csv-dataformat.adoc
index 7979999..0f8157d 100644
--- a/docs/components/modules/ROOT/pages/csv-dataformat.adoc
+++ b/docs/components/modules/ROOT/pages/csv-dataformat.adoc
@@ -47,6 +47,7 @@ The CSV dataformat supports 28 options, which are listed below.
 | useOrderedMaps | false | Boolean | Whether the unmarshalling should produce ordered maps (LinkedHashMap) for the lines values instead of lists. It requires to have header (either defined or collected).
 | recordConverterRef |  | String | Refers to a custom CsvRecordConverter to lookup from the registry to use.
 | contentTypeHeader | false | Boolean | Whether the data format should set the Content-Type header with the type from the data format if the data format is capable of doing so. For example application/xml for data formats marshalling to XML, or application/json for data formats marshalling to JSon etc.
+| marshallerFactory | | String | Refers to a custom `CsvMarshallerFactory` to lookup from the registry to use.
 |===
 // dataformat options: END
 // spring-boot-auto-configure options: START
@@ -89,6 +90,7 @@ The component supports 29 options, which are listed below.
 | *camel.dataformat.csv.trim* | Sets whether or not to trim leading and trailing blanks. | false | Boolean
 | *camel.dataformat.csv.use-maps* | Whether the unmarshalling should produce maps (HashMap)for the lines values instead of lists. It requires to have header (either defined or collected). | false | Boolean
 | *camel.dataformat.csv.use-ordered-maps* | Whether the unmarshalling should produce ordered maps (LinkedHashMap) for the lines values instead of lists. It requires to have header (either defined or collected). | false | Boolean
+| *camel.dataformat.csv.marshaller-factory-ref* | Refers to a custom `CsvMarshallerFactory` to lookup from the registry to use. |  | String
 |===
 // spring-boot-auto-configure options: END
 ND
diff --git a/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java b/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java
index b2fc5b5..f8d6bce 100644
--- a/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java
+++ b/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java
@@ -132,6 +132,10 @@ public class CsvDataFormatConfiguration
      */
     private Boolean trailingDelimiter = false;
     /**
+     * Sets the CsvMarshallerFactory reference.
+     */
+    private String marshallerFactoryRef;
+    /**
      * Whether the unmarshalling should produce an iterator that reads the lines
      * on the fly or if all the lines must be read at one.
      */
@@ -344,6 +348,14 @@ public class CsvDataFormatConfiguration
         this.trailingDelimiter = trailingDelimiter;
     }
 
+    public String getMarshallerFactoryRef() {
+        return marshallerFactoryRef;
+    }
+
+    public void setMarshallerFactoryRef(String marshallerFactoryRef) {
+        this.marshallerFactoryRef = marshallerFactoryRef;
+    }
+
     public Boolean getLazyLoad() {
         return lazyLoad;
     }


[camel] 04/04: CAMEL-13026: Polished Closes #2705

Posted by dm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dmvolod pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git

commit b870eeb55036d220d460db5cf21c123787973da6
Author: Dmitry Volodin <dm...@gmail.com>
AuthorDate: Tue Jan 22 19:32:30 2019 +0300

    CAMEL-13026: Polished
    Closes #2705
---
 .../main/java/org/apache/camel/model/dataformat/CsvDataFormat.java | 7 ++++---
 components/camel-csv/src/main/docs/csv-dataformat.adoc             | 2 +-
 .../main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java   | 1 +
 .../csv/CsvMarshalHeaderWithCustomMarshallFactoryTest.java         | 1 +
 docs/components/modules/ROOT/pages/csv-dataformat.adoc             | 4 ++--
 5 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java b/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java
index 36e7406..81c2c82 100644
--- a/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java
+++ b/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java
@@ -85,7 +85,7 @@ public class CsvDataFormat extends DataFormatDefinition {
     private Boolean trim;
     @XmlAttribute
     private Boolean trailingDelimiter;
-    @XmlAttribute
+    @XmlAttribute @Metadata(label = "advanced")
     private String marshallerFactoryRef;
 
     // Unmarshall options
@@ -213,9 +213,10 @@ public class CsvDataFormat extends DataFormatDefinition {
     }
 
     /**
-     * Sets the <code>CsvMarshallerFactory</code> reference.
+     * Sets the implementation of the CsvMarshallerFactory interface which is able to customize marshalling/unmarshalling 
+     * behavior by extending CsvMarshaller or creating it from scratch.
      *
-     * @param marshallerFactoryRef the <code>CsvMarshallerFactory</code> reference. Could be <code>null</code>.
+     * @param marshallerFactoryRef the <code>CsvMarshallerFactory</code> reference.
      */
     public void setMarshallerFactoryRef(String marshallerFactoryRef) {
         this.marshallerFactoryRef = marshallerFactoryRef;
diff --git a/components/camel-csv/src/main/docs/csv-dataformat.adoc b/components/camel-csv/src/main/docs/csv-dataformat.adoc
index 3a1253d..8b4255f 100644
--- a/components/camel-csv/src/main/docs/csv-dataformat.adoc
+++ b/components/camel-csv/src/main/docs/csv-dataformat.adoc
@@ -42,7 +42,7 @@ The CSV dataformat supports 29 options, which are listed below.
 | ignoreHeaderCase | false | Boolean | Sets whether or not to ignore case when accessing header names.
 | trim | false | Boolean | Sets whether or not to trim leading and trailing blanks.
 | trailingDelimiter | false | Boolean | Sets whether or not to add a trailing delimiter.
-| marshallerFactoryRef |  | String | Sets the `CsvMarshallerFactory` reference.
+| marshallerFactoryRef |  | String | Sets the implementation of the CsvMarshallerFactory interface which is able to customize marshalling/unmarshalling behavior by extending CsvMarshaller or creating it from scratch.
 | lazyLoad | false | Boolean | Whether the unmarshalling should produce an iterator that reads the lines on the fly or if all the lines must be read at one.
 | useMaps | false | Boolean | Whether the unmarshalling should produce maps (HashMap)for the lines values instead of lists. It requires to have header (either defined or collected).
 | useOrderedMaps | false | Boolean | Whether the unmarshalling should produce ordered maps (LinkedHashMap) for the lines values instead of lists. It requires to have header (either defined or collected).
diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java
index d2fee61..1ad903f 100644
--- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java
+++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java
@@ -74,6 +74,7 @@ public abstract class CsvMarshaller {
      * @throws NoTypeConversionAvailableException if the body cannot be converted
      * @throws IOException                        if we cannot write into the given stream
      */
+    @SuppressWarnings("rawtypes")
     public void marshal(Exchange exchange, Object object, OutputStream outputStream) throws NoTypeConversionAvailableException, IOException {
         CSVPrinter printer = createPrinter(exchange, outputStream);
         try {
diff --git a/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderWithCustomMarshallFactoryTest.java b/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderWithCustomMarshallFactoryTest.java
index 0916bc4..f46921f 100644
--- a/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderWithCustomMarshallFactoryTest.java
+++ b/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderWithCustomMarshallFactoryTest.java
@@ -127,6 +127,7 @@ public class CsvMarshalHeaderWithCustomMarshallFactoryTest extends CamelTestSupp
             }
         }
 
+        @SuppressWarnings("unchecked")
         public void marshal(Exchange exchange, Object object, OutputStream outputStream) throws IOException {
             Iterator<Map<String, String>> it = (Iterator<Map<String, String>>) ObjectHelper.createIterator(object);
             synchronized (printer) {
diff --git a/docs/components/modules/ROOT/pages/csv-dataformat.adoc b/docs/components/modules/ROOT/pages/csv-dataformat.adoc
index 0f8157d..8b4255f 100644
--- a/docs/components/modules/ROOT/pages/csv-dataformat.adoc
+++ b/docs/components/modules/ROOT/pages/csv-dataformat.adoc
@@ -12,7 +12,7 @@ exported/imported by Excel.
 ### Options
 
 // dataformat options: START
-The CSV dataformat supports 28 options, which are listed below.
+The CSV dataformat supports 29 options, which are listed below.
 
 
 
@@ -42,12 +42,12 @@ The CSV dataformat supports 28 options, which are listed below.
 | ignoreHeaderCase | false | Boolean | Sets whether or not to ignore case when accessing header names.
 | trim | false | Boolean | Sets whether or not to trim leading and trailing blanks.
 | trailingDelimiter | false | Boolean | Sets whether or not to add a trailing delimiter.
+| marshallerFactoryRef |  | String | Sets the implementation of the CsvMarshallerFactory interface which is able to customize marshalling/unmarshalling behavior by extending CsvMarshaller or creating it from scratch.
 | lazyLoad | false | Boolean | Whether the unmarshalling should produce an iterator that reads the lines on the fly or if all the lines must be read at one.
 | useMaps | false | Boolean | Whether the unmarshalling should produce maps (HashMap)for the lines values instead of lists. It requires to have header (either defined or collected).
 | useOrderedMaps | false | Boolean | Whether the unmarshalling should produce ordered maps (LinkedHashMap) for the lines values instead of lists. It requires to have header (either defined or collected).
 | recordConverterRef |  | String | Refers to a custom CsvRecordConverter to lookup from the registry to use.
 | contentTypeHeader | false | Boolean | Whether the data format should set the Content-Type header with the type from the data format if the data format is capable of doing so. For example application/xml for data formats marshalling to XML, or application/json for data formats marshalling to JSon etc.
-| marshallerFactory | | String | Refers to a custom `CsvMarshallerFactory` to lookup from the registry to use.
 |===
 // dataformat options: END
 // spring-boot-auto-configure options: START


[camel] 02/04: [CAMEL-13026] Using 'ObjectHelper.notNull' as suggested

Posted by dm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dmvolod pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git

commit f0a0d1b79524c998818cde9179b4caa506d2bf77
Author: Christian Ribeaud <ch...@novartis.com>
AuthorDate: Thu Jan 10 10:17:56 2019 +0100

    [CAMEL-13026] Using 'ObjectHelper.notNull' as suggested
---
 .../src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java  | 4 ++++
 .../java/org/apache/camel/dataformat/csv/CsvMarshallerFactory.java    | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java
index 648cb4a..d2fee61 100644
--- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java
+++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java
@@ -51,6 +51,8 @@ public abstract class CsvMarshaller {
      * @return New instance
      */
     public static CsvMarshaller create(CSVFormat format, CsvDataFormat dataFormat) {
+        org.apache.camel.util.ObjectHelper.notNull(format, "CSV format");
+        org.apache.camel.util.ObjectHelper.notNull(dataFormat, "CSV data format");
         // If we don't want the header record, clear it
         if (format.getSkipHeaderRecord()) {
             format = format.withHeader((String[]) null);
@@ -93,6 +95,8 @@ public abstract class CsvMarshaller {
      * @return a new {@link CSVPrinter}. Never <code>null</code>.
      */
     protected CSVPrinter createPrinter(Exchange exchange, OutputStream outputStream) throws IOException {
+        org.apache.camel.util.ObjectHelper.notNull(exchange, "Exchange");
+        org.apache.camel.util.ObjectHelper.notNull(outputStream, "Output stream");
         return new CSVPrinter(new OutputStreamWriter(outputStream, ExchangeHelper.getCharsetName(exchange)), format);
     }
 
diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshallerFactory.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshallerFactory.java
index e88db04..c206164 100644
--- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshallerFactory.java
+++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshallerFactory.java
@@ -35,7 +35,7 @@ public interface CsvMarshallerFactory {
      *
      * @param format     the <b>CSV</b> format. Can NOT be <code>null</code>.
      * @param dataFormat the <b>CSV</b> data format. Can NOT be <code>null</code>.
-     * @return a new {@link CsvMarshaller}.
+     * @return a new {@link CsvMarshaller}. Never <code>null</code>.
      */
     CsvMarshaller create(CSVFormat format, CsvDataFormat dataFormat);
 }


[camel] 01/04: [CAMEL-13026] Add 'CsvMarshallerFactory' and corresponding tests

Posted by dm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dmvolod pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git

commit ba75f65a66c10f05a860620c00531c7f1080f132
Author: Christian Ribeaud <ch...@novartis.com>
AuthorDate: Wed Jan 9 22:08:07 2019 +0100

    [CAMEL-13026] Add 'CsvMarshallerFactory' and corresponding tests
---
 .../apache/camel/dataformat/csv/CsvDataFormat.java |  25 +++-
 .../apache/camel/dataformat/csv/CsvMarshaller.java |  19 ++-
 .../camel/dataformat/csv/CsvMarshallerFactory.java |  41 ++++++
 .../camel/dataformat/csv/CsvMarshalHeaderTest.java | 109 +++++++++++++++
 ...MarshalHeaderWithCustomMarshallFactoryTest.java | 153 +++++++++++++++++++++
 5 files changed, 342 insertions(+), 5 deletions(-)

diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java
index e9bee40..db19351 100644
--- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java
+++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java
@@ -69,6 +69,8 @@ public class CsvDataFormat extends ServiceSupport implements DataFormat, DataFor
     private boolean useOrderedMaps;
     private CsvRecordConverter<?> recordConverter;
 
+    private CsvMarshallerFactory marshallerFactory = CsvMarshallerFactory.DEFAULT;
+
     private volatile CsvMarshaller marshaller;
     private volatile CsvUnmarshaller unmarshaller;
 
@@ -94,7 +96,7 @@ public class CsvDataFormat extends ServiceSupport implements DataFormat, DataFor
 
     @Override
     protected void doStart() throws Exception {
-        marshaller = CsvMarshaller.create(getActiveFormat(), this);
+        marshaller = marshallerFactory.create(getActiveFormat(), this);
         unmarshaller = CsvUnmarshaller.create(getActiveFormat(), this);
     }
 
@@ -208,6 +210,27 @@ public class CsvDataFormat extends ServiceSupport implements DataFormat, DataFor
     }
 
     /**
+     * Sets the {@link CsvMarshaller} factory.
+     * If {@code null}, then {@link CsvMarshallerFactory#DEFAULT} is used instead.
+     *
+     * @param marshallerFactory
+     * @return Current {@code CsvDataFormat}, fluent API
+     */
+    public CsvDataFormat setMarshallerFactory(CsvMarshallerFactory marshallerFactory) {
+        this.marshallerFactory = (marshallerFactory == null) ? CsvMarshallerFactory.DEFAULT : marshallerFactory;
+        return this;
+    }
+
+    /**
+     * Returns the used {@link CsvMarshallerFactory}.
+     *
+     * @return never {@code null}.
+     */
+    public CsvMarshallerFactory getMarshallerFactory() {
+        return marshallerFactory;
+    }
+
+    /**
      * Sets the CSV format by name before applying any changes.
      *
      * @param name CSV format name
diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java
index 5c5ad70..648cb4a 100644
--- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java
+++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshaller.java
@@ -28,18 +28,18 @@ import java.util.Map;
 import org.apache.camel.Exchange;
 import org.apache.camel.NoTypeConversionAvailableException;
 import org.apache.camel.support.ExchangeHelper;
-import org.apache.camel.util.IOHelper;
 import org.apache.camel.support.ObjectHelper;
+import org.apache.camel.util.IOHelper;
 import org.apache.commons.csv.CSVFormat;
 import org.apache.commons.csv.CSVPrinter;
 
 /**
  * This class marshal data into a CSV format.
  */
-abstract class CsvMarshaller {
+public abstract class CsvMarshaller {
     private final CSVFormat format;
 
-    private CsvMarshaller(CSVFormat format) {
+    protected CsvMarshaller(CSVFormat format) {
         this.format = format;
     }
 
@@ -73,7 +73,7 @@ abstract class CsvMarshaller {
      * @throws IOException                        if we cannot write into the given stream
      */
     public void marshal(Exchange exchange, Object object, OutputStream outputStream) throws NoTypeConversionAvailableException, IOException {
-        CSVPrinter printer = new CSVPrinter(new OutputStreamWriter(outputStream, ExchangeHelper.getCharsetName(exchange)), format);
+        CSVPrinter printer = createPrinter(exchange, outputStream);
         try {
             Iterator it = ObjectHelper.createIterator(object);
             while (it.hasNext()) {
@@ -85,6 +85,17 @@ abstract class CsvMarshaller {
         }
     }
 
+    /**
+     * Creates and returns a {@link CSVPrinter}.
+     *
+     * @param exchange     Exchange (used for access to type conversion). Could NOT be <code>null</code>.
+     * @param outputStream Output stream of the CSV. Could NOT be <code>null</code>.
+     * @return a new {@link CSVPrinter}. Never <code>null</code>.
+     */
+    protected CSVPrinter createPrinter(Exchange exchange, OutputStream outputStream) throws IOException {
+        return new CSVPrinter(new OutputStreamWriter(outputStream, ExchangeHelper.getCharsetName(exchange)), format);
+    }
+
     private Iterable<?> getRecordValues(Exchange exchange, Object data) throws NoTypeConversionAvailableException {
         // each row must be a map or list based
         Map<?, ?> map = exchange.getContext().getTypeConverter().tryConvertTo(Map.class, exchange, data);
diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshallerFactory.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshallerFactory.java
new file mode 100644
index 0000000..e88db04
--- /dev/null
+++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvMarshallerFactory.java
@@ -0,0 +1,41 @@
+/**
+ * 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.dataformat.csv;
+
+import org.apache.commons.csv.CSVFormat;
+
+/**
+ * A {@link CsvMarshaller} factory.
+ */
+public interface CsvMarshallerFactory {
+
+    CsvMarshallerFactory DEFAULT = new CsvMarshallerFactory() {
+        @Override
+        public CsvMarshaller create(CSVFormat format, CsvDataFormat dataFormat) {
+            return CsvMarshaller.create(format, dataFormat);
+        }
+    };
+
+    /**
+     * Creates and returns a new {@link CsvMarshaller}.
+     *
+     * @param format     the <b>CSV</b> format. Can NOT be <code>null</code>.
+     * @param dataFormat the <b>CSV</b> data format. Can NOT be <code>null</code>.
+     * @return a new {@link CsvMarshaller}.
+     */
+    CsvMarshaller create(CSVFormat format, CsvDataFormat dataFormat);
+}
diff --git a/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderTest.java b/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderTest.java
new file mode 100644
index 0000000..af7c689
--- /dev/null
+++ b/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderTest.java
@@ -0,0 +1,109 @@
+/**
+ * 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.dataformat.csv;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * <b>Camel</b> based test cases for {@link org.apache.camel.dataformat.csv.CsvDataFormat}.
+ */
+public class CsvMarshalHeaderTest extends CamelTestSupport {
+
+    @Rule
+    public TemporaryFolder folder = new TemporaryFolder();
+
+    @Produce(uri = "direct:start")
+    private ProducerTemplate producerTemplate;
+
+    private File outputFile;
+
+    @Override
+    protected void doPreSetup() throws Exception {
+        outputFile = new File(folder.newFolder(), "output.csv");
+    }
+
+    @Test
+    public void testSendBody() throws IOException {
+        Map<String, String> body = new LinkedHashMap<>();
+        body.put("first_name", "John");
+        body.put("last_name", "Doe");
+        String fileName = outputFile.getName();
+        assertEquals("output.csv", fileName);
+        producerTemplate.sendBodyAndHeader(body, Exchange.FILE_NAME, fileName);
+        body = new LinkedHashMap<>();
+        body.put("first_name", "Max");
+        body.put("last_name", "Mustermann");
+        producerTemplate.sendBodyAndHeader(body, Exchange.FILE_NAME, fileName);
+        List<String> lines = Files.lines(Paths.get(outputFile.toURI()))
+                .filter(l -> l.trim().length() > 0).collect(Collectors.toList());
+        // We got twice the headers... :(
+        assertEquals(4, lines.size());
+    }
+
+    @Test
+    public void testSendBodyWithList() throws IOException {
+        List<List<String>> body = Collections.singletonList(Arrays.asList("John", "Doe"));
+        String fileName = outputFile.getName();
+        assertEquals("output.csv", fileName);
+        producerTemplate.sendBodyAndHeader(body, Exchange.FILE_NAME, fileName);
+        body = Collections.singletonList(Arrays.asList("Max", "Mustermann"));
+        producerTemplate.sendBodyAndHeader(body, Exchange.FILE_NAME, fileName);
+        List<String> lines = Files.lines(Paths.get(outputFile.toURI()))
+                .filter(l -> l.trim().length() > 0).collect(Collectors.toList());
+        // We got twice the headers... :(
+        assertEquals(4, lines.size());
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                String uri = String.format("file:%s?charset=utf-8&fileExist=Append", outputFile.getParentFile().getAbsolutePath());
+                from("direct:start").marshal(createCsvDataFormat()).to(uri);
+            }
+        };
+    }
+
+    private static CsvDataFormat createCsvDataFormat() {
+        CsvDataFormat dataFormat = new CsvDataFormat();
+        dataFormat.setDelimiter('\t');
+        dataFormat.setTrim(true);
+        dataFormat.setIgnoreSurroundingSpaces(true);
+        dataFormat.setHeader((String[]) Arrays.asList("first_name", "last_name").toArray());
+        return dataFormat;
+    }
+}
\ No newline at end of file
diff --git a/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderWithCustomMarshallFactoryTest.java b/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderWithCustomMarshallFactoryTest.java
new file mode 100644
index 0000000..0916bc4
--- /dev/null
+++ b/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvMarshalHeaderWithCustomMarshallFactoryTest.java
@@ -0,0 +1,153 @@
+/**
+ * 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.dataformat.csv;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.support.ObjectHelper;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVPrinter;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * <b>Camel</b> based test cases for {@link CsvDataFormat}.
+ */
+public class CsvMarshalHeaderWithCustomMarshallFactoryTest extends CamelTestSupport {
+
+    @Rule
+    public TemporaryFolder folder = new TemporaryFolder();
+
+    @Produce(uri = "direct:start")
+    private ProducerTemplate producerTemplate;
+
+    private File outputFile;
+
+    @Override
+    protected void doPreSetup() throws Exception {
+        outputFile = new File(folder.newFolder(), "output.csv");
+    }
+
+    @Test
+    public void testSendBody() throws IOException {
+        Map<String, String> body = new LinkedHashMap<>();
+        body.put("first_name", "John");
+        body.put("last_name", "Doe");
+        String fileName = outputFile.getName();
+        assertEquals("output.csv", fileName);
+        producerTemplate.sendBodyAndHeader(body, Exchange.FILE_NAME, fileName);
+        body = new LinkedHashMap<>();
+        body.put("first_name", "Max");
+        body.put("last_name", "Mustermann");
+        producerTemplate.sendBodyAndHeader(body, Exchange.FILE_NAME, fileName);
+        List<String> lines = Files.lines(Paths.get(outputFile.toURI()))
+                .filter(l -> l.trim().length() > 0).collect(Collectors.toList());
+        assertEquals(3, lines.size());
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                String uri = String.format("file:%s?charset=utf-8&fileExist=Append", outputFile.getParentFile().getAbsolutePath());
+                from("direct:start").marshal(createCsvDataFormat()).to(uri);
+            }
+        };
+    }
+
+    private static CsvDataFormat createCsvDataFormat() {
+        CsvDataFormat dataFormat = new CsvDataFormat();
+        dataFormat.setDelimiter('\t');
+        dataFormat.setTrim(true);
+        dataFormat.setIgnoreSurroundingSpaces(true);
+        dataFormat.setHeader((String[]) Arrays.asList("first_name", "last_name").toArray());
+        dataFormat.setMarshallerFactory(new CsvMarshallerFactory() {
+
+            @Override
+            public CsvMarshaller create(CSVFormat format, CsvDataFormat dataFormat) {
+                return new SinglePrinterCsvMarshaller(format);
+            }
+        });
+        return dataFormat;
+    }
+
+    //
+    // Helper classes
+    //
+
+    private static final class SinglePrinterCsvMarshaller extends CsvMarshaller {
+
+        private final CSVPrinter printer;
+
+        private SinglePrinterCsvMarshaller(CSVFormat format) {
+            super(format);
+            printer = createPrinter(format);
+        }
+
+        private static CSVPrinter createPrinter(CSVFormat format) {
+            try {
+                // Headers and header comments are written out in the constructor already.
+                return format.print(new StringBuilder());
+            } catch (IOException e) {
+                throw RuntimeCamelException.wrapRuntimeCamelException(e);
+            }
+        }
+
+        public void marshal(Exchange exchange, Object object, OutputStream outputStream) throws IOException {
+            Iterator<Map<String, String>> it = (Iterator<Map<String, String>>) ObjectHelper.createIterator(object);
+            synchronized (printer) {
+                while (it.hasNext()) {
+                    printer.printRecord(getMapRecordValues(it.next()));
+                }
+                // Access the 'Appendable'
+                StringBuilder stringBuilder = (StringBuilder) printer.getOut();
+                outputStream.write(stringBuilder.toString().getBytes());
+                // Reset the 'Appendable' for the next exchange.
+                stringBuilder.setLength(0);
+            }
+        }
+
+        @Override
+        protected Iterable<?> getMapRecordValues(Map<?, ?> map) {
+            List<String> result = new ArrayList<>(map.size());
+            for (Object key : map.keySet()) {
+                result.add((String) map.get(key));
+            }
+            return result;
+        }
+    }
+}
\ No newline at end of file