embulk-input-singer_tap 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +38 -0
- data/build.gradle +98 -0
- data/config/checkstyle/checkstyle.xml +128 -0
- data/config/checkstyle/default.xml +108 -0
- data/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/gradle/wrapper/gradle-wrapper.properties +5 -0
- data/gradlew +172 -0
- data/gradlew.bat +84 -0
- data/lib/embulk/input/singer_tap.rb +3 -0
- data/src/main/java/org/embulk/input/singer_tap/ArrayParser.java +31 -0
- data/src/main/java/org/embulk/input/singer_tap/BooleanParser.java +16 -0
- data/src/main/java/org/embulk/input/singer_tap/IntegerParser.java +16 -0
- data/src/main/java/org/embulk/input/singer_tap/NullableArrayParser.java +19 -0
- data/src/main/java/org/embulk/input/singer_tap/NullableBooleanParser.java +15 -0
- data/src/main/java/org/embulk/input/singer_tap/NullableIntegerParser.java +15 -0
- data/src/main/java/org/embulk/input/singer_tap/NullableNumberParser.java +15 -0
- data/src/main/java/org/embulk/input/singer_tap/NullableObjectParser.java +21 -0
- data/src/main/java/org/embulk/input/singer_tap/NullableStringParser.java +15 -0
- data/src/main/java/org/embulk/input/singer_tap/NumberParser.java +16 -0
- data/src/main/java/org/embulk/input/singer_tap/ObjectParser.java +52 -0
- data/src/main/java/org/embulk/input/singer_tap/ParserGenerator.java +79 -0
- data/src/main/java/org/embulk/input/singer_tap/RecordParser.java +11 -0
- data/src/main/java/org/embulk/input/singer_tap/SingerTapInputPlugin.java +265 -0
- data/src/main/java/org/embulk/input/singer_tap/StringParser.java +16 -0
- data/src/test/java/org/embulk/input/singer_tap/TestSingerTapInputPlugin.java +5 -0
- metadata +100 -0
data/gradlew.bat
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
@if "%DEBUG%" == "" @echo off
|
2
|
+
@rem ##########################################################################
|
3
|
+
@rem
|
4
|
+
@rem Gradle startup script for Windows
|
5
|
+
@rem
|
6
|
+
@rem ##########################################################################
|
7
|
+
|
8
|
+
@rem Set local scope for the variables with windows NT shell
|
9
|
+
if "%OS%"=="Windows_NT" setlocal
|
10
|
+
|
11
|
+
set DIRNAME=%~dp0
|
12
|
+
if "%DIRNAME%" == "" set DIRNAME=.
|
13
|
+
set APP_BASE_NAME=%~n0
|
14
|
+
set APP_HOME=%DIRNAME%
|
15
|
+
|
16
|
+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
17
|
+
set DEFAULT_JVM_OPTS=
|
18
|
+
|
19
|
+
@rem Find java.exe
|
20
|
+
if defined JAVA_HOME goto findJavaFromJavaHome
|
21
|
+
|
22
|
+
set JAVA_EXE=java.exe
|
23
|
+
%JAVA_EXE% -version >NUL 2>&1
|
24
|
+
if "%ERRORLEVEL%" == "0" goto init
|
25
|
+
|
26
|
+
echo.
|
27
|
+
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
28
|
+
echo.
|
29
|
+
echo Please set the JAVA_HOME variable in your environment to match the
|
30
|
+
echo location of your Java installation.
|
31
|
+
|
32
|
+
goto fail
|
33
|
+
|
34
|
+
:findJavaFromJavaHome
|
35
|
+
set JAVA_HOME=%JAVA_HOME:"=%
|
36
|
+
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
37
|
+
|
38
|
+
if exist "%JAVA_EXE%" goto init
|
39
|
+
|
40
|
+
echo.
|
41
|
+
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
42
|
+
echo.
|
43
|
+
echo Please set the JAVA_HOME variable in your environment to match the
|
44
|
+
echo location of your Java installation.
|
45
|
+
|
46
|
+
goto fail
|
47
|
+
|
48
|
+
:init
|
49
|
+
@rem Get command-line arguments, handling Windows variants
|
50
|
+
|
51
|
+
if not "%OS%" == "Windows_NT" goto win9xME_args
|
52
|
+
|
53
|
+
:win9xME_args
|
54
|
+
@rem Slurp the command line arguments.
|
55
|
+
set CMD_LINE_ARGS=
|
56
|
+
set _SKIP=2
|
57
|
+
|
58
|
+
:win9xME_args_slurp
|
59
|
+
if "x%~1" == "x" goto execute
|
60
|
+
|
61
|
+
set CMD_LINE_ARGS=%*
|
62
|
+
|
63
|
+
:execute
|
64
|
+
@rem Setup the command line
|
65
|
+
|
66
|
+
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
67
|
+
|
68
|
+
@rem Execute Gradle
|
69
|
+
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
70
|
+
|
71
|
+
:end
|
72
|
+
@rem End local scope for the variables with windows NT shell
|
73
|
+
if "%ERRORLEVEL%"=="0" goto mainEnd
|
74
|
+
|
75
|
+
:fail
|
76
|
+
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
77
|
+
rem the _cmd.exe /c_ return code!
|
78
|
+
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
79
|
+
exit /b 1
|
80
|
+
|
81
|
+
:mainEnd
|
82
|
+
if "%OS%"=="Windows_NT" endlocal
|
83
|
+
|
84
|
+
:omega
|
@@ -0,0 +1,31 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import com.fasterxml.jackson.databind.node.ArrayNode;
|
5
|
+
import org.embulk.spi.type.Type;
|
6
|
+
import org.embulk.spi.type.Types;
|
7
|
+
import org.msgpack.value.Value;
|
8
|
+
import org.msgpack.value.ValueFactory;
|
9
|
+
|
10
|
+
import java.util.ArrayList;
|
11
|
+
import java.util.List;
|
12
|
+
|
13
|
+
public class ArrayParser implements RecordParser {
|
14
|
+
private RecordParser elementParser;
|
15
|
+
|
16
|
+
public ArrayParser(JsonNode node) throws Exception {
|
17
|
+
elementParser = ParserGenerator.generateParser(node);
|
18
|
+
}
|
19
|
+
|
20
|
+
public Value parse(JsonNode node) {
|
21
|
+
ArrayNode arrNode = (ArrayNode) node;
|
22
|
+
List<Value> list = new ArrayList<>();
|
23
|
+
for (JsonNode element : arrNode) {
|
24
|
+
list.add(elementParser.parse(element));
|
25
|
+
}
|
26
|
+
|
27
|
+
return ValueFactory.newArray(list);
|
28
|
+
}
|
29
|
+
|
30
|
+
public Type embulkType() { return Types.JSON; }
|
31
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import org.embulk.spi.type.Type;
|
5
|
+
import org.embulk.spi.type.Types;
|
6
|
+
import org.msgpack.value.Value;
|
7
|
+
import org.msgpack.value.ValueFactory;
|
8
|
+
|
9
|
+
public class BooleanParser implements RecordParser {
|
10
|
+
@Override
|
11
|
+
public Value parse(JsonNode node) {
|
12
|
+
return ValueFactory.newBoolean(node.asBoolean());
|
13
|
+
}
|
14
|
+
|
15
|
+
public Type embulkType() { return Types.BOOLEAN; }
|
16
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import org.embulk.spi.type.Type;
|
5
|
+
import org.embulk.spi.type.Types;
|
6
|
+
import org.msgpack.value.Value;
|
7
|
+
import org.msgpack.value.ValueFactory;
|
8
|
+
|
9
|
+
public class IntegerParser implements RecordParser {
|
10
|
+
@Override
|
11
|
+
public Value parse(JsonNode node) {
|
12
|
+
return ValueFactory.newInteger(node.asLong());
|
13
|
+
}
|
14
|
+
|
15
|
+
public Type embulkType() { return Types.LONG; }
|
16
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import org.msgpack.value.Value;
|
5
|
+
import org.msgpack.value.ValueFactory;
|
6
|
+
|
7
|
+
public class NullableArrayParser extends ArrayParser {
|
8
|
+
public NullableArrayParser(JsonNode node) throws Exception {
|
9
|
+
super(node);
|
10
|
+
}
|
11
|
+
|
12
|
+
@Override
|
13
|
+
public Value parse(JsonNode node) {
|
14
|
+
if (node.isNull()) {
|
15
|
+
return ValueFactory.newNil();
|
16
|
+
}
|
17
|
+
return super.parse(node);
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import org.msgpack.value.Value;
|
5
|
+
import org.msgpack.value.ValueFactory;
|
6
|
+
|
7
|
+
public class NullableBooleanParser extends BooleanParser {
|
8
|
+
@Override
|
9
|
+
public Value parse(JsonNode node) {
|
10
|
+
if (node.isNull()) {
|
11
|
+
return ValueFactory.newNil();
|
12
|
+
}
|
13
|
+
return ValueFactory.newBoolean(node.asBoolean());
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import org.msgpack.value.Value;
|
5
|
+
import org.msgpack.value.ValueFactory;
|
6
|
+
|
7
|
+
public class NullableIntegerParser extends IntegerParser {
|
8
|
+
@Override
|
9
|
+
public Value parse(JsonNode node) {
|
10
|
+
if (node.isNull()) {
|
11
|
+
return ValueFactory.newNil();
|
12
|
+
}
|
13
|
+
return ValueFactory.newInteger(node.asLong());
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import org.msgpack.value.Value;
|
5
|
+
import org.msgpack.value.ValueFactory;
|
6
|
+
|
7
|
+
public class NullableNumberParser extends NumberParser {
|
8
|
+
@Override
|
9
|
+
public Value parse(JsonNode node) {
|
10
|
+
if (node.isNull()) {
|
11
|
+
return ValueFactory.newNil();
|
12
|
+
}
|
13
|
+
return ValueFactory.newFloat(node.asDouble());
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import com.fasterxml.jackson.databind.node.ObjectNode;
|
5
|
+
import org.msgpack.value.Value;
|
6
|
+
import org.msgpack.value.ValueFactory;
|
7
|
+
|
8
|
+
public class NullableObjectParser extends ObjectParser {
|
9
|
+
public NullableObjectParser(ObjectNode node) throws Exception {
|
10
|
+
super(node);
|
11
|
+
}
|
12
|
+
|
13
|
+
@Override
|
14
|
+
public Value parse(JsonNode node) {
|
15
|
+
if (node.isNull()) {
|
16
|
+
return ValueFactory.newNil();
|
17
|
+
}
|
18
|
+
return super.parse(node);
|
19
|
+
}
|
20
|
+
|
21
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import org.msgpack.value.Value;
|
5
|
+
import org.msgpack.value.ValueFactory;
|
6
|
+
|
7
|
+
public class NullableStringParser extends StringParser {
|
8
|
+
@Override
|
9
|
+
public Value parse(JsonNode node) {
|
10
|
+
if (node.isNull()) {
|
11
|
+
return ValueFactory.newNil();
|
12
|
+
}
|
13
|
+
return ValueFactory.newString(node.asText());
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import org.embulk.spi.type.Type;
|
5
|
+
import org.embulk.spi.type.Types;
|
6
|
+
import org.msgpack.value.Value;
|
7
|
+
import org.msgpack.value.ValueFactory;
|
8
|
+
|
9
|
+
public class NumberParser implements RecordParser {
|
10
|
+
@Override
|
11
|
+
public Value parse(JsonNode node) {
|
12
|
+
return ValueFactory.newFloat(node.asDouble());
|
13
|
+
}
|
14
|
+
|
15
|
+
public Type embulkType() { return Types.DOUBLE; }
|
16
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import com.fasterxml.jackson.databind.node.ObjectNode;
|
5
|
+
import org.embulk.spi.type.Type;
|
6
|
+
import org.embulk.spi.type.Types;
|
7
|
+
import org.msgpack.value.Value;
|
8
|
+
import org.msgpack.value.ValueFactory;
|
9
|
+
|
10
|
+
import java.util.HashMap;
|
11
|
+
import java.util.Iterator;
|
12
|
+
import java.util.LinkedHashMap;
|
13
|
+
import java.util.Map;
|
14
|
+
|
15
|
+
public class ObjectParser implements RecordParser {
|
16
|
+
private Map<String, RecordParser> fieldParser;
|
17
|
+
|
18
|
+
public ObjectParser(ObjectNode node) throws Exception {
|
19
|
+
fieldParser = new LinkedHashMap<>();
|
20
|
+
for (Iterator<Map.Entry<String, JsonNode>> it = node.fields(); it.hasNext(); ) {
|
21
|
+
Map.Entry<String, JsonNode> e = it.next();
|
22
|
+
fieldParser.put(e.getKey(), ParserGenerator.generateParser(e.getValue()));
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
@Override
|
27
|
+
public Value parse(JsonNode node) {
|
28
|
+
ObjectNode objNode = (ObjectNode) node;
|
29
|
+
Map<Value, Value> map = new HashMap<>();
|
30
|
+
|
31
|
+
for (Map.Entry<String, RecordParser> entry : fieldParser.entrySet()) {
|
32
|
+
String key = entry.getKey();
|
33
|
+
JsonNode valNode = objNode.get(key);
|
34
|
+
Value val;
|
35
|
+
if (valNode == null) {
|
36
|
+
val = ValueFactory.newNil();
|
37
|
+
}
|
38
|
+
else {
|
39
|
+
val = fieldParser.get(key).parse(valNode);
|
40
|
+
}
|
41
|
+
map.put(ValueFactory.newString(key), val);
|
42
|
+
}
|
43
|
+
|
44
|
+
return ValueFactory.newMap(map);
|
45
|
+
}
|
46
|
+
|
47
|
+
public Map<String, RecordParser> properties() {
|
48
|
+
return fieldParser;
|
49
|
+
}
|
50
|
+
|
51
|
+
public Type embulkType() { return Types.JSON; }
|
52
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
4
|
+
import com.fasterxml.jackson.databind.node.ArrayNode;
|
5
|
+
import com.fasterxml.jackson.databind.node.ObjectNode;
|
6
|
+
|
7
|
+
public class ParserGenerator {
|
8
|
+
public static RecordParser generateParser(JsonNode node) throws Exception {
|
9
|
+
String type;
|
10
|
+
Boolean nullable = false;
|
11
|
+
JsonNode type_node = node.get("type");
|
12
|
+
if (type_node.isTextual()) {
|
13
|
+
type = type_node.asText();
|
14
|
+
}
|
15
|
+
else if (type_node.isArray()) {
|
16
|
+
ArrayNode types = (ArrayNode) type_node;
|
17
|
+
int len = types.size();
|
18
|
+
if (len == 2) {
|
19
|
+
String type_0 = types.get(0).asText();
|
20
|
+
String type_1 = types.get(1).asText();
|
21
|
+
if (type_0.equals("null")) {
|
22
|
+
nullable = true;
|
23
|
+
type = type_1;
|
24
|
+
}
|
25
|
+
else if (type_1.equals("null")) {
|
26
|
+
nullable = true;
|
27
|
+
type = type_0;
|
28
|
+
}
|
29
|
+
else {
|
30
|
+
type = "undefined";
|
31
|
+
}
|
32
|
+
}
|
33
|
+
else if (len == 1){
|
34
|
+
type = types.get(0).asText();
|
35
|
+
}
|
36
|
+
else {
|
37
|
+
throw new Exception("number of possible types must be 1 or 2!");
|
38
|
+
}
|
39
|
+
}
|
40
|
+
else {
|
41
|
+
throw new Exception("invalid type!");
|
42
|
+
}
|
43
|
+
|
44
|
+
if (nullable) {
|
45
|
+
switch (type) {
|
46
|
+
case "string":
|
47
|
+
return new NullableStringParser();
|
48
|
+
case "number":
|
49
|
+
return new NumberParser();
|
50
|
+
case "integer":
|
51
|
+
return new NullableIntegerParser();
|
52
|
+
case "boolean":
|
53
|
+
return new NullableBooleanParser();
|
54
|
+
case "object":
|
55
|
+
return new NullableObjectParser((ObjectNode) (node.get("properties")));
|
56
|
+
case "array":
|
57
|
+
return new NullableArrayParser(node.get("items"));
|
58
|
+
default:
|
59
|
+
throw new Exception("Undefined type");
|
60
|
+
}
|
61
|
+
}
|
62
|
+
switch (type) {
|
63
|
+
case "string":
|
64
|
+
return new StringParser();
|
65
|
+
case "number":
|
66
|
+
return new NumberParser();
|
67
|
+
case "integer":
|
68
|
+
return new IntegerParser();
|
69
|
+
case "boolean":
|
70
|
+
return new BooleanParser();
|
71
|
+
case "object":
|
72
|
+
return new ObjectParser((ObjectNode) (node.get("properties")));
|
73
|
+
case "array":
|
74
|
+
return new ArrayParser(node.get("items"));
|
75
|
+
default:
|
76
|
+
throw new Exception("Undefined type");
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
@@ -0,0 +1,265 @@
|
|
1
|
+
package org.embulk.input.singer_tap;
|
2
|
+
|
3
|
+
import java.io.*;
|
4
|
+
import java.util.*;
|
5
|
+
|
6
|
+
import com.fasterxml.jackson.databind.JsonNode;
|
7
|
+
import com.fasterxml.jackson.databind.ObjectMapper;
|
8
|
+
|
9
|
+
import org.embulk.config.*;
|
10
|
+
import org.embulk.spi.*;
|
11
|
+
import org.msgpack.value.Value;
|
12
|
+
|
13
|
+
|
14
|
+
public class SingerTapInputPlugin
|
15
|
+
implements InputPlugin
|
16
|
+
{
|
17
|
+
public interface PluginTask
|
18
|
+
extends Task
|
19
|
+
{
|
20
|
+
@Config("tap_command")
|
21
|
+
public String getTapCommand();
|
22
|
+
|
23
|
+
@Config("config")
|
24
|
+
public String getConfig();
|
25
|
+
|
26
|
+
@Config("catalog")
|
27
|
+
@ConfigDefault("null")
|
28
|
+
public Optional<String> getCatalog();
|
29
|
+
|
30
|
+
@Config("properties")
|
31
|
+
@ConfigDefault("null")
|
32
|
+
public Optional<String> getProperties();
|
33
|
+
|
34
|
+
@Config("input_state")
|
35
|
+
@ConfigDefault("null")
|
36
|
+
public Optional<String> getInputState();
|
37
|
+
|
38
|
+
@Config("output_state")
|
39
|
+
@ConfigDefault("null")
|
40
|
+
public Optional<String> getOutputState();
|
41
|
+
|
42
|
+
String getSchemaFileName();
|
43
|
+
void setSchemaFileName(String schemaFile);
|
44
|
+
|
45
|
+
List<String> getCommandLine();
|
46
|
+
void setCommandLine(List<String> commandLine);
|
47
|
+
|
48
|
+
@ConfigInject
|
49
|
+
BufferAllocator getBufferAllocator();
|
50
|
+
|
51
|
+
}
|
52
|
+
|
53
|
+
@Override
|
54
|
+
public ConfigDiff transaction(ConfigSource config,
|
55
|
+
InputPlugin.Control control)
|
56
|
+
{
|
57
|
+
PluginTask task = config.loadConfig(PluginTask.class);
|
58
|
+
|
59
|
+
String command = task.getTapCommand() + " --config " + task.getConfig();
|
60
|
+
|
61
|
+
File schemaFile;
|
62
|
+
if (task.getCatalog().isPresent()) {
|
63
|
+
if (task.getProperties().isPresent()) {
|
64
|
+
throw new ConfigException("only one of 'catalog' or 'properties' parameter is needed");
|
65
|
+
}
|
66
|
+
String schemaFileName = task.getCatalog().get();
|
67
|
+
schemaFile = new File(schemaFileName);
|
68
|
+
task.setSchemaFileName(schemaFileName);
|
69
|
+
command = command + " --catalog " + schemaFileName;
|
70
|
+
}
|
71
|
+
else {
|
72
|
+
if (!task.getProperties().isPresent()) {
|
73
|
+
throw new ConfigException("'catalog' or 'properties' parameter is needed");
|
74
|
+
}
|
75
|
+
String schemaFileName = task.getProperties().get();
|
76
|
+
schemaFile = new File(schemaFileName);
|
77
|
+
task.setSchemaFileName(schemaFileName);
|
78
|
+
command = command + " --properties " + schemaFileName;
|
79
|
+
}
|
80
|
+
Schema schema = generateSchema(schemaFile);
|
81
|
+
|
82
|
+
if (task.getInputState().isPresent()) {
|
83
|
+
String stateFileName = task.getInputState().get();
|
84
|
+
command = command + " --state " + stateFileName;
|
85
|
+
}
|
86
|
+
|
87
|
+
List<String> cmdline = new ArrayList<>(Arrays.asList("sh", "-c"));
|
88
|
+
cmdline.add(command);
|
89
|
+
task.setCommandLine(cmdline);
|
90
|
+
|
91
|
+
int taskCount = 1; // number of run() method calls
|
92
|
+
|
93
|
+
return resume(task.dump(), schema, taskCount, control);
|
94
|
+
}
|
95
|
+
|
96
|
+
@Override
|
97
|
+
public ConfigDiff resume(TaskSource taskSource,
|
98
|
+
Schema schema, int taskCount,
|
99
|
+
InputPlugin.Control control)
|
100
|
+
{
|
101
|
+
control.run(taskSource, schema, taskCount);
|
102
|
+
return Exec.newConfigDiff();
|
103
|
+
}
|
104
|
+
|
105
|
+
@Override
|
106
|
+
public void cleanup(TaskSource taskSource,
|
107
|
+
Schema schema, int taskCount,
|
108
|
+
List<TaskReport> successTaskReports)
|
109
|
+
{
|
110
|
+
}
|
111
|
+
|
112
|
+
@Override
|
113
|
+
public TaskReport run(TaskSource taskSource,
|
114
|
+
Schema schema, int taskIndex,
|
115
|
+
PageOutput output)
|
116
|
+
{
|
117
|
+
PluginTask task = taskSource.loadTask(PluginTask.class);
|
118
|
+
BufferAllocator allocator = task.getBufferAllocator();
|
119
|
+
|
120
|
+
RecordParser parser;
|
121
|
+
try {
|
122
|
+
JsonNode schemaNode = getSchema(new File(task.getSchemaFileName()));
|
123
|
+
parser = ParserGenerator.generateParser(schemaNode);
|
124
|
+
}
|
125
|
+
catch (Exception e) {
|
126
|
+
throw new ConfigException(e.getMessage());
|
127
|
+
}
|
128
|
+
|
129
|
+
List<String> cmdline = task.getCommandLine();
|
130
|
+
ProcessBuilder pb = new ProcessBuilder(cmdline);
|
131
|
+
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
132
|
+
String state = "";
|
133
|
+
try {
|
134
|
+
Process process = pb.start();
|
135
|
+
InputStream stream = process.getInputStream();
|
136
|
+
try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
|
137
|
+
String line;
|
138
|
+
JsonNode root;
|
139
|
+
PageBuilder pageBuilder = new PageBuilder(allocator, schema, output);;
|
140
|
+
ObjectMapper recordMapper = new ObjectMapper();
|
141
|
+
while ((line = br.readLine()) != null) {
|
142
|
+
root = recordMapper.readTree(line);
|
143
|
+
String type = root.get("type").asText();
|
144
|
+
if (type.equals("RECORD")) {
|
145
|
+
JsonNode recordNode = root.get("record");
|
146
|
+
Value record = parser.parse(recordNode);
|
147
|
+
if (!(record.isMapValue())) {
|
148
|
+
throw new DataException("invalid record");
|
149
|
+
}
|
150
|
+
Map<String, Value> rec = new HashMap<>();
|
151
|
+
for (Map.Entry<Value, Value> entry : record.asMapValue().entrySet()) {
|
152
|
+
rec.put(entry.getKey().asStringValue().asString(), entry.getValue());
|
153
|
+
}
|
154
|
+
for (Column column : pageBuilder.getSchema().getColumns()) {
|
155
|
+
setColumn(column, rec, pageBuilder);
|
156
|
+
}
|
157
|
+
pageBuilder.addRecord();
|
158
|
+
}
|
159
|
+
else if (type.equals("SCHEMA")) {
|
160
|
+
|
161
|
+
}
|
162
|
+
else if (type.equals("STATE")) {
|
163
|
+
state = root.get("value").toString();
|
164
|
+
}
|
165
|
+
else {
|
166
|
+
throw new DataException("invalid message type: " + type);
|
167
|
+
}
|
168
|
+
}
|
169
|
+
pageBuilder.finish();
|
170
|
+
}
|
171
|
+
}
|
172
|
+
catch (IOException e) {
|
173
|
+
throw new DataException(e.getMessage());
|
174
|
+
}
|
175
|
+
finally {
|
176
|
+
if (task.getOutputState().isPresent()) {
|
177
|
+
String statePath = task.getOutputState().get();
|
178
|
+
try (FileOutputStream writer = new FileOutputStream(statePath)) {
|
179
|
+
writer.write(state.getBytes());
|
180
|
+
}
|
181
|
+
catch (Exception e) {
|
182
|
+
throw new DataException(e.getMessage());
|
183
|
+
}
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
return Exec.newTaskReport();
|
188
|
+
}
|
189
|
+
|
190
|
+
@Override
|
191
|
+
public ConfigDiff guess(ConfigSource config)
|
192
|
+
{
|
193
|
+
return Exec.newConfigDiff();
|
194
|
+
}
|
195
|
+
|
196
|
+
private JsonNode getSchema(File catalog) {
|
197
|
+
ObjectMapper mapper = new ObjectMapper();
|
198
|
+
try {
|
199
|
+
JsonNode streams = mapper.readTree(catalog).get("streams");
|
200
|
+
JsonNode schemaNode = null;
|
201
|
+
for (JsonNode stream : streams) {
|
202
|
+
JsonNode schema = stream.get("schema");
|
203
|
+
JsonNode selected = schema.get("selected");
|
204
|
+
if (selected != null && selected.asBoolean()) {
|
205
|
+
schemaNode = schema;
|
206
|
+
break;
|
207
|
+
}
|
208
|
+
}
|
209
|
+
if (schemaNode == null) {
|
210
|
+
throw new ConfigException("schema not selected");
|
211
|
+
}
|
212
|
+
return schemaNode;
|
213
|
+
}
|
214
|
+
catch (Exception e) {
|
215
|
+
throw new ConfigException(e.getMessage());
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
private Schema generateSchema(File catalog) throws ConfigException {
|
220
|
+
Schema.Builder builder = Schema.builder();
|
221
|
+
try {
|
222
|
+
JsonNode schemaNode = getSchema(catalog);
|
223
|
+
RecordParser parser = ParserGenerator.generateParser(schemaNode);
|
224
|
+
if (!(parser instanceof ObjectParser)) {
|
225
|
+
throw new DataException("invalid schema");
|
226
|
+
}
|
227
|
+
ObjectParser toplevelParser = (ObjectParser) parser;
|
228
|
+
for (Map.Entry<String, RecordParser> entry : toplevelParser.properties().entrySet()) {
|
229
|
+
String colName = entry.getKey();
|
230
|
+
RecordParser colParser = entry.getValue();
|
231
|
+
builder.add(colName, colParser.embulkType());
|
232
|
+
}
|
233
|
+
}
|
234
|
+
catch (Exception e) {
|
235
|
+
throw new ConfigException(e.getMessage());
|
236
|
+
}
|
237
|
+
return builder.build();
|
238
|
+
}
|
239
|
+
|
240
|
+
private void setColumn(Column column, Map<String, Value> mapValue, PageBuilder pageBuilder) {
|
241
|
+
String key = column.getName();
|
242
|
+
Value val = mapValue.get(key);
|
243
|
+
if (val.isStringValue()) {
|
244
|
+
pageBuilder.setString(column, val.asStringValue().asString());
|
245
|
+
}
|
246
|
+
else if (val.isIntegerValue()) {
|
247
|
+
pageBuilder.setLong(column, val.asIntegerValue().asLong());
|
248
|
+
}
|
249
|
+
else if (val.isFloatValue()) {
|
250
|
+
pageBuilder.setDouble(column, val.asFloatValue().toDouble());
|
251
|
+
}
|
252
|
+
else if (val.isBooleanValue()) {
|
253
|
+
pageBuilder.setBoolean(column, val.asBooleanValue().getBoolean());
|
254
|
+
}
|
255
|
+
else if (val.isMapValue() || val.isArrayValue()) {
|
256
|
+
pageBuilder.setJson(column, val);
|
257
|
+
}
|
258
|
+
else if (val.isNilValue()) {
|
259
|
+
pageBuilder.setNull(column);
|
260
|
+
}
|
261
|
+
else {
|
262
|
+
throw new DataException("invalid type of record for column: " + key + ": "+ val.getValueType());
|
263
|
+
}
|
264
|
+
}
|
265
|
+
}
|