Ratpack を Java の Micro Web Application Framework として使う

# 概要
こちらの[SpringOne Platform の参加報告記事](http://ameblo.jp/principia-ca/entry-12187086771.html)を読んでいたら、 [Ratpack](https://ratpack.io/) という Web Application Framework があることを知りましたので、Java の Web アプリケーション開発で使ってみました。

## Ratpack とは
> Ratpack is a set of Java libraries for building modern HTTP applications.
It provides just enough for writing practical, high performance, apps.
It is built on Java 8, Netty and reactive principles.
([公式サイト](https://ratpack.io/)より引用)

Java や Groovy で Web アプリケーションを開発できる Micro Web Application Framework だそうです。開発したアプリケーションは fat-jar を作成することで簡単にデプロイできます。

軽く調べてみた限りでは Groovy での開発サンプルが多いようですが、Java でもアプリケーションを開発できるようですので、今回は Java で試してみました。

ちなみに、ratpack という単語について調べてみたところ、[あまりよい意味ではありませんでした](https://kotobank.jp/ejword/ratpack)。[Rat Pack(Wikipedia 英語版)](https://en.wikipedia.org/wiki/Rat_Pack_(disambiguation))を見ると、 Frank Sinatra に関係のある語らしいので、それが由来なのかなと思いました。

### ライセンス
[Apache License 2.0](https://github.com/ratpack/ratpack/blob/ecab039cdf9fe31faebd4f228e638b4e3f879cb6/LICENSE.txt) です。

### [Google トレンド](https://www.google.co.jp/trends/explore?date=today%2012-m&q=ratpack%20framework)
```ratpack framework``` で調べると下記のグラフが表示されました。このグラフによると、最近急激に注目が高まっているようです。

<img width="832" alt="ratpack_trend.png" src="https://qiita-image-store.s3.amazonaws.com/0/102004/a1e038d7-f5f4-4b13-c4a2-303bf1d6d203.png">

----

# 実行環境

| Java SE | 1.8.0_102
|:---|:---
| OS | Windows 10
| Ratpack | 1.4.1

----

# 依存の追加
依存のダウンロードを始めたところ、結構時間がかかります。
Framework のコア部分で使用されている Netty をはじめ、下記のライブラリが使われているようです。

1. Guava
2. Jackson
3. SLF4J
4. Javassist
5. SnakeYAML

```:dependencies&nbsp;tree
$ gradle dependencies
:dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

archives - Configuration for archive artifacts.
No dependencies

compile - Dependencies for source set 'main'.
\--- io.ratpack:ratpack-core:1.4.1
     +--- io.netty:netty-codec-http:4.1.4.Final
     |    \--- io.netty:netty-codec:4.1.4.Final
     |         \--- io.netty:netty-transport:4.1.4.Final
     |              +--- io.netty:netty-buffer:4.1.4.Final
     |              |    \--- io.netty:netty-common:4.1.4.Final
     |              \--- io.netty:netty-resolver:4.1.4.Final
     |                   \--- io.netty:netty-common:4.1.4.Final
     +--- io.netty:netty-handler:4.1.4.Final
     |    +--- io.netty:netty-buffer:4.1.4.Final (*)
     |    +--- io.netty:netty-transport:4.1.4.Final (*)
     |    \--- io.netty:netty-codec:4.1.4.Final (*)
     +--- io.netty:netty-transport-native-epoll:4.1.4.Final
     |    +--- io.netty:netty-common:4.1.4.Final
     |    +--- io.netty:netty-buffer:4.1.4.Final (*)
     |    \--- io.netty:netty-transport:4.1.4.Final (*)
     +--- com.google.guava:guava:19.0
     +--- org.slf4j:slf4j-api:1.7.21
     +--- org.reactivestreams:reactive-streams:1.0.0
     +--- com.github.ben-manes.caffeine:caffeine:2.3.1
     +--- org.javassist:javassist:3.19.0-GA
     +--- com.fasterxml.jackson.core:jackson-databind:2.7.5
     |    +--- com.fasterxml.jackson.core:jackson-annotations:2.7.0
     |    \--- com.fasterxml.jackson.core:jackson-core:2.7.5
     +--- com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.7.5
     |    +--- com.fasterxml.jackson.core:jackson-core:2.7.5
     |    \--- org.yaml:snakeyaml:1.15
     +--- com.fasterxml.jackson.datatype:jackson-datatype-guava:2.7.5
     |    +--- com.fasterxml.jackson.core:jackson-databind:2.7.5 (*)
     |    \--- com.fasterxml.jackson.core:jackson-core:2.7.5
     +--- org.yaml:snakeyaml:1.15
     +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.7.5
     |    +--- com.fasterxml.jackson.core:jackson-core:2.7.5
     |    \--- com.fasterxml.jackson.core:jackson-databind:2.7.5 (*)
     \--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.5
          +--- com.fasterxml.jackson.core:jackson-core:2.7.5
          \--- com.fasterxml.jackson.core:jackson-databind:2.7.5 (*)

……省略……

(*) - dependencies omitted (listed previously)
```

# build.gradle の修正
この記事を書いている段階では1.4.1が最新でした。

```groovy:build.gradle
apply plugin: 'application'

sourceCompatibility = 1.8
mainClassName       = "jp.toastkid.ratpack.Main"


repositories {
  mavenCentral()
}

dependencies {
  compile 'io.ratpack:ratpack-core:1.4.1'
  testCompile 'junit:junit:4.12'
}

jar {
  manifest {
    attributes 'Main-Class'   : mainClassName
  }
  destinationDir = projectDir
  from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
```

# サンプルアプリケーションを試す
最初の方に出てくるサンプルはテストパッケージのを使っているので今回はスルーし、[4 Launching](https://ratpack.io/manual/current/launching.html)のサンプルを試します。

```java
import java.net.URI;

import ratpack.server.RatpackServer;
import ratpack.server.ServerConfig;

public class Main {
    public static void main(final String... args) throws Exception {
        RatpackServer.start(server -> server
                .serverConfig(ServerConfig.embedded().publicAddress(new URI("http://company.org")))
                .registryOf(registry -> registry.add("World!"))
                .handlers(chain -> chain
                  .get(ctx -> ctx.render("Hello " + ctx.get(String.class)))
                  .get(":name", ctx -> ctx.render("Hello " + ctx.getPathTokens().get("name") + "!"))
                )
              );
    }
}
```

起動すると下記の通り出力されます。

```:起動
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
WARNING: No slf4j logging binding found for Ratpack, there will be no logging output.
WARNING: Please add an slf4j binding, such as slf4j-log4j2, to the classpath.
WARNING: More info may be found here: http://ratpack.io/manual/current/logging.html
Ratpack started (development) for http://localhost:61762
```

## 正常系
ブラウザで ```http://localhost:61762``` にアクセスすると、 ```Hello World!``` と表示されています。

## エラーページ
```http://localhost:61762/notfound/page``` のような適当なパスにアクセスするとエラーページが表示されます。

## namepath
```http://localhost:61762/toastkid``` にアクセスすると、表示されるメッセージが ```Hello toastkid!``` に変化します。

----

# fat-jar から起動
もちろん fat-jar を作ってそれをサーバ上で動かすこともできます。

```:fat-jarの作成
$ gradle clean jar
:clean UP-TO-DATE
:compileJava
:processResources UP-TO-DATE
:classes
:jar

BUILD SUCCESSFUL

Total time: 3.544 secs
```

```:実行
$ java -jar ratpack_example.jar
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
WARNING: No slf4j logging binding found for Ratpack, there will be no logging output.
WARNING: Please add an slf4j binding, such as slf4j-log4j2, to the classpath.
WARNING: More info may be found here: http://ratpack.io/manual/current/logging.html
Ratpack started (development) for http://localhost:61810
```

```http://localhost:61810``` にアクセスすると ```Hello World!``` と表示されます。

----
# いろいろ試す
Ratpack を使ってどのようなことができるのか、いろいろ見ていきます。

## ポート番号指定
ServerConfigBuilder の port(int) メソッドで指定できます。
ここでポート番号を指定すると、ランダムではなく、必ずこのポートで起動するようになります。

```diff:port(int)
RatpackServer.start(server -> server
                .serverConfig(
                        ServerConfig.embedded()
                            .publicAddress(new URI("http://company.org"))
+                           .port(8940)
                            )
```

## GET パラメータ受け取り
GET はそんなに難しくなく、handlers 内に下記を追加するだけで取れます。

```java:GET
context.getRequest().getQueryParams().getOrDefault("paramName", "defaultValue")
```

もうちょっと長めに取り出すとこのような感じです。

```java:GET
.handlers(chain -> chain
  .get(context -> {
      final String name = context.getRequest().getQueryParams().getOrDefault("name", "Guest");
      context.render(String.format("Hello %s.", name));
  })
)
```

サーバを起動し、ブラウザでアクセスしてみます。上記のコードの場合、パラメータがない時は```Hello Guest.```、ある時は名前が表示されます。

## POST パラメータ受け取り
POST のパラメータを受け取る場合は下記のようにします。

```java:POST
.post("wc/result", context -> {
    final Promise<Form> form = context.parse(Form.class);
    form.then(f -> {
        final String param= f.get("paramName");
        context.render(String.format("paramName=%s.", param));
    }
```

## エラー表示(Development)
Development 環境ではエラーメッセージが専用の画面で、StackTrace と併せて表示されます。

例えば存在しないパスにアクセスした場合、下記のようなエラー画面が出ます。

![スクリーンショット (2).png](https://qiita-image-store.s3.amazonaws.com/0/102004/8409f7a1-350d-c8cc-5512-b6d9fa840af1.png)


## ContentType の指定
デフォルトだと text/plain で描画されます。HTML で描画する場合は指定が必要です。

最初は下記のように2行に分けて書いていました。

```java:ContentTypeを指定してrender
context.getResponse().contentType(MediaType.TEXT_HTML);
context.render(template.make(renderArgs).toString());
```

どうやら下記でよいらしいです。

```java:ContentTypeを指定してrender
context.getResponse().send(MediaType.TEXT_HTML, template.make(renderArgs).toString());
```

## リソースフォルダの指定
ドキュメントを見ても全然わからなかったので、[GitHubのサンプルプロジェクト](https://github.com/ratpack/example-ratpack-gradle-java-app) を見て真似してみたらようやくわかりました。

### baseDir を指定
設定を追加します。

```java:baseDirを指定
server.serverConfig(ServerConfig.embedded()
    .baseDir(BaseDir.find())
)
```

### リソースフォルダと同じ階層に .ratpack ファイルを配置
```.ratpack``` ファイルはただのマーカーなので空ファイルでよいです。Windows では . で始まるファイル名を作ろうとするとエラーになりますので、 IDE から、もしくは touch コマンド(Git Bash 等を入れているなら)で作ります。

例えば、下記のようなフォルダ構成で、public 以下のリソースを参照したいとします。

```:ファイルツリー
└─src
    ├─main
    │  └─resources
    │      └─public
    │          ├─images
    │          ├─javascripts
    │          │  ├─d3
    │          │  └─d3-cloud
    │          └─stylesheets
```

その場合、resources フォルダの直下に ```.ratpack``` ファイルを置いてください。

```:ファイルツリー
└─src
    ├─main
    │  └─resources
    │      ├─public
    │      └─.ratpack
```

```.ratpack``` ファイルの配置が適切でないと、

> Could not find marker file '.ratpack' via context class loader

というエラーメッセージが出力され、アプリケーションの起動に失敗します。

### リソースを指定
JavaScript や CSS や画像ファイルといったリソースを使いたい場合、コード上で下記のように指定します。

```java
chain.prefix(
    "アプリケーションで参照するときのパス",
    nested -> nested.fileSystem("マーカーファイルの位置から見た、追加したいリソースへのパス", Chain::files)
)
```

1ファイル単位ないし1フォルダ単位での指定が可能です。下記の場合だと、 public を指定して images と javascripts と stylesheets をまとめてリンクすることはできません。

```java:chain.prefixでフォルダ単位でリンク
.handlers(chain -> chain
    .prefix("public/images",      nested -> nested.fileSystem("public/images",      Chain::files))
    .prefix("public/javascripts", nested -> nested.fileSystem("public/javascripts", Chain::files))
    .prefix("public/stylesheets", nested -> nested.fileSystem("public/stylesheets", Chain::files))
```

## HTML テンプレート
今回は Groovy のテンプレートエンジンを使ってみました。groovy-all の依存を追加するだけで使えます。

```diff:build.gradleに依存を追加
dependencies {
+  compile 'org.codehaus.groovy:groovy-all:2.4.5'
}
```

```java:Templateの用意
final Template template = new StreamingTemplateEngine()
    .createTemplate(Paths.get(PATH_TO_TEMPLATE).toFile());
```

```java:パラメータのバインディング
final Map<String, String> renderArgs = new HashMap<>();
......
final String html = template.make(renderArgs).toString();
```

例えば、```<title>${title}</title>``` というテンプレートを用意した場合、上記の ```renderArgs``` に ```"title", "タイトル"``` というペアを追加しておくと、 ```html``` に ```<title>タイトル</title>``` という文字列が入ります。

Groovy のテンプレートエンジンの詳細については下記のリンク先をご参照ください。

1. [Groovy のテンプレートエンジン](http://beta.mybetabook.com/showpage/51053be10cf2ffb79bb047f0)
1. [Template engines](http://docs.groovy-lang.org/latest/html/documentation/template-engines.html)

----
# Word Cloud
いつものように Word Cloud アプリケーションを作ってみました。ソースコードは [ratpack_word_cloud(GitHub)](https://github.com/toastkidjp/ratpack_word_cloud/tree/qiita2) に置いてありますので、興味がございましたらご覧ください。

----

# リンク

## Ratpack
1. [Official site](https://ratpack.io/)
1. [GitHub repository](https://github.com/ratpack/ratpack)
2. [GitHubのサンプルプロジェクト](https://github.com/ratpack/example-ratpack-gradle-java-app)
1. [Learning Ratpack(英語書籍)](http://shop.oreilly.com/product/0636920037545.do)……[無料サンプル](http://cdn.oreillystatic.com/oreilly/booksamplers/9781491921661_sampler.pdf)あり
1. [Ratpackについて(前編)](http://grimrose.bitbucket.org/blog/html/2014/12/06/g_advent_calendar_2014_ratpack_01.html)
1. [Ratpackについて(後編)](http://grimrose.bitbucket.org/blog/html/2014/12/07/g_advent_calendar_2014_ratpack_02.html)
1. [Ratpackについて(延長戦)](http://grimrose.bitbucket.org/blog/html/2014/12/31/g_advent_calendar_2014_ratpack_overtime.html)
1. [Building Web Apps in Ratpack](http://www.slideshare.net/danveloper/slides-27337436)

## Groovy テンプレートエンジン
1. [Groovy のテンプレートエンジン](http://beta.mybetabook.com/showpage/51053be10cf2ffb79bb047f0)
1. [Template engines](http://docs.groovy-lang.org/latest/html/documentation/template-engines.html)
1. [Groovyのヒアドキュメント](http://d.hatena.ne.jp/Kazuhira/20130715/1373878511)

## Groovy での Ratpack 開発
1. [[Groovy]RatpackでシンプルなWEBアプリケーションを開発する -インストール&Hello World-](http://qiita.com/saba1024/items/10151863c35e5c64f541)
1. [[Groovy]RatpackでシンプルなWEBアプリケーションを開発する -基本的なHTTPルーティング-](http://qiita.com/saba1024/items/efcc75a6685240188b6b)
1. [[Groovy]RatpackでシンプルなWEBアプリケーションを開発する -JSONとJSONPを扱う-](http://qiita.com/saba1024/items/4cb5a44196865b2d8b3d)