JavaEEだけどDockerがしたい!- GlassFish on Docker

Java EE Advent Calendar 2014 14日目です。

今年はDockerが大躍進した年だな、と思います。

そこで、このビックウェーブに乗り遅れないように、GlassFishを使って、Dockerベースの環境を作ってみました。

今回利用するコンテナはこちら

Appコンテナ

GlassFishが乗っているサーバです。下記のプロセスが動いています。このコンテナの数を増やす事でスケールアウトが可能。

本体はGlassFishですが、周辺システムとしてFluentdとConsulをインストールしてあります。

Dockerテクニック的に言えば、FuluentdやConsulを入れた状態でいったんイメージを構築して、それを継承する形で、glassfishイメージを作ってるので、wildflytomcatにすげ替えることも、比較的簡単に出来る構成にしてあります。

また、通常、Dockerでは1プロセスしか起動できないので、"run.sh"を作って、Fuluentd, Consul, GlassFishの3つのプロセスを起動させています。この辺りは、もしかたしたらKubernetesとかコンテナを管理するシステムを用いることでもっとシンプルに出来るかもしれないです。

他にも、Dockerらしく作るために気をつけた点としてApplicationを最初からデプロイしています。最初は起動前からデプロイしてある構成を想定してたのですが、アプリケーションのデプロイ時にJDBC Sourceを参照するためエラーになるので、auto-deployにして、起動時にデプロイする方式にしました。

DBコンテナ

App Serverが使うDBコンテナです。GlassFish上のアプリがJDBCを利用しているので、App コンテナより先に起動する必要があります。

Consulコンテナ

サービスディスカバリやオーケストレーションを提供するConsulの中央サーバです。今回の構成ではDNSも兼ねています。

  • Consul server agent

Log集計コンテナ

Fuluentdのログ集計サーバです。今回はファイルに吐いてますが、本番だとmongodbとかasticsearchに連携する形になるかと思われます。

ConsulやFuluentdを入れてる理由は、Dockerを使うユースケースでは古き良きオンプレの静的な構成ではなく、クラウド的な動的な構成が想定されるためです。

起動

コンテナは構築済み&docker-hubに上げてあるので、動作確認はシンプルに下記で実施できます。

git clone https://github.com/koduki/docker-javaee.git
cd docker-javaee
./docker-mng.sh up

これで動作します。"http://localhost:8080/javaee-simple-tester/"にアクセスして頂ければ、アクセスが可能です。

まとめ

こんかいはGlassFishクラスタも組んでませんし、リバースプロクシも入れてないので、高可用構成にはなっていません。

ConsulのDNSを経由してラウンドロビンにアクセスすることは可能ですが、HA ProxyとしてConsulを使っても潰れないのかは調査不足。

一方、Consulのイベント通知機能を使えば、Consulクラスタになった時点でGlassFishクラスタに入れるのは簡単そうです。

ただ、その構成にすべきかは、いまいち思案中。設定もデプロイもDockerレベルで完了しちゃうので、GlassFishクラスタを組むメリットは小さいです。

それよりもLBを入れて、負荷分散するほうがよっぽど重要。ただ、GlassFishは原則スティッキーなアクセスを求めるので、この辺をどう解決するのかが課題。クラスタ組まないとセッションレプリケーションもできないですしね。

単純にセッションを無理やりKVSに詰める仕組みを自作してもJavaEEとの相性が悪いので、なんとかHttpSessionレベルをフックする必要があるので、こと辺りは課題として要調査。

来年はこの構成をベースに、もっとスケールアウト出来る仕組みを考えないとなー。

明日はemagさんの「Arquillian Cubeについて」です。

参考

Excelを使わない技術 - 正しい神の殺し方

ドキュメント作成技術 Advent Calendar 2014 13日目です。

※ なお、本題とは一切関係ありませんが中2力全開気味なのでご注意ください。

Excel、というツールをご存知でしょうか? まあ、知らない人はいないと思います。

それは表計算ツールであり、グラフ描画ツールであり、データーベースであり、ワープロであり、DTPであり、時にはゲームエンジンとなる万能の釜です。

はじめは計算をシンプルにしたいという素朴な願いから生まれたそれは、やがてアレもしたい、コレもしたいという人の願いを叶え続け、万能と成り果てました。 今では、こんなことも出来ます。

Excelすげーですね!

ここまでスゴイ使い方はあまり見ませんが、方眼紙Excelで作った入力フォームとか、方眼紙Excelで作ったUMLとか、あまつさえ方眼紙Excelで作った表とかを見たりメンテナンスして、職人の凄さにむせび泣いた方なら多いかと思います。人はそれらを神Excelといいい、おそれ、あるいは尊びました。

「ネ申 Excel」問題 という論文もありますし、職人ならざる我々もドキュメントを書き、育てることを来年の抱負とするためにも、Excelを使わない技術に関して紹介していきます。

はじめに言葉があった

Advent Calendarを読むような方の期待をいきなり裏切って申し訳ないですが、Wordを使いましょう。文章書きたいなら、ね。

Wordだけではありません。UMLを描きたいならastahを使いましょう、WBSを描きたいならProjectLibreを使いましょう、画像を表示したいならそもそもjpegで良いのです。なんのために貼り付けてあるんですか?

もちろんツールは何でも良いのですが、餅は餅屋です。無理して全部Excelでやるから無駄に保守性が下がるので、適切なツールを使って、それをチーム/社内標準にすれば良いですし、印刷や他社配布ならPDFにプリントしてやれば良いのです。最近のOS使ってるならそのくらい造作も無いことなので、その作業は本当にExcelでするべきか考えてみましょう

どうでも良いですがWordを「言葉 -> コトノハ」と読むと素敵ですね。

されど我は愚者の夢を見る

Excelには適切なツールを使うことでさっくりと消滅してもらえば良いのですが、まだ不満はあります。

それはgitなどのバージョン管理システムとの相性です。

「履歴」という謎のシートやページに編集内容や編集者を記載し、さらに「変更箇所の色を赤で変えておきました」的なコメントを見かける事があります。これはExcelを表として適切に使ってても起こる悲しい問題です。

バイナリの差分管理が適切にできれば良いのですが、あいにく私は良いツールを知らないので、コミットログなんかだけだと更新内容が判断できません。

ソースコードみたいにdiffが出ればいいんですけど。

そう、逆に言えばソースコードみたいにテキストで管理することでdiffを含めて適切に管理できます。テキストファイルで管理することで、Pull Requestで差分管理/レビューできますし、良いことづく目ですね。

テキストで書いた場合は専用ツールで書いた場合に比べて表現力が劣ることが多いですが、後述するツールでかなり補えますし、意外に気にならないものです。

巨人の肩

かのニュートンは言いました。「私がより遠くまで見渡せたとすれば、それは巨人の肩の上に乗ることによってです。」と。先人に倣って、まずは既存のツールを紹介します。

最初の一つは一日目のsky_yさんも紹介されているPandocとMarkdown. Markdownはプレーンテキストで論理構造をいい感じに表現出来る単なる「記法」ですが、githubを初め多くのツールWebサービスが対応してるため、標準化されたWiki記法として人気を博しています。

私も普段書くメモもそうですし、このブログもMarkdown形式で書いています。pandocはMarkdownをHTMLやPDFあるいはWord形式で出力してくれるツールです。 これもかなり便利。以前書いた記事ですが、書きのようにプレゼン資料を作ることも出来ます。

Keynote風のプレゼンテーションをMarkdownで作ってみた - ブログなんだよもん

他にも図形を書くblockdiagとか、サーバの設定を記述するserverspecとか、テスト仕様書を記述するCucumberturnipも便利です。

そして13番目の奴は訪れる

既存ツールも紹介したことなので、個人的にJSONを使った表なんかも作ってるのでちょっと紹介。 Excelを使った表でデータを管理ってのは個人的にというか仕事でよくやります。サーバ一覧とかACL一覧とかパラメータシートとかそういう奴。Excelで作るのが簡単だし、見栄えも悪く無いですしね。

ただ、前述したとおり、まともにdiffが取れないので、更新履歴シートを作ったり、変更箇所を赤色にしたり、ゴミみたいな運用をせざるえません。

更新が噛み合ったらマージも出来ない阿鼻叫喚。マスタドキュメント系なのでこれはかなり不便で、下手をすると変更担当の人に依頼するという謎の運用になります。

そんな時はこんな感じでJSONで記述して、それを埋め込んだHTMLのJavaScriptを使って表として表示することが出来ます。

      "Description":"GlassFish Application Server",
      "Environment":["production"],
      "Network":{
        "Host":["localhost.localdomain"],
        "IP":["enp0s3", "10.0.2.15"]
      },
      "OSName":["Linux dev-ubuntu 3.13.0-32-generic"],

詳細:gitst

f:id:pascal256:20141213221120p:plain

わりと見栄えも悪くないので、個人的には結構お気に入り。

JSONだけ外出しにした方が、そのJSONを解析するツールとかが作りやすくて便利(事実、私もserverspec風のツールを作ってます)ですが、IE以外はセキュリティの問題でローカルファイルは参照禁止なので ローカルで管理が中心の時はgistに書いたみたいにHTMLの中に埋め込む方が良いかと思います。

ちなみに、XMLの方が好きって人はXSLTを使うことも出来ます。なにげにIE, Chrome, Firefox, Sfari, Operaとほぼすべてのブラウザが対応していますしね。 こいつが普及してたらまた違ったのかなー。

幼年期の終わり

Excelは良いツールですが、なんでもExcelでするのは微妙ですし、そもそも現代のソフトウェア技術は残念ながらテキストの方が管理に向いています。

私達は、Excelという揺りかごを飛び立ち、新しい世界へと行く必要があります。ドキュメントを作って印刷するだけの時代は終わりました。現在は、作って、メンテナンスし育てる必要があるので、それに見合った方法を色々模索していかないとなー、と思います。

以上、酔っ払った勢いで、主に見出し的に中2力を出した文章を最後まで読んでいただきありがとうございました。

それでは、Happy Hacking!

Consul with Dockerを手軽に試せる環境を作ってみた

Consulを使ってDockerの名前解決を簡単に実現する - Qiita でConsulを使って、サービスディスカバリをして、DNSの名前解決をする方法を書いたんだけど、検証用の環境を簡単に作れるようにしたのでこっちに公開。

koduki/consul-with-docker-example · GitHub

まあ、基本的に、Qiitaで書いたことをそのまま実行するためのスクリプトとDockerイメージを作っただけだど。

最初はfigで作ってたんだけど、起動のタイミングとかでどうも名前解決が出来ないこととかあって、シンプルにシェルにした。Consulが起動してから、sleepで3秒待つので、その間にクラスタの構築を待つ感じ。

sudo ./docker-mng.sh up # 起動
sudo ./docker-mng.sh  stop # 終了

ってだけの超シンプルシェル。この辺は先達もいるし、本格的に使うなら良いツールはいっぱいあるだろうしね。fig含めて。

あと、Qiitaの記事でも少し触れたけど、おそらくRH7系及びFedoraでDocker起動すると、セキュリティの関係かわからないけど、docker0にフォーワードしたポートはコンテナからはアクセス出来ない。

セキュリティ的には当たり前といえば当たり前の気がするし、かと言ってiptablesを毎回イジるのはさすがに面倒なので、Consulのクライアントになる側ではコンテナをRUNするときにlink名でconsulのサーバIPを抽出して、resolve.confを書き換えるというハックを入れています。

DNS_HOST=consulboot
DNS_IP=`grep ${DNS_HOST} /etc/hosts|awk '{print $1}'`
sed "s/172.17.42.1/${DNS_IP}/g" /etc/resolv.conf  > resolv.conf.tmp
\cp -f resolv.conf.tmp /etc/resolv.conf
rm -f resolv.conf.tmp

ちょっと、特殊なイメージになってしまい、使い回ししづらいけど、致し方なし...

この辺はDockerコンテナをホスティングしてるサービスによってもやり方違いそうだから、本番に上げるときには再度研究かなー。

それでは、Happy Hacking!

Java VM別の簡単なベンチマークをしてみた

最近のJavaは速い。

この言葉は良く聞くけど基本Java1.2とか1.4みたいな古代のバージョンと比較してのこと(まだ動いてそうだが...)

最近のバージョンはどうなんだろう? と気になったのだけど、あんまりVM毎のベンチって出回って無さそうだったので、試しに実施してみた。 本当はベンチマークキットみたいな適切なベンチ項目があれば良いのだけど、良いのが見当たらなかったので、とりあえず自前で適当に。

評価項目は下記の通り。

  • StringBuilderによる文字列結合(100,000,000回)
  • プラス演算子による文字列結合(100,000回)
  • リフレクションによるプロパティアクセス
  • BeanUtilsによるプロパティアクセス

結果はそれぞれ10回呼び出して計測時間の中央値を使ってる。実行した時のマシン状況による差分を多少でも減らすために、一応2回実行してみた。ソースコードはこちら

実行結果は下記の通り。

concat string with StringBuilder(100,000,000) concat string with plus(100,000) replace string with regular expression property access with refrection property access with beanutils
Apple Inc. Java HotSpot(TM) 64-Bit Server VM 1.6.0_65(1回目) 1428.5 12100.5 4434 1133.5|1783
Apple Inc. Java HotSpot(TM) 64-Bit Server VM 1.6.0_65(2回目) 1194.5 10400 3012 1076.5|1726
Oracle Java HotSpot(TM) 64-Bit Server VM 1.7.0_65(1回目) 1166.5 2135 2768.5 2037.5|1785.5
Oracle Java HotSpot(TM) 64-Bit Server VM 1.7.0_65(2回目) 1133 2178 2828.5 2040.5|1754.5
Oracle Java HotSpot(TM) 64-Bit Server VM 1.8.0_11(1回目) 1170 1788 2363 2144.5|1900
Oracle Java HotSpot(TM) 64-Bit Server VM 1.8.0_11(2回目) 1209.5 1779.5 2202.5 2087.5|1942.5

評価項目のせいかもだけど、あまり劇的に性能が変わるところは無さそう。プラス演算子による文字列結合だけやたら1.6で遅いのは、1.6はApple製なことと影響してるのかな? Linuxでも測ってみる必要もあるな。

正規表現と文字列結合は順調にバージョンが上がる毎にパフォーマンスが向上してるみたい。リフレクション周りが少し微妙だけど他の性能を上げるために犠牲になったのかな?

1.8でPermMemoryの使い方とかも変わってるので、メモリとかをもっといっぱい使うようなベンチをちゃんと作らないと面白いデータは取れ無さそう。

まあ、少なくとも遅くなってないことはわかったので、それはそれで収穫か。

それではLet's Happy Hacking!

問い:Java 8のStream APIは業務でどんな時に使うの? 答え:あなたがfor文使いたい時

※ サンプルがJDK7までとJDK8までで意味が変わっていてわかりにくいという指摘があったので、少し直しました。 ※ boxedを使う書き方だと無駄なAutoboxingが走るとの指摘を頂きましたのでmapToObjを利用するように変えました。

Java8の目玉機能の一つにStream APIがあります。

目玉機能だけあって、先日のJava Day Tokyo 2014を含めて色んな所で発表やブログの記事が公開されているので、どんなものかを知ってる人は多いと思います。

Stream APIといえば「".parallel()"と書くだけで並列化してスピードアップ出来る!」という魅惑的なキーワードで紹介されることが多いので、並列化のための仕様だと勘違いされそうですが、そうではありません。 ※ もちろんそういった記事の中をちゃんと読めばそう単純な話じゃないことも分かります。

むしろ、並列化に関しては、Webアプリケーションなど一つの処理が十分に小さく大量にさばくことを期待されるシステムだと積極的に忘れても良い気がします。

もちろん、それでも価値があります。

一般に内部イテレータと呼ばれるもので、Ruby, Python, JavaScript, C#, Scala, PHP, Hakell, LISPと出自である関数型言語系はもちろんのこと、RubyPython、JSといったLL系Javaと類似の用途で使われるC#も似たようなものを持っています。

現在業務で使われやすい言語では概ね対応しているのでその辺に親しみがある人は、ようやく素直に出来るようになったのかという感じでしょう。

一方で、Stream APIでどんな事ができるのか分かったけど、業務のどこで使えば良いのかが分からないという意見もチラチラ聞きます。 実際、私は初めてRubyで内部イテレータを使ったのですが、最初はfor文を使わない文化に戸惑ったものです。

Webの記事とか見ても「for文禁止!」とかセンセーショナルな書き方がされることが多くて、実際その通りと思いはするものの、Java8怖いとかになって普及が阻まれないかなぁとも思うので、今回は「for文禁止」とまで言われるその背景の説明をしたいと思います。

なお、Stream APIそのものの機能や詳細はすでに色々なところで語られているので、今回は省きます。

語彙によって意図を明確にする

Javaに拡張for文(foreach)が入った時にも同じような話をしたのですが、言語やFWの語彙を適切に使うことでコードの意図を明確に出来ます。

これは本質的には適切なクラス名やメソッド名を記載するというのと同じことです。

まずはこいつを見てくれ・・・どう思う?

gist182f1fc4863bf41018df

Java的に遺失呪文(予約語だがコンパイルエラーになる)なgotoを使ったFizzBuzzです。ループ構文が無い古い言語仕様で書く時は今でもこうかく必要がありますね。

一言で言えば「わけがわからないよ

gotoはご存知の通り極めて柔軟な仕様なので、ループという概念に限定されておらず、良く読まないと繰り返しであることが分かりません。そして、汎用性の高い構文なので、もちろん記述が冗長です。

つづいて同様にループを表現する構文であるWile文を使ってみましょう。

gist127ff4b858d89474bc71

さっきより大分見やすくなりましたね。Wileを使うことでループ表現であることが瞬時に理解出来ます。 ただ、これでは1から100までの順次表示であることが表現出来ていません。なので、普通はfor文で書きますよね?

gistfe59ae6d8465a24ac5df

ようやく普通の感じになってきました。落ち着きますね。

ここでfor文の良い所は1から100まで1個ずつインクリメントしてプログラムが処理されることをシンプルに保証したことです。

さて、ここまで読んでJavaのようにfor文が使える言語で、FizzBuzzをgotoやwhileを使って書きたいという方はいらっしゃるでしょうか?

え、書きたいんですか!? もの好きですね。。。 

でも、仕事では迷惑なので書かないでください。

長々と書いてしまいましたが、ここで言いたかったことは、そのプログラム言語が許す限り適切な表現で目的を記述しましょうということです。

殆どの場合、昔からある既存の書き方でも表現出来るでしょうが、新しい表現が出来たということはそれに特化したユースケースが有るということなので、そのケースでは新しい表現を使うことでより抽象度が高く、簡潔で理解しやすいコードになります。これはもちろん今回導入されたStream APIも同様です。

Loopではなくリスト操作

さて、前章は長かった割には完全にStream API関係無い話でした。ここからはそもそもどういうユースケースを想定しているかを話していきたいと思います。

ところで、前章で最後に書いたFIzzBuzzのコードが業務でコードレビューに上がってきたらどうしますか? 私は基本的にNGにします。

なぜなら、ロジックの中で標準出力である"System.out.println"を使ってるので、ファイル出力とかに変えようとしたり出力フォーマットを変えようとしたら、ロジック書き直すかコピーしないといけないし、戻り値がvoidなのでUnitTestも書けません。プロダクトコードとしては非常にイケてない部類に入るでしょう。

なので、普通は仕事で書くならこんな感じに書くと思います。

gist998edee7a06eb26b51f8

直接表示するのではなく、FizzBuzzロジック本体はListを返すことで出力先を容易に切り替えることが出来、テスタビリティも格段と向上しています。

最初のコードだと下記のような思考パターンでコーディングをしていたと思います。

  1. 1から100までの数を繰り返し処理しよう
  2. 15で割りれるなら"FizzBuzz"を、5で割り切れるなら"Buzz"を、3で割り切れるなら"Fizz"を、そうでないならそのままListに追加しよう
  3. Listの中身を全部表示しよう

これがJava 7までの良くやる考え方ですね。ここでパラダイム・シフト。下記のように考えてみましょう。

  1. 1から100までの数を持ったリストを作ろう
  2. 1で作ったリストを15で割りれるなら"FizzBuzz"を、5で割り切れるなら"Buzz"を、3で割り切れるなら"Fizz"を、そうでないならそのままというリストに変換しよう
  3. 2で作ったリストの中身を全部表示しよう

一見同じように見えますが、1と2の意味が少し違います。最初からリストを作ることで「繰り返し処理」からリストの作成・変換といった「リスト操作」に置き換わっています。

これはリストの操作が得意な関数型言語に由来する考え方です。こうして「繰り返し処理」という制御構文を「リスト操作」という関数にしてしまうことで、戻り値のある小さなパーツとして扱いやすくなり、UnixのシェルなどのようなPipes and Filters アーキテクチャを適用出来ます

ようは、この処理を適用して、その結果をこの処理に適用して、それからこの条件で抽出してみたいなちょっとずつ処理を書き足していくやり方ですね。

gist52b41e5ed3d63dcd3997

Java7で書くとこんな感じですね。こうすることで

入力となるリスト -> 変換処理 -> 出力となるリスト

という形にすることが出来、先ほど言ったような恩恵を得ることが出来ます。ただ、あまり簡潔ではないですね。元々、関数型言語由来の考え方ということもあって、Javaで実現出来なくはないのですが、どうしてもストレートには行きません。

ちょうどC言語でもオブジェクト指向で書くことは書けますが、オブジェクト指向言語ではないので、書くのが面倒というのと同じです。

というわけでこのリスト操作スタイルをダイレクトにサポートするために最適な機能がStream APIとなります。

Stream APIによるリスト操作

驚くべきことに、こんなに長々とJava8の記事を書いてるのにまだJava8のコードが出てきていませんwでも、安心してください。ここからようやくJava8のコードが現れます。

まずは、先ほど書いたリスト操作スタイルのFizzBuzz(Java7版)をリスト操作スタイルのFizzBuzz(Java8版) に書き直しましょう。

gist4106ff067830296d5845

Java7版と比べるとかなりシンプルになりましたね。

mapの中で使ってるのがif文ではなく三項演算子なことにも注目です。たぶん、if文でも書けるのですがmapは各要素に引数で渡されたラムダ式を実行した結果を持ったリストを返す、という仕様なので値を返す三項演算子の方がずっとシンプルに書けたりします。

boxedを付けないとIntStreamはオブジェクトのStreamに変換できない面倒な仕様があるものの、概ね問題ありません。

さて、せっかくなのでPipes and Filtersっぽくメソッドチェインで処理を追加してみます。

gist8265b34f5598bafa0843

これはFIzzBuzzのルールで作り出した文字列のリストからさらに"FizzBuzz"という文字列を抽出してその数を数えるサンプルです。Unixのシェルっぽい感じですね。

ブコメで中間状態をログに出したりデバック目的で取り出したいという話もあったので、その例も追加してみました。こういうケースではpeekを使います。処理は実行するけどリストには変換を与えずそのまま返すメソッドです。

とはいえ、個人的にはログとか向上的に確認したいほど中間状態の粒度がハッキリしてるなら、変数に入れてしまうのがシンプルと思います。

Unixのシェルやワンライナーで書くときもそうですが、あまり長く書きすぎると書くときは問題無いのですが、読むのが大変になります。

また、Stream API重要なポイントとして、Java7のスタイルに較べてStream APIをはオーダー数が異なることです。Java7版では見て分かる通り、Listの作成処理や変換、そして表示とループが3回回っているため、オーダが3Nとなってしまうので、データが大きな時は非効率です。

その点、Java8のStreamはUnixのシェル等と同様に基本的にはループ数は1回に収まります。パフォーマンスを気にしなくて良くなるので、とても嬉しいですね。

まとめ

色々書いてきましたが、今日書いたことを整理すると以下の2点です。

  1. ループ処理はリストに変換することで保守性高く書ける
  2. ストリームAPIはリストスタイルを推奨するための語彙

for文禁止という一見苛烈に見えるコメントは、ほとんどのループ処理はリスト処理で表現可能、ということと、StreamAPIと組み合わせて使うなら、ソッチのほうがずっとシンプルで、安全という事があるからです。

本当はfor文でしか書きづらいケースは無くはないのですが、まずは一旦禁止して思考の仕方を変えるためのテクニックですね。ループ処理ではなく自然とリスト変換でデータ処理を考えられるようになった時、for文を解禁するとよいでしょう。

故に「Java 8のStream APIは業務でどんな時に使うの?」という問いには私はこう答えます。「あなたがfor文使いたい時」と。

それではHappy Hacking!

参考

Java Day Tokyo 2014に参加してきました

勉強会はブログに参加記事を書くまでが勉強会だと以前習ったので、昨日参加してきたJava Day Tokyoの感想とか書いときます。

Java SE 8がちょうどでたばかりということもあって、今回はほとんどのセッションがJava8祭りでした。

https://oj-events.jp/public/application/add/170

当日の雰囲気とかはToggeterにまとめられてるのでこちらを。

http://togetter.com/li/670503

基調講演

午前中はOracle本社メンバーによる基調講演。Javaといえばサーバサイド!って印象が強いのですが、今回はJavaMEの話をはじめIoTに注力してることをかなりアピールされて、少し意外でした。 Javaといえば元々組み込み用として始まったわけで、それ故にこその "Write once, run anywhere"を掲げてたと思うのですが、原点回帰というか、ここにきてようやく実ったのかなという印象でした。

実際、デモの中でDukePadでもPCでもアーム型のロボットでもチェスを打つというコードをJavaで実装してたのは面白かったですね。あと、デモでまいんどすとーむやらチェスゲームのDukeが可愛かったのですが公開されてないのかな...

他にも気になったこととしては2016年に出来るであろうJavaEE8ではJAX-RS/MVCが登場予定とのこと。 これで、最近ようやくマイナスがゼロになったJSFとおさらばできます。あれはURLを大事にする近代的なWebサイトを素直に作れないので><

あとは忘れてはいけないのはJava SE8の日本語が出来ました!

Java SE 8日本語ドキュメントの公開 http://docs.oracle.com/javase/jp/8/

これは普及させるためには非常に心強いですね。このスピード感でできてくるのは意味では勢いを感じる象徴的なものに見えます。 他にもコミュニティについてもかなり言及されていてJava Magazine 日本版についても話されてました。

あとNECのロボットとか国内での事例紹介もありましたね。対話型のロボットで現在導入パートナーを探し中とのこと。

講演は全般的に英語でしたが、まあゆっくり喋られてたので、通訳聞かなくてもある程度中身が分かる感じでした。

Java SE 8時代のJava EE 7アプリケーション開発

続いて寺田さんのセッション。 まずはJava EE7 のWebSocketでマインドストームをリモート操作。曰く、組み込みのナレッジが無くても使い慣れたJavaでかなり簡単にキャッチアップ出来たとのこと。 ハードウェア自体の高機能化の恩恵も多分にあると思いますがJavaMEとかでフットプリントが小さくなって、普通に書ける範囲が広がったのはいいことですよね。なんか試したくなる。

その後はJava EE7とJava SE8を組み合わせてのConcurrency Utilities for Java EE(JSR-236)の使い方について。 Future.getが同期なのは知らなかったです。これは確かに嵌りそう... そして、 大事なことは「Java SE8ではCallable禁止」とのことでみなさんCompletableFutureを使いましょう! Java EE7とJava SE 8って組み合わせは新しいもの好きには堪らない選択肢だと思うので罠とかは今のうちに確認しときたいですね。

Java SE 8におけるHotSpotの進化

デイビッド・バックによるセッション。 Java SE 8になってHotSpotがどう変わったかのお話です。言語仕様ではなくVM仕様なので、厳密にはJava8ではなくOracle Java, OepnJDKの最新版に関する話、という理解が正しいですね。

ラムダ式とか言語周りが注目されることが多いですが、今回はJVM周りもプロジェクトHotRokitの本格稼働の一つとしてかなり手が入っているでなにげに一番気になってたセッションです。

まずはやはり気になるPermGenの廃止について。 PermGenは通常のヒープとは違いJavaのstaticのメンバーやクラス情報などJavaの静的な情報を格納する場所でした。 しかし、チューニングがそれなりに難しく、パフォーマンス劣化やOutOfMemoryErrorの原因となっていました。

というPermGenのデメリットを上げた上で、とは言えメリットはと次のスライドをめくったところで白紙w そして「....あったけ?」というコメントに会場が爆笑に包まれましたw

でJava8からPermGenが廃止されてMetaspaceへ。基本的な役割は変わらないのですが、ポイントはヒープではなくネイティブメモリ管理になったこと。 つまり、GCの対象ではなくVMの都合で状況に応じて伸縮出来るようになりました。元々ヒープとは特性が違うものだったので、これは言われてみれば妥当な気がしますね。 クラスローダ単位の管理になっていてHigh Water Mark(HWM)を指定することでクリーニングするタイミングもある程度指定できるようです。 とはいえ、特性が変わっただけでメモリ枯渇自体はありえるのでOutOfMemoryError自体が完全に無くなるわけでもないようです。 この辺のチューニングポイントは多少変わりそうなので、負荷試験とかをして実運用までに情報を集めたいところです。

他の話としては、フォールスシェアリング回避や64bitメモリアドレスを可能なら32bitに圧縮するCompressed Oopsや、C1(-Client)とC2(-server)を従来のように指定するのではなく上手く切り替えて良いとこ取りをするTiered Compilation、AESの性能改善など中々聞けない話がたくさん聞けて大満足です。

また、Java7から導入されたinvokeDynamic(indy)がJava8では大幅強化されているようです。 これは、Java7では初回なので正しく動くことを重要視していたのと、Java8でラムダ式やNashornで積極的に利用してる事もあってかなりチューニングが施されているようです。 ちなみにラムダ式Indyで実装されてるのはこの時初めて知りました。てっきり内部クラスかと...

あとjcmdがネットワーク越しでも使えるようになったのも地味に便利ですね。まあ、運用環境と開発環境でjcmdが使うようなACLって普通開けないってのも有るのですが...

Javaアプリケーション開発におけるテストとTDDの実践

@t_wadaさん、@nekopさん、@shuji_w6eさんによるTDD談義。TDDというよりテスト談義かな。 自分が聞いた中では、というか多分今日のセッションで唯一かもしれないJava8関係ないセッションでした。

各メンバーがどうやってテストをしているかの話が主にされて、その中であったcmtest-db を使うとJUnit4に上手く統合されるようなので今度試してみたいですね。 Arquallianは私もちょこちょこ使っていて、EJBのテストには本当に便利! とはいえ、結構使いこ直してない所も多いし、Weblogicだと組み込みで動かないみたいだからどうしようかなとも思ってたんだけど、今回仕切りに本物のモジュールで、という話も強調されてたし、また試さないトナー。動画で撮影とか知らない機能もあったし。

総じて言われてたように感じたのは

  • システムテストでモックに拘り過ぎず本物のモジュールを使うこと
  • カバレッジは目安。テストの点数でもなければ完全でもない
  • それはTDDじゃない、とか「テスト書かないとかそれ@t_wadaの前でも言えんの?」とか原理主義的なことを言わない。自分を助けてくれるかを考える

とかですね。この辺の話はうちのチームだとプログラマが多いのでぜひ色々共有したいところです。

Lambda式とストリームAPI、並列処理の詳細

Stuart MarksによるストリームAPIとかの話。 目玉だけあって、Lambda式やストリームAPIは各所ですでに語られまくってる事もあって、聞き取れた範囲ではあまり新鮮味のある内容もなかったけれど、スライドにサンプルがいっぱい書いてあったので後で見なおしてみると色々発見があるかも。 元々自分がRubyScala、JSやLISPなどmap/reduce/filterがある言語を結構使ってるので、ようやく頭で翻訳せずに使えるようになったか、という以上でも実はなかったりする。

とはいえ、twitterとか見てるとストリームAPIの使い方はわかったけど、業務でどう使うかわからんって話があって、これは私も他の言語でこの辺の概念を学んだ時には感じたことなんで、今度記事とか書いてみようと思う。

Java Day Tech Night

Oracle本社の中の人に質問ある人どうぞコーナー。 勝手にLT枠だと思ってましたがちょっと違いました。そして、ハッキリ言ってグダグダでしたw 通訳挟んでるしテンポやコミュニケーションロスの問題もあるしね。

とはいえ、かなり色んな話が聞けました。Lambda式が内部クラスではなくIndyで実装されてるから初回のコストはともかく2回目以降は通常のメソッドとほぼ変わらない。詳しくはJavaOneの資料見ろ、とか。 Lombokは標準に取り込まれないの? 的な話は試しに使って見てたけど、余計な問題で困ることが多いから別の方法を模索中だけど決定案も無い、というかEJBとかJavaEE系は意図的にgetter/setterとpublic fieldを区別せず動くように作ってあるからそっち使え、とか。 あとLambada式内での例外の取り扱いもやっぱり決定案ないから気をつけろだとか、OpenJDKに協力するならまずはML登録だ!とか。

他にもInterfaceにメソッド実装持てるようになったけど、フィールドてないから片手落ちじゃね? って話にはScalaと違って互換性を大事にしてるからそんなにダイナミックな変更は出来ない。という感じの回答も参考になりました。 とはいえ、基本的にはMix-inをするためのTrait的な用途は想定してないって話だと思うんだけど、こんな感じで書けばフィールドを直接持てなくても、困らないので個人的にはガシガシmix-inに使っていく所存。

そして、JavaEE7でまともに動くのwildflyだけじゃない! 私達はJavaEE7を信じて乗っていいの? 他の言語や非標準FWに行っちゃうよ? という問いには結構ぐだぐだした返しがw まあ、禁断の質問ですからね。標準作る立場としては色いろあるだろうけど、せめてGlassFishWeblogicはお膝元だからもうちょっと頑張って欲しいってのも有りますねー。早く運用で使いたいし!

まとめ

搭乗予定だった飛行機が30分遅れて、危うく間に合わないかとも思ったのですが無事基調講演から参加出来ました。 実のところ行くかどうかは少し迷ってたのですが、Java熱が自分の中でも高まってきたので、いくつかJava8の機能を試して記事書いてみたくなりまし、なにより楽しかったです!

来年もぜひ行きたいですね。

それでは、Happy Hacking!

Androidのエンドツーエンドテストの自動化も Cucumber から Turnip へ

以前はAndroidのATDDにはCabash-Androidを入れてたのだけど、やっぱ時代はCucumberからTurnipだよねってことで、AndroidなTurnip環境を作ったので構築メモ。

ちなみに出来たものはこちらへ。

github - koduki/turnip_with_android_example

インストール

まずはこの辺参考にGenymotionを入れる。 別にGenymotionじゃなくて実機とかでも問題無いと思うけど、開発とかCIとかにはGenymotionが速くて便利。

インストールしたらデバックモードを有効にする。

Android 4.2以上はデフォルトでは開発者向けオプションがないらしいので

  • 「設定」=>「端末情報」=>「ビルド番号」を7回タップすると「設定」に開発者向けオプションが表示される
  • デバッグモード」をonにする

続いてappiumをnpm経由で入れる。

sudo npm install -g appium
sudo npm install wd

これで、実行環境の準備は完了。最後にTurnipの環境を作る。

まずはインストール用のディレクトリを作成.

% mkdir turnip_with_android_example/
% cd turnip_with_android_example
% mkdir spec/
% mkdir spec/acceptance/
% mkdir spec/steps/

続いて

% $EDITOR Gemfile

中身は下記の通り。

source "https://www.rubygems.org"
 
gem 'rspec'
gem 'selenium-webdriver'
gem 'turnip

Turnip用にfeatureを取り込めるように.rspecに下記を記載

% $EDITOR .rspec

中身は下記の通り。

-r turnip
% $EDITOR spec/spec_helper.rb
require 'bundler/setup'
require 'selenium-webdriver'

Dir.glob("spec/steps/**/*steps.rb") { |f| load f, true }

これで必要なファイルが出来たので、Bundlerで依存ライブラリをインストールする

% bundle install --path vendor/bundle

シナリオの作成

さて、ようやく準備が整ったところでシナリオの作成。

開発中のアプリとかをテストするのが本来だけど、サンプルとしてのポータビリティを加味して、デフォルトで入っている[Settting]を対象にテストを書いてみる。

まずはfeatureファイルを作成。

% $EDITOR spec/acceptance/settings.feature
Feature: 設定画面
  Scenario: 端末情報を表示する
    Given テスト対象は "android" 端末
    When "About phone" をタップする
    Then Android Version は "4.2.2" が表示されていること

一旦この時点で実行してみる。bundlerで環境作ってるのでbundler経由でrspecを実行するのがポイント

% bundle exec rspec

stepファイルが無いので下記のようなエラーが出るはずです。

設定画面
  端末情報を表示する
    テスト対象は "android" 端末 -> "About phone" をタップする -> Android Version は "4.2.2" が表示されていること (PENDING: No such step: 'テスト対象は "android" 端末')

Pending:
  設定画面 端末情報を表示する テスト対象は "android" 端末 -> "About phone" をタップする -> Android Version は "4.2.2" が表示されていること
    # No such step: 'テスト対象は "android" 端末'
    # ./spec/acceptance/settings.feature:3

続いてstepファイルを記述。中身は下記の通り。

# coding: utf-8

module SettingsStep
  step 'テスト対象は :device 端末' do |device|
    @device = device
  end

  step ':target をタップする' do |target|
    about = find_first_element('//text[@text="' + target + '"]')
    about.click
  end

  step 'Android Version は :expected が表示されていること' do |expected|
    version_setting = find_first_element('//list/linear[4]/relative')
    version_value = version_setting.find_element(:xpath, '//text[2]')

    expect(version_value.text).to eq(expected)
  end

  def find_first_element xpath
    #flick the screen until find the target item
    while driver.find_elements(:xpath, xpath).count == 0
      begin
        driver.execute_script 'mobile: flick', :startY => 0.9, :endY => 0.1
      rescue
      end
    end

    driver.find_elements(:xpath, xpath).first 
  end

  def driver
    case @device
    when 'android'
      desired_caps = {
            'browserName' => 'android',
            'platform' => 'linux',
            'version' => '4.1',
            'app-activity'=> '.Settings',
            'app-package'=> 'com.android.settings'
        }
      server_url = "http://127.0.0.1:4723/wd/hub"

      @driver ||= Selenium::WebDriver.for(:remote, :desired_capabilities => desired_caps, :url => server_url)
      @driver.manage.timeouts.implicit_wait = 3
    end
    @driver
  end

  def cleanup
    if @driver
      driver.quit
      @driver = nil
    end
  end
end

RSpec.configure do |conf|
  conf.include SettingsStep
  conf.after(:each) do
    cleanup
  end
end

基本的にはただのrsepcですね。1点注意としてはfind_first_elementの中でやってるみたいに画面上に表示されてない項目を出すためにスクロールする必要があります。 これ分からなくて、最初嵌った...

これを実行すると、下記の用にGreenな結果になる。

% bundle exec rspec

設定画面
  端末情報を表示する
    テスト対象は "android" 端末 -> "About phone" をタップする -> Android Version は "4.2.2" が表示されていること

Finished in 9.14 seconds
1 example, 0 failures

まとめ

とりあえずAndroidでturnipな環境が完成。割と便利そう。 ただ、構築が少し面倒なところも。npmとgem使うので、そっち系の親和性があってかつAnrdoidでJavaも書くぜーってエンジニアが居ないと、使うのはともかく導入に手間取るかなーという印象でした。国内情報少ないしね><

あと、cucumberの時もそうだったけど、Firebugとかに相当するDOMインスペクタが無いとstep書くのがちとしんどいのよね。 でも動画でそれっぽいものがappiumある的なことを言ってたので、調べてみようかな。

なんにしても後はテストをひたすら書くだけじゃー!! それではHappy Hacking!

参考