こんにちわ、みけです。
不勉強なもので、
SinatraライクなJavaのwebアプリケーション・サーバーの
Sparkなるものがあるということを、
きしださんのブログで初めて知ったので、
試してみました。
準備
maven centralレポジトリーにartifactがあるので、
それを入手します。
repositories {
mavenCentral()
}
dependencies {
compile 'com.sparkjava:spark-core:2.0.0'
}
コードを書く
コードは至ってシンプルで、
- HTTPメソッド
- パス
- ハンドラー
を記述します。
package sample; import static spark.Spark.*; public class SparkSampleApplication { public static void main(String[] args) { get("/", (req, res) -> "<!DOCTYPE html>" + "<html>" + "<head>" + "<title>" + "spark sample" + "</title>" + "</head>" + "<body>" + "<h1>" + "Hello, World!" + "</h1>" + "</body>" + "</html>"); } }
実行する
public static void main
に記述しているので、
サーブレットコンテナとかの設定なしに、すぐに起動できます。
localhostのポート4567にアクセスすれば、
上記で作成したページを見ることができます。
超お手軽です。
herokuで動かすための工夫
ローカルで遊ぶ分には、ここまでの手順で十分ですが、
せっかくなのでherokuにのせましょう。
以前、僕が書いた記事
に基いて、herokuアプリ化していきます。
気をつけるポイントは
- herokuに乗せる場合にはポート番号を環境変数から取得する必要がある(もちろんローカルでは環境変数にないので
null
の扱いをうまくやる必要がある) public static void main
で始まるアプリケーションなのでmanifestを準備してエントリーポイントを設定する必要がある- gradle buildだけでは必要ライブラリーが同梱されないので、fat jarを作る必要がある。
- その他もろもろ
ポート番号の扱い
null
な値も入ってくる可能性を考慮して、
ここはJava8らしくOptional<V>
を使いましょう。
先ほどのルーティングを設定するコードの直前にポートの設定を行います。
import java.util.Optional; import static spark.Spark.*; public class SparkSampleApplication { public static void main(String[] args) { Optional<String> optionalPort = Optional.ofNullable(System.getenv("PORT")); optionalPort.ifPresent(p -> { int port = Integer.parseInt(p); setPort(port); }); /* 以下略 */ } }
環境変数$PORT
に値が設定されていれば、
それをSparkBase#setPort
に渡して、port番号設定します。
System.getenv("PORT")
で環境変数$PORT
が設定されていなければ、
Optional<String>#ifPresent(Consumer)
でConsumerの処理が実行されません。
よってポートはデフォルトの値が利用されます。
なお、SparkBase
のstaticメソッドを読んでいるはずなのに、
importにSpark
しかないのは、
Spark
クラスがSparkBase
クラスを継承したクラスであるためです。
manifestファイルの作成
これはgradleの書き方でいくらでも作れます。
jar { manifest { attributes('Main-Class' : 'sample.SparkSampleApplication') } }
先ほどのpublic static void main
メソッドを
エントリーポイントとして認識するmanifest.fileが
作成されます
fat jarの作成
gradleのbuildでは、特に設定がなければ、
プロジェクトのjarを作るだけです。
これをProcfileで一々-cp
オプションをつけて
ライブラリーのリンクをするのも面倒なので、
fat jarにしました。
fat jarを作るbuild.gradleは次のような感じになります。
jar { from { configurations.compile.collect { it.isDirectory()? it : zipTree(it) } configurations.runtime.collect { it.isDirectory()? it : zipTree(it) } } }
その他もろもろ
stage
タスクの作成
herokuはgradleアプリケーションの場合stage
タスクを呼び出すので、
そこでビルドをさせます。
task stage (dependsOn: 'clean') {
finalizedBy build
}
最初task stage(dependsOn: ['clean', 'build'])
と書いてみたのですが、
なんかstage
タスクが終了するとbuild
ディレクトリがごっそりなくなっている
という謎の事態が発生したので、
finalizedBy
でbuild
タスクを最後に実行するようにしています。
なお、heroku上でテストを実行したくない場合は、
stage.finalizedBy jar
でもよいです。
Procfile
ファイルの作成
Procfile
にはアプリケーションを起動する場合のコマンドを書いておきます。
今回は実行可能jarを作成したので、それを起動するコマンドを書きます。
web: java $JAVA_OPTS -jar build/libs/project-name-version.jar
system.properties
ファイルの作成
これ重要です。
今回はJava8で動かしますので、system.properties
にもJava8を指定するように書きます。
java.runtime.version=1.8
herokuアプリの作成
以下、git管理において、herokuアプリを作成して、herokuにプッシュします。
$ git init $ git add . $ git commit -m 'first commit' $ heroku apps:create your-app-name $ git push heroku master
まとまらないまとめ
こんな感じで作成したのが、こちらです。
トータルで15分くらいでアプリを公開出来ました。
Javaが冗長でアプリを作るのに時間がかかるとdisってる皆さんも、
一度は試されてみてはいかがでしょうか?