Help us understand the problem. What is going on with this article?

Heroku で Payara Micro (Gradle)

More than 3 years have passed since last update.

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

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

環境構築

こっち参照

やること

基本はキクタローさんのこちらの記事を参考にしました。

ただ、こちらのスライドの方法だと、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 の設定ファイル

/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 ファイルにビルドする。

/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
/webapp/build.gradle
apply plugin: 'war'

war.baseName = 'webapp'

dependencies {
    providedCompile payaraDependency
}

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

war.mustRunAfter clean

dependsOn で指定したタスクの実行順序を制御する

/build.gradle
    task stage(dependsOn: ['clean', ':server:installDist', ':webapp:war'])

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

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

/server/build.gradle
installDist.mustRunAfter clean
/webapp/build.gradle
war.mustRunAfter clean

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

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

Heroku 用の設定ファイル

Procfile
web: server/build/install/server/bin/server
system.properties
java.runtime.version=1.8

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

Java 実装

server

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

MyApplication.java
package sample.heroku.webapp;

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

@ApplicationPath("/api")
public class MyApplication extends Application {
}
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

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

おまけ(失敗例)

以前、同じように Heroku で Payara Micro を動かすために、以下のような方法を試したことがある。

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

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

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 が生成される。

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 で動かすと以下のようにエラーが発生する。

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 で繋いでみたところ、なんとなく原因が分かった。

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 ファイルは実行環境の方に持ってこられていないのだと思う。おそらく。

参考

opengl-8080
ただのSE。Java好き。
tis
創業40年超のSIerです。
https://www.tis.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした