VAIO Tap 11 購入レポート

先週、SONYタブレット PC である VAIO Tap 11 を買っちゃいました。
VAIO を購入するのは実に 13 年振りです (Windows マシンを購入するのも 11 年振りくらい) 。
1 週間使ってみての感想とかをまとめてみました。

購入のきっかけ

今家にある情報端末は次のような感じです。

はい、Apple 製品ばっかりです。母艦の PC が MBP なのですが、デスクトップ代わりに購入したので、持ち運びにはちょっと辛いです。
外に PC を持ち運ぶ際には仕方なくこれを外に持ち出すのですが、会社で支給された MBA の携帯性を体験すると辛くなってしまいました。
iPad を使うという手もあるのですが、これはよく言われているようにコンテンツの消化には最高なのですが、これで何かを書いたり作ったりするのはちょっと厳しいです。
外付けキーボードを付けても、キーの入力に本体の処理速度が追い付かないなど、やはり PC の使用感にはほど遠いものでした。第一、iPad ではプログラミングができない!

そこで登場した Windows8。PC とタブレットの融合を狙ったあのインターフェースなら、もしかして MBAiPad の役割をこれ 1 台で満たすことができるのではないか?の期待を持ったわけです。
次のような条件を満たすマシンが出たら買いたいな、と思ったのですが、Windows8 リリース直後では中々これに適合するデバイスが出てきませんでした。

  • タブレットとキーボードが分離可能であること。
  • タブレット部が軽量であること。
    • 最低限 1kg は切っていて欲しい。
  • WindowsRT ではなく Windows8 であること。
    • これまでの Windows アプリケーションが動かせないなんてあり得ない!
    • というか RT だったら開発できない。
  • 解像度はフル HD 以上。

最近になってようやくこの条件を満たせそうなマシンが出てくるようになり、最終的に Surface Pro 2 とこの VAIO Tap 11 が候補に残りました。
そうこうしているうちに Surface Pro 2 は生産が追い付かなくなって受注停止、一方 VAIO の方は値引きキャンペーンを開始したので、やや衝動的にぽちってしまいましたw *1

次のようなスペックで購入しました。

  • ホワイトモデル
  • Core i3
  • SSD 256GB
  • メモリー 4GB

外観

外観はこんな感じです。ディスプレイサイズが 11 インチなので、タブレットとしてはやはり大きく感じます。手にした重さについては大きさの割に軽く感じられます。

背面はこんな感じ。白は高級感があっていいです。スタンドは Surface と違い、角度が無段階になっています。

タブレット本体は薄いのですが、ファンはあります。この辺は PC っぽさがあります。

付属のキーボードです。Surface と違い、完全に分離しており、無線で本体につながります。非常にしっかりしたキーボードです。ただ、トラックパッドが...。

キーボードはこのように本体にふたとしてぺたっと貼り付けられます。というか、キーボードはこのようにして本体に装着しないと充電できません。
下に見えているのはスタイラスで、これもしっかりした作りで使い易いです (電池が必要ですが) 。

iPad 4th gen と重ねてみました。iPad をさらに「長く」したような感じですね。

使ってみた感想

ハードウェア面

ディスプレイはとてもクリアです。ここはさすが SONY と言ったところ。
解像度はフル HD (1920 * 1080) 。200ppi 弱になり、さすがに Retina Mac には少し及びませんが、文字はとてもくっきりです。ある程度目を近づけないとドットが目立ちません。
11 インチでこの高解像度であるため、そのままでは全てが小さく表示されてしまうので、デフォルトで 125% の DPI スケーリング (120dpi) に設定されていました。 *2
ただ、このスケーリングでも字が小さいと感じる人がいそうだと思いました。人によってはもう一段階スケールを上げた方が良いかもしれません。

先述したようにキーボードの打ち味はとても良いです。下手なノート PC よりよっぽど良かったりします。分離式なので好きな場所にキーボードを置けるというのもいいです。
ただ、トラックパッドの操作性がひどい。 カーソルが思ったように動かないし、かと思うとタイピングしている最中に手のひらが触れてしまったら反応して事故が起きたり...。
トラックパッドを殺すことのできるハードスイッチがあったので、タイピングしているときはこのスイッチを入れるようにした方が良さそうです。
別途マウスを調達した方がいいかなあと思いました。特にデスクトップモードで作業しているときはタッチだけでマウスの代用をすることは不可能なので。

タブレットとしての使い勝手ですが、タッチパネルは iPad と比べるとちょっと精度が落ちるかなあと感じるところがありました。たまにタッチしても反応しなかったりすることが。やはり Apple はこの辺りをとても良くチューニングしています。
とは言え、Windows8 自体の操作性がとてもいいので、基本的にタッチパネルでの操作感は良好です。
大きさの割には軽い (780g) ため、持ち運びやすい点もグッドです。
ただ、横持ちで使っているときはいいのですが、アスペクト比が 16:9 であるため、縦持ちにするととても細長くなって、ちょっと気持ち悪いです。
Web や横書きの文書を閲覧するときは縦持ちの方がいいのですが、これではちょっと違和感が大きいですね。タブレットとして使うのならば iPadアスペクト比が一番丁度良いと思います。

ソフトウェア面

最初に面食らったのが、インストールされている OS が 8.1 ではなく、無印の Windows8 だった点です。
そのためいきなり OS のインストール作業をする羽目に...。
Windows8 ではデスクトップモードの操作性にかなり問題がありましたが、8.1 で大分マシになりましたね。

色々言われている Windows8 ですが、タブレットとして使った場合の操作性は素晴らしいです。
あのスタート画面も、アプリを開くことなくサマリの情報が表示されるので、これはこれで便利です。
Store アプリケーションは操作性の統一が iOS 以上に徹底されています。次の規則を覚えると、どのアプリケーションでもすぐに使い方を覚えられるようになります。

  • 上下のエッジスワイプでコンテキストメニューの表示
  • 右のエッジスワイプで出てくるチャームメニューで他アプリとの連携
  • サマリ/詳細の切り替えにセマンティックズームが使える (ピンチイン、アウトで表示を切り替えられる)

他アプリと連携するコントラクト (Andoroid のインテントに相当) もとても便利です。
問題は、肝心の Windows Store の品揃えが壊滅的なことです。TwitterFacebook、Flipboard、Evernote といった超メジャーどころはありますが、いずれも iOSAndroid 版と比べると機能が少なく、かなりおざなりな感じです。操作感はいいのに...。
そのため、iPad の完全な代替になるかというと、ちょっと厳しいです。

デスクトップモードについては、キーボードが使い易いこともあって、ノート PC として使っても快適に感じます。
ただ、高 DPI スケーリングに対応していないアプリケーションが多いことが分かりました。
ぼやっと拡大されるアプリケーションはまだいいですが、文字が豆粒のようになってしまうのは困りものです。
例を挙げるとデスクトップ版 EvernoteJava を使ったアプリケーションなどです。
NetBeans はデフォルトだとコンソールの文字が豆粒のようになってしまい、とても悲しかったです (タスクバーの大きさに対するコンソールの文字の大きさに着目) 。

そのうち JavaFX で高 DPI に対応するにはどうすればいいか、調べてみるつもりです。

まとめると、タブレットとして使っても、ノート PC として使っても、細かいところで不満点はありますが、基本的に快適です。
iPadMBA の役割を 1 台で満たすデバイスを手に入れるという野望がどの程度満たされたかというと、次のような理由から 50% くらいかなあという感想です。

  • 縦持ちで使いにくい。
  • Store アプリが貧弱であるため、タブレットとして使った場合に制約が多い。
  • トラックパッドの操作性が良くない。

でも、これなら出先でプログラミングや Office 文書の編集ができるし、しかも Web や SNS のチェックとかはタブレットの操作性で行えるので、今後外へ持ち歩くデバイスは基本的にこれになると思います。
まあ良い買い物したと思っています。 *3

おまけ

Windows タブレットの事実上の標準ベンチマークとなっているアレですが、Corei3 だけあってさすがに快適に動きます。

ただし、バックグラウンドで別のタスクが走っている場合は極端に遅くなります。
あと、結構ファンが回るので、バッテリーの消費が激しくなる点には注意です。 *4

*1:でも VAIO Tap 11 の方も発注してから納品するまで1ヶ月掛かったんですけどね。

*2:Surface Pro は同じフル HD でも 150% に調整されているようです。Surface の方がディスプレイサイズが1インチ小さいからですかね。

*3:あと、図らずも SONYVAIO を手放すことになったので、その意味でも記念購入になっちゃいましたね。

*4:ぶっちゃけ艦これFlash の作りはかなりチューニングの余地があると思っているんですけどねえ...。

GlassFishで仮想ディレクトリマッピングを行う方法 (GlassFish Advent Calendar 23日目)

このエントリは GlassFish Advent Calendar 2013 の 23 日目のエントリです。前日は id:makoyanagisawa さんによる「@btnrougeが教えてくれなかったこと」でした。

当初、今年の Java Day Tokyo で披露した FX GlassFish Monitor に絡めて、GlassFish が公開している監視項目の REST API の話にしようと思ったのですが、蓮沼さんの第8日目のエントリでやられてしまったので、別の小ネタにします。

今回取り上げるのは、WAR の外にあるコンテンツをアプリケーションのドキュメントルートにマッピングする方法です。仮想ディレクトリマッピングと呼ばれるやつです。 *1

例えば次のディレクトリに存在するコンテンツを http:////content にマッピングしたいとします。

/home/hoge/external/content/

その場合、glassfish-web.xml に次のような記述を追加します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="">
  ...
  <property name="alternatedocroot_1" value="from=/content/* dir=/home/hoge/external"/>
</glassfish-web-app>

glassfish-web-app 要素の子要素である property 要素に alternatedocroot_* (* には連番が入る) という名前で設定を記述します。複数設定したい場合は連番を 1, 2, ... と続けます。
値には "from=(コンテキストパス以下のパス) dir=(マッピングしたいドキュメントルート)" という形式で記述します。
from で指定したパスがサブディレクトリとして付くことに注意してください。この例では /home/hoge/external/content 以下を見に行きます。

開発者以外の人が管理しているコンテンツ (例えばヘルプドキュメントとか) がある場合にはこうすると WAR に含めずに済むので便利ではないでしょうか。

というわけでちょっとしたネタでした。
明日は蓮沼 (@) さんの予定です。

*1:WebLogic だと weblogic.xml 要素で設定するやつです。

Java EE Managed Beanについて (Java EE Advent Calendar2013 19日目)

このエントリは Java EE Advent Calendar 2013 の 19 日目のエントリです。前日は @さんによる、「JSF 2.2 でさらに便利になったMarkupを使ってみよう」でした。

はじめに

Java EEの仕様の中でも立ち位置が微妙すぎて、かの金魚本でもガン無視されちゃっている Java EE Managed Bean のお話を取り上げます。
(JSF の Managed Bean のお話じゃないのでご注意を!)
Managed Bean は JSR-316 Java EE 6 の一部として含まれている仕様で、仕様書である "Managed Beans 1.0 Specification" は本文がたったの 11 ページというとてもあっさりとした仕様です。
重厚長大なイメージのある Java EE 仕様の中で、このあっさりさは際立っているように思えます。

Managed Bean とは?

Managed Bean は Java EE アプリケーションサーバーのコンテナによって管理される JavaBean です。もう少し具体的に言うと EJB のスーパーセットです。
コンテナの管理下に入ることにより、次のような機能がサポートされます。

  • リソースのインジェクション
  • ライフサイクルに応じたコールバックメソッド
  • インターセプタの適用

EJB と異なり、次のような機能はサポートされません。

作り方

Managed Bean の作り方は簡単です。POJO に @javax.annotation.ManagedBean アノテーションを付与するだけで OK です。
POJO でいいのですが、次のような制約があります。

  • final ではない
  • 抽象クラスではない
  • 内部クラスの場合、static なクラスであること (ネストクラスであること)

Serializable であることは強制されません。
JSFアノテーション (@javax.faces.bean.ManagedBean) を間違えて付けてしまわないように注意しましょう。

@ManagedBean("myManagedBean")
public class MyManagedBean {
    @Resources
    private OtherResource otherResource;
    /**
     * コンポーネント初期化後に呼び出されるメソッド
     */
    @PostConstruce
    public void setUp() {
    }
    /**
     * コンポーネント破棄前に呼び出されるメソッド
     */
    @PreDestroy
    public void cleanup() {
    }
}

@ManagedBean アノテーションの属性には JNDI ルックアップを行う際に使う名前を指定します。Managed Bean を利用するのに後述の @Resource アノテーションしか使わないのならば省略しても OK ですが、JNDI ルックアップを行う際には必ず記述する必要があります。

EJB と同様、@PostConstruct、@PreDestroy アノテーションを使うことができ、インスタンス生成直後、インスタンス破棄前に処理を挟み込むことが可能になります。
また、@Resource を使って他のコンテナ管理リソースをインジェクトすることもできます。
基本的な作り方は EJB と全く変わらないことが分かると思います。

使い方

Managed Bean の使い方は次の2通りです。

  1. @Resource アノテーションによるインジェクション
  2. JNDI ルックアップ

まず、@Resource アノテーションを使ったインジェクションはとても簡単です。インジェクトしたいフィールドなり setter メソッドなりに指定すれば OK です。

@Stateless
public class MyEJB {
    @Resource
    private MyManagedBean myManagedBean;
    ...
}

JNDI ルックアップを行う場合、ルックアップに使用する名前空間は次のようになります。

アプリケーションレベルの名前空間
java:app//
モジュール内での名前空間
java:module/

はデプロイしたアーカイブの名前 (WAR ファイルの名前など) になります。
は @ManagedBean アノテーションに指定した属性になります。EJBCDI と違って、デフォルトでは名前が生成されないので注意してください。
モジュールをまたいで利用することは少ないでしょうから、通常は後者の名前空間を使って取得することになるでしょう。

    InitialContext ic = new InitialContext();
    MyManagedbean myManagedBean = (MyManagedbean) ic.lookup("java:module/myManagedBean");

使いどころ

コンテナ管理オブジェクトにして、コンテナ提供の機能 (特にリソースインジェクション) を使いたいけど、EJB を使うまでもない、という場面で使うことになるでしょうか。
要はトランザクション境界にする必要のないコンポーネントの場合に使うといったところですかね。
以前自分が開発した JAX-RS アプリケーションでは、次のコンポーネントを Managed Bean にしました。

  • Repository クラス (DAO クラス)
  • JAX-RS のリソースクラス

そして、トランザクション境界となる Service コンポーネントを Stateless Session Bean としています。

...と書きましたが、それだったら CDI 使ったら良いんじゃないのー、という突っ込みが入りそうです。いや全くその通りだと思います。

Java EE 6 の仕様策定時、CDI はぎりぎりまで EE 6 に入るかどうか微妙で、Managed Bean はそんな中産まれた、微妙な立ち位置の仕様に見えます。

今年の JavaOne で、Java EE の将来を考えるというパネルディスカッションを聴いたのですが、そこでもこれからは CDI に寄せた方がいいんじゃないの?という雰囲気でした。
なので、今後は CDI を使うようにした方がいいように思えます。個人的には Managed Bean のシンプルな仕様は結構気に入っているんですけどね。

分からないこと

Managed Bean のインスタンス管理方式については仕様では明確になっていません。
アプリケーションサーバーの実装に任されており、恐らく Singleton Session Bean と同じ (1つしかインスタンスを作らない) 方式になっているものと思われますが、実際どうなんでしょう?
SLSB のようにインスタンスプールを作っている実装もあったりするのでしょうか?

というわけで、Managed Bean という今後日の目を見ることはないであろう、かわいそうな仕様についての紹介でしたw

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

ListViewやTableViewのセルをカスタマイズする方法 (JavaFX Advent Calendar2013 7日目)

このエントリは JavaFX Advent Calendar 2013 の 7 日目のエントリです。前日は蓮沼 (@) さんによる、「e(fx)clipseで作るJavaFXアプリケーション」でした。

はじめに

Twitter やブログなどで JavaFX に関するエントリを見ていると、ListViewTableView のセルの表示をカスタマイズする方法がよく分からずに苦戦している人をよく見かけます。
このようなリスト系コンポーネントの表示をカスタマイズする方法は、実はどの GUI ツールキットでも大体やり方が似ているのですが、HTML を使った UI の開発ばかりをやっている人には馴染みがないかも知れません。
そこで、少し地味ではありますがこの方法について解説します。また、セルのカスタマイズをする際には性能面で注意が必要なポイントがあるので、それについても触れたいと思います。
なお、ListView も TableView も基本的には同じような方法でセルのカスタマイズが行えるので、以降の説明は ListView に絞って行います。

ListView の仕組み

リストのセルの表示をカスタマイズする方法を理解するためには、まずは ListView の仕組みを知っておいた方がいいと思うので、まずはそこから説明します。
まずは次のようなシンプルなリストを見てみましょう。

このリストは 500 件のアイテムを表示しています。大量にアイテムがあるので、大半が隠れていて、スクロールを行う必要があります。
一見すると隠れている領域にもセルのインスタンスがあるかのように思えますね。
ところが、シーングラフの状態を確認することができる、Scenic View を使って見てみると...。

なんと表示されている範囲のインスタンスしか生成されていないのです! (ListCell インスタンスの数に注目してください)
ListView はこのように表示されている範囲のセルのインスタンスだけを作り、スクロールが発生するとセルの表示をスクロール位置に対応したデータに合わせて切り替えることで、あたかも隠れた範囲から新しいセルが入ってきたかのように見せかけているのです。
こうすることで、大量のデータを表示しても性能が落ちないようにしているのです。

セルのカスタマイズ方法

それではセルのカスタマイズ方法について説明しましょう。セルの表示をカスタマイズするには次の 2 つのことを行う必要があります。

  1. カスタムの Cell クラスを作り、表示したいレイアウトを実装する
  2. ListView に対して上で作ったカスタムの Cell クラスを使うように CellFactory を設定する

具体例を示しながら説明していきます。今、自分用にソーシャルブックマークサービスDiigo のビューアを JavaFX で作ろうと考えているのですが、それのブックマークリスト部分を先に作ってみることにします。
セルの表示を次のようにすることを考えてみます。

上から順にタイトル、コメント、タグを表示します。タグはハイパーリンクとして横に並べるようにします。

まずはリストのデータソースとなるモデルを用意します。次のような JavaFX 形式の JavaBean として作ります。

/**
 * ブックマークを表すモデル.
 */
public class BookmarkModel {
    private StringProperty title = new SimpleStringProperty();
    private StringProperty comment = new SimpleStringProperty();
    private ObservableList<String> tagList = FXCollections.observableArrayList();

    /**
     * コンストラクタ.
     * @param title タイトル
     * @param comment コメント
     * @param tagList タグのリスト
     */
    public BookmarkModel(String title, String comment, List<String> tagList) {
        this.title.set(title);
        this.comment.set(comment);
        this.tagList.addAll(tagList);
    }

    public StringProperty titleProperty() {
        return title;
    }

    public StringProperty commentProperty() {
        return comment;
    }

    public ObservableList<String> getTagList() {
        return tagList;
    }
}

このオブジェクトを ObservableList にくるんで ListView にデータとして渡すことになります。

次はカスタムの Cell クラスを作りましょう。

public class BookmarkCell extends ListCell<BookmarkModel> {
    private VBox cellContainer;
    private Text txtTitle;
    private Text txtComment;
    private HBox tagContainer;
    private boolean bound = false;

    public BookmarkCell() { // (1)
        initComponent();
        initStyle();
    }

    private void initStyle() {
        txtTitle.setFont(new Font("System Bold", 18.0));
    }

    private void initComponent() { // (2)
        cellContainer = new VBox(5);
        txtTitle = new Text();
        VBox.setVgrow(txtTitle, Priority.NEVER);
        txtComment = new Text();
        VBox.setVgrow(txtComment, Priority.ALWAYS);
        tagContainer = new HBox(); // (3)
        VBox.setVgrow(tagContainer, Priority.NEVER);
        cellContainer.getChildren().addAll(txtTitle, txtComment, tagContainer);
    }

    @Override
    protected void updateItem(BookmarkModel bookmarkModel, boolean empty) { // (4)
        super.updateItem(bookmarkModel, empty);
        if (!bound) { // (5)
            txtTitle.wrappingWidthProperty().bind(getListView().widthProperty().subtract(25));
            txtComment.wrappingWidthProperty().bind(getListView().widthProperty().subtract(25));
            bound = true;
        }
        if (empty) {
            setText(null);
            setGraphic(null);
        } else { // (6)
            txtTitle.setText(bookmarkModel.titleProperty().get());
            txtComment.setText(bookmarkModel.commentProperty().get());
            tagContainer.getChildren().clear();
            List<String> tags = bookmarkModel.getTagList();
            for (String tagName : tags) {
                tagContainer.getChildren().add(createTagLink(tagName));
            }
            setGraphic(cellContainer);
        }
    }

    private Hyperlink createTagLink(String tagName) {
        Hyperlink hyperlink = new Hyperlink(tagName);
        hyperlink.setTextFill(Color.BLUE);
        return hyperlink;
    }
}

カスタムの Cell は javafx.scene.control.Cell を継承して実装しますが、ListView 向けには javafx.scene.control.ListCell という基底クラスが用意されているので、通常はこれを継承して実装します。 (TableView の場合は javafx.scene.control.TableCell を継承します)
型パラーメータには ListView のデータソースとなるクラスを指定します。今回の場合、先ほど上で作った BookmakrModel を指定することになります。

カスタムの Cell では updateItem() メソッドをオーバーライドします。
このメソッドは初期表示やスクロール時など、セルの表示を更新する必要があるときにコールバックとして呼び出されます。
第1引数には、このセルが表示を担当することになったデータソース (この例では BookmarkModel のインスタンス) が渡されます。第2引数にはこのセルが表示するデータが空であるかが渡されます。(空の場合に true)
第1引数に渡されたデータを使って、セルの表示内容を更新することになります。Cell クラスは Label や Button のスーパークラスである javafx.scene.contorol.Labeled を継承しており、setText() メソッドを使ってテキストを、setGraphic() メソッドを使って任意のグラフィックを表示するために設定することができます。
よって、任意のコンテンツをセルに表示したい場合、表示する Node を作って、この updateItem() メソッド内において、setGraphic() メソッドの引数にその Node を渡せばいいわけです。

ここで1つ注意点があります。それは、updateItem() メソッド内での Node オブジェクトの生成は極力避ける、ということです。
updateItem() メソッドはスクロールを行った場合など、高頻度で呼び出される可能性があります。そのため、このメソッド内で Node オブジェクトの生成を行うと、その負荷はかなりのものとなります。
従って、可能な限り Node オブジェクトの生成は Cell クラスの初期化時に行い、updateItem() メソッドではプロパティやレイアウトの変更にとどめるようにします。

以上を踏まえて今回の実装例について解説します。
まず、Cell クラスのコンストラクタで初期化可能な Node のインスタンスを全て作っています。(1)
実際に Node の生成を行っているのは initComponent() メソッドです。(2)
セルでは上からブックマークタイトル、コメント、そしてタグのリストを並べるのですが、これは VBox を作って配置します。
ブックマークタイトル、コメントの表示には javafx.scene.text.Text を使います。
タグについては BookmarkModel が保持するタグの数に応じて動的に変化するので、これは updateItem() メソッドで生成するようにしていますが、そのタグを水平に並べるための HBox だけをここでは生成して、コンテナとなる VBox に追加しています。(3)

続いて updateItem() メソッドの実装です。(4)
まず、ブックマークタイトルやコメントのテキストがセルの幅に合わせて折り返されるように、親となる ListView の width プロパティと、それぞれの wrappingWidth プロパティをバインドしています。(5)
コンストラクタの中で行わず、最初に updateItem() メソッドが呼び出されたときにこのバインドを実行している理由は、コンストラクタが呼び出される時点ではまだセルが配置される ListView と結びつけられていないからです。
そして、第2引数が false の場合に、引数として渡される BookMarkModel オブジェクトから値を取り出して、Text や HyperLink オブジェクトに設定しています。(6)
今回は少し手を抜いて、updateItem() メソッドの中で HyperLink を生成していますが、さらに性能を重視するのなら、コンストラクタである程度の数のインスタンスを作ってキャッシュしておいた方が良いでしょうね。

それでは、このカスタムのセルを使ってみることにします。まずは FXML から。

<BorderPane id="BorderPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="460.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="aoetk.SampleListViewController">
  <center>
    <ListView fx:id="listView" prefHeight="200.0" prefWidth="200.0" />
  </center>
</BorderPane>

BorderPane の真ん中に ListView を貼っているだけです。続いて Controller の実装です。

public class SampleListViewController implements Initializable {
    public ListView<BookmarkModel> listView;

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        listView.setCellFactory(new Callback<ListView<BookmarkModel>, ListCell<BookmarkModel>>() { // (1)
            @Override
            public ListCell<BookmarkModel> call(ListView<BookmarkModel> listView) {
                return new BookmarkCell();
            }
        });
        ObservableList<BookmarkModel> bookmarkModels = createBookmarkModels();
        listView.setItems(bookmarkModels); // (2)
    }

    private ObservableList<BookmarkModel> createBookmarkModels() { // (3)
        final String longComment = "長いコメントのサンプルです。長いコメントのサンプルです。長いコメントのサンプルです。"
                + "長いコメントのサンプルです。長いコメントのサンプルです。長いコメントのサンプルです。長いコメントのサンプルです。"
                + "長いコメントのサンプルです。長いコメントのサンプルです。長いコメントのサンプルです。長いコメントのサンプルです。";
        final String normalComment = "普通の長さのコメント。";
        ObservableList<BookmarkModel> bookmarkModels = FXCollections.observableArrayList();
        for (int i = 0; i < 500; i++) {
            String comment = "";
            if (i % 3 == 0) {
                comment = longComment;
            } else if (i % 3 == 1) {
                comment = normalComment;
            }
            BookmarkModel model = new BookmarkModel(
                    "ブックマークタイトル" + i, comment, Arrays.asList("tag1", "tag2", "tag3"));
            bookmarkModels.add(model);
        }
        return bookmarkModels;
    }
}

initialize() メソッドで ListView にセルの設定を行っています。ListView#setCellFactory() メソッドを使い、セルを生成する CellFactory を設定しています。(1)
CellFactory は、javafx.util.Callback インターフェースを実装したクラスとなります。call() メソッドで、使いたい Cell クラスのインスタンスを返すようにします。
後はデータの設定です。createBookmarkModels() メソッドにて、500個の BookmarkModel のインスタンスを生成して (3) 、そのリストを ListView のアイテムとして設定しています。(2)

これを実行すると次のようにカスタマイズしたセルを使った ListView が表示されます。

いかがでしたでしょうか。ちょっと回りくどいと感じる方もいるかもしれません。ですが、このような作りになっていることでモデルとビューが綺麗に分離できますし、リストの項目数が多くても描画性能へ与える影響を小さくすることができるというメリットがあります。
また上でも述べたように、JavaFX に限らず、GUI アプリケーションではリスト系コンポーネントの表示をカスタマイズしようとしたら、大体これと同じような方法を採ります。なので他の GUI プラットフォームで開発するときにも同じ考え方が通用するので、是非この機会に覚えてもらえたらと。

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

追記

ソースコード全体は gist にアップしています。ご参照ください。
https://gist.github.com/aoetk/7827455

JavaOne参加レポート (9/26)

JavaOne 最終日となる 5 日目のレポートです。この日のセッションは前日同様夕方までで、午前中はコミュニティセッションでした。
前日のイベントの疲れもあったのか、コミュニティキーノートはちょっと遅れての到着でした。(> <)
そして夜には伝統の蟹パーティにも参加しました!

この日に参加したセッションは次の通りです。

Java Community Keynote [KEY11027]

最終日はコミュニティキーノートから始まりました。前述したように、ちょっと送れての到着となりました。
最初は組み込み向けプロセッサ開発で有名な freescale 社によるスポンサーセッションでした。
やはり内容は IoT についてです。エッジノード (センサーとか電源とか) 、短距離接続 (Wi-Fi とか Bluetooth とか) 、ゲートウェイ、WAN、クラウドといったレイヤ分けを行い、それぞれのレイヤにおいてソフトウェアに求められることの違いみたいなことを説明していました。
また、組み込み開発における Java ME の C++ に対する優位性 (クロスプラットフォームロバストな点が優位) 、Java ME を中核とした開発のプラットフォームを作ったことなどを話していました。
事例として紹介した、ヘルスケアでのデータ解析の話がちょっと目を引きました。各種医療機器からのレコードをゲートウェイ (Home Health Hub と呼んでいました) を通して集め、それを OEP (Oracle Event Processing) という Oracle の CEP (Complex Event Processing) エンジンを通して分析するというものです。OEP for Java Embedded というものがあるようで、組み込み向け CEP があるということに驚きました。

続いてコミュニティキーノートです。
Raspberry Pi チャレンジの話があり、そこで Oracle が何と Raspberry Pi と OEM 契約したことが発表されました。今後は最初から Java が入ったイメージが出てくるようです。
また、Square が OpenJDK に参加するというニュースもありました。CDI の仕様策定過程で物議を醸した Crazy Bob こと Bob Lee さんの写真が出てきて驚きました。

そこから話の内容が教育に移り、Devoxx 4 Kids の様子がビデオ紹介されました。続いて 10 歳の Minecraft ハッカー、Aditya Gupta くん (Java EE の世界で有名な Arun Gupta さんの息子さんのようです) が登場し、壇上で Eclipse を広げて Minecraft をハックするデモンストレーションを行い、大いに盛り上がりました!
今後、ソフトウェアの重要性は上がる一方なので、確かに子供の教育は重要になってきますね。最近は普通の人が触るコンピューターはプログラミングの可能な PC から、プログラミング環境のないタブレットスマートフォンといったデバイスに移ってきているだけに、なおのこと重要になっていると思います。

次に安全、環境、宇宙、金融という 4 つの分野における Java の関わりについての話になりました。
安全ではロボットや自動運転の話が挙がり、Java for LEGO Mindstorms の紹介や、Java での自動車自動運転のビデオが流されたりしました。
環境では省エネの話になり、家庭における電力消費状況やそれに関するイベントの可視化をする様子が紹介されました。キーワードとして HadoopHibernate が登場していました。日本でもスマートメータースマートグリッドの話題が良く出てくるようになりましたし、この分野はどんどんホットになっていくでしょうね。
宇宙では初日の NetBeans のセッションでも見た、NetBeans + JavaFX で作ったアプリが登場しました。3D で太陽風を測定する衛星のフォーメーションを管理している様子を紹介していました。Geertjan Wielenga さんのブログでも良く紹介されるのですが、NetBeans プラットフォームは科学研究分野でよく使われている印象があります。
金融ではゴールドマンサックスの人が登場し、GS では 3000 人の開発者いて、コードは 1 億行に達し、22PB のストレージ、63K の CPU を使っていることを話していました。恐るべしです。そして、JCP にも OpenJDK にも参加しているよ、とアピールしていました。あちらではユーザー企業側が自ら開発しているのですよねえ。金融分野はもはやシステムが心臓となっているので、ある意味当然なのですが、日本のメガバンクも今後 JCP に参加するくらいのことをするのだろうか?

最後に予想はしていましたが Gosling さんも登場しました。初日の NetBeans のセッションで話したことと全く同じ内容の発表を行っていました。(^^;;

あ、そうそう。寺田さんが T シャツ投げデビューしてました。(^^)

nuvos: The Universal SDK―Write in Java; Run on IOS, Android, HTML5, and JVM [CON10821]

IntraMeta 社の nuvos という Java によるリッチアプリケーション開発プラットフォームの紹介を行うセッションでした。
本来は別のセッションを入れていたのですが、キャンセルになったので代わりにこれを聞くことにしました。

nuvos は Java でアプリケーションを作ったら、そこから iOSAndroid といったスマートデバイス向けアプリケーション、デスクトップ向けアプリケーションを生成するという製品です。
スマートデバイス向けには GWT ベースの JavaScript アプリケーションを、デスクトップ向けには JavaFX アプリケーションを生成するようです。
なかなかすごい製品だとは思いますが、プレゼンテーションの流れがとても退屈で、正直ちょっと眠かったです...。題材が良くても説明の仕方が下手だと台無しになることがよく分かりました。

デモンストレーションは面白かったです。nuvos を用いて出力したアプリケーションが、HTML5 バージョンも JavaFX バージョンも見た目、操作性がほぼ同じで驚きました。製品としてはとても面白いなと思いました。

Moving from Swing to JavaFX [CON7852]

Dassault Systems 社による、Swing アプリケーションを JavaFX に移植した事例について紹介するセッションです。Dassault Systems 社は航空機とかで有名なフランスの Dassult のシステム子会社です。

Enginuity という調剤の設計を行うためのソフトウェアを JavaFX に移行する話をしてくれました。
ビデオ機能やマルチタッチ機能が必要で、JavaFX を利用することにしたとのことです。アプリケーションの国際化やデザインのカスタマイズを行えるようにできるかが懸念事項だったとのことです。
以前のバージョンと同じ動作をすること、ビジネス価値に寄与する開発ができること、開発したものの資産化ができること、自動テストができること、といった点を移行に当たって重視したとのことでした。

JavaFX の利点として、HTML5 アプリケーションと組み合わせられること、ビルトインコンポーネントが豊富であること、次に示すような Swing の課題を解決していた点を挙げていました。

  • JTable のカスタマイズが難しい
  • エフェクトの実装が難しい
  • UI とデータモデルをバインディングする仕組みがない
  • L&F のカスタムが難しい
    • Nimbus L&F には package protected なコードがある!

JavaFX に変更するに当たってのチャレンジングな点としては、Swing とコンポーネントモデル、スレッドモデル、イベント、バインディングなどが異なる点を挙げていました。

続いて移植の内容についての説明になりました。
先述したようにリグレッションは許されないので、リグレッションテストスイートを作ったとのことです。
JavaFXアーキテクチャiOS に似ているため (FXML と Controller の関係が iOS の Screen と ViewController の関係に似ている) 、iOS での開発スタイルを参考にしたようです。
Controller はプレゼンテーション技術から切り離すようにし、画面間の共通に使われるプレゼンテーションロジックは Component Delegate を作ってそこに委譲するスタイルと取っています。Component Delegate はバインド等のエラーをハンドルし、ビジュアルフィードバックを返せるように作られています。データは JavaFXバインディングの仕組みを利用しています。

全部を一度に移植するのはリスキーなので、手始めに簡単なプロパティエディタから移植したとのことです。リグレッションテストスイートをまず作成し、そのテストが通ったら徐々に JavaFX ならではの機能追加を行っていきます。そこから徐々に移植対象を増やしていきました。
さすがに業務系のソフトウェア開発であるため、とても堅実なアプローチでやっていますね!

苦労した点として、Swing に WebView を貼り付けたときにスクロールバーが表示されなかった問題 (WebView を先に Scene に貼り付けてから JFXPanel に Scene を追加することで解決)、ファイルチューザーの見た目が変わったこと *1 、自動テストツールJavaFX を認識しなかったことを挙げていました。
また、Scene Builder について、これは開発者向けツールなので別途デザイナ向けツールが必要なこと、プロパティエディタの強化が必要なこと、プラグイン機構が欲しいことをリクエストとして挙げていました。

Swing から JavaFX への移植に限らず、システム移行のやり方としてもとても参考になるセッションでした。

Be Creative and Create Your Own JavaFX 8 Controls [CON2494]

いつもかっこいい JavaFX カスタムコントロールを色々作って、Harmonic Code というブログに投稿している、Gerrit Grunwald による JavaFX 8 でのカスタムコントロールの作成方法についてのセッションです。

JavaFX はスタイリングに CSS を使えるのが特徴的ですが、JavaFX 8 からは自分で作ったコンポーネントに独自の CSS プロパティを用意できるようになります。疑似クラスも定義できます。
CSS の属性を定義するには、コントロールにおいて、プロパティを StyleableProperty インターフェースを実装したプロパティクラスで定義します。
実装クラスでは getCssMetaData() と getControleCssMetaData() というメソッドを実装するのですが、このメソッドCSS エンジンが拾うそうです。
CSS 属性に対して疑似クラスを用意したい場合は、BooleanPropertyBase を実装したプロパティを用意し (そのプロパティの名称が CSS での疑似クラス名になります) 、invalidated() メソッドの中でスタイルの変更を行います。
CSS を自分でカスタムできるようになりたければ、JavaFX 8 の標準テーマである Modena の CSS コードを理解することが大事だとのことでした。読むのは大変だけど、その価値はあるとのことです。

カスタムコントロールを作るには、Control クラスを継承したクラスを作り、さらに対となる Skin クラスを用意します。
MVC アーキテクチャに照らし合わせると、Control が Model の、Skin が View と Controller の役割を担います。
つまり、Control にはカスタムコントロールの各種プロパティを保持し、Skin が見た目とユーザーインタラクションに対する応答を提供します。
よって、Skin クラスではスタイルとイベントリスナを登録します。スタイルは CSS で作ります。CSS の属性には SVG の記述も渡せるので、それでシェイプを作ることができます。
「Swing よりずっと楽になっているよー」とのことでした。 *2

ただ、この方法は本当に汎用的なコントロールを作りたい場合の話で、もう少し簡略化した作り方もできるとして、以下の 3 種類の方法を紹介していました。

  1. Canvas で作る方法
  2. Region を継承する方法
  3. Control + Skin + CSS で作る方法

いずれの方法でも初期サイズ、初期に表示するグラフィック、リスナ登録、プロパティのハンドリング、リサイズについての考慮が必要となります。
3 つ目は上で述べた方法です。
1 つ目の方法は Region を継承したクラスを作り、その中に Canvas を保持して、直接ドローイングする方法です。一番ストレートな方法ですね。サイズに対してリスナを登録しておき、変化があったら描き直します。ユーザーインタラクションがない場合はこの方法で充分ですが、CSS でスタイリングできないという欠点があります。
2 つめの方法は、やはり Region を継承するのですが、Region 内にさらに別の Shape やコントロールを配置します。要は既存のコンポーネントを組み合わせるという方法ですね。この方法の場合は CSS を使うことができます。大抵の場合はこのやり方で充分とのことです。私もカスタムコントロールを作るときは基本的にこの方法で作っています。

最後に、カスタムコントロールを作るのに便利なツールとして、以下のツールを紹介していました。

Caspian Styler
FX Experience Tools に含まれている、CSS 設定を確認できるツール
Scenic View
シーングラフの状態を確認できるツール
Inkscape or Illustrator or Fireworks
説明不要ですね
FXG Converter
Gerrit さんが開発した、Adobe FXG ファイルを JavaFX コードにコンバートするツール

Java EE 8 and Beyond [CON2406]

このセッションはパネルディスカッション形式のセッションでした。基調講演でも Java EE パートの説明をした Cameron Purdy さんや金魚本で有名な Antonio Goncalves さんをはじめ、IBMRed Hatアプリケーションサーバーの開発に携わっている人達がパネリストになっていました。

Java EE 8 はまだ具体的な動きだしがほとんどないようで、Java EE 8 はどうあるべきかをこの場で語り合おう、という内容になっていました。
正直私の英語力では聞き取りが結構厳しかったのです。

やはり話題になっていたのは "DevOps" でした。この言葉がバズっていたのは日本だけじゃなかったんですねえ。EE 7 では結局見送られた PaaS 対応に俄然期待が掛かるという感じですね。
つまり、モジュラリティの向上や、DevOps 向けの API の充実とかしていきたいねえ、という話をしていました。
(毎回 SLF4J の設定とかいちいちやりたくないよね?みたいなことを話してましたねえ)
他にもアノテーションをもっとシンプルに、新しい技術にどんどん追従して欲しいという声が挙がっていました。

EJB もやはり話題に挙がりました。CDI の登場により、Java EE における EJB の位置付けが微妙になってきています。アノテーションについて、CDI 側に寄せた方が良いのか、従来の EJBアノテーションを残した方が良いのか、を聴講者に聞いていましたが、前者に手を上げる人が多かったですね。

そして Spring はどうしても話題に挙がりますね。ことあるごとに「それ Spirng でも」な突っ込みが入っていましたw
他には「Oracle ってちゃんと JavaJava EE で儲かっているの?」とか (Fusion MiddlewareWebLogic でちゃんと儲けてるよ、との回答) 、「JSF どうするよ?」とか (Single Page Web App の開発では効いてくるのでは?という話が) 、Java EE にも HTML5 みたいなロゴが必要なんじゃない? という話が出てきたりしました。
まあ、何というか全般的にまったりした感じで話し合ってましたねえ。

Java EE 7 がリリースされたばかりなので、EE 8 についてはまだ何をするのかほとんど決まっていないような感じです。なので、今はみんなで色々リクエストを出すと良いのかも知れませんね。


以上、初めて参加した JavaOne のセッションレポートでした。ハードでしたが非常に中身の濃い 5 日間を過ごすことができました。
今回初めて知ったのですが、JavaOne って AppleWWDC より遙かに規模が大きいのですね。やはり Java のコミュニティの大きさは最大規模であることがよく分かりました。
セッションの内容もここでしか聞けないことが多いのももちろんですが、この大きな Java コミュニティの雰囲気を体感できることや、Java の仕様策定に携わっている人達の話が直接聞けること (その人達の人柄を見ることができたこと) も大きかったです。いやー、本当に行けてよかった。

*1:JavaFX はネイティブのファイルチューザーを表示します。

*2:ちなみに Gerrit さんは Swing でもかっこいいコントロールを色々作っています

JavaOne参加レポート (9/25)

JavaOne 4 日目となる 9/25 のレポートです。JJUGJavaOne 報告会の準備でこちらの作業を後回しにしてしまい、こんなに遅い公開となりました。ごめんなさい。
この日に参加したセッションは次の通りです。この日は夜に Oracle Open World と一緒に開催される Appreciatition Event があったので、夕方まででセッションが終わりました。

  • In-Database Container for Hadoop: When MapReduce Meets RDBMS [CON9240]
  • Java 8 Streams: Lambda in Top Gear [CON7942]
  • Java 8 Collections and Concurrency [CON7962]
  • Optimizing JavaFX Applications [CON3141]
  • Top 10 Web Application Defenses for Java Developers [CON5523]
  • The State of Java Web Container Security [CON8016]

JavaFXJava SE ではとても濃い内容の話を聞けました。また、Hadoop という JavaOne ではちょっと異色のセッションにも参加しています。

In-Database Container for Hadoop: When MapReduce Meets RDBMS [CON9240]

Hadoop についてのセッションです。Oracle では何と HadoopOracle のコンテナ上で動かしてしまおうという試みを行っているようで、そのアーキテクチャ、利用方法についてデモを交えて解説するというものでした。

Hadoop を利用する上で大きな課題がデータの移動です。多くの場合、Hadoop を使って解析したいデータは何らかの別のストレージ (多くはデータベース) に置かれています。
これを Hadoop (HDFS) 側に持ってくる必要がありますが、何せ対象は「ビッグデータ」。移動するだけでも大変ですが、セキュリティ、データの整合性などにも気をつかう必要があり、相当な運用スキルが要求されます。

そこで Oracle が考えたのは、いっそのこと Oracle の上で Hadoop を動かしちゃおう!というものです。
Oracle DB は JVM も動かしていますが、この上で Apache Hadoop を動かします。Oracle の Parallel Query エンジンがデータのパーティショニングやジョブのスケジューリングを行うというアーキテクチャです。
データストレージとしては通常のファイルの他に Oracle 上のテーブルやビューも対象とします。Oracle のテーブルやビューを読み書きするために専用の TableReader、TableWriter クラスを用意しています。ダイレクトに Oracle のテーブル上のデータを読み書きできるので、わざわざデータを移動する必要がないということになります。

MapReduce ジョブの実行方法についてですが、Mapper と Reducer は通常の Hadoop の開発と同じように作ります。この MapReduce ジョブは Java でドライバクラスを作って実行する方法 (通常の Hadoop におけるジョブ実行方法) と、SQL から実行する方法があります。
Java で記述する場合、oracle.sql.hadoop パッケージにある Job クラスを使って、ジョブの実行設定を記述します。Job#setMapOutputKeyDBType("VARCHAR2(10)") とか記述しますw Job#run() の引数に読み込み元のテーブルと出力先のテーブルを指定します。
Java で作ったジョブは Oracle に登録し、Java ストアドプロシージャとして実行することになります。
SQL の場合、SELCT * FROM TABLE (HREDUCE_JP_WORDCOUNT(:RedConf, ... (HMAP_JP_WORDCOUNT(:MapConf, ... という感じで、Map ジョブや Reduce ジョブの結果をテーブルに見立てて SQL を記述します。うまく書けば SQL でデータのパイプラインを組み立てられることになります。
これもやはりストアドプロシージャとして登録して実行することになります。

実際にデモも行ってもらいました。MapReduce を使ってアクセス数を集計するというものです。SQL* Plus から MapReduce ジョブを実行する様はなかなかシュールでしたw
MapReduce タスクが読み込みやすいような形のビューを作って実行させていました。

なかなかすごいのですが、現時点ではプロトタイプのみで製品にはなっていないようです。ですがデータ移動に困っている人は結構多いはずですので、製品化を期待して待っています。
でも高そう。製品化するとなると恐らく Big Data Appliance や Exadata といったアプライアンス製品に載るんでしょうね。

なお、先日行われたJavaOne 2013 サンフランシスコ報告会 TokyoでもLTでこのセッションについてのお話しをしました。当日の発表資料を以下に示しておくのでご参考までに。

JavaOne2013報告会 LT資料 Hadoopの話を聞いてきた from Takashi Aoe

Java 8 Streams: Lambda in Top Gear [CON7942]

Brian Goetzさんによる、Stream についてみっちり解説するセッションです。大変人気のあるセッションで、立ち見が発生していました。

Java SE 8 になって導入される Lambda 式ですが、その Lambda 式の導入で最も大きな変化があるのがコレクションの操作です。Stream の導入により、Java でもデータ操作を宣言的に行えるようになります。

Stream はデータセットに対する処理の抽象化です。データ構造ではありません。
無限リストを取り扱うことができ、処理は UNIX のコマンドのようにパイプラインをつなげるような形で処理を行います。
Stream のパイプラインはソース、0かそれ以上の中間操作、終端操作で構成されます。基本的に中間操作を行うメソッドパイプラインを組み立てるだけで、可能な限り遅延処理されます。パイプラインの実行を行うのは終端操作となります。

Stream<Txn> s1 = txns.stream();                                          // ソース
Stream<Txn> s2 = s1.filter(t -> t.getBuyer().getState().equals(“NY”)); // 中間操作
IntStream   s3 = s2.mapToInt(Txn::getPrice);                             // 中間操作
int         sum = s3.sum();                                              // 終端操作

ソースの生成方法は色々あります。通常はコレクションの stream() メソッドを呼び出して作りますが、IntStream.range() や Files.walk() みたいなのもあります。
このソースによって SIZED、ORDERED、DISTINCT、SORTED といった Stream の性質が決まります。例えば ArrayList だったら SIZED と ORDERED という性質を持ちます。
また、ソースによって Decomposition (ばらしやすさ) が異なり、並列処理に影響します。例えば LinkedList はばらせないので、並列処理の効果がありません。
中間操作の処理は先ほども述べたように遅延されます。また、中間操作はパイプラインの性質に影響を与えます。例えば map() は SIZED を保持しますが、DISTINCT や SORTED の性質は消えます。
そして終端操作がパイプラインを実行することになります。この処理がシーケンシャルもしくはパラレルに実行されることになります。

また注意点として、パイプラインは一度きりの実行で分岐や再利用はできないこと、実行中はソースをいじらないこと、処理はステートレスに行う必要があることを挙げていました。
最後の点についてですが、例えば次のように forEach() の中で外部のコレクションに値を追加するようなことは NG です。こうするとスレッドセーフでなくなり、並列化できなくなります。

List<Person> sellers = new ArrayList<>();
txns.map(Txn::getSeller).forEach(s -> sellers.add(s));

次のように畳み込み処理のために用意されたメソッドを使うようにします。

txn.map(Txn::getSeller).collect(Collectors.toList());

並列処理を行う場合は stream() メソッドの代わりに parallelStream() メソッドを使います。当然のことながら並列化してもしなくても結果が同じになるようにしなければいけません。 *1
また、タスクごとに互いの結果に触れることもできません。
Java SE 7 で導入された Fork/Join フレームワークをベースに作られている *2 ので、分割統治法で処理されます。

最後に並列化の性能について触れていましたが、データセットのサイズを N 、パイプライン処理のコストを Q として、N * Q が高い値になるほど並列化の効果が得られやすいとのことでした。
実データの比較結果を見せてもらいましたが、だいたいレコード数が 10,000 を超えたあたりで逆転していました。

とても濃い内容のセッションでした。仕様策定に関わった人から直接こういう濃い内容の話を聞けるというのはいいですねえ。

Java 8 Collections and Concurrency [CON7962]

Java SE 8 におけるクラスライブラリの細かい改善について解説するセッションです。具体的には JSR-335、JEP-142、155、171、180 についての解説です。

JEP-142 は @Contended というアノテーションを追加し、False Sharing 問題を解決するというものです。
Java でフィールドを複数持つクラスを定義すると、それぞれのフィールドが連続したメモリアドレスに配置されますが、マルチコアの場合、共有していないデータを CPU キャッシュ上の同一ラインで共有してしまうという、False Sharing 問題を発生させることがあります。 *3
そこで新たに加わった @Contended アノテーションを片方のフィールドに付与すると、別々のラインに配置してくれるようになるとのことです。

JEP-155 は並行処理に関する小さなアップデートを集めたものです。以下に概要を列挙します。

  • 複数のスレッドから更新され、参照頻度が少ない場合に従来の AtomicLong や AtomicDouble などの代わりに使うことのできる、LongAccumulator や LongAdder といったクラスが追加された。
    • Accumulator は汎用的な更新に使う。
    • Adder には increment() や add()、sum() といった演算メソッドが用意されている。
  • ConcurrentHashMap が強化されている。大きな要素数になった場合のスキャン性能が強化され、バルク操作もできるようになる。よりキャッシュ向きになった。
  • ForkJoin の改善。多数のタスクをコミットしたときのスループットが向上している。
  • ForkJoin 共有プール。Java SE 8 からは明示的に ForkJoinPool を作らなくても、デフォルトで ForkJoinTask の共有プールが用意されている。
  • ReadWriteLock に対する StampedLock というものが新たに追加される。これはいわゆる楽観ロック。
    • StampedLock#tryOptimistcRead() して stamp をとり、StampedLock#vaidate() に渡す、という使い方をする。
  • CompletionStage というインターフェースが加わる (実装クラスとして CompletableFuture がある) 。これは Deferred オブジェクトに相当するもの。
    • stage.thenApply(Lambda 式).thenAccept(Lambda 式).thenRun(Lambda 式) という風に書ける。

JEP-171 はメモリ操作の順序性を保証する機能 (Fence) を追加するというもののようで、sun.misc.Unsafe クラスに読み込み、書き込み、読み書き両方のそれぞれに相当する loadFence()、storeFence()、fullFence() というメソッドが追加されるとのことです。
でも sun パッケージに入っているので表には出てこないということですね。

JEP-180 は HashMap の改善に関するもので、これまではキーのハッシュの衝突が発生した場合、LinkedList に格納していましたが (O(n) オーダーの検索になる) 、8 ではエントリが Comparable の場合に限って、B ツリーに格納するように改善されたようです。
もっとも、良いハッシュアルゴリズムを使うことが一番大事だよと話していました。まあそうですねえ。

JSR-335 はコレクションの拡張です。次のような改善が行われています。

  • sort() にデフォルト実装が入る。
  • コアクラスの実装がより最適化されている。
  • map に computeIfAbsent() が入る!
    • これは個人的に嬉しかったです。次のようなことができます。
map.computeIfAbsent(key, k -> new ArrayList()).add(elm)

このような感じで、細かいアップデートについてカバーするというセッションでした。こういう細かい情報は案外得られなかったりするので、とってもありがたかったです。

Optimizing JavaFX Applications [CON3141]

JavaFXレンダリング処理の詳細について解説するという実にディープな内容のセッションでした。

JavaFX は主に 2 つのスレッドが使われています。1 つは FX アプリケーションスレッド (以下 FX スレッド) 。こちらはアプリケーションの入力イベントや後述の pulse イベントを処理するスレッドです。もう 1 つはレンダースレッドで、こちらはシーングラフの状態をレンダリングする処理のために使われます。

そして pulse についての説明です。pulse とは 16ms おきにタイマーで起動されるイベントで、シーングラフの状態を調べて、どうペイントすべきかを決定する処理を行います。タイマーチェックの際に次の条件を満たしていれば実行されます。

  • アニメーションが実行中である
  • シーン上で変更が発生している (dirty scenes が存在する、という言い方をしていました)
  • 明示的に pulse の実行が要求された
  • 前の pulse がまだ実行中ではない

pulse の中では、アニメーションの実行 -> CSS の評価 -> レイアウトの評価 -> コンポーネント境界の更新 -> (前回の pulse の結果発生した) レンダリング完了の待機 -> Scene 上の Node の同期 -> RenderJob の作成、の順で処理が行われます。そして、最後の処理で作った RenderJob をレンダースレッドに渡して、レンダースレッドに描画を行ってもらいます。
なお、JavaFX 2.x では FX スレッドとレンダースレッドの並行性に問題があり、レンダースレッドでの RenderJob の実行が完了しなければ次の pulse が実行されないようになっていました。JavaFX 8 では pulse の途中でレンダリング完了を待つフェーズを挟み込むようにし、より並行性を高めているとのことです。

続いて、ハードウェアアクセラレーションが効いている場合とそうでない場合の違いについての説明に入りました。
Windows では Direct3D を使いますが、その場合、 pulse で作られた RenderJob はレンダースレッドに渡され、レンダースレッドにて paint して D3DSwapChain#present() をコールします。
これに対し、ソフトウェアレンダリングになった場合は、レンダースレッドで paint した後、ピクセルをバッファにコピーして FX スレッドに戻してピクセルを更新します。つまり、D3D の場合と違って描画処理の一部が FX スレッドを使うことになります。
Mac の場合は OpenGL を使いますが、レンダースレッドで paint 後、 FX スレッドに戻して、FX スレッドで OpenGL のコンテキストを使ってテクスチャを描画します。
また、JFXPanel を使って Swing 上に JavaFX のシーングラフを置いた場合、Java2D を使ってシーングラフを描画することになります。この場合はレンダースレッドが AWT の Component#repaint() をコールして、AWT に描画させます。

WebView については、WebKit に対して代理のタイマーを渡しているそうです。このタイマーは常に空のアニメーションを実行しており、JavaScript によるアニメーションをハンドルします。
また、Web ページ上でダーティリージョンが発生した場合、レンダリングキューを作って処理をレンダースレッドに渡しているとのことです。

このような JavaFX の動きについて説明した上で、パフォーマンスの計測方法についての解説を行いました。
PerformanceTracker というクラスが com.sun.javafx.perf パッケージにあります。このクラスの getSceneTracker() メソッドの引数に Scene を渡してインスタンスを作ります。このクラスには getInstantFPS() や getAverageFPS() といった性能測定に必要なメソッドが用意されています。
setOnPulse() や setOnRenderedFrameTask() といった、処理フェーズの合間に処理を挟み込めるようなメソッドも用意されています。
また pulse の状態を見るには PulseLogger というものがあり、-Djavafx.pulseLogger=true を設定することで出力されるようになります。pulse の処理情報、実行時間、カウンタなどが出力されます。

最後に JavaFX ランタイムの挙動を変更するいくつかのオプションを紹介していました。

  • Glass Robot。com.sun.glass.ui.Robot クラスがそれで、JavaFX 専用の Robot クラス。JemmyFX が使用している。
  • フルスピードモードというものがある。-Djavafx.animation.fullspeed=true を設定すると有効化され、pulse の間の待ち時間がなくなる。
    • つまり 60FPS 以上を出すことが可能になります。使うとしたらベンチマークの時くらいでしょうね。
  • -Dquantums.norenderjobs=true を設定するとシングルスレッドモードになる。
  • GLTrace という OpenGL/EGL API の呼び出し状況をトレースするツールがあり、Linux 版及び MacOSX 版がある。
    • openjfx/rt/gltrace にソースがある。

最後に JavaFX におけるプロファイリングについてのアドバイスがあり、JavaFX は多くの処理を GPU に行わせており、また CPU を使う処理も大半は JavaFX ランタイムが処理を行っているため、CPU プロファイリングは余り効果的ではないとのことでした。
このセッションで紹介した PulseLogger などのツールを活用すべし、ということでしょうね。

いや、実に濃いセッションでした。こういう話を聞くと、JavaOne に来て良かったなあと思いますね。

Top 10 Web Application Defenses for Java Developers [CON5523]

OWASP のメンバーの Jim Manico さんによる、Web アプリケーションのセキュリティ対策についてのセッションです。
この方、とにかくノリのいい、楽しい方で、すさまじいマシンガントークで、ノリノリになってまくし立てていました。それを聞いているだけで楽しかったです。

SQL インジェクション、パスワード保存、XSS、HTML サニタイズCSRF、暗号化、クリックジャッキング、アクセスコントロールなどについて、防御方法を順に説明していました。
それらの説明に割と共通していたのが、「下手に自分で作り込むよりも、過去の知見が集積された OSS ライブラリを活用するように」という点でした。これはなるほどと感心しました。以下、概要を列挙します。

  • SQL インジェクションの防止はパラメータ化。Web アプリケーションがクラックされるトップの原因は「';」をテキストボックスに入力されること。
  • パスワードの保存についての注意点。
    • パスワードは絶対に文字種を限定しないこと!
    • 暗号化する場合、キーはクレデンシャルとは別のストアに置くこと。
    • ハッシュ生成は別サービスで行う。
    • ソルトを付けること。
  • XSS の対策はもちろんエスケープだけど、エスケープは色んな種類があって大変。そこで OWASP Java Encoder Project がお勧め。コンテキスト別に必要な関数を提供しており、パフォーマンスもとても良い。
  • HTML のサニタイズは、最近だとリッチテキストエディタを用意する必要もあったりして、一概に全てサニタイズできない。そこで OWASP HTML Sanitizer Project がお勧め。許可するタグだけを設定してサニタイズができるようになる。
  • CSRF 対策。
    • 最近だとホームルーターCSRF 脆弱性を突いてパスワードを変更するといった事例も出てきている。
    • トークンを入れるのが基本的かつ強力な対策。ただし、XSS 対策がきちんと行われていることが前提!
    • Amazon のように、再認証プロセスを入れるのも良い。
  • クリックジャッキング。
    • iframe を透明化して、別 Web サイトを被せて誤操作させる手口。
    • X-FRAME-OPTIONS に SAMEORIGIN を設定して対策する。
  • アクセスコントロール。
    • 自分で認証認可のロジックを作り込むと、バグがあったときに大変。
    • Apache Shiro のようなライブラリを活用しよう。
  • Certificate Pinning (証明書のピン留め) 。

このような感じで、結構いいまとめでした。なによりセッション全体が楽しかったですねえ。あんな風に楽しいプレゼンができるようになりたいなあ。

The State of Java Web Container Security [CON8016]

これはパネルディスカッションでした。今後 Java EE コンテナに対して、どのようなセキュリティ面での対策が加わるようになるといいのか、について議論するものです。
正直こういうフリーディスカッションの形式だと、自分の英語力では少し厳しかったです。(>_<)
次のような話が話題に挙がりました。

  • CSRF 対策。
    • Tomcat は対策が入っている。
    • タグを書き込む形式がいい?それともコンポーネントを用意?
    • トークンの生成頻度はどれが適当?
      • セッション単位?毎回?
      • Tomcat はレベルを設定出来るよね。
    • 一律に対策を入れちゃうとブックマークができなくちゃっちゃうよね。
  • HTTP パラメータ汚染。
    • 今のサーブレットでは URL のクエリパラメータが form パラメータに優先されるという問題がある。
      • どちらも HttpServletRequest#getParameter() で取得するので。
    • 明確に GET と POST で取り出す場所を分けるべきでは? PHP みたいに。
  • セキュリティ制約。
    • 今は URL パターンのみだよね。
    • ブラウザに指示する HTTP ヘッダ (Strict-Transport=security とか X-Frame-Options とか) を扱えるようにすべきでは?
  • 認証認可。
    • Spring Security ライクに XML で外部定義したいなあ、という意見が。
      • (それに対し、「いや設定変えたらテストするからどっちみちコンパイルするだろ」という突っ込みがw)

私が聞き取れたのは大体こんなところです。(^^;;
確かにもう少し JavaEE レベルでこのあたりの対策は追加していって欲しいかな、とは思っています。

*1:deterministic でないといけない、という言い方をしますね。

*2:なので、使用スレッド数などの設定も同様であるとのことでした。

*3:当日セッションを聞いていたときは、この False Sharing 問題についてよく知らなかったのですが、ここの解説が分かり易かったです。

JavaOne報告会でJavaFXについての発表&LTを行ってきました

10/19 に実施されたJavaOne 2013 サンフランシスコ報告会 Tokyoにて、JavaFXのアップデートについての発表とLTを行ってきました。

当日のJavaFXアップデートの資料は以下の通りです。

JavaOne2013報告会 JavaFX Update from Takashi Aoe

JavaOne初参加の身でありながら、例年は櫻庭さんが担当されているポジションを引き継ぐことになったので、とても緊張しました。
自分の前に寺田さん、大山さん、櫻庭さんが発表がありました。3人の発表で場が大盛り上がりした状態で自分の発表に入ったのですが、自分の発表になると雰囲気がしーんとした感じになってしまったので、「うわ、これは失敗かなあ...」とショックを受けたのですが、後から聞いた話だと結構皆さんそれなりに楽しんで聞いて頂けたようで、ほっとしています。
そうは言ってもこのような大きな場で長丁場の発表をするのにはまだまだ修行が必要だと言うことがよーく分かりました。精進します。

本編のJavaFXアップデートに続いて、LTでもお話しをしてきました。こちらはJavaOneではちょっと珍しい感じのHadoopのセッションについてです。
JavaFXの方は真面目な感じで行ったので、こちらはちょっとくだけた感じにしました。
発表資料は次の通りです。

JavaOne2013報告会 LT資料 Hadoopの話を聞いてきた from Takashi Aoe

今のところSlideShareでのページビューはLTの方がちょっと上ですね。何だかんだ言ってHadoopは注目ネタですかね。

さて、ブログの方のJavaOne報告もまだ半分ほど残っていますね。こちらも早めにアップします。(^^;;