ちょっと話題の記事

[Java] 今年流行るかもしれないDropwizardフレームワークを使ってみる

2014.01.09

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Javaが動く環境だけあればOKなWebサービスフレームワーク

また新しいJavaのフレームワークかよ!と思ったのですが、
実際に少し使ってみると、いままでにない(自分が知らなかった)タイプのフレームワークでした。
今回は、(一部で)今年大ブレイクが予想されているらしい、「Dropwizard」というフレームワークの紹介をします。

Dropwizardは元々YammerのバックエンドWebサービスを提供するために作られたフレームワークだそうです。
このフレームワークが持つ基本的な機能は以下のとおりです。

  • 組み込みWebサーバ(Jetty)
  • JaxRSベースのRESTフレームワーク(Jersey)
  • ORM
  • Metricsを収集するためのライブラリや監視ツール

機能だけを見るとどこにでもあるようなフレームワークかと思いますが、
特徴的なのはこのフレームワークを使用したアプリのデプロイ方法です。
Dropwizardは、ビルド時に依存するライブラリをすべて1つのjarにパッケージングし、
組み込みのJettyサーバを利用します。
なお、ビルドするためのツールはmaven/gradle/sbt等なんでもOKです。

デプロイとして必要な作業はパッケージングされたjarファイルとymlファイルを置いて、
通常のJavaアプリケーションとして起動するだけです。

java -jar yourDropWizardApp.jar server your-dropwizard-config.yml

環境変数はymlファイルに定義しておくので、Java実行環境だけあればどこでも動作します。
では、ここを参考に、Mavenを使用してDropWizardアプリを試してみましょう。

環境構築方法

今回使用した動作環境は以下のとおりです。

  • OS : MacOS X 10.9
  • Java : 1.7.0_45
  • Maven : 3.1.1

サンプルプロジェクト作成

まずはMavenでひな形となるプロジェクトを作成します。

% mkdir dropwizard && cd dropwizard
% mvn archetype:create -DgroupId=com.example -DartifactId=sample
% cd sample

pomの作成

pom.xmlに、次のようにdropwizardライブラリ追加し、
maven-shade-pluginプラグインとmaven-jar-pluginを追加しましょう。

・
・
<build>
<plugins>
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>1.6</version>
    <configuration>
        <createDependencyReducedPom>true</createDependencyReducedPom>
        <filters>
            <filter>
                <artifact>*:*</artifact>
                <excludes>
                    <exclude>META-INF/*.SF</exclude>
                    <exclude>META-INF/*.DSA</exclude>
                    <exclude>META-INF/*.RSA</exclude>
                </excludes>
            </filter>
        </filters>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.example.HelloWorldService</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
        <archive>
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
            </manifest>
        </archive>
    </configuration>
</plugin>
    </plugins>
</build>


<dependencies>
    <dependency>
      <groupId>com.yammer.dropwizard</groupId>
      <artifactId>dropwizard-core</artifactId>
      <version>0.6.2</version>
    </dependency>
</dependencies>
・
・

Configクラスの作成

pom.xmlができたらConfigurationクラスを作成します。
src/main/java/com/exampleディレクトリにHelloWorldConfiguration.javaを次のように作成します。

package com.example;

import com.yammer.dropwizard.config.Configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
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;
    }
}

環境固有のパラメータを指定Configurationクラスでは環境固有のパラメータを設定します。
パラメータは、この後作成するymlファイルで指定します。

pom.xmlと同じ階層に、下記内容でhello-world.ymlを作成します。
ここで記述しているtemplateとdefaultNameはHelloWorldConfigurationのフィールドと対応します。

template: Hello, %s!
defaultName: Stranger

サービスクラスの作成

次にアプリのエンドポイントとなるサービスクラスを作成します。
ここのmainメソッドでrunメソッドを呼び出します。runメソッドではこのあと作成するリソースクラスを登録したり、
healthチェックの登録をしたりしています。

package com.example;

import com.yammer.dropwizard.Service;
import com.yammer.dropwizard.config.Bootstrap;
import com.yammer.dropwizard.config.Environment;

import com.example.resources.HelloWorldResource;
import com.example.health.TemplateHealthCheck;

public class HelloWorldService extends Service<HelloWorldConfiguration> {
    public static void main(String[] args) throws Exception {
        new HelloWorldService().run(args);
    }

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


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

}

リソースクラスの作成

次に、JSON形式のレスポンスを返すため、POJOを作成します。
src/main/java/com/example/coreディレクトリを作成し、Saying.javaを作成しましょう、

package com.example.core;

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;
    }
}

そしてsrc/main/java/com/example/resourcesにリソースクラスを作成します。(HelloWorldResource.java)
このクラスはアノテーションでURIに紐付いています。

package com.example.resources;

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

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 defaultName;
    private final AtomicLong counter;

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

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

/hello-worldに対してGETリクエストを送ると、sayHelloメソッドが実行されます。
返り値には先ほど作成したSayingを返しています。

Health Checkクラスの作成

最後に、src/main/java/com/example/healthディレクトリを作成し、TemplateHealthCheck.javaを作成しましょう。
このクラスはアプリの状態をチェックするために必要になります。

package com.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 doesn't include a name");
        }
        return Result.healthy();
    }
}

ビルドと動作確認

ソースの記述がおわったらビルドします。

% mvn package
・
・
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.955s
[INFO] Finished at: Thu Jan 09 14:31:06 JST 2014
[INFO] Final Memory: 7M/81M
[INFO] ------------------------------------------------------------------------

ビルドが成功すると、targetディレクトリにsample-1.0-SNAPSHOT.jarができています。
javaコマンドで起動してみましょう。

% java -jar target/sample-1.0-SNAPSHOT.jar server hello-world.yml

ブラウザでhttp://localhost:8080/hello-worldにアクセスしてみてください。
レスポンスがJSONで表示されます。また、nameパラメータを指定することもできます。
そして、http://localhost:8081/healthにアクセスすると、healthチェック情報を確認することができます。

まとめ

とりあえずGetting Startedのサンプルを動かしてみました。
APサーバを用意しなくてもjarを1つ渡せば環境構築の手間も省けますし、
簡単にWebサービスを動作させることができるのは非常に便利なのではないでしょうか。

参考サイトなど