DropwizardのHello World

2014年にブレイクすると聞いたのでDropwizardのHello Worldを動かしてみた。

[Getting Started](http://dropwizard.codahale.com/getting-started/)を上から順番に進めていく。

# プロジェクト作成

サンプルとは違ってMavenではなくGradleでプロジェクトを作る。
全体的にパッケージは変更してある。
あとfatjarを作るための設定も含めておく。

```groovy:build.gradle
apply plugin: 'idea'
apply plugin: 'java'

def defaultEncoding = 'UTF-8'
def jdkVersion = '1.7'

repositories {
	mavenCentral()
}

dependencies {
    compile 'com.yammer.dropwizard:dropwizard-core:0.6.2'
}

compileJava {
    options.encoding = defaultEncoding
}
compileTestJava {
    options.encoding = defaultEncoding
}

idea {
	project {
		jdkName = jdkVersion
		languageLevel = jdkVersion
	}
}

jar {
    from (configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {
        exclude 'META-INF/MANIFEST.MF'
        exclude 'META-INF/*.SF'
        exclude 'META-INF/*.DSA'
        exclude 'META-INF/*.RSA'
    }
    manifest {
        attributes('Main-Class': 'example.service.HelloWorldService')
    }
}
```

# Configurationクラスの作成

yamlファイルから設定値を受け取るクラスを作成する。
jarを叩いた時に指定されたyamlファイルの内容がこのクラスに設定される。

```java:HelloWorldConfiguration.java
package example.configuration;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.yammer.dropwizard.config.Configuration;
import org.hibernate.validator.constraints.NotEmpty;

public class HelloWorldConfiguration extends Configuration {

    @NotEmpty
    @JsonProperty
    private String template;

    @NotEmpty
    @JsonProperty
    private String defaultName = "Stranger";

    public String getTemplate() {
        return template;
    }

    public String getDefaultName() {
        return defaultName;
    }
}
```

yamlファイルは以下の様な感じ。
このファイルは/src/main/resourcesじゃなくて/settingに置いた。

```yaml:hello-world.yml
template: Hello, %s!
defaultName: Stranger
```

# Serviceクラスの作成

Mainクラスとして実行されるクラスを作成する。
HelloWorldService#runメソッド内でResourceクラスとかHealth Checkクラスを登録している。

```java:HelloWorldService.java
package example.service;

import com.yammer.dropwizard.Service;
import com.yammer.dropwizard.config.Bootstrap;
import com.yammer.dropwizard.config.Environment;
import example.configuration.HelloWorldConfiguration;
import example.health.TemplateHealthCheck;
import example.resource.HelloWorldResource;

public class HelloWorldService extends Service<HelloWorldConfiguration> {
    @Override
    public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
        bootstrap.setName("hello-world");
    }

    @Override
    public void run(HelloWorldConfiguration configuration, Environment environment) throws Exception {
        final String template = configuration.getTemplate();
        final String defaultName = configuration.getDefaultName();
        environment.addResource(new HelloWorldResource(template, defaultName));
        environment.addHealthCheck(new TemplateHealthCheck(template));
    }

    public static void main(String[] args) throws Exception {
        new HelloWorldService().run(args);
    }
}
```

# Representationクラスを作成する

Jsonで返す値を保持するPOJOを作成する。

```java:Saying.java
package example.representation;

public class Saying {

    private final long id;
    private final String content;

    public Saying(long id, String content) {
        this.id = id;
        this.content = content;
    }

    public long getId() {
        return id;
    }

    public String getContent() {
        return content;
    }
}
```

# Resourceクラスを作成する

JerseyのResourceクラスを作成する。
Serviceクラスで登録する必要がある。

```java:HelloWorldResource.java
package example.resource;

import com.google.common.base.Optional;
import com.yammer.metrics.annotation.Timed;
import example.representation.Saying;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.concurrent.atomic.AtomicLong;

@Path("/hello-world")
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorldResource {

    private final String template;
    private final String defaltName;
    private final AtomicLong counter;

    public HelloWorldResource(String template, String defaltName) {
        this.template = template;
        this.defaltName = defaltName;
        this.counter = new AtomicLong();
    }

    @GET
    @Timed
    public Saying sayHello(@QueryParam("name") Optional<String> name) {
        return new Saying(counter.incrementAndGet(), String.format(template, name.or(defaltName)));
    }
}
```

# Health Checkクラスを作成する

設定の不備なんかをチェックするクラスを作成する？
↑の認識で合ってるかよくわかんない。
Serviceクラスで登録する必要がある。

```java:TemplateHealthCheck.java
package example.health;

import com.yammer.metrics.core.HealthCheck;

public class TemplateHealthCheck extends HealthCheck {

    private final String template;

    public TemplateHealthCheck(String template) {
        super("template");
        this.template = template;
    }

    @Override
    protected Result check() throws Exception {
        final String saying = String.format(template, "TEST");
        if (!saying.contains("TEST")) {
            return Result.unhealthy("template dosen't include a name");
        }
        return Result.healthy();
    }
}
```

# fatjarを作成する。

Dropwizardを動かすためにこれまで作成してきたクラスとDropwizardが必要とするクラスを一つのjarにまとめる。
build.gradleに設定は書いてあるので`gradle jar`を叩くだけ。

# Hello Worldを動かす。

プロジェクト直下で`java -jar build/libs/xxx.jar server setting/hello-world.yml`を叩く。
xxx.jarは`gradle jar`で作成したjarファイル。

[localhost:8080](http://localhost:8080/hello-world?name=World)で動いているのが確認できる。
[localhost:8081](http://localhost:8081/healthcheck)ではHealth Checkクラスの動作確認ができる。

こんな感じになるはず。

![dropwizard.png](https://qiita-image-store.s3.amazonaws.com/0/17179/183ed268-dc78-9998-26e7-ad5531d2d8a1.png)