JavaOne 2017 レポート 10/2 Day-2 キーノート&技術セッション
さて、Day-2からがいよいよ本番です。
これ以降、技術的セッションとか真面目に掘り下げると色々破綻するので(理解できてないとか時間が足りないとか)、ざっくり駆け足のレポートでお茶を濁していきます。
なお、当日の見聞きした(気がしている)そのままであり全部裏を取ってる訳ではないとか、そもそも発表自体が「現状ではこの予定です」みたいなものがあったり(Safe Harbor Statement)なので、これ以降の技術内容については不正確・誤り等がある可能性がありますが、その辺はご承知おきください。
IntelliJ IDEA Tips and Tricks
ひたすら、IntelliJ IDEAのTIPSを紹介していくだけのセッション。 とりあえず、同じ時間帯で他にめぼしいのがなかったので、耳慣らしを兼ねて。
結構普通に使ってる機能が多かったのですが、↓あたりは使いこなしてないので、使ってみようかなと思います。
(以下、メモから書き起こしただけなので名称やキーバインドは間違ってるかも)
- Search Everywhere (Shiftx2)
- Recent files (Cmd+E)
- タブ表示を無しにしても開いているファイルを簡単に行き来できる
- Productifity Guide
- テンプレート系
- psvm ->
public static void main
の略でメインメソッドを生成する - toar -> コレクションから配列に変換する(?)
- Template list (Cmd+J)
- どんなテンプレートがあるか覚えてなくてもリストから選べばよい
- psvm ->
- Extract Variables (Option+Cmd+V)
- fキーで抽出定義した変数宣言に
final
をつけるかどうかをトグルできる
- fキーで抽出定義した変数宣言に
- Next Error (F2)
- 警告やエラーの下線部分にジャンプしていく
- 補完時に
h.f
おようにドット区切りで書いてから補完するとhoge.foo()
のようにメソッドまで一気に補完できる - コンテキストにあった補完 (Shift+Cmdx2)
- Navigate Delcaration (Cmd+B)
- Stringリテラル内の文字列に対して、XMLやSQLなどの別のフォーマットを指定できる
- Abbreviationを自分で定義すると便利
- (Templateとの違いがよくわかってない)
- Stream Debugger
Improving Your Groovy Kung-Fu
Groovy in Actionの著者であるDierk Konig氏のGroovyのTIPS的セッション。 スライドなしで、Groovy Consoleやテキストエディタを使って、ライブ的に進める流れ。
- sdkmanのサブセットっぽい、senvというオレオレスクリプトをネタに、GroovyでのスクリプトコーディングのTipsを説明
- IPアドレスを取得するワンライナで、JavaのAPIがそのまま使えるよ、みたいな話
- GParsのDataflowsを使うと、parallelで明示的に並列処理&JOINするより並行処理的知識を隠蔽できる
- GroovyFXでGUIがコンパクトなスクリプトで書ける
みたいな、(期待はしていませんが)ほぼ既知の内容でした。
GParsのDataflowsは名前は知っているものの実際に使ったことがなかったので、ちょっと気になりました。
import static groovyx.gpars.dataflow.Dataflow.task def df = new Dataflows() task { df.z = df.x + df.y } task { df.x = 10 } task { df.y = 5 } println "Result: ${df.z}"
こんな感じのコードが書けます。 非同期処理においてそれぞれの値が確定する時間差などを気にせずに、宣言的に記述できるのが良さそう。
細かいですが、逆の意味で気になったのは、次のような例外catch部分。
try { //... } catch (all) { println "Error: $all" }
Groovyではcatch節でも型を省略できるんですが、その場合Exception
型を指定したときと同じになります。
慣習的に変数名としてe
あたりが無難ですが、all
というのはなぁ。
「型指定してないし、Exception
型以下ごっそりキャッチしまっせ」的な気持ちかもしれないけど、あまり同意しかねる感じ。
ちょうどつい先日、StackOverflowの回答のサンプルコードで見かけて「変なの」と思ってたので、引っかかりました。
Java Keynote
午後からはMoscone Northの地下会場で、基調講演を聞きました。 内容は各種メディア系ですでに素晴らしい感じでまとめられてるので割愛1。
What's New in Gradle 4.0
Gradle 4.0(一部3.xも含む)で追加された新機能などをざっと紹介していくセッション。
- パフォーマンス向上系
- New Tools
- Java Library Plugin
- プロダクトが「アプリケーション」なのか「ライブラリ」なのかで依存性のあり方が違うはずである
- 今までの
cmpile
とruntime
というコンフィギュレーションの分け方では非効率 - 新しく
api
とimplementation
というコンフィギュレーション形式にした。- ライブラリとして使う部にも意識させたい依存性は
api
として定義する - 単なる内部実装にすぎなくて、使う側に意識させたくない/させる必要がない場合は、
implementation
として定義する
- ライブラリとして使う部にも意識させたい依存性は
- Java以外のNative系のビルド
- Parallel Compile/Linkingがデフォルトサポート
- Composite Building
- 複数のPJ間依存関係において、いい感じにビルドを連携する(?)
- Java Library Plugin
- User Experience
Module Development with JDK 9
3部構成で、JDK 9でモジュールを活用した開発をするために必要な情報を整理したセッション。
- 第1部 「モジュールは再利用のためのパッケージのセットである」
- 以下のような流れで変遷してる
- Programs are Classes
- Programs are Packages
- Programs are Modules ←イマココ
- Java 8までの
public
は、Java 9で3つに細分化された- public for everyone (モジュール内外を問わず、誰でもアクセスできる。Java 8のpublic相当)
- public to only friend (モジュール内は誰でもOK、モジュール外からは
to
で指定したモジュールからだけアクセスできる) - public only within a module (同一モジュール内の全てのクラスに対してはpublicだけど、モジュール外からはアクセスできない)
- JARファイル単位=再利用単位
- Fat Jarのように、JARファイルの中に依存先のJARファイルを同梱している訳ではない
- 複数のモジュール(JAR)で、同じパッケージを作ることはできない
- それぞれのモジュール内で、「だれが開発したか(どのモジュールに含まれているか)」がわかるようなパッケージ名を使う必要がある
- (他のJava 9対応HowTo系のセッションをみると、この制約に対する修正について割と手間がかかりそうな雰囲気でした)
- それぞれのモジュール内で、「だれが開発したか(どのモジュールに含まれているか)」がわかるようなパッケージ名を使う必要がある
- 以下のような流れで変遷してる
- 第2部 モジュールへのマイグレーション
- jdepsツールを使うと便利 (java 8から同梱済み)
- 既存プログラムの依存性分析ができる
- Automatic Modules
- 命名規約に基づいて、モジュール化未対応のライブラリもモジュールとして扱える
- モジュール依存関係に登場する全てを
requires
に指定したような依存関係を持つとみなされる
- jdepsツールを使うと便利 (java 8から同梱済み)
- 第3部 Modular JDK
本日のディナー
JCP Partyでウェーーイとビールや軽食をいただいた後、若干怪しい道を通ってベトナム料理店へ。 だいぶお腹いっぱいな中の2軒目でしたが安くて美味しかったです。
JavaOne 2017 レポート 10/1 Day-1 Community Day
JavaOneの初日はCommunity Dayということで、コミュニティ主催だったり、どちらかというと技術よりもコミュニティ活動自体にフォーカスしたセッションの多い日です。
この日にトークを控えていたスピーカの方には大変心苦しいですが、個人的には耳慣らし&気持ちを高めていく前哨戦のような感じで、割とリラックスして臨んでます。
The Road to Contributing to the Java Technology Community by Developing a Tool
我がグループが誇る世界のクボタさんがなんとダブルヘッダで登壇です。 さすがです。 HeapStatsの開発を通して、以下にJavaコミュニティに貢献していくのか、という戦略・戦術についてのトークでした。
Secrets of Rock Star Developers (and How to Become One!)
2本目の方は、毎回JavaOneにブラジル国旗をマントのように羽織って闊歩する有名人のBruno氏がモデレートする大人気セッションです。
今回も満員御礼でしたが、その聴衆の前で世界のJava開発者に向けて胸熱のメッセージを発信されていました。 カッコよかったです。マジリスペクトです1。
WIT Lightning Storm (Unconference)
Day-1、最後のセッションは同じく日本から参加のjavajoのよこなさんセッション。 2コマ枠ぶち抜きで、色々な立場の女性による10〜15分くらいのショートトークを繋げていく感じの流れ。
スライドなしのライブ感溢れるトークが連続して、正直初日からだいぶやられましたが、よこなさんも堂々たるトークでした。すごい。写真はないです。
本日のディナー
フェリービルディングのシーフードレストランで、牡蠣三昧とIPAを堪能しました。
その後、Thirsty Bearsの2Fで開催中のAppDev Partyに合流して更にビールづくしでした。
-
このぐらい褒めておけばだいたい大丈夫ですかね。↩
JavaOne 2017 レポート 9/30 Day-0 サンフランシスコへ
1年以上放置してたはてなブログ、久しぶりの投稿です。
2010年、2011年から長い充電期間を経て、6年ぶり3回目のJavaOneに参加してきました。 一応、今回も会社の出張です。ありがたやありがたや。
以前はHilton、Nikko、Parc 55あたりの会議室でやっていたのですが、今年から経費削減なのか原点回帰してほとんどのセッションがMoscone Westになってました(去年もホテルの会議室だった、というのは他の参加者の方に聞きました)。 一部のハンズオン系がHiltonなどの別会場を使ってましたが、Moscone WestもDay-4までで終了で、Day-5は全セッションがMariottホテルが会場でした。
で、結局、自分が選んだセッションはDay-5以外は全部Moscone会場でした。 せっかく色々画策して、ベースキャンプ地をHiltonにしたのに会場が遠くて少し残念です(1Fに降りればすぐに会場があると思ってた)。
空路
この日は単なる移動日です。
以前の2回は成田発のANA便でしたが、今回は、エコノミーなのに微妙に席幅が広いとか機内食が美味しいという噂の羽田発JAL便を使ってみました。 実際、座席幅も足元も余裕があってとても快適でした。
現地到着前のランチは噂のAir吉野家。まあまあ美味しかったです。
到着
一応、ホテル近くの通りにはJavaOneのノボリ(?)が掲示されてました。
本日のディナー
人生初レベルの標高を誇る本格アメリカンステーキを堪能しました。
WindowsのコマンドプロンプトでGrails 3開発する(エンコーディング地獄編)
どうも、ひさしぶりにはてダで書きます。
昨今、ソースコードはUTF-8で書くのがデファクトだと思うんですが、WindowsでのJavaはデフォルトエンコーディングが相変わらずwindows-31jなので、何をするにも明示的にエンコードを指定しないといけません。 あと、コマンドプロンプトはデフォルトでUTF-8表示できないので、実動作はOKだけど出力が文字化けする、みたいな話もあって、結局どうしたらいいんだっけ、というのをGrails 3向きに整理してみました。
普段Windowsで開発していないので、もっといい方法があったら是非教えてください。 ちなみに、Windowsのバージョンは未だ7です。
Windows 10のbashがきたら、LANGとかうまくJavaに伝わっていい感じにデフォルトエンコーディング問題は解消するんですかね。どうなんですかね。
結論 (2016/06/03時点)
仕込み
以下を設定する。前者はプロジェクトに対して1回、後者は環境に対して1回設定すればOK.
- gradlew.bat内のjava起動行の末尾に
"-Dfile.encoding=UTF-8"
(ダブルクォートがポイント)を追加する。
74: @rem Execute Gradle 75: "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% "-Dfile.encoding=UTF-8"
- 環境変数
GRAILS_OPTS=-Dfile.encoding=UTF-8
をグローバルに設定する(コンパネ等)。
開発時
コマンドプロンプト上でアプリを実行する場合は、gradlewコマンドを使う。
gradlew.bat bootRun
テストをする場合もgradlewがよい。
gradlew.bat test
create-xxxxなどを使う場合は、grailsコマンドを使う。
grailsコマンドを使ってアプリ実行やテストをする場合は、コマンドプロンプトへの標準出力が文字化けするが気にしなければOK。 文字化けした内容が気になるのであればgradlewで再実行すれば良い。
ちなみに、chcp 65001
を使ってコマンドプロンプト自体をUTF-8表示可能なようにカスタマイズすれば、grailsコマンドonlyでうまいこといける気はするが、色々ググったり試したりしてもいまいちいい感じに設定できなかったため、ひとまず上記での運用を勧めてみる。
試行錯誤編
- 注意事項
gradlewコマンド編
(A) 普通に起動する
gradlew clean gradlew bootRun
- →×
- applicaiton.ymlに日本語がある場合起動時にエラーになる
- ※実行時エラーではあるが、ビルド時に誤ったエンコーディング(windows-31j)でビルドされていると、実行時に正しいUTF-8を指定してもNGとなる(原因は後述)。
- applicaiton.ymlに日本語がある場合起動時にエラーになる
... Caused by: java.nio.charset.MalformedInputException: Input length = 2 at java.nio.charset.CoderResult.throwException(CoderResult.java:281) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:339) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) at java.io.InputStreamReader.read(InputStreamReader.java:184) at org.yaml.snakeyaml.reader.UnicodeReader.read(UnicodeReader.java:123) at java.io.Reader.read(Reader.java:140) at org.yaml.snakeyaml.reader.StreamReader.update(StreamReader.java:184) ... 56 more :bootRun FAILED
(B) コマンドラインでエンコーディング指定する
gradlew clean gradlew bootRun -Dfile.encoding=UTF-8
- →○
- ログファイル文字化けなし=アプリの動作としてOK
- ※今回、実際アプリの挙動確認の代わりに手っ取り早くログファイルでの文字化け有無でアプリが正常にビルド&実行されているかを確認した
- コマンドプロンプト標準出力文字化けなし=開発しやすい
- ログファイル文字化けなし=アプリの動作としてOK
(C) 環境変数でエンコーディング指定する
JAVA_OPTS
などでもよいが、すべてのJVM起動時にフックできてかつ Pickup..
というメッセージが出力されて効いていることがわかりやすいため、undocumentedではあるが、_JAVA_OPTIONS
を使っている。(このような試行錯誤では、設定したと思っても設定方法自体が間違っていて効いていない、という何を確認しているのだかわからない残念な事故が多発するので、確実に効いて確実に確認できる方法がよい。)
gradlew clean export _JAVA_OPTIONS=-Dfile.encoding=UTF-8 gradlew bootRun
- →△
- ログファイル文字化けなし=アプリの動作としてOK
- コマンドプロンプト標準出力文字化けあり=開発しづらい
(D) gradlew.batを直接変更してエンコーディング指定する
gradlew.bat内のjava起動行の末尾に "-Dfile.encoding=UTF-8"
(ダブルクォートがポイント)を追加する。
74: @rem Execute Gradle 75: "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% "-Dfile.encoding=UTF-8"
gradlew clean gradlew bootRun
- →○
- ログファイル文字化けなし=アプリの動作としてOK
- コマンドプロンプト標準出力文字化けなし=開発しやすい
gradlew編のまとめ
基本は(B)でよい。 ただし、毎回毎回打つのが大変なのでサボりたくなるのが正しい開発者の姿。 しかし、安直に(C)のようにしても、挙動としては正しいのだが、コマンドプロンプト上の出力が化けるので若干使いづらい。
結局、(D)が良さそう。
(D)におけるポイントは、ダブルクォート。
コマンドライン引数で指定した場合とまったく同じように動作させるには、このダブルクォートが必要。
一つ前のように、環境変数で直接 -Dfile.encoding=UTF-8
を評価してしまうと、Gradleプロセス自体の文字エンコーディングがUTF-8になってしまう。
一般的にはそれで良いのだが、そうなるとGradleはコマンドプロンプトへの標準出力もUTF-8として出力しようとしてしまうため、結果としてコマンドプロンプト上の出力のみ化けた状態になってしまう。
ダブルクォートすることで、あえてGradleのメインプロセス自体はWindows標準のwindows-31jとして動作させつつ、Gradleプロセス内部でキックされるワーカプロセス(コンパイル用ワーカプロセス、Grailsアプリプロセス等)にのみ -Dfile.encoding=UTF-8
を渡してUTF-8として動作させる。
こうすることで、実際のワーカプロセスの動作はUTF-8となりつつ、Gradleがワーカプロセスの標準入出力とコマンドプロンプトへの橋渡しをするときに適切な変換を行うことで(要出典)、コマンドプロンプト上で文字化けせずに、しかも、ANSIカラー対応の出力すら可能となっている(※)。
※動作上はこのようにみえるのですが、実装詳細については確認できていないので、あくまで想像です。間違ってたらすいません。
grailsコマンド編
(A) 普通に起動する
grails clean grails run-app
- →×
- gradlew編(A)と同様に、applicaiton.ymlに日本語がある場合起動時にエラーになる
(B) コマンドラインでエンコーディング指定する
grails clean grails run-app -Dfile.encoding=UTF-8
- →×
- ログファイル文字化けあり=アプリの動作としてNG
- コマンドプロンプト標準出力文字化けあり=開発しづらい
gradlewでは効果があったコマンドライン引数であるが、ビルド時には効果があるようだが(日本語含みのapplication.yml起因のエラーが出ていない)、実行時には効いていないようだ。
(C) 環境変数_JAVA_OPTIONSでエンコーディング指定する
grails clean set _JAVA_OPTIONS=-Dfile.encoding=UTF-8 grails run-app
- →△
- ログファイル文字化けなし=アプリの動作としてOK
- コマンドプロンプト標準出力文字化けあり=開発しづらい
ビルドワーカプロセスにも実行ワーカプロセスにも確実に効くように、_JAVA_OPTIONS
を使ってみたところ、期待通りアプリの動作としてはOKとなった。
しかし、コマンドプロンプトの出力が文字化けするので、gradlewの(B)と比べると若干負けた気分。
(D) grails.batを直接変更してエンコーディング指定する
インストール済みのgrails.batをいじるのであまりお勧めはできないが、gradlewでの成功体験から試してみる。
$GRAILS_HOME/bin/grails.bat内のjava起動行の末尾に "-Dfile.encoding=UTF-8"
(ダブルクォートがポイント)を追加する。
74: @rem Execute grails 75: "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRAILS_OPTS% -classpath "%CLASSPATH%" org.grails.cli.GrailsCli %CMD_LINE_ARGS% "-Dfile.encoding=UTF-8"
grails clean grails run-app
- →×
- ログファイル文字化けあり=アプリの動作としてNG
- コマンドプロンプト標準出力文字化けあり=開発しづらい
まったく無力だった。
(E) 環境変数GRAILS_OPTSでエンコーディング指定する
_JAVA_OPTIONS
は強すぎるので、もう少し弱い環境変数ではどうか。うまいことバランスとれたりしないか。
grails clean set GRAILS_OPTS=-Dfile.encoding=UTF-8 grails run-app
- →△
- ログファイル文字化けなし=アプリの動作としてOK
- コマンドプロンプト標準出力文字化けあり=開発しづらい
やはりこんなところか...。
grails編のまとめ
grailsコマンドを使う場合は、コマンドプロンプト上の文字化けには目をつむって、(C)か(E)を採用するぐらい。
ただし、(C)の _JAVA_OPTIONS
を使ってしまうと、gradlew編の(C)の条件を満たしてしまい、gradlewを使った場合にもコマンドプロンプト出力が文字化けしてしまう。
よって、影響がgrailsコマンドに対して局所的になるように(E)の GRAILS_OPTS
を使った設定をするのがバランスが良いと思う。
MalformedInputExceptionの原因を探ってみる
※gradlewでの仕込みをいったんなくしてデフォルト状態に戻しておくこと
実行時のみエンコーディング指定
gradlew clean classes gradlew bootRun -Dfile.encoding=UTF-8
... Caused by: java.nio.charset.MalformedInputException: Input length = 2 at java.nio.charset.CoderResult.throwException(CoderResult.java:281) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:339) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) at java.io.InputStreamReader.read(InputStreamReader.java:184) at org.yaml.snakeyaml.reader.UnicodeReader.read(UnicodeReader.java:123) at java.io.Reader.read(Reader.java:140) at org.yaml.snakeyaml.reader.StreamReader.update(StreamReader.java:184) ... 56 more :bootRun FAILED
application.ymlに日本語が含まれる場合、このようにエラーになる。
ためしに、build/resources/application.ymlの日本語コメントを削除してからbootRunすると...と試そうとして気づいた、このファイル....壊れてやがる!!!
diffをとってみると、 日本語部分が化けて制御文字含みになってしまっている のがわかる。 実行時にエラーになるのはコレが原因だったのだ!
$ diff grails-app/conf/application.yml build/resources/main/application.yml 10,12c10,12 < name: '@info.app.name@' < version: '@info.app.version@' < grailsVersion: '@info.app.grailsVersion@' --- > name: 'myapp' > version: '1.0' > grailsVersion: '3.1.7' 71c71 < # 空文字は空文字、nullはnullとして扱う --- > # 空▒?字▒?▒空▒?字▒?▒nullはnullとして扱▒? 73c73 < # 前後の半角スペースのトリムはしない --- > # 前後▒?▒半角スペ▒?▒スのトリ▒?はしな▒?
他の差分箇所を見ればわかるが、アプリ名などをビルドによって置換されている。 ファイルをそのままコピーするのではなく、いったん内容を解釈して文字列置換をしているのだ。
つまり、ビルド時に適切なデフォルトエンコーディングを指定しない場合、実際はUTF-8であるgrails-app/conf/application.ymlをwindows-31jとして読み込んで置換処理してしまい、結果としてマルチバイト部分が壊れてしまうことになる。 実行時に正しくデフォルトエンコーディングをUTF-8指定しても、すでに壊れたファイルを読み込むことになるため、当然エラーになる。
結局、実行時だけではなく、ビルド時にも適切なデフォルトエンコーディングを効かせないといけないよ、という話であり、まあ、当たり前の話なのであった。
(参考)ビルド時のみエンコーディング指定
gradlew clean classes -Dfile.encoding=UTF-8 gradlew bootRun
コマンドプロンプト上では一件正常に起動して、文字化けもせずにログ出力できているように見える。
しかし、ログファイルへの出力は文字化けしている。おそらく画面も文字化けする。
実行時デフォルトエンコーディングも当然重要ですよ、という話。
JJUG CCC 2015 SpringでGroovyについて話してきた
2015/4/11のJJUG CCC 2015 Springで、Groovyについてお話ししてきました。
CFPに応募するかしばらく迷ってたんですが、ネタが思いつかなかったので先送りしていたら、〆切最終日辺りにさくらばさんや複数の人から「初心者向けでいいんですよ、むしろいいんですよ」的なメッセージを受信したのと、その近辺で今回の発表ネタのようなことを業務的にやるために割と時間をかけて資料を作り込んだのでその余波で割とどうにかなるんではないかということで応募してみたところ、有り難いことにAcceptされたのでした。
というわけで、今回は「Groovy入門+小人さんスクリプト」という構成でお話ししました。
以前にJavaOneの報告会で「Groovyの使いどころ、7つの導入パターン」というお話をさせていただいたのですが、その中の「小人さんスクリプト(House Elf)」パターンにあたります。
来ていただける方がGroovyをすでに使い込んでいることは基本的に期待できないですし、応用の話だけでは全員置いてけぼり必死と思われたので、「Javaは割と知っている人」に対して一歩ずつGroovyを理解していけるような構成にしてみました。色んな声を聞いた結果、割と成功したんではないかと思います。
というわけで、今回の資料は以下からご参照ください。
なお、これは144ページあるんですが、当日は時間の都合上絶対無理な感じだったので、50ページぐらい削りました*1。上記の公開版資料では削った分も含めて完全版にしてあるので、当日聞いていただいた方ももう一度頭から読み直す価値は結構あるかと思います。
サンプルコードはこちらからどうぞ。 nobeans/jjug-ccc-2015-spring-groovy · GitHub
それにしても今回は参加者数が多くてすごかったですね。いったいJavaに何が起こっているのか。とりあえず僕のセッションも割と満員御礼な雰囲気だったので大変有り難かったです。基調講演から自分のセッション〜懇親会まで含めて最高にハイな一日でした。とても楽しかった。スタッフの皆さん、来場された皆さん、本当にありがとうございました。
Let's enjoy Java and Groovy life!
*1:結果的に持ち時間50分ちょうどで終われたので良かった良かった
GrailsでシンプルなRESTサーバを素早く立ち上げる
はじめに
これはWeb API Advent Calendar 2014、10日目のエントリです。
Grailsでは某RESTful DAO的なものがサクッと実装できますよ、というお話です。 だから何だ、とか、○○のFWでもできるぞこの野郎、といった苦情はご遠慮ください。
実装する
とりあえず、Grailsプロジェクトを作成して、ドメインクラス(Entityクラス)を作成します。
$ grails create-app rest-sample
$ cd rest-sample
$ grails create-domain-class Book
$ vi grails-app/domain/rest/sample/Book.groovy
Bookドメインクラスの中身を以下のように修正しましょう。 適当なtitleプロパティを追加して(制約はあってもなくても関係ない)、Resourceアノテーションを追加してるだけです。このResourceアノテーションがいわゆる魔法詠唱ポイントです。
package rest.sample import grails.rest.Resource // これをつけるだけで、まったくコントローラを実装しなくても指定したURIでREST APIが有効になる @Resource(uri='/books') class Book { // とりあえずタイトルだけ String title static constraints = { // 気の済むように制約を書く title maxSize: 1000, blank: false, unique: true } }
とりあえず実装はこれだけでOK。 Grailsが手元にある状態からであればカップヌードルの待ち時間ぐらいでできますね。
アクセスしてみる
早速組み込みTomcatでサーバを起動して...
$ grails run-app ... | Server running. Browse to http://localhost:8080/rest-sample
cURLでアクセスしてみましょう。
$ curl http://127.0.0.1:8080/rest-sample/books <?xml version="1.0" encoding="UTF-8"?><list />
GETでリストを取得してみると、初期状態なので空ですね*1。
本を2件登録してみます。ちまたで話題のアノ本ですね。
$ curl http://127.0.0.1:8080/rest-sample/books -X POST -d "title=プログラミングGroovy" $ curl http://127.0.0.1:8080/rest-sample/books -X POST -d "title=Gradle徹底入門"
もう一度GETでリストを確認すると...
$ curl http://127.0.0.1:8080/rest-sample/books <?xml version="1.0" encoding="UTF-8"?><list><book id="1"><title>プログラミングGroovy</title></book><book id="2"><title>Gradle徹底入門</title></book></list>
うまく入っていますね。 もちろんDELETEもサポートしてます。
$ curl http://127.0.0.1:8080/rest-sample/books/2 -X DELETE $ curl http://127.0.0.1:8080/rest-sample/books <?xml version="1.0" encoding="UTF-8"?><list><book id="1"><title>プログラミングGroovy</title></book></list>
参照専用API
Resourceアノテーションの引数にreadOnly=true
を指定すると、GETによる参照APIのみだけが提供されるようになります。
POSTしても405 Method Not Allowedになります。
情報開示系APIならreadOnlyをつけておくと良さそうです。
コンテントネゴシエーションでレスポンス形式を指定する
デフォルトではレスポンスはXML形式で返ってきますが、拡張子を使ったコンテントネゴシエーションをサポートしているので、URI末尾に.json
をつけるだけで
$ curl http://127.0.0.1:8080/rest-sample/books.json [{"class":"rest.sample.Book","id":1,"title":"プログラミングGroovy"}]
とJSON形式でレスポンスを受け取ることができます。 ただ、classとか勝手に入っていて内部情報を暴露しすぎでアレなので、ドメインクラス→JSONへのマーシャル方法をカスタマイズした方がよいですね。 方法についてはここでは割愛します。
Content-typeの指定はAcceptヘッダでもOKです。
curl http://127.0.0.1:8080/rest-sample/books -H "Accept:application/json" [{"class":"rest.sample.Book","id":1,"title":"プログラミングGroovy"}]
まとめ
データ構造を表現するドメインクラスはとりあえず決める必要があるので、ドメインクラスを先に実装するわけです。 で、REST APIとして提供するつもりならサクッとResourceアノテーションでAPIを実装しておきつつ、徐々に、制約とかJSONのマーシャライザとかを整えたり、RESTな自前コントローラも追加して、最終的にResoruceアノテーションを削除する、というようなステップを踏めそうなので、そういう意味では初期バージョンの素早いリリースのために使うというのはアリな気がします。
なお、自前のコントローラを実装する場合も、ドメインクラスのオブジェクトとJSON/XMLなどへのコンテントネゴシエーションに基づく変換機能はもちろんそのまま活用できます。
また、Grailsは次のメジャーアップデートである3.0で*2Spring Bootベースに大きく方向転換をする予定なのですが、このようなRESTサポートは多少形を変えつつ残っていくようです。
といったところで。
関連しているようでしていない、ちょっとだけ関連している書籍
- 作者: 関谷和愛,上原潤二,須江信洋,中野靖治
- 出版社/メーカー: 技術評論社
- 発売日: 2011/07/06
- メディア: 単行本(ソフトカバー)
- 購入: 6人 クリック: 392回
- この商品を含むブログ (155件) を見る
Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築
- 作者: 綿引琢磨,須江信洋,林政利,今井勝信
- 出版社/メーカー: 翔泳社
- 発売日: 2014/11/05
- メディア: 大型本
- この商品を含むブログ (2件) を見る
Java8のInvokeDynamic実装一新によるGroovyのindyモードの性能改善効果が凄すぎた件
本日、Java Day Tokyo 2014に来ています。
で、ついさっきのセッションで「JDK8ではInvokeDynamic(以下、indy)の実装を一新したのですごく速くなったよ」という話を聞いたので、Groovyのindyモードで試してみました。
Groovyは2.0(現在は2.3)でindy対応されています。
それ以前はGroovy独自機構による性能改善が行われていました。
しかし、JDK7からのホヤホヤなindyを前提にしてしまうと、JDK6で動かなくなってしまいます。
Groovyは下位互換性が重視されているので、これは問題です。
というわけで、indyが使えないJDK上でも使えるように既存の独自機構もそのまま残っているし、indy不要バージョンが標準になっていて、indyオプションを利用可能にするには$GROOVY_HOME/lib
配下を$GROOVY_HOME/indy
配下にあるファイルで上書き置換してあげないといけません。
と言うぐらいに色々面倒くさいのですが、じゃあそこまでやってindyを使ったところでどれだけ嬉しいのかというと、JDK7までは「全然うれしくない」が答えでした。 JDK7のindy実装がこなれてない&Groovyのindy対応もカリカリではないので、全然性能がでなかったんですね。 独自機構の方が全然速かったわけです。
さて、「JDK8ではInvokeDynamic(以下、indy)の実装を一新したのですごく速くなったよ」という話を受けて試してみたんですが、indyモード&JDK8すごくヤバいことになってました。
サンプルコード
コードとしてはフィボナッチ数の算出です。 ローカルにあったファイルを発掘したもなので、実装が云々というツッコミはこの際受け付けません。
class Fibonacci { def calc = { n -> return (n == 0) ? 0 : (n == 1) ? 1 : call(n - 1) + call(n - 2) } static void main(String[] args) { def num = args[0] as int println "Fib($num) = ${new Fibonacci().calc(num)}" } }
これをJDKの7u55と8u05で実行してtimeコマンドで計ってみました。 厳密なベンチではないので参考ぐらいのやさしい気持ちで読んでください。 ちなみにGroovyは最新ホヤホヤの2.3.0です。
Groovy 2.3.0 (indy有効) + JDK 7u55
$ java -version java version "1.7.0_55" Java(TM) SE Runtime Environment (build 1.7.0_55-b13) Java HotSpot(TM) 64-Bit Server VM (build 24.55-b03, mixed mode) $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 42.03s user 0.35s system 102% cpu 41.538 total $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 33.75s user 0.34s system 102% cpu 33.197 total $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 41.01s user 0.35s system 102% cpu 40.512 total
Groovy 2.3.0 (indy有効) + JDK 8u05
$ java -version java version "1.8.0_05" Java(TM) SE Runtime Environment (build 1.8.0_05-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode) $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 20.04s user 0.29s system 108% cpu 18.777 total $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 19.73s user 0.29s system 108% cpu 18.520 total $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 20.27s user 0.31s system 107% cpu 19.076 total
実に2倍。これはヤバい。
(参考)Groovy 2.3.0 (indy無効=Groovy独自機構) + JDK 8u05
参考までにindyを使わないGroovy独自機構による性能も見てみましょう。
$ java -version java version "1.8.0_05" Java(TM) SE Runtime Environment (build 1.8.0_05-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode) $ time groovy Fibonacci.groovy 40 Fib(40) = 102334155 groovy Fibonacci.groovy 40 25.60s user 0.30s system 106% cpu 24.407 total $ time groovy Fibonacci.groovy 40 Fib(40) = 102334155 groovy Fibonacci.groovy 40 23.40s user 0.28s system 107% cpu 22.031 total $ time groovy Fibonacci.groovy 40 Fib(40) = 102334155 groovy Fibonacci.groovy 40 23.68s user 0.28s system 107% cpu 22.288 total
なんと、JDK7+indyにはダブルスコアで勝ってたのに、JDK8+indyに追い越されてしまいました。
indy全盛時代の幕開けを感じますね。
というようなことを、Java-ja枠のTDDセッションを聞きながらまとめてみました。