ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

JDK 8 build b91 を使って JavaScript から JavaFX の WebView をいじる

JDK 8 と JavaFX と Nashorn

OpenJDK のページを見ていると、JDK 8 のマイルストーンとして 5 月 23 日に Feature Complete とあった。

というわけで 5 月 23 日にリリースされた JDK 8 build b91 に一通りの機能が詰まってるものだと思って *1、主に JavaFX とか Nashorn とかを試してみた。

JavaFX 2

クライアントサイドの GUI プラットフォーム (?)。 JDK 7 の途中から標準で JDK に含まれるようになった。 JavaFX 1.x 系はあんまり使われてなかった印象があるけど、JavaFX 2.x 系になってからは実態もガラッと変わり、Swing を置きかえて今後の JavaGUI プログラミングの主流になるっぽい。

JavaFX には WebView があって、ページ中の DOM オブジェクトに Java 側から触れるというあたりが web 系の開発者としては興味深いところ。 JDK 8 build b91 にも含まれている。

JavaScript 処理エンジン Nashorn

Java で書かれた JavaScript 処理エンジン。 JDK 8 以降に標準で含まれる。

もともと JavaJavaScript 処理系といえば Mozilla 製品の Rhino があったわけで、JDK 6 以降には Rhino が標準で含まれていたのだけれど、JDK 8 以降ではこれが Nashorn で置きかえられる。 JDK 8 build b91 にも含まれている。

(Rhino のときからそうだけど) JavaScript から Java のライブラリを (少ない障壁で) 触れるというのが使いやすくて好き。

JDK 8 Early Access Releases のインストール

Early Access Releases として JDK 8 のページでダウンロードできるようになっている。 5 月 23 日には build b91 がリリースされている。

ダウンロードして展開してパスを通せば使えるはず *3

jjs コマンドで JavaScript を実行する

コマンドラインツールとして jjs コマンドが含まれている。 このコマンドにファイルパスを渡すとそのファイルを JavaScript として実行できる。 また、ファイルパスを渡さなければ対話型の JS 処理系が開始される (Ruby における irb みたいなもの)。

当然ながら Java の入出力機能が使えるので “Hello world!” の出力は以下のようになる。

java.lang.System.out.println("Hello world!");

まあ print 関数を使った方が短くなるけど。

print("Hello world!");

みんな大好き public static void main を書かなくても実行できちゃうのは寂しいところ。

jjs コマンドで JavaFX アプリケーションを実行する

最近 (?) jjs コマンドに -fx オプションが追加されたらしい。 詳細は下記ブログ記事に書かれている。

jjs コマンドで -fx オプションを使うと、JavaScript で簡単に JavaFX アプリケーションを書くことができる。

// Java の import 文の代わりにこんな感じに書ける
var StackPane = javafx.scene.layout.StackPane;
var Scene     = javafx.scene.Scene;
var Label     = javafx.scene.control.Label;
var Screen    = javafx.stage.Screen;

function start(stage) {
    stage.title = "Hello World!";

    var label = new Label("Hello world!");
    var root  = new StackPane();
    root.getChildren().add(label);

    var scene = new Scene(root, 300, 200);
    stage.setScene(scene);

    stage.show();
}

上のコードを helloworld.js というファイル名で保存しておいて、

jjs -fx helloworld.js

という感じで jjs コマンドを実行すれば、次のようなウィンドウが表示されるはず。

f:id:nobuoka:20130525200048p:plain

ちょっとした GUI アプリケーションを書くときに便利かも?

JavaFX の WebView と WebEngine を使う

ここからが本題。 上にも書いたように、JavaFX には WebView や WebEngine といったものが含まれている。

これらを使えば、指定の web ページを WebEngine に読み込ませて、その DOM に Java から触ることができる。 もちろん Nashorn を使えば JavaScript から Java に触れるわけなので、JavaScript から読み込んだページの DOM とかを触れるわけだ。

例えばこのブログの全リンク (anchor 要素) のテキストと href 属性を取得して表示するプログラムは次のようになる。

var Scene   = javafx.scene.Scene;
var WebView = javafx.scene.web.WebView;

function start(stage) {
    var view = new WebView();
    var webEngine = view.getEngine();
    webEngine.load("http://vividcode.hatenablog.com/");

    // 読み込み完了時の処理
    webEngine.getLoadWorker().stateProperty().addListener(
        // Java の匿名内部クラス的な構文
        // 他の書き方があるかもしれないけどよく知らない
        new javafx.beans.value.ChangeListener() {
            changed: function (ov, oldState, newState) {
                if (newState === javafx.concurrent.Worker.State.SUCCEEDED) {
                    stage.setTitle(webEngine.getLocation());
                    printAllAnchorElemsFromDoc(webEngine.getDocument());
                    exit(); // 本当は何か終了処理を入れないとダメかもしれない
                }
            }
        }
    );

    stage.setScene(new Scene(view, 800, 600));
    stage.show();
}

function printAllAnchorElemsFromDoc(doc) {
    var elems = doc.querySelectorAll("a");
    for (var i = 0; i < elems.length; ++i) {
        var elem = elems[i];
        var caption = elem.textContent;
        if (caption === null) caption = "";
        caption = caption.trim().replaceAll(/\s+/g, " ");
        var link = elem.getAttribute("href");
        //表示
        java.lang.System.out.printf("%s%n → %s%n", caption, link);
    }
}

もちろん Java だけでも同様のことはできるわけだけど、ちょっとした処理なら JS でぱぱっと書けるのは便利だと思う。

(追記) 別に表示させる必要がないなら WebEngine だけ使えばよくて WebView を使う必要はない。

ヘッドレスでの実行

似たようなことができるものとしては PhantomJS があって、あっちはバージョン 1.5 から完全にヘッドレスで処理されるようになっている。 JavaFX の方には今のところヘッドレスでの処理の機能はないようである。

X ウィンドウサーバーが動く環境でヘッドレスにしたいなら Xvfb を使うのが良さそう。

Xvfb :99

というコマンドで仮想的なディスプレイを動かしておいて、

DISPLAY=:99 jjs -fx test.js

という感じで仮想的なディスプレイを指定して JavaFX アプリケーションを実行。

あなたと Nashorn、今すぐダウンロード

最近はコマンドラインで JS を処理させる方法として Node.js が主流という感じではありますが、次のような方には Nashorn がおすすめです!!!

*1:思ってるだけで実際にどうなのかは不明。 Feature Complete とかよくわからない。

*2:どうでもいいけどこの JDK 8 のページ (Oracle 管理っぽい?) と OpenJDK の関係がいまいちよくわからない。

*3:環境変数 JAVA_HOME の設定もいるかもしれない。