概要
こちらのSpringOne Platform の参加報告記事を読んでいたら、 Ratpack という Web Application Framework があることを知りましたので、Java の Web アプリケーション開発で使ってみました。
Ratpack とは
Ratpack is a set of Java libraries for building modern HTTP applications.
It provides just enough for writing practical, high performance, apps.
It is built on Java 8, Netty and reactive principles.
(公式サイトより引用)
Java や Groovy で Web アプリケーションを開発できる Micro Web Application Framework だそうです。開発したアプリケーションは fat-jar を作成することで簡単にデプロイできます。
軽く調べてみた限りでは Groovy での開発サンプルが多いようですが、Java でもアプリケーションを開発できるようですので、今回は Java で試してみました。
ちなみに、ratpack という単語について調べてみたところ、あまりよい意味ではありませんでした。Rat Pack(Wikipedia 英語版)を見ると、 Frank Sinatra に関係のある語らしいので、それが由来なのかなと思いました。
ライセンス
Google トレンド
ratpack framework
で調べると下記のグラフが表示されました。このグラフによると、最近急激に注目が高まっているようです。
実行環境
Java SE | 1.8.0_102 |
---|---|
OS | Windows 10 |
Ratpack | 1.4.1 |
依存の追加
依存のダウンロードを始めたところ、結構時間がかかります。
Framework のコア部分で使用されている Netty をはじめ、下記のライブラリが使われているようです。
- Guava
- Jackson
- SLF4J
- Javassist
- SnakeYAML
$ gradle dependencies
:dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
archives - Configuration for archive artifacts.
No dependencies
compile - Dependencies for source set 'main'.
\--- io.ratpack:ratpack-core:1.4.1
+--- io.netty:netty-codec-http:4.1.4.Final
| \--- io.netty:netty-codec:4.1.4.Final
| \--- io.netty:netty-transport:4.1.4.Final
| +--- io.netty:netty-buffer:4.1.4.Final
| | \--- io.netty:netty-common:4.1.4.Final
| \--- io.netty:netty-resolver:4.1.4.Final
| \--- io.netty:netty-common:4.1.4.Final
+--- io.netty:netty-handler:4.1.4.Final
| +--- io.netty:netty-buffer:4.1.4.Final (*)
| +--- io.netty:netty-transport:4.1.4.Final (*)
| \--- io.netty:netty-codec:4.1.4.Final (*)
+--- io.netty:netty-transport-native-epoll:4.1.4.Final
| +--- io.netty:netty-common:4.1.4.Final
| +--- io.netty:netty-buffer:4.1.4.Final (*)
| \--- io.netty:netty-transport:4.1.4.Final (*)
+--- com.google.guava:guava:19.0
+--- org.slf4j:slf4j-api:1.7.21
+--- org.reactivestreams:reactive-streams:1.0.0
+--- com.github.ben-manes.caffeine:caffeine:2.3.1
+--- org.javassist:javassist:3.19.0-GA
+--- com.fasterxml.jackson.core:jackson-databind:2.7.5
| +--- com.fasterxml.jackson.core:jackson-annotations:2.7.0
| \--- com.fasterxml.jackson.core:jackson-core:2.7.5
+--- com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.7.5
| +--- com.fasterxml.jackson.core:jackson-core:2.7.5
| \--- org.yaml:snakeyaml:1.15
+--- com.fasterxml.jackson.datatype:jackson-datatype-guava:2.7.5
| +--- com.fasterxml.jackson.core:jackson-databind:2.7.5 (*)
| \--- com.fasterxml.jackson.core:jackson-core:2.7.5
+--- org.yaml:snakeyaml:1.15
+--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.7.5
| +--- com.fasterxml.jackson.core:jackson-core:2.7.5
| \--- com.fasterxml.jackson.core:jackson-databind:2.7.5 (*)
\--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.5
+--- com.fasterxml.jackson.core:jackson-core:2.7.5
\--- com.fasterxml.jackson.core:jackson-databind:2.7.5 (*)
……省略……
(*) - dependencies omitted (listed previously)
build.gradle の修正
この記事を書いている段階では1.4.1が最新でした。
apply plugin: 'application'
sourceCompatibility = 1.8
mainClassName = "jp.toastkid.ratpack.Main"
repositories {
mavenCentral()
}
dependencies {
compile 'io.ratpack:ratpack-core:1.4.1'
testCompile 'junit:junit:4.12'
}
jar {
manifest {
attributes 'Main-Class' : mainClassName
}
destinationDir = projectDir
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
サンプルアプリケーションを試す
最初の方に出てくるサンプルはテストパッケージのを使っているので今回はスルーし、4 Launchingのサンプルを試します。
import java.net.URI;
import ratpack.server.RatpackServer;
import ratpack.server.ServerConfig;
public class Main {
public static void main(final String... args) throws Exception {
RatpackServer.start(server -> server
.serverConfig(ServerConfig.embedded().publicAddress(new URI("http://company.org")))
.registryOf(registry -> registry.add("World!"))
.handlers(chain -> chain
.get(ctx -> ctx.render("Hello " + ctx.get(String.class)))
.get(":name", ctx -> ctx.render("Hello " + ctx.getPathTokens().get("name") + "!"))
)
);
}
}
起動すると下記の通り出力されます。
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.
WARNING: No slf4j logging binding found for Ratpack, there will be no logging output.
WARNING: Please add an slf4j binding, such as slf4j-log4j2, to the classpath.
WARNING: More info may be found here: http://ratpack.io/manual/current/logging.html
Ratpack started (development) for http://localhost:61762
正常系
ブラウザで http://localhost:61762
にアクセスすると、 Hello World!
と表示されています。
エラーページ
http://localhost:61762/notfound/page
のような適当なパスにアクセスするとエラーページが表示されます。
namepath
http://localhost:61762/toastkid
にアクセスすると、表示されるメッセージが Hello toastkid!
に変化します。
fat-jar から起動
もちろん fat-jar を作ってそれをサーバ上で動かすこともできます。
$ gradle clean jar
:clean UP-TO-DATE
:compileJava
:processResources UP-TO-DATE
:classes
:jar
BUILD SUCCESSFUL
Total time: 3.544 secs
$ java -jar ratpack_example.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.
WARNING: No slf4j logging binding found for Ratpack, there will be no logging output.
WARNING: Please add an slf4j binding, such as slf4j-log4j2, to the classpath.
WARNING: More info may be found here: http://ratpack.io/manual/current/logging.html
Ratpack started (development) for http://localhost:61810
http://localhost:61810
にアクセスすると Hello World!
と表示されます。
いろいろ試す
Ratpack を使ってどのようなことができるのか、いろいろ見ていきます。
ポート番号指定
ServerConfigBuilder の port(int) メソッドで指定できます。
ここでポート番号を指定すると、ランダムではなく、必ずこのポートで起動するようになります。
RatpackServer.start(server -> server
.serverConfig(
ServerConfig.embedded()
.publicAddress(new URI("http://company.org"))
+ .port(8940)
)
GET パラメータ受け取り
GET はそんなに難しくなく、handlers 内に下記を追加するだけで取れます。
context.getRequest().getQueryParams().getOrDefault("paramName", "defaultValue")
もうちょっと長めに取り出すとこのような感じです。
.handlers(chain -> chain
.get(context -> {
final String name = context.getRequest().getQueryParams().getOrDefault("name", "Guest");
context.render(String.format("Hello %s.", name));
})
)
サーバを起動し、ブラウザでアクセスしてみます。上記のコードの場合、パラメータがない時はHello Guest.
、ある時は名前が表示されます。
POST パラメータ受け取り
POST のパラメータを受け取る場合は下記のようにします。
.post("wc/result", context -> {
final Promise<Form> form = context.parse(Form.class);
form.then(f -> {
final String param= f.get("paramName");
context.render(String.format("paramName=%s.", param));
}
エラー表示(Development)
Development 環境ではエラーメッセージが専用の画面で、StackTrace と併せて表示されます。
例えば存在しないパスにアクセスした場合、下記のようなエラー画面が出ます。
ContentType の指定
デフォルトだと text/plain で描画されます。HTML で描画する場合は指定が必要です。
最初は下記のように2行に分けて書いていました。
context.getResponse().contentType(MediaType.TEXT_HTML);
context.render(template.make(renderArgs).toString());
どうやら下記でよいらしいです。
context.getResponse().send(MediaType.TEXT_HTML, template.make(renderArgs).toString());
リソースフォルダの指定
ドキュメントを見ても全然わからなかったので、GitHubのサンプルプロジェクト を見て真似してみたらようやくわかりました。
baseDir を指定
設定を追加します。
server.serverConfig(ServerConfig.embedded()
.baseDir(BaseDir.find())
)
リソースフォルダと同じ階層に .ratpack ファイルを配置
.ratpack
ファイルはただのマーカーなので空ファイルでよいです。Windows では . で始まるファイル名を作ろうとするとエラーになりますので、 IDE から、もしくは touch コマンド(Git Bash 等を入れているなら)で作ります。
例えば、下記のようなフォルダ構成で、public 以下のリソースを参照したいとします。
└─src
├─main
│ └─resources
│ └─public
│ ├─images
│ ├─javascripts
│ │ ├─d3
│ │ └─d3-cloud
│ └─stylesheets
その場合、resources フォルダの直下に .ratpack
ファイルを置いてください。
└─src
├─main
│ └─resources
│ ├─public
│ └─.ratpack
.ratpack
ファイルの配置が適切でないと、
Could not find marker file '.ratpack' via context class loader
というエラーメッセージが出力され、アプリケーションの起動に失敗します。
リソースを指定
JavaScript や CSS や画像ファイルといったリソースを使いたい場合、コード上で下記のように指定します。
chain.prefix(
"アプリケーションで参照するときのパス",
nested -> nested.fileSystem("マーカーファイルの位置から見た、追加したいリソースへのパス", Chain::files)
)
1ファイル単位ないし1フォルダ単位での指定が可能です。下記の場合だと、 public を指定して images と javascripts と stylesheets をまとめてリンクすることはできません。
.handlers(chain -> chain
.prefix("public/images", nested -> nested.fileSystem("public/images", Chain::files))
.prefix("public/javascripts", nested -> nested.fileSystem("public/javascripts", Chain::files))
.prefix("public/stylesheets", nested -> nested.fileSystem("public/stylesheets", Chain::files))
HTML テンプレート
今回は Groovy のテンプレートエンジンを使ってみました。groovy-all の依存を追加するだけで使えます。
dependencies {
+ compile 'org.codehaus.groovy:groovy-all:2.4.5'
}
final Template template = new StreamingTemplateEngine()
.createTemplate(Paths.get(PATH_TO_TEMPLATE).toFile());
final Map<String, String> renderArgs = new HashMap<>();
......
final String html = template.make(renderArgs).toString();
例えば、<title>${title}</title>
というテンプレートを用意した場合、上記の renderArgs
に "title", "タイトル"
というペアを追加しておくと、 html
に <title>タイトル</title>
という文字列が入ります。
Groovy のテンプレートエンジンの詳細については下記のリンク先をご参照ください。
Word Cloud
いつものように Word Cloud アプリケーションを作ってみました。ソースコードは ratpack_word_cloud(GitHub) に置いてありますので、興味がございましたらご覧ください。
リンク
Ratpack
- Official site
- GitHub repository
- GitHubのサンプルプロジェクト
- Learning Ratpack(英語書籍)……無料サンプルあり
- Ratpackについて(前編)
- Ratpackについて(後編)
- Ratpackについて(延長戦)
- Building Web Apps in Ratpack