Heroku で Payara Micro (Gradle)

ソースは GitHub に上げてます。

https://github.com/opengl-8080/payara-micro-on-heroku

#環境構築
[こっち参照](http://qiita.com/opengl-8080/items/9dc9243c4e68cd0674f8#hello-world)。

#やること
基本は[キクタローさんのこちらの記事][1]を参考にしました。

ただ、こちらのスライドの方法だと、Payara Micro を起動するアプリとは別に Web アプリを作成しておき、 war を手動？で Payara Micro 起動用のアプリに移すことになっている。
そこを１つのリポジトリで作成できるようにしてみる。

#フォルダ構成
```text
|-server/
|  |-src/main/java/sample/heroku/
|  |  `-Main.java
|  |
|  `-build.gradle
|
|-webapp/
|  |-src/main/java/sample/heroku/webapp/
|  |  |-MyApplication.java
|  |  `-HelloResource.java
|  |
|  `-build.gradle
|
|-gradle/
|-build.gradle
|-settings.gradle
|-gradlew
|-gradlew.bat
|-Procfile
`-system.properties
```

Gradle のマルチプロジェクト構成でアプリケーションを作る。

`server` は、 Payara Micro を起動するためのプロジェクト。
`webapp` は、 Payara Micro にデプロイする Web アプリのプロジェクト。

#実装
##Gradle の設定ファイル
```groovy:/build.gradle
subprojects {
    apply plugin: 'eclipse'
    apply plugin: 'java'

    sourceCompatibility = '1.8'
    targetCompatibility = '1.8'

    compileJava.options.encoding = 'UTF-8'

    repositories {
        mavenCentral()
    }

    task stage(dependsOn: ['clean', ':server:installDist', ':webapp:war']) // ★
}

ext {
    payaraDependency = 'fish.payara.extras:payara-micro:4.1.153'
}

task wrapper(type: Wrapper) {
    gradleVersion = '2.7'
}
```

`stage` タスクが実行されたら、 `server` プロジェクトを実行可能な状態にビルドして、 `webapp` プロジェクトを war ファイルにビルドする。

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

mainClassName = 'sample.heroku.Main'
applicationName = 'server'

dependencies {
    compile payaraDependency
}

eclipse.project.name = 'payara-micro-on-heroku-server'

installDist.mustRunAfter clean
```

```groovy:/webapp/build.gradle
apply plugin: 'war'

war.baseName = 'webapp'

dependencies {
    providedCompile payaraDependency
}

eclipse.project.name = 'payara-micro-on-heroku-webapp'

war.mustRunAfter clean
```

###dependsOn で指定したタスクの実行順序を制御する
```groovy:/build.gradle
    task stage(dependsOn: ['clean', ':server:installDist', ':webapp:war'])
```

`stage` タスクが依存するタスクとして `clean`, `:server:installDist`, `:webapp:war` を指定している。
一見するとこの順序でタスクが実行されそうだが、実際はそうならない。

`dependsOn` で並べた依存タスクの実行順序を制御するには、それぞれのタスクの `mustRunAfter()` メソッドを使って以下のように定義しないといけない。

```groovy:/server/build.gradle
installDist.mustRunAfter clean
```

```groovy:/webapp/build.gradle
war.mustRunAfter clean
```

`<後に実行されるタスク>.mustRunAfter <先に実行されるタスク>` とする。

これをしておかないと、ビルドした後に `clean` タスクが実行されて残念なことになる。

##Heroku 用の設定ファイル
```text:Procfile
web: server/build/install/server/bin/server
```

```properties:system.properties
java.runtime.version=1.8
```

`Procfile` で、 `:server:installDist` タスクで生成される起動用スクリプトファイルを叩くようにしている。

##Java 実装
###server
```java:Main.java
package sample.heroku;

import fish.payara.micro.PayaraMicro;

public class Main {
    
    public static void main(String[] args) throws Exception {
        PayaraMicro.getInstance()
            .setHttpPort(Integer.parseInt(System.getenv("PORT")))
            .addDeployment("webapp/build/libs/webapp.war") // ★ :webapp:war タスクで生成される war
            .bootStrap();
    }
}
```

###webapp
```java:MyApplication.java
package sample.heroku.webapp;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/api")
public class MyApplication extends Application {
}
```

```java:HelloResource.java
package sample.heroku.webapp;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/hello")
public class HelloResource {
    
    @GET
    public String hello() {
        return "<h1>Hello Payara Micro on Heroku!!!!</h1>";
    }
}
```

#動作確認
https://payara-micro.herokuapp.com/webapp/api/hello にブラウザからアクセス。

![heroku.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/bc403755-a3ff-573b-f44a-9d7d43cb9af6.jpeg)

※404 エラーが表示される場合はアプリが再起動している最中なので、しばらく（１分ほど）待ってから再度アクセスしたら見れるようになります。

#おまけ（失敗例）
以前、同じように Heroku で Payara Micro を動かすために、以下のような方法を試したことがある。

- プロジェクトは war ファイル用のプロジェクトを１つだけ用意する。
- Payara Micro の依存関係は `providedCompile` で定義しておく。
- `stage` タスクが実行されるときに `Procfile` を動的に生成する。
- `Procfile` には、 Gradle がビルドするときにダウンロードした jar ファイルを使って Payara Micro を立ち上げるようにコマンドを記述する。

`build.gradle` は以下のような感じ。

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

repositories {
    mavenCentral()
}

dependencies {
    providedCompile 'fish.payara.extras:payara-micro:4.1.153'
}

war.baseName = 'webapp'
war.mustRunAfter clean

task wrapper(type: Wrapper) {
    gradleVersion = '2.7'
}

task stage(dependsOn: ['clean', 'war']) << {
    new File('./Procfile').delete()

    // ★ここで Procfile を動的に生成
    configurations
        .providedCompile
        .findAll {it.name =~ /payara-micro.*\.jar/}
        .each { file ->
            def payaraJar = file.absolutePath
            new File('./Procfile').write("web: java \$JAVA_OPTS -jar ${payaraJar} --deploy ${war.archivePath} --port \$PORT")
        }
}
```

`configurations.providedCompile` で、依存関係で指定した jar のパスが指定できるので、それを利用している。

`stage` タスクが実行されると、以下のような `Procfile` が生成される。

```text:Procfile
java -jar /app/tmp/cache/.gradle/caches/modules-2/files-2.1/fish.payara.extras/payara-micro/4.1.153/ce4e16681e0873bf05074cc2017c43e69db7c1e1/payara-micro-4.1.153.jar --deploy /tmp/build_1ca90667e1a4987c5289672cd294bee9/build/libs/webapp.war --port 52002
```

ちゃんと、 Heroku 上のパスで `Procfile` が生成されているっぽい。

しかし、実際に Heroku で動かすと以下のようにエラーが発生する。

```text:Herokuにアップしたときのログ
app[web.1]: Error: Unable to access jarfile /app/tmp/cache/.gradle/caches/modules-2/files-2.1/fish.payara.extras/payara-micro/4.1.153/ce4e16681e0873bf05074cc2017c43e69db7c1e1/payara-micro-4.1.153.jar
heroku[web.1]: Process exited with status 1
heroku[web.1]: State changed from starting to crashed
```

jar にアクセスできないらしい。

当時は原因が分からなかったが、今回試しにサーバーに SSH で繋いでみたところ、なんとなく原因が分かった。

```bash:SSHでサーバーに接続
> heroku run bash

~ $ pwd
/app

~ $ ls
LICENSE   README.md     gradle   gradlew.bat  settings.gradle    webapp
Procfile  build.gradle  gradlew  server       system.properties
```

`/app/tmp` が存在しない。

たぶん、ビルドしている環境とビルド後のアプリケーション（slug）が実行される環境は別で、 Gradle がキャッシュした jar ファイルは実行環境の方に持ってこられていないのだと思う。おそらく。

#参考
- [GlassFish Users Group Japan勉強会で発表させて頂きました | Challenge Java EE !][1]
- [Gradle1.6の新機能mustRunAfterでタスク間の実行順序が制御可能になった #gradle - 豆無日記](http://nobeans.hatenablog.com/entry/2013/06/20/103255)
- [Payara Micro を試してみる - Programming Studio](http://www.coppermine.jp/docs/programming/2015/04/payara-micro.html)

[1]: http://kikutaro777.hatenablog.com/