Mac上のOpenJDK/OracleJDKはApple実装モジュールへの依存が残っている

はじめに

Mac OS X 向けの Java 実装はかつて Apple 自身が開発、提供していました。ですがそれは 6 で終了し、7 以降は Apple も OpenJDK に参加、OpenJDK 上で OS X 向け実装も開発されるようになり、Oralce から Mac OS X 向け JDK/JRE を提供されるようになっています。

ところが、全てが OpenJDK に移ったわけではなく、一部 Apple が実装、メンテナンスしているモジュールへの依存が残っていることを知り、ちょっと驚いたのでその内容についてまとめてみました。

Yosemite以降の新Look & FeelへのSwingの対応について

これに気付いたきっかけは、Mac OS X の UI デザインが Yosemite で一新されたことです。

JavaGUI ツールキットの 1 つである Swing は、OS の UI ツールキットが提供するコントロールを直接利用せず、Java2D を使って自分でコントロールを描画します。OS の UI ツールキットに似せた見た目にする、システム Look & Feel もありますが、これは自分で描画して似せているだけです。

さて、Mac OS X では Yosemite になって Look & Feel が一新されましたが、Swing については Swing 側が対応しない限り見た目は前のままのはずです。実際、次のスクリーンショットのように Mavericks 以前の Look & Feel のままでした。
f:id:aoe-tk:20160214010144p:plain

最近、家で使っているマシンを新しく Mac mini に変えました。これには El Capitan がプリインストールされているのですが、こちらでは Swing の Look & Feel が Yosemite 以降のそれに変更されていたのです。
f:id:aoe-tk:20160214010230p:plain

Java のアップデートで対応したのかな?と思って、別にある Yosemite 環境のマシンについても Java を最新版にしました。ところが、JRE のバージョンが全く同じなのに、こちらでは古い Look & Feel のままなのです (上 2 つのスクリーンショットJava バージョン表記を確認してください) 。

これは一体どういうことなのだろうと不思議に思って、調べてみました。すると、OpenJDK の ML に次のようなスレッドがあるのを見つけました。

http://mail.openjdk.java.net/pipermail/swing-dev/2015-September/004855.html

そこでは次のような説明がありました。

The Aqua look and feel is based on the JRS library, which is provided by Apple. As far as I know the Apple provide the new "modern style controls" in the new version of JRS in OS X v10.11. You can ask additional information on java-dev mailing list at apple.com [1]

Mac OS X の Aqua 風 Look & Feel については Apple が提供している JRS と呼ばれるライブラリによって提供されているとのことです。そして OS X 10.11 (つまり El Capitan) では新しい JRS が提供され、その JRS では新しい Look & Feel に対応しているため、El Capitan でだけ Swing の見た目がアップデートされたということになります。

JRSとは何か?

JRS の正式名称は JavaRuntimeSupport になります。実体は以下の場所にありました。

/System/Library/Frameworks/JavaVM.framework/Versions/A/Frameworks/JavaRuntimeSupport.framework/Versions/A/JavaRuntimeSupport

この JRS は OpenJDK には含まれておらず、ソースコードApple によってメンテナンス、提供されているとのことです。次の Apple の開発者向け ML にその点を説明するスレッドがありました。

http://lists.apple.com/archives/java-dev/2015/Dec/msg00007.html

これには驚きました。最初に述べたように Java 7 以降の Mac 上の Java 開発は OpenJDK に移行したはずです。それなのに、Apple 側から OS アップデートを通して提供されるモジュールがなければ変わらない部分があったということです。UI の Look & Feel に関わる部分であるため、意匠権絡みなどで移せない事情でもあったのでしょうか?

今後はどうなる?

というわけで Mac 環境における OpenJDK (Oracle JDK) には Apple 実装モジュールへの依存が残っていることが分かりましたが、少し不安なところがあります。というのも、Apple は旧 Apple Java 6 のサポートは El Capitan を最後にすると宣言しているからです。

applech2.com

そのため、JRS のような Apple 提供モジュールの更新も今後行われなくなる可能性があります。OpenJDK 側でもこの状況については Issue が上がっていますが、さてどうなることやら...。

[JDK-8024281] Mac OS X: stop relying on Apple's JavaVM Frameworks - Java Bug System
https://bugs.openjdk.java.net/browse/JDK-8024281

おまけ

Apple Java 6 は El Capitan が最後のサポートバージョンになるとのことですが、その El Capitan 向けインストーラApple から提供されています。 *1

ダウンロード - Java for OS X 2015-001
https://support.apple.com/kb/DL1572?locale=ja_JP&viewlocale=ja_JP

このインストーラYosemite 以前のバージョンにも使うことができます。しかもこのインストーラでインストールされる Java 6 には、El Capitan 向けの JRS がバックポートされています。よって、Yosemite にこのインストーラを使って Java 6 をインストールすると、次のように Yosemite でも Swing の Look & Feel が新デザインに変わります!
f:id:aoe-tk:20160214015307p:plain

*1:El Capitan ではこのバージョンで導入された rootless の関係で、以前のインストーラが使えなかったようです

javapackagerの紹介

このエントリは JavaFX Advent Calendar 2015 の 19 日目のエントリです。前日は id:yumix_h さんによる「 JavaFXで画面解像度を調べてみる 」でした。

今回は JDK に付属しているツールである javapackager について紹介します。このツール、私が見る限り公式のドキュメント以外では断片的な解説しか無い (主にネイティブパッケージの解説などでしか登場しない) ように見受けられるので、ここでこのツールができること全般について紹介したいと思います。

アプリケーション配布を巡る環境の変化

まず、javapackager のようなツールが登場した背景について触れたいと思います。これにはアプリケーション配布を巡る環境の変化が大きく関わっていると考えています。

既にご存じの通り、Java は "Write Once, Run Anywhere." を目指して作られたものであり、Java で実装、ビルドしたアプリケーションはどの環境でもそのまま動くことが期待されています。

この Java アプリケーションの配布ですが、元々はどのプラットフォームにも JRE がインストールされていることを前提とした考え方でした。クライアントアプリケーションの配布は実行可能 JAR の形態で行うことが普通です。サーバサイド Java では JRE の上にさらに Java EE コンテナのレイヤを敷き、アプリケーションは WAR もしくは EAR として配布しますね。

ところが、最近はこのように予めランタイムを別途インストールしておき、その上にアプリケーションをデプロイするスタイルはあまり好まれなくなっているように見受けられます。必要なものは1つのパッケージに全て含まれており、箱から出したらすぐ使えるような形態 (これを「自己完結型パッケージ」と呼びます) が好まれるようになっています。

これは OS ベンダ提供のアプリケーションストアという配布形態の登場が大きく関係していると考えています。アプリケーションストアから配布するアプリケーションはサンドボックス化されており、自己完結型であることが望まれています。エンドユーザとしても、アプリケーションストアからワンクリックでインストールが可能なスタイルに慣れてしまうと、わざわざランタイムをインストールするのは煩わしく感じられるようになるでしょう。特にデフォルトでは JavaFlash などがインストールされておらず、アプリケーションストアの利用率が高い Mac 環境ではその傾向が強いように思われます。

そしてもう一つの環境の変化はセキュリティです。どの OS、プラットフォームでも以下のようにセキュリティ対策が強化され、以前のように何も考えずに「野良アプリケーション」をほいほいインストールできなくなっています。

  • Java Web StartJava Applet では未署名のアプリケーションの実行はブロックされるようになりました。
  • Windows では未署名アプリケーションをダウンロードする際にはブラウザが警告を出します。
  • Mac では Gatekeeper という仕組みが導入され、デフォルトの状態では Mac App Storeからインストールしたアプリケーション、もしくはAppleデベロッパ登録し、そのデベロッパ ID で署名したアプリケーションのみが起動可能になっています。
  • FirefoxGoogle Chrome といった Web ブラウザの拡張機能でさえも、ブラウザベンダが運営する配布サイトからのみのインストールに制限されるようになりました。

JDK に同梱されている javapackager はこのようなクライアントアプリケーション配布を巡る環境の変化に対応できるようになっています。

javapackager の概要

javapackager は JDK に付属しており、Java アプリケーションのパッケージング、デプロイメントのためのツールです。

元々は javafxpackager という名前で、その名の通り JavaFX アプリケーションをパッケージングするための専用ツールとして JavaFX SDK に同梱されていました。JDK7 update6 以降は JDK に同梱されるようになり、Java アプリケーション全般に利用可能なツールになりました。JDK8 update20 以降は javapackager に名を変え、名実共に Java SE のためのツールに昇格 (?) しています。

javapackager が提供する機能は次のようなものになります。

  • 実行可能 JAR のビルド
  • 配布用パッケージの生成
    • Java Web StartJava Applet 向けバンドルの生成
    • 自己完結型パッケージの生成
      • インストール先の OS に合わせたネイティブインストーラの生成
      • JRE も含めたパッケージを生成し、OS 側にランタイムの事前インストールを要求しない
  • 配布プラットフォームに合わせたアプリケーションの署名
  • アプリケーションストア向けパッケージの生成

以降では上に挙げた javapackager の各機能について簡単に説明していきます。

なお、この javapackager の使い方を含む、クライアント Java アプリケーションのデプロイメント全般について詳細に解説した Oracle 公式のドキュメントがあります。日本語化もされています。詳細についてはこちらを参照してもらうとして、ここではつかみの部分を解説することにします。

Java Platform, Standard Editionデプロイメント・ガイド
http://docs.oracle.com/javase/jp/8/docs/technotes/guides/deploy/

javapackager 提供機能の簡単な説明

基本的な使い方

javapackager はコマンドラインツールです。javac や jar コマンドなどと同じディレクトリにインストールされているはずなので、これらコマンドへのパスが通っていれば使うことができます。また、Ant 向けタスクも用意されているのですが、ここではコマンドラインとしての使い方に限定して説明します。Ant での利用方法については上記ドキュメントを参照してください *1

次のような使い方をします。

$ javapackager コマンド [オプション]

javapackager に続けて実行したいタスクに応じたコマンドを指定するのが基本です。コマンド別にオプションがあり、必要に応じて指定します。コマンドは次の 5 種類があります。

コマンド 実行するタスク
-createbss CSS ファイルをバイナリ形式に変換する。
-createjar 実行可能 JAR を生成する。
-deploy デプロイ可能なパッケージを生成する。
-makeall -createjar と -deploy の両方を実行し、実行しているプラットフォームで生成可能な全ての形式のアーカイブを生成する。
-signjar JAR ファイルを署名する。

実行可能 JAR のビルド

まずは基本となる実行可能 JAR のビルドです。通常の jar コマンドが提供する機能に加え、次のような JavaFX 固有の機能を提供しています。

  • プリローダアプリケーションの指定
  • CSS のバイナリ化

1番目にあるプリローダとは、JavaFX アプリケーション本体が起動するまでの間に表示する小さなアプリケーションのことです。主にネットワークからアプリケーションをダウンロードする Java Web StartApplet で使われます。

2番目は CSS ファイルをバイナリ変換するというもので、CSS ファイルが巨大な場合などに、ファイルのパース速度を上げる目的で使用されます *2

-createjar コマンドを使って本機能を利用します。以下にコマンドの実行方法を示します。

$ javapackager -createjar -nocss2bin -appclass アプリケーションクラス名 -srcdir JARに含めるファイルのディレクトリ -outdir JARの出力ディレクトリ -outfile JARファイル名 -preloader プリローダクラス名

-nocss2bin オプションを指定すると、CSS ファイルのバイナリ化が実行されなくなります。未指定の場合だと CSS ファイルのバイナリ化が勝手に実行されてしまうので注意が必要です。 -appclass オプションで指定するのは main メソッドを含むクラスの名前です。マニフェストファイルは勝手に作ってくれますが、自分で追加の属性を指定したい場合は -manifestAttrs オプションに続けて "名前=値,名前=値,..." の形式で指定します。

配布用パッケージの生成

これが javapackager のメイン機能となります。アプリケーション JAR ファイルを準備した状態で -deploy コマンドを使って実行しますが、オプションの指定次第で様々なことができます。

まずは基本的な使い方を示します。以下に示すコマンドを実行すると、Java Web StartApplet 向けに JNLP ファイル及び Applet を実行する HTML ファイルが出力されます。

$ javapackager -deploy -outdir 出力ディレクトリ -outfile 出力ファイル名 -srcdir JARのあるディレクトリ -srcfiles 対象となるJARファイル名 -appclass アプリケーションクラス名 -name アプリケーション名称 -title アプリケーションタイトル

このコマンドを実行すると -name オプションで指定した名前の HTML、JNLP ファイルが出力されます。-title オプションは JNLP ファイル内の <title> タグに反映されます。また、これから説明する自己完結型パッケージの生成でも意味を持ちます。他にもオプションはあるのですが、詳細は javapackager コマンドのリファレンス ( Windows 向けMac、Linux 向け ) を参照してください。

ここでさらに -native オプションを指定すると、自己完結型パッケージを生成することが可能になります。インストール先の OS 向けのネイティブインストーラを生成し、アプリケーション専用の JRE も含めたインストールイメージを作成します。これにより、インストール先の OS に JRE の事前インストールを要求せず、また OS に予めインストールされている public JRE の影響も受けません。これならば「アプリケーションの検証が終わっていないので、JRE のアップグレードはしないでください (> <)」みたいなかっこ悪いことも言わずに済みますね。:)

-native オプションに続けて、次のバンドルタイプを指定することができます。

タイプ 説明
all 無指定の場合はこれが選ばれる。installer、image の両方を指定した場合と同じ結果になる。
installer コマンドを実行している OS が対応している全てのインストーラを生成する。
image アプリケーションインストールディレクトリの内容を展開する。Mac の場合は .app ディレクトリを作り、その下に展開する。
exe Windows の .exe インストーラを生成する。Inno Setup のバージョン 5 以上がインストールされている必要がある。
msi Windows の .msi インストーラを生成する。WiX Toolset のバージョン 3.8 以上がインストールされている必要がある。
dmg MacDMG パッケージ (ドラッグ&ドロップ形式のインストーラ) を生成する。
pkg Mac の PKG インストーラを生成する。
mac.appStore Mac App Store 用のパッケージを生成する。
rpm RPM パッケージを生成する。
deb Debian パッケージを生成する。

指定できるタイプはコマンドを実行する OS で利用可能なパッケージに限定されます。Windows では exe や msi を指定できますが、dmg や pkg、rpmdeb は指定できません。Mac App Store 向けのバンドルタイプもありますね!

-native オプションを使って、自己完結型パッケージを生成する場合、パッケージ固有のオプションを -Bオプション名=値 の形式で指定することができます。ここでは、WindowsMac 向けの代表的なオプションを紹介します。

Windows 向けパッケージ生成オプション

Windows の場合、-native exe もしくは -native msi オプションでインストーラを生成しますが、さらに次のようなオプションを指定することで、カスタマイズを行うことができます。

オプション 説明
-BappVersion=バージョン文字列 アプリケーションのバージョンを指定します。アプリケーションのプロパティで確認できるバージョンになります。
-Bicon=icoファイルのパス アプリケーションのアイコンファイル (.ico ファイル) を指定します。パスは -srcdir で指定するディレクトリからの相対パスになります。
-Bcopyright=コピーライト文字列 アプリケーションのコピーライト文字列を指定します。
-BlicenseFile=ファイルパス exe の場合にのみ有効で、インストーラに使用許諾契約を表示したい場合、そのファイルへのパスを指定します。
-BmenuHint=boolean インストール後、スタートメニューにショートカットを追加したい場合は true を指定します。
-BshortcutHint=boolean インストール後、デスクトップにショートカットを追加したい場合は true を指定します。
-BsystemWide=boolean アプリケーションをユーザローカルにインストールするか、システムレベルにインストールするかを選択します。
-Bwin.menuGroup=グループ名称 スタートメニューにショートカットを追加する場合、スタートメニューにグループを追加した上でその下にショートカットを作ります。そのグループ名を指定します。
-Bvendor=任意文字列 アプリケーションを提供する組織や個人名などを指定します。

結構色んな指定ができることが分かるでしょう。少し重要なのが -BsystemWide オプションです。true にした場合はシステムレベルでインストールされ、いわゆる Program Files ディレクトリ以下にイントールされるようになります。つまり、インストールに管理者権限が必要となります。false の場合はユーザローカルにインストールされます (インストールディレクトリは %LOCALAPPDATA%) 。この場合は管理者権限は不要です。

生成されたインストーラに対して、SignTool を使って署名することもできます。

以下に、実際のコマンド実行例を示します。自分が開発している Social Bookmark Viewer FX をパッケージ化したときのコマンドです。

> javapackager -deploy -native exe -outdir target -outfile SocialBookmarkViewer -srcdir target -srcfiles social-bookmark-viewer-fx-0.1-SNAPSHOT.jar -appclass aoetk.bookmarkviewer.MainApp -name "SocialBookmarkViewer" -title "Social Bookmark Viewer" -BappVersion=0.1 -BsystemWide=true -Bwin.menuGroup="Social Bookmark Viewer"

これを実行すると、次のような出力が得られます。
f:id:aoe-tk:20151219164935p:plain

生成されたインストーラを実行すると、次のようなおなじみのアプリケーションインストーラが立ち上がることになります。
f:id:aoe-tk:20151219165022p:plain

インストールが完了すると、スタートメニューにも登録されました!
f:id:aoe-tk:20151219165035p:plain

インストールディレクトリの下は次のようになっています。exe を実行して起動します。runtime ディレクトリの下には JRE のライブラリが入っています *3
f:id:aoe-tk:20151219165052p:plain

Mac 向けパッケージ生成オプション

Mac の場合は -native dmg-native pkg オプションを使いますが、Windows の場合と異なり、インストーラを生成するために特別なアプリケーションをインストールする必要が無いので、単純に -native だけを指定しちゃっても問題ありません。Mac 向けのカスタマイズオプションを示します。

オプション 説明
-BappVersion=バージョン文字列 アプリケーションのバージョンを指定します。アプリケーションのプロパティで確認できるバージョンになります。
-Bicon=icnsファイルのパス アプリケーションのアイコンファイル (.icns ファイル) を指定します。パスは -srcdir で指定するディレクトリからの相対パスになります。
-Bcopyright=コピーライト文字列 アプリケーションのコピーライト文字列を指定します。
-BlicenseFile=ファイルパス pkg の場合にのみ有効で、インストーラに使用許諾契約を表示したい場合、そのファイルへのパスを指定します。
-BsystemWide=boolean アプリケーションをユーザローカルにインストールするか、システムレベルにインストールするかを選択します。
-Bmac.CFBundleName=名称 アプリケーションメニューバーに表示するアプリケーション名称を -name オプションで指定した名称とは別の名称にしたい場合に指定します。
-Bmac.signing-key-developer-id-app=署名キー名 dmg の場合のオプション。Gatekeeper 向け署名を行いたい場合に指定します。キーがインストールされている場合はデフォルトでそれが使用されるようです。
-Bmac.signing-key-developer-id-installer=署名キー名 pkg の場合のオプション。Gatekeeper 向け署名を行いたい場合に指定します。キーがインストールされている場合はデフォルトでそれが使用されるようです。

Mac の場合、-BsystemWide は true にすると /Applications ディレクトリにインストールされます。Mac の場合はここにアプリケーションをインストールするのが一般的であるため、無指定の場合は true 扱いになります。false にした場合はユーザの Desktop ディレクトリがターゲットになります。

また、Mac 向けにはアプリケーションを "Gatekeeper Ready" にするための対応が入っていることが分かりますね。

Mac 向けのコマンド実行例も同様に示しておきますね。

$ javapackager -deploy -native -outdir target -outfile SocialBookmarkViewer -srcdir target -srcfiles social-bookmark-viewer-fx-0.1-SNAPSHOT.jar -appclass aoetk.bookmarkviewer.MainApp -name "SocialBookmarkViewer" -title "Social Bookmark Viewer" -BappVersion=0.1

これを実行すると、次のような出力が得られます。
f:id:aoe-tk:20151219021419p:plain

生成された DMG ファイルを起動すると、次のようなおなじみのインストール画面が出てきます。
f:id:aoe-tk:20151219021613p:plain

まとめ

このエントリでは javapackager の使い方について掴みの部分の紹介を行いました。最近のクライアントアプリケーション配布を巡る環境の変化に対応した、重要なツールであることを分かってもらえたらと思います。

このツールはクライアントアプリケーションだけでは無く、サーバアプリケーションでも使えると思います。最近はサーバアプリケーションもコンテナにデプロイするのでは無く、単一 JAR に全てをパッケージングしてデプロイする方法も好まれるようになってきました。javapackager を使えば、ランタイムごとパッケージングできるので、より配布が容易になるのではないでしょうか。

javapackager は今回紹介した内容の他にもまだ色々できることがあります (Mac App Store 向けパッケージの作成とか) 。詳しくは上に挙げた Oracle 公式のドキュメント読んでもらえればと。

明日は @ さんの予定です。

*1:というか、この公式ドキュメントでは Ant タスクで利用する方法ばかり説明されています

*2:バイナリ変換すると、ファイルの拡張子が ".css" から ".bss" に変わります。コード側もそれに合わせる必要があります。

*3:昔は JDK を丸ごと放り込むという豪快な感じになっていましたが、最近は結構スリムアップしました。JDK9 の Jigsaw が入るともっと効率よくなるでしょう。

離婚しました

はい、標題の通りです。昨年結婚してからわずか1年とちょっとなのですが、つい先日結婚生活を終了することになりました。ブログに書くかどうか迷いましたが、昨年結婚報告を書いていたので、こちらでも報告することにしました。

色んな方面から多くのお祝いやお心遣いの言葉を頂いたにも関わらず、このような残念な結果になってしまい、誠に申し訳ありません。

理由についてはここでは語らないことにします。ただ、相当に悩んだ上での決断であり、決断に対しては後悔していません。

これからまた人生を再スタートすることになります。このような情けない結果になってしまいましたが、どうかこれからもよろしくお願いします。

JavaFX9に追加される機能が増えるかもしれません

このエントリは JavaFX Advent Calendar 2015 の 7 日目のエントリです。前日は id:bitter_fox / @ さんによる「 JavaFXを直接実行できるjshellを作った 」でした。 今年の Advent Calendar の自分の担当日はもう少し後だったのですが、空いていたので急遽ホットなネタで埋めることにしました。

先日、JavaFX コミュニティの間で、JavaFX を取り巻く現状について怒りをぶちまけた、以下のブログエントリが話題を呼んでいました。

Should Oracle Spring Clean JavaFX?
https://www.codenameone.com/blog/should-oracle-spring-clean-javafx.html

Java によるモバイルアプリケーション開発プラットフォームである Codename One の開発者である Shai Almog 氏 *1 によるエントリで、JavaFX の現状について、「Swing を置き換えるには到底至っていない」「Oracle 自体が JavaFX にコミットする姿勢を見せていない」とかなり厳しく批判しています。

このエントリは OpenJFX の ML でも話題になり、かなり激しい議論になりました。様々な意見が飛び交いましたが、やはり OracleJavaFX に対するコミットを疑問視する意見はかなり出てきました。
確かにここ最近の Oracle の行動には JavaFX から手を抜き始めているように見られてもおかしくないところが目に付きます。

Raspberry Pi 向け Oracle JDK での JavaFX サポートは停止し *2 、Scene Builder のバイナリ配布も停止してしまいました *3
Java9 と共にリリースされる JavaFX9 の新機能についても、大きな変更は Jigsaw 導入に対応して、これまで com.sun.javafx パッケージにあった API のうち、重要度の高いものを public するというものだけです。確かにこの対応は非常にリソースを割く作業であることは理解できますが、それにしても新しいコンポーネントの追加などが一切無いというのは寂しいものです。

ある程度議論が進んだところで、現在の JavaFX チームのリーダーである Kevin Rushforth 氏がコメントを挟んできました。

http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-December/018320.html

JavaFX はOpenJFX というコミュニティで開発を進めており、意見はオープンに取り入れるつもりであること、決して数が多くは無いけど、JavaFX9 には中々興味深い改善をするつもりだよ、といった内容です。
まず、JavaOne でも発表したことのようですが、JavaFX9 の新機能として以下のようなものをリストアップしていました。

  • A modularized JavaFX (into 6 core modules + deploy, swing interop, swt interop)
  • JEP 253 -- Control Skins & additional CSS APIs (proper support for third-party controls)
  • High DPI enhancements (full support on Windows; add support for Linux)
  • Public API for commonly used methods from internal packages:
    • Nested Event Loop
    • Pulse Listener
    • Platform Startup
    • Text API (HitTest, etc)
    • Static utility functions (under investigation)
  • New versions of WebKit and GStreamer

4 番目はこれまで internal だった API のうち、便利そうなものを public にするというものですが、確かに地味に良さげなものが並んでしますね。Text API とか気になる。

さらに Java9 リリース延期の提案 を受けて、もう少し機能追加しても良いかもとのコメントをしています。候補として次のような機能を挙げています。

  • Provide a JavaFX equivalent for JEP 272 / AWT ‘Desktop’ API
  • Make UI Control Behaviors public
  • UI Control Actions API
  • Public Focus Traversal API
  • JavaFX support for multi-resolution images
  • Draggable tabs
  • Image IO

1 番目は AWT 向けの JEP である JEP 272: Platform-Specific Desktop FeaturesJavaFX 向けにも提供しようというものです。この JEP はプラットフォーム固有のデスクトップ機能をより利用できるようにするというものです。過去には Java6 で一度強化が入っており、タスクトレイへのアクセスなどが可能になったりしましたが、久々にこの分野にテコ入れが入ることになります。

  • Mac のアプリケーションメニューの利用 (かつて Apple Java に含まれていた EAWT では提供されていました) 。
  • Windows タスクバー (Mac ではドック) のジャンプリストへのアクセス。
  • Windows タスクバー (Mac ではドック) でのプログレス表示。

よりネイティブなアプリケーションとして振る舞うことが可能になるので、この強化は是非とも入って欲しいなあと思っています。ましてや近年は javapackager を使ってネイティブアプリっぽく配布することが推奨されていますし。まあ、AWT には入るので、最悪 JavaFX 側に入らなくても何とかなるのではありますが、JavaFX 側から AWT の API を触るときは別スレッドにする必要があって面倒ですし。

3 番目とか 4 番目は Swing にある同名の API と同じかな。Action API は欲しいですねえ。

5 番目は HiDPI 環境におけるビットマップ画像の扱いのことでしょうか。WindowsMac 共に HiDPI 対応が入りましたが、ビットマップ画像の扱いは Mac 式になっており、整数倍にしか対応していません。Mac 式にしたのは恐らく暫定対応でしょうから、ここできちんと対応するということなのでしょう。

最後の Image IO も詳細は不明ですが、このあたりはまだまだ足りないところが多いので期待したいところですねえ。

こんな感じで思わぬところから、今後の機能強化について話が出てきました。幸い (?) Java9 のリリース延期はほぼ確実でしょうから、JavaFX9 ももう少し新しい機能が入った状態でリリースされることにはなりそうです。
まあ、そうは言ってもやっぱり OracleJavaFX に対してリソース抑えているよなあとは思いますが、それはまた別の話で。

明日は id:skrb / @ さんの予定です。

*1:元々は Sun で LWUIT の開発をされていた方のようです

*2:OpenJFX での開発は継続しています。

*3:Gluon が開発を継続し、 ここ でバイナリの配布も行っています。

JavaFX 8u60の新機能 (特にWindowsでのHiDPI対応について)

先日、JDK8 の機能アップデート版である JDK 8u60 がリリースされました。このバージョンのリリースノートは以下です。

http://www.oracle.com/technetwork/java/javase/8u60-relnotes-2620227.html

ですが、このリリースノートにはバンドルされている JavaFX 8u60 についての記述がありません。
以前から、JavaFX については 8u60 は安定性向上がメインのリリースになる、とのアナウンスがあったのですが、機能面でのアップデートが少し入っているようなので、自分が調べた範囲で分かったことをまとめます。

自分が調べた範囲では JavaFX 8u60 に次のアップデートが入っていることが分かりました。

  1. Windows で HiDPI 対応が入っている
  2. WebView の WebKit のバージョンが上がっている

Windows での HiDPI 対応について

まず 1 の Windows における HiDPI 対応についてです。こちらについては自分のブログでも以前に次のようなエントリを書いていました。

aoe-tk.hatenablog.com

そのエントリで「とにもかくにも JavaFXWindows でも Device Independent Pixel (DIP) に対応してくれるのが一番なんですけどねえ。」と結んでいたのですが、まさしくその対応が入ったことになります。

最近増えてきた高解像度端末では、そのままだと文字やアイコンなどが小さくなって読めたものじゃありません。そこで、Windows では物理的なスクリーンのサイズと解像度を見て、125%、150%、200%...と自動的にスケーリングを行うようになります。
私が使っている VAIO Tap 11 は 11 インチのディスプレイで解像度が Full HD (1920 x 1080) の端末なのですが、デフォルトで 125% のスケーリングが掛かっています。なのでこの端末だと、100px の長さのコンテンツが画面上では 125px を使って描画されるようになります (Windows のスケーリング設定を見てくれる処理系である場合) 。

これまでの JavaFX では、基準フォントサイズについては Windows のスケーリング設定を見て描画してくれたのですが *1 、プログラム上のピクセル指定についてはそのような考慮がありませんでした。それが 8u60 ではスケーリング設定の倍数を掛けたピクセル数で描画してくれるようになります。

詳細については OpenJFX の ML の以下のポストを参照してください。

http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-June/017337.html

と言うわけで自分の VAIO Tap 11 でもスケーリングが掛かる!と思っていたのですが、 変わりませんでした
調べてみたところ、 スケーリングの設定が有効になるのは、スケーリングが 150% 以上の場合 という制約が掛かっているようです。
JDK Bug System の次のチケットなどでこの点について議論されていました。

https://bugs.openjdk.java.net/browse/JDK-8129862

https://bugs.openjdk.java.net/browse/JDK-8130748

どうも、125% のスケーリングではフォントがぼやけて描画されてしまう問題を解決できず、125% ではスケーリングを切ることにした模様です。残念。 *2

ただ、起動引数を使って強制的にスケーリングの設定を掛けることはできます。Javaシステムプロパティ glass.win.uiScale を使って、次のように指定します。

$ java -Dglass.win.uiScale=125% -jar Application.jar

これを使って、自分の VAIO Tap 11 で Ensemble.jar を起動してみたスクリーンショットを示します。

f:id:aoe-tk:20150823231117p:plain

きちんと 1.25 倍にされていることが分かりますね。フォントは確かに少しぼやけてましたが、そんなに気にするほどでもなかったかなあ。
次のアコーディオンのサンプルとかも分かり易いと思います。このアコーディオンは高さ、幅をピクセルで指定しています。

f:id:aoe-tk:20150823231356p:plain

HiDPI 対応についてまとめるとこんな感じです。

  • Windows 上でスケーリングの掛かっている環境では、プログラム中のピクセル指定に設定された倍数を掛けた数のピクセル数で描画される。
  • ただしこの設定が有効になるのは 150% 以上から。
  • 強制的に任意のスケーリングを指定したい場合はシステムプロパティ glass.win.uiScale を使って指定する。
  • ビットマップ画像については、Apple 式の規則が適用され、整数倍の大きさの画像を用意しておけば、それが適用される。

詳細については上に示した OpenJFX の ML のポストを参照してください。

WebKit のバージョンアップについて

JavaFX 8u60 では WebView に使っている WebKit のバージョンが上がっています。主にセキュリティフィックスを取り込むためですが、少しだけ機能追加があるようです。

まず WebView の User-Agent の違いを示しておきます。

JavaFX 8u40
Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/537.44 (KHTML, like Gecko) JavaFX/8.0 Safari/537.44
JavaFX 8u60
Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/538.19 (KHTML, like Gecko) JavaFX/8.0 Safari/538.19

WebKit のバージョンが少し上がっていますね。

HTML5TEST を使ってみると、次のような違いがありました。

  • ECMA Script 6 の Promise に対応している。
  • URL API に対応している...らしい。
    • これどんな API
  • picture 要素に対応していないのに、なぜか srcset 属性には対応しているという結果が出る。
    • これは多分テストが何かおかしい。

大きな違いはありませんが、Promise に対応したというのは大きいですね。
8u40 でのテスト結果は こちら で、8u60 での結果は こちら で参照することができます。

なお、HTML5 で新たに追加されたフォーム要素でカレンダーや色のピッカーがまともに動いていない問題 (私の過去の このエントリ でも触れています) 、は未だに対応していませんねえ。
バグトラッカにも チケットが上がっています が、放置状態です...。

以上、JavaFX 8u60 の新機能について、私が調べた範囲では大体こんなところです。他にも何か情報があったら、教えてもらえると幸いです。

*1:そのため CSS で長さの単位に em を使用することで、HiDPI 対応することは可能でした。

*2:Surface Pro シリーズならば 150% 以上のスケーリングが掛かっているので、デフォルトでスケーリングが有効になっているはずです。誰か試して。

JJUG ナイトセミナー 「Reactive Streams特集」の感想

6/24 に開催された 【東京】JJUG ナイトセミナー 「Reactive Streams特集」 に参加してきました。これはその感想エントリになります。
ここ最近は勉強会に参加しても、それについてのエントリを書くのをサボってましたが (^^;; 、今回の勉強会は自分にとって非常に考えるところが多かった内容でしたので、ちょっと思ったところをだらだらっと並べてみました。

勉強会について

勉強会の内容は 2 部構成になっていました。前半は岡本雄太 (id:okapies) さんによる「Reactive Streams 入門」で、Reactive Streams について、似た言葉である Reactive Programming や Reactive Manifesto と併せて、それぞれの概念の違い、関わりについて分かり易く説明してもらいました。

https://speakerdeck.com/okapies/reactive-streams-ru-men-number-jjug

またこの発表内容について、岡本さん自身が素晴らしいフォローアップエントリを書いてくださっています。 okapies.hateblo.jp

後半はよしだ (@) さんによる「Reactive Streamsを使ってみよう」で、こちらは RxJava を題材に実際のコード例について解説するという内容でした。

http://www.grimrose.org/jjug-2015-reactive-streams/#!index.md

よしださんも自身の発表内容についてのフォローアップエントリをあげています。 http://www.grimrose.org/blog/2015/06/jjug-2015-reactive-streams/www.grimrose.org

勉強会の感想

今、ソフトウェア開発の世界では「Reactive なんちゃら」という言葉がすごく流行っている印象があります。
私が主に目にしていたのは、当日岡本さんが発表していたところの "Reactvie Programming" であったわけですが、次に示すような、一見交わらない世界でそれぞれブームになっている印象があってとても興味深いなと思っていたところでした。

  • GUI 開発の世界
    • 特にデスクトップアプリ、スマートフォンアプリ、Web の SPA のようなリッチクライアントの実装
  • 大規模分散並列処理の世界

そんなところで Typesafe 社らが中心になって、 Reactive Manifesto なる宣言をどーん、と出してきたりして、「あれ、これって流行りの Reactive Programming の話と関係するの?」「でもその割にはターゲットとしているところが違うような...」「ていうか何このメンツ?」と正直混乱していたところでした。

岡本さんは「Reactive なんちゃら」なキーワードとして次の 3 つについて、それぞれの違いについて次のようにとても分かり易く説明してくれました。

  • Reactive Programming は「プログラミングモデル」
  • Reactive Manifesto は「アーキテクチャ
  • Reactive Streams は「ランタイム」

Reactive Programming について

Reactive Programming はデータフローの仕組みを構築するために、関数を DAG としてつなぎ合わせる形で記述し、データの伝搬はランタイムに任せるというプログラミングスタイルです。データの変更を自動的に伝搬させることができる、非同期化、並列化しやすいというメリットがあります。

少し違うところがありますが、このプログラミングスタイル、GUI の世界では「データバインディング」という形でかなり馴染みのある概念だったと思います。
例えば、岡本さんの発表で紹介されたサンプルの式を JavaFXバインディングを使って表現してみると、次のように記述することができます。

   @FXML
   Label answer;
   @FXML
   Spinner<Integer> valueA;
   @FXML
   Spinner<Integer> valueB;

   private void bind() {
       IntegerProperty a = new SimpleIntegerProperty(0);
       a.bind(valueA.valueProperty());
       IntegerBinding a1 = a.add(1); // a1 = a + 1
       IntegerProperty b = new SimpleIntegerProperty(0);
       b.bind(valueB.valueProperty());
       IntegerBinding b1 = b.add(-1).multiply(2); // b1 = (b - 1) * 2
       answer.textProperty().bind(a1.add(b1).asString()); // a1 + b1
   } 

これを動作させると次のように動きます。

全体のコードは Gist にアップしておきました。
https://gist.github.com/aoetk/9c74ee1bb17afb304bff

このように GUI の世界ではこのプログラミングスタイルがかなり前から浸透していましたが、同じようなプログラミングスタイルをもっと他の場所でも使えないか?ということで .NET の世界で Reactive Extensions が登場し、一気に Reactive Programming が流行りだした、というのが私の認識です。
ただ、データバインディングはアプリケーションが扱っているデータの変化に着目していますが、Reactive Programming を GUI に適用する場合はイベントのストリームに着目しているという違いがあるかなと私は考えています。
ちなみに JavaFX の世界でも、イベントストリームに対して Reactive Programming を行うための ReactFX が登場しているようで、機会があれば自分で作るアプリケーションにも使ってみたいな、と思っています。

Reactive Manifesto について

Reactive Manifesto については今回の説明を聞いて、「ああ、これは REST のような、アーキテクチャ原則についてまとめたものだったんだあ」とやっと腑に落ちました。
Reactive Manifesto の中では、昨今見られる大規模システムに共通してみられる設計を備えたシステムを Reactive Systems と名付けています。それらには即応性、弾力性、耐障害性、メッセージ駆動といった特徴を備えているとのことです。
具体例が特に挙げておられず、マーケティング色の濃い文書であるとの説明がありましたが *1 、Microservices というスタイルのアーキテクチャが評価されてきていたり、TwitterFacebook では大量のメッセージを捌くために、非同期でメッセージ駆動ベースのアーキテクチャを構築していますし、昨今のシステム開発においてこのようなアーキテクチャスタイルが求められるようになってきているもの確かだな、と思いました。
成功したパターンから原則を見出しているところも REST と似ていますね。あれも WWW が何故成功したのか?を出発点として出てきたものなので。

Reactive Streams について

で、本題の Reactive Streams ですが、これは Java における Non-Blocking かつ Back Pressure 付きの非同期ストリーム処理を標準化するためのものであるとの説明でした。何と Java9 での標準化を目指しているとのことで、これは驚きました。 *2
非同期ストリーム処理版 JDBC のようなものですが、JDBC とは比較にならないほど対象が広くふわふわとしてるなあと正直思いました。(^^;;

今のところ API 自体はインターフェースが 4 つあるだけのとてもシンプルなものです。まあこれだけ広い対象から最大公約数的に抽出するとこうなっちゃいますかね。
一般的な Publish-Subscribe パターンに加えて、Subscriber 側の処理能力を超えないように、Subscriber 側が Subscription という形で要求を示す (これを Back Pressure と呼ぶそうです) というアーキテクチャになっています。

シンプルではありますが、API を標準化することのメリットは小さくないと思います。今回のよしださんの発表では RxJava と Akka Streams をつなげるサンプルがありましたが、このように異なる世界を同じプログラミングスタイルでつなげられるのは大きいと思いました。

まとめ

だらだらと書き連ねてきましたが、Reactive Programming はプログラミングスタイルに、Reactive Systems はアーキテクチャスタイルについて言及したもので直接的にはつながるものではない (ただし、後者は思想的には前者の影響を受けている) 。Reactive Streams は Reactive Systems のようなシステムを構築する上での道具立てを、Reactive Programming なスタイルで可能にすることを目指したものというのが自分の理解です。
今まで断片的にしか目にしていなかった情報がかなりつながったので、参加してとても良かったと思いました。
素晴らしい発表をしてくださった岡本さん、よしださん、そしてこのような場を設けてくださった JJUG 幹事の皆さん、ありがとうございました。

*1:確かに Typesafe 社の Typesafe Reactive Platform を売り込むものなんだろうなという感じがします

*2:java.util.concurrent パッケージに入れようとしているようです

JavaFXのWebViewの検索を実現するのにもっと簡単な方法がありました

昨年の JavaFX Advent Calendar で次のようなエントリを書きました。 aoe-tk.hatenablog.com

このエントリでは、JavaFX の WebView を使ったアプリケーションに検索機能を実装する方法として、JavaScript のページ検索ライブラリを使う方法を紹介しています。
そこでは jQuery プラグインを使って実装していましたが、そのライブラリの CSSJavaScript オブジェクト定義が WebView で表示しているコンテンツのそれとバッティングする危険性がありました。

ところが、window.find() という非標準の関数があり、JavaFX の WebView がそれをサポートしていることを知りました。 *1
これを使えば特別なライブラリを読み込むこと無く、簡単に Web ページの検索機能を実現することができます。

以前の方法では、WebView 上のドキュメント読み込み完了時に検索のための jQuery プラグインの読み込みや、そのプラグインが要求する CSS クラスの定義の読み込みを行っていましたが、 window.find メソッドを使う場合はその必要はありません。何せ組み込みのメソッドなので。
件のエントリで解説した highlightPage() メソッドの実装を次のように変更します。

private static final String FIND_FUNCTION = "window.find(\"{0}\", false, false, true, false, true, false)";

private void highlightPage(Optional<String> word) {
    if (webEngine.getDocument() != null) {
        final String keyword = word.orElse("");
        if (!keyword.isEmpty()) {
            webEngine.executeScript(MessageFormat.format(FIND_FUNCTION, Encode.forJavaScript(keyword)));
        }
    }
}

単純に JavaScriptwindow.find メソッドを呼び出すように変更しています。
メソッドに渡す文字列に Encode.forJavaScript() というメソッドを噛ましていますが、これは OWASP Java Encoder Project というライブラリが提供する JavaScirpt 特殊文字をエスケープするための関数です。

また window.find メソッドは繰り返し呼ぶと、ページ内の次の一致箇所にフォーカスしてくれるので、次のように検索テキストフィールドのアクションイベントで highlightPage() メソッドを呼び出すようにし、検索フィールドにフォーカスがある状態で Enter キーを叩くと、次の一致箇所にフォーカスするようにしました。

<TextField fx:id="pageSearchBox" onAction="#handleSearchBoxAction" promptText="Find in page" HBox.hgrow="NEVER" />
@FXML
void handleSearchBoxAction(ActionEvent event) {
    highlightPage(Optional.ofNullable(pageSearchBox.getText()));
}

こうすると次のように検索フィールドにフォーカスがある状態での Enter キーをヒットすると、次の検索一致箇所にフォーカスが移動します。
f:id:aoe-tk:20150615001101p:plain

と言うわけで、JavaFX WebView で検索機能を実現する方法の訂正版でした。組み込みのメソッドを利用するので、この方法が一番良いと思われます。

実装の全体を確認されたい方は、私が開発している Social Bookmark Viewer FXBookmarkViewController.java 及び BookmarkView.fxml の実装を参照してください。

*1:この関数は元々 Netscape Navigator が独自に実装していた関数で、Firefoxレンダリングエンジンである Gecko もサポートし、WebKit もサポートしたようです。