2015-12-31

Wayland と X Window System

Wayland は、X Window System の代替を目指している Linux 向けに開発されたディスプレイサーバーのプロトコルで、OpenGL ES ベースのグラフィックシステムです。なぜ、Linux 向けなのかというと、Wayland は Linux カーネル組み込みの DRM (Direct Rendering Manager) 1. や、高速な GPU メモリ管理機構の GEM (Graphics Execution Manager) 2. を利用しているためです。Wayland は Red Hat の Kristian Høgsberg 氏を中心に開発が進められており、MIT のライセンスに基づいて配布されています。

Wayland は X Window System よりもシンプルで柔軟性があり、今やどこにでもあるグラフィックス・アクセラレータ用ハードウェアをサポートしています。そのため、現在の X を上回る高速な描画性能が期待できます。

すでにいくつかの GUI ライブラリやウィンドウマネージャが Wayland をサポートしています。GNOME はバージョン 3.14、KDE はバージョン 4.11 からサポートされています。

参考サイト [3] によると、次の Fedora 24 では、GNOME on Wayland がデフォルトのウィンドウマネージャになる可能性に触れています。もっとも、オプションとしては、GNOME on Wayland は Fedora 21 から利用可能になっています 4.。当然、Fedora 23 でも利用可能です。

ノート PC にインストールしてある Fedora 23 で、デフォルトの GNOME から GNOME on Wayland に切り替えて少し使ってみたところ、画面の描画速度が目覚ましく速くなったという実感はありませんでしたが、デフォルトの GNOME とほぼ差異なく使用できます。

ただ以下の二点が気になりました。

マウス中ボタンによるペースト
昔は UNIX/X 用に 3 ボタンマウスが販売されていましたが、今ではほとんど見つけることができません。その代わりマウスホイールをクリックすることで、従来の中ボタンの代替ができていますが、この機能が Wayland 上では効いていません。もしかするとこのようなマウスイベントは Wayland がサポートする範囲外なのかもしれません。
外付けディスプレイとの切り替え
ノート PC を使っているときは、使っている最中に外付けディスプレイを接続したり外したりして画面を切り替えることがあるのですが、デフォルトの GNOME に較べて、制御ができなくなる不具合が生じる頻度が多いと感じました。残念ながら詳しくデータとして残せるような検証まではしていません。

なお、Raspberry Pi などでは、画面描画速度が Wayland によって明らかに改善されているという報告がされています 5.

Ubuntu を支援している Canonical も 2010 年に Wayland の採用を発表していましたが、その後、独自に Ubuntu 向けに Mir というディスプレイサーバを開発して、それを次世代の Unity で採用することに計画が変更されました 6.。Mir は GPLv3 に基づいて配布されます。

いろいろ似たようなプロジェクトが出てきて競争をもたらすことは結構なことですが、多様性を制限なしに許すと、開発者にとってコストの増加につながりますので、頭の痛いところです。

Linux ユーザーとしては、X から軽量のディスプレイサーバに置き換わって、グラフィックボードが持つ性能を十分享受できるようになれば、いずれが主流になっても構いません。しかし、そこまで期待するのであれば、オープンソースの成果を利用している一人として、何か貢献できることがないか考えなければ…。

X が現在のバージョン 11 (X11) になったのは 1987 年。それ以来、マイナーな改良を続けながら実に四半世紀以上も、Unix系オペレーティングシステム (OS) などでのグラフィカルユーザインタフェース (GUI) を構築するためのプロトコルとして利用され続けています。

参考サイト

  1. カーネル2.4の特徴は
  2. Graphics Execution Manager - Wikipedia
  3. Fedora 23 lands with GNOME 3.18, Wayland progress, and a new upgrade system | PCWorld
  4. GNOME on Wayland in Fedora 21 - Fedora Magazine
  5. Raspberry Pi Gains Graphics Speed as Wayland Replaces X | Linux.com
  6. Canonical、独自のディスプレイサーバーを開発することを発表 | スラド Linux
  7. Wayland

2015-12-30

【備忘録】「公開」ディレクトリの使い方

Fedora をノート PC にインストール後、Gnome をデスクトップ環境に使うようになりました。今までデスクトップ用 PC では LXDEMate を使ってきており、Gnome のデスクトップ環境にあまり慣れ親しんでいませんでした。

Gnome では、ホームディレクトリにデフォルトで作成されるディレクトリに「公開」があるのですが、このディレクトリの使い方が判らなかったので、今更ながら調べたことを備忘録とします。

まず、「設定」画面から「共有」を選択します。

「共有」のダイアログで「パーソナルファイル共有」を選択して、オンにします。必要に応じてパスワードを設定します。

「共有」のダイアログの「パーソナルファイル共有」がアクティブに変わります。

設定は以上です。

ネットワーク上にある別の Linux PC で Nautilus を起動、左側の「他の場所」を選択すると、先ほどアクティブにした共有ファイル(ディレクトリ)が表示されるので、これをクリックします。この例では、bitwalk's public files on notepc と表示されているアイコンです。

すると、同名 (bitwalk's public files on notepc) のネットワークディレクトリのアイコンが表示されますので、それをクリックすると、ディレクトリの内容が表示されます。

出来てしまえばなんていうことはありません。ネットワーク内で複数の PC を使っている時には重宝しそうな機能です。

なお、これは Gnome においてファイルアクセス抽象化機能を提供している GVFSWebDAV クライアント機能を利用しているようですので、Windows からも適当なツールを使えばアクセスできそうです。Windows からのアクセスについては、また別の機会に紹介します。その前に、WebDAV について知識が足りないので、勉強する必要があります。


2015-12-29

XMind 7 for Fedora 23

XMindは、マインドマップを作成できるソフトウェアです。下記サイトから Linux 版がダウンロードできますが、パッケージは deb パッケージのみで、残念ながら rpm パッケージは提供されていません。

昨年も年末に同じような記事を書きましたが、XMind のパッケージがバージョンアップされているので、今年も deb パッケージから rpm パッケージに変換しました。

使用した環境は次の通りです。

  • OS: Fedora 23 (x86_64)
  • JDK: jdk1.8.0_66-1.8.0_66-fcs.x86_64 (Oracle)
  • alien: alien-8.90-5.fc23.noarch
  • rpm-build: rpm-build-4.13.0-0.rc1.7.fc23.x86_64

alien を用いて、dep から rpm への変換は問題なく出来ますが、相変わらずインストール時にエラーが出てインストールできません。そこで、昨年の記事(下記 [1])と同じ方法で rpm を作成しました。

今回は、下記 [2] へ作成した rpm パッケージをアップロードしておきました。xmind-3.6.0-2.x86_64.rpm をダウンロードしてお使いください。

  1. bitWalk's: 【備忘録】XMind と Fedora 21
  2. XMind RPM Wiki - OSDN

rpm パッケージについては、alien が生成した spec ファイルに最低限の修正を加えただけでビルドしています。個人的には、いろいろと手を加えたいところですが、キリがないので止めました。

$ rpm -qi xmind
Name        : xmind
Version     : 3.6.0
Release     : 2
Architecture: x86_64
Install Date: 2015年12月28日 21時07分16秒
Group       : Converted/non-free/editors
Size        : 134188775
License     : see /usr/share/doc/xmind/copyright
Signature   : (none)
Source RPM  : xmind-3.6.0-2.src.rpm
Build Date  : 2015年12月28日 21時03分48秒
Build Host  : notepc
Relocations : (not relocatable)
Summary     : Professional & Powerful Mind Mapping Software
Description :
XMind, is an open source brainstorming and mind mapping software tool
developed by XMind Ltd. It helps people to capture ideas, organize to
various charts, and share them for collaboration. Supporting mind maps,
fishbone diagrams, tree diagrams, org-charts, logic charts, and even
matrix. Often used for knowledge management, meeting minutes, task
management, and GTD. XMind is compatible with Freemind/Mindmanager.
XMind is dual licensed under 2 open source licenses: the Eclipse Public
License v1.0 (EPL) and the GNU Lesser General Public License v3 (LGPL).
XMind Plus/Pro is released under the terms of the XMIND PROPRIETARY LICENSE
AGREEMENT, which is available at http://www.xmind.net/license/xpla/ .


(Converted from a deb package by alien version 8.90.)
$ 

実行例を以下に示しました。

2015-12-28

Mozilla、Firefox OSのスマートフォン事業から撤退へ | マイナビニュース

Apple 社の iPhone を今年 2 月に会社から支給されて以来、Firefox OS への興味が無くなってしまいました。とは言い過ぎですが、au から発売された Firefox OS 搭載のスマホ Fx0 を購入する動機が激減してしまったことは事実です。

この Firefox OS 搭載のスマートフォンを、携帯キャリア経由で提供することを取り止める計画だというニュースが 12 月 8 日(米国時間)に TechCrunch から報じられました 1.。その後、Mozilla からもアナウンスが出ています 2.

  1. Mozilla Will Stop Developing And Selling Firefox OS Smartphones | TechCrunch (2015/12/08)
  2. Firefox OS Pivot to Connected Devices | Mozilla Press Center (2015/12/09)
  3. Mozilla、Firefox OSのスマートフォン事業から撤退へ | マイナビニュース (2015/12/10)

スマホ用 OS であることを断念した Firefox OS の今後はどうなるのでしょうか。上記 2. によると、

We will focus on products and technologies that allow people to access and manage their world of connected devices, helping to ensure people are empowered, safe and independent.

と、注力対象をスマホからコネクテド・デバイスに変えるとあります。私はコネクテド・デバイスとはテレビなどの家電をイメージしてしまいますが、具体的には述べられていません。今後の動向に注目します。


2015-12-27

MD5 & SHA Checksum Utility

1970 年代後半に、月刊マイコン(電波新聞社)、月刊アスキー(アスキー)、I/O(工学社)、月刊 RAM(廣済堂)、Oh!PC(日本ソフトバンク)などのマイコン誌が次々と創刊され、ゲームを中心としたプログラムが毎月掲載されるようになりました。BASIC の実行速度の遅さを克服するため、機械語あるいはマシン語と当時呼ばれていた、CPU の命令セット(バイナリ)を16進数で表現した数字の列も多く掲載されていました。私はそれを修行僧の苦行であるかのように、ただひたすら手入力していたマイコン少年の一人です。その際にチェックサムには大変お世話になりました。

そのチェックサムは、一行のバイト列の個々のバイトの総計 (sum) の下位1バイトを、そのまま符号値とします。この方法は単純な加算であるため、各バイトを保持したまま順序のみ変化させた場合にも同じ値を示しますし、それ以外の誤りに対しても、符号値が同じになる確率は決して低くなく、誤り検出の方式としての信頼性は高くありませんでした。それでも、入力誤りの簡易的な確認のためには一定の成果をもたらしてくれたのでした。

今では、通常の PC を使用する限り、CPU の命令セットをプログラムを実行させるためにバイナリで手入力するという機会はなくなり、チェックサムを利用する場面は無くなりましたが、それでも、サイズの大きいファイルをダウンロードした際に、正しくダウンロードできたかどうかを検証する必要性は、依然として残っています。

単純なチェックサムは、誤り検出の信頼性が高くないので、現在ではもっぱら暗号学的ハッシュ関数が用いられます。符号化されたデータをメッセージ (message) と呼ぶことが多いので、メッセージのハッシュ値をメッセージダイジェスト (message digest) とも呼びます。広く使われている暗号学的ハッシュ関数には MD5SHA があります。

Linux であれば、大抵はデフォルトでインストールされている GNU Core Utilities (coreutils) パッケージで一般的な暗号学的ハッシュ関数を利用することが出来ます。例えば sha256sum は、SHA256 メッセージダイジェストの計算と照合をおこないます。SHA256 は、2001 年に米国家安全保障局 (NSA) が開発し、米国立標準技術研究所 (NIST) がハッシュ関数の国家標準の一つとして採用された、通称 SHA-2 と呼ばれる暗号学的ハッシュ関数のひとつです。

$ ls /usr/bin/sha256sum
/usr/bin/sha256sum
$ rpm -qf /usr/bin/sha256sum
coreutils-8.24-4.fc23.x86_64
$ 

前述の、サイズの大きいファイルのダウンロード例として、Linux などの OS のイメージが挙げられます。下記は Fedora 23 Workstation の ISO イメージをダウンロードして、検証ファイルと照合した例です。

$ sha256sum -c Fedora-Workstation-23-x86_64-CHECKSUM.txt
sha256sum: Fedora-Workstation-netinst-x86_64-23.iso: そのようなファイルやディレクトリはありません
Fedora-Workstation-netinst-x86_64-23.iso: オープンまたは読み込みに失敗しました
Fedora-Live-Workstation-x86_64-23-10.iso: 完了
sha256sum: 警告: 書式が不適切な行が 20 行あります
sha256sum: 警告: 一覧にある 1 個のファイルが読み込めませんでした
$ 

前置きが大変長くなりましたが、Windows 上で同じことをしようとすると、機能として備わっていないので、なにか良いフリーソフトがないか探しだすことになります。実は前の記事でその必要に迫られました。GUI が日本語対応していませんが、使いやすいツールを見つけましたので紹介します。ハッシュ値を入力しておけば照合してくれます。

使用例を下記に示しました。

2015-12-26

ASUS X200MA に Fedora 23 をインストール

ASUS X200MA は、今年の 2 月に廉価に惹かれて購入したノート PC ですが、Fedora 22 のリリース時にインストールをトライしたもののうまく行かず、代わりに Ubuntu 15.04 をインストールし、11 月には 16.04(開発版)にアップグレードして使っていました。

年末年始の休みに入り、時間の余裕ができたので Fedora 23 Workstation 版を試したところ、問題なくインストーラが起動してインストールが出来てしまいました。

業務で RHEL 用のインストールイメージを USB メモリで用意する必要があったので、Windows 7 上で Rufus を使って作成しましたが、ついでに Fedora 23 のイメージも焼いておき、それを利用しました。今後は、Fedora 上ですべて準備ができるようにしたいものです。

関連サイト

  1. bitWalk's: ASUS ノートブック (2015-02-11)
  2. bitWalk's: ASUS X200MA に Ubuntu 15.04 をインストール (2015-05-31)
  3. bitWalk's: Ubuntuのアップグレードのやり方 - Ubuntu入門 (2015-11-10)

2015-12-15

モノリス

Java 9 関連の記事を調べているとき、下記の文で気が付いてしまいました、「モノリシック」という表記が誤植でないことに。

Java 9では2つのことを解決しようとしている。1つはClaspath、もう1つは巨大なモノリシックとなったJDKだ。

そもそもモノシリックってどういう意味だったんだっけ、と思って調べてみたら、自分の間違いに気が付いたのでした。恥ずかしい話ですが、ずっと「モノシリック」と覚えていました。

monolith(一枚岩、石柱)の形容詞が monolithic、しっかり覚えておこう。

誤植は Claspath の方なんですけどね。

2015-12-06

Linux / UNIX: DNS Lookup Command

Linux をサーバに採用することは大いに賛成なのですが、職場では Windows を使いたがる人が多いので、そういうことを言うのは極めて少数派になってしまいます。しかし、事情によって Linux サーバを扱わなければならない時、そういう自分に出番が回ってきたりして、ちょっと嬉しい気分になります。

とは言え、社内テストができる都合の良い人間としてうまく使われているだけ、という気もしないではありませんが、まあ、あまり気にしないことにしています。

そんな折、新しいテストサーバが入ってきて、目も眩むような豪勢な仕様にクラクラしながらも嬉々として設定を始めました。社内の IT 部門に固定 IP アドレスを割り振ってもらったのですが、一緒に通知された DNS のひとつに見慣れないアドレスが含まれていました。DNS サーバは幾つもあるはずなので、まさか間違いではないとは思うが、自力で確認できたはず…。不覚にも思い出せなかったので調べてみたら、いいサイトがあったので、備忘録とします。

そうだ host コマンドで確認できたるんだったと、ちょっと唖然としました。

下記の例は、Ubuntu をインストールしてあるノート PC で google.com を調べた例です。

dig コマンドだと次のようになります。

そう言えば、思い出せなかったのは nslookup でした。

2015-11-12

更新 - フェドラをつかおう

11 月 3 日(米国時間)にリリースされた Fedora 23 をインストール(アップグレード)してみました。実をいうと、例年リリースが遅れているので、また 12 月ごろになるだろうと思っていたのですが、ふとしたことから、すでにリリースされていることを知り、慌ててアップグレードに取りかかりました。

従来は怪しいアップグレードの方法をとっていましたが、今回は通用しなかったので、下記のサイトに記載されているアップグレードの方法に沿っておこないました。その顛末を紹介します。

以下のように、アップグレードは問題なく進んだように見えました。

# dnf upgrade
...
(省略)
...
# echo $LANG
ja_JP.utf8
# export LANG=C ← 日本語のロケールでは、次の system-upgrade が実行できなかったための措置です。
# echo $LANG
C
# dnf system-upgrade download --releasever=23 --best
Fedora 23 - x86_64                              7.8 MB/s |  43 MB     00:05    
Copr repo for openjfx owned by gferon            95  B/s | 257  B     00:02    
RPM Fusion for Fedora 23 - Free - Updates       206 kB/s | 216 kB     00:01    
RPM Fusion for Fedora 23 - Nonfree - Updates     55 kB/s |  18 kB     00:00    
RPM Fusion for Fedora 23 - Free                 481 kB/s | 738 kB     00:01    
Fedora 23 - x86_64 - Updates                    5.1 MB/s | 5.6 MB     00:01    
RPM Fusion for Fedora 23 - Nonfree              207 kB/s | 218 kB     00:01    
Last metadata expiration check performed 0:00:00 ago on Wed Nov 11 21:10:11 2015.
Dependencies resolved.
================================================================================
 Package                        Arch   Version                    Repository
                                                                           Size
================================================================================
Installing:
 GeoIP                          x86_64 1.6.6-1.fc23               fedora  118 k
 GeoIP-GeoLite-data             noarch 2015.09-1.fc23             fedora  353 k
 ant-lib                        noarch 1.9.6-2.fc23               fedora  1.8 M
...
(省略)
...
 sos                            noarch 3.2-0.3.a.fc23             fedora  309 k
 sox                            x86_64 14.4.2-3.fc23              fedora  383 k

Transaction Summary
================================================================================
Install     115 Packages
Upgrade    3324 Packages
Remove        4 Packages
Downgrade    26 Packages

Total download size: 3.4 G
Is this ok [y/N]: y
Downloading Packages:
(1/3465): efivar-libs-0.21-1.fc23.x86_64.rpm    115 kB/s |  53 kB     00:00
...
(省略)
...
(3465/3465): zenity-3.18.1.1-1.fc23.x86_64.rpm  5.4 MB/s | 3.4 MB     00:00    
--------------------------------------------------------------------------------
Total                                           5.3 MB/s | 3.4 GB     10:56     
warning: /var/lib/dnf/system-upgrade/efivar-libs-0.21-1.fc23.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID 34ec9cba: NOKEY
Importing GPG key 0x34EC9CBA:
 Userid     : "Fedora (23) "
 Fingerprint: EF45 5106 80FB 0232 6B04 5AFB 3247 4CF8 34EC 9CBA
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-23-x86_64
Is this ok [y/N]: y
Key imported successfully
warning: /var/lib/dnf/system-upgrade/smplayer-14.9.0.6966-2.fc22.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID e051b67e: NOKEY
Importing GPG key 0xE051B67E:
 Userid     : "RPM Fusion free repository for Fedora (23) "
 Fingerprint: 1E0D 69F0 77CA 4960 C32C 17E2 5B03 78C0 E051 B67E
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-rpmfusion-free-fedora-23
Is this ok [y/N]: y
Key imported successfully
warning: /var/lib/dnf/system-upgrade/rpmfusion-nonfree-release-23-0.1.noarch.rpm: Header V4 RSA/SHA1 Signature, key ID 5ca6c469: NOKEY
Importing GPG key 0x5CA6C469:
 Userid     : "RPM Fusion nonfree repository for Fedora (23) "
 Fingerprint: 15B9 D223 C819 BF69 86A6 F203 D26D EA72 5CA6 C469
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-rpmfusion-nonfree-fedora-23
Is this ok [y/N]: y
Key imported successfully
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction

Removed:
  kernel.x86_64 4.1.10-200.fc22                                                 
  kernel-core.x86_64 4.1.10-200.fc22                                            
  kernel-modules.x86_64 4.1.10-200.fc22                                         
  kernel-modules-extra.x86_64 4.1.10-200.fc22                                   

Installed:
  GeoIP.x86_64 1.6.6-1.fc23                                                     
  GeoIP-GeoLite-data.noarch 2015.09-1.fc23                                      
  ant-lib.noarch 1.9.6-2.fc23                                                   
...
(省略)
...

Upgraded:
  Field3D.x86_64 1.6.1-7.fc23                                                   
  GConf2.x86_64 3.2.6-15.fc23                                                   
  GREYCstoration.x86_64 2.8-20.fc23                                             
...
(省略)
...

Downgraded:
  efivar-libs.x86_64 0.21-1.fc23                                                
  libkgapi.x86_64 2.2.0-4.fc23                                                  
  libnl3.x86_64 3.2.27-0.1.fc23                                                 
...
(省略)
...

Complete!
Download complete! Use 'dnf system-upgrade reboot' to start the upgrade.
The downloaded packages were saved in cache till the next successful transaction.
You can remove cached packages by executing 'dnf clean packages'.
# dnf system-upgrade reboot

しかし再起動後、「Starting system upgrade. This will take a while.」と表示されるところまで進まず勝手に再起動してします。ひょっとしてシステムのロケールがここでも関係しているのかも、とは思いましたが、あとの祭です。なお、本当にロケールが原因かどうかは確認できていません。

しかたがないので、他の PC を使って DVD に Fedora 23 の iso イメージを焼いて、クリーンインストールする羽目になりました。

関連サイト

  1. 「Fedora 23」がリリース--GNOME3.18採用 - ZDNet Japan
  2. 「GNOME 3.18」をサポートした「Fedora 23」登場 | OSDN Magazine

2015-11-10

Ubuntuのアップグレードのやり方 - Ubuntu入門

5 月に購入した手頃な価格のノート PC に Ubuntu 15.04 をインストールして評価も兼ねて使っていました。[記事:ASUS X200MA に Ubuntu 15.04 をインストール

Ubuntu のバージョン番号は、リリース時の年(下二桁)と月から決められているようですが、15.10 (Wily Werewolf) が先月リリースされたようなので、早速アップグレードをしてみようと、やり方を調べました。

下記のサイトに、アップグレードの方法が丁寧にまとめられていましたので、その内容に従ってアップグレードをしてみました。

アップグレード後に、リリース番号を調べてみると、あれれっ、なんと 16.04 (の開発版)になっていました。Ubuntu については使用経験が浅く、15.10 へのアップグレードを期待していたのですが要領がわかりません。とにかく、このバージョンは LTS (Long Term Support) 版のようで、2021 年 4 月までサポートされるようです。今から使いはじめるとなると 5 年以上使えることになります。そこまで使い続けるかどうかは別として、ちょっと得をした気分です。日本語入力などは、特に不自由がないので、しばらく使ってみることにします。

 

2015-11-06

【備忘録】文字列を解析して日付時刻オブジェクトを生成 - Java

Java で、とあるデータから取り出した "October 1, 2015 10:36:58 am" のような文字列を、日付時刻オブジェクトへ変換する際に、エラーなく parse するのに苦労しましたので、備忘録として残しておきます。

データから取り出した日時/時刻を表す文字列は、できるだけ元のままでオブジェクトへ変換したかったのですが、am/pm の部分だけは、日付時刻クラスの parse メソッドで、小文字が許容されなかったので、やむなく parse 直前で大文字に置き換えています。

動作を確認した環境は次の通りです。

  • OS: Fedora 22 (x86_64)
  • Java: Java SE 1.8.0_65 (jdk1.8.0_65-1.8.0_65-fcs.x86_64)
  • IDE: NetBeans IDE 8.0.2

サンプルプログラムを以下に示しました。

package datetimetest;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class DateTimeTest {

    public static void main(String[] args) {
        Locale.setDefault(Locale.ENGLISH);

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy   h:m:s a");
        String strDateTime = "October 1, 2015   10:36:58 am";

        LocalDateTime localDateTime = LocalDateTime.parse(strDateTime.replace("am", "AM").replace("pm", "PM"), formatter);
        System.out.println(localDateTime);
    }
}

実行例

NetBeans 上で実行した例を下記に示します。

run:
2015-10-01T10:36:58
ビルド成功(合計時間: 0秒)

参考サイト

  1. Parse date | Level Up Lunch
  2. java - displaying AM and PM in small letter after date formatting - Stack Overflow
  3. Java日付時刻フォーマッターメモ(Hishidama's Java8 DateTimeFormatter Memo)

 

2015-10-30

【備忘録】zip 形式で圧縮されたテキストファイルを行単位で読み込む - Java

zip 形式で圧縮されたテキストファイルを行単位で読み込むメソッドです。できることは判っていても、スクラッチから作ろうとすると、あれこれ試行錯誤するのが目に見えています。参考サイトをブックマークしておき、必要な時にそこを参照すれば良いのですが、どこにブックマークしたかを忘れることも多く、結局インターネットで探すことになります。ところが、この手の情報は豊富にあるが故に、それぞれのコーディングの仕方の違いに悩んだりして、それはそれで勉強にはなるものの、効率を落とす原因になります。

そういうわけで、自分の書き方で最低限必要な処理を用意して、それを備忘録としました。そのため、圧縮されているファイルやフォルダの個数チェックやなど、気の利いたこと一切していません。テキストファイルをそのままzip ファイルにしてあるというのが前提です。

動作を確認した環境は次の通りです。

  • OS: Fedora 22 (x86_64)
  • Java: Java SE 1.8.0_65-b17
  • IDE: NetBeans IDE 8.0.2
    void readZipFile(ZipFile zipFile) throws IOException {
        Enumeration<? extends ZipEntry> entries = zipFile.entries();
        ZipEntry entry = entries.nextElement();

        InputStream in = zipFile.getInputStream(entry);
        InputStreamReader sr = new InputStreamReader(in, "UTF-8");

        try (BufferedReader br = new BufferedReader(sr)) {
            String line = null;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        }
    }

参考サイト

  1. How to read files within a zip file - Java Tips

 

2015-10-26

Dialog とウィンドウを閉じるときのイベント処理 - 改

以前、Dialog とウィンドウを閉じるときのイベント処理 で紹介した記事ですが、確認用ダイアログのウィンドウ左上にアイコンを表示させるように少し変更しました。適当な PNG 画像を question.png というファイル名で、DialogSample.java と同じディレクトリに保存しておいてください。

Java SE 8 Update 40 より、JavaFX に DIalog が導入されたということですので、遅ればせながら Dialog のサンプルを紹介します。ウィンドウの右上の X をクリックしてウィンドウを閉じようとしたときに、確認用のダイアログを表示させるサンプルです。

動作環境は次の通りです。

  • OS: Fedora 22 (x86_64)
  • Java: Java SE 1.8.0_65-b17
  • IDE: NetBeans IDE 8.0.2
リスト:DialogSample.java 
package dialogsample;

import java.util.Optional;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.image.Image;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

public class DialogSample extends Application {

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setOnCloseRequest((WindowEvent t) -> {
            abortAction(t);
        });

        Button btn = new Button();
        btn.setText("Say 'Hello World'");
        btn.setOnAction((ActionEvent event) -> {
            System.out.println("Hello World!");
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void abortAction(WindowEvent t) {
        Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
        alert.setTitle("確認");
        alert.setHeaderText(null);
        alert.setContentText("本当に終了しますか?");

        Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
        stage.getIcons().add(new Image(this.getClass().getResource("question.png").toString()));

        Optional<ButtonType> result = alert.showAndWait();
        if (result.get() == ButtonType.OK) {
            System.exit(0);
        } else {
            t.consume();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }

}

実行例を以下に示します。

参考サイト

  1. JavaFX 8u40 のダイアログを探る - Programming Studio
  2. JavaFX Dialogs (official) | code.makery.ch
  3. Dialog (JavaFX 8)
  4. javafx - How do you set the icon of a Dialog control Java FX/Java 8 - Stack Overflow

 

2015-10-21

Oracle、Java SE 8 Update 65 (8u65) 公開  2015年10月CPU - コトハノオト

Java SE 8u65 (CPU, クリティカルパッチアップデート)がリリースされました。

オラクルからお知らせメール (Oracle Critical Patch Update for October 2015) が来ていたのに気付かず、会社のパソコンで Java のアプリケーションを起動した時にアップデートのメッセージが出て気がつきました。

追記

今回リリースされたのは以下の二種類です。

  • 8u65:4半期毎のCritical Patch Update(CPU)
  • 8u66:CPUに加え、複数の不具合を修正する累積パッチを含むPatch Set Update(PSU)

2015-10-19

シェルスクリプトマガジン 30

POSIX 原理主義の逆襲という特集記事が目に入り、シェルスクリプトマガジン Vol. 30 (10 October 2015)(税込 540 円)を丸善で衝動買いしました。

最近はすっかり Amazon.jp のお世話になりっ放しで、書店をゆっくり見て回る機会が少なくなってしまいました。なにかの用事で書店に立ち寄った時、面白そうな雑誌に偶然出会うことがあります。今日はまさにそんな日でした。

さて、本誌の特集記事「POSIX 原理主義の逆襲」は、高汎用性で長寿命なアプリケーションを開発するにはどうすればよいか。それは、仕様が長年に亙って緩やかにしか変化せず、10 年、20 年のような長期間で通用する POSIX の仕様を厳密に守ったシェルスクリプトによるプログラミングにこそ答えがあると述べています。「原理主義」とはなかなか強烈な表現ですが、メンテナンスをほとんどする必要がなく、長期間安心して利用できるソフトウェアを作るためには、POSIX のような国際規格に従うことは確かに理にかなったやり方ではあります。

2020 年の東京オリンピックに向けて、東京メトロの経路を表示するような公共性の高いアプリを準備することを例に挙げ、如何にメンテナンスフリーのソフトウェアを作ることが重要であるかを筆者は問うていますが、それは全くその通りだと思います。

私の頭が悪いせいか、何度も読みかえして理解する必要がありました。何回も読みかえしたせいでしょうか、校正不足による誤植も目につきましたが、それでも、POISX で用意されている貧弱なコマンド群を補うため、有志により足りない機能を補強していく活動が紹介されており、シェルスクリプティングの可能性を十分に感じさせてくれる読み物でした。なお、「シェルスクリプトは書き捨て言語」なんて言われていたとは、知りませんでした。

参考サイト

  1. The Open Group Base Specifications Issue 7, 2013 Edition
  2. UEC - usp engineers' community - usp Tukubai

2015-10-17

JavaFX: NotePad 〜 ドラッグ&ドロップ

NotePad はメモ帳プログラムです。

デスクトップ用アプリケーションとしては陳腐ですが、そういった小さなアプリケーションを JavaFX で作って紹介していきたいと思い、少しずつ作り始めています。今回はその第三弾です[参考サイトのリンク]。

前述したようにいわゆるメモ帳プログラムですが、まだ全然できておらず、必要最低限の機能すら実装されていません。ただ、最初にドラッグ&ドロップの機能を試したくて、取り敢えず、その部分が出来た時点で紹介をしてしまいました。NotePad には実装してみたい機能がありますので、それらが実装できた度に紹介していきたいと思います。

開発環境は以下の通りです。

  • OS: Fedora 22 x86_64
  • jdk1.8.0_60-1.8.0_60-fcs.x86_64 (Oracle)
  • IDE: NetBeans IDE 8.0.2

実行例を以下に示しました。

ファイルの拡張子が txt あるいは log のファイルのアイコンを、ファイルマネージャ(MATE の Caja を使用しています)から NotePad の TextArea の上へドラッグしてドロップ(マウスの左ボタンを離す)すると、読み込みます。

参考サイト

  1. NotePad

【追記】GitHub では、本名で登録しています。


2015-09-19

JavaFX: Clock 〜 アナログ時計

Clock は アナログ時計のエミュレータプログラムです

デスクトップ用アプリケーションとしては陳腐ですが、そういった小さなアプリケーションを JavaFX で作って紹介していきたいと思い、少しずつ作り始めていますが。今回はその第二弾です[参考サイトのリンク]。今回は、jewelsea 氏が GitHub Gist で公開している Sample of an animated clock in JavaFXフォークして編集しました。

開発環境は以下の通りです。

  • OS: Fedora 22 x86_64
  • jdk1.8.0_60-1.8.0_60-fcs.x86_64 (Oracle)
  • IDE: NetBeans IDE 8.0.2

実行例を以下に示しました。

参考サイト

  1. Clock in JavaFX

【追記】GitHub では、本名で登録しています。


2015-09-14

Fedora 用 openjfx パッケージ

Fedora 用の OpenJFX のパッケージが公開されているサイトを見つけました(下記)。

今までは、Fedora で配布される OpenJDK のパッケージには OpenJFX (JavaFX) のパッケージが含まれていないので、JavaFX を使いたいがために、Java は Oracle が配布している JDK のパッケージを使用していました。早速、/etc/yum.repos.d 内にレポジトリファイルを作成して java-1.8.0-openjfx をインストールしましたが、残念ながら java-1.8.0-openjdk とアップデート番号が合わず、利用できませんでした。アップデート番号が更新されるかもしれませんので、暫く様子を見ることにします。

ちなみに、OpenJFX が OpenJDK と一緒に配布されない理由は、一部のプロリエタリなソフトウェアのためのようです。下記に詳細がありますが、この問題は解消されたのでしょうか?詳しいことが判れば報告します。


2015-09-12

JavaFX: Calc - Simple Calculator

Calc はシンプルな電卓エミュレータプログラムです。

デスクトップ用アプリケーションとしては陳腐ですが、そういった小さなアプリケーションを JavaFX で作って紹介していきたいと思っています。今回はその第一弾です。目標は、最新の Java の実行環境さえあれば、Linux をはじめ、Windows や MacOS 上でも動作するマルチプラットフォームのアプリケーションを作成することです。願わくば、可読性が高く、極力シンプルなソースで記述することを目指しています。

配布するアプリケーションとしては未完成ですが、最低限の機能は実装できたので、バージョン 0.2 として、下記の参考資料のサイト(GitHub へのリンク)に公開しました。

なお、開発環境は以下の通りです。

  • OS: Fedora 22 x86_64
  • jdk1.8.0_60-1.8.0_60-fcs.x86_64 (Oracle)
  • IDE: NetBeans IDE 8.0.2

実行例を以下に示しました。

参考サイト

  1. bitwalk123/Calc [release]

【追記】GitHub では、本名で登録しています。


2015-08-08

記者の眼 - Windows 10が話題の今、Linuxをお薦めする7つの理由:ITpro

Linux はきっと今後、Windows と匹敵するほどのポピュラーな OS になるに違いないと、Linux を使い始めた当時は思っていました。

「当時」とは昔の話になってしまいますが Windows 95 が発売された 1995 年前後です。ちょうど Tcl/Tk を盛んに使い始めていた時期で、会社のワークステーションであった SunOS/Solaris と(同じではないものの)同じような環境を、Linux で極めて廉価に自宅の非力な PC 上で構築できたことがかなりの衝撃だったのです。もともと Windows 上で Visual C++ などのいろいろなコンパイラ環境を買い求めることに金銭的に疲れ、もっと手軽にプログラミングを楽しめる環境が欲しかったことが Linux を使い始めた動機でした。当時はデュアルブートで Windows と Linux の両方を使っていたので、結局 Visual Studio 6.0 までは買い続けましたが、その後は Linux を中心としたプログラミング環境へ完全に移行しました。今では、自宅で新旧合わせて三台の PC が稼働していますが全て Linux です。

一方、世の中は、20 年前に自分が思ったこととは異なり、デスクトップやノート PC 市場で Linux は普及していません1.。それどころか、コンシューマ向け PC そのものの市場が縮小しているようです2.。家電量販店の、いわゆる PC コーナーは勢いが無いばかりか、店内の売場面積も縮小されているようにさえ見えます。

そんな中で、久しぶりに Linux を薦める記事を読みました。

この記事を読んで、多くの人や企業が PC に求めることはなんだろうと考えさせられました。

自宅で使うのであれば、きっと、家電量販店あるいはネットショップから手ごろな PC を選んで、自宅に設置してすぐ使えるものであればそれで十分なのだと思います。それが Windows PC なのです。企業にしても、デスクトップ用途には、サポートあるいは苦情先がはっきりしている、納入業者が薦める Windows PC を購入しているのが大半でしょう。Windows サーバの Active Directory 下で企業のネットワークを構築してしまえば、そこそこ動いてしまうだろうし、サポートも契約次第でバッチリです。自分の経験では、昔々は電子メールを含むインターネット関係のサーバーは UNIX 系 OS でカバーしていましたので、そういう管理をする UNIX のシステム管理者がいましたが、今では、Windows の知識だけで管理できてしまいます。Microsoft 社がここまで Windows を社会に普及させたことは偉業なのでしょう。

だから、特に個人で使用する場合、OS を気にする人以外は、一番簡単な方法を選べばよいのでしょう。PC が古くなって速度が気になれば新しい PC に買い替えるべきでしょう。Linux だって、PC の性能が上がればそれだけ力を発揮します。古い PC でも Linux だったら十分使えるのかもしれませんが、果たして使う人はそれで満足するのでしょうか。

Windows PC が非力で、少しでも効率的に動かしたいと考えていた時代は、Linux はソリューションのひとつになりえました。しかし今や 4G, 8G のメモリ搭載は当たり前になり、必要があればもっと搭載することも可能な時代になりました。会社のチームでリモート接続で利用できる解析用の Windows PC に搭載されているメモリが 128GB だと最近知りびっくりしました。これだけ搭載されていれば、そうそうリソース不足には悩まされることはないでしょう。CPU も強力ですので、Windows とか Linux とかもはや関係なさそうです。これはまだ極端な例なのかもしれませんが、OS に何を使うべきか、なんて議論は、もう時代遅れになってしまったのかもしれません。

そう、使いたければ、好きな OS を選べばよいのです。サーバ分野で Linux は活躍しているから、Linux ファンとしては良しとしないと…3.

参考サイト

  1. Operating system market share
  2. タブレット端末、今年度にノートPCと出荷台数が逆転へ | RBB TODAY(2015年6月22日)
  3. Usage share of operating systems - Wikipedia, the free encyclopedia

2015-08-02

【備忘録】Tomcat のインストールと起動

Apache Tomcat は、Java Servlet や JavaServer Pages (JSP) を実行するための Web コンテナです。

Fedora 22 において、Apache HTTP Server の方は、パッケージ (httpd-2.4.16-1.fc22.x86_64) をインストールしてサービスを起動すれば、とにかく Web サーバーが起動しましたが、tomcat (tomcat-7.0.59-4.fc22.noarch) の方はそうはいきませんでした。調べてみたら下記にぴったりのサイトがありましたので試してみました。

# dnf install tomcat-webapps tomcat-admin-webapps
Last metadata expiration check performed 1:05:53 ago on Sun Aug  2 13:28:37 2015.
Dependencies resolved.
================================================================================
 Package                   アーキテクチャ
                                       バージョン             リポジトリー
                                                                           容量
================================================================================
インストールしています:
 tomcat-admin-webapps      noarch      1:7.0.59-4.fc22        fedora       44 k
 tomcat-webapps            noarch      1:7.0.59-4.fc22        fedora      359 k

トランザクションの要約
================================================================================
インストール  2 Packages

総ダウンロード容量: 403 k
インストール済み容量: 1.2 M
これでいいですか? [y/N]y
パッケージをダウンロードしています:
(1/2): tomcat-admin-webapps-7.0.59-4.fc22.noarc 361 kB/s |  44 kB     00:00    
(2/2): tomcat-webapps-7.0.59-4.fc22.noarch.rpm  1.8 MB/s | 359 kB     00:00    
--------------------------------------------------------------------------------
合計                                            148 kB/s | 403 kB     00:02     
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  インストールしています: tomcat-admin-webapps-1:7.0.59-4.fc22.noarch       1/2 
  インストールしています: tomcat-webapps-1:7.0.59-4.fc22.noarch             2/2 
  Verifying             : tomcat-webapps-1:7.0.59-4.fc22.noarch             1/2 
  Verifying             : tomcat-admin-webapps-1:7.0.59-4.fc22.noarch       2/2 

インストール:
  tomcat-admin-webapps.noarch 1:7.0.59-4.fc22                                   
  tomcat-webapps.noarch 1:7.0.59-4.fc22                                         

完了しました!
# systemctl enable tomcat.service
Created symlink from /etc/systemd/system/multi-user.target.wants/tomcat.service to /usr/lib/systemd/system/tomcat.service.
# systemctl start tomcat.service
# ls -l /usr/share/tomcat/
合計 4
drwxr-xr-x. 2 root root   4096  4月 25 09:55 bin
lrwxrwxrwx. 1 root tomcat   11  3月 20 18:29 conf -> /etc/tomcat
lrwxrwxrwx. 1 root tomcat   22  3月 20 18:29 lib -> /usr/share/java/tomcat
lrwxrwxrwx. 1 root tomcat   15  3月 20 18:29 logs -> /var/log/tomcat
lrwxrwxrwx. 1 root tomcat   22  3月 20 18:29 temp -> /var/cache/tomcat/temp
lrwxrwxrwx. 1 root tomcat   23  3月 20 18:29 webapps -> /var/lib/tomcat/webapps
lrwxrwxrwx. 1 root tomcat   22  3月 20 18:29 work -> /var/cache/tomcat/work
# lsof -i :8080
COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
java    7086 tomcat   48u  IPv6 206231      0t0  TCP *:webcache (LISTEN)
#

念の為、ブラウザで localhost:8080 を見てみると確かにサービスが起動していることがわかります。


2015-07-20

JavaFX: チャートのカスタマイズ (2)

JavaFX のチャートを使いこなせるようになりたいと、いくつかチャートのサンプルを紹介してきましたが、前回は、LineChart クラスを継承した MyChart1 を紹介しました。今回はさらに話を進めて、XYChart から継承したサンプルを紹介します。

と言っても自分自身のオリジナルサンプルではなく、Oracle の JavaFX のサンプル(参考サイト [1])にある CandleStickeChart クラス(右図)です。

なお Candlestick Chart は、日本語ではローソク足チャートと呼ばれていて、株価などの相場の値動きを時系列に沿った図表を表す時の手法の一つです。

動作環境

動作環境は次の通りです。

  • OS: Fedora 22 x86_64
  • jdk1.8.0_51-1.8.0_51-fcs.x86_64 (Oracle)
  • IDE: NetBeans IDE 8.0.2

配布ライセンス

なお、今回紹介するサンプルは、少し手を加えてはあるものの、Oracle が配布しているサンプルほぼそのままです。この記事の全てのサンプルの配布は以下のライセンスに従っています。

Copyright (c) 2008, 2013 Oracle and/or its affiliates.
All rights reserved. Use is subject to license terms.

This file is available and licensed under the following license:

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of Oracle Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

CandleStickChart クラス

リスト:CandleStickChart.java
package candlestickchart;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javafx.animation.FadeTransition;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.util.Duration;

/**
 * A candlestick chart is a style of bar-chart used primarily to describe price
 * movements of a security, derivative, or currency over time.
 *
 * The Data Y value is used for the opening price and then the close, high and
 * low values are stored in the Data's extra value property using a
 * CandleStickExtraValues object.
 */
public class CandleStickChart extends XYChart<Number, Number> {

    // -------------- CONSTRUCTORS ----------------------------------------------
    /**
     * Construct a new CandleStickChart with the given axis.
     *
     * @param xAxis The x axis to use
     * @param yAxis The y axis to use
     */
    public CandleStickChart(Axis<Number> xAxis, Axis<Number> yAxis) {
        super(xAxis, yAxis);
        getStylesheets().add(getClass().getResource("CandleStickChart.css").toExternalForm());
        setAnimated(false);
        xAxis.setAnimated(false);
        yAxis.setAnimated(false);
    }

    /**
     * Construct a new CandleStickChart with the given axis and data.
     *
     * @param xAxis The x axis to use
     * @param yAxis The y axis to use
     * @param data The data to use, this is the actual list used so any changes
     * to it will be reflected in the chart
     */
    public CandleStickChart(Axis<Number> xAxis, Axis<Number> yAxis, ObservableList<XYChart.Series<Number, Number>> data) {
        this(xAxis, yAxis);
        setData(data);
    }

    // -------------- METHODS ------------------------------------------------------------------------------------------
    /**
     * Called to update and layout the content for the plot
     */
    @Override
    protected void layoutPlotChildren() {
        // we have nothing to layout if no data is present
        if (getData() == null) {
            return;
        }
        // update candle positions
        for (int seriesIndex = 0; seriesIndex < getData().size(); seriesIndex++) {
            XYChart.Series<Number, Number> series = getData().get(seriesIndex);
            Iterator<XYChart.Data<Number, Number>> iter = getDisplayedDataIterator(series);
            Path seriesPath = null;
            if (series.getNode() instanceof Path) {
                seriesPath = (Path) series.getNode();
                seriesPath.getElements().clear();
            }
            while (iter.hasNext()) {
                XYChart.Data<Number, Number> item = iter.next();
                double x = getXAxis().getDisplayPosition(getCurrentDisplayedXValue(item));
                double y = getYAxis().getDisplayPosition(getCurrentDisplayedYValue(item));
                Node itemNode = item.getNode();
                CandleStickExtraValues extra = (CandleStickExtraValues) item.getExtraValue();
                if (itemNode instanceof Candle && extra != null) {
                    Candle candle = (Candle) itemNode;

                    double close = getYAxis().getDisplayPosition(extra.getClose());
                    double high = getYAxis().getDisplayPosition(extra.getHigh());
                    double low = getYAxis().getDisplayPosition(extra.getLow());

                    // calculate candle width
                    double candleWidth = -1;
                    if (getXAxis() instanceof NumberAxis) {
                        NumberAxis xa = (NumberAxis) getXAxis();
                        candleWidth = xa.getDisplayPosition(xa.getTickUnit()) * 0.90; // use 90% width between ticks
                    }

                    // update candle
                    candle.update(close - y, high - y, low - y, candleWidth);
                    candle.updateTooltip(item.getYValue().doubleValue(), extra.getClose(), extra.getHigh(), extra.getLow());

                    // position the candle
                    candle.setLayoutX(x);
                    candle.setLayoutY(y);
                }
                if (seriesPath != null) {
                    if (seriesPath.getElements().isEmpty()) {
                        seriesPath.getElements().add(new MoveTo(x, getYAxis().getDisplayPosition(extra.getAverage())));
                    } else {
                        seriesPath.getElements().add(new LineTo(x, getYAxis().getDisplayPosition(extra.getAverage())));
                    }
                }
            }
        }
    }

    @Override
    protected void dataItemChanged(XYChart.Data<Number, Number> item) {
    }

    @Override
    protected void dataItemAdded(XYChart.Series<Number, Number> series, int itemIndex, XYChart.Data<Number, Number> item) {
        Node candle = createCandle(getData().indexOf(series), item, itemIndex);
        if (shouldAnimate()) {
            candle.setOpacity(0);
            getPlotChildren().add(candle);

            // fade in new candle
            FadeTransition ft = new FadeTransition(Duration.millis(500), candle);
            ft.setToValue(1);
            ft.play();
        } else {
            getPlotChildren().add(candle);
        }

        // always draw average line on top
        if (series.getNode() != null) {
            series.getNode().toFront();
        }
    }

    @Override
    protected void dataItemRemoved(XYChart.Data<Number, Number> item, XYChart.Series<Number, Number> series) {
        final Node candle = item.getNode();

        if (shouldAnimate()) {
            // fade out old candle
            FadeTransition ft = new FadeTransition(Duration.millis(500), candle);
            ft.setToValue(0);
            ft.setOnFinished((ActionEvent actionEvent) -> {
                getPlotChildren().remove(candle);
            });
            ft.play();
        } else {
            getPlotChildren().remove(candle);
        }
    }

    @Override
    protected void seriesAdded(XYChart.Series<Number, Number> series, int seriesIndex) {
        // handle any data already in series
        for (int j = 0; j < series.getData().size(); j++) {
            XYChart.Data item = series.getData().get(j);
            Node candle = createCandle(seriesIndex, item, j);
            if (shouldAnimate()) {
                candle.setOpacity(0);
                getPlotChildren().add(candle);

                // fade in new candle
                FadeTransition ft = new FadeTransition(Duration.millis(500), candle);
                ft.setToValue(1);
                ft.play();
            } else {
                getPlotChildren().add(candle);
            }
        }
        // create series path
        Path seriesPath = new Path();
        seriesPath.getStyleClass().setAll("candlestick-average-line", "series" + seriesIndex);
        series.setNode(seriesPath);
        getPlotChildren().add(seriesPath);
    }

    @Override
    protected void seriesRemoved(XYChart.Series<Number, Number> series) {
        // remove all candle nodes
        series.getData().stream().map((d) -> d.getNode()).forEach((candle) -> {
            if (shouldAnimate()) {

                // fade out old candle
                FadeTransition ft = new FadeTransition(Duration.millis(500), candle);
                ft.setToValue(0);
                ft.setOnFinished((ActionEvent actionEvent) -> {
                    getPlotChildren().remove(candle);
                });
                ft.play();
            } else {
                getPlotChildren().remove(candle);
            }
        });
    }

    /**
     * Create a new Candle node to represent a single data item
     *
     * @param seriesIndex The index of the series the data item is in
     * @param item The data item to create node for
     * @param itemIndex The index of the data item in the series
     * @return New candle node to represent the give data item
     */
    private Node createCandle(int seriesIndex, final XYChart.Data item, int itemIndex) {
        Node candle = item.getNode();
        // check if candle has already been created
        if (candle instanceof Candle) {
            ((Candle) candle).setSeriesAndDataStyleClasses("series" + seriesIndex, "data" + itemIndex);
        } else {
            candle = new Candle("series" + seriesIndex, "data" + itemIndex);
            item.setNode(candle);
        }
        return candle;
    }

    /**
     * This is called when the range has been invalidated and we need to update
     * it. If the axis are auto ranging then we compile a list of all data that
     * the given axis has to plot and call invalidateRange() on the axis passing
     * it that data.
     */
    @Override
    protected void updateAxisRange() {
        // For candle stick chart we need to override this method as we need to let the axis know that they need to be able
        // to cover the whole area occupied by the high to low range not just its center data value
        final Axis<Number> xa = getXAxis();
        final Axis<Number> ya = getYAxis();
        List<Number> xData = null;
        List<Number> yData = null;
        if (xa.isAutoRanging()) {
            xData = new ArrayList<>();
        }
        if (ya.isAutoRanging()) {
            yData = new ArrayList<>();
        }
        if (xData != null || yData != null) {
            for (XYChart.Series<Number, Number> series : getData()) {
                for (XYChart.Data<Number, Number> data : series.getData()) {
                    if (xData != null) {
                        xData.add(data.getXValue());
                    }
                    if (yData != null) {
                        CandleStickExtraValues extras = (CandleStickExtraValues) data.getExtraValue();
                        if (extras != null) {
                            yData.add(extras.getHigh());
                            yData.add(extras.getLow());
                        } else {
                            yData.add(data.getYValue());
                        }
                    }
                }
            }
            if (xData != null) {
                xa.invalidateRange(xData);
            }
            if (yData != null) {
                ya.invalidateRange(yData);
            }
        }
    }
}
リスト:Candle.java
package candlestickchart;

import javafx.scene.Group;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Region;
import javafx.scene.shape.Line;

/** Candle node used for drawing a candle */
public class Candle extends Group {
    private final Line highLowLine = new Line();
    private final Region bar = new Region();
    private String seriesStyleClass;
    private String dataStyleClass;
    private boolean openAboveClose = true;
    private final Tooltip tooltip = new Tooltip();

    Candle(String seriesStyleClass, String dataStyleClass) {
        setAutoSizeChildren(false);
        getChildren().addAll(highLowLine, bar);
        this.seriesStyleClass = seriesStyleClass;
        this.dataStyleClass = dataStyleClass;
        updateStyleClasses();
        tooltip.setGraphic(new TooltipContent());
        Tooltip.install(bar, tooltip);
    }

    public void setSeriesAndDataStyleClasses(String seriesStyleClass, String dataStyleClass) {
        this.seriesStyleClass = seriesStyleClass;
        this.dataStyleClass = dataStyleClass;
        updateStyleClasses();
    }

    public void update(double closeOffset, double highOffset, double lowOffset, double candleWidth) {
        openAboveClose = closeOffset > 0;
        updateStyleClasses();
        highLowLine.setStartY(highOffset);
        highLowLine.setEndY(lowOffset);
        if (candleWidth == -1) {
            candleWidth = bar.prefWidth(-1);
        }
        if (openAboveClose) {
            bar.resizeRelocate(-candleWidth / 2, 0, candleWidth, closeOffset);
        } else {
            bar.resizeRelocate(-candleWidth / 2, closeOffset, candleWidth, closeOffset * -1);
        }
    }

    public void updateTooltip(double open, double close, double high, double low) {
        TooltipContent tooltipContent = (TooltipContent) tooltip.getGraphic();
        tooltipContent.update(open, close, high, low);
    }

    private void updateStyleClasses() {
        getStyleClass().setAll("candlestick-candle", seriesStyleClass, dataStyleClass);
        highLowLine.getStyleClass().setAll("candlestick-line", seriesStyleClass, dataStyleClass,
                openAboveClose ? "open-above-close" : "close-above-open");
        bar.getStyleClass().setAll("candlestick-bar", seriesStyleClass, dataStyleClass,
                openAboveClose ? "open-above-close" : "close-above-open");
    }
}
リスト:CandleStickExtraValues.java
package candlestickchart;

/** Data extra values for storing close, high and low. */
public class CandleStickExtraValues  {
    private final double close;
    private final double high;
    private final double low;
    private final double average;

    public CandleStickExtraValues(double close, double high, double low, double average) {
        this.close = close;
        this.high = high;
        this.low = low;
        this.average = average;
    }

    public double getClose() {
        return close;
    }

    public double getHigh() {
        return high;
    }

    public double getLow() {
        return low;
    }

    public double getAverage() {
        return average;
    }

    @Override
    public String toString() {
        return "CandleStickExtraValues{" + "close=" + close + ", high=" + high + ", low=" + low + ", average=" + average + '}';
    }
}

ツールチップに表示する内容は、日本語に換えてあります。

リスト:TooltipContent.java
package candlestickchart;

import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;

/**
 * The content for Candle tool tips
 */
public class TooltipContent  extends GridPane {
    private final Label openValue = new Label();
    private final Label closeValue = new Label();
    private final Label highValue = new Label();
    private final Label lowValue = new Label();

    TooltipContent() {
        Label open = new Label("始値:");
        Label close = new Label("終値:");
        Label high = new Label("高値:");
        Label low = new Label("安値:");
        open.getStyleClass().add("candlestick-tooltip-label");
        close.getStyleClass().add("candlestick-tooltip-label");
        high.getStyleClass().add("candlestick-tooltip-label");
        low.getStyleClass().add("candlestick-tooltip-label");
        setConstraints(open, 0, 0);
        setConstraints(openValue, 1, 0);
        setConstraints(close, 0, 1);
        setConstraints(closeValue, 1, 1);
        setConstraints(high, 0, 2);
        setConstraints(highValue, 1, 2);
        setConstraints(low, 0, 3);
        setConstraints(lowValue, 1, 3);
        getChildren().addAll(open, openValue, close, closeValue, high, highValue, low, lowValue);
    }

    public void update(double open, double close, double high, double low) {
        openValue.setText(Double.toString(open));
        closeValue.setText(Double.toString(close));
        highValue.setText(Double.toString(high));
        lowValue.setText(Double.toString(low));
    }
}

CandleStickChart の CSS では、チャートの基本的なフォントサイズなどの修飾も追加してあります。

リスト:CandleStickChart.css
/* ====== CANDLE STICK CHART =========================================================== */
.candlestick-tooltip-label {
    -fx-font-size: 9pt;
    -fx-font-weight: bold;
    -fx-text-fill: white;
    -fx-padding: 2 5 2 0;
}
.candlestick-average-line {
    -fx-stroke: #106ece;
    -fx-stroke-width: 2px;
}
.candlestick-candle {
    -fx-effect: dropshadow( two-pass-box , rgba(0,0,0,0.4) , 10, 0.0 , 2 , 4 );
}
.candlestick-line {
    -fx-stroke: gray;
    -fx-stroke-width: 2px;
}
.candlestick-bar {
    -fx-padding: 5;
    -candlestick-bar-fill: red;
    -fx-background-color: linear-gradient(derive(-candlestick-bar-fill,-30%), derive(-candlestick-bar-fill,-40%)),
        linear-gradient(derive(-candlestick-bar-fill,100%), derive(-candlestick-bar-fill, 10%)),
        linear-gradient(derive(-candlestick-bar-fill,30%), derive(-candlestick-bar-fill,-10%));
    -fx-background-insets: 0,1,2;
}
.candlestick-bar.close-above-open {
    -candlestick-bar-fill: greenyellow;
}
.candlestick-bar.open-above-close {
    -candlestick-bar-fill: lightpink;
}

.chart {
    -fx-padding: 10px;
    -fx-background-color: white;
}
 
.chart-content {
    -fx-padding: 10px;    
}
 
.chart-title {
    -fx-font-size: 18pt;    
}
 
 
.axis-label {
    -fx-font-size: 14pt;
}
 
.axis {
    -fx-tick-label-font: 12pt system;
}

実行例

CandleStickChart クラスを利用したサンプルを下記に紹介します。東証など日本の株式の履歴は、無料では長い期間を参照できなかったので、Yahoo Finance へアクセスして US の某社の株価データを使用しました。

リスト:CandleStickChartSample.java
package candlestickchart.sample;

import candlestickchart.CandleStickChart;
import candlestickchart.CandleStickExtraValues;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;

public class CandleStickChartSample extends Application {

    // DAY, OPEN, CLOSE, HIGH, LOW, AVERAGE
    private static final double[][] data = new double[][]{
        {1, 17.9, 17.9, 17.69, 17.77, 17.795},
        {2, 18.43, 18.48, 17.64, 17.87, 18.06},
        {3, 18.9, 18.99, 18.52, 18.57, 18.755},
        {4, 19.31, 19.42, 18.86, 18.89, 19.14},
        {5, 18.9, 19.63, 18.87, 19.31, 19.25},
        {6, 18.83, 18.99, 18.72, 18.77, 18.855},
        {7, 18.74, 18.91, 18.51, 18.52, 18.71},
        {8, 19, 19.04, 18.52, 18.55, 18.78},
        {9, 19.23, 19.3, 18.73, 19.17, 19.015},
        {10, 19.38, 19.72, 19.26, 19.27, 19.49},
        {11, 19.55, 19.64, 19.45, 19.61, 19.545},
        {12, 19.34, 19.66, 19.34, 19.51, 19.5},
        {13, 19.28, 19.41, 19.06, 19.22, 19.235},
        {14, 19.59, 19.59, 19.19, 19.21, 19.39},
        {15, 19.96, 20.16, 19.63, 19.78, 19.895},
        {16, 20.22, 20.28, 20.01, 20.06, 20.145},
        {17, 20.24, 20.46, 20.08, 20.1, 20.27},
        {18, 19.84, 20.13, 19.83, 19.96, 19.98},
        {19, 19.79, 19.95, 19.7, 19.86, 19.825},
        {20, 19.83, 19.93, 19.68, 19.72, 19.805},
        {21, 19.62, 19.86, 19.58, 19.84, 19.72},
        {22, 19.79, 19.9, 19.52, 19.54, 19.71},
        {23, 19.64, 19.85, 19.57, 19.82, 19.71},
        {24, 19.45, 19.79, 19.34, 19.71, 19.565},
        {25, 19.74, 19.79, 19.59, 19.62, 19.69},
        {26, 20.04, 20.11, 19.74, 19.78, 19.925},
        {27, 20, 20.14, 19.96, 20.04, 20.05},
        {28, 19.82, 20.01, 19.71, 19.91, 19.86},
        {29, 20.14, 20.24, 19.81, 19.87, 20.025},
        {30, 20.04, 20.23, 19.93, 20.15, 20.08},
        {31, 20.24, 20.31, 19.86, 20.06, 20.085},
        {32, 20.54, 20.59, 20.33, 20.36, 20.46},
        {33, 20.27, 20.59, 20.09, 20.38, 20.34},
        {34, 20.03, 20.34, 20.03, 20.31, 20.185},
        {35, 20.43, 20.43, 20.06, 20.13, 20.245},
        {36, 20.34, 20.6, 20.29, 20.36, 20.445},
        {37, 19.96, 20.5, 19.96, 20.34, 20.23},
        {38, 20.11, 20.24, 19.88, 19.99, 20.06},
        {39, 20.18, 20.28, 20.07, 20.14, 20.175},
        {40, 19.83, 20.26, 19.82, 20.15, 20.04},
        {41, 19.95, 20.07, 19.87, 19.91, 19.97},
        {42, 20.2, 20.33, 19.93, 19.99, 20.13},
        {43, 20.26, 20.34, 20.02, 20.19, 20.18},
        {44, 20.57, 20.59, 20.12, 20.2, 20.355},
        {45, 20.03, 20.1, 19.79, 19.86, 19.945},
        {46, 19.95, 20.04, 19.78, 19.93, 19.91},
        {47, 19.91, 19.99, 19.78, 19.89, 19.885},
        {48, 19.8, 19.96, 19.73, 19.92, 19.845},
        {49, 19.78, 19.85, 19.65, 19.69, 19.75},
        {50, 19.21, 19.67, 19.2, 19.56, 19.435},
        {51, 19.45, 19.56, 19.17, 19.45, 19.365},
        {52, 19.77, 19.84, 19.32, 19.36, 19.58},
        {53, 19.98, 20.08, 19.86, 19.88, 19.97},
        {54, 19.93, 20.11, 19.82, 19.98, 19.965},
        {55, 19.92, 19.92, 19.65, 19.79, 19.785},
        {56, 19.95, 20.01, 19.63, 20.01, 19.82},
        {57, 20.13, 20.49, 19.99, 20.04, 20.24},
        {58, 20.53, 20.55, 19.64, 19.97, 20.095},
        {59, 22.27, 22.35, 21.75, 21.8, 22.05},
        {60, 22.14, 22.24, 21.95, 22.17, 22.095},
        {61, 22.26, 22.42, 22.01, 22.31, 22.215},
        {62, 22.27, 22.67, 22.06, 22.29, 22.365},
        {63, 21.71, 21.93, 21.68, 21.76, 21.805},
        {64, 21.7, 21.76, 21.37, 21.49, 21.565},
        {65, 22.2, 22.22, 21.71, 21.88, 21.965},
        {66, 22.06, 22.71, 22.05, 22.55, 22.38}
    };

    private CandleStickChart chart;
    private NumberAxis xAxis;
    private NumberAxis yAxis;

    public Parent createContent() {
        xAxis = new NumberAxis(0, 67, 1);
        xAxis.setMinorTickCount(0);
        yAxis = new NumberAxis(17, 23, 1);
        chart = new CandleStickChart(xAxis, yAxis);
        chart.setTitle("ローソク足チャートのサンプル");

        // setup chart
        xAxis.setLabel("日付(順序)");
        yAxis.setLabel("株価 (USD)");

        // add starting data
        XYChart.Series<Number, Number> series = new XYChart.Series<>();
        for (double[] day : data) {
            series.getData().add(new XYChart.Data<>(day[0], day[5], new CandleStickExtraValues(day[1], day[2], day[3], day[4])));
        }

        ObservableList<XYChart.Series<Number, Number>> stockData = chart.getData();
        if (stockData == null) {
            stockData = FXCollections.observableArrayList(series);
            chart.setData(stockData);
        } else {
            chart.getData().add(series);
        }
        return chart;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Scene scene = new Scene(createContent(), 600, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * Java main for when running without JavaFX launcher
     *
     * @param args command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}

ローソク足チャートは、XYChart を継承して、カスタマイズしたチャートを作成したい時に大いに参考となるサンプルですが、このサンプルをちょっと変更するだけでも使い道がありそうです。例えば、エラーバーをトレンドチャートに追加するときや、ボックスプロットのトレンドを作成するときなどです。

 

参考サイト

  1. JavaFX Samples