Vert.x 3系を stand-alone かつ最小構成で動かす

# 概要
リアクティヴプログラミングで Web アプリケーションを構築できる Framework の [Vert.x](http://vertx.io/) 3系を、 Gradle を使って stand-alone で動かしてみました。

---

# Vert.x とは？
> Vert.x is a tool-kit for building reactive applications on the JVM.
出典：[公式サイト](http://vertx.io/)

## Scale
1. イベントドリブン、すべてコールバックで結果が返る……ただし単体だとコールバック地獄に陥りやすいので RxJava との併用が推奨されている
2. Non-blockking I/O
3. 少数のスレッドで大量のコネクションを並行処理

## Polyglot
コアは Java だが、さまざまな言語でのアプリケーション開発をサポート
1. Java
2. JavaScript
3. Groovy
4. Ruby
5. Ceylon

## ライセンス
バージョン 3.3.0.CR2 の時点では [Eclipse Public License version 1.0 と Apache License 2.0 のデュアルライセンス](https://github.com/eclipse/vert.x/blob/3.3.0.CR2/LICENSE-eplv10-aslv20.html) のようです。

## トレンド
[Google トレンドで調べてみました](https://www.google.co.jp/trends/explore#q=Vert.x&date=1%2F2011%2066m&cmpt=q&tz=Etc%2FGMT-9)。

2012/05 と 2013/01 に大きな山があります。[Vert.x History](https://github.com/vert-x3/wiki/wiki/Vert.x-History) によると、 2012/05/09(Wed) には version 1.0 の最終リリース、2013/01 には Eclipse Foundation への参加があったようです。

<img width="638" alt="vertx_trend.png" src="https://qiita-image-store.s3.amazonaws.com/0/102004/f9ae971a-c9fc-042d-4736-5f7bc0e76c11.png">

関連キーワードを見ると、 Node.js との比較を求めるユーザが多いようです。

<img width="276" alt="vertx_trend1.png" src="https://qiita-image-store.s3.amazonaws.com/0/102004/0858ee9a-2d0f-2880-da45-74424dd393eb.png">


---
# 実行環境
| Java | SE 1.8.0_91
|:---|:---
| Eclipse | Mars 4.5.2
| Vert.x  | 3.3.0.CR1
| Gradle  | 2.12

---
# 最小構成で動かす
Vert.x を動かすには Java アプリケーションの開発と別に Framework 本体もダウンロードする必要があると思っていたのですが、本体をダウンロードしなくとも Vert.x の依存を追加するだけで動かすことが可能なようです。ということで、今回は stand-alone で Vert.x アプリケーションを開発し動かしてみます。

## build.gradle の定義

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

sourceCompatibility = 1.8
mainClassName       = "io.vertx.core.Starter"

def defaultEncoding = 'UTF-8'
def vertxVersion    = '3.0.0'
def mainVerticle    = "HelloVerticle"

repositories {
    mavenCentral()
}

dependencies {
  compile "io.vertx:vertx-core:${vertxVersion}"
}

run {
  args = ["run", mainVerticle]
}

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

重要な点は下記の通りです。
1. mainClassName に ```"io.vertx.core.Starter"``` を指定
2. run で 引数に mainVerticle を指定
3. jar manifest に ```Main-Verticle``` と ```Main-Class```、 2つの attributes を指定

## Verticle クラスの定義

```java:HelloVerticle.java
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;

public class HelloVerticle extends AbstractVerticle {

    @Override
    public void start(final Future<Void> future) {
        vertx
            .createHttpServer()
            .requestHandler(r -> 
                r.response().end("<h1>Hello from my first Vert.x 3 application</h1>")
                )
            .listen(8940, result -> {
                if (result.succeeded()) {
                    future.complete();
                } else {
                    future.fail(result.cause());
                }
            });
    }
}
```

このコードでは [AbstractVerticle](https://github.com/eclipse/vert.x/blob/master/src/main/java/io/vertx/core/AbstractVerticle.java)の [Vertx](https://github.com/eclipse/vert.x/blob/master/src/main/java/io/vertx/core/Vertx.java) オブジェクト vertx に対し、下記3つを実施しています。
1. Http Server の作成
2. Request Handler の設定
3. Listen Port の変更

### ファイルツリー

```
$ tree /F
│  build.gradle
│  LICENSE
│  settings.gradle
└─src
    └─main
        └─java
                HelloVerticle.java
```

## 実行

```
$ gradle run
Download https://repo1.maven.org/maven2/io/vertx/vertx-core/3.0.0/vertx-core-3.0.0.jar
Download https://repo1.maven.org/maven2/io/netty/netty-common/4.0.28.Final/netty-common-4.0.28.Final.jar
Download https://repo1.maven.org/maven2/io/netty/netty-buffer/4.0.28.Final/netty-buffer-4.0.28.Final.jar
Download https://repo1.maven.org/maven2/io/netty/netty-transport/4.0.28.Final/netty-transport-4.0.28.Final.jar
Download https://repo1.maven.org/maven2/io/netty/netty-handler/4.0.28.Final/netty-handler-4.0.28.Final.jar
Download https://repo1.maven.org/maven2/io/netty/netty-codec-http/4.0.28.Final/netty-codec-http-4.0.28.Final.jar
Download https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.5.3/jackson-core-2.5.3.jar
Download https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.5.3/jackson-databind-2.5.3.jar
Download https://repo1.maven.org/maven2/io/netty/netty-codec/4.0.28.Final/netty-codec-4.0.28.Final.jar

:compileJava
:processResources UP-TO-DATE
:classes
:run
6 18, 2016 8:45:55 午前 io.vertx.core.Starter
情報: Succeeded in deploying verticle
> Building 75% > :run
```

## 確認
出力が止まったところで http://localhost:8940/ にアクセスし、 "Hello from my first Vert.x 3 application" と表示されていることを確認します。

---

# FatJar を生成
サーバにデプロイする時は FatJar を作って、それをサーバ上で実行します。

```
$ gradle jar
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar

BUILD SUCCESSFUL

Total time: 5.227 secs
```

## 実行
生成された FatJar を java -jar で動かします。

```
$ java -jar vertx_v.jar
6 18, 2016 8:58:30 午前 io.vertx.core.Starter
情報: Succeeded in deploying verticle
```

先ほどと同様、http://localhost:8940/ にアクセスして "Hello from my first Vert.x 3 application" と表示されていることを確認します。

---
# ワードクラウドを生成する Web アプリケーションを作る
Web アプリケーション開発での使い方を勉強するために、小さめの Web アプリケーションを作ってみました。入力された文章を単語に分解して数え上げ、ワードクラウドを生成するものです。

## 構成
| 要素 | 採用
|:---|:---
| テンプレートエンジン | Freemarker
| 単語分解 | tinySegmenter
| ワードクラウド描画用ライブラリ | D3-cloud.js

## 開発
ソースコード全体は [こちら](https://github.com/toastkidjp/vertx_word_cloud/tree/qiita) をご参照ください。以下、ポイントを紹介します。

### build.gradle
Freemarker のテンプレートエンジンを使うため、依存を1つ追加します。

```groovy:build.gradle_dependencies
dependencies {
  compile "io.vertx:vertx-core:${vertxVersion}"
  compile "io.vertx:vertx-web-templ-freemarker:${vertxVersion}" // 追加
}
```

### start メソッド
listen メソッドでポートを 8940 に変えているほか、 routing を route という別メソッドで実行します。

```java
@Override
public void start(final Future<Void> future) {
    vertx
        .createHttpServer()
        .requestHandler(route()::accept)
        .listen(8940, result -> {
            if (result.succeeded()) {
                future.complete();
            } else {
                future.fail(result.cause());
            }
        });
}
```

### route メソッド
パス "/wc" にアクセスがあった際、フォームから送信されたパラメータを受け取り、ワードクラウド描画に必要な形に変換してテンプレートエンジンの描画に必要なパラメータを context に put していきます。

```java
main.get("/wc").handler(context -> {
    context.put("title",  TITLE);

    final String sentence = context.request().getParam("sentence");

    if (sentence == null || sentence.trim().length() == 0) {
        handler.handle(context);
        return;
    }
    context.put("wcData",    new WordCloud().count(sentence));
    context.put("paramSentence", sentence);
    context.put("width",     WIDTH);
    context.put("height",    HEIGHT);
    handler.handle(context);
});
```

### ファイルツリー

```
$ tree /F
│  build.gradle
│  LICENSE
│  settings.gradle
│  
└─src
    └─main
        ├─java
        │  └─jp
        │      └─toastkid
        │          ├─libs
        │          │  └─tinysegmenter
        │          │          CharacterClassifier.java
        │          │          ScoreMap.java
        │          │          TinySegmenter.java
        │          │          
        │          └─wordcloud
        │                  WordCloud.java
        │                  WordCloudVerticle.java
        │                  
        └─resources
            ├─assets
            │  │  index.html
            │  │  
            │  ├─images
            │  │      icon.png
            │  │      
            │  ├─javascripts
            │  │  ├─d3
            │  │  │      d3.js
            │  │  │      LICENSE(d3)
            │  │  │      
            │  │  └─d3-cloud
            │  │          d3.layout.cloud.js
            │  │          index.js
            │  │          LICENSE
            │  │          package.json
            │  │          README.md
            │  │          
            │  └─stylesheets
            │          main.css
            │          
            └─templates
                    c.ftl
                    common_header.ftl
```


## 動かす
```
$ gradle run
:compileJava UP-TO-DATE
:processResources
:classes
:run
6 26, 2016 5:49:49 午後 io.vertx.core.Starter
情報: Succeeded in deploying verticle
> Building 75% > :run
```

http://localhost:8940/wc にアクセスすると下記のように表示されます。

<img width="1079" alt="wcss_0.png" src="https://qiita-image-store.s3.amazonaws.com/0/102004/2a8391ac-724a-1336-317c-4766d69ac268.png">

下部の TextArea に何か文章を入力し、「送信」ボタンを押すと……

<img width="1080" alt="wcss_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/102004/bddfa833-e29e-69cf-88f7-52c038c6cf74.png">

下図のようにワードクラウドが生成されます。

<img width="1080" alt="wcss_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/102004/357a7582-cae0-40ba-f0bc-77ccc1d96372.png">

## 詰まった点

### 先に route で設定した方が優先される
CSS 等の静的リソース用の route は最初に定義しておくとよいでしょう。下記の例だと src/main/resources/assets 以下のファイルが static リソースとして参照可能になります。

```java:static_resources
main.route("/assets/*").handler(StaticHandler.create("assets"));
```

### context に渡したパラメータは、テンプレート側では context.＜パラメータ名＞ で参照しないといけない
[テストで参照している template](https://github.com/vert-x3/vertx-web/blob/d552ab8b2dda317bb15a8a5fa89e4579b4c94b92/vertx-template-engines/vertx-web-templ-freemarker/src/test/filesystemtemplates/test-freemarker-template3.ftl) を見てようやく気付きました。これ、知らないと本当に詰まります。

例えば、 Vert.x App 内で下記のように指定したパラメータ "paramSentence" は

```java:指定
context.put("paramSentence", sentence);
```

テンプレートファイル内で下記のようにして参照します。

```html:参照
<p>${context.paramSentence}</p>
```

### context には data というメソッドがあるので、 data という名前のパラメータをテンプレートファイルに渡して使おうとすると一見意味不明のエラーが出る

```:エラーメッセージ
For "${...}" content: Expected a string or something automatically convertible to string (number, date or boolean), but this has evaluated to a method+sequence (wrapper: f.e.b.SimpleMethodModel):
==> context.data  [in template "templates/freemarker/word_cloud.ftl" at line 38, column 26]
```

「そんな抽象的な名前付けなさんな」ということでしょう。data から別の名前に変えました。

### 文字化け
Windows 環境で Vert.x を動かすと文字化けがついて回って非常にやりづらかったです。 accept-charset で Form の文字コードを指定できると知りました。

```html
<form method="get" action="/wc" accept-charset="UTF-8">
```

### テンプレートファイルのパス
Freemarker テンプレートエンジンを使うと、なぜか「パスの最後の1文字+拡張子」というパスのテンプレートファイルを参照しようとします。ちょっと意味がわからないです。今回はパスが "/wc" なので、 templates/c.ftl という場所にテンプレートファイルを用意しました。なお、テンプレートファイル内から参照するのは特に問題がないようです。

## 課題
### GET でパラメータを受け取っている
普通であれば POST で受け渡しをするものです。Vert.x で POST を使ったデータの受け渡しをやるのが少し手間だったので、今回は GET で妥協してしまいました。

Windows 環境では次のように、 ```context.request().bodyHandler``` のコールバックを利用すれば POST パラメータの "sentence" を取り出せるようです、が、取り出した後の処理が上手く動きませんでした。

```java:動かないコードなのでコピペしないでください
main.post("/wc")
    .handler(context -> {
        context.request().bodyHandler(body -> {
            final String sentence = extractSentence(body).get();
            if (sentence == null || sentence.trim().length() == 0) {
                context.next();
            }
            handler.handle(context);
        });
    });

private Optional<String> extractSentence(final Buffer body) {
    try {
        final String decoded = URLDecoder.decode(new String(body.getBytes(), "shift-jis"), "shift-jis");
        return Stream.of(decoded.split("\\&"))
                    .filter(str -> str.startsWith("sentence"))
                    .map(str -> str.substring("sentence".length() + 2))
                    .findFirst();
    } catch (final UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return Optional.empty();
}
```

解決に挑む際は下記の記事が参考になりそうです。

1. [GroovyでVert.x - 2. HTTPリクエストハンドラ その1 -](http://d.hatena.ne.jp/touchez_du_bois/20120527/1338141796)
2. [Http Server (Java on Vertx) not getting POST parameter](http://stackoverflow.com/questions/30251479/http-server-java-on-vertx-not-getting-post-parameter)

### テンプレートエンジンに Freemarker を使っている
今回は以前作った Freemarker のものを流用しました。できれば [Thymeleaf](http://www.thymeleaf.org/) を使ってみたいです。

Vert.x では下記のテンプレートエンジンが利用可能です。

1. [Thymeleaf](https://github.com/vert-x3/vertx-web/tree/master/vertx-template-engines/vertx-web-templ-thymeleaf)
2. [Pebble](https://github.com/vert-x3/vertx-web/tree/master/vertx-template-engines/vertx-web-templ-pebble)
3. [MVEL](https://github.com/vert-x3/vertx-web/tree/master/vertx-template-engines/vertx-web-templ-mvel)
4. [Jade](https://github.com/vert-x3/vertx-web/tree/master/vertx-template-engines/vertx-web-templ-jade)
5. [Handlebars](https://github.com/vert-x3/vertx-web/tree/master/vertx-template-engines/vertx-web-templ-handlebars)
6. [Freemarker](https://github.com/vert-x3/vertx-web/tree/master/vertx-template-engines/vertx-web-templ-freemarker)

## ソースコード
1. [vertx_word_cloud(GitHub repository)](https://github.com/toastkidjp/vertx_word_cloud/tree/qiita)

---

# 参考

## 今回開発したアプリケーションのソースコード全体
1. [Hello world(GitHub repository)](https://github.com/toastkidjp/vertx_verification)
2. [ワードクラウド(GitHub repository)](https://github.com/toastkidjp/vertx_word_cloud/tree/qiita)

## GitHub repository
1. [Vert.x Core](https://github.com/eclipse/vert.x)
2. [Vert.x Examples](https://github.com/vert-x3/vertx-examples)

## Documents
1. [Vertx3 and Gradle Application Plugin](http://beckje01.com/blog/2015/05/17/vertx3-and-gradle-application-plugin/)
2. [My first Vert.x 3 Application](http://vertx.io/blog/my-first-vert-x-3-application/)
3. [Vert.x 公式サイト](http://vertx.io/)

## Vert.x & RxJava の開発事例
1.[RxJava + Vert.x + jOOλ で Microservice的な何かを作ってみた](https://speakerdeck.com/chimerast/rxjava-plus-vert-dot-x-plus-jool-te-microservicede-nahe-kawozuo-tutemita)

## HTML Form
1. [ページの文字コードとは違う任意の文字コードをformから送信する方法](http://www.webbibo.com/blog/htmlcss/accept-charset.html)