Dropwizardで気軽にユーザ向けのツールを作成する


DropwizardはRESTなフルスタックフレームワークです。
しかし、一部の機能を利用するだけで、ユーザに配布するようなツール作成時にとても役に立ちます。

ここで気軽と思う条件は

1. プロジェクトの作成が楽
2. buildが楽
3. javaだけでなくgroovyでも開発できる
4. fat jarで配布・実行が楽
5. 利用者が設定ファイルで値を変更できる(例えばユーザ毎の設定とか)

です。
この中で主にDropwizardに関連するのは最後の条件だけですが、これが非常に便利でした。
設定ファイルの入力バリデーターやオブジェクト変換などをDropwizardが担ってくれます。
さらにソースがすっきりします。

他の1〜4の条件は、Gradle、mavenのどちらでも可能です。この記事ではGradleを使います。

1の条件をgradleでやるのは気軽という意味で重要なので、別途記事にすると思います。(気が向いたらですが。。)
簡単に書くと
Mavenのarchetype catalogでプロジェクトを生成し、それで作ったpom.xmlを```gradle init```で変換するのが気楽かなと思います。
([gradle-templates](https://github.com/townsfolk/gradle-templates/wiki/Usage)やgradle initだけでもある程度できるけど、まだmaven archetypeに追いついていない気がする。たぶん時間が解決するはずです。)

# dropwizard-configuration

まずはGradleを使って2〜4の条件を解決します。

mainClassNameには自分のメインクラスを記述します。

```gradle shadowJar```コマンドで実行可能なfat-jarを作成してくれます。

``` build.gradle
import javax.security.auth.login.Configuration;
import javax.security.auth.login.ConfigurationSpi;

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'groovy'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'

mainClassName='com.github.ko2ic.Main'

sourceCompatibility = 1.7
version = '1.0'

repositories { mavenCentral() }

buildscript {
    repositories { jcenter() }
    dependencies { classpath 'com.github.jengelman.gradle.plugins:shadow:1.0.3' }
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.0-beta-1'
    compile 'io.dropwizard:dropwizard-configuration:0.7.1'
    testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

uploadArchives {
    repositories {
        flatDir { dirs 'repos' }
    }
}

jar {
    manifest {
        attributes('Implementation-Title': 'spike-groovy-executable-jar', 'Implementation-Version': version)
    }
}

shadowJar {
    tasks.clean.execute()
    appendManifest { attributes 'Main-Class': mainClassName }
    exclude 'META-INF/*.SF'
    exclude 'META-INF/*.DSA'
    exclude 'META-INF/*.RSA'

    mergeServiceFiles()
}


startScripts {
    applicationName = 'startSpike'
    defaultJvmOpts = ['-Xms256m', '-Xmx1024m']
}
```

注目すべきは、dependenciesのdropwizard-configurationです。

Dropwizardの設計が優れている点の一つに、機能ごとにjarが独立している点です。
ちゃんと意味のある単位でプロジェクトを依存性を考えて作成しています。
なので、dropwizard-configurationだけを切り出して、webではない別のツールでも利用できるのです。

# 設定ファイルを使うための実装

二つのクラスを作成するだけです。
今回はgroovyで書いていますが、javaでもほとんど同じです。

DropwizardのConfiguration機能を使うための実装です。
たったこれだけで設定ファイル関連のエラー処理、オブジェクト変換がされます。

```ConfigFactory.groovy
package com.github.ko2ic.configuration;

import io.dropwizard.configuration.DefaultConfigurationFactoryFactory
import io.dropwizard.configuration.FileConfigurationSourceProvider
import io.dropwizard.jackson.Jackson

import javax.validation.Validation

class ConfigFactory {

    static Config create(filePath)  {
        def objectMapper = Jackson.newObjectMapper();
        def defaultFactory = new DefaultConfigurationFactoryFactory();
        def validator = Validation.buildDefaultValidatorFactory().getValidator();
        def provider = new FileConfigurationSourceProvider();
        def configFactory = defaultFactory.create(Config.class, validator, objectMapper, "");
        if (filePath) {
            return configFactory.build(provider, filePath);
        }
        return configFactory.build();
    }
}
```

以下は、作成するツールの設定ファイルのオブジェクト表現です。
アノテーションで設定ファイルのバリデーションができます。
このクラスは当然ツールごとに違います。

```
package com.github.ko2ic.configuration;

import javax.validation.Valid
import javax.validation.constraints.NotNull

import com.fasterxml.jackson.annotation.JsonProperty


class Config {

    @JsonProperty("names")
    @Valid
    @NotNull
    def names = [] as HashSet
}
```

ちなみにこのツールの設定ファイルは以下です。

```conf.yml
names: 
    - test1 
    - test2 
    - test3
```

# 使い方

コマンド引数に上記設定ファイルのパスを渡します。
例えば、以下のような感じです。

```
$ java -jar spike-groovy-executable-jar-1.0-all.jar ../../conf.yml
```

ユーザには、シェルやbatファイルを一緒に付けるといいでしょう。
(```gradle startShadowScripts```がうまく動かないため、```gradle distShadowZip```コマンドで作成したscriptファイルを変更すると楽です。)

メインクラスでは、設定ファイルのパスを先ほど作った```ConfigFactory#create()```に渡して、結果がオブジェクトとして渡されるので、それを利用してツールを実装していくことになります。

```Main.groovy
package com.github.ko2ic

import com.github.ko2ic.configuration.ConfigFactory


class Main{
    static void main(String[] args){

        assert args.length > 0 ,'Please specify configuration file.'

        def config = ConfigFactory.create(args[0])
        config.names.each {
            println it
        }
    }
}
```

# 参考

このソースは、以下に置きました。
https://github.com/ko2ic/spike-groovy-executable-jar