最小構成の enkan を Gradle で動かす

# 概要
川島さん([@kawasima](https://twitter.com/kawasima/))が開発した Java9 時代のマイクロ Web Framework の enkan を Gradle で動かしてみました。enkan の詳細は下記の記事をご覧ください。

1. [enkanとkotowari 〜 Java9時代の新しいマイクロフレームワーク](http://qiita.com/kawasima/items/723646eb30a91d724352)
2. [マイクロフレームワークEnkan(とKotowari)ではじめるREPL駆動開発](http://www.slideshare.net/kawasima/enkankotowarirepl)……JJUG CCC 2016 Spring のセッション

## なぜ Gradle？
公式のサンプルだと Maven3 を使うことが推奨されていますし、 JJUG CCC の川島さんのセッションでも Maven3 を使うようにとのお話がありました。が、私の PC には Maven が入っていないので、 Gradle で動かせないかと頑張ってみた次第です。

# 作業

## 作業環境

| OS | Windows 10
|:---|:---|
| Java | 1.8.0_91
| Gradle | 2.12

## build.gradle を記述

### application plugin を指定
gradle run で起動するために必要です。

```groovy:plugin
apply plugin: 'application'

mainClassName = 'Main'
```

### Maven repository の指定
oss.sonatype.org を追加します。

```groovy:plugin
repositories {
    maven {
        url "http://oss.sonatype.org/content/repositories/snapshots"
    }
}
```

### dependencies
動かすには下記の依存が必要でした。

```groovy:dependencies 
dependencies {
    compile 'net.unit8.enkan:kotowari:0.1.0-beta1'
    compile 'net.unit8.enkan:enkan-devel:0.1.0-beta2'
    compile 'net.unit8.enkan:enkan-repl-pseudo:0.1.0-beta1'
    compile 'net.unit8.enkan:enkan-component-undertow:0.1.0-beta1'
    compile 'net.unit8.enkan:enkan-component-metrics:0.1.0-beta1'
    testCompile 'junit:junit:4.12'
}
```

## Java クラスの実装
以下4種類のクラスが必要でした。[kotowari-example](https://github.com/kawasima/enkan/tree/master/kotowari-example/src/main/java/kotowari/example)を参考に実装しています。

1. [Main](https://github.com/toastkidjp/enkan_verification/blob/qiita2/src/main/java/Main.java)
2. [SystemFactory](https://github.com/toastkidjp/enkan_verification/blob/qiita2/src/main/java/SystemFactory.java)
3. [ApplicationFactory](https://github.com/toastkidjp/enkan_verification/blob/qiita2/src/main/java/AppFactory.java)
4. [Controller](https://github.com/toastkidjp/enkan_verification/blob/qiita2/src/main/java/Controller.java)

### 実行
```sh:jar作成
$ gradle clean jar
:clean
:compileJava
:processResources UP-TO-DATE
:classes
:jar

BUILD SUCCESSFUL

Total time: 7.629 secs
```

```sh:実行
$ java -jar enkan_verification.jar
java -jar enkan_verification.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.
5 23, 2016 9:45:14 午後 org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 5.2.2.Final
enkan> Connected to server (port = 61613)
```

```sh:起動
/start
5 23, 2016 9:45:21 午後 org.xnio.Xnio <clinit>
INFO: XNIO version 3.3.6.Final
5 23, 2016 9:45:21 午後 org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.3.6.Final
Started server
```

ブラウザから http://localhost:3000/ にアクセスすると 「Hello」 と表示されます。

これでめでたしめでたし……というわけにはいかないようです。

# 問題
jar にする今回の方法だと、せっかくの Hot reloading の機能を使えません。

## run の standardInput を修正
Gradle の run コマンドで標準入力を受け付けられるようにしてみます。

```groovy:run
run {
    standardInput  = System.in
}
```

~~しかし、enkan では標準入力ではなく JLine を使っているようでして、この方法で $ gradle run しても enkan の REPL 操作ができません。standardInput で JLine を指定すればよいのでしょうが、そのやり方がわかりませんでした。~~これは私の勘違いでした。Mac OS であれば $ gradle run で REPLの起動ができることを確認しています。ただ、Windows ではやはり動いておりません。

```shell-session:Building75%で固まってしまう
enkan_verification>gradle run
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:run
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.
5 28, 2016 10:49:01 午前 org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 5.2.2.Final
enkan> Connected to server (port = 61621)
> Building 75% > :run/start
```

<img width="491" alt="cmd.png" src="https://qiita-image-store.s3.amazonaws.com/0/102004/f67a1445-f081-ef77-9c8b-d37a739d2b9b.png">

コマンドプロンプトだけでなく ConEmu や Git Bash でも同様だったので、Windows と gradle run コマンドとの相性なのかと思っています。

### 追記
Linux + urxvt の環境で動作したと @kawasima さんからコメントをいただきました。Windows の環境固有の問題かもしれません。

### 追記2
私の方でも Linux サーバ上で何の問題もなく今回のサンプルコードが動作することを確認しております。Linux であれば Gradle でも(compile 以外は)開発可能です。

Mac OS では Main.java のシャットダウンフック設定の場所を入れ替える必要がありました。

```java:修正
final ReplClient replClient = new ReplClient();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {replClient.close();}));
replClient.start(repl.getPort().get());
// Runtime.getRuntime().addShutdownHook(new Thread(() -> {replClient.close();})); //ここじゃダメ
```

## Hot reloading
enkan には変更を自動で反映できる機能があるのですが、この機能は Maven を利用して再コンパイルを実行しているようでして、 Gradle だけの環境ですと下記の例外が発生します。この点に関しましては、要件に Maven3 と書いてありますので当然のことだと思います。enkan の機能をフルに使って開発したいのであれば、推奨環境を用意しましょう。

```sh
enkan> /compile
java.lang.IllegalStateException: MAVEN_HOME not set
    at enkan.system.devel.MavenCompiler.execute(MavenCompiler.java:22)
    at enkan.system.devel.DevelCommandRegister.lambda$register$5(DevelCommandRegister.java:68)
    at enkan.system.repl.PseudoRepl.lambda$run$8(PseudoRepl.java:151)
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
```

### 追記
[Gradle でのコンパイルができる devel プロジェクト](https://github.com/kawasima/enkan/commit/83531633a7b6bf65403ff931c2c7973660f73ae6)を @kawasima さんが開発してくださいました。現在、develop ブランチで公開されています。

[enkan-devel-gradle](https://github.com/kawasima/enkan/tree/develop/enkan-devel-gradle)


依存関係を更新し、 Main.java を下記の通り書き換えれば使えます。

```java:Main.java(for_Gradle)
import enkan.system.command.MetricsCommandRegister;
import enkan.system.devel.DevelCommandRegister;
import enkan.system.devel.compiler.GradleCompiler;
import enkan.system.repl.PseudoRepl;
import enkan.system.repl.ReplBoot;
import enkan.system.repl.pseudo.ReplClient;
import kotowari.system.KotowariCommandRegister;

public class Main {
    public static void main(final String[] args) throws Exception {
        final PseudoRepl repl = new PseudoRepl(SystemFactory.class.getName());
        ReplBoot.start(
                repl,
                new KotowariCommandRegister(),
                new DevelCommandRegister(new GradleCompiler()),
                new MetricsCommandRegister()
                );

        final ReplClient replClient = new ReplClient();
        replClient.start(repl.getPort().get());
    }
}
```


# まとめ
残念ながら Windows + Gradle では enkan の REPL を動かせませんでした。大人しく Maven で動かせばいいのでしょうが、もうしばらく Gradle で動かせないかを調べてみようと思います。何かご存知のことがございましたら、コメント等でお教えくださいますと幸いです。

---

# 参考
1. [今回のソースコード全体](https://github.com/toastkidjp/enkan_verification/tree/qiita2)
2. [How can I execute a Java application that asks for user input?](https://discuss.gradle.org/t/how-can-i-execute-a-java-application-that-asks-for-user-input/3264)