Skip to content

Instantly share code, notes, and snippets.

@hiroyuki-sato
Last active March 10, 2021 04:52
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hiroyuki-sato/b9d76bd882efa6e29ae16e01396c82ae to your computer and use it in GitHub Desktop.
Save hiroyuki-sato/b9d76bd882efa6e29ae16e01396c82ae to your computer and use it in GitHub Desktop.
embulk plugin developer guide (WIP)

注意: まだ書きかけです。

プラグインを開発する

このガイドでは、Embulkのプラグインを開発する方法を説明します。

新しいプラグインを作ることで、新規のクラウドサービスからデータを取得したり、新しいデータウェアハウスシステムにデータを出力できるようになります。

入力から出力まで全てのプラグインを開発する必要はありません。Embulkには既に多くのプラグインが提供されています。既存のプラグインでは足りない部分だけを開発すれば良いのです。

使用したEmbulkのバージョン

本ガイドで使用したEmbulkのバージョンは、0.8.9です。バージョン番号が異なる場合は、こちらのガイドどおりに動作しないことがあるかもしれません。その際はこちらのガイドを適宜修正するので、問題点をお知らせください。

Embulkプラグインの概要

開発言語(Java, JRuby)

Embulkのプラグインは、Javaまたは、JRuby(Java版Ruby)で開発することができます。

備考: いくつかのプラグインは、Scala(Java VM上で動く関数型言語)を使って作成されています。プラグインの開発にScalaを使いたい場合は、Qiitaの記事を参考にしてください。

開発できるプラグイン

現在Java, Rubyそれぞれの言語を使って作成できるプラグインは次のとおりです。

plugin Java Ruby
input
file-input ×
output
file-output ×
parser
formatter
decoder ×
encoder ×
executor

inputとinput-file, outputとoutput-fileの違い

開発に必要な環境

OS

プラグイン開発に利用出来るOSは、LinuxやOSX等のUNIX互換のシステムを利用できます。

備考: Windowsを使って開発を行うことも可能ですが、幾つかの制限事項があります。Windowsを使っている人は、Cygwinを導入して開発をしていることが多いようです。

JDK

Java7以上のJDK SEが必要です。

Rubygemのアカウント

プラグインを作成すると、embulk-TYPE-NAME-X.Y.Z.gemというファイルが作成されます。TYPEやNAMEの内容はつぎのとおりです。

  • TYPE: inputやoutputなどプラグインの種類名
  • NAME: 作成したプラグインの名前(例、csv, bigquery)
  • X.Y.Z: バージョン番号

gemファイルは、Rubyの世界で利用しているパッケージ管理システムです。gemパッケージを公開するんのがRubygems(https://rubygems.org)サイトです。 開発したプラグインを公開するためには、アカウントの作成が必要です。

諸般の事情で、作成したプラグインを限定した環境でのみ利用する場合は、Gem in a Box(https://github.com/geminabox/geminabox) というRubyのパッケージを使って、プライベートはRubygems環境を構築することができます。

チュートリアル

Java

embulk newコマンドを使って、Java用のinputプラグインの雛形を作成します。

embulk new java-input java_example
2016-07-17 22:07:09.046 +0900: Embulk v0.8.9
Creating embulk-input-java_example/
  Creating embulk-input-java_example/README.md
  Creating embulk-input-java_example/LICENSE.txt
  Creating embulk-input-java_example/.gitignore
  Creating embulk-input-java_example/gradle/wrapper/gradle-wrapper.jar
  Creating embulk-input-java_example/gradle/wrapper/gradle-wrapper.properties
  Creating embulk-input-java_example/gradlew.bat
  Creating embulk-input-java_example/gradlew
  Creating embulk-input-java_example/config/checkstyle/checkstyle.xml
  Creating embulk-input-java_example/config/checkstyle/default.xml
  Creating embulk-input-java_example/build.gradle
  Creating embulk-input-java_example/lib/embulk/input/java_example.rb
  Creating embulk-input-java_example/src/main/java/org/embulk/input/java_example/JavaExampleInputPlugin.java
  Creating embulk-input-java_example/src/test/java/org/embulk/input/java_example/TestJavaExampleInputPlugin.java

Plugin template is successfully generated.
Next steps:

  $ cd embulk-input-java_example
  $ ./gradlew package

Embulk newコマンドは次のようなファイルを作成します。それぞれファイルの役割については、後ほど詳しく説明をします。

.
|-- LICENSE.txt
|-- README.md
|-- build.gradle
|-- config
|   `-- checkstyle
|       |-- checkstyle.xml
|       `-- default.xml
|-- gradle
|   `-- wrapper
|       |-- gradle-wrapper.jar
|       `-- gradle-wrapper.properties
|-- gradlew
|-- gradlew.bat
|-- lib
|   `-- embulk
|       `-- input
|           `-- java_example.rb
`-- src
    |-- main
    |   `-- java
    |       `-- org
    |           `-- embulk
    |               `-- input
    |                   `-- java_example
    |                       `-- JavaExampleInputPlugin.java
    `-- test
        `-- java
            `-- org
                `-- embulk
                    `-- input
                        `-- java_example
                            `-- TestJavaExampleInputPlugin.java

この状態で、embulk-input-java_exampleディレクトリに移動をして、./gradlew classpathコマンド(Windowsの場合は、gradlew.bat classpath)を実行します。

 ./gradlew classpath
:compileJava
警告: [options] ブートストラップ・クラスパスが-source 1.7と一緒に設定されていません
警告1個
:processResources UP-TO-DATE
:classes
:jar
:classpath

BUILD SUCCESSFUL

Total time: 7.149 secs

This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.10/userguide/gradle_daemon.html

TODO: このままだと入力例にならないので、コードの修正が必要

Ruby

embulk newコマンドを使って、Ruby用のinputプラグインの雛形を作成します。

embulk new ruby-input ruby_example
2016-07-17 21:29:16.803 +0900: Embulk v0.8.9
Creating embulk-input-ruby_example/
  Creating embulk-input-ruby_example/README.md
  Creating embulk-input-ruby_example/LICENSE.txt
  Creating embulk-input-ruby_example/.gitignore
  Creating embulk-input-ruby_example/Rakefile
  Creating embulk-input-ruby_example/Gemfile
  Creating embulk-input-ruby_example/.ruby-version
  Creating embulk-input-ruby_example/embulk-input-ruby_example.gemspec
  Creating embulk-input-ruby_example/lib/embulk/input/ruby_example.rb

Plugin template is successfully generated.
Next steps:

  $ cd embulk-input-ruby_example
  $ bundle install                      # install one using rbenv & rbenv-build
  $ bundle exec rake                    # build gem to be released
  $ bundle exec embulk run config.yml   # you can run plugin using this command

Embulk newコマンドは次のようなファイルを作成します。それぞれファイルの役割については、後ほど詳しく説明をします。

.
|-- Gemfile
|-- LICENSE.txt
|-- README.md
|-- Rakefile
|-- embulk-input-ruby_example.gemspec
`-- lib
    `-- embulk
        `-- input
            `-- ruby_example.rb

3 directories, 6 files

Javaと違い、Rubyの場合はコンパイルが不要です。

conf.ymlファイルを、embulk newコマンドを実行したディレクトリと同じ場所で、作成します。ファイルの中身は次のような形式です。

embulk newで生成するテンプレートファイルは、option1を必須オプションで、整数型であることを前提にしています。

in:
  type: ruby_example
  option1: 123

out:
  type: stdout

作成した設定ファイルを指定して、embulk runコマンドを実行してみましょう。runコマンドの引数として、-I embulプラグイン/libディレクトリを指定することで、embulk gemコマンドを使ってプラグインをインストールせずに、プラグインの動作を確認することができます。

実行結果は次のようになります。

embulk run -I embulk-input-ruby_example/lib conf.yml 
2016-07-17 21:30:51.441 +0900: Embulk v0.8.9
2016-07-17 21:30:53.963 +0900 [INFO] (0001:transaction): Loaded plugin embulk/input/ruby_example from a load path
2016-07-17 21:30:54.025 +0900 [INFO] (0001:transaction): Using local thread executor with max_threads=8 / output tasks 4 = input tasks 1 * 4
2016-07-17 21:30:54.047 +0900 [INFO] (0001:transaction): {done:  0 / 1, running: 0}
example-value,1,0.1
example-value,2,0.2
2016-07-17 21:30:54.164 +0900 [INFO] (0001:transaction): {done:  1 / 1, running: 0}
2016-07-17 21:30:54.236 +0900 [INFO] (main): Committed.
2016-07-17 21:30:54.236 +0900 [INFO] (main): Next config diff: {"in":{},"out":{}}

Embulk newコマンドがサンプルで生成するファイルは、options1の値を入力データとして利用していません。そこでソースコードを修正して、入力データにoptions1の値を利用するように修正をしましょう。

--- a/lib/embulk/input/ruby_example.rb
+++ b/lib/embulk/input/ruby_example.rb
@@ -46,8 +46,8 @@ module Embulk
       end
 
       def run
-        page_builder.add(["example-value", 1, 0.1])
-        page_builder.add(["example-value", 2, 0.2])
+        page_builder.add(["example-value", @option1, 0.1])
+        page_builder.add(["example-value", @option1, 0.2])
         page_builder.finish
 
         task_report = {}

修正後、embulk runコマンドを実行すると、options1に指定した値を出力していることが確認できます。

embulk run -I embulk-input-ruby_example/lib conf.yml 
2016-07-17 21:33:20.908 +0900: Embulk v0.8.9
2016-07-17 21:33:23.234 +0900 [INFO] (0001:transaction): Loaded plugin embulk/input/ruby_example from a load path
2016-07-17 21:33:23.281 +0900 [INFO] (0001:transaction): Using local thread executor with max_threads=8 / output tasks 4 = input tasks 1 * 4
2016-07-17 21:33:23.307 +0900 [INFO] (0001:transaction): {done:  0 / 1, running: 0}
example-value,123,0.1
example-value,123,0.2
2016-07-17 21:33:23.414 +0900 [INFO] (0001:transaction): {done:  1 / 1, running: 0}
2016-07-17 21:33:23.500 +0900 [INFO] (main): Committed.
2016-07-17 21:33:23.500 +0900 [INFO] (main): Next config diff: {"in":{},"out":{}}

プラグインを開発する(Java編)

プラグインの開発の準備(Java)

コーディングスタイルAirlist

EmbulkのJava関連のコードは、Prestoと同じコードスタイル(Airlift codestyle)で記述されています。幾つかのスタイルについて、実例を挙げて説明します。詳細についてはhttps://github.com/airlift/codestyleを参照してください。

空白

ifやwhileは、空白を開け、{の前も空白を開けます。

次のような形式ではなく

if(x > 0){

次のように表記します。

if (x > 0) {

if分などの()の前後は、空白を入れないで記載します。

次のような形式ではなく

if ( x > 0 ) {

次のように表記します。

if (x > 0) {

elseやcatch, finallyは、独立した行にする。(※、つまり}を独立した行にするということです)

次のような形式ではなく

} else if (x < 0) {

次のように表記します。

}
else if (x < 0) {

カンマの後は、スペースを入れます。

次のような形式ではなく

foo(boolean a,int x,int y,int z)

次のように表記します。

foo(boolean a, int x, int y, int z)

改行後は、空行を入れません。

次のような形式ではなく

package com.intellij.samples;


import com.intellij.idea.Main;

次のように表記します。

package com.intellij.samples;

import com.intellij.idea.Main;

GradleやIntelliJによるスタイルのチェック方法

コーディングスタイルに従っているかどうかを下記の手順で、確認することができます。

Gradleの設定とIntelliJ IDEA 15 CE(OSX) の設定それぞれについて解説します。

プラグイン内のコードスタイルをチェックする設定は、Embulk 0.8から利用できるようになっています。

なお、Embulkのプロジェクトではプラグイン作成者の方が小気味良いハックで目の前の問題を解決することも許されても良いという方針から、スタイルを厳密に守ることを重要視していません。プラグインを作られる方の好きなスタイルでプラグインを作るのも自由です。

Gradleでは、次の手順で設定をします。

  • 設定
  • チェックコマンドを実行

build.gradleの設定(embulk 0.8からは自動設定)

embulk 0.8からは、embulk newをすると、次の設定がbuild.gradleに追加されます。

embulk 0.8より前のプラグインでもembulk migrate path/to/plugin_dirコマンドを実行すれば同じ設定が適用されます。

plugins {
    // 略
    id "checkstyle"
}
// 略
checkstyle {
    configFile = file("${project.rootDir}/config/checkstyle/checkstyle.xml")
    toolVersion = '6.14.1'
}
checkstyleMain {
    configFile = file("${project.rootDir}/config/checkstyle/default.xml")
    ignoreFailures = true
}
checkstyleTest {
    configFile = file("${project.rootDir}/config/checkstyle/default.xml")
    ignoreFailures = true
}

またチェックに使うファイルが次の場所に作成されます。

  • config/checkstyle/default.xml: 緩いチェック
  • config/checkstyle/checkstyle.xml より厳密なチェック

二つのファイルはチェックする内容が若干異なります。 例えば、default.xmlではimportの順序に関するチェックは行わないようになっています。

チェックコマンドを実行

gradlew <チェックの種類>でチェックを行うことができます。

チェックは次の種類が利用できます。checkは、checkstyleMaincheckstyleTestそれぞれを実行します。

  • check: 緩いチェックで、警告のみ
    • checkstyleMain: プラグイン本体のチェック
    • checkstyleTest: テストコードのスタイルチェック
  • checkstyle: より厳密なチェック、スタイルに従わないとエラーになる。

checkstyleの実行例はつぎのとおりです。

./gradlew checkstyle
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:checkstyleMain
[ant:checkstyle] /path/to/embulk-input-hdfs/src/main/java/org/embulk/input/hdfs/HdfsFileInputPlugin.java:27: 'java.io.File' は上のインポート文から1行空けるべきです。
[ant:checkstyle] /path/to/embulk-input-hdfs/src/main/java/org/embulk/input/hdfs/HdfsFileInputPlugin.java:180:45: ':' の前にホワイトスペースがありません。
[ant:checkstyle] /path/to/embulk-input-hdfs/src/main/java/org/embulk/input/hdfs/HdfsFileInputPlugin.java:187:57: 名前 'rewind_seconds' はパターン '^[a-z][a-zA-Z0-9]*$' に一致しなければなりません。
[ant:checkstyle] /path/to/embulk-input-hdfs/src/main/java/org/embulk/input/hdfs/HdfsPartialFile.java:3:8: 使用されないインポートです - org.apache.hadoop.fs.Path。
[ant:checkstyle] /path/to/embulk-input-hdfs/src/main/java/org/embulk/input/hdfs/HdfsPartialFile.java:23:30: '{' は新しい行にあるべきです。
[ant:checkstyle] /path/to/embulk-input-hdfs/src/main/java/org/embulk/input/hdfs/HdfsPartialFile.java:38: Blank line before closing brace
[ant:checkstyle] /path/to/embulk-input-hdfs/src/main/java/org/embulk/input/hdfs/HdfsPartialFileInputStream.java:0: ファイルが新しい行で終了していません。
[ant:checkstyle] /path/to/embulk-input-hdfs/src/main/java/org/embulk/input/hdfs/HdfsPartialFileInputStream.java:48:40: 'cast' の後にホワイトスペースがありません。
:checkstyleMain FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':checkstyleMain'.
> Checkstyle rule violations were found. See the report at: file:///path/to/embulk-input-hdfs/build/reports/checkstyle/main.xml

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 2.97 secs

IntelliJ IDEA 15 CEの場合

Airlift.xmlをダウンロードして、~/Library/Preferences/IdeaIC15/codestyles/Airlift.xml として保存します。

[Preferences] -> [Editor] -> [Code style]でSchemaからAirliftが選択できるようになります。

IntelliJ IDEA 15 CE

ソースコードのフォーマット

Reformatting Source Code によると、Code -> Reformat Codeで自動整形をしてくれます。

Java

  • 設定の取得

    • 設定の取得方法
    • デフォルト値
  • guessプラグイン

  • インプット

    • ファイルインプットとインプットプラグイン
    • transaction
    • resume
    • cleanup
    • run
  • デコーダー

  • パーサー

  • フィルタ

  • フォーマッター

  • アウトプット

  • エンコーダー

  • エクゼキューター

  • エラーハンドリング

    • リトライ
    • 無限リトライから回避
  • テスト

プラグインを開発する(Ruby編)

  • guessプラグイン
  • インプット
  • パーサー
  • フィルタ
  • アウトプット
  • フォーマッター
  • テスト

参考資料

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment