Dropwizardを使用してMySQLにつなげてみた

###まえがき

いまどきのフレームワークを使用してWebAPIを作成してみる。
ということで、巷で話題のDropwizardにチャレンジしてみたので備忘録として書き留める。

ところで、Dropwizardは、mavenを使用しているためmavenの知識も必要である。
gradleなどで構築している事例もあったので全体像を掴めば構築可能と思われるが、
入門のためmavenを使用して先に進める。

***
###mavenプロジェクトの作成

まずは、mavenプロジェクトとして新規プロジェクトを作成する。
![001_002.png](https://qiita-image-store.s3.amazonaws.com/0/67237/9aac0ad6-1639-cc2f-da09-165a6b398199.png)

![001_003.png](https://qiita-image-store.s3.amazonaws.com/0/67237/d2a4f149-19a2-e1cb-13f8-4b667dee73f4.png)

![001_005.png](https://qiita-image-store.s3.amazonaws.com/0/67237/5aadaa95-9fb1-f84b-d104-1ee93668479d.png)

グループIdは、本来はドメイン利用した一意名にするのが正と思われるが、
ここではテスト用に仮パッケージ名としている。

また、アーティファクトId:についてもsampleAppとした。
![001_006.png](https://qiita-image-store.s3.amazonaws.com/0/67237/f3427ac5-1a80-23c8-614e-b2e841952a6a.png)

開発環境には、<a href="http://mergedoc.sourceforge.jp/">Pleiades All in One 4.4 (Luna) </a>を利用させていただいているが、
この環境下ではデフォルトがJ2SE1.5利用となっているためJava7に変更する。
![001_008.png](https://qiita-image-store.s3.amazonaws.com/0/67237/bf018e70-2c55-53c6-306e-997d72209ae5.png)

プロジェクトsampleApp上にてプロパティを選択。
![001_009.png](https://qiita-image-store.s3.amazonaws.com/0/67237/21d3a419-b12b-da76-0aeb-e22b119a253b.png)

Javaビルド・パスからライブラリータブを選択し、編集をクリック。
JavaSE-1.7(java7)を選択し、完了を押してダイアログを閉じる。
![001_010.png](https://qiita-image-store.s3.amazonaws.com/0/67237/d7978fbe-0047-ac14-ea69-a88e1313994f.png)

OKボタンを押してプロパティウィンドウを閉じると自動ビルドが行われ先ほどの警告が消える。

***
####pom.xmlの編集

以下の条件により、pom.xml を編集する。
　・Hibernateを使用してMySQLと接続する。
　・jarファイルを同包したjarファイルを作成する。
　・依存するライブラリも一緒にjarファイルに含める。

```xml:pom.xml
<project 
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.examples</groupId>
  <artifactId>sampleApp</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>sampleApp</name>

  <properties>
    <dropwizard.version>0.7.1</dropwizard.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <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.examples.sampleApp.SampleApp</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>io.dropwizard</groupId>
      <artifactId>dropwizard-core</artifactId>
      <version>${dropwizard.version}</version>
    </dependency>
    <dependency>
      <groupId>io.dropwizard</groupId>
      <artifactId>dropwizard-hibernate</artifactId>
      <version>${dropwizard.version}</version>
    </dependency>
    <dependency>
      <groupId>io.dropwizard</groupId>
      <artifactId>dropwizard-migrations</artifactId>
      <version>${dropwizard.version}</version>
    </dependency>
    <dependency>
      <groupId>io.dropwizard</groupId>
      <artifactId>dropwizard-testing</artifactId>
      <version>${dropwizard.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.dropwizard</groupId>
      <artifactId>dropwizard-assets</artifactId>
      <version>${dropwizard.version}</version>
    </dependency>
    <dependency>
      <groupId>io.dropwizard</groupId>
      <artifactId>dropwizard-views-freemarker</artifactId>
      <version>${dropwizard.version}</version>
    </dependency>
    <dependency>
      <groupId>io.dropwizard</groupId>
      <artifactId>dropwizard-views-mustache</artifactId>
      <version>${dropwizard.version}</version>
    </dependency>

    <dependency>
      <groupId>io.dropwizard</groupId>
      <artifactId>dropwizard-auth</artifactId>
      <version>${dropwizard.version}</version>
    </dependency>
    <dependency>
      <groupId>io.dropwizard</groupId>
      <artifactId>dropwizard-spdy</artifactId>
      <version>${dropwizard.version}</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.34</version>
    </dependency>

  </dependencies>

</project>
```

注意点としては、propertiesにsourceEncodingを記述する。
これを記述しないと、eclipseでのビルド時に失敗する。

```
  <properties>
    <dropwizard.version>0.7.1</dropwizard.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
```

・dropwizard.version
　使用するdropwizardのバージョンを書く。(2015.1.26現在)
・project.build.sourceEncoding
　ソースコードのエンコーディングを書く。


build時に使用するプラグインとして以下のプラグインを記述。
・maven-shade-plugin
　依存関係を調べて同包するために使用
・maven-jar-plugin
　jarファイルを作成するために使用

dropwizard関連の設定は、dependenciesに記述する。
mysqlのjdbcドライバの設定もdependenciesに記述する。

pom.xmlの編集が終わったら保存する。
自動でビルドが開始される。

正常に終了すると以下のようなディレクトリ構成が表示される。
![001_011.png](https://qiita-image-store.s3.amazonaws.com/0/67237/d01ad57a-37c3-22f5-1288-2d8dcfde169d.png)

***

###dropwizardのお作法に従ってファイルを作成する。

dropwizardの<a href="http://dropwizard.io/getting-started.html">Getting Started</a>にしたがって各ファイルを作成する。

####Configurationファイルの作成
アプリケーションとしての設定をここにまとめる。
yamlファイルから個別設定情報を読み込み設定することが可能。

``` java:SampleAppConfiguration.java

package com.examples.sampleApp;
import io.dropwizard.Configuration;
import io.dropwizard.db.DataSourceFactory;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonProperty;

public class SampleAppConfiguration extends Configuration {
	@Valid
	@NotNull
	private DataSourceFactory database = new DataSourceFactory();

	@JsonProperty("database")
	public DataSourceFactory getDataSourceFactory() {
		return database;
	}

	@JsonProperty("database")
	public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
		this.database = dataSourceFactory;
	}
}
```
App.javaをSampleApp.javaに変更する。

``` java:SampleApp.java
package com.examples.sampleApp;

import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.migrations.MigrationsBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.dropwizard.views.ViewBundle;

import com.examples.sampleApp.Resource.PersonResource;
import com.examples.sampleApp.db.PersonDAO;
import com.examples.sampleApp.entity.Person;

public class SampleApp extends Application<SampleAppConfiguration>
{
  private final HibernateBundle<SampleAppConfiguration> hibernateBundle =
    new HibernateBundle<SampleAppConfiguration>(Person.class) {
      @Override
      public DataSourceFactory 
        getDataSourceFactory(SampleAppConfiguration configuration) {
        return configuration.getDataSourceFactory();
    }
  };

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

  @Override
  public void initialize(Bootstrap<SampleAppConfiguration> bootstrap) {
    bootstrap.addBundle(new AssetsBundle());
    bootstrap.addBundle(new MigrationsBundle<SampleAppConfiguration>() {
      @Override
      public DataSourceFactory getDataSourceFactory
        (SampleAppConfiguration configuration) {
          return configuration.getDataSourceFactory();
      }
    });
    bootstrap.addBundle(hibernateBundle);
    bootstrap.addBundle(new ViewBundle());
  }

  @Override
  public void run(SampleAppConfiguration configuration, Environment environment) {
    final PersonDAO dao = new PersonDAO(hibernateBundle.getSessionFactory());

    //使用するAPIはここで登録する。
    environment.jersey().register(new PersonResource(dao));
  }
}
```

***
####entityの作成
データ受け渡し用entityを作成する。

``` java:Person.java
package com.examples.sampleApp.entity;

import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name = "person")
@NamedQueries(
  { @NamedQuery(
      name = "com.examples.sampleApp.entity.Person.findAll", 
      query = "SELECT p FROM Person p"
    ) }
)
public class Person {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  @Column(name = "fullName", nullable = false)
  private String fullName;

  @Column(name = "jobTitle", nullable = false)
  private String jobTitle;

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getFullName() {
    return fullName;
  }

  public void setFullName(String fullName) {
    this.fullName = fullName;
  }

  public String getJobTitle() {
    return jobTitle;
  }

  public void setJobTitle(String jobTitle) {
    this.jobTitle = jobTitle;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Person)) return false;

    final Person that = (Person) o;

    return 
      Objects.equals(this.id, that.id) && 
      Objects.equals(this.fullName, that.fullName) && 
      Objects.equals(this.jobTitle, that.jobTitle);
  }

  @Override
  public int hashCode() {
    return Objects.hash(id, fullName, jobTitle);
  }
}
```
***

####データベースアクセス用クラスを作成する。
データ転送用にdaoを作成。

``` java:PersonDAO.java
package com.examples.sampleApp.db;

import io.dropwizard.hibernate.AbstractDAO;
import java.util.List;
import org.hibernate.SessionFactory;
import com.examples.sampleApp.entity.Person;
import com.google.common.base.Optional;

public class PersonDAO extends AbstractDAO<Person> {
  public PersonDAO(SessionFactory factory) {
    super(factory);
  }

  public Optional<Person> findById(Long id) {
    return Optional.fromNullable(get(id));
  }

  public Person create(Person person) {
    return persist(person);
  }

  public List<Person> findAll() {
    return list(namedQuery("com.example.core.Person.findAll"));
  }
}
```
***
####URLに対応したリソースファイルを作成
アクセス用URLに対応した処理を記述する。
ここでは、.../person/1 のURLでアクセスすることにより、
個人のデータをデータベースから取得し、表示する。

``` java:PersonResource.java
package com.examples.sampleApp.Resource;

import io.dropwizard.hibernate.UnitOfWork;
import io.dropwizard.jersey.params.LongParam;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.examples.sampleApp.db.PersonDAO;
import com.examples.sampleApp.entity.Person;
import com.google.common.base.Optional;

@Path("/person")
@Produces(MediaType.APPLICATION_JSON)
public class PersonResource {
  private final PersonDAO personDao;

  public PersonResource(PersonDAO personDao) {
    this.personDao = personDao;
  }

  @GET
  @UnitOfWork
  @Path("/{personId}")
  public Person getPerson(@PathParam("personId") LongParam personId) {
    final Optional<Person> person = personDao.findById(personId.get());
    return person.get();
  }
}

```
***
###アプリケーション個別設定ファイルを作成する。
アプリケーション個別設定ファイルは、YAML形式にてプロジェクト直下に作成する。

``` yml:sample.yml
database:
  driverClass: com.mysql.jdbc.Driver
  user: root
  password:
  url: jdbc:mysql://localhost:3306/sample
  properties:
    charSet: UTF-8
    hibernate.dialect: org.hibernate.dialect.MySQLDialect
    hibernate.show_sql: true # ←
  maxWaitForConnection: 1s
  validationQuery: "/* MyApplication Health Check */ SELECT 1"
  minSize: 8
  maxSize: 32
  checkConnectionWhileIdle: false
```
.javaファイルの編集は以上。
作成後のTreeは下図のようになる。

![001_016.png](https://qiita-image-store.s3.amazonaws.com/0/67237/d47d2710-2cb2-fade-8c4d-f4b476dc9427.png)

***

###MySQLの設定
あらかじめインストールされたMySQLにPersonテーブルを作成する。
![001_013.png](https://qiita-image-store.s3.amazonaws.com/0/67237/d7a434ef-d881-72db-a08f-29c16bc65b35.png)

テスト用に2,3件データを入力する。

| id| Full Name   | Job Title |
|:-:|:------------|:-----:|
| 1 | Test Taro   |     IT|
| 2 | Test Jiro   |     SE|
| 3 | Test Hanako |     PM|

***

####Buildと実行
maven clean

```
[INFO] Scanning for projects...
[INFO] 
[INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building sampleApp 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ sampleApp ---
[INFO] Deleting D:\IDE\ws\luna\ws2\test02\sampleApp\target
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.666 s
[INFO] Finished at: 2015-01-26T15:38:45+09:00
[INFO] Final Memory: 5M/120M
[INFO] ------------------------------------------------------------------------
```
maven install

```
[INFO] Scanning for projects...
[INFO] 
[INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building sampleApp 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ sampleApp ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\IDE\ws\luna\ws2\test02\sampleApp\src\main\resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ sampleApp ---
[INFO] Compiling 5 source files to D:\IDE\ws\luna\ws2\test02\sampleApp\target\classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.836 s
[INFO] Finished at: 2015-01-26T15:42:07+09:00
[INFO] Final Memory: 9M/152M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.5.1:compile (default-compile) on project sampleApp: Fatal error compiling: tools.jar not found: D:\IDE\pleiades\java\7\..\lib\tools.jar -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
```

tools.jarがないと怒られるので追記する。
ウィンドウメニューから設定を開き、インストール済みのJREを選択。
java7を選択して編集ボタンを押し、外部Jar追加ボタンよりpleiades/java/7/lib/tools.jarを
追加する。

![001_014.png](https://qiita-image-store.s3.amazonaws.com/0/67237/98ebce1c-890f-f34f-9eaf-c0ddc3306522.png)

再度 maven install を行う。今度は成功した。

```
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 38.336 s
[INFO] Finished at: 2015-01-26T16:19:21+09:00
[INFO] Final Memory: 14M/205M
[INFO] ------------------------------------------------------------------------
```
***
###jarファイルを実行する。
jarファイルが作成できたらコマンドプロンプトから起動する。
プロジェクトの上で右クリックし、コマンドプロンプトを選択。

![001_015.png](https://qiita-image-store.s3.amazonaws.com/0/67237/b997c2e1-9b68-7eea-6c30-2aecd3d50a23.png)

コマンドプロンプト上でjarファイルを実行する。
java -jar target/sampleApp-0.0.1-SNAPSHOT.jar server sampleApp.yml

![001_017.png](https://qiita-image-store.s3.amazonaws.com/0/67237/45454174-bd57-c477-843d-def5357e8dd5.png)

サーバが立ち上がるので、ブラウザから
http://localhost:8080/person/1
へアクセスする。


JSON形式でレスポンスが帰ってくる。

{"id":1,"fullName":"Test Taro","jobTitle":"IT"}


以上。