ソースは GitHub に上げてます。
#環境構築
こっち参照。
#やること
基本はキクタローさんのこちらの記事を参考にしました。
ただ、こちらのスライドの方法だと、Payara Micro を起動するアプリとは別に Web アプリを作成しておき、 war を手動?で Payara Micro 起動用のアプリに移すことになっている。
そこを1つのリポジトリで作成できるようにしてみる。
#フォルダ構成
|-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 の設定ファイル
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 ファイルにビルドする。
apply plugin: 'application'
mainClassName = 'sample.heroku.Main'
applicationName = 'server'
dependencies {
compile payaraDependency
}
eclipse.project.name = 'payara-micro-on-heroku-server'
installDist.mustRunAfter clean
apply plugin: 'war'
war.baseName = 'webapp'
dependencies {
providedCompile payaraDependency
}
eclipse.project.name = 'payara-micro-on-heroku-webapp'
war.mustRunAfter clean
###dependsOn で指定したタスクの実行順序を制御する
task stage(dependsOn: ['clean', ':server:installDist', ':webapp:war'])
stage
タスクが依存するタスクとして clean
, :server:installDist
, :webapp:war
を指定している。
一見するとこの順序でタスクが実行されそうだが、実際はそうならない。
dependsOn
で並べた依存タスクの実行順序を制御するには、それぞれのタスクの mustRunAfter()
メソッドを使って以下のように定義しないといけない。
installDist.mustRunAfter clean
war.mustRunAfter clean
<後に実行されるタスク>.mustRunAfter <先に実行されるタスク>
とする。
これをしておかないと、ビルドした後に clean
タスクが実行されて残念なことになる。
##Heroku 用の設定ファイル
web: server/build/install/server/bin/server
java.runtime.version=1.8
Procfile
で、 :server:installDist
タスクで生成される起動用スクリプトファイルを叩くようにしている。
##Java 実装
###server
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
package sample.heroku.webapp;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/api")
public class MyApplication extends Application {
}
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 にブラウザからアクセス。
※404 エラーが表示される場合はアプリが再起動している最中なので、しばらく(1分ほど)待ってから再度アクセスしたら見れるようになります。
#おまけ(失敗例)
以前、同じように Heroku で Payara Micro を動かすために、以下のような方法を試したことがある。
- プロジェクトは war ファイル用のプロジェクトを1つだけ用意する。
- Payara Micro の依存関係は
providedCompile
で定義しておく。 -
stage
タスクが実行されるときにProcfile
を動的に生成する。 -
Procfile
には、 Gradle がビルドするときにダウンロードした jar ファイルを使って Payara Micro を立ち上げるようにコマンドを記述する。
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
が生成される。
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 で動かすと以下のようにエラーが発生する。
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 で繋いでみたところ、なんとなく原因が分かった。
> 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 ファイルは実行環境の方に持ってこられていないのだと思う。おそらく。
#参考