GradleでWildflyを自動でダウンロードしてからArquillianを実行する

Java EEなアプリケーションのテストを回すのに Arquillian が便利なんですけど、 サンプル書くのに手動でアプリケーションサーバをダウンロードするのはちょい面倒なので、 Gradleにダウンロードしてもらうっていう話です。

アプリケーションサーバは Wildfly を使用します。

それではbuild.gradleをいじりましょー!

まずdependenciesにWildflyのアーカイブを追加します。

dependencies {
    archives "org.wildfly:wildfly-dist:$wildflyVersion@zip"
}

次にアーカイブをunzipするタスクを書きます。

task readyWildfly(type: Copy) {
    def wildflyZip = configurations.archives.find { it.name ==~ /wildfly.*/ }

    from zipTree(wildflyZip)
    into buildDir

    inputs.file wildflyZip
    outputs.upToDateWhen { new File(buildDir, "wildfly-$wildflyVersion").exists() }
}

既にunzipされたWildflyがあったらタスクをスキップしてほしいので inputs.fileoutputs.upToDateWhen でこちょこちょやっています。

最後にテストを実行する前にreadyWildflyタスクを実行するようにします。

test.dependsOn readyWildfly

あとは gradlew build するだけ。

ログはこんな感じ。

:compileJava
:processResources UP-TO-DATE
:classes
:war
:assemble
:readyWildfly
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

Total time: 31.107 secs

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

testの前にreadyWildflyタスクが実行されていますね。 この時点で所定の場所へWildflyがunzipされています。

ではもう一度 gradlew build してみましょう。

:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:war UP-TO-DATE
:assemble UP-TO-DATE
:readyWildfly
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build UP-TO-DATE

BUILD SUCCESSFUL

Total time: 10.617 secs

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

なんということでしょう! readyWildflyタスクがスキップされま……されてない!?

あれー???(´・_・`)

も、もう一度実行だ!

:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:war UP-TO-DATE
:assemble UP-TO-DATE
:readyWildfly UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build UP-TO-DATE

BUILD SUCCESSFUL

Total time: 8.532 secs

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

なぜスキップされたし(・_・)

というわけで、なんかあと一歩で出来てない感がありますが、一番の目的である 「自動でWildflyの準備してArquillian走らせる」ってのは出来たので一旦これで良いやー。

Doma 2.2.0からEntityListenerをDIコンテナから取得できるようになった #doma2

Doma 2.1.0までは EntityType 実装クラス(コンパイル時に自動生成されるクラス)のコンストラクタ内で単純にインスタンス化されていましたが、 Doma 2.2.0からは ConfiggetEntityListenerProvider というメソッドが追加され、 そのメソッドが返す EntityListenerProvider をカスタマイズすることで EntityListener のインスタンス取得をフックできるようになりました。

EntityListenerProviderEntityListener のインスタンスを取得する get メソッドを持っています。 EntityListenerProvider.get メソッドのデフォルト実装は次のようになっています。

default <ENTITY, LISTENER extends EntityListener<ENTITY>> LISTENER get(
        Class<LISTENER> listenerClass, Supplier<LISTENER> listenerSupplier) {
    return listenerSupplier.get();
}

ご覧のように単純に Supplier.get メソッドを実行しているだけです。

この EntityListenerProvider.get メソッドをオーバーライドしてDIコンテナから EntityListener のインスタンスを取得する例を書きます。 この例ではGuiceを使用しており Config 実装クラスと EntityListenerProvider 実装クラスもGuiceで管理しています。

まずは EntityListenerProvider 実装クラス。 Guiceの Injector をインジェクションしてそこから EntityListener のインスタンスを取得しています。

package sample;

import java.util.function.Supplier;

import javax.inject.Inject;

import org.seasar.doma.jdbc.EntityListenerProvider;
import org.seasar.doma.jdbc.entity.EntityListener;

import com.google.inject.Injector;

public class SampleEntityListenerProvider implements EntityListenerProvider {

    @Inject
    private Injector injector;

    @Override
    public <ENTITY, LISTENER extends EntityListener<ENTITY>> LISTENER get(
            Class<LISTENER> listenerClass, Supplier<LISTENER> listenerSupplier) {
        return injector.getInstance(listenerClass);
    }
}

次に Config 実装クラス。 EntityListenerProvider をインジェクションしてそのまま返しているだけです。

package sample;

import javax.inject.Inject;
import javax.sql.DataSource;

import org.seasar.doma.jdbc.Config;
import org.seasar.doma.jdbc.EntityListenerProvider;
import org.seasar.doma.jdbc.dialect.Dialect;

public class SampleConfig implements Config {

    @Inject
    private EntityListenerProvider entityListenerProvider;

    @Inject
    private DataSource dataSource;

    @Inject
    private Dialect dialect;

    @Override
    public EntityListenerProvider getEntityListenerProvider() {
        return entityListenerProvider;
    }

    @Override
    public DataSource getDataSource() {
        return dataSource;
    }

    @Override
    public Dialect getDialect() {
        return dialect;
    }
}

Guice以外のDIコンテナでも似たような方法を取れるでしょう。 例えばCDIだと Injector ではなく BeanManager をインジェクションして BeanManager から EntityListener 実装クラスのインスタンスをルックアップすると良いと思います。 (CDI 1.1以降であれば CDI.current().select(listenerClass) でも良いと思います)

EntityListener をDIコンテナから取得できるようになると色々とインジェクションできるのも嬉しいですし、 インターセプターをかますことも出来たりしてさらに嬉しいですね!!!