🚢

Embulk v0.11 / v1.0 に向けて: プラグイン開発者の皆様へ

2021/04/28に公開

プラグイン型バルク・データ・ローダーの Embulk をメンテナンスしている @dmikurube です。

今後の Embulk のロードマップについて、一年ほど前に、記事を (英語ですが) 出したり、ミートアップで話したりしていました。その内容は、開発版 (非安定版) として Embulk v0.10 でしばらく大改造を行い、そこから次期安定版の v0.11 を経て v1.0 を出しますよ、というものでした。

それから一年経ち、その v0.11.0 のリリースがいよいよ視界に入り、プラグイン API/SPI の(再)定義もほぼ固まりました。本記事は Embulk プラグインを v0.11 / v1.0 対応させる手順を解説するものです。 (プラグイン開発者ではなくユーザー向けのまとめはこちら)

(Embulk 開発者サイトにある英語版 の翻訳ですが、同一人物が書いているので、おそらく同じ内容になっていると思います。もし違いがありましたら、英語版の方を一次情報として解釈しつつ、ぜひ筆者までご連絡ください)

まず、どうなればプラグインは「Embulk v0.11 / v1.0 対応」と言えるの?

プラグインが以下の条件を満たしたら、そのプラグインは「Embulk v0.11 / v1.0 対応」と言って大丈夫です。

  • Maven artifact の org.embulk:embulk-core に依存していない。
  • org.embulk:embulk-apiorg.embulk:embulk-spicompileOnly で依存している。
  • Gradle プラグインの org.embulk.embulk-plugins でビルドされている。

このようにした build.gradle は以下のようになります。

plugins {
    id "java"
    id "maven-publish"
    id "org.embulk.embulk-plugins" version "0.4.2"  // 2021-07-15 時点の最新が 0.4.2
}

group = "your.maven.group"  // "io.github.your-github-username" など、ご自身の Maven groupId を設定してください ("org.embulk" 以外)
version = "X.Y.Z"

dependencies {
    // 0.11 から "embulk-api" と "embulk-spi" は違うバージョン体系になります。
    compileOnly "org.embulk:embulk-api:0.10.??"
    compileOnly "org.embulk:embulk-spi:0.10.??"
    // NO! compileOnly "org.embulk:embulk-core:0.10.??"
    // NO! compileOnly "org.embulk:embulk-deps:0.10.??"

    // この Gradle プラグインは、新しい Gradle の "implementation" にまだ対応していません。 "compile" にする必要があります。
    // そのため Gradle 7 ではなく 6 に留める必要があります。 (Gradle 7 をサポートする予定はあります)

    // "org.embulk:embulk-util-*" というライブラリがいくつかあります。 "embulk-core" に直接依存する代わりにそれらを使ってください。
    compile "org.embulk:embulk-util-config:0.3.1"  // 2021-07-15 時点の最新が 0.3.1

    // ...

    testCompile "org.embulk:embulk-api:0.10.??"
    testCompile "org.embulk:embulk-spi:0.10.??"
    testCompile "org.embulk:embulk-junit4:0.10.??"  // "embulk-test" から名前が変わりました
    testCompile "org.embulk:embulk-core:0.10.??"  // "testCompile" では "embulk-core" を使っても大丈夫
    testCompile "org.embulk:embulk-deps:0.10.??"  // "testCompile" では "embulk-deps" を使っても大丈夫
}

言い換えると、もしプラグインをビルドするのに org.embulk:embulk-core が必要なら、そのプラグインはまだ Embulk v0.11 対応とは言えません。

Ruby プラグインはどうすれば?

JRuby は Embulk v0.11 以降にビルトインではありません。 Embulk v1.0 以降では JRuby との連携はあまりアクティブにはメンテナンスされない予定です。言い換えると Ruby (プラグイン) は Embulk の第一線の機能としてはサポートされなくなります。

今ある Ruby プラグインは Embulk v1.0.0 でもおそらくそのまま動きます。しかし v1.0.0 以降のどこかで、急に一部の Ruby プラグインが動かなくなる、ということがありうると想定しておいてください。 Ruby プラグインを新しく作ることはお勧めしません。また、既存の Ruby プラグインもできれば Java に書き換えていくことをお勧めします。 (JRuby 連携のメンテナンスに参加してくれる方は今も歓迎しています)

で、どうすれば Embulk v0.11 対応できるの?

Gradle wrapper を Gradle 6 に上げる

./gradlew wrapper --gradle-version=6.8.3

(2021-04-27 時点の最新が 6.8.3)

前述の通り、まだ Gradle 7 には対応していません。

Gradle プラグイン org.embulk.embulk-plugins を適用する

Gradle プラグインのガイドに従って適用してみてください: https://github.com/embulk/gradle-embulk-plugins

Maven groupId を決める

前述のとおり JRuby は Embulk v0.11 以降にビルトインではありません。 Ruby gems は、今後 Embulk プラグインで最も使われるフォーマットではなくなっていきます。 Maven リポジトリにリリースされる Maven artifacts がその代わりになっていきます。

オープンソースの Embulk プラグインは、いくつかある他の Maven リポジトリではなく Maven Central にリリースするのをおすすめしています。 (Bintray と JCenter が終了するのは見ましたよね?)

Maven Central にリリースするには Maven の groupId を設定する必要があります。また、その groupId に対する権限を Maven Central / Sonatype で持っている必要があります。以下の Maven と Java のガイドラインに従って groupId を決めてください。

例えば GitHub ドメイン名を使って io.github.your-github-username などにする案があります。その場合は、リリースする前に GitHub Pages を設定・公開 しておくことをおすすめします。

あなたのプラグインの groupIdorg.embulk を使わないでください。 org.embulkhttps://github.com/embulk 以下で管理しているプラグイン用に予約されています。

Maven リポジトリに Embulk プラグインをリリースする設定をする

前述のとおり、オープンソースの Embulk プラグインは Maven Central にリリースするのをおすすめしています。他へのリリースを制限しているわけではありませんが、いずれにせよ、どんな Maven リポジトリにリリースするのでもそのリポジトリ用に build.gradle を設定する必要があります。

その設定は以下のようになります。 Maven Cetnral であれば Maven のガイド (Getting started)必要事項を確認してください。

// Maven Central では "-javadoc" と "-sources" JAR もリリースする必要があります。
// Gradle 6 以降なら、この設定で自動的に生成できます。
java {
    withJavadocJar()
    withSourcesJar()
}

// 以下の設定で "./gradlew publishMavenPublicationToMavenCentralRepository" を実行すると Maven Central にリリースできます。
// ただし Sonatype OSSRH JIRA の設定をして JIRA にチケットを作り、さらに PGP 署名も用意しておく必要があります。
publishing {
    publications {
        maven(MavenPublication) {
            groupId = "${project.group}"
            artifactId = "${project.name}"

            from components.java

            // Maven Central では "-javadoc" と "-sources" JAR もリリースする必要があります。
            // "javadocJar" と "sourcesJar" は、上の "java.withJavadocJar()" と "java.withSourcesJar()" があれば自動で追加されます。
            // See: https://docs.gradle.org/current/javadoc/org/gradle/api/plugins/JavaPluginExtension.html

            pom {  // https://central.sonatype.org/pages/requirements.html
                packaging "jar"

                name = "${project.name}"
                description = "${project.description}"
                // url = "https://github.com/your-github-username/your-plugin-name"

                licenses {
                    // Maven Central ではライセンスを明示的に指定する必要があります。
                    // これは MIT License の例です。
                    license {
                        // http://central.sonatype.org/pages/requirements.html#license-information
                        name = "MIT License"
                        url = "http://www.opensource.org/licenses/mit-license.php"
                    }
                }

                developers {
                    developer {
                        name = "Your Name"
                        email = "you@example.com"
                    }
                }

                scm {
                    connection = "scm:git:git://github.com/your-github-username/your_repository.git"
                    developerConnection = "scm:git:git@github.com:your-github-username/your_repository.git"
                    url = "https://github.com/your-github-username/your_repository"
                }
            }
        }
    }

    repositories {
        maven {
            name = "mavenCentral"
            if (project.version.endsWith("-SNAPSHOT")) {
                url "https://oss.sonatype.org/content/repositories/snapshots"
            } else {
                url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            }

            // この設定は "~/.gradle/gradle.properties" に "ossrhUsername" と "ossrhPassword" があることを仮定しています。
            // Sonatype OSSRH のユーザー名とパスワードを "~/.gradle/gradle.properties" に入れてください。
            // https://central.sonatype.org/publish/publish-gradle/#credentials
            credentials {
                username = project.hasProperty("ossrhUsername") ? ossrhUsername : ""
                password = project.hasProperty("ossrhPassword") ? ossrhPassword : ""
            }
        }
    }
}

// Maven Central では PGP 署名が必要です。
// https://central.sonatype.org/publish/requirements/gpg/
// https://central.sonatype.org/publish/publish-gradle/#credentials
signing {
    sign publishing.publications.maven
}

依存関係の整理

最初に挙げたとおり Embulk v0.11 以降対応のプラグインは org.embulk:embulk-core に依存できません。つまり、最新のプラグインは org.embulk:embulk-core だけにあって org.embulk:embulk-apiorg.embulk:embulk-spi に入っていないクラスを使えません。

これがプラグインの互換性にとても大きな影響があることは Embulk 開発チームも理解しています。ですが、将来の Embulk プラグインが複雑な依存関係の問題から解放されるように、これを強行することにしました。

たとえば org.embulk:embulk-core には Jackson 2.6.7 への依存があります。プラグインは org.embulk:embulk-core の下でロードされるので、プラグインは Jackson 2.6.7 がとても古いことをわかっていながら 2.6.7 を使わなければなりません。一方 org.embulk:embulk-core の Jackson を新しくするのは、既存の Jackson 2.6.7 を想定しているプラグインに影響するので、簡単ではありません。板挟みだったのです。

また org.embulk:embulk-core には、プラグインのためのユーティリティクラスもたくさんありました。 Embulk の開発チームが新しい機能のために 変更を加えると、予想外に多くのプラグインが影響を受けたことが何度もありました。そのため開発チームは Embulk 本体に改善を加えることをためらうようになっていました。

ひとたびほとんどのプラグインが v0.11 対応になったら、使う Jackson のバージョンはプラグインの自由になります。また Embulk 本体の変更からも影響を受けにくくなります。

Embulk v0.11 以降の org.embulk:embulk-apiorg.embulk:embulk-spi は、本体とプラグインの間の「契約 (contract)」になります。これらはあまり頻繁には更新せず、プラグインが本体の変更に影響されないよう、互換性を保って注意深くメンテナンスされます。一方の org.embulk:embulk-core は、新しい機能のためにもっと頻繁に更新されるでしょう。

embulk-apiembulk-spiorg.embulk:embulk-core とは違うバージョン体系になります。 embulk-apiembulk-spi のバージョンは embulk-core と違って、単に 0.11, 1.0, 1.1, 1.2 のようになります。この一・二桁目も同期しません。たとえば org.embulk:embulk-core:1.3.2org.embulk:embulk-api:1.1org.embulk:embulk-spi:1.1 を想定する、というようなこともありえます。

org.embulk:embulk-api:0.11org.embulk:embulk-spi:0.11 は Embulk v0.11.0 と同時にリリースされます。同様に org.embulk:embulk-api:1.0org.embulk:embulk-spi:1.0 は Embulk v1.0.0 と同時にリリースされます。しかし、以降の embulk-apiembulk-spi は、あまり頻繁ではない API と SPI への追加があったときのみリリースされます。

Embulk config の処理

これまでプラグインは config を処理するのに org.embulk.config.ConfigSource#loadConfigorg.embulk.config.TaskSource#loadTask を使ってきました. しかし、これらはもう非推奨になりました。外部ライブラリになった org.embulk:embulk-util-config を代わりに使ってください。

compile "org.embulk:embulk-util-config:0.3.1"

使い方はそこまで複雑ではありません。まず @Config, @ConfigDefault, Taskembulk-util-config のものに置き換えてください。

- import org.embulk.config.Config;
- import org.embulk.config.ConfigDefault;
- import org.embulk.config.Task;
+ import org.embulk.util.config.Config;
+ import org.embulk.util.config.ConfigDefault;
+ import org.embulk.util.config.Task;

そしてプラグインの Task 定義を確認してください。これが Task (org.embulk.config.Taskorg.embulk.util.config.Task) のみを継承しているなら、特に問題はありません。もしこれが他の古い interface (たとえば TimestampParser.Task) を継承していたら、代わりの org.embulk.util.config.* を使った新しいものを探すか、ユーザーから見て互換になるように中のメソッド定義をコピーしてきてください。

public interface PluginTask extends Task, org.embulk.spi.time.TimestampParser.Task {  // => TimestampParser.Task を消す
    // ...

    // org.embulk.spi.time.TimestampParser.Task からコピー
    @Config("default_timezone")
    @ConfigDefault("\"UTC\"")
    String getDefaultTimeZoneId();

    // org.embulk.spi.time.TimestampParser.Task からコピー
    @Config("default_timestamp_format")
    @ConfigDefault("\"%Y-%m-%d %H:%M:%S.%N %z\"")
    String getDefaultTimestampFormat();

    // org.embulk.spi.time.TimestampParser.Task からコピー
    @Config("default_date")
    @ConfigDefault("\"1970-01-01\"")
    String getDefaultDate();
}

次に org.embulk.util.config.ConfigMapperFactory のインスタンスを用意してください。

import org.embulk.util.config.ConfigMapperFactory;

private static final ConfigMapperFactory CONFIG_MAPPER_FACTORY = ConfigMapperFactory.builder().addDefaultModules().build();

Embulk config から、あまり標準的ではないクラスへのマッピングが必要なときは、任意の Jackson Module を追加できます。

private static final ConfigMapperFactory CONFIG_MAPPER_FACTORY =
        ConfigMapperFactory.builder().addDefaultModules().addModule(new SomeJacksonModule()).build();

Apache BVal を使って config の検証を有効にすることもできます。

import javax.validation.Validation;
import javax.validation.Validator;
import org.apache.bval.jsr303.ApacheValidationProvider;

private static final Validator VALIDATOR =
        Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory().getValidator();

private static final ConfigMapperFactory CONFIG_MAPPER_FACTORY =
        ConfigMapperFactory.builder().addDefaultModules().withValidator(VALIDATOR).build();

最後に loadConfigloadTaskorg.embulk.util.config.ConfigMapper#maporg.embulk.util.config.TaskMapper#map にそれぞれ置き換えてください。

+ import org.embulk.util.config.ConfigMapper;
+ import org.embulk.util.config.TaskMapper;

- PluginTask task = config.loadConfig(PluginTask.class);
+ final ConfigMapper configMapper = CONFIG_MAPPER_FACTORY.createConfigMapper();
+ final PluginTask task = configMapper.map(config, PluginTask.class);

- PluginTask task = taskSource.loadTask(PluginTask.class);
+ final TaskMapper taskMapper = CONFIG_MAPPER_FACTORY.createTaskMapper();
+ final PluginTask task = taskMapper.map(taskSource, PluginTask.class);

もし、プラグインが Exec.newConfigDiff(), Exec.newConfigSource(), Exec.newTaskReport(), Exec.newTaskSource() などを使って各インスタンスを生成している場合は、それぞれ CONFIG_MAPPER_FACTORY.newConfigDiff(), CONFIG_MAPPER_FACTORY.newConfigSource(), CONFIG_MAPPER_FACTORY.newTaskReport(), CONFIG_MAPPER_FACTORY.newTaskSource() に置き換えましょう。旧 Exec.new...() メソッド群は旧 org.embulk.config 用のインスタンスを生成するもので、これは embulk-util-config では動作しません。

embulk-util-config 自体が Jackson に依存しているので、これを使うとプラグインは Jackson への依存を直接持つようになります。

プラグインが Embulk v0.9 でもしばらくは動くようにするには、新しい Embulk v0.11.0 がリリースされても、しばらくは Jackson 2.6.7 のままにしておくことを検討してください。他の多くのプラグインが v0.11 対応になり、多くのユーザーが v0.11.0 以降を使うようになったら、もうプラグインがどの Jackson バージョンを使っても動くはずです!

日付・時刻とタイムゾーンの扱い

Embulk から Joda-Time は削除されます。 org.embulk.spi.time.TimestampFormatterorg.embulk.spi.time.TimestampParser は非推奨です。 Java 8 標準の java.time クラス群と、外部ライブラリの org.embulk:embulk-util-timestamp を代わりに使ってください.

compile "org.embulk:embulk-util-timestamp:0.2.1"

Embulk の org.embulk.spi.time.Timestamp も非推奨になっています。 java.time.Instant を代わりに使ってください。

v0.10.13 から PageBuilder#setTimestamp(Column, Instant), PageBuilder#setTimestamp(int, Instant), PageReader#getTimestampInstant(Column), PageReader#getTimestampInstant(int) などの java.time.Instant を引数に取ったり返したりするメソッドが追加されています。しかしこれらは v0.9.23 には実装されていないので、プラグインからこれらのメソッドを使うと、そのプラグインは v0.9.23 では動かなくなってしまいます。 v0.9.23 でも動かしたい場合は、旧 org.embulk.spi.Timestamp を使うメソッドを使いましょう。 TimestampInstant は相互変換できます。 (これらをバックポートしたバージョンを v0.9.24 としてリリースする計画もありますが、基本的には旧メソッドを使っておくほうがしばらくは安全です)

Joda-Time の DateTime は、代わりに java.time.OffsetDateTime または java.time.ZonedDateTime を使ってください。 OffsetDateTime で十分な場合は、基本的に OffsetDateTime の方を使っておくことをおすすめします。地理的地域ベースのタイムゾーンを使うと、その複雑さからすべてが面倒くさくなります。 (タイムゾーンのデフォルトを America/Los_Angeles にしているときに 2017-03-12 02:30:00 を受け取ったらどうなるか想像してみましょう。)

Joda-Time の DateTimeZone は、代わりに java.time.ZoneOffset または java.time.ZoneId を使ってください。上と同じ理由から、それで足りる場合は ZoneOffset に制限しておくことをおすすめします。

org.embulk.spi.time.TimestampFormatterorg.embulk.spi.time.TimestampParser は前述のとおり非推奨です。 embulk-util-timestamporg.embulk.util.timestamp.TimestampFormatter を代わりに使ってください。「そのまま」移行する典型的なやり方は以下のようになります。

// embulk-util-config の org.embulk.util.config.Task を使って PluginTask を定義。
private interface PluginTask extends org.embulk.util.config.Task {
    // ...

    // org.embulk.spi.time.TimestampParser.Task からコピー
    @Config("default_timezone")
    @ConfigDefault("\"UTC\"")
    String getDefaultTimeZoneId();

    // org.embulk.spi.time.TimestampParser.Task からコピー
    @Config("default_timestamp_format")
    @ConfigDefault("\"%Y-%m-%d %H:%M:%S.%N %z\"")
    String getDefaultTimestampFormat();

    // org.embulk.spi.time.TimestampParser.Task からコピー
    @Config("default_date")
    @ConfigDefault("\"1970-01-01\"")
    String getDefaultDate();
}

// org.embulk.spi.time.Timestamp{Formatter|Parser}.TimestampColumnOption と互換の TimestampColumnOption を自分で定義。
private interface TimestampColumnOption extends org.embulk.util.config.Task {
    @Config("timezone")
    @ConfigDefault("null")
    java.util.Optional<String> getTimeZoneId();

    @Config("format")
    @ConfigDefault("null")
    java.util.Optional<String> getFormat();

    @Config("date")
    @ConfigDefault("null")
    java.util.Optional<String> getDate();
}

// PluginTask に変換した config 全体を取得。
final PluginTask task = configMapper.map(configSource, PluginTask.class);

// カラムごとのオプションを embulk-util-config で取得。
final TimestampColumnOption columnOption = configMapper.map(columnConfig.getOption(), TimestampColumnOption.class);

// TimestampFormatter を構築。
final TimestampFormatter formatter = TimestampFormatter
        .builder(columnOption.getFormat().orElse(task.getDefaultTimestampFormat(), true)
        .setDefaultZoneFromString(columnOption.getTimeZoneId().orElse(task.getDefaultTimeZoneId()))
        .setDefaultDateFromString(columnOption.getDate().orElse(task.getDefaultDate()))
        .build();

Instant instant = formatter.parse("2019-02-28 12:34:56 +09:00");
System.out.println(instant);  // => "2019-02-28T03:34:56Z"

String formatted = formatter.format(Instant.ofEpochSecond(1009110896));
System.out.println(formatted);  // => "2017-12-23 12:34:56 UTC"

Jackson と JSON の扱い

org.embulk:embulk-core はもう Jackson への依存を持っていません。プラグイン自体の中で Jackson を使う必要がある場合は、明示的に Jackson を依存に持つ必要があります。

もう前述の org.embulk:embulk-util-config を使い始めているなら、そのプラグインはもう Jackson (2.6.7) への依存を持っているはずです。そこに Jackson のサブライブラリを自分で足すこともできます。

Embulk の org.embulk.spi.json.JsonParser は非推奨です。 org.embulk:embulk-util-json を代わりに使ってください。使い方はほぼ同じです。

前述したように、プラグインが古い Embulk v0.9 でも今しばらく動くようにするには Jackson 2.6.7 に留めておいてください。

Google Guava と Apache Commons Lang 3

org.embulk:embulk-core はもう Google Guava と Apache Commons Lang 3 への依存を持っていません。プラグインがそれらを使っている場合は Java 8 の標準クラスで置き換えるか、それらへの依存を明示的に持たせてください。

Java 8 は十分に強力です。 Google Guava や Apache Commons Lang 3 に強く依存した使い方をしているのでなければ Java 8 の標準クラスで置き換えるのをおすすめします。それらのライブラリ (特に Guava) にはときどき非互換が入ってきて、イライラさせられることがあるでしょう。

少なくとも Guava の com.google.common.base.Optionalembulk-util-config では動きません。これは java.util.Optional に置き換える必要があります。

Guava の典型的なちょっとした使い方は、以下のように置き換えられます。

Guice

プラグインの中で Guice を使うのはできるだけ止めてください。

もし、他の依存ライブラリが Guice を使っているなどの理由で避けられない場合は、明示的に Guice を依存に持ってください。ただし、古い Embulk v0.9 では即座に動かなくなる可能性があります。

JRuby (org.jruby クラス)

プラグインの中で JRuby やそのクラスを使うのは止めてください。

Java プラグインから JRuby を呼ぶ必要があった典型例は、日付・時刻の解釈や出力でした。これは embulk-util-timestampembulk-util-rubytime で置き換えられます。

FindBugs

org.embulk:embulk-core は、もう FindBugs の com.google.code.findbugs:annotations (@SuppressFBWarnings) への依存を持っていません。

FindBugs はもう長い間メンテナンスされていないことが知られています。少なくとも FindBugs を使うのは止めることをおすすめします。

SpotBugs は FindBugs のいい置き換えになるかもしれません。ですが Embulk 本体から SpotBugs 向けのサポートを特別に用意する予定はありません。プラグインの中でご自分で試してみてください。

Java 11 以降への準備

Java エコシステムでは、新しい Java のバージョンに向けて準備をしていかなければなりません。 Java 8 から 11 以降に移るのには大きなギャップがありますが、その中でも Java EE 関連のクラス群 (典型例は JAXB javax.xml.*) が Java 11 の実行環境から消えるのは特に影響が大きいところでしょう。もしプラグインがこれらのクラスを使っている場合、そのままでは Java 11 以降では動きません。詳細は JEP 320 を確認してください。

Embulk v0.11 以降では、プラグインからこれらのクラスが使われたことを検出すると (Java 8 で動かしていても) 以下のような警告メッセージをログに出します。

Class javax.xml.bind.JAXB is loaded by the parent ClassLoader, which is removed by JEP 320. The plugin needs to include it on the plugin side. See https://github.com/embulk/embulk/issues/1270 for more details.

フレームワークとしての Embulk からは、これ以上のサポートは特に提供しません。もしプラグインがこれらのクラスを使っている場合は、代わりになる依存関係を明示的に自分で明示的に追加してください。

典型例をいくつか以下に掲載します。

// JAXB への依存を明示的に追加。

// JAXB 2.2.11 を選んだのは以下の理由です。
// 1. JDK 8 に同梱の JAXB は 2.2.8 です。
//    Java 8 がベースにある場合や Embulk v0.9 でも動かしたい場合を考え、しばらくは近いバージョンにしておくのがいいでしょう。
//    https://javaee.github.io/jaxb-v2/doc/user-guide/ch02.html#a-2-2-8
// 2. しかし com.sun.xml.bind:jaxb-core:2.2.8 も com.sun.xml.bind:jaxb-impl:2.2.8 も Maven Central にはありません。
// 3. JAXB 2.2 系の中では 2.2.11 が最もよく使われているようです。
//    https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api
//    https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-core
//    https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-impl
// 4. JAXB 2.2.8 と 2.2.11 は、同じ名前のクラス群で構成されているようです。
//    内部の実装が若干違っていたとしても、クラスローダーを混乱させる懸念は薄いでしょう。
compile "javax.xml.bind:jaxb-api:2.2.11"
compile "com.sun.xml.bind:jaxb-core:2.2.11"
compile "com.sun.xml.bind:jaxb-impl:2.2.11"
// Activation Framework への依存を明示的に追加。

// Activation Framework は、よくJAXB と一緒に必要になります。
// 1.1.1 を選んだ理由は、これが JAXB 2.2.11 がリリースされた時点で最新だったものだからです。
compile "javax.activation:activation:1.1.1"
// Annotation API への依存を明示的に追加。

// 1.2 を選んだ理由は Java 8 の時代には 1.2 しかなかったようだからです。
// https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api
compile "javax.annotation:javax.annotation-api:1.2"

ログ

org.embulk.spi.Exec.getLogger は非推奨です。 SLF4Jorg.slf4j.LoggerFactory.getLogger を直接使ってください。

embulk-standards の利用

Embulk v0.11 以降 org.embulk:embulk-standards という Maven artifact はなくなりました。旧標準プラグインは個別の Maven artifacts に分割して Maven 形式のプラグインとして Maven Central にアップロードされています。 (例: embulk-input-file / embulk-guess-csv)

プラグインは、標準プラグインのクラスやメソッドを直接使うことはできなくなります。プラグイン本体でそれが必要な場合は、標準プラグインの Maven artifact をライブラリとして使用してください。

compile "org.embulk:embulk-guess-csv:0.10.??"

テストコードで標準プラグインを使いたい場合は testCompiletestImplementation で各プラグインを参照した上で、多くの場合はテストコードから明示的に登録する必要があります。 (参考: embulk-parser-csv:0.10.31 のテスト)

org.embulk:embulk-core にあるその他のユーティリティクラス

  • File-like なバイト列の処理 (org.embulk.spi.utilFileInputInputStreamFileOutputOutputStream)
  • テキストの処理 (org.embulk.spi.utilLineDecoderLineEncoder)
  • org.embulk.spi.utilDynamicColumnSetter 関連
  • リトライ (org.embulk.spi.utilRetryExecutor)

置き換えのまとめ

Embulk config の処理

@Config embulk-util-config@org.embulk.util.config.Config
@ConfigDefault embulk-util-config@org.embulk.util.config.ConfigDefault
@ConfigInject Exec.get???() (たとえば Exec.getBufferAllocator)
ConfigSource#loadConfig embulk-util-configConfigMapper#map
TaskSource#loadTask embulk-util-configTaskMapper#map
Task embulk-util-configorg.embulk.util.config.Task
ColumnConfig embulk-util-configorg.embulk.util.config.units.ColumnConfig
SchemaConfig embulk-util-configorg.embulk.util.config.units.SchemaConfig
ModelManager 使うのを止めてください。代わりが必要な場合は Jackson の ObjectMapper を自分で構築してください。
LocalFile in PluginTask embulk-util-configorg.embulk.util.config.modules.LocalFileModuleorg.embulk.util.config.units.LocalFile
Charset in PluginTask embulk-util-configorg.embulk.util.config.modules.CharsetModule
Column in PluginTask embulk-util-configorg.embulk.util.config.modules.ColumnModule
Schema in PluginTask embulk-util-configorg.embulk.util.config.modules.SchemaModule
Type in PluginTask embulk-util-configorg.embulk.util.config.modules.TypeModule

日付・時刻とタイムゾーンの扱い

org.joda.time.DateTime java.time.OffsetDateTime または java.time.ZonedDateTime
org.joda.time.DateTimeZone java.time.ZoneOffset または java.time.ZoneId
org.joda.time.DateTimeZone in PluginTask 必要なら java.time.ZoneIdembulk-util-configorg.embulk.util.config.modules.ZoneIdModule
Timestamp できるだけ java.time.Instant
TimestampFormatter embulk-util-timestamporg.embulk.util.timestamp.TimestampFormatter
TimestampParser embulk-util-timestamporg.embulk.util.timestamp.TimestampFormatter

Jackson と JSON の扱い

org.embulk.spi.json.* embulk-util-jsonorg.embulk.util.json.*

Google Guava と Apache Commons Lang 3

Guava Optional java.util.Optional
Guava Throwables Guava のドキュメント を読んでください

JRuby (org.jruby クラス)

org.jruby.* 使うのを止めてください。日付・時刻の操作には embulk-util-tiemstampembulk-util-rubytime を使ってください。

FindBugs

@SuppressFBWarnings 削除するか、使い方を模索してみてください。

Java 11 以降への準備

javax.* JEP 320 を見て、それが Java 11 以降で消えたものかどうか確認してください。 (Issue)

ログ

Exec.getLogger org.slf4j.LoggerFactory.getLogger

org.embulk:embulk-core にあるその他のユーティリティクラス

org.embulk.spi.util.* embulk-util-file, embulk-util-text, embulk-util-dynamic, embulk-util-retryhelper

テスト

v0.9 までテストで使っていた org.embulk:embulk-testorg.embulk:embulk-junit4 に名前が変わっています。

testCompile "org.embulk:embulk-junit4:0.10.??"

テストでは、まだ org.embulk:embulk-core と、さらに org.embulk:embulk-deps を、依存に追加する必要があります。

testCompile "org.embulk:embulk-core:0.10.??"
testCompile "org.embulk:embulk-deps:0.10.??"

テストでは embulk-core にしかない内部クラスを使いたくなることがあるでしょう。典型的なものは org.embulk.spi.ExecInternal から取得できます。例えば ExecInternal.getModelManager()org.embulk.config.ModelManager のインスタンスが手に入ります。

ただしそれらの内部クラスは、予告なく互換性を失うことがあると気に留めておいてください。例えば ModelManager の以下のメソッドは v0.11 で削除されました。

  • public <T> T readObject(Class<T>, com.fasterxml.jackson.core.JsonParser)
  • public <T> T readObjectWithConfigSerDe(Class<T>, com.fasterxml.jackson.core.JsonParser)

今後 v0.11 系の開発の中で、テスト用のフレームワークを拡充する予定です。

最後に

これまで Embulk プラグインを開発してくれた方々には、ご面倒をおかけします。

この一連の変更が終了すると、本体の更新がプラグインに影響を及ぼすことが (ゼロとは言えませんが) かなり減るはずです。ぜひご協力をよろしくお願いいたします。

Discussion