You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@batchee.apache.org by rs...@apache.org on 2015/12/02 15:42:41 UTC
incubator-batchee git commit: BATCHEE-70 added header support for
jsefa
Repository: incubator-batchee
Updated Branches:
refs/heads/master 6cb7b3739 -> 711054266
BATCHEE-70 added header support for jsefa
Project: http://git-wip-us.apache.org/repos/asf/incubator-batchee/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-batchee/commit/71105426
Tree: http://git-wip-us.apache.org/repos/asf/incubator-batchee/tree/71105426
Diff: http://git-wip-us.apache.org/repos/asf/incubator-batchee/diff/71105426
Branch: refs/heads/master
Commit: 711054266b945b38477495dc7361199a87f98323
Parents: 6cb7b37
Author: Reinhard Sandtner <rs...@apache.org>
Authored: Wed Dec 2 15:41:15 2015 +0100
Committer: Reinhard Sandtner <rs...@apache.org>
Committed: Wed Dec 2 15:41:15 2015 +0100
----------------------------------------------------------------------
.../java/org/apache/batchee/jsefa/Header.java | 39 +++
.../apache/batchee/jsefa/JSefaCsvMapping.java | 118 +++++++++
.../apache/batchee/jsefa/JSefaCsvReader.java | 29 ++-
.../apache/batchee/jsefa/JSefaCsvWriter.java | 57 +++++
.../batchee/jsefa/JSefaCsvReaderTest.java | 43 ++++
.../batchee/jsefa/JSefaCsvWriterHeaderTest.java | 249 +++++++++++++++++++
.../batchee/jsefa/JSefaCsvWriterTest.java | 93 +++++++
.../org/apache/batchee/jsefa/bean/Address.java | 62 +++++
.../org/apache/batchee/jsefa/bean/Person.java | 54 ++++
.../batchee/jsefa/bean/PersonWithAddress.java | 42 ++++
.../batchee/jsefa/bean/RecordWithHeader.java | 51 ++++
.../org/apache/batchee/jsefa/util/CsvUtil.java | 115 +++++++++
.../java/org/apache/batchee/jsefa/util/IOs.java | 46 +++-
.../batch-jobs/jsefa-csv-reader-header.xml | 30 +++
...-csv-writer-header-fromFieldsMoreObjects.xml | 29 +++
...sefa-csv-writer-header-fromFieldsOneType.xml | 29 +++
...-writer-header-fromFieldsWithInheritance.xml | 29 +++
...a-csv-writer-header-fromHeaderAnnotation.xml | 29 +++
.../batch-jobs/jsefa-csv-writer-header.xml | 29 +++
.../jsefa-csv-writer-multiOjectTypes.xml | 28 +++
.../jsefa-csv-writer-nestedObjects.xml | 28 +++
21 files changed, 1215 insertions(+), 14 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/Header.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/Header.java b/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/Header.java
new file mode 100644
index 0000000..52bab1d
--- /dev/null
+++ b/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/Header.java
@@ -0,0 +1,39 @@
+/*
+ * 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.batchee.jsefa;
+
+import org.apache.batchee.doc.api.Documentation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to add a custom Header for a {@link net.sf.jsefa.csv.annotation.CsvField}.
+ */
+@Target(value = ElementType.FIELD)
+@Retention(value = RetentionPolicy.RUNTIME)
+@Documentation("This annotation is used to specify a custom header for a annotated @CsvField.")
+public @interface Header {
+
+
+ /**
+ * @return header for the annotated field
+ */
+ String value();
+}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvMapping.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvMapping.java b/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvMapping.java
new file mode 100644
index 0000000..e6d6e22
--- /dev/null
+++ b/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvMapping.java
@@ -0,0 +1,118 @@
+/*
+ * 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.batchee.jsefa;
+
+import net.sf.jsefa.csv.annotation.CsvDataType;
+import net.sf.jsefa.csv.annotation.CsvField;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+public class JSefaCsvMapping {
+
+ /**
+ * storage for CsvHeaders
+ */
+ private final List<String> headers = new ArrayList<String>();
+
+
+ private JSefaCsvMapping(Class<?> type) {
+ calculateHeaders(type);
+ }
+
+
+ public Iterable<String> getHeader() {
+ return headers;
+ }
+
+
+
+ private void calculateHeaders(Class<?> type) {
+
+ SortedMap<Integer, SortedMap<Integer, String>> allHeaders = new TreeMap<Integer, SortedMap<Integer, String>>();
+
+ calculateHeaders(type, allHeaders, 0);
+
+ for (SortedMap<Integer, String> headerMap : allHeaders.values()) {
+
+ for (String header : headerMap.values()) {
+ headers.add(header);
+ }
+ }
+ }
+
+ private void calculateHeaders(Class<?> type, SortedMap<Integer, SortedMap<Integer, String>> allHeaders, int index) {
+
+ if (type.getSuperclass() != Object.class) {
+ // decrement index for inheritance, because index will be the same
+ // but if we have another object within OR the same positions specified in the @CsvField annotation
+ // we override the values
+ calculateHeaders(type.getSuperclass(), allHeaders, index - 1);
+ }
+
+ SortedMap<Integer, String> typeHeaders = new TreeMap<Integer, String>();
+
+ for (Field field : type.getDeclaredFields()) {
+
+ CsvField fieldAnnotation = field.getAnnotation(CsvField.class);
+ if (fieldAnnotation == null) {
+ continue;
+ }
+
+ if (field.getType().getAnnotation(CsvDataType.class) != null) {
+ calculateHeaders(field.getType(), allHeaders, fieldAnnotation.pos());
+ } else {
+
+ String header = null;
+
+ Header headerAnnotation = field.getAnnotation(Header.class);
+ if (headerAnnotation != null) {
+ header = headerAnnotation.value();
+ }
+
+ // use field.getName() as default
+ if (header == null || header.isEmpty()) {
+ header = field.getName();
+ }
+
+ String previousField = typeHeaders.put(fieldAnnotation.pos(), header);
+ if (previousField != null) {
+ throw new IllegalArgumentException(String.format("multiple fields for position %d defined! Fields: %s! Type: %s", fieldAnnotation.pos(),
+ previousField + ", " + field.getName(),
+ type.getName()));
+ }
+ }
+ }
+
+ allHeaders.put(index, typeHeaders);
+ }
+
+
+ public static List<JSefaCsvMapping> forTypes(Class<?>... types) {
+ List<JSefaCsvMapping> mappings = new ArrayList<JSefaCsvMapping>(types.length);
+
+ for (Class<?> type : types) {
+ mappings.add(new JSefaCsvMapping(type));
+ }
+
+ return mappings;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvReader.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvReader.java b/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvReader.java
index 6939571..4068e19 100644
--- a/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvReader.java
+++ b/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvReader.java
@@ -17,7 +17,9 @@
package org.apache.batchee.jsefa;
import net.sf.jsefa.Deserializer;
+import net.sf.jsefa.common.lowlevel.filter.HeaderAndFooterFilter;
import net.sf.jsefa.csv.CsvIOFactory;
+import net.sf.jsefa.csv.config.CsvConfiguration;
import org.apache.batchee.doc.api.Documentation;
import javax.batch.api.BatchProperty;
@@ -80,14 +82,33 @@ public class JSefaCsvReader extends JSefaReader {
@Documentation("should deliimter be used after last field")
private String useDelimiterAfterLastField;
+ @Inject
+ @BatchProperty
+ @Documentation("should the header be ignored")
+ private Boolean ignoreHeader;
+
+ @Inject
+ @BatchProperty()
+ @Documentation("number of header lines")
+ private Integer headerSize;
+
+
@Override
protected Deserializer initDeserializer() throws Exception {
- return CsvIOFactory.createFactory(
- JsefaConfigurations.newCsvConfiguration(
+ CsvConfiguration config = JsefaConfigurations.newCsvConfiguration(
defaultNoValueString, defaultQuoteMode, fieldDelimiter, lineBreak, quoteCharacter,
quoteCharacterEscapeMode, useDelimiterAfterLastField, validationMode, validationProvider,
lineFilter, lowLevelConfiguration, lineFilterLimit, objectAccessorProvider,
- specialRecordDelimiter, simpleTypeProvider, typeMappingRegistry),
- JsefaConfigurations.createObjectTypes(objectTypes)).createDeserializer();
+ specialRecordDelimiter, simpleTypeProvider, typeMappingRegistry);
+
+ if (config.getLineFilter() == null &&
+ Boolean.TRUE.equals(ignoreHeader) &&
+ headerSize != null && headerSize > 0) {
+
+ config.setLineFilter(new HeaderAndFooterFilter(headerSize, false, false));
+ }
+
+ return CsvIOFactory.createFactory(config, JsefaConfigurations.createObjectTypes(objectTypes))
+ .createDeserializer();
}
}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvWriter.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvWriter.java b/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvWriter.java
index 502dd5f..9874d9b 100644
--- a/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvWriter.java
+++ b/extensions/jsefa/src/main/java/org/apache/batchee/jsefa/JSefaCsvWriter.java
@@ -18,10 +18,13 @@ package org.apache.batchee.jsefa;
import net.sf.jsefa.Serializer;
import net.sf.jsefa.csv.CsvIOFactory;
+import net.sf.jsefa.csv.CsvSerializer;
+import net.sf.jsefa.csv.lowlevel.config.CsvLowLevelConfiguration;
import org.apache.batchee.doc.api.Documentation;
import javax.batch.api.BatchProperty;
import javax.inject.Inject;
+import java.io.Serializable;
@Documentation("Writes a CSV file using JSefa.")
public class JSefaCsvWriter extends JSefaWriter {
@@ -80,6 +83,17 @@ public class JSefaCsvWriter extends JSefaWriter {
@Documentation("should deliimter be used after last field")
private String useDelimiterAfterLastField;
+ @Inject
+ @BatchProperty
+ @Documentation("the header for the file")
+ private String header;
+
+ @Inject
+ @BatchProperty
+ @Documentation("Should the header be calculated from @Header or fieldnames if @Header is not present on fields annotated with @CsvField. " +
+ "This property will be ignored if the header property is set.")
+ private Boolean writeHeader;
+
@Override
protected Serializer createSerializer() throws Exception {
return CsvIOFactory.createFactory(
@@ -91,4 +105,47 @@ public class JSefaCsvWriter extends JSefaWriter {
JsefaConfigurations.createObjectTypes(objectTypes))
.createSerializer();
}
+
+ @Override
+ public void open(Serializable checkpoint) throws Exception {
+ super.open(checkpoint);
+
+ // write header only on first run
+ if (checkpoint != null) {
+ return;
+ }
+
+ char delimiter;
+ if (fieldDelimiter != null && !fieldDelimiter.isEmpty()) {
+ delimiter = fieldDelimiter.charAt(0);
+ } else {
+ delimiter = CsvLowLevelConfiguration.Defaults.DEFAULT_FIELD_DELIMITER;
+ }
+
+ //X TODO align behavoir of DefaultBAtchArtifactFactory and CDIBatchArtifactFactory?
+ //X DefaultBatchArtifactFactory only resolves properties which are set, while
+ //X CDIBatchArtifactFactory resolves every field...
+ if (header == null && writeHeader != null && writeHeader) {
+ Class<?>[] classes = JsefaConfigurations.createObjectTypes(objectTypes);
+
+ StringBuilder headerBuilder = new StringBuilder(50);
+ for (JSefaCsvMapping mapping : JSefaCsvMapping.forTypes(classes)) {
+
+ for (String headerPart : mapping.getHeader()) {
+
+ if (headerBuilder.length() > 0) {
+ headerBuilder.append(delimiter);
+ }
+
+ headerBuilder.append(headerPart);
+ }
+ }
+
+ header = headerBuilder.toString();
+ }
+
+ if (header != null && !header.trim().isEmpty()) {
+ ((CsvSerializer) serializer).getLowLevelSerializer().writeLine(header.trim());
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvReaderTest.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvReaderTest.java b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvReaderTest.java
index bed7596..5d1eede 100644
--- a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvReaderTest.java
+++ b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvReaderTest.java
@@ -16,9 +16,14 @@
*/
package org.apache.batchee.jsefa;
+import org.apache.batchee.extras.typed.TypedItemProcessor;
+import org.apache.batchee.jsefa.bean.Address;
+import org.apache.batchee.jsefa.bean.PersonWithAddress;
import org.apache.batchee.jsefa.bean.Record;
+import org.apache.batchee.jsefa.util.CsvUtil;
import org.apache.batchee.jsefa.util.IOs;
import org.apache.batchee.util.Batches;
+import org.testng.Assert;
import org.testng.annotations.Test;
import javax.batch.api.chunk.ItemProcessor;
@@ -51,6 +56,33 @@ public class JSefaCsvReaderTest {
}
}
+
+ @Test
+ public void testReadWithHeader() {
+ String path = "target/work/JsefaCsvReaderWithHeader.csv";
+
+ Properties properties = new Properties();
+ properties.setProperty("input", path);
+
+ StringBuilder csvBuilder = new StringBuilder(200);
+ for (int i = 0; i < 10; i++) {
+ csvBuilder.append(IOs.LINE_SEPARATOR)
+ .append(CsvUtil.toCsv(new PersonWithAddress("firstName_" + i,
+ "lastName_" + i,
+ new Address("street_" + i,
+ "zip_" + i,
+ "city_" + i))));
+ }
+
+ IOs.write(path, csvBuilder.toString());
+
+ JobOperator operator = BatchRuntime.getJobOperator();
+ Batches.waitForEnd(operator, operator.start("jsefa-csv-reader-header", properties));
+
+ Assert.assertEquals(Storage.ITEMS.size(), 10);
+
+ }
+
public static class StoreItems implements ItemProcessor {
public static final List<Record> ITEMS = new ArrayList<Record>(3);
@@ -60,4 +92,15 @@ public class JSefaCsvReaderTest {
return item;
}
}
+
+ public static class Storage extends TypedItemProcessor<PersonWithAddress, PersonWithAddress> {
+
+ static final List<PersonWithAddress> ITEMS = new ArrayList<PersonWithAddress>(10);
+
+ @Override
+ protected PersonWithAddress doProcessItem(PersonWithAddress item) {
+ ITEMS.add(item);
+ return item;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvWriterHeaderTest.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvWriterHeaderTest.java b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvWriterHeaderTest.java
new file mode 100644
index 0000000..baaf3d8
--- /dev/null
+++ b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvWriterHeaderTest.java
@@ -0,0 +1,249 @@
+/*
+ * 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.batchee.jsefa;
+
+import org.apache.batchee.extras.typed.NoStateTypedItemReader;
+import org.apache.batchee.jsefa.bean.Address;
+import org.apache.batchee.jsefa.bean.Person;
+import org.apache.batchee.jsefa.bean.PersonWithAddress;
+import org.apache.batchee.jsefa.bean.Record;
+import org.apache.batchee.jsefa.bean.RecordWithHeader;
+import org.apache.batchee.jsefa.util.CsvUtil;
+import org.apache.batchee.jsefa.util.IOs;
+import org.apache.batchee.util.Batches;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import javax.batch.api.chunk.AbstractItemReader;
+import javax.batch.operations.JobOperator;
+import javax.batch.runtime.BatchRuntime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+public class JSefaCsvWriterHeaderTest {
+
+
+ /**
+ * Tests a specified header via xml-file
+ *
+ * {@code <property name="header" value="VALUE1;VALUE2" />}
+ */
+ @Test
+ public void testWriteWithPropertyHeader() {
+
+ startBatchAndAssertResult("target/work/JSefaCsvWriterHeaderTest.csv",
+ "jsefa-csv-writer-header",
+ "VALUE1;VALUE2",
+ RecordReader.STORAGE);
+ }
+
+ @Test
+ public void testWriteHeaderFromFieldsOneType() {
+
+ startBatchAndAssertResult("target/work/JSefaCsvWriterHeaderFromFieldsOnTypeTest.csv",
+ "jsefa-csv-writer-header-fromFieldsOneType",
+ "firstName;lastName",
+ SimpleOneTypeReader.STORAGE);
+ }
+
+ @Test
+ public void testWriteHeaderFromFieldsWithInheritance() {
+
+ startBatchAndAssertResult("target/work/JSefaCsvWriterHeaderWithInheritance.csv",
+ "jsefa-csv-writer-header-fromFieldsWithInheritance",
+ "firstName;lastName;street;zip;city",
+ InheritanceReader.STORAGE);
+ }
+
+ @Test
+ public void testWriteHeaderFromFieldsMoreObjectTypes() {
+
+ startBatchAndAssertResult("target/work/JSefaCsvWriterHeaderMoreObjects.csv",
+ "jsefa-csv-writer-header-fromFieldsMoreObjects",
+ "firstName;lastName;street;zip;city",
+ MoreObjectReader.STORAGE);
+ }
+
+ @Test
+ public void testWriteHeaderFromHeaderAnnotation() {
+
+ startBatchAndAssertResult("target/work/JsefaCsvWriterHeaderFromAnnotation.csv",
+ "jsefa-csv-writer-header-fromHeaderAnnotation",
+ "VALUE_HEADER;ANOTHER_VALUE_HEADER",
+ HeaderAnnotationReader.STORAGE);
+ }
+
+
+ private void startBatchAndAssertResult(String path,
+ String jobName,
+ String header,
+ List storage) {
+
+ Properties properties = new Properties();
+ properties.setProperty("output", path);
+
+ JobOperator jobOperator = BatchRuntime.getJobOperator();
+ Batches.waitForEnd(jobOperator, jobOperator.start(jobName, properties));
+
+ List<String> lines = IOs.getLines(path);
+
+ int expectedSize = header == null ? storage.size() : storage.size() + 1;
+ Assert.assertEquals(lines.size(), expectedSize);
+
+ for (int i = 0; i < lines.size(); i++) {
+
+ String line = lines.get(i);
+ String expected;
+
+ if (header == null) {
+ expected = CsvUtil.toCsv(storage.get(i));
+ }
+ else if (i == 0) {
+ expected = header;
+ }
+ else {
+ expected = CsvUtil.toCsv(storage.get(i - 1));
+ }
+
+ Assert.assertEquals(line, expected);
+ }
+ }
+
+
+ public static class RecordReader extends NoStateTypedItemReader<Record> {
+
+ static final List<Record> STORAGE = new ArrayList<Record>(5);
+
+
+ private int count;
+
+ @Override
+ protected Record doRead() {
+
+ if (count++ < 5) {
+ Record record = new Record("Entry_" + count + "_Value_");
+
+ STORAGE.add(record);
+ return record;
+ }
+
+ return null;
+ }
+ }
+
+ public static class SimpleOneTypeReader extends AbstractItemReader {
+
+ static final List<Person> STORAGE = new ArrayList<Person>();
+
+
+ private int count;
+
+
+ @Override
+ public Object readItem() throws Exception {
+
+ if (count++ < 10) {
+
+ Person person = new Person("firstName_" + count, "lastName_" + count);
+
+ STORAGE.add(person);
+ return person;
+ }
+
+ return null;
+ }
+ }
+
+ public static class InheritanceReader extends AbstractItemReader {
+
+ static final List<PersonWithAddress> STORAGE = new ArrayList<PersonWithAddress>();
+
+
+ private int count;
+
+
+ @Override
+ public Object readItem() throws Exception {
+
+ if (count++ < 10) {
+
+ PersonWithAddress person = new PersonWithAddress("firstName_" + count,
+ "lastName_" + count,
+ new Address("street_" + count,
+ "zip_" + count,
+ "city_" + count));
+
+ STORAGE.add(person);
+ return person;
+ }
+
+ return null;
+ }
+ }
+
+ public static class MoreObjectReader extends AbstractItemReader {
+
+ static final List<Object> STORAGE = new ArrayList<Object>();
+
+
+ private int count;
+
+
+ @Override
+ public Object readItem() throws Exception {
+
+ Object item;
+ if (count++ == 10) {
+ item = null;
+ }
+ else if (count % 2 == 0) {
+ item = new Person("firstName_" + count, "lastName_" + count);
+ }
+ else {
+ item = new Address("street_" + count, "zip_" + count, "city_" + count);
+ }
+
+ if (item != null) {
+ STORAGE.add(item);
+ }
+
+ return item;
+ }
+ }
+
+ public static class HeaderAnnotationReader extends AbstractItemReader {
+
+ static final List<RecordWithHeader> STORAGE = new ArrayList<RecordWithHeader>(5);
+
+
+ private int count;
+
+ @Override
+ public Object readItem() throws Exception {
+
+ if (count++ < 5) {
+
+ RecordWithHeader record = new RecordWithHeader(count);
+ STORAGE.add(record);
+ return record;
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvWriterTest.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvWriterTest.java b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvWriterTest.java
index f8f4ede..d9ab821 100644
--- a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvWriterTest.java
+++ b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/JSefaCsvWriterTest.java
@@ -16,17 +16,25 @@
*/
package org.apache.batchee.jsefa;
+import org.apache.batchee.jsefa.bean.Address;
+import org.apache.batchee.jsefa.bean.Person;
+import org.apache.batchee.jsefa.bean.PersonWithAddress;
import org.apache.batchee.jsefa.bean.Record;
+import org.apache.batchee.jsefa.util.CsvUtil;
import org.apache.batchee.jsefa.util.IOs;
import org.apache.batchee.util.Batches;
import org.testng.annotations.Test;
+import javax.batch.api.chunk.AbstractItemReader;
import javax.batch.api.chunk.ItemReader;
import javax.batch.operations.JobOperator;
import javax.batch.runtime.BatchRuntime;
import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Properties;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
public class JSefaCsvWriterTest {
@@ -45,6 +53,43 @@ public class JSefaCsvWriterTest {
assertTrue(output.contains("v2_1;v2_2"));
}
+ @Test
+ public void testWriteMultipleObjectTypes() {
+
+ startBatchAndAssertResult("target/work/JSefaCsvWriterMulitObjectTypes.csv",
+ "jsefa-csv-writer-multiOjectTypes",
+ new Properties(),
+ MultipleObjectTypesReader.STORAGE);
+ }
+
+ @Test
+ public void testWriteNestedObjects() {
+
+ startBatchAndAssertResult("target/work/JSefaCsvWriterNestedObjects.csv",
+ "jsefa-csv-writer-nestedObjects",
+ new Properties(),
+ NestedObjectReader.STORAGE);
+ }
+
+
+ private void startBatchAndAssertResult(String path,
+ String jobName,
+ Properties jobProperties,
+ List storage) {
+
+ jobProperties.setProperty("output", path);
+
+ JobOperator jobOperator = BatchRuntime.getJobOperator();
+ Batches.waitForEnd(jobOperator, jobOperator.start(jobName, jobProperties));
+
+ List<String> batchOutput = IOs.getLines(path);
+ assertEquals(batchOutput.size(), storage.size());
+
+ for (int i = 0; i < batchOutput.size(); i++) {
+ assertEquals(batchOutput.get(i), CsvUtil.toCsv(storage.get(i)));
+ }
+ }
+
public static class TwoItemsReader implements ItemReader {
private int count = 0;
@@ -71,4 +116,52 @@ public class JSefaCsvWriterTest {
return null;
}
}
+
+ public static class MultipleObjectTypesReader extends AbstractItemReader {
+ static final List<Object> STORAGE = new ArrayList<Object>();
+
+ private int count;
+
+ @Override
+ public Object readItem() throws Exception {
+
+ if (count++ == 10) {
+ return null;
+ }
+
+ Object item;
+ if (count % 2 == 0) {
+ item = new Person("firstName_" + count, "lastName_" + count);
+ }
+ else {
+ item = new Address("street_" + count, "zip_" + count, "city_" + count);
+ }
+
+ STORAGE.add(item);
+ return item;
+ }
+ }
+
+ public static class NestedObjectReader extends AbstractItemReader {
+ static final List<Person> STORAGE = new ArrayList<Person>(10);
+
+ private int count;
+
+ @Override
+ public Object readItem() throws Exception {
+
+ if (count++ == 10) {
+ return null;
+ }
+
+ PersonWithAddress item = new PersonWithAddress("firstName_" + count,
+ "lastName_" + count,
+ new Address("street_" + count,
+ "zip_" + count,
+ "city_" + count));
+ STORAGE.add(item);
+
+ return item;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/Address.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/Address.java b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/Address.java
new file mode 100644
index 0000000..14f332d
--- /dev/null
+++ b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/Address.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.batchee.jsefa.bean;
+
+import net.sf.jsefa.csv.annotation.CsvDataType;
+import net.sf.jsefa.csv.annotation.CsvField;
+
+@CsvDataType(defaultPrefix = "ADDRESS")
+public class Address {
+
+ @CsvField(pos = 1)
+ private String street;
+
+ @CsvField(pos = 2)
+ private String zip;
+
+ @CsvField(pos = 3)
+ private String city;
+
+
+ private Address() {
+ // no-op -> needed for jsefa
+ }
+
+ public Address(String street, String zip, String city) {
+ this.street = street;
+ this.zip = zip;
+ this.city = city;
+ }
+
+
+ public String getStreet() {
+ return street;
+ }
+
+ public String getZip() {
+ return zip;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+
+ public String toCsvString(String seperator) {
+ return street + seperator + zip + seperator + city;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/Person.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/Person.java b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/Person.java
new file mode 100644
index 0000000..5b152eb
--- /dev/null
+++ b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/Person.java
@@ -0,0 +1,54 @@
+/*
+ * 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.batchee.jsefa.bean;
+
+import net.sf.jsefa.csv.annotation.CsvDataType;
+import net.sf.jsefa.csv.annotation.CsvField;
+
+@CsvDataType(defaultPrefix = "PERSON")
+public class Person {
+
+ @CsvField(pos = 1)
+ private String firstName;
+
+ @CsvField(pos = 2)
+ private String lastName;
+
+
+ private Person() {
+ // no-op -> needed for jsefa
+ }
+
+ public Person(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+
+ public String toCsvString(String seperator) {
+ return firstName + seperator + lastName;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/PersonWithAddress.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/PersonWithAddress.java b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/PersonWithAddress.java
new file mode 100644
index 0000000..f29d35e
--- /dev/null
+++ b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/PersonWithAddress.java
@@ -0,0 +1,42 @@
+/*
+ * 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.batchee.jsefa.bean;
+
+import net.sf.jsefa.csv.annotation.CsvDataType;
+import net.sf.jsefa.csv.annotation.CsvField;
+
+@CsvDataType
+public class PersonWithAddress extends Person {
+
+ @CsvField(pos = 3)
+ private Address address;
+
+
+ private PersonWithAddress() {
+ this(null, null, null);
+ }
+
+ public PersonWithAddress(String firstName, String lastName, Address address) {
+ super(firstName, lastName);
+ this.address = address;
+ }
+
+
+ public Address getAddress() {
+ return address;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/RecordWithHeader.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/RecordWithHeader.java b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/RecordWithHeader.java
new file mode 100644
index 0000000..224f8d1
--- /dev/null
+++ b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/bean/RecordWithHeader.java
@@ -0,0 +1,51 @@
+/*
+ * 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.batchee.jsefa.bean;
+
+import net.sf.jsefa.csv.annotation.CsvDataType;
+import net.sf.jsefa.csv.annotation.CsvField;
+import org.apache.batchee.jsefa.Header;
+
+@CsvDataType
+public class RecordWithHeader {
+
+ @Header("VALUE_HEADER")
+ @CsvField(pos = 1)
+ private String value;
+
+ @Header("ANOTHER_VALUE_HEADER")
+ @CsvField(pos = 2)
+ private String anotherValue;
+
+
+ private RecordWithHeader() {
+ }
+
+ public RecordWithHeader(int count) {
+ this.value = "value_" + count;
+ this.anotherValue = "anotherValue_" + count;
+ }
+
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getAnotherValue() {
+ return anotherValue;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/util/CsvUtil.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/util/CsvUtil.java b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/util/CsvUtil.java
new file mode 100644
index 0000000..5afe1ff
--- /dev/null
+++ b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/util/CsvUtil.java
@@ -0,0 +1,115 @@
+/*
+ * 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.batchee.jsefa.util;
+
+import net.sf.jsefa.csv.annotation.CsvDataType;
+import net.sf.jsefa.csv.annotation.CsvField;
+import org.testng.Assert;
+
+import java.lang.reflect.Field;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+public final class CsvUtil {
+
+ private CsvUtil() {
+ // no instantiation
+ }
+
+
+ /**
+ * Converts the given Object annotated with {@link CsvDataType} to a csv-{@link String}
+ *
+ * @param object to convert
+ *
+ * @return the csv-{@link String}
+ */
+ public static String toCsv(Object object) {
+ return toCsv(object, false);
+ }
+
+ /**
+ * Converts the given Object annotated with {@link CsvDataType} to a csv-{@link String}
+ *
+ * @param object to convert
+ * @param ignorePrefix if {@code true} the {@link CsvDataType#defaultPrefix()} will be ignored for conversion
+ *
+ * @return the csv-{@link String}
+ */
+ public static String toCsv(Object object, boolean ignorePrefix) {
+ StringBuilder csvBuilder = new StringBuilder(30);
+
+ Class<?> clazz = object.getClass();
+ CsvDataType dataType = clazz.getAnnotation(CsvDataType.class);
+
+ // dataType can never be null, otherwise it can't be serialized with jsefa
+ if (!ignorePrefix && !"".equals(dataType.defaultPrefix())) {
+ csvBuilder.append(dataType.defaultPrefix());
+ }
+
+ for (String value : getValues(object, object.getClass(), new TreeMap<Integer, String>()).values()) {
+ if (csvBuilder.length() > 0) {
+ csvBuilder.append(";");
+ }
+
+ csvBuilder.append(value);
+ }
+
+ return csvBuilder.toString();
+
+ }
+
+
+ private static SortedMap<Integer, String> getValues(Object object, Class<?> clazz, SortedMap<Integer, String> values) {
+
+ if (clazz.getSuperclass() != Object.class) {
+ values = getValues(object, clazz.getSuperclass(), values);
+ }
+
+ Field[] fields = clazz.getDeclaredFields();
+ for (Field field : fields) {
+ CsvField annotation = field.getAnnotation(CsvField.class);
+ if (annotation == null) {
+ continue;
+ }
+
+ try {
+ field.setAccessible(true);
+ Object value = field.get(object);
+
+ if (value != null) {
+
+ String valuetoPut;
+ if (value.getClass().getAnnotation(CsvDataType.class) != null) {
+ valuetoPut = toCsv(value, true);
+ }
+ else {
+ valuetoPut = value.toString();
+ }
+
+ values.put(annotation.pos(), valuetoPut);
+ }
+ }
+ catch (IllegalAccessException e) {
+ Assert.fail("Cannot read from field " + field.getName(), e);
+ }
+ }
+
+ return values;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/util/IOs.java
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/util/IOs.java b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/util/IOs.java
index b5e7c5a..5e5cd49 100644
--- a/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/util/IOs.java
+++ b/extensions/jsefa/src/test/java/org/apache/batchee/jsefa/util/IOs.java
@@ -21,8 +21,14 @@ import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
public class IOs {
+
+ public static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+
public static void write(final String path, final String content) {
final File file = new File(path);
if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
@@ -40,20 +46,40 @@ public class IOs {
public static String slurp(final String path) {
final StringBuilder builder = new StringBuilder();
+
+ for (String line : getLines(path)) {
+ builder.append(line).append(LINE_SEPARATOR);
+ }
+
+ return builder.toString();
+ }
+
+ public static List<String> getLines(final String path) {
+ List<String> lines = new ArrayList<String>();
+
+ BufferedReader reader = null;
try {
- final BufferedReader reader = new BufferedReader(new FileReader(path));
+ reader = new BufferedReader(new FileReader(path));
String line;
- do {
- line = reader.readLine();
- if (line != null) {
- builder.append(line).append(System.getProperty("line.separator"));
- }
- } while (line != null);
- reader.close();
- } catch (final Exception e) {
+ while ((line = reader.readLine()) != null) {
+ lines.add(line);
+ }
+ }
+ catch (Exception e) {
// no-op
}
- return builder.toString();
+ finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ }
+ catch (IOException e) {
+ // bad luck
+ }
+ }
+ }
+
+ return lines;
}
private IOs() {
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-reader-header.xml
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-reader-header.xml b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-reader-header.xml
new file mode 100644
index 0000000..069a234
--- /dev/null
+++ b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-reader-header.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ See the NOTICE file distributed with this work for additional information
+ regarding copyright ownership. Licensed 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.
+-->
+<job id="jsefa-csv-reader-header" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
+ <step id="step1">
+ <chunk>
+ <reader ref="jsefaCsvReader">
+ <properties>
+ <property name="file" value="#{jobParameters['input']}"/>
+ <property name="objectTypes" value="org.apache.batchee.jsefa.bean.PersonWithAddress"/>
+ <property name="ignoreHeader" value="true" />
+ </properties>
+ </reader>
+ <processor ref="org.apache.batchee.jsefa.JSefaCsvReaderTest$Storage" />
+ <writer ref="noopWriter" />
+ </chunk>
+ </step>
+</job>
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsMoreObjects.xml
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsMoreObjects.xml b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsMoreObjects.xml
new file mode 100644
index 0000000..43462fb
--- /dev/null
+++ b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsMoreObjects.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ See the NOTICE file distributed with this work for additional information
+ regarding copyright ownership. Licensed 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.
+-->
+<job id="jsefa-csv-writer-header-fromFieldsMoreObjects" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
+ <step id="step1">
+ <chunk>
+ <reader ref="org.apache.batchee.jsefa.JSefaCsvWriterHeaderTest$MoreObjectReader" />
+ <writer ref="jsefaCsvWriter">
+ <properties>
+ <property name="file" value="#{jobParameters['output']}"/>
+ <property name="objectTypes" value="org.apache.batchee.jsefa.bean.Person,org.apache.batchee.jsefa.bean.Address" />
+ <property name="writeHeader" value="true" />
+ </properties>
+ </writer>
+ </chunk>
+ </step>
+</job>
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsOneType.xml
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsOneType.xml b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsOneType.xml
new file mode 100644
index 0000000..3032c1d
--- /dev/null
+++ b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsOneType.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ See the NOTICE file distributed with this work for additional information
+ regarding copyright ownership. Licensed 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.
+-->
+<job id="jsefa-csv-writer-header-fromFieldsOneType" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
+ <step id="step1">
+ <chunk>
+ <reader ref="org.apache.batchee.jsefa.JSefaCsvWriterHeaderTest$SimpleOneTypeReader" />
+ <writer ref="jsefaCsvWriter">
+ <properties>
+ <property name="file" value="#{jobParameters['output']}"/>
+ <property name="objectTypes" value="org.apache.batchee.jsefa.bean.Person" />
+ <property name="writeHeader" value="true" />
+ </properties>
+ </writer>
+ </chunk>
+ </step>
+</job>
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsWithInheritance.xml
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsWithInheritance.xml b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsWithInheritance.xml
new file mode 100644
index 0000000..1b48844
--- /dev/null
+++ b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromFieldsWithInheritance.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ See the NOTICE file distributed with this work for additional information
+ regarding copyright ownership. Licensed 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.
+-->
+<job id="jsefa-csv-writer-header-fromFieldsWithInheritance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
+ <step id="step1">
+ <chunk>
+ <reader ref="org.apache.batchee.jsefa.JSefaCsvWriterHeaderTest$InheritanceReader" />
+ <writer ref="jsefaCsvWriter">
+ <properties>
+ <property name="file" value="#{jobParameters['output']}"/>
+ <property name="objectTypes" value="org.apache.batchee.jsefa.bean.PersonWithAddress" />
+ <property name="writeHeader" value="true" />
+ </properties>
+ </writer>
+ </chunk>
+ </step>
+</job>
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromHeaderAnnotation.xml
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromHeaderAnnotation.xml b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromHeaderAnnotation.xml
new file mode 100644
index 0000000..49021c8
--- /dev/null
+++ b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header-fromHeaderAnnotation.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ See the NOTICE file distributed with this work for additional information
+ regarding copyright ownership. Licensed 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.
+-->
+<job id="jsefa-csv-writer-header-fromHeaderAnnotation.xml" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
+ <step id="step1">
+ <chunk>
+ <reader ref="org.apache.batchee.jsefa.JSefaCsvWriterHeaderTest$HeaderAnnotationReader" />
+ <writer ref="jsefaCsvWriter">
+ <properties>
+ <property name="file" value="#{jobParameters['output']}"/>
+ <property name="objectTypes" value="org.apache.batchee.jsefa.bean.RecordWithHeader" />
+ <property name="writeHeader" value="true" />
+ </properties>
+ </writer>
+ </chunk>
+ </step>
+</job>
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header.xml
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header.xml b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header.xml
new file mode 100644
index 0000000..4d27242
--- /dev/null
+++ b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-header.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ See the NOTICE file distributed with this work for additional information
+ regarding copyright ownership. Licensed 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.
+-->
+<job id="jsefa-csv-writer-header" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
+ <step id="step1">
+ <chunk>
+ <reader ref="org.apache.batchee.jsefa.JSefaCsvWriterHeaderTest$RecordReader" />
+ <writer ref="jsefaCsvWriter">
+ <properties>
+ <property name="file" value="#{jobParameters['output']}"/>
+ <property name="objectTypes" value="org.apache.batchee.jsefa.bean.Record"/>
+ <property name="header" value="VALUE1;VALUE2" />
+ </properties>
+ </writer>
+ </chunk>
+ </step>
+</job>
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-multiOjectTypes.xml
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-multiOjectTypes.xml b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-multiOjectTypes.xml
new file mode 100644
index 0000000..0ae4545
--- /dev/null
+++ b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-multiOjectTypes.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ See the NOTICE file distributed with this work for additional information
+ regarding copyright ownership. Licensed 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.
+-->
+<job id="jsefa-csv-writer-multiObjectTypes" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
+ <step id="step1">
+ <chunk>
+ <reader ref="org.apache.batchee.jsefa.JSefaCsvWriterTest$MultipleObjectTypesReader" />
+ <writer ref="jsefaCsvWriter">
+ <properties>
+ <property name="file" value="#{jobParameters['output']}"/>
+ <property name="objectTypes" value="org.apache.batchee.jsefa.bean.Person,org.apache.batchee.jsefa.bean.Address" />
+ </properties>
+ </writer>
+ </chunk>
+ </step>
+</job>
http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/71105426/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-nestedObjects.xml
----------------------------------------------------------------------
diff --git a/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-nestedObjects.xml b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-nestedObjects.xml
new file mode 100644
index 0000000..4efe6a5
--- /dev/null
+++ b/extensions/jsefa/src/test/resources/META-INF/batch-jobs/jsefa-csv-writer-nestedObjects.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ See the NOTICE file distributed with this work for additional information
+ regarding copyright ownership. Licensed 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.
+-->
+<job id="jsefa-csv-writer-multiObjectTypes" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
+ <step id="step1">
+ <chunk>
+ <reader ref="org.apache.batchee.jsefa.JSefaCsvWriterTest$NestedObjectReader" />
+ <writer ref="jsefaCsvWriter">
+ <properties>
+ <property name="file" value="#{jobParameters['output']}"/>
+ <property name="objectTypes" value="org.apache.batchee.jsefa.bean.PersonWithAddress" />
+ </properties>
+ </writer>
+ </chunk>
+ </step>
+</job>