Raspberry Pi 上で稼働するJavaFXと 近距離無線通信

Johan Vos:
1995 年から
Java に関わる。
ソーシャル・ネッ
トワーキング・
ソフトウェア向
けの Java ベー
ス・ソリューショ
ンに取り組む
LodgON の共同
創設者。組込み
開発とエンター
プライズ開発の
両方に熱心で
あり、徹底して
JavaFX と Java
EE を使用したエ
ンド・ツー・エ
ンドの Java に
専念。
Java 8
Is Here
写真:
TON HENDRIKS
J
avaFX が、Raspberry Pi 上
Java SE 8 を使用して、近距離
で稼働する素晴らしいプラッ
無線通信(NFC)対応型カード・
トフォームであることは、すで
リーダーとの通信、画面への
に実証されています。
ステータス・メッセージの表示、
組込みデバイスのクライアン
バックエンド・システムへの情
ト・サイド Java 開発に携わっ
報送信を行う方法を説明しま
ていれば、おそらくJavaFX と
す。
Raspberry Pi の相性のよさはご
注:本記事で紹介するサン
存知でしょう。組込みデバイス
プルのソース・コードは https://
はキオスク端末環境で多用さ
bitbucket.org/johanvos/
れます。その一例がカード・リー
javafx-pi-nfc からダウンロード
ダーを接続した組込みデバイ
できます。
スで、利用者は何ら
対象システムのコン
かのアクション(入
準備万端
ポーネント
室許可を得る、支払
NFC 仕様では、デ
いを行う、融資を受 現在の Java ス
けるなど)を開始す キルを活用して、 バイス間の近距離
無線通信(近接す
るために、カードを
異なる環境に及
るデバイス間の非
スキャンする必要が
あります。
ぶエンド・ツー・ 接触通信)に関す
る標準が多数定義
世の中には、さま
エンドのアプリ
されています。さま
ざまなカード・リー
ケーションを構
ざまな種類のチッ
ダー、カード、プ
プセット搭載型カー
ロトコルが存在し
築できます。
ドがありますが、本
ます。本記事では、
記事では MIFARE
Raspberry Pi 用の
ORACLE.COM/JAVAMAGAZINE ///////////////////////////// MARCH/APRIL 2014
DESFire カードを使用します。
カード・リーダーは、
Raspberry Pi で標準的に動作
するものを選択する必要があり
ます。
Advanced Card Systems Ltd.
製の USB ベース・カード・リー
ダーである ACR122U は、広範
に利用されているデバイスであ
り、Raspberry Pi に簡単に接続
して、MIFARE カードを読み取
ることができます。
注:カード・リーダーは
電力を大量に消費するため、
Raspberry Pi との接続には、電
源付き USB ハブを使用するこ
とをお勧めします。
視覚的なフィードバックを得
るために、Raspberry Pi に画
面を接続する必要があります。
一般的な HDMI モニターをは
じめ、車両やキオスク端末、
コピー機に適した小型画面ま
で、さまざまな画面が販売さ
れています。
本記事で利用するセットアッ
プ方法は一例です。
Raspberry Pi のセットアップ
このプロジェクトのコードを実
行するためには、実際に動作
するセットアップが必要です。
本記事で利用する Raspberry
Pi 向けイメージはこちらからダ
ウンロードできます。
ACR122U カード・リーダー
をこのイメージと連携させるた
めに、さらに 2 つのパッケー
ジをインストールする必要があ
ります。以下のコマンドを実行
してインストールします。
sudo apt-get update
sudo apt-get install
libpcsclite1 pcscd
Java のインストール
Java SE 8 をダウンロードして、
Raspberry Pi にインストールし
ます。
Java SE では javax.
smartcardio パッケージを使用
してスマート・カード・リーダー
COMMUNITY
JAVA IN ACTION
JOHAN VOS
現在の Java スキルを活用して、組込みデバイスに接続されたカード・リーダーから
バックエンド・システムに及ぶエンド・ツー・エンドのアプリケーションを作成する
JAVA TECH
Raspberry Pi 上で稼働する JavaFX と
近距離無線通信
ABOUT US
//embedded /
blog
52
private StringProperty latestId =
new SimpleStringProperty(" --- ");
この単純なユーザー・インタフェー
Task task = new Task() {
…
};
Thread thread = new Thread(task);
thread.start();
この Task は以下の操作を実行しま
す。
1. カード・リーダーを検出します。
2. カード・リーダーで認識されて
いるカードを検出します。
3. そのカードの識別子を読み取り
ます。
4. latestId の値を更新します。
5. カードが離されるまで待機しま
す。
6. ステップ 2 に戻ります。
カード・リーダーの検出:カー
ド・リーダーにアクセスするため
には、TerminalFactory が必要で
す。javax.microcard パッケージの
静的メソッドを使用して、デフォル
トの TerminalFactory を取得できま
す。また、カード・リーダーの種類
ORACLE.COM/JAVAMAGAZINE ///////////////////////////// MARCH/APRIL 2014
リスト4
リスト5
リスト6
HBox hbox = new HBox(3);
Label info = new Label("Last checkin by: ");
Label latestCheckinLabel = new Label();
latestCheckinLabel.textProperty().bind(latestId);
hbox.getChildren().addAll(info, latestCheckinLabel);
Scene scene = new Scene(hbox, 400, 250);
primaryStage.setScene(scene);
primaryStage.show();
すべてのリストのテキストをダウンロード
に応じた固有の TerminalFactory イ
ンスタンスを作成することもできま
す。デフォルトの TerminalFactory
では、前項で Raspberry Pi にインス
トールした PC/SC スタックが使用さ
れます。リスト 2 のコードの呼出し
により、TerminalFactory を取得しま
す。次に、リスト 3 のコードにより
terminalFactory を問い合わせて、イ
ンストールされているカード・リー
ダーを検出できます。
この例ではセットアップが成功し
ており、cardTerminalList に 1 つ
の要素が含まれています。リスト 4
のコードを実行して、この 1 つの
CardTerminal を取得します。
カードの検出:カード・リーダーの
参照を取得した後、カードの読取り
を開始できます。カードの読取りは、
リスト 5 のように無限ループを使用し
て実行します。なお、現実的には、
読取りを停止する手段としてダミーの
カードを使用することが考えられま
す。
リーダーによってカードが検出さ
れるまで、スレッドがブロックされま
す。カードの検出後は、次項で説明
する方法によりカードを処理します。
その後、カードが離されるまでスレッ
ドが再度ブロックされます。
カードの処理:カードがカード・リー
ダーの近距離にあることを検出した
ときに、handleCard メソッドを呼び
出します。カードは読取り中にも離
される可能性があるため、適切な例
外処理も必要であることに注意してく
ださい。
カードへの接続は、リスト 6 のコー
ドを実行して取得できます。
カードの識別子を読み取るために
は、読取り中のカードの種類を把握
する必要があります。カードの種類
に応じて異なるアドレスに識別子が
保管されます。この単純なデモでは、
MIFARE DESFire カードのみを対象と
していますが、対応するカードの種
類を増やすようにサンプルを拡張す
ることも簡単です。
読取り中のカードの種類に関する
情報は、以下のようにカードの ATR
(Answer To Reset)バイト・データ
を調べることで取得できます。
JAVA TECH
java -Dsun.security.smartcardio
.library=/path/to/libpcsclite.so
JavaFX アプリケーション
カード・リーダーと接続してカードを
読み取る動作には、ある程度の時間
がかかる場合があります。そのため、
これらのタスクを JavaFX アプリケー
ション・スレッドのレベルで実行する
のは得策ではありません。代わりに、
カード・リーダーを処理し、JavaFX
の標準的な手法でユーザー・インタ
フェースを更新する専用のスレッドを
作成します。
ユーザー・インタフェースの作成最後
にスキャンされたカードの識別子を
表示する、ごく単純なユーザー・イ
ンタフェースを作成します。この識別
子は、以下のように StringProperty
latestId に格納されます。
スは、静的情報を含むラベルと最後
にスキャンされたカードの識別子を
含むラベルを並べた 1 つの HBox の
みで構成されます。この HBox の作
成方法をリスト 1 に示します。
カード・リーダーとの通信:カー
ド・リーダーと通信するスレッドを
doCardReaderCommunication とい
う関数内で作成します。この関数を
JavaFX アプリケーションの start() メ
ソッド内で呼び出します。
以下のように JavaFX の Task が作成
され、新しいスレッドに割り当てられ
た後、開始されます。
リスト3
ABOUT US
と通信できます。このパッケージは
デフォルトで、システム上の PC/SC
(Personal Computer/Smart Card)
実装を検索します。
必要な Linux パッケージを
Raspberry Pi にインストールした後
は、Java 仮想マシンから pcsclite ラ
イブラリの場所を参照できるようにす
るだけです。
そのためには、以下のようにシ
ステム・プロパティ sun.security.
smartcardio.library の値として
pcsclite ライブラリの場所を設定しま
す。
リスト2
COMMUNITY
リスト1
JAVA IN ACTION
//embedded /
blog
53
リスト7
Platform.runLater(new Runnable()
{
public void run() {
latestId.setValue(uid);
}
このように処理を分離することで、
カード・リーダーとの通信とユー
ザー・インタフェースのアクティビティ
が相互に阻害されないことも保証さ
れます。
バックエンドへのデータ送信:多くの
場合、リーダーでカードがスキャン
された際には視覚的なフィードバッ
クが必要です。また、場合によって
は、バックエンド・システムに情報
を送信する必要があります。次項で
は、データをバックエンドに送信す
るごく単純なクライアント・サーバー・
モジュールを使用して、識別子を
Raspberry Pi から Java EE 7 バックエ
ンドに送信し、Web ページに表示し
ORACLE.COM/JAVAMAGAZINE ///////////////////////////// MARCH/APRIL 2014
リスト10
リスト11
リスト12
JAVA IN ACTION
static byte[] desfire =
new byte[]{0x3b, (byte) 0x81, (byte) 0x80,
0x01, (byte) 0x80, (byte) 0x80};
すべてのリストのテキストをダウンロード
ます。JavaFX アプリケーションから
REST ベース・バックエンド・システ
ムへのデータ送信は、DataFX を使
用して容易に実行できます。
リスト 11 のコードは、識別
子 uid を REST エンドポイン
ト(http://192.168.1.6:8080/
webmonitor/rest/card/checkin)に
送信します。
この送信では、REST リクエスト内
でフォームのパラメータを指定して
いるため、POST メソッドを使用し
ます。新しい識別子を読み取るたび
に、このメソッドを呼び出す必要が
あります。スレッド管理は DataFX に
よって行われるため、このメソッドは
JavaFX アプリケーション・スレッドか
ら呼び出す必要があります。したがっ
て、latestId プロパティの値を設定す
るコード・ブロック内で、このメソッ
ドを呼び出すことができます(リスト
12)。
実際のアプリケーションでは、サー
バーからクライアントに他の情報
(カード利用者の実名など)も送信
されると考えられます。クライアント・
アプリケーションではこの情報を利用
して、各利用者に合わせたユーザー・
インタフェースを作成できます。
バックエンド
次に、REST リクエストを受信して
Web ページに表示する、ごく単純な
バックエンドを作成します。前提とし
て、いつカードがスキャンされてい
るかは Web ページの利用者にはわ
かりません。そのため、カードのス
キャンが完了するたびに Web ペー
ジを動的に更新することは合理的
です。動的な更新は、Java EE 7 の
WebSocket API を利用して容易に実
行できます。必要なコード量の少な
さに驚くはずです。
まずは Web ページの作成に取り
掛かります。この Web ページは、
checkins という名前のブロック要素
JAVA TECH
Arrays.equals(
desfire, atr.getBytes());
リスト 7 に示すとおり、desfire は
期待する情報を格納した静的バイト
配列です。
対応している種類のカード(つま
り DESFire カード)である場合は、
その識別子を問い合わせることがで
きます。そのためには、カードの基
本論理チャネル経由で APDU(アプ
リケーション・プロトコル・データ・
ユニット)コマンドを送信し、レス
ポンスを読み取る必要があります。
この一連の処理は javax.microcard
パッケージを利用して行います(リス
ト 8)。
このコードで渡している
getAddress パラメータは、DESFire
カードの識別子
キオスク端末の基
を読み取る方法
本
を定義した静的
キオスク端末では、カー
バイト配列です。
このパラメータの
ド・リーダーが組込みデ
宣言をリスト 9 に
バイスに接続され、ユー
示します。必要
なバイト配列は、
ザーがカードをスキャン
カードの種類に
して何らかのアクション
よって異なりま
す。
を開始します。
リスト9
ABOUT US
ATR atr = card.getATR();
次に、以下のように、この atr オ
ブジェクト内のバイト・データを
DESFire カードで期待されるバイト・
データと比較します。
readable(byte[]) 関数は、バイト配
列を人間に判読できる形式にするた
めに、各バイト値を 16 進の String
表現に変換します(リスト 10)。
これで、判読可能なカード識別子
を作成できたので、次は、この識別
子をユーザー・インタフェースに表
示する必要があります。識別子の取
得は、JavaFXアプリケーション・スレッ
ドとは異なるスレッド(通信用スレッ
ド)で行っています。そのため、通
信用スレッドから直接 StringProperty
latestId を変更するのではなく、以下
のように JavaFX アプリケーション・
スレッド上で変更を行う必要があり
ます。
リスト8
COMMUNITY
//embedded /
blog
54
ORACLE.COM/JAVAMAGAZINE ///////////////////////////// MARCH/APRIL 2014
リスト15
リスト16
function openConnection() {
connection =
new WebSocket('ws://localhost:8080/webmonitor/endpoint');
JAVA TECH
}
connection.onmessage = function(evt) {
var date = new Date();
var chld = document.createElement("p");
chld.innerHTML = date + " :  " + evt.data;
var messages = document.getElementById("checkins");
messages.appendChild(chld);
};
すべてのリストのテキストをダウンロード
WebSocket クライアントに識別子を
送信するように変更できます(リスト
16)。
まとめ
本記事では、組込み Java アプリケー
ションの作成、単純な JavaFX ユー
ザー・インタフェースによるアプリ
ケーションの強化、外部ハードウェ
ア(カード・リーダー)への接続、
Java EE システムへの接続、そして
HTML5 を使用した情報の視覚化を、
いずれも容易に行えることを確認しま
した。
基盤となるすべてのシステムで同
じ Java SE コードが使用されます。
そのため、開発者は現在の Java ス
キルを活用して、異なる環境に及ぶ
エンド・ツー・エンドのアプリケーショ
ンを構築できます。</article>
ABOUT US
(div)を含む単純な HTML ページ
を受信する専用メソッドは checkin
です。このブロック要素内に識別子
というパスに関連付けられます。ま
のリストと、各カードのスキャン実行
た、この専用メソッドは、id という
時のタイムスタンプを表示します。
名前のフォーム・パラメータを受け
HTML ページのロード時に、単
取ります。このパラメータに識別子
純なバックエンドを参照する
が格納されています(リスト 14)。
WebSocket が作成されます。この
この情報を WebSocket クライア
WebSocket 経由でメッセージが到着
ント(例:前項で作成した単純な
するたびに、checkins
Web ページ)に送信す
ブロック要素の内容が再 試してみよう
る場合は、WebSocket
設定されます。リスト 13
エンドポイントも登録す
組込み Java アプリ
に示すコードにより、こ
る必要があります。1 つ
ケーションの作成、
の目的を容易に達成で
のクラスに複数のアノ
単純な
JavaFX
ユー
きます。
テーションを追加でき
この HTML ページで
るため、この目的でも
ザー・インタフェース
は、localhost:8080/
CardHandler を利用で
によるアプリケーショ
webmonitor/endpoint
きます。こうして、すべ
に対する WebSocket
ンの強化、カード・リー てのバックエンド・コー
が開かれます。一方、
ドを単一のファイル内
ダーへの接続、Java
JavaFX アプリケーション
に配置します。単一ファ
EE
システムへの接続、
では、localhost:8080/
イルによる実装は、複
webmonitor/rest/card/
雑な本番システムの場
HTML5 を使用した情
checkin に対して識別子
合、おそらく最適では
報の視覚化を容易に
がプッシュされます。こ
ありません。しかし本記
れらのコンポーネントを
事の実装から、REST と
行うことができます。
まとめるのが、単一ファ
WebSocket を組み合わ
イルによるバックエンド・
せた Java エンタープラ
システムです。このバッ
イズ・アプリケーション
クエンド・システムを CardHandler
の構築がいかに簡単であるかがわか
クラス内で実装します。
ります。
CardHandler は REST エンドポ
WebSocket エンドポイントで接続
イントとなるため、javax.ws.rs.
リクエストを受信したときには、作
core.Application を継承します。ま
成されたセッションを Set に格納しま
た、REST インタフェースの場所
す。接続が閉じられるときには、こ
の値が rest であることを示す @
のセッションを Set から削除します(リ
ApplicationPath アノテーションが
スト 15)。
付加されます。このハンドラ自体は
さらに、リスト 14 で作成した
card というパスに登録され、識別子
checkin メソッドを、接続中の
リスト14
COMMUNITY
リスト13
JAVA IN ACTION
//embedded /
MORE ON TOPIC:
Java 8
Is Here
blog
LEARN MORE
• Raspberry Pi
• JavaFX
55