BATCHEE-80 adding jsonp reader/writer
diff --git a/extensions/extension-doc-helper/pom.xml b/extensions/extension-doc-helper/pom.xml
index 50cc1b8..a7df850 100644
--- a/extensions/extension-doc-helper/pom.xml
+++ b/extensions/extension-doc-helper/pom.xml
@@ -87,6 +87,11 @@
<artifactId>batchee-shiro</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.batchee</groupId>
+ <artifactId>batchee-jsonp</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
<build>
diff --git a/extensions/jsonp/pom.xml b/extensions/jsonp/pom.xml
new file mode 100644
index 0000000..4c166f4
--- /dev/null
+++ b/extensions/jsonp/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>batchee-extensions</artifactId>
+ <groupId>org.apache.batchee</groupId>
+ <version>0.3-incubating-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>batchee-jsonp</artifactId>
+ <name>BatchEE :: Extensions :: Jackson</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-json_1.0_spec</artifactId>
+ <version>1.0-alpha-1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.batchee</groupId>
+ <artifactId>batchee-extras</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.johnzon</groupId>
+ <artifactId>johnzon-core</artifactId>
+ <version>0.9.2-incubating</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <properties>
+ <jackson.version>2.2.3</jackson.version>
+ </properties>
+</project>
diff --git a/extensions/jsonp/src/main/java/org/apache/batchee/jsonp/JsonPartialReader.java b/extensions/jsonp/src/main/java/org/apache/batchee/jsonp/JsonPartialReader.java
new file mode 100644
index 0000000..d5e9fa6
--- /dev/null
+++ b/extensions/jsonp/src/main/java/org/apache/batchee/jsonp/JsonPartialReader.java
@@ -0,0 +1,170 @@
+/*
+ * 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.jsonp;
+
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonStructure;
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParsingException;
+
+public class JsonPartialReader {
+ private final JsonParser parser;
+ private final JsonProvider provider;
+ private boolean closed = false;
+
+ public JsonPartialReader(final JsonProvider provider, final JsonParser parser) {
+ this.parser = parser;
+ this.provider = provider == null ? JsonProvider.provider() : provider;
+ }
+
+ public JsonStructure read(final JsonParser.Event event) {
+ switch (event) {
+ case START_OBJECT:
+ final JsonObjectBuilder objectBuilder = provider.createObjectBuilder();
+ parseObject(objectBuilder);
+ return objectBuilder.build();
+ case START_ARRAY:
+ final JsonArrayBuilder arrayBuilder = provider.createArrayBuilder();
+ parseArray(arrayBuilder);
+ return arrayBuilder.build();
+ default:
+ throw new JsonParsingException("Unknown structure: " + parser.next(), parser.getLocation());
+ }
+
+ }
+
+ public void close() {
+ if (!closed) {
+ closed = true;
+ parser.close();
+ }
+ }
+
+ private void parseObject(final JsonObjectBuilder builder) {
+ String key = null;
+ while (parser.hasNext()) {
+ final JsonParser.Event next = parser.next();
+ switch (next) {
+ case KEY_NAME:
+ key = parser.getString();
+ break;
+
+ case VALUE_STRING:
+ builder.add(key, parser.getString());
+ break;
+
+ case START_OBJECT:
+ final JsonObjectBuilder subObject = provider.createObjectBuilder();
+ parseObject(subObject);
+ builder.add(key, subObject);
+ break;
+
+ case START_ARRAY:
+ final JsonArrayBuilder subArray = provider.createArrayBuilder();
+ parseArray(subArray);
+ builder.add(key, subArray);
+ break;
+
+ case VALUE_NUMBER:
+ if (parser.isIntegralNumber()) {
+ builder.add(key, parser.getLong());
+ } else {
+ builder.add(key, parser.getBigDecimal());
+ }
+ break;
+
+ case VALUE_NULL:
+ builder.addNull(key);
+ break;
+
+ case VALUE_TRUE:
+ builder.add(key, true);
+ break;
+
+ case VALUE_FALSE:
+ builder.add(key, false);
+ break;
+
+ case END_OBJECT:
+ return;
+
+ case END_ARRAY:
+ throw new JsonParsingException("']', shouldn't occur", parser.getLocation());
+
+ default:
+ throw new JsonParsingException(next.name() + ", shouldn't occur", parser.getLocation());
+ }
+ }
+ }
+
+ private void parseArray(final JsonArrayBuilder builder) {
+ while (parser.hasNext()) {
+ final JsonParser.Event next = parser.next();
+ switch (next) {
+ case VALUE_STRING:
+ builder.add(parser.getString());
+ break;
+
+ case VALUE_NUMBER:
+ if (parser.isIntegralNumber()) {
+ builder.add(parser.getLong());
+ } else {
+ builder.add(parser.getBigDecimal());
+ }
+ break;
+
+ case START_OBJECT:
+ JsonObjectBuilder subObject = provider.createObjectBuilder();
+ parseObject(subObject);
+ builder.add(subObject);
+ break;
+
+ case START_ARRAY:
+ JsonArrayBuilder subArray = provider.createArrayBuilder();
+ parseArray(subArray);
+ builder.add(subArray);
+ break;
+
+ case END_ARRAY:
+ return;
+
+ case VALUE_NULL:
+ builder.addNull();
+ break;
+
+ case VALUE_TRUE:
+ builder.add(true);
+ break;
+
+ case VALUE_FALSE:
+ builder.add(false);
+ break;
+
+ case KEY_NAME:
+ throw new JsonParsingException("array doesn't have keys", parser.getLocation());
+
+ case END_OBJECT:
+ throw new JsonParsingException("'}', shouldn't occur", parser.getLocation());
+
+ default:
+ throw new JsonParsingException(next.name() + ", shouldn't occur", parser.getLocation());
+ }
+ }
+ }
+}
diff --git a/extensions/jsonp/src/main/java/org/apache/batchee/jsonp/JsonpReader.java b/extensions/jsonp/src/main/java/org/apache/batchee/jsonp/JsonpReader.java
new file mode 100644
index 0000000..57402fc
--- /dev/null
+++ b/extensions/jsonp/src/main/java/org/apache/batchee/jsonp/JsonpReader.java
@@ -0,0 +1,90 @@
+/*
+ * 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.jsonp;
+
+import org.apache.batchee.doc.api.Documentation;
+import org.apache.batchee.extras.transaction.CountedReader;
+
+import javax.batch.api.BatchProperty;
+import javax.inject.Inject;
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonParser;
+import java.io.FileInputStream;
+import java.io.Serializable;
+
+@Documentation("Reads a JSON file using JSON-P providing JsonStructure as item.")
+public class JsonpReader extends CountedReader {
+ @Inject
+ @BatchProperty
+ @Documentation("Incoming file")
+ private String file;
+
+ @Inject
+ @BatchProperty
+ @Documentation("Should root be skipped (default: true)")
+ private String skipRoot;
+
+ @Inject
+ @BatchProperty
+ @Documentation("JSON-P provider if not using the default")
+ private String provider;
+
+ private JsonParser parser;
+ private JsonPartialReader reader;
+ private JsonParser.Event end = null;
+
+ @Override
+ public void open(final Serializable checkpoint) throws Exception {
+ final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ final JsonProvider provider = this.provider == null ? JsonProvider.provider() : JsonProvider.class.cast(loader.loadClass(this.provider));
+ parser = provider.createParser(new FileInputStream(file));
+ reader = new JsonPartialReader(provider, parser);
+
+ if (skipRoot == null || "true".equalsIgnoreCase(skipRoot)) {
+ final JsonParser.Event event = parser.next();
+ if (event == JsonParser.Event.START_ARRAY) {
+ end = JsonParser.Event.END_ARRAY;
+ } else {
+ end = JsonParser.Event.END_OBJECT;
+ }
+ }
+ super.open(checkpoint);
+ }
+
+ @Override
+ protected Object doRead() throws Exception {
+ JsonParser.Event event;
+ do {
+ event = parser.next();
+ } while (
+ (event != JsonParser.Event.START_OBJECT && event != end) ||
+ (end == null && (event == JsonParser.Event.END_ARRAY ||
+ event == JsonParser.Event.END_OBJECT)));
+ if (!parser.hasNext()) {
+ return null;
+ }
+
+ return reader.read(event);
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+}
diff --git a/extensions/jsonp/src/main/java/org/apache/batchee/jsonp/JsonpWriter.java b/extensions/jsonp/src/main/java/org/apache/batchee/jsonp/JsonpWriter.java
new file mode 100644
index 0000000..a4af1a6
--- /dev/null
+++ b/extensions/jsonp/src/main/java/org/apache/batchee/jsonp/JsonpWriter.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.batchee.jsonp;
+
+import org.apache.batchee.doc.api.Documentation;
+import org.apache.batchee.extras.transaction.TransactionalWriter;
+
+import javax.batch.api.BatchProperty;
+import javax.batch.api.chunk.ItemWriter;
+import javax.batch.operations.BatchRuntimeException;
+import javax.inject.Inject;
+import javax.json.JsonStructure;
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonGenerator;
+import java.io.File;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Documentation("Write a JSON file using JSON-P and taking JsonStructure as items.")
+public class JsonpWriter implements ItemWriter {
+ @Inject
+ @BatchProperty
+ @Documentation("output file")
+ private String file;
+
+ @Inject
+ @BatchProperty
+ @Documentation("output encoding")
+ private String encoding;
+
+ @Inject
+ @BatchProperty
+ @Documentation("comma separated key value pairs for the generator factory (converted to a Map<?, ?>)")
+ private String configuration;
+
+ @Inject
+ @BatchProperty
+ @Documentation("is the array wrapped in an object or not")
+ private String skipRoot;
+
+ @Inject
+ @BatchProperty
+ @Documentation("how to generate field names for each item, default uses item1, item2, ...")
+ private String fieldNameGeneratorClass;
+
+ @Inject
+ @BatchProperty
+ @Documentation("JSON-P provider if not using the default")
+ private String provider;
+
+ private JsonGenerator generator;
+ private TransactionalWriter writer;
+ private FieldNameGenerator fieldNameGenerator = null;
+
+ @Override
+ public void open(final Serializable checkpoint) throws Exception {
+ final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ final JsonProvider provider = this.provider == null ? JsonProvider.provider() : JsonProvider.class.cast(loader.loadClass(this.provider));
+
+ final File outputFile = new File(file);
+ if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) {
+ throw new BatchRuntimeException("Can't create " + outputFile.getAbsolutePath());
+ }
+
+ writer = new TransactionalWriter(outputFile, encoding, checkpoint);
+ generator = provider.createGeneratorFactory(buildConfig()).createGenerator(writer);
+ if (fieldNameGeneratorClass != null) {
+ if ("default".equals(fieldNameGeneratorClass)) {
+ fieldNameGenerator = new FieldNameGenerator() {
+ private int count = 0;
+
+ @Override
+ public String nextName() {
+ return "item" + ++count;
+ }
+ };
+ } else {
+ fieldNameGenerator = FieldNameGenerator.class.cast(Thread.currentThread().getContextClassLoader().loadClass(fieldNameGeneratorClass).newInstance());
+ }
+ }
+
+ if (useGlobalWrapper()) {
+ if (fieldNameGenerator != null) {
+ generator.writeStartObject();
+ } else {
+ generator.writeStartArray();
+ }
+ }
+ }
+
+ private Map<String, ?> buildConfig() {
+ final Map<String, Object> map = new HashMap<String, Object>();
+ if (configuration != null) {
+ for (final String entry : configuration.trim().split(" *, *")) {
+ final String[] parts = entry.split(" *= *");
+ if (parts.length != 2) {
+ throw new IllegalArgumentException(entry + " not matching a=b pattern");
+ }
+ map.put(parts[0], parts[1]);
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (generator != null) {
+ if (useGlobalWrapper()) {
+ generator.writeEnd();
+ }
+ generator.close();
+ }
+ }
+
+ @Override
+ public void writeItems(final List<Object> items) throws Exception {
+ final List<JsonStructure> structures = List.class.cast(items);
+ for (final JsonStructure structure : structures) {
+ if (fieldNameGenerator != null) {
+ generator.write(fieldNameGenerator.nextName(), structure);
+ } else {
+ generator.write(structure);
+ }
+ }
+ writer.flush();
+ }
+
+ @Override
+ public Serializable checkpointInfo() throws Exception {
+ return writer.position();
+ }
+
+ private boolean useGlobalWrapper() {
+ return skipRoot == null || !"true".equalsIgnoreCase(skipRoot);
+ }
+
+ public interface FieldNameGenerator {
+ String nextName();
+ }
+}
diff --git a/extensions/jsonp/src/main/resources/META-INF/batchee.xml b/extensions/jsonp/src/main/resources/META-INF/batchee.xml
new file mode 100644
index 0000000..9208df6
--- /dev/null
+++ b/extensions/jsonp/src/main/resources/META-INF/batchee.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<batch-artifacts xmlns="http://xmlns.jcp.org/xml/ns/javaee">
+ <ref id="jsonpReader" class="org.apache.batchee.jsonp.JsonpReader" />
+ <ref id="jsonpWriter" class="org.apache.batchee.jsonp.JsonpWriter" />
+</batch-artifacts>
diff --git a/extensions/jsonp/src/test/java/org/apache/batchee/jsonp/JsonpReaderTest.java b/extensions/jsonp/src/test/java/org/apache/batchee/jsonp/JsonpReaderTest.java
new file mode 100644
index 0000000..afe69ef
--- /dev/null
+++ b/extensions/jsonp/src/test/java/org/apache/batchee/jsonp/JsonpReaderTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.jsonp;
+
+import org.apache.batchee.jsonp.util.IOs;
+import org.apache.batchee.util.Batches;
+import org.testng.annotations.Test;
+
+import javax.batch.api.chunk.ItemWriter;
+import javax.batch.operations.JobOperator;
+import javax.batch.runtime.BatchRuntime;
+import javax.json.JsonObject;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import static org.testng.Assert.assertEquals;
+
+public class JsonpReaderTest {
+ @Test
+ public void read() {
+ IOs.write("target/work/jsonp-input.json", "[" +
+ " {" +
+ " \"v1\":\"record 1 # field 1\"," +
+ " \"v2\":\"record 1 # field 2\"" +
+ " }," +
+ " {" +
+ " \"v1\":\"record 2 # field 1\"," +
+ " \"v2\":\"record 2 # field 2\"" +
+ " }" +
+ "]");
+
+ final JobOperator operator = BatchRuntime.getJobOperator();
+ Batches.waitForEnd(operator, operator.start("jsonp-reader", new Properties()));
+ assertEquals(Writer.ITEMS.size(), 2);
+ for (int i = 1; i < Writer.ITEMS.size() + 1; i++) {
+ final JsonObject record = Writer.ITEMS.get(i - 1);
+ assertEquals("record " + i + " # field 1", record.getString("v1"));
+ assertEquals("record " + i + " # field 2", record.getString("v2"));
+ }
+ }
+
+ public static class Writer implements ItemWriter {
+ public static List<JsonObject> ITEMS = new ArrayList<JsonObject>(2);
+
+ @Override
+ public void open(final Serializable checkpoint) throws Exception {
+ // no-op
+ }
+
+ @Override
+ public void close() throws Exception {
+ // no-op
+ }
+
+ @Override
+ public void writeItems(final List<Object> items) throws Exception {
+ ITEMS.addAll(List.class.cast(items));
+ }
+
+ @Override
+ public Serializable checkpointInfo() throws Exception {
+ return null;
+ }
+ }
+}
diff --git a/extensions/jsonp/src/test/java/org/apache/batchee/jsonp/JsonpWriterTest.java b/extensions/jsonp/src/test/java/org/apache/batchee/jsonp/JsonpWriterTest.java
new file mode 100644
index 0000000..b039fc9
--- /dev/null
+++ b/extensions/jsonp/src/test/java/org/apache/batchee/jsonp/JsonpWriterTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.jsonp;
+
+import org.apache.batchee.jsonp.util.IOs;
+import org.apache.batchee.util.Batches;
+import org.testng.annotations.Test;
+
+import javax.batch.api.chunk.ItemReader;
+import javax.batch.operations.JobOperator;
+import javax.batch.runtime.BatchRuntime;
+import javax.json.Json;
+import java.io.Serializable;
+import java.util.Properties;
+
+import static org.testng.Assert.assertEquals;
+
+public class JsonpWriterTest {
+ @Test
+ public void write() {
+ final JobOperator operator = BatchRuntime.getJobOperator();
+ Batches.waitForEnd(operator, operator.start("jsonp-writer", new Properties()));
+ final String output = IOs.slurp("target/work/jsonp-output.json");
+ assertEquals(output.replace("\n", "").replace("\r", "").replace(" ", "").replace("\t", ""),
+ "[{\"v1\":\"v11\",\"v2\":\"v21\"},{\"v1\":\"v12\",\"v2\":\"v22\"}]");
+ }
+
+ public static class Reader implements ItemReader {
+ private int count = 0;
+
+ @Override
+ public void open(final Serializable checkpoint) throws Exception {
+ // no-op
+ }
+
+ @Override
+ public void close() throws Exception {
+ // no-op
+ }
+
+ @Override
+ public Object readItem() throws Exception {
+ if (count++ < 2) {
+ return Json.createObjectBuilder().add("v1", "v1" + count).add("v2", "v2" + count).build();
+ }
+ return null;
+ }
+
+ @Override
+ public Serializable checkpointInfo() throws Exception {
+ return null;
+ }
+ }
+}
diff --git a/extensions/jsonp/src/test/java/org/apache/batchee/jsonp/util/IOs.java b/extensions/jsonp/src/test/java/org/apache/batchee/jsonp/util/IOs.java
new file mode 100644
index 0000000..0b807e2
--- /dev/null
+++ b/extensions/jsonp/src/test/java/org/apache/batchee/jsonp/util/IOs.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.jsonp.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class IOs {
+ public static void write(final String path, final String content) {
+ final File file = new File(path);
+ if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
+ throw new RuntimeException("Can't create " + path);
+ }
+
+ try {
+ final FileWriter writer = new FileWriter(file);
+ writer.write(content);
+ writer.close();
+ } catch (final IOException e) {
+ // no-op
+ }
+ }
+
+ public static String slurp(final String path) {
+ final StringBuilder builder = new StringBuilder();
+ try {
+ final BufferedReader reader = new BufferedReader(new FileReader(path));
+ String line;
+ do {
+ line = reader.readLine();
+ if (line != null) {
+ builder.append(line);
+ }
+ } while (line != null);
+ reader.close();
+ } catch (final Exception e) {
+ // no-op
+ }
+ return builder.toString();
+ }
+
+ private IOs() {
+ // no-op
+ }
+}
diff --git a/extensions/jsonp/src/test/resources/META-INF/batch-jobs/jsonp-reader.xml b/extensions/jsonp/src/test/resources/META-INF/batch-jobs/jsonp-reader.xml
new file mode 100644
index 0000000..f7df4f5
--- /dev/null
+++ b/extensions/jsonp/src/test/resources/META-INF/batch-jobs/jsonp-reader.xml
@@ -0,0 +1,32 @@
+<?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="jsonp-reader" version="1.0"
+ xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ http://xmlns.jcp.org/xml/ns/javaee
+ http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd">
+ <step id="step1">
+ <chunk>
+ <reader ref="jsonpReader">
+ <properties>
+ <property name="file" value="target/work/jsonp-input.json"/>
+ </properties>
+ </reader>
+ <writer ref="org.apache.batchee.jsonp.JsonpReaderTest$Writer" />
+ </chunk>
+ </step>
+</job>
diff --git a/extensions/jsonp/src/test/resources/META-INF/batch-jobs/jsonp-writer.xml b/extensions/jsonp/src/test/resources/META-INF/batch-jobs/jsonp-writer.xml
new file mode 100644
index 0000000..072493f
--- /dev/null
+++ b/extensions/jsonp/src/test/resources/META-INF/batch-jobs/jsonp-writer.xml
@@ -0,0 +1,32 @@
+<?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="jsonp-writer" version="1.0"
+ xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ http://xmlns.jcp.org/xml/ns/javaee
+ http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd">
+ <step id="step1">
+ <chunk>
+ <reader ref="org.apache.batchee.jsonp.JsonpWriterTest$Reader" />
+ <writer ref="jsonpWriter">
+ <properties>
+ <property name="file" value="target/work/jsonp-output.json"/>
+ </properties>
+ </writer>
+ </chunk>
+ </step>
+</job>
diff --git a/extensions/pom.xml b/extensions/pom.xml
index fbb38a6..c990745 100644
--- a/extensions/pom.xml
+++ b/extensions/pom.xml
@@ -40,6 +40,7 @@
<module>hazelcast</module>
<module>modelmapper</module>
<module>commons-csv</module>
+ <module>jsonp</module>
<module>extension-doc-helper</module>
</modules>