embulk-output-multi 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/.circleci/config.yml +42 -0
- data/.gitignore +13 -0
- data/LICENSE +21 -0
- data/README.md +43 -0
- data/build.gradle +44 -0
- data/gradle.properties +1 -0
- data/gradle/dependency-locks/compileClasspath.lockfile +35 -0
- data/gradle/dependency-locks/testCompileClasspath.lockfile +48 -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/output/multi.rb +3 -0
- data/settings.gradle +10 -0
- data/src/main/java/org/embulk/output/multi/MultiOutputPlugin.java +347 -0
- data/src/test/java/org/embulk/output/multi/TestMultiOutputPlugin.java +127 -0
- data/src/test/resources/yaml/in_base.yml +8 -0
- data/src/test/resources/yaml/out_base.yml +7 -0
- metadata +91 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cdd6eeb1ca104b3fca01e57c679d0d52c5e483a2
|
4
|
+
data.tar.gz: 03fdeb08212e6769baa2652f149d2e2b27a97f81
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4536fd0e3f5c31acbdcd78931282b776dbe6842f7c5e26ecaeb5c0bd899cf85d9979db6453f6bc8aa77277a0b0d6ec4aa58748e6c95e35a382de739bbe60bebe
|
7
|
+
data.tar.gz: 890c8205e4b6ab4970bdfcb5b2faa9dc47dc5bd367a1c0cc6603c2dc9d4c4dab17724b2064d2bfbef7e078a0063e9971d31971f774f07dd3db7486ac97d467e1
|
@@ -0,0 +1,42 @@
|
|
1
|
+
version: 2
|
2
|
+
jobs:
|
3
|
+
build:
|
4
|
+
docker:
|
5
|
+
- image: openjdk:8
|
6
|
+
working_directory: ~/embulk-filter-hash
|
7
|
+
steps:
|
8
|
+
- checkout
|
9
|
+
- restore_cache:
|
10
|
+
keys:
|
11
|
+
- v1-gradle-{{ checksum "gradle/dependency-locks/compileClasspath.lockfile" }}-{{ checksum "gradle/dependency-locks/testCompileClasspath.lockfile" }}
|
12
|
+
- v1-gradle-
|
13
|
+
- run: ./gradlew check --info
|
14
|
+
- run:
|
15
|
+
name: Save test results
|
16
|
+
command: |
|
17
|
+
mkdir -p ~/junit/
|
18
|
+
find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \;
|
19
|
+
when: always
|
20
|
+
- store_test_results:
|
21
|
+
path: ~/junit
|
22
|
+
- store_artifacts:
|
23
|
+
path: ~/junit
|
24
|
+
|
25
|
+
- save_cache:
|
26
|
+
paths:
|
27
|
+
- "~/.gradle"
|
28
|
+
- "~/.m2"
|
29
|
+
key: v1-gradle-{{ checksum "gradle/dependency-locks/compileClasspath.lockfile" }}-{{ checksum "gradle/dependency-locks/testCompileClasspath.lockfile" }}
|
30
|
+
|
31
|
+
- deploy:
|
32
|
+
name: Push Gem to RubyGems.org and bump up
|
33
|
+
command: |
|
34
|
+
if [ "${CIRCLE_BRANCH}" == "release" ]; then
|
35
|
+
mkdir -p ~/.gem
|
36
|
+
curl -f -u ${RUBYGEMS_USER}:${RUBYGEMS_PASSWORD} https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials
|
37
|
+
git config --global user.email "shiketaudonko41@gmail.com"
|
38
|
+
git config --global user.name "kamatama41"
|
39
|
+
git checkout master
|
40
|
+
git reset --hard origin/master
|
41
|
+
./gradlew release -Prelease.useAutomaticVersion=true
|
42
|
+
fi
|
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2019 Shinichi Ishimura
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Multi output plugin for Embulk
|
2
|
+
|
3
|
+
This plugin can copies an output to multiple destinations.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
* **Plugin type**: output
|
8
|
+
* **Load all or nothing**: no
|
9
|
+
* **Resume supported**: yes
|
10
|
+
* **Cleanup supported**: yes
|
11
|
+
|
12
|
+
## Configuration
|
13
|
+
|
14
|
+
- **outputs**: Configuration of output plugins (array, required)
|
15
|
+
|
16
|
+
## Example
|
17
|
+
|
18
|
+
```yaml
|
19
|
+
out:
|
20
|
+
type: copy
|
21
|
+
outputs:
|
22
|
+
# Output to stdout
|
23
|
+
- type: stdout
|
24
|
+
# Output to file as CSV
|
25
|
+
- type: file
|
26
|
+
path_prefix: out_file_
|
27
|
+
file_ext: csv
|
28
|
+
formatter:
|
29
|
+
type: csv
|
30
|
+
# Output to file as TSV
|
31
|
+
- type: file
|
32
|
+
path_prefix: out_file_
|
33
|
+
file_ext: tsv
|
34
|
+
formatter:
|
35
|
+
type: csv
|
36
|
+
delimiter: "\t"
|
37
|
+
```
|
38
|
+
|
39
|
+
## Build
|
40
|
+
|
41
|
+
```
|
42
|
+
$ ./gradlew gem # -t to watch change of files and rebuild continuously
|
43
|
+
```
|
data/build.gradle
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
plugins {
|
2
|
+
id "com.github.kamatama41.embulk" version "0.4.0"
|
3
|
+
id "net.researchgate.release" version "2.8.0"
|
4
|
+
}
|
5
|
+
|
6
|
+
sourceCompatibility = 1.8
|
7
|
+
targetCompatibility = 1.8
|
8
|
+
|
9
|
+
repositories {
|
10
|
+
maven { url 'http://kamatama41.github.com/maven-repository/repository' }
|
11
|
+
}
|
12
|
+
|
13
|
+
configurations {
|
14
|
+
[compileClasspath, testCompileClasspath].each {
|
15
|
+
it.resolutionStrategy.activateDependencyLocking()
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
dependencies {
|
20
|
+
testImplementation "org.embulk:embulk-standards:0.9.15"
|
21
|
+
testImplementation "org.embulk:embulk-test:0.9.15"
|
22
|
+
testImplementation "com.github.kamatama41:embulk-test-helpers:0.7.1"
|
23
|
+
testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.2"
|
24
|
+
testImplementation "org.junit.jupiter:junit-jupiter-engine:5.3.2"
|
25
|
+
}
|
26
|
+
|
27
|
+
test {
|
28
|
+
useJUnitPlatform()
|
29
|
+
}
|
30
|
+
|
31
|
+
embulk {
|
32
|
+
version = "0.9.15"
|
33
|
+
category = "output"
|
34
|
+
name = "multi"
|
35
|
+
authors = ["Shinichi ISHIMURA"]
|
36
|
+
email = "shiketaudonko41@gmail.com"
|
37
|
+
description = "Dump records into multiple outputs"
|
38
|
+
homepage = "https://github.com/kamatama41/embulk-output-multi"
|
39
|
+
}
|
40
|
+
|
41
|
+
release {
|
42
|
+
git { requireBranch = 'master' }
|
43
|
+
}
|
44
|
+
afterReleaseBuild.dependsOn gemPush
|
data/gradle.properties
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
version=0.1.0
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# This is a Gradle generated file for dependency locking.
|
2
|
+
# Manual edits can break the build and are not advised.
|
3
|
+
# This file is expected to be part of source control.
|
4
|
+
aopalliance:aopalliance:1.0
|
5
|
+
com.fasterxml.jackson.core:jackson-annotations:2.6.7
|
6
|
+
com.fasterxml.jackson.core:jackson-core:2.6.7
|
7
|
+
com.fasterxml.jackson.core:jackson-databind:2.6.7
|
8
|
+
com.fasterxml.jackson.datatype:jackson-datatype-guava:2.6.7
|
9
|
+
com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.6.7
|
10
|
+
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.6.7
|
11
|
+
com.fasterxml.jackson.module:jackson-module-guice:2.6.7
|
12
|
+
com.google.code.findbugs:annotations:3.0.0
|
13
|
+
com.google.guava:guava:18.0
|
14
|
+
com.google.inject.extensions:guice-multibindings:4.0
|
15
|
+
com.google.inject:guice:4.0
|
16
|
+
com.ibm.icu:icu4j:54.1.1
|
17
|
+
commons-beanutils:commons-beanutils-core:1.8.3
|
18
|
+
commons-cli:commons-cli:1.3.1
|
19
|
+
commons-collections:commons-collections:3.2.1
|
20
|
+
commons-lang:commons-lang:2.4
|
21
|
+
io.airlift:slice:0.9
|
22
|
+
io.netty:netty-buffer:4.0.44.Final
|
23
|
+
io.netty:netty-common:4.0.44.Final
|
24
|
+
javax.inject:javax.inject:1
|
25
|
+
javax.validation:validation-api:1.1.0.Final
|
26
|
+
joda-time:joda-time:2.9.2
|
27
|
+
org.apache.bval:bval-core:0.5
|
28
|
+
org.apache.bval:bval-jsr303:0.5
|
29
|
+
org.apache.commons:commons-lang3:3.4
|
30
|
+
org.apache.velocity:velocity:1.7
|
31
|
+
org.embulk:embulk-core:0.9.15
|
32
|
+
org.jruby:jruby-complete:9.1.15.0
|
33
|
+
org.msgpack:msgpack-core:0.8.11
|
34
|
+
org.slf4j:slf4j-api:1.7.12
|
35
|
+
org.yaml:snakeyaml:1.18
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# This is a Gradle generated file for dependency locking.
|
2
|
+
# Manual edits can break the build and are not advised.
|
3
|
+
# This file is expected to be part of source control.
|
4
|
+
aopalliance:aopalliance:1.0
|
5
|
+
com.fasterxml.jackson.core:jackson-annotations:2.6.7
|
6
|
+
com.fasterxml.jackson.core:jackson-core:2.6.7
|
7
|
+
com.fasterxml.jackson.core:jackson-databind:2.6.7
|
8
|
+
com.fasterxml.jackson.datatype:jackson-datatype-guava:2.6.7
|
9
|
+
com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.6.7
|
10
|
+
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.6.7
|
11
|
+
com.fasterxml.jackson.module:jackson-module-guice:2.6.7
|
12
|
+
com.github.kamatama41:embulk-test-helpers:0.7.1
|
13
|
+
com.google.code.findbugs:annotations:3.0.0
|
14
|
+
com.google.guava:guava:18.0
|
15
|
+
com.google.inject.extensions:guice-multibindings:4.0
|
16
|
+
com.google.inject:guice:4.0
|
17
|
+
com.ibm.icu:icu4j:54.1.1
|
18
|
+
commons-beanutils:commons-beanutils-core:1.8.3
|
19
|
+
commons-cli:commons-cli:1.3.1
|
20
|
+
commons-collections:commons-collections:3.2.1
|
21
|
+
commons-lang:commons-lang:2.4
|
22
|
+
io.airlift:slice:0.9
|
23
|
+
io.netty:netty-buffer:4.0.44.Final
|
24
|
+
io.netty:netty-common:4.0.44.Final
|
25
|
+
javax.inject:javax.inject:1
|
26
|
+
javax.validation:validation-api:1.1.0.Final
|
27
|
+
joda-time:joda-time:2.9.2
|
28
|
+
junit:junit:4.12
|
29
|
+
org.apache.bval:bval-core:0.5
|
30
|
+
org.apache.bval:bval-jsr303:0.5
|
31
|
+
org.apache.commons:commons-compress:1.10
|
32
|
+
org.apache.commons:commons-lang3:3.4
|
33
|
+
org.apache.velocity:velocity:1.7
|
34
|
+
org.apiguardian:apiguardian-api:1.0.0
|
35
|
+
org.embulk:embulk-core:0.9.15
|
36
|
+
org.embulk:embulk-standards:0.9.15
|
37
|
+
org.embulk:embulk-test:0.9.15
|
38
|
+
org.hamcrest:hamcrest-core:1.3
|
39
|
+
org.hamcrest:hamcrest-library:1.3
|
40
|
+
org.jruby:jruby-complete:9.1.15.0
|
41
|
+
org.junit.jupiter:junit-jupiter-api:5.3.2
|
42
|
+
org.junit.jupiter:junit-jupiter-engine:5.3.2
|
43
|
+
org.junit.platform:junit-platform-commons:1.3.2
|
44
|
+
org.junit.platform:junit-platform-engine:1.3.2
|
45
|
+
org.msgpack:msgpack-core:0.8.11
|
46
|
+
org.opentest4j:opentest4j:1.1.1
|
47
|
+
org.slf4j:slf4j-api:1.7.12
|
48
|
+
org.yaml:snakeyaml:1.18
|
Binary file
|
data/gradlew
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
#!/usr/bin/env sh
|
2
|
+
|
3
|
+
##############################################################################
|
4
|
+
##
|
5
|
+
## Gradle start up script for UN*X
|
6
|
+
##
|
7
|
+
##############################################################################
|
8
|
+
|
9
|
+
# Attempt to set APP_HOME
|
10
|
+
# Resolve links: $0 may be a link
|
11
|
+
PRG="$0"
|
12
|
+
# Need this for relative symlinks.
|
13
|
+
while [ -h "$PRG" ] ; do
|
14
|
+
ls=`ls -ld "$PRG"`
|
15
|
+
link=`expr "$ls" : '.*-> \(.*\)$'`
|
16
|
+
if expr "$link" : '/.*' > /dev/null; then
|
17
|
+
PRG="$link"
|
18
|
+
else
|
19
|
+
PRG=`dirname "$PRG"`"/$link"
|
20
|
+
fi
|
21
|
+
done
|
22
|
+
SAVED="`pwd`"
|
23
|
+
cd "`dirname \"$PRG\"`/" >/dev/null
|
24
|
+
APP_HOME="`pwd -P`"
|
25
|
+
cd "$SAVED" >/dev/null
|
26
|
+
|
27
|
+
APP_NAME="Gradle"
|
28
|
+
APP_BASE_NAME=`basename "$0"`
|
29
|
+
|
30
|
+
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
31
|
+
DEFAULT_JVM_OPTS=""
|
32
|
+
|
33
|
+
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
34
|
+
MAX_FD="maximum"
|
35
|
+
|
36
|
+
warn () {
|
37
|
+
echo "$*"
|
38
|
+
}
|
39
|
+
|
40
|
+
die () {
|
41
|
+
echo
|
42
|
+
echo "$*"
|
43
|
+
echo
|
44
|
+
exit 1
|
45
|
+
}
|
46
|
+
|
47
|
+
# OS specific support (must be 'true' or 'false').
|
48
|
+
cygwin=false
|
49
|
+
msys=false
|
50
|
+
darwin=false
|
51
|
+
nonstop=false
|
52
|
+
case "`uname`" in
|
53
|
+
CYGWIN* )
|
54
|
+
cygwin=true
|
55
|
+
;;
|
56
|
+
Darwin* )
|
57
|
+
darwin=true
|
58
|
+
;;
|
59
|
+
MINGW* )
|
60
|
+
msys=true
|
61
|
+
;;
|
62
|
+
NONSTOP* )
|
63
|
+
nonstop=true
|
64
|
+
;;
|
65
|
+
esac
|
66
|
+
|
67
|
+
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
68
|
+
|
69
|
+
# Determine the Java command to use to start the JVM.
|
70
|
+
if [ -n "$JAVA_HOME" ] ; then
|
71
|
+
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
72
|
+
# IBM's JDK on AIX uses strange locations for the executables
|
73
|
+
JAVACMD="$JAVA_HOME/jre/sh/java"
|
74
|
+
else
|
75
|
+
JAVACMD="$JAVA_HOME/bin/java"
|
76
|
+
fi
|
77
|
+
if [ ! -x "$JAVACMD" ] ; then
|
78
|
+
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
79
|
+
|
80
|
+
Please set the JAVA_HOME variable in your environment to match the
|
81
|
+
location of your Java installation."
|
82
|
+
fi
|
83
|
+
else
|
84
|
+
JAVACMD="java"
|
85
|
+
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
86
|
+
|
87
|
+
Please set the JAVA_HOME variable in your environment to match the
|
88
|
+
location of your Java installation."
|
89
|
+
fi
|
90
|
+
|
91
|
+
# Increase the maximum file descriptors if we can.
|
92
|
+
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
93
|
+
MAX_FD_LIMIT=`ulimit -H -n`
|
94
|
+
if [ $? -eq 0 ] ; then
|
95
|
+
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
96
|
+
MAX_FD="$MAX_FD_LIMIT"
|
97
|
+
fi
|
98
|
+
ulimit -n $MAX_FD
|
99
|
+
if [ $? -ne 0 ] ; then
|
100
|
+
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
101
|
+
fi
|
102
|
+
else
|
103
|
+
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
104
|
+
fi
|
105
|
+
fi
|
106
|
+
|
107
|
+
# For Darwin, add options to specify how the application appears in the dock
|
108
|
+
if $darwin; then
|
109
|
+
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
110
|
+
fi
|
111
|
+
|
112
|
+
# For Cygwin, switch paths to Windows format before running java
|
113
|
+
if $cygwin ; then
|
114
|
+
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
115
|
+
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
116
|
+
JAVACMD=`cygpath --unix "$JAVACMD"`
|
117
|
+
|
118
|
+
# We build the pattern for arguments to be converted via cygpath
|
119
|
+
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
120
|
+
SEP=""
|
121
|
+
for dir in $ROOTDIRSRAW ; do
|
122
|
+
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
123
|
+
SEP="|"
|
124
|
+
done
|
125
|
+
OURCYGPATTERN="(^($ROOTDIRS))"
|
126
|
+
# Add a user-defined pattern to the cygpath arguments
|
127
|
+
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
128
|
+
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
129
|
+
fi
|
130
|
+
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
131
|
+
i=0
|
132
|
+
for arg in "$@" ; do
|
133
|
+
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
134
|
+
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
135
|
+
|
136
|
+
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
137
|
+
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
138
|
+
else
|
139
|
+
eval `echo args$i`="\"$arg\""
|
140
|
+
fi
|
141
|
+
i=$((i+1))
|
142
|
+
done
|
143
|
+
case $i in
|
144
|
+
(0) set -- ;;
|
145
|
+
(1) set -- "$args0" ;;
|
146
|
+
(2) set -- "$args0" "$args1" ;;
|
147
|
+
(3) set -- "$args0" "$args1" "$args2" ;;
|
148
|
+
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
149
|
+
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
150
|
+
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
151
|
+
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
152
|
+
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
153
|
+
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
154
|
+
esac
|
155
|
+
fi
|
156
|
+
|
157
|
+
# Escape application args
|
158
|
+
save () {
|
159
|
+
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
160
|
+
echo " "
|
161
|
+
}
|
162
|
+
APP_ARGS=$(save "$@")
|
163
|
+
|
164
|
+
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
165
|
+
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
166
|
+
|
167
|
+
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
168
|
+
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
169
|
+
cd "$(dirname "$0")"
|
170
|
+
fi
|
171
|
+
|
172
|
+
exec "$JAVACMD" "$@"
|
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
|
data/settings.gradle
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
/*
|
2
|
+
* This file was generated by the Gradle 'init' task.
|
3
|
+
*
|
4
|
+
* The settings file is used to specify which projects to include in your build.
|
5
|
+
*
|
6
|
+
* Detailed information about configuring a multi-project build in Gradle can be found
|
7
|
+
* in the user guide at https://docs.gradle.org/4.5.1/userguide/multi_project_builds.html
|
8
|
+
*/
|
9
|
+
|
10
|
+
rootProject.name = 'embulk-output-multi'
|
@@ -0,0 +1,347 @@
|
|
1
|
+
package org.embulk.output.multi;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.annotation.JsonCreator;
|
4
|
+
import com.fasterxml.jackson.annotation.JsonProperty;
|
5
|
+
import org.embulk.config.Config;
|
6
|
+
import org.embulk.config.ConfigDefault;
|
7
|
+
import org.embulk.config.ConfigDiff;
|
8
|
+
import org.embulk.config.ConfigException;
|
9
|
+
import org.embulk.config.ConfigSource;
|
10
|
+
import org.embulk.config.Task;
|
11
|
+
import org.embulk.config.TaskReport;
|
12
|
+
import org.embulk.config.TaskSource;
|
13
|
+
import org.embulk.plugin.PluginType;
|
14
|
+
import org.embulk.spi.Buffer;
|
15
|
+
import org.embulk.spi.Exec;
|
16
|
+
import org.embulk.spi.ExecSession;
|
17
|
+
import org.embulk.spi.OutputPlugin;
|
18
|
+
import org.embulk.spi.Page;
|
19
|
+
import org.embulk.spi.Schema;
|
20
|
+
import org.embulk.spi.TransactionalPageOutput;
|
21
|
+
import org.slf4j.Logger;
|
22
|
+
import org.slf4j.LoggerFactory;
|
23
|
+
|
24
|
+
import java.util.ArrayList;
|
25
|
+
import java.util.Arrays;
|
26
|
+
import java.util.List;
|
27
|
+
import java.util.Optional;
|
28
|
+
import java.util.concurrent.Callable;
|
29
|
+
import java.util.concurrent.CancellationException;
|
30
|
+
import java.util.concurrent.CountDownLatch;
|
31
|
+
import java.util.concurrent.ExecutionException;
|
32
|
+
import java.util.concurrent.ExecutorService;
|
33
|
+
import java.util.concurrent.Executors;
|
34
|
+
import java.util.concurrent.Future;
|
35
|
+
import java.util.function.Function;
|
36
|
+
|
37
|
+
public class MultiOutputPlugin implements OutputPlugin {
|
38
|
+
public interface PluginTask extends Task {
|
39
|
+
@Config("outputs")
|
40
|
+
List<ConfigSource> getOutputConfigs();
|
41
|
+
|
42
|
+
@Config(CONFIG_NAME_OUTPUT_CONFIG_DIFFS)
|
43
|
+
@ConfigDefault("null")
|
44
|
+
Optional<List<ConfigDiff>> getOutputConfigDiffs();
|
45
|
+
|
46
|
+
List<TaskSource> getTaskSources();
|
47
|
+
void setTaskSources(List<TaskSource> taskSources);
|
48
|
+
}
|
49
|
+
|
50
|
+
private static final String CONFIG_NAME_OUTPUT_CONFIG_DIFFS = "output_config_diffs";
|
51
|
+
private static final String CONFIG_NAME_OUTPUT_TASK_REPORTS = "output_task_reports";
|
52
|
+
private static final Logger LOGGER = LoggerFactory.getLogger(MultiOutputPlugin.class);
|
53
|
+
private final ExecutorService executorService;
|
54
|
+
|
55
|
+
public MultiOutputPlugin() {
|
56
|
+
this.executorService = Executors.newCachedThreadPool();
|
57
|
+
}
|
58
|
+
|
59
|
+
@Override
|
60
|
+
public ConfigDiff transaction(ConfigSource config, Schema schema, int taskCount, OutputPlugin.Control control) {
|
61
|
+
final PluginTask task = config.loadConfig(PluginTask.class);
|
62
|
+
if (task.getOutputConfigs().isEmpty()) {
|
63
|
+
throw new ConfigException("'outputs' must have more than than or equals to 1 element.");
|
64
|
+
}
|
65
|
+
final ExecSession session = Exec.session();
|
66
|
+
final RunControlTask controlTask = new RunControlTask(task, control, executorService);
|
67
|
+
controlTask.runAsynchronously();
|
68
|
+
return buildConfigDiff(mapWithPluginDelegate(task, session, delegate ->
|
69
|
+
delegate.transaction(schema, taskCount, controlTask)
|
70
|
+
));
|
71
|
+
}
|
72
|
+
|
73
|
+
@Override
|
74
|
+
public ConfigDiff resume(TaskSource taskSource, Schema schema, int taskCount, OutputPlugin.Control control) {
|
75
|
+
final PluginTask task = taskSource.loadTask(PluginTask.class);
|
76
|
+
final ExecSession session = Exec.session();
|
77
|
+
final RunControlTask controlTask = new RunControlTask(task, control, executorService);
|
78
|
+
controlTask.runAsynchronously();
|
79
|
+
return buildConfigDiff(mapWithPluginDelegate(task, session, delegate ->
|
80
|
+
delegate.resume(schema, taskCount, controlTask)
|
81
|
+
));
|
82
|
+
}
|
83
|
+
|
84
|
+
@Override
|
85
|
+
public void cleanup(TaskSource taskSource, Schema schema, int taskCount, List<TaskReport> successTaskReports) {
|
86
|
+
final PluginTask task = taskSource.loadTask(PluginTask.class);
|
87
|
+
final ExecSession session = Exec.session();
|
88
|
+
mapWithPluginDelegate(task, session, delegate -> {
|
89
|
+
delegate.cleanup(schema, taskCount, successTaskReports);
|
90
|
+
return null;
|
91
|
+
});
|
92
|
+
}
|
93
|
+
|
94
|
+
@Override
|
95
|
+
public TransactionalPageOutput open(TaskSource taskSource, Schema schema, int taskIndex) {
|
96
|
+
final PluginTask task = taskSource.loadTask(PluginTask.class);
|
97
|
+
final ExecSession session = Exec.session();
|
98
|
+
final List<TransactionalPageOutput> delegates = mapWithPluginDelegate(task, session, delegate ->
|
99
|
+
delegate.open(schema, taskIndex)
|
100
|
+
);
|
101
|
+
|
102
|
+
return new TransactionalPageOutput() {
|
103
|
+
@Override
|
104
|
+
public void add(Page original) {
|
105
|
+
final Buffer originalBuffer = original.buffer();
|
106
|
+
for (TransactionalPageOutput output : delegates) {
|
107
|
+
final Buffer copiedBuffer = Buffer.wrap(originalBuffer.array());
|
108
|
+
copiedBuffer.offset(originalBuffer.offset());
|
109
|
+
copiedBuffer.limit(originalBuffer.limit());
|
110
|
+
|
111
|
+
final Page copiedPage = Page.wrap(copiedBuffer);
|
112
|
+
copiedPage.setStringReferences(new ArrayList<>(original.getStringReferences()));
|
113
|
+
copiedPage.setValueReferences(new ArrayList<>(original.getValueReferences()));
|
114
|
+
|
115
|
+
output.add(copiedPage);
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
@Override
|
120
|
+
public void finish() {
|
121
|
+
for (TransactionalPageOutput output : delegates) {
|
122
|
+
output.finish();
|
123
|
+
}
|
124
|
+
}
|
125
|
+
|
126
|
+
@Override
|
127
|
+
public void close() {
|
128
|
+
for (TransactionalPageOutput output : delegates) {
|
129
|
+
output.close();
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
@Override
|
134
|
+
public void abort() {
|
135
|
+
for (TransactionalPageOutput output : delegates) {
|
136
|
+
output.abort();
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
@Override
|
141
|
+
public TaskReport commit() {
|
142
|
+
final TaskReport report = Exec.newTaskReport();
|
143
|
+
final List<TaskReport> reports = new ArrayList<>();
|
144
|
+
for (TransactionalPageOutput output : delegates) {
|
145
|
+
reports.add(output.commit());
|
146
|
+
}
|
147
|
+
report.set(CONFIG_NAME_OUTPUT_TASK_REPORTS, new TaskReports(reports));
|
148
|
+
return report;
|
149
|
+
}
|
150
|
+
};
|
151
|
+
}
|
152
|
+
|
153
|
+
private static class RunControlTask implements Callable<List<TaskReport>> {
|
154
|
+
private final PluginTask task;
|
155
|
+
private final OutputPlugin.Control control;
|
156
|
+
private final ExecutorService executorService;
|
157
|
+
private final CountDownLatch latch;
|
158
|
+
private final TaskSource[] taskSources;
|
159
|
+
private Future<List<TaskReport>> future;
|
160
|
+
|
161
|
+
RunControlTask(PluginTask task, OutputPlugin.Control control, ExecutorService executorService) {
|
162
|
+
this.task = task;
|
163
|
+
this.control = control;
|
164
|
+
this.executorService = executorService;
|
165
|
+
this.latch = new CountDownLatch(task.getOutputConfigs().size());
|
166
|
+
this.taskSources = new TaskSource[task.getOutputConfigs().size()];
|
167
|
+
}
|
168
|
+
|
169
|
+
@Override
|
170
|
+
public List<TaskReport> call() throws Exception {
|
171
|
+
latch.await();
|
172
|
+
task.setTaskSources(Arrays.asList(taskSources));
|
173
|
+
return control.run(task.dump());
|
174
|
+
}
|
175
|
+
|
176
|
+
void runAsynchronously() {
|
177
|
+
future = executorService.submit(this);
|
178
|
+
}
|
179
|
+
|
180
|
+
void cancel() {
|
181
|
+
future.cancel(true);
|
182
|
+
}
|
183
|
+
|
184
|
+
void addTaskSource(int index, TaskSource taskSource) {
|
185
|
+
taskSources[index] = taskSource;
|
186
|
+
latch.countDown();
|
187
|
+
}
|
188
|
+
|
189
|
+
List<TaskReport> waitAndGetResult() throws ExecutionException, InterruptedException {
|
190
|
+
return future.get();
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
private static class OutputPluginDelegate {
|
195
|
+
private final int pluginIndex;
|
196
|
+
private final PluginType type;
|
197
|
+
private final OutputPlugin plugin;
|
198
|
+
private final ConfigSource config;
|
199
|
+
private final TaskSource taskSource;
|
200
|
+
private final ExecutorService executorService;
|
201
|
+
|
202
|
+
OutputPluginDelegate(
|
203
|
+
int pluginIndex,
|
204
|
+
PluginType type,
|
205
|
+
OutputPlugin plugin,
|
206
|
+
ConfigSource config,
|
207
|
+
TaskSource taskSource,
|
208
|
+
ExecutorService executorService
|
209
|
+
) {
|
210
|
+
this.pluginIndex = pluginIndex;
|
211
|
+
this.type = type;
|
212
|
+
this.plugin = plugin;
|
213
|
+
this.config = config;
|
214
|
+
this.taskSource = taskSource;
|
215
|
+
this.executorService = executorService;
|
216
|
+
}
|
217
|
+
|
218
|
+
Future<ConfigDiff> transaction(Schema schema, int taskCount, RunControlTask controlTask) {
|
219
|
+
LOGGER.debug("Run transaction for {}", getPluginNameForLogging());
|
220
|
+
return executorService.submit(() -> {
|
221
|
+
try {
|
222
|
+
return plugin.transaction(config, schema, taskCount, new ControlDelegate(pluginIndex, controlTask));
|
223
|
+
} catch (CancellationException e) {
|
224
|
+
LOGGER.error("Canceled transaction for {} by other plugin's error", getPluginNameForLogging());
|
225
|
+
throw e;
|
226
|
+
} catch (Exception e) {
|
227
|
+
LOGGER.error("Transaction for {} failed.", getPluginNameForLogging(), e);
|
228
|
+
controlTask.cancel();
|
229
|
+
throw e;
|
230
|
+
}
|
231
|
+
});
|
232
|
+
}
|
233
|
+
|
234
|
+
Future<ConfigDiff> resume(Schema schema, int taskCount, RunControlTask controlTask) {
|
235
|
+
LOGGER.debug("Run resume for {}", getPluginNameForLogging());
|
236
|
+
return executorService.submit(() -> {
|
237
|
+
try {
|
238
|
+
return plugin.resume(taskSource, schema, taskCount, new ControlDelegate(pluginIndex, controlTask));
|
239
|
+
} catch (CancellationException e) {
|
240
|
+
LOGGER.error("Canceled resume for {} by other plugin's error", getPluginNameForLogging());
|
241
|
+
throw e;
|
242
|
+
} catch (Exception e) {
|
243
|
+
LOGGER.error("Resume for {} failed.", getPluginNameForLogging(), e);
|
244
|
+
controlTask.cancel();
|
245
|
+
throw e;
|
246
|
+
}
|
247
|
+
});
|
248
|
+
}
|
249
|
+
|
250
|
+
void cleanup(Schema schema, int taskCount, List<TaskReport> successTaskReports) {
|
251
|
+
LOGGER.debug("Run cleanup for {}", getPluginNameForLogging());
|
252
|
+
List<TaskReport> successReportsForPlugin = new ArrayList<>();
|
253
|
+
for (TaskReport successTaskReport : successTaskReports) {
|
254
|
+
final TaskReport report = successTaskReport.get(TaskReports.class, CONFIG_NAME_OUTPUT_TASK_REPORTS).get(pluginIndex);
|
255
|
+
successReportsForPlugin.add(report);
|
256
|
+
plugin.cleanup(taskSource, schema, taskCount, successReportsForPlugin);
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
TransactionalPageOutput open(Schema schema, int taskIndex) {
|
261
|
+
LOGGER.debug("Run open for {}", getPluginNameForLogging());
|
262
|
+
return plugin.open(taskSource, schema, taskIndex);
|
263
|
+
}
|
264
|
+
|
265
|
+
private String getPluginNameForLogging() {
|
266
|
+
return String.format("%s output plugin (pluginIndex: %s)", type.getName(), pluginIndex);
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
270
|
+
private static class ControlDelegate implements OutputPlugin.Control {
|
271
|
+
private final int pluginIndex;
|
272
|
+
private final RunControlTask controlTask;
|
273
|
+
|
274
|
+
ControlDelegate(int index, RunControlTask controlTask) {
|
275
|
+
this.pluginIndex = index;
|
276
|
+
this.controlTask = controlTask;
|
277
|
+
}
|
278
|
+
|
279
|
+
@Override
|
280
|
+
public List<TaskReport> run(TaskSource taskSource) {
|
281
|
+
controlTask.addTaskSource(pluginIndex, taskSource);
|
282
|
+
List<TaskReport> reports;
|
283
|
+
try {
|
284
|
+
reports = controlTask.waitAndGetResult();
|
285
|
+
} catch (InterruptedException | ExecutionException e) {
|
286
|
+
throw new RuntimeException(e);
|
287
|
+
}
|
288
|
+
|
289
|
+
final List<TaskReport> result = new ArrayList<>();
|
290
|
+
for (TaskReport taskReport : reports) {
|
291
|
+
final TaskReport report = taskReport.get(TaskReports.class, CONFIG_NAME_OUTPUT_TASK_REPORTS).get(pluginIndex);
|
292
|
+
result.add(report);
|
293
|
+
}
|
294
|
+
return result;
|
295
|
+
}
|
296
|
+
}
|
297
|
+
|
298
|
+
private static class TaskReports {
|
299
|
+
private final List<TaskReport> reports;
|
300
|
+
|
301
|
+
@JsonCreator
|
302
|
+
TaskReports(@JsonProperty("reports") List<TaskReport> reports) {
|
303
|
+
this.reports = reports;
|
304
|
+
}
|
305
|
+
|
306
|
+
@JsonProperty("reports")
|
307
|
+
List<TaskReport> getReports() {
|
308
|
+
return reports;
|
309
|
+
}
|
310
|
+
|
311
|
+
TaskReport get(int index) {
|
312
|
+
return reports.get(index);
|
313
|
+
}
|
314
|
+
}
|
315
|
+
|
316
|
+
private static ConfigDiff buildConfigDiff(List<Future<ConfigDiff>> runPluginTasks) {
|
317
|
+
final ConfigDiff configDiff = Exec.newConfigDiff();
|
318
|
+
List<ConfigDiff> configDiffs = new ArrayList<>();
|
319
|
+
for (Future<ConfigDiff> pluginTask : runPluginTasks) {
|
320
|
+
try {
|
321
|
+
configDiffs.add(pluginTask.get());
|
322
|
+
} catch (InterruptedException | ExecutionException e) {
|
323
|
+
throw new RuntimeException(e);
|
324
|
+
}
|
325
|
+
}
|
326
|
+
configDiff.set(CONFIG_NAME_OUTPUT_CONFIG_DIFFS, configDiffs);
|
327
|
+
return configDiff;
|
328
|
+
}
|
329
|
+
|
330
|
+
private <T> List<T> mapWithPluginDelegate(PluginTask task, ExecSession session, Function<OutputPluginDelegate, T> action) {
|
331
|
+
List<T> result = new ArrayList<>();
|
332
|
+
for (int pluginIndex = 0; pluginIndex < task.getOutputConfigs().size(); pluginIndex++) {
|
333
|
+
final ConfigSource config = task.getOutputConfigs().get(pluginIndex);
|
334
|
+
if (task.getOutputConfigDiffs().isPresent()) {
|
335
|
+
config.merge(task.getOutputConfigDiffs().get().get(pluginIndex));
|
336
|
+
}
|
337
|
+
final PluginType pluginType = config.get(PluginType.class, "type");
|
338
|
+
final OutputPlugin outputPlugin = session.newPlugin(OutputPlugin.class, pluginType);
|
339
|
+
TaskSource taskSource = null;
|
340
|
+
if (task.getTaskSources() != null) {
|
341
|
+
taskSource = task.getTaskSources().get(pluginIndex);
|
342
|
+
}
|
343
|
+
result.add(action.apply(new OutputPluginDelegate(pluginIndex, pluginType, outputPlugin, config, taskSource, executorService)));
|
344
|
+
}
|
345
|
+
return result;
|
346
|
+
}
|
347
|
+
}
|
@@ -0,0 +1,127 @@
|
|
1
|
+
package org.embulk.output.multi;
|
2
|
+
|
3
|
+
import org.embulk.config.ConfigSource;
|
4
|
+
import org.embulk.test.EmbulkPluginTest;
|
5
|
+
import org.embulk.test.EmbulkTest;
|
6
|
+
import org.embulk.test.Record;
|
7
|
+
import org.embulk.test.TestingEmbulk;
|
8
|
+
import org.junit.jupiter.api.Test;
|
9
|
+
|
10
|
+
import java.io.BufferedReader;
|
11
|
+
import java.io.ByteArrayInputStream;
|
12
|
+
import java.io.ByteArrayOutputStream;
|
13
|
+
import java.io.IOException;
|
14
|
+
import java.io.InputStreamReader;
|
15
|
+
import java.io.PrintStream;
|
16
|
+
import java.util.Arrays;
|
17
|
+
import java.util.Set;
|
18
|
+
|
19
|
+
import static java.util.stream.Collectors.joining;
|
20
|
+
import static java.util.stream.Collectors.toSet;
|
21
|
+
import static org.embulk.test.LocalObjectOutputPlugin.assertRecords;
|
22
|
+
import static org.embulk.test.Utils.configFromResource;
|
23
|
+
import static org.embulk.test.Utils.record;
|
24
|
+
import static org.junit.jupiter.api.Assertions.assertEquals;
|
25
|
+
|
26
|
+
@EmbulkTest(MultiOutputPlugin.class)
|
27
|
+
class TestMultiOutputPlugin extends EmbulkPluginTest {
|
28
|
+
@Test
|
29
|
+
void testMultipleOutputWorking() throws IOException {
|
30
|
+
final ConfigSource inConfig = configFromResource("yaml/in_base.yml");
|
31
|
+
final ConfigSource outConfig = configFromResource("yaml/out_base.yml");
|
32
|
+
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
33
|
+
System.setOut(new PrintStream(outputStream));
|
34
|
+
|
35
|
+
runOutput(inConfig, outConfig);
|
36
|
+
|
37
|
+
// Check local_object output plugin working
|
38
|
+
final Record[] values = new Record[]{record(1, "user1", 20), record(2, "user2", 21)};
|
39
|
+
assertRecords(values);
|
40
|
+
|
41
|
+
// Check stdout output plugin working
|
42
|
+
final BufferedReader reader = new BufferedReader(
|
43
|
+
new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray()))
|
44
|
+
);
|
45
|
+
// Header
|
46
|
+
assertEquals("id,name,age", reader.readLine());
|
47
|
+
// Values
|
48
|
+
Set<String> expected = Arrays.stream(values)
|
49
|
+
.map(v -> v.getValues().stream().map(Object::toString).collect(joining(",")))
|
50
|
+
.collect(toSet());
|
51
|
+
Set<String> actual = reader.lines().collect(toSet());
|
52
|
+
assertEquals(expected, actual);
|
53
|
+
}
|
54
|
+
|
55
|
+
@Test
|
56
|
+
void testMultipleOutputWorkingWithBigRecords() throws IOException {
|
57
|
+
final ConfigSource inConfig = configFromResource("yaml/in_base.yml");
|
58
|
+
// Set 1000 records
|
59
|
+
Object[][][] values = generateValues(1000);
|
60
|
+
inConfig.set("values", values);
|
61
|
+
|
62
|
+
final ConfigSource outConfig = configFromResource("yaml/out_base.yml");
|
63
|
+
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
64
|
+
System.setOut(new PrintStream(outputStream));
|
65
|
+
|
66
|
+
runOutput(inConfig, outConfig);
|
67
|
+
|
68
|
+
// Check local_object output plugin working
|
69
|
+
assertRecords(toRecords(values));
|
70
|
+
|
71
|
+
// Check stdout output plugin working
|
72
|
+
final BufferedReader reader = new BufferedReader(
|
73
|
+
new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray()))
|
74
|
+
);
|
75
|
+
// Header
|
76
|
+
assertEquals("id,name,age", reader.readLine());
|
77
|
+
// Values
|
78
|
+
Set<String> expected = Arrays.stream(values).map(v -> String.format("%s,%s,%s", v[0][0], v[0][1], v[0][2])).collect(toSet());
|
79
|
+
Set<String> actual = reader.lines().collect(toSet());
|
80
|
+
assertEquals(expected, actual);
|
81
|
+
}
|
82
|
+
|
83
|
+
@Test
|
84
|
+
void testOutputConfigDiffs() {
|
85
|
+
final ConfigSource inConfig = configFromResource("yaml/in_base.yml");
|
86
|
+
// Set 3 records
|
87
|
+
Object[][][] values = generateValues(3);
|
88
|
+
inConfig.set("values", values);
|
89
|
+
|
90
|
+
// Set incremental column
|
91
|
+
final ConfigSource outConfig = configFromResource("yaml/out_base.yml");
|
92
|
+
|
93
|
+
outConfig.set("incremental", true);
|
94
|
+
outConfig.set("incremental_column", "id");
|
95
|
+
|
96
|
+
// Run Embulk
|
97
|
+
TestingEmbulk.RunResult result = runOutput(inConfig, outConfig);
|
98
|
+
|
99
|
+
// All records should be loaded.
|
100
|
+
assertRecords(toRecords(values));
|
101
|
+
|
102
|
+
// Set 4 records
|
103
|
+
values = generateValues(4);
|
104
|
+
inConfig.set("values", values);
|
105
|
+
|
106
|
+
// Rerun with config diff
|
107
|
+
runOutput(inConfig, outConfig, result.getConfigDiff());
|
108
|
+
|
109
|
+
// Only 1 records (id=4) should be loaded.
|
110
|
+
final Record[] records = toRecords(values);
|
111
|
+
assertRecords(records[3]);
|
112
|
+
}
|
113
|
+
|
114
|
+
private static Object[][][] generateValues(int size) {
|
115
|
+
Object[][][] values = new Object[size][1][3];
|
116
|
+
for (int i = 0; i < size; i++) {
|
117
|
+
values[i][0][0] = i + 1; // ID
|
118
|
+
values[i][0][1] = "user" + (i + 1); // Name
|
119
|
+
values[i][0][2] = 20; // Age
|
120
|
+
}
|
121
|
+
return values;
|
122
|
+
}
|
123
|
+
|
124
|
+
private static Record[] toRecords(Object[][][] values) {
|
125
|
+
return Arrays.stream(values).map(v -> record(v[0])).toArray(Record[]::new);
|
126
|
+
}
|
127
|
+
}
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: embulk-output-multi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Shinichi ISHIMURA
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-03-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.0'
|
19
|
+
name: bundler
|
20
|
+
prerelease: false
|
21
|
+
type: :development
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '10.0'
|
33
|
+
name: rake
|
34
|
+
prerelease: false
|
35
|
+
type: :development
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Dump records into multiple outputs
|
42
|
+
email:
|
43
|
+
- shiketaudonko41@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".circleci/config.yml"
|
49
|
+
- ".gitignore"
|
50
|
+
- LICENSE
|
51
|
+
- README.md
|
52
|
+
- build.gradle
|
53
|
+
- classpath/embulk-output-multi-0.1.0.jar
|
54
|
+
- gradle.properties
|
55
|
+
- gradle/dependency-locks/compileClasspath.lockfile
|
56
|
+
- gradle/dependency-locks/testCompileClasspath.lockfile
|
57
|
+
- gradle/wrapper/gradle-wrapper.jar
|
58
|
+
- gradle/wrapper/gradle-wrapper.properties
|
59
|
+
- gradlew
|
60
|
+
- gradlew.bat
|
61
|
+
- lib/embulk/output/multi.rb
|
62
|
+
- settings.gradle
|
63
|
+
- src/main/java/org/embulk/output/multi/MultiOutputPlugin.java
|
64
|
+
- src/test/java/org/embulk/output/multi/TestMultiOutputPlugin.java
|
65
|
+
- src/test/resources/yaml/in_base.yml
|
66
|
+
- src/test/resources/yaml/out_base.yml
|
67
|
+
homepage: https://github.com/kamatama41/embulk-output-multi
|
68
|
+
licenses:
|
69
|
+
- MIT
|
70
|
+
metadata: {}
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 2.6.8
|
88
|
+
signing_key:
|
89
|
+
specification_version: 4
|
90
|
+
summary: Multi output plugin for Embulk
|
91
|
+
test_files: []
|