2012/04/28

Ubuntu 12.04 LTSでlowlatencyカーネルを導入してみる

Ubuntu 12.04 LTSにアップグレード必要があるかどうかは、おそらく多くの人にとって様子をみるべきかなと感じている今日このごろです。

とはいえ、新規にインストールするなら、2013年04月までサポートされるDesktop版Ubuntu 10.04 LTSをインストールする理由はもはやないでしょう。

WindowsというよりもMacのように、バージョンの違いは劇的な変化はもたらしませんが、確実にいくつかの気がつきにくい利点はあると思います。

今回はlowlatencyカーネルを導入してみることにしました。 NVIDIAのGPU用にnvidia-currentを導入しているため、それとの組み合せでおこった問題についてまとめています。

apt-getからのカーネルの導入

lowlatancyカーネルの導入自体は、簡単に終ります。 $ apt-cache search lowlatencyで導入するべきパッケージの候補がいくつか出てきますが、 バージョンアップを適宜行なうためには具体的なバージョンが書かれていないパッケージを導入しましょう。

バージョン番号を現す3.2.0-23等の数字付きパッケージを省くと、導入する候補は次のようになりました。

  • linux-image-lowlatency - lowlatency Linux kernel image
  • linux-lowlatency - Complete lowlatency Linux kernel
  • linux-image-lowlatency-pae - lowlatency Linux kernel image
  • linux-lowlatency-pae - Complete lowlatency Linux kernel

PAEパッケージはx86_64環境では使えないので、実質的にlinux-image-lowlatencyかlinux-lowlatencyかのいずれかです。

カーネルモジュールなどをコンパイルするために/usr/src以下にヘッダーファイルも必要なので、linux-image-lowlatencyとlinux-headers-lowlatencyの両方を導入するのかと思いきや、linux-lowlatencyパッケージがあるので、これを導入しましょう。

 $ sudo apt-get install linux-lowlatency 

導入自体は、これで終りですが、手元の環境ではリブートしてもlowlatencyカーネルでは起動してくれませんでした。

GRUBのカーネル起動順序の変更

リブートの起動時に10秒ほど待ち時間があるはずなので、GRUBの画面を表示させてlowlatencyカーネルを選択して問題なく起動することを確認しましょう。

ここでNVIDIAのGPUカード周りで問題があったのですが、それは後で説明する事にして、先にGRUBの設定変更についてまとめておきます。

GRUB2が導入されている場合には、/etc/default/grubを変更した後に $ sudo update-grub コマンドで、/boot/grub以下の設定ファイル(grub.cfg)に反映させます。

ここで変更したのは、GRUB_DEFAULTの行です。

/etc/default/grubファイルの先頭部分

GRUB_DEFAULT=2
GRUB_HIDDEN_TIMEOUT=0
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_TIMEOUT=10
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="rootfstype=ext4 rootflags=data=writeback,relatime quiet splash"
GRUB_CMDLINE_LINUX=""

手元の環境では最新のカーネルは3.2.0-24で、lowlatencyカーネルのバージョンは3.2.0-23でした。

このため後から導入したにも関わらず、/boot/grub/grub.cfgの先頭に設定されているのは3.2.0-24カーネルの方で、lowlatencyは上から3番目に設定が書かれています。

GRUB_DEFAULTのパラメータは0から始まるので、3番目の設定で自動的に起動するために=2を設定しました。

この状態でgrub.cfgを再作成すると、自動的にlowlatencyカーネルで起動するようになります。

 $ sudo update-grub 

NVIDIAのビデオカードが認識されない問題

起動時にはデフォルトのVGAデバイスでlightdmが起動してしまったので気がつかなかったのですが、後からlowlatencyカーネルを導入した時にはカーネルモジュールの再作成はされませんでした。

一度lowlatencyパッケージで起動した後で、次のようにnvidia-currentパッケージを再構成すると良いでしょう。

 $ sudo dpkg-reconfigure nvidia-current 

これでlightdmを再起動( $ sudo /etc/init.d/lightdm restart)するか、再起動すれば動き始めるはずです。

まとめ

lowlatencyパッケージは、動画、音声関連のアプリケーションパッケージをまとめているUbuntu Studioグループが作成しています。

はたしてlowlatencyパッケージが必要なのかどうかは、疑問に思うところもあるのですが、とりあえず使わないと善し悪しが分からないので、しばらく使ってみる事にしました。

アプリケーション内部でnice値を適切に設定しないと、適切なリアルタイム性が得られないという指摘があります。 通常のデスクトップアプリケーションで果してレスポンスが良くなるか、lowlatencyカネールを使えば良いという考え方ではまずいかもしれない事は認識しておくべきだと思います。

Ubuntu 10.04 LTSで使われていた2.6.32パッケージから3.2の間で多くの変更がカーネルに行なわれています。

2.6.37前後ではext4,xfsファイルシステムに関連した修正が多く行なわれていますし、SMP環境ではいまいちだったreiserfsにもワークアラウンドが実装されています。

プロセスの応答性については常にスケジューラーの見直しが行なわれているところでもあるので、普通に便利に使えれば良いという方々にはノーマルなカーネルをまずはお勧めします。

個人的にはUbuntu 10.04 LTSを使っている方は、アップグレード作業の懸念さえなければ、12.04 LTSに以降した方が幸せになれるだろうと思います。 ああ、Gnome周りはログイン時にXfceを選択するとか、別のWindowManagerを選択するとか、ちょっとした事はあるかもしれませんが、使い勝手に大きな変化はなく、最新のパッケージが使える事でいろいろ便利になるでしょう。

Ubuntu 12.04 LTSのFirefoxにuimで日本語入力ができない

日本語入力にはAndroidとiOS以外ではSKK(skkime, AquaSKK, uim-skk)を常に使っています。 今回はFirefoxから日本語入力ができなくなったので、その対策をとりました。

症状:Firefoxで日本語が入力できなくなった

Firefoxでは検索欄、アドレス欄、Formのどこででも、C-x + C-j を押しても日本語入力モードになりませんし、Shift + Space、C-Space を押しても入力モードが切り替わらずにアルファベットだけが入力できる状態でした。

どうせGTK_IM_MODULE環境変数周りだろうと見当はついたので、uimが設定されているところを適当にximを設定して起動してみました。

 $ env GTK_IM_MODULE=xim firefox 

結果はみごとに解決して、他に実用上の問題もなさそうだったので、

修正点:GTK_IM_MODULE環境変数の修正

日本語入力に関連する環境変数はX11用から、代表的なものはGTK, QT用にいくつか存在します。 emacsのようにelispのIMEかX11経由のIMEか選択できるものもありますが、ここ数年以内の環境であれば、GTK, QTツールキットで作成されたアプリケーションを多く使っているはずで、そららはGTK_IM_MODULE環境変数、QT_IM_MODULE環境変数を、それ以外はXMODIFIERS環境変数の値によってX11レベルで日本語入力ができるようになります。

Ubuntu 12.04 LTSの場合は、一連の環境変数は/etc/X11/Xsessionから起動される一連のスクリプトのどこかで設定されます。/etc/X11/Xsession.d/80im-switchの中にその候補がありますが、通常は ~/.xinput.d/ の中にあるスクリプトが使われるはずです。

~/.xinit.d/以下にある$LANG環境変数の.(ピリオド)の左半分と同じファイルが読み込まれ、手元のシステムでは、システムが管理するファイルへのシンボリック・リンクになっています。

現状のxinputの設定

lrwxrwxrwx 1 yasu yasu 35 Sep 17  2011 /home/yasu/.xinput.d/ja_JP -> /etc/X11/xinit/xinput.d/uim-toolbar

現状のxinput設定の内容

# uim with GTK toolbar indicator
XIM=uim
XIM_PROGRAM=/usr/bin/uim-xim
XIM_ARGS=
GTK_IM_MODULE=uim
QT_IM_MODULE=uim
# It seems to me that the system needs to be initialized.
# Folowing trick will wait 10 seconds without slowing down X start up.
XIM_PROGRAM_XTRA="(sleep 10; uim-toolbar-gtk)"
DEPENDS="uim-xim,uim-gtk2.0|uim-qt,uim-anthy|uim-canna|uim-prime|uim-skk|uim-m17nlib"

今回はGTK_IM_MODULE変数について、uimからximに変更してしまいます。 さすがにシステム全体の設定を上書きするのは気がひけるので、手元にコピーして書き換えることにしました。

 $ rm ~/.xinput.d/ja_JP
 $ cp /etc/X11/xinit/xinput.d/uim-toolbar ~/.xinput.d/ja_JP

このコピーした ~/.xinput.d/ja_JP ファイルのGTK_IM_MODULEで始まる一行を変更して一度ログアウト・ログインし、設定を反映させました。

設定変更後の設定内容

...
GTK_IM_MODULE=xim
QT_IM_MODULE=uim
...

この後は無事にFirefoxで日本語入力ができるようになっています。

まとめ、と余計なこと…

日本語入力は重要なところですが、環境の変化によっていろいろ面倒な事がおこりやすいところでもあります。

WindowsやMacでは、IMEが切り替わらないといった問題が発生した経験はありませんが、1990年代末期まではX11に限らず入力文字を切り替えるのは面倒な作業でした。

X11R5,6の頃にXIMを設計したメンバーが、Sunを中心にIIIMFが立ち上がった経緯の資料がどこかにあったと思うので、興味がある方は検索サイトで探してみてもいいかもしれません。

話はかわって、最近Ruby MLでUTF-8な文字から、そのテキストの言語を推し量りたいという方がいましたが、実際には同じ文字コードは日本語や中国語で重複して使われているので、どの国の言葉か、その言語を確実に知る方法はありません。

政治を持ち込む気はないですが、日本語を扱うのであれば、なんで文字コードがUTF-8だけじゃないんだよと文句を言いたくなるかもしれませんが、JISコードと呼ばれたISO-2022(-JP)体系とUTF-8の歴史については学んばなければいけないと思います。もっともいまとなっては雑学の部類なのかもしれませんけどね。

話しを元に戻して、デスクトップでLinuxを使う時のネックは日本語入力ではないでしょうか。

ATOKがたびたび移植され現在も動くはずですし、Wnnもここ2,3年は聞きませんが、Ubuntu 8.04 LTS向けには正式対応版が販売されていました。

いろいろ苦労している方々がいる背景の中では、自分がSKKをMac, Windows, Linux (+ Emacs)の環境で、ほとんど同じように使えるのは幸せだなぁと感じるところです。GTK2が主流のwidgetになったX11の世界になって久しいですがuim-skkには本当に感謝しています。

ああ、IIIMFの名前を出しておいて、なんでUIMに感謝するんだよ、っていう話の理由はいろいろな経緯と現実的に普及したのがUIMだったという、これもIIIMFについて調べれば分かると思います。

しかしIIIMFの問題提起自体は正しかったと思いますし、それがなければ現状もなかったのではないでしょうか。 そういう意味では触れずにはいられないわけです。

AndroidではLifeTouch向けにSKKが移植されているようですが、uimを組み込んだエディタなんてのがあっても個人的にはいいんですよね。何にしてもGoogle playの中からダウンロードできる形でまとまっていないのが残念です。

2012/04/26

Ubuntu 10.04 LTSを12.04 LTS Beta2へアップグレードしてみた

予定では今日の日付でリリースされるはずのUbuntu 12.04 LTSですが、ファイルが世界中のミラーと同期が取れるまでは正式な発表は遅れるんじゃないかなと思って先にBeta2にアップグレードしてみました。

現状までのKnown Issuesをみる限りは、NVIDIAのビデオカード周りでは不具合が想定されますが、それほど大きな問題はないように思えました。

しかし実際には様々な外部リポジトリを参照している環境でのアップグレードは、なかなか困難な作業となってしまいました。

環境について

まとめるのは難しいのですが、メインのデスクトップ環境として使っているUbuntu 10.04 LTS環境は、monodevelopやUbuntu Japanese Teamのリポジトリなど、Ubuntu Teamが想定しているよりも多くのパッケージを導入しています。

こういう状況が今回は困難を招く事となりました。

正式リリースでは解決して欲しいのですが、万が一同じような状況に遭遇した場合は参考にしてください。

Beta2へのアップグレード

12.04 LTS Beta2の導入自体はいつものコマンドで完結することになっています。 $ sudo do-release-upgrade -d

しかし実際には、いくつかのエラーに遭遇して処理が進まない事態となりました。

エラー:バージョン表記が数字から始まらない事によるエラー

Ubuntu Japanese Forumで以前に指摘されていた現象ですが、/var/lib/dpkg/statusファイルの中でipa-monafontのバージョン表記が数字で始まるべきなのに、release1.0.8-から始まっているためにdpkgがエラーを出して処理が止まりました。

エラーメッセージに表示される/var/lib/dpkg/statusファイルの問題となる個所は、パッケージの削除はできない状況になったので、以下の個所を手動で変更することで解決しました。

/var/lib/dpkg/statusファイルからの問題個所の抜粋

Package: ttf-ipamonafont
Status: deinstall ok config-files
Priority: optional
Section: x11
Installed-Size: 96
Maintainer: Jun Kobayashi <jkbys@ubuntu.com>
Architecture: all
Version: release1.0.8-0ubuntu0ja3
Config-Version: release1.0.8-0ubuntu0ja3
Depends: opfc-modulehp-ipamonafont-source (= 1.1.1+1.0.8-0ubuntu0ja1), xutils (>= 4.0.2), defoma, fontconfig
Conffiles:
 /etc/defoma/hints/ttf-ipamonafont.hints e062172955750867c763992e06ffdf8a
Description: Setup script for IPA Mona Font
 This package makes symlink to IPA Mona fonts and setup them.

これは手でファイルを修正して、 $ sudo apt-get -f installの後で、再び $ sudo do-release-upgrade -d を実行しました。

修正後の/var/lib/dpkg/statusファイル

...
Version: 1.0.8-0ubuntu0ja3
Config-Version: 1.0.8-0ubuntu0ja3
...
エラー:gitの更新ができない

Ubuntu 10.04 LTSに導入されているgit関連のバージョン表記は4.x.yとなっていますが、12.04 LTSからは1.7.xに変更になっています。

通常バージョン番号は、より新しいパッケージが、より新しい数字を持つ前提になっているため、どうやってもこの依存関係を解決する事ができませんでした。

最終的には強制的にgit*パッケージを削除し、やはり強制的に/var/cache/apt/archives/にダウンロードされているgit関連パッケージを導入しました。

試行錯誤して完全なログがないのと、場合によって手順が変わるので、実行したコマンドの概要を記録しておきます。

 $ sudo dpkg -r --force-depends git
 $ sudo dpkg -i --force-depends /var/cache/apt/archives/git_1%3a1.7.9.5-1_amd64.deb

git-coreやgit-manといった関連パッケージはapt-get removeではなく、dpkg -rで削除できたので、最終的にはgitパッケージだけを強制的にremoveしたはずです。

この他に依存関係が壊れてしまった場合の対応

何回か do-release-upgradeが動かなくなる事態に遭遇しました。 どうしようもない状態では、apt-get -f installを試しましょう。

 $ sudo apt-get -f install

実際の作業はdo-release-upgradeとapt-get -f installの繰り返しになっています。 スクリプトが停止した都度、問題個所を修正して、何回か繰り返してUbuntu 12.04 LTS Beta2へのアップグレードが完了しました。

最後にapt-get upgradeしたタイミングで、12.04 LTS のリリース版に更新されました。

こういった状況が複合的に問題を解決したのかもしれません。

さいごに

これらの手を尽してdo-release-upgradeコマンドの処理が進んでも、時々/etc/servicesやらを更新するかといった質問と伴に処理が止まります。netstat -aで表示される80番ポートの表記がwwwからhttpに変わるといった確認をしながら置き換えるか判断する事になります。

時々、現状維持を意味するNがデフォルトのまま止まりますが、明かに更新しないと動かなくなりそうな事が多々あるので、(もちろんその逆もありますが)、よく考えて判断が必要です。

対象のパッケージをあまり使っていなくて、新規導入と同じ条件で問題ないならYを選択するべきです。 こういった経験則から判断すると、Nではなくて上書きを意味するYを押すタイミングは次のような場合でしょうか。

  • 自分が過去に編集して、利用しているアプリケーションの設定ファイル
  • アプリケーションが新規導入後と同じ状態でよい場合
  • (/etc/services等)独自の変更内容が軽微で簡単に変更が反映できる場合

どちらを選ぶ場合でも "filename + .dpkg-old" や "filename + .dpkg-dist" といった名前でファイルは並んでいるのでdiffを確認したり、後からコピーする事ができるので焦らないようにしましょう。

よく使っているアプリケーションが更新しなかった設定ファイルのせいで動かなければ"filename + .dpkg-dist"を確認して必要な修正を取り込みましょう。

慣れる作業ではないかもしれませんが、それほど時間はかからずに終ると思います。Have a good time, enjoy!

2012/04/25

DTI ServersMan@VPSで運用しているDNSサーバーをDNSSEC対応にしてみた

Debian 6 (Squeeze)で運用しているDTIのVPSサーバーはDNSサーバーとしても動いています。 今回はこの DNS - bind9 9.7.3 をDNSSEC対応にしてみました。

別途、課題の項目に掲載していますが、DNSSECに対応するだけでは不十分で、実用にするためにはDSレコードの公開が必要です。これにはドメインを登録しているRegistarがDNSSECに対応する必要があるので、国内ではほぼJPRS系列のJPDirectがサポートしている.jpドメインのみではないかなと思われます。

ドメイン毎の対応状況

今回の作業の目的は、サーバーが応答として返した検索結果が正しい事をクライアント側が検証できる環境を作る事です。

メールサーバーはSPF - Sender Policy Frameworkに対応させたので、今回の作業は、この補強の意味あいが強いです。

とはいえ、目的の.netドメインはルートサーバー側がDSレコードに対応しているのみで、ISCのDLVには未対応です。 この状況自体は何も問題がなくて、むしろ後発ならDSレコード方式にのみ対応しているのは正しいのですが、ここら辺の意味と対応状況が分かるように下調べをする事が必要です。

とはいえ日本語の情報も充実してきているので、DNSSECについて解説ページを一通り読めば問題なく作業に入れると思います。

参考文献

今回の作業は全面的にConfiguring DNSSEC On BIND9 (9.7.3) On Debian Squeeze/Ubuntu 11.10に依っています。

その他にはIANAやISCのドキュメントも読んでいますが、作業自体にはこれだけで良いと思います。

作業中に気がついた事

導入しておくべきパッケージ

dnssec-toolsパッケージが必要な事は当然ですが、署名を行なうzonesignerはPerlスクリプトで組まれているので、なくても動きますが、libcrypt-openssl-random-perlパッケージもほぼ必須です。 bind9の他にこの2つのパッケージを導入することは忘れないでください。

zonesignerコマンドの引数

手順の中でzonesignerコマンドを実行するところがありますが、適宜ゾーン名などのを変更する必要があります。

この引数に指定されているpri.example.orgはzoneファイル名で、/etc/bind以下に存在するファイル名を指定します。実際には次のようなコマンドラインになるはずです。

 $ cd /etc/bind
$ sudo zonesigner -genkeys -usensec3 -zone example.org pri.example.org

"-zone"オプションに指定しているゾーン名が、pri.example.orgファイルにあるzoneエントリ名と一致する事を確認してください。

pri.example.orgというゾーンファイルの名称

Debian系のディストリビューションでは、ゾーンを記述するファイル名は、通常はdb.で始まるファイル名を使用します。

pri.example.orgという名前をみて、最初の説明をちゃんと覚えておかないと、何のことやら迷ってしまうかもしれないので注意しましょう。

DNSエントリの修正方法

作業の流れは従来の無署名なzoneファイル(記事中での pri.example.org)を編集して、その内容に対して従来の鍵を使って新しい .signed ゾーンファイルを生成します。

2回目以降に-genkeysオプションを付けると、失敗するので注意しましょう。

 $ sudo zonesigner -zone example.org pri.example.org 
スレーブサーバーをBIND9 9.8系で構成する

基本的に9.8系はDNSSECに対応しているので、dnssec-validation auto;を含めるだけで対応できます。

スレーブサーバー側は他のサーバーからの問い合わせに対して、正しく署名された返答を行なわなければいけないわけですが、

シリアル値の更新について

良いのかどうか、zonesignerは無署名のpri.example.orgファイルのシリアル値を1つづつ更新してくれます。

編集する時に気を使う必要はないのですが、日付+シリアルという典型的な管理方法を行なっている組織だと、予定していたシリアル値より1つ多い数字が使われてしまう事で、台帳情報と噛み合わないといった混乱を招くかもしれません。

センダリDNSサーバーについて

これまでは、お名前.comに登録しているドメインだったこともあり、セカンダリDNSサーバーサービスを利用していました。

この方式は、ほぼ何もセキュリティのない状態でIPアドレスだけをキーにゾーン転送を受け付け、他のサーバーからの問い合わせに答えてくれる、原始的なDNSサーバーサービスが提供されるだけでした。

今回はDNSSECに対応した事でセカンダリサーバーを、さくらのVPSで使用しているサーバーに構築しました。 前年ながらお名前.comのセカンダリDNSサービスはエントリから削除しています。

もう少し様子をみますが、最終的にはメールとDNSの機能はDTIの490円プランで2台を大阪と東京に配置して、プライマリー、セカンダリーという構成になるでしょう。

DNSSECに対応させたプライマリ、セカンダリサーバーの設定を検証する

DSレコードの追加を申請する前に、手元のUbuntu 10.04 LTSでbind9を動かして、DNSSECが動くかどうか検証します。

基本的な考え方

手元のbind9にはDNSSECに変更したドメインに対するリクエストをフォワードして、DNSSECリクエストを解釈するために必要な鍵情報を追加します。

named.conf.localファイルから抜粋

zone "example.org" {
  type forward;
  forward only;
  forwarders { 183.1xx.165.1xx; };
};

Ubuntu 10.04 LTS上の/etc/bind/named.conf.optionsの下半分くらいは以下のような内容になっています。

named.conf.optionsファイルから抜粋

...
        listen-on-v6 { internals; };

        dnssec-enable yes;
        dnssec-validation yes;
        dnssec-lookaside auto;
};

include "/etc/bind/bind.keys";

managed-keys {
        example.org. initial-key 257 3 8 "AwEAAbjthg82WErIMm+gcsOeNlI6j7/9VuihQtYVnt9dOFWeddfZxlbv VIFKklxBLMmBt4Z5GULTDKg+2BA6hGq3UGTHJMg1cpYTZtUBF4R1LnxL 2KB15rBFtU8b3C8OtrpGsEI/VUWeii5IPopFU04QMDCQkXBiulwHbG6Z cynlvYeaUC94CVabjTPpO95BysAZqBrxQsWyokMWwMtX6V0+uYlzGIU2 OJazpYkWsIrAfpY2dRL15pugx4gCWMZwdsrfiHZSS7nlDCaDbAgsTS5t QiU4zy2YQ7vst7U4Zmh0+WbfHefeyVByCdiQaF2UmVsmnTxuEtu1Y3SS ClmDzq2/wW8=";
};

このmanaged-keys行に加える内容は3つ生成されたキーファイルの中で、先頭に"a key-signing key"と書かれているKSKファイルの内容です。

キーファイルの内容はexample.org. IN DNSKEY 257 3 8 ...で始まっているので、これをexample.org. initial-key 257 3 8 "...に変更して行の最後に";を忘れずに追加します。

Ubuntu 10.04 LTSでのdig +dnssecコマンド固有の修正

明日(4/26)から12.04 LTSが出るので、どうでも良いんですが、bind9周りが古すぎるので、修正が必要です。

一番肝心な/etc/bind/bind.keysの内容が古く、ISCのDLVに対応した1エントリしかないのでDebina6サーバー側からコピーしてきました。"dlv.isc.org"で始まるエントリの他に. initial-key 257 3 8 で始まるエントリが含まれているはずです。

/etc/bind/bind.keysファイルを最新にしたら、ローカルのbind9を再起動します。 $ /etc/init.d/bind9 restart

digコマンドによる検証

成功するとflags行に"ad"が追加されます。

 $ dig +dnssec www.yadiary.net @localhost 

出力結果

; <<>> DiG 9.7.0-P1 <<>> +dnssec sakura.yadiary.net @localhost
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24794
;; <em>flags: qr rd ra ad</em>; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;sakura.yadiary.net.		IN	A

;; ANSWER SECTION:
sakura.yadiary.net.	1800	IN	A	49.212.135.212
sakura.yadiary.net.	1800	IN	RRSIG	A 8 3 1800 20120525032448 20120425032448 61503 yadiary.net. k+rxyPboH5QMm+vU/bPU3NH6crnjyefv5Ns5Oq4a2jyfVB4N8zhiO21H FupjuoRvyQaScgjy1jVe6PaZtnhpmbb837IEpl5Xm4u+WnullBtyBshV fszUKq5fFhdkkueMEmm0KWXuOLqajRBO2Mzr13xxWfGj2pAxkz2hybkb OcY=

;; AUTHORITY SECTION:
yadiary.net.		1800	IN	NS	sakura.yadiary.net.
yadiary.net.		1800	IN	NS	wwwdti.yadiary.net.
yadiary.net.		1800	IN	RRSIG	NS 8 2 1800 20120525032448 20120425032448 61503 yadiary.net. hrV/CTOqW9cO5PSnlgg5/cDjkqtIHExQ37nrptaxK0wkls4tdvz6yPDt wBOW3dgdwX5A+Nl0Cd9Vg4oyPei0J5s3lg5eXAXfpW9JNOPwYiyQzmT7 BYYg52EOEzDQfRlQzi0hSkNI2uhcolUwRQFS7Z7MK8yACE5l5yFbJwK5 cbg=

DNSSECに対応したDNSサーバーを公開する最後の課題

最後の最後で問題があって、DNSサーバーをDNSSECに対応させても、実際にはドメインを登録しているレジストラがマスター、スレーブのDNSサーバー情報に加えて、DSレコード情報の登録・公開にも対応している必要があります。

TLD毎の対応状況はICANNが公開していますが、これに掲載されているからといって必ずDNSSECに対応できるわけではありません。

どのRegistarが、どのドメインのDSレコードをメンテナンスできるかはICANNが別途まとめています。

例えばJPRSが運営する.jpドメインは、ほぼ全てがDNSSECに対応可能となっていて、DSレコードの登録受け付けと処理方法が案内されています。

4/19付け更新のICANNの情報にもJPRSは載っていないわけですから、国内で.net/.com/.org等に対応しているRegistarがないとは限りませんが、この状況だとGoDaddy辺りにトランスファーしようかなと思ってしまうわけです。

とりあえず今回はDNSSECに対応はしましたが、自分のドメインを管理しているRegistarがDSレコードの公開に対応するまでは、このまま放置する事になってしまいます。

国内でDNSSECが普及するとしたら、.jpドメインが積極的に対応していく以外にはないでしょうね。

しょうがないのでDLVに登録してみた

dlv.isc.orgにKSKファイルの内容を登録してみました。

最初にKSKファイルの内容を登録すると、DNSにTXTレコードを追加するように促されるので、その通りにエントリを追加します。

ここがポイントですが、自動的にはre-checkは行なわれないので、dlv.isc.orgの"Managed Zones"から状況を確認して"details" → "details" と2ページほど先に進むと、Last Verification Statusという欄があるので、そのrequest re-checkリンクをクリックします。

これでDNS

DLVレコードのチェック方法

処理が進むと、DLVレコードが example.org.dlv.isc.org といった domain name + ".dlv.isc.org" に追加されます。 $ dig dlv example.org.dlv.isc.org などとすると、検索できます。

これを手元から利用可能にするには、bind9のdnssec-lookaside行を修正します。

bind9 9.7用の設定

options { ...

	dnssec-enable yes;
        dnssec-validation yes;
        dnssec-lookaside . trust-anchor dlv.isc.org.;
};

この状態で手元から $ dig +dnssec example.org のように実行すると、flagsにadが追加され、検証された事がわかります。

Ubuntu 10.04 LTSやDebian6のbind9をデフォルトで運用している限りはDNSSECは有効にならないので、キャッシュ用DNSサーバーでは設定変更が必要になります。

手元のキャッシュDNSサーバーをすべて変更して、FirefoxのDNSSEC Validator Addonを使ったところ無事に検証できました。

さいごに

駆け足でしたがVPSサーバーを2台使う事にしたので、DNSSECに対応させました。 ちなみにDNSのmaster/slave構成の間はTSIGを使ってAXFRされるデータの認証を行なっています。

TSIGは暗号化ではなく認証のみを提供する、簡単に実装できる仕組みですが、別の機会に説明したいと思います。

ただDNSSECを導入するのであれば、マスタ・スレーブ間のデータコピーは確実に行なう必要があり、これはDNSSECではサポートされない点に注意してください。DNSSECはあくまでも各レコードの内容について署名をするだけです。

Androidで外部JarがAPKファイルに取り込まれなくなった時の対処法

ひさしぶりに自宅でAndroidアプリを開発しようとして、開発環境をNVIDIAが提供している最新のTegra Android Development Pack 1.0r6に変更したところ、いくつかのアプリケーションで実行時にjava.lang.NoClassDefFoundError:エラーを出して実行できなくなっていました。

どうやらAndroid SDK Tools r17以降(正確にはADT 17.0.0以降)では、外部Jarファイルの扱いについて変更が入った模様です。

Android SDK r17からの変更点について

r17からのJarファイルの取り扱いについて、どのように問題を解決したかは、FoxykeepがHow to fix the “NoClassDefFoundError” with ADT 17にまとめています。

エラー内容

今回の問題は実行時にならないと分からず、Eclipseの中では無事に整合性が取れてコンパイルが通ってしまう事が特徴です。

アプリケーションを実行するとLogCatには次のようなエラーが記録されていました。

04-25 09:51:55.080: E/AndroidRuntime(23064): FATAL EXCEPTION: main
04-25 09:51:55.080: E/AndroidRuntime(23064): java.lang.NoClassDefFoundError: com.google.ads.AdView
04-25 09:51:55.080: E/AndroidRuntime(23064): 	at net.yadiary.android.jpostal.JPostalSearchActivity.onResume(JPostalSearchActivity.java:204)

AdMobは別のディレクトリに最新版を持っていて、複数のアプリケーションからそれを参照する構成になっていました。

解決方法

外部Jarの参照を止めて、次のステップで修正を行ないます。

  • Java Build Pathから外部Jarを参照している項目を全て削除する
  • Refactorでlibディレクトリをlibsに変更する
  • 外部Jarで参照していたJarを作成したlibsにコピーする

これでEclipse上はJava Build Pathにプロジェクト内部のlibsフォルダにあるJarファイルが登録されているはずです。

この状態でアプリケーションを実行したところ、無事にアプリケーションが動き出しました。

さいごに

ADT 17.0.0のリリースノートをみると、libsディレクトリを使う事は変更点としてまとまっています。

ADT 17.0.0リリースノートからの抜粋

Added feature to automatically setup JAR dependencies. Any .jar files in the /libs folder are added to the build configuration (similar to how the Ant build system works). Also, .jar files needed by library projects are also automatically added to projects that depend on those library projects. 

つまり、必要なJarファイルはlibsディレクトリにコピーさえしてくれれば、ADTが勝手に組み込むよ、というわけです

まさか依存関係にあるJarファイルをAPKに組み込まなくなるとは思わなかったので、解決までに時間を取ってしまいました。

2012/04/21

さくらVPS 2GでのIPv6接続

さくらのVPSが受付を再開したのでメモリ2GBのプランを申し込んでみました。

レスポンスはお名前.comのVPSが良いように感じますが、お名前.comのVPSはIPv6環境としては実用レベルの環境にする事は難しいと感じたので、さくらのVPSはIPv6を中心にみていく事にします。

カスタムOSへの変更

以前と環境を合わせるために、Debian6 amd64版をまずインストールしました。 その上で6rdでのIPv6環境を構築するために、パッケージをstableからtestingに変更しています。

/etc/apt/sources.listの内容

## for 6rd kernel
deb http://ftp.jp.debian.org/debian testing main contrib non-free
deb-src http://ftp.jp.debian.org/debian testing main contrib non-free
#

6rdのガイドに従ってカーネルを更新すると、関連した周辺パッケージも導入されるので、最終的には全体をtestingパッケージに合わせる事になると思います。

 # apt-get update
 # apt-get dist-upgrade

ping6による速度計測

まずは比較用に標準でIPv6をサポートしているDTI VPSまでのping6結果です。

比較用:自宅からDTI ServersMan@VPSまでのping6結果

PING 2001:2e8:601:0:3:1:0:bc(2001:2e8:601:0:3:1:0:bc) 56 data bytes
64 bytes from 2001:2e8:601:0:3:1:0:bc: icmp_seq=1 ttl=56 time=20.6 ms
64 bytes from 2001:2e8:601:0:3:1:0:bc: icmp_seq=2 ttl=56 time=21.0 ms
64 bytes from 2001:2e8:601:0:3:1:0:bc: icmp_seq=3 ttl=56 time=19.7 ms

次に6rdでIPv6アドレスを振ったさくらVPS 2Gまでのping6結果です。

自宅からさくらVPSまでのping6結果

PING 2001:e41:31d4:87d4::1(2001:e41:31d4:87d4::1) 56 data bytes
64 bytes from 2001:e41:31d4:87d4::1: icmp_seq=1 ttl=48 time=31.4 ms
64 bytes from 2001:e41:31d4:87d4::1: icmp_seq=2 ttl=48 time=31.6 ms
64 bytes from 2001:e41:31d4:87d4::1: icmp_seq=3 ttl=48 time=31.5 ms

まとめ

ping6の結果はTokyo6to4でIPv6化したお名前.comと比べると、だいたい10倍くらい早くなっています。ちなみにさくらVPSからwww.iij.ad.jpへのping6の結果をみると、前述の30ms台というのは自宅側に問題があって、さくらのVPSとDTIのVPSにレイテンシについては20ms前後で、違いはほとんどないようです。

体感的なパフォーマンスやIPv6を除いた全体的な印象では、お名前.comがお勧めです。 さくらのVPSでは、6rdが期限延長されたとはいえ、代替案なしにIPv6サポートとがなくなるリスクはありますが、IPv6が必要な方には、現時点でのベストチョイスの一つでしょう。

もちろんIPv6についていえばDTIのServersMan@VPSでも問題はないと思います。 私自身はIPv6対応DNSサーバーが必要なのでServersMan@VPSは現状のまま維持するとは思いますが、ip6tablesに対する不満は強く持っています。

DTIが現状のままであれば、将来さくらのVPSがIPv6を公式にサポートすれば完全に移行するでしょう。 そんな風に感じた数日間でした。

2012/04/19

Ubuntu 10.04 LTSでのUTF-8対応Chasen + ipadicの利用方法について

そろそろUbuntu 10.04 LTSから12.04 LTSに乗り換えるタイミングが迫っていますが、如何お過ごしでしょうか。 最新版のChasenパッケージはみていませんが、とりあえず作業ログをまとめておきます。

cannadicを使っている場合には、これから説明するような問題はないので気にしないでください。 今回の対象はパッケージのchasenとipadicを使っていて、UTF-8に対応させたい場合です。

ipadicパッケージを導入せずにchasen単体を導入すると、naistがまとめているjdicが導入されるはずです。 通常の使用であれば、こちらを使うのがお勧めです。

おすすめの方法

/var/lib/chasen/dic/naist-jdic-utf8 が導入されている事と /etc/chasenrc が /etc/alternatives/chasenrc へのシンボリックリンクになっていて、その先が /usr/share/chasen/chasenrc-utf8 になっている事を確認してください。 これだけで UTF-8 に対応したchasenを利用する事が可能になります。

ここから先は自力でipadic-2.7.0を導入したい人向けです。いまさらそんな需要はないと思いますけどね。

UTF-8対応にする際の課題

公式サイトのFAQをみると、UTF8に乗り換える手順は次のようになっています。

 $ tmpfile=/tmp/foo.$$
$ cd /etc
$ nkf -w chasenrc > $tmpfile 
$ cp $tmpfile chasenrc
$ cd/usr/share/chasen/dic/ipadic
$ for file in *.dic *.cha ; do nkf -w $file > $tmpfile ; sudo cp $tmpfile $file ; done
$ sudo `chasen-config --mkchadic`/makemat -i w
$ sudo `chasen-config --mkchadic`/makeda -i w chadic *.dic

全体的な流れは設定ファイルと辞書ファイルをUTF-8に変換して、chasenのmakemat,makedaを実行することです。

ここで問題になるのは、*.dicファイルがパッケージに付属してこないことです。

この時点でパッケージのipadicを使ってのUTF-8変換を行なう事は諦めました。

/usr/local/へのipadicの導入

ipadicパッケージは使い物にならないので、このまま残して、/usr/local以下にUTF-8対応のipadicを導入することにします。

ipadicパッケージを削除しようとすると、通常は依存関係にあるchasenも消えてしまうので注意が必要です。

ipadic-2.7.0の取得

ipadic-2.7.0を展開してコンパイルだけしておきます。

 $ tar xvzf ipadic-2.7.0.tar.gz
 $ ./configure
 $ make
/usr/local以下への展開

場所は /usr/local/share/chasen/dic/ipadic にします。場所は適当にかえてください。 この場所を/etc/chasenrcから指す事になります。

 $ sudo mkdir -p /usr/local/share/chasen/dic/ipadic
 $ sudo cp *.dic *.cha chasenrc /usr/local/share/chasen/dic/ipadic

ここから先は /usr/local/share/chasen/dic/ipadic に移動して実行します。

 $ /usr/local/share/chasen/dic/ipadic
$ tmpfile=/tmp/foo.$$
$ for file in *; do nkf -w $file > $tmpfile ; sudo cp $tmpfile $file ; done
$ sudo `chasen-config --mkchadic`/makemat -i w
$ sudo `chasen-config --mkchadic`/makeda -i w chadic *.dic
辞書の置き換え

最後に /etc/chasenrc を置き換えます。

 $ sudo cp -ip /etc/chasenrc /etc/chasenrc.backup.$$
 $ sudo cp /usr/local/share/chasen/dic/ipadic/chasenrc /etc/chasenrc

最後に /etc/chasenrc の先頭にある辞書を示す行を変更しておきます。

--- /usr/local/share/chasen/dic/ipadic/chasenrc	2012-04-19 14:36:22.935553612 +0900
+++ /etc/chasenrc	2012-04-19 14:39:02.456165344 +0900
@@ -5,8 +5,8 @@
 ;;;
 ;;;  grammar.cha/ctypes.cha/cforms.cha location /文法ファイル
 ;;;
-;(文法ファイル  /var/lib/chasen/dic/debian/ipadic)
-(GRAMMAR  /var/lib/chasen/dic/debian/ipadic)
+;(文法ファイル  /usr/local/share/chasen/dic/ipadic)
+(GRAMMAR  /usr/local/share/chasen/dic/ipadic)
 
 ;;;
 ;;;  dictionary /辞書

稼働確認

/etc/chasenrcはUTF-8に変更してあるので、これを処理させてみます。

 $ chasen -i w /etc/chasenrc

この結果を眺めて、名詞やらがちゃんと抜き出されているか確認します。

この流れだとパッケージに含まれているファイルの中で変更する対象は /etc/chasenrc だけになるので、いろいろやりやすいと思います。

Firebirdで壊れたDBの修復

Firebirdを使ってブックマークを管理するアプリケーションを作っていた時のこと。 気がついたらFast CGIがエラーを返していました。 最初はコーディングのミスかと思ったものの、その周辺は変更していないのでquery自体に失敗しているのに気づくのに時間が少しばかりかかりました。

今回は、そんなデータベースファイルの修復過程のログです。

まずは、エラーメッセージ

普通にqueryを発行してエラーが出たところまでは、つきとめました。

 $ isql-fb -u sysdba -p masterkey ~/lib/fb/bkmkapp.fdb

isql-fbで SELECT * FROM INFO; といった感じのSQLを実行した時のメッセージ

Statement failed, SQLCODE = -902
database file appears corrupt (/home/user1/lib/fb/bkmkapp.fdb)
-wrong page type
-page 3100 is of wrong type (expected 5, found 0)

定番の修復方法

Webを検索したり、手元の焼き鳥本をみて方法を探しました。 gfixとgbakコマンドで壊れたDBの修復を試みて、最終的にはデータを取り出し、正常なDBに取り出せた正常なデータを書き戻すという方法が一般的なようです。

まずはgfixを使って修復を試みてみます。

 $ cd ~/lib/fb
 $ gfix -v -full bkmkapp.fdb

出力されたメッセージ

Your user name and password are not defined. Ask your database administrator to set up a Firebird login.

DBに接続する時にもユーザー、パスワードが必要そうですが、コマンドラインはisql-fbとは少し違うようです。

 $ gfix -v -f -user sysdba -password masterkey bkmkapp.fdb
Summary of validation errors
	Number of Blob page errors	: 149
	Number of data page errors	: 120
	Number of index page errors	: 8
	Number of database page errors	: 3915

なんだか壊れたページ数が多すぎるんじゃないかという気もしますが、とりあえずデータを取り出してみます。 まずは壊れた部分を取り出さないようにマーキングしておきます。

 $ gfix -m -user sysdba -password masterkey bkmkapp.fdb

それから修復用に-e -gオプションを追加して、newdb.fdbにexportします。

 $ gbak -b -e -g -user sysdba -password masterkey bkmkapp.fdb newdb.fdb

newdb.fdbが作られなかった時のエラー

gbak: ERROR:invalid request BLR at offset 13
gbak: ERROR:    table id 138 is not defined
gbak: ERROR:gds_$compile_request failed
gbak:Exiting before completion due to errors

通常であれば、newdb.fdbが作成されるので、gbakのオプションを-rにして解決しますが、 今回はどうにも手がありません。

Ubuntu 10.04 LTSへのFirebird 2.5.1のインストール

まぁパッケージで提供されているFirebirdを2.5.1にして悪あがきをしてみます。

既存パッケージの削除

$ dpkg -l | grep firebird で表示されるパッケージを$ sudo apt-get removeの引数に指定して消してしまいます。

 $ sudo /etc/init.d/firebird2.1-super stop
 $ sudo apt-get remove firebird2.1-server-common firebird2.1-examples firebird2.1-common-doc firebird2.1-common
 $ sudo dpkg --purge firebird2.1-common firebird2.1-server-common firebird2.1-super

purgeをすると、設定を残すかデータベースファイルを消すか、質問されるので適当に答えておきます。

FirebirdCS-2.5.1.26351-0.amd64.tar.gzの導入

公式サイトのdownloadセクションからLinux AMD64版のFirebirdCS-2.5.1.26351-0.amd64.tar.gzをダウンロードしてきました。

展開するとディレクトリができるので、その中のinstall.shを実行します。

 $ cd FirebirdCS-2.5.1.26351-0.amd64
 $ sudo ./install.sh
Firebird classic 2.5.1.26351-0.amd64 Installation

Press Enter to start installation or ^C to abort
Extracting install data
Updated /etc/services
Please enter new password for SYSDBA user: masterkey
Install completed

sysdbaのパスワードは教科書と同じにしておきました。

この方法で導入すると、/opt/firebirdが作成され必要なファイルが導入されます。 ps auxwwで確認するとfirebirdプロセスはないように見えますが、/etc/inetd.confに/opt/firebird/bin/fb_inet_serverが追加されていて3050ポートはLISTEN状態になっています。

ここら辺をふまえて、同じ事をやってみます。

isqlからのSELECT文による問題の再現

ファイルのパスが変ってくるので、注意が必要です。 それと明示的にサーバー経由でアクセスするようにlocalhost:と絶対パスで指定しています。

$ /opt/firebird/bin/isql -u sysdba -p masterkey localhost:/home/user1/lib/fb/bkmkapp.fdb
...
...
SQL> 

…。普通に正常終了してしまいました。止まっていたFast CGIも動いてしまったのでサービス自体は継続できています。 しかし、というかもちろん、gfixでは問題は解決していません。 -mオプションでマークをつけた個所を無視しているだけなのかもしれないけれど、真相は不明です。

サーバーのバージョンアップで引き続き動き続ける事はできそうですが、引き伸ばしただけで有益とはいえなさそうです。問題の解決には商用のツールを使わざるを得ないようです。

原因について

いろいろ調べてみると、どうやら稼働中のDBに対してCREATE TABLEなどのメタデータに関連する操作を行なうと、特にDB破損の可能性が高まるようです。

さいごに

実は開発中のDBなのでテーブル定義後やデータ挿入後のファイルはコピーを取っていたり、壊れても問題ないようにはしているのですが、本番運用を想定して直し方を経験する事にしました。

RDBMSの管理方法はメーカーに依らずだいたい同じ、基本はオンライン or オフラインデータの書き戻しとヒストリーログからの書き戻しをする以外にはありません。

実際にはバックアップファイルやヒストリデータを確実にあるタイミングで取得するために、ZFSのスナップショットのような機能があると便利になるわけです。

今回はけっきょく解決しなかったわけですが、本番では、こういう事態に備えて考慮していく事になりそうです。 ユーザーが接続していたりサーバーを稼働させたままRECREATE TABLEするような事を避ければ、滅多に出ないとは思いますけどね。

この記事で取り上げた品々

2012/04/17

お名前.comのVPS(KVM)でのIPv6接続

お名前.comのサーバーを少し使ってみて、パフォーマンスには驚くばかりです。 これが1ヶ月単位の契約でも1300円ちょっとというのは、言葉になりません。

さて自宅の中も外もIPv6環境を整えているので、サーバーとして使うにはなにかしらIPv6リーチャブルでないと困ってしまいます。

今回はTokyo6to4を使って、IPv6環境を設定してみました。

6to4を使う前提

Ipv6に対応する手段として6to4はお手軽で強力な方法ですが、Ipv4の固定アドレスが必要になります。 いわゆるVPSと呼ばれるものは、固定IPv4アドレスを持っているので、問題になることはないでしょう。

自宅のサーバーなんかを6to4に接続する場合には、固定IPv4アドレスの契約をしている事と、ホームルーター(ブロードバンドルーター)を自作するのがお勧めです。

Tokyo6to4設定方法

設定情報はTokyo6to4が「6to4の利用方法」としてまとめています。

何も難しくないというか、固定IPv4アドレスからグローバルアドレスを計算する方法ぐらいしか間違えそうな要素がありません。

手動でIPv6アドレスを計算していたので、この部分については、次のようなコマンドでも自動化できると思います。

 $ env LANG=C ip -4 -o addr | awk '!/127.0./ {sub("...$","",$4);split($4,list,"."); printf "2002:%02x%02x:%02x%02x::1\n",list[1],list[2],list[3],list[4];}'

さすがに、これはない。これなら固定IPv4を素直に人間が分解して、ガイドの通りprintfコマンドに渡した方が簡単ですね。

これでガイドの通りの設定を加えるとinterfacesファイル全体は次のようになります。

$ cat /etc/network/interfaces の出力結果


# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug eth0
iface eth0 inet dhcp

auto tun6to4
iface tun6to4 inet6 v4tunnel
        address 2002:cbbd:60af::1
        netmask 16
        gateway ::192.88.99.1
        local 203.189.96.175
        endpoint any
        ttl 64
設定の有効化

サービス開始前ならちゃんと再起動して確認した方がいいと思いますが、とりあえず設定を確認するのに再起動を繰り返すのは時間が無駄なので、次のコマンドで確認します。

$ sudo ifup tun6to4

設定を再度試すには$ sudo ifdown tun6to4で一度設定を戻してから試します。

うまくいけば再起動時にも判定されるか、reboot(shutdown -r now)して試しましょう。

各種アプリケーションからのIPv6アドレス指定方法

DNSにAAAAレコードを追加するまではIPアドレスを直接してして試すしかありません。

Tokyo6to4の設定確認方法にも載っているping6コマンドは、直接2002から始まるIPv6アドレスを渡します。

$ ping6 2002:cbbd:60af::1

しかし、firefoxなどのURLでは[]で括って渡してあげます。

http://[2002:cbbd:60af::1]/
$ wget 'http://[2002:cbbd:60af::1]/'

sshクライアントではping6と同じように2002で始まるアドレスを渡します。

$ ssh 2002:cbbd:60af::1

ここら辺がちょっと判りづらいところですよね。

本番運用する時の考慮点

とりあえずIPv4によるバックアップがあるので、IPv6へのサービス提供、サービスアクセスが常に必要でなければ6to4で十分な場合が多いと思います。固有なサービスを作りたいのであれば、避けるべきだと思います。

レイテンシについて6to4は早いとはいえず、IPv6に対応しているServersman@VPSが良いのは間違いありません。 とはいえIPv6に対応しているServersman@VPSのサーバー自身の可用性がいまいち信頼できないので、安定して稼働して欲しいなぁとは思っています。最近は調子が良いですけどね。

6to4サービスのレイテンシ

ping6の結果はだいたい15倍ぐらいの開きがあって、これが6to4サービスをサーバー側で使うことのネックになるでしょう。TCPのセッションを確立するだけで、だいたい1秒くらいかかる事になります。

$ ping6 2001:2e8:601:0:3:1:0:bc


PING 2001:2e8:601:0:3:1:0:bc(2001:2e8:601:0:3:1:0:bc) 56 data bytes
64 bytes from 2001:2e8:601:0:3:1:0:bc: icmp_seq=1 ttl=56 time=20.2 ms
64 bytes from 2001:2e8:601:0:3:1:0:bc: icmp_seq=2 ttl=56 time=20.2 ms

$ ping6 2002:cbbd:60af::1

$ ping6 2002:cbbd:60af::1
PING 2002:cbbd:60af::1(2002:cbbd:60af::1) 56 data bytes
64 bytes from 2002:cbbd:60af::1: icmp_seq=1 ttl=52 time=323 ms
64 bytes from 2002:cbbd:60af::1: icmp_seq=2 ttl=52 time=319 ms

ただ2002で始まるアドレスを使う6to4には逆引きDNSサービスが受けられます。 AAAAレコードを登録できるDNSサーバを持つ事が前提ですけれど、とりあえずはセカンダリDNSサービスに紛れる事にします。

お名前.comのVPS(KVM)で最初にやったこと

さくらのVPSが予約受け付け中で使えなかったので、お名前.comのVPSを試す事にしました。 IPv6対応については、さくらのVPSは本番運用向けではないもののIPv6へのパスがあるのに、お名前.comのIPv6サポートの意向表明だけで提供時期についてはまったく情報がないのが不満ですが、とりあえずそれ以外の部分を試す事にしました。

標準OSからカスタムOS: Debian 6への移行

とりあえず準備されているカスタムOS用のISOイメージから64bit版 Debian6を選択してインストールします。

メモリ2GBのプランで70GBのディスクが使えますが、内部はだいたい20GB + 50GBの2Disk構成になっています。 標準のCentOSでは先頭の20GBの領域が/boot用に512MBのパーティションに分けられていたり、かなり標準的な構成になっています。

とりあえず1台目の/dev/vdaはインストール時に削除します。

  • /dev/vda1 - /
  • /dev/vda2 - swap
  • /dev/vdd1 - /app

導入時のパッケージ選択ではDesktopアプリ用パッケージが選択されていたので、標準ユーティリティとSSH Serverの2つだけにチェックを入れています。

削除したパッケージ

インストールが終わり稼働したDebianにログインしてみると、Desktopパッケージは省いているものの、いくらか不要なパッケージやdaemonが動いています。

まず$ dpkg -lでパッケージのリストを眺めて、次のパッケージを削除しました。 実際には関連するパッケージが若干巻き込まれて、同時に消えます。

 # apt-get remove acpi acpid acpi-support-base libx11-6 wget w3m libxcb1
 # apt-get purge

libkrb5-3自身も不要ですが、多くのユーティリティがライブラリを参照しているため削除はできません。 それよりもX関連のパッケージが含まれている事が問題かな。

追加パッケージの導入

次に必要なファイルをインストールします。

システム管理用パッケージの導入

システム管理用にいくつかのパッケージを導入しておきます。sudoは一般ユーザから主にroot権限でコマンドを実行するために使います。

xstowは/usr/local/stowディレクトリを作成して、/usr/local以下に自前でコンパイルしたアプリケーションを管理するために使います。

 # apt-get install sudo xstow 

これと平行して各ユーザーの~/.ssh/authorized_keysに手元のコンピュータのid_dsa.pubを設定してから、/etc/ssh/sshd_configにPasswordAuthentication noを設定しています。

パスワードはkeepassxを使って管理しているので、自動生成したパスワードを設定しています。 コンソールは使えますが、実質的にパスワードを使ってログインはできなくなります。

XFSファイルシステム関連ファイルの導入
 $ sudo apt-get install xfsprogs xfslibs-dev

/dev/vdb1パーティションをXFSファイルシステムにするために必要になります。

ファイルシステムの選択は必ずしも必要ではありませんが、今回はXFSを使うためにユーティリティパッケージを導入しました。

Firebirdの導入

Debian6ではFirebird 2.5が使えるので、パッケージから導入します。

 $ sudo apt-get install firebird2.5-superclassic firebird2.5-dev

ただし、この状態では世界中からアクセスできてしまうので次の2点だけは設定しておきます。

  • $ sudo dpkg-reconfigure firebird2.5-superclassicからSYSDBAのパスワードをランダムなものに変更し、/etc/firebird/2.5/SYSDBA.passwordを更新
  • /etc/firebird/2.5/firebird.confを編集し、RemoteBindAddress = 127.0.0.1を設定

アプリケーションからはsysdbaを使わずにgsecを使って追加したユーザー、パスワードを使用して、テーブルにはGRANTで追加したユーザーのアクセス権を加えておきます。

この後の使い道

しばらく使ってみて問題がなければ、yadiary.netドメインの1台に加えるか検討する事になるでしょう。ただしIPv6が使えないので、マイグレーションの予定が立てられないのがちょっと問題かな。

IPv6の設定はTokyo6to4が使えないか、後で検討する事にします。

データベースに格納されている画像データをWebページに表示する

これまでWebアプリケーションを作る際のサムネイルデータなど、サイズのごく限られた画像ファイルをBLOBデータとしてデータベースに格納してきました。こういったケースは比較的よくあると思います。 最近では比較的大きなサイズの画像ファイルでもBLOBとして格納してしまうかもしれません。

今回は比較的サイズの小さな画像データをWebページに大量に貼り付けたい場合のお話しです。

背景

これまでは、DB上にあってファイルとしてアクセスできない画像を表示するために、画像データのストリームを返す小さなWebアプリケーションを準備してきました。

これだと画像ファイルの数が多くなった場合には、画像のデータ分だけのコネクションが必要になります。 keep-aliveが使えて実際のコネクション数が参照数と同じではないとしても、同一ページに小さな画像を大量に表示する事 は全体のパフォーマンス上の懸念材料になってきました。

いままでのベストプラクティスであれば、1ページは30kbyte以下に抑えるとか、画像データの取り扱いについてもいろいろありましたが、デスクトップアプリケーションの代りとしてWebアプリを作る場合には、これまでのルールが当てはまらない場合もでてきました。

HTMLに画像データを埋め込む技術的な話し

これまでの手法だとimgタグのsrc属性に画像表示用のURLを指定してidなどで画像を指定するようにしてきましたが、直接に画像データを指定します。

データを埋め込む例

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAABLCAIAAAA06NSGAAAACXBIWXMAAABI
AAAASABGyWs+AAAACXZwQWcAAAB4AAAASwDhLqRrAAAki0lEQVR42u18a5Bc
..." />

いままで試したプログラミング言語毎の例を載せておきます。

RubyでBase64を扱う方法

いま手元にあるのはRuby 1.9.3-p125ですが、方法は共通のはずで、だいたい次のようなコードになっています。

Base64の文字列を得るのにpackを使っています。

rubyでのイメージファイルのbase64化


## この例ではオリジナルデータをファイルにしています
@filepath = "/somewhere/foo.jpg"

@prefix = "data:$MIME_TYPE;base64,"

## この戻り値をimgタグのsrc属性に指定します。
def getSrcData(data)
  ret = @prefix.sub("$MIME_TYPE", getMimeType())
  ret += [IO.read(@filepath)].pack("m")
  return ret
end

def getMimeType()
  ext = File.extname(@filepath).downcase.sub("\.","")
  ext = "jpeg" if ext =~ /jpg/i
  return format "image/%s", ext
end
JavascriptでBase64を扱う方法

以前、Google Chromeの機能拡張でExifの情報を扱うアプリケーションを作成しました。 Chromeでは同じ機能拡張の中でもメインのタブとポップアップウィンドウの間ではデータ参照が制限されているので、メインコンテンツの画像を機能拡張のポップアップウィンドウの中で表示するには、外部参照のURLを渡すか、データ列を渡すかの方法をとることになります。

その時はExifを解析するためにメインコンテンツ側でダウンロードしたデータをBase64で渡して、ポップアップウィンドウ側に表示させました。これはダウンロード回数を最小にするべきだろうという理由での判断でした。

Javascript自身にはbase64化の手段がないので、Masanao Izumoさんが公開されているbase64.jsを利用しました。

Javascriptでのbase64化


  imgtag_data_list.push("data:image/jpeg;base64,"+base64encode(EXIF.getRawData(node)));

この時はいくつかbase64化のライブラリを試しましたがテキストではなく、バイナリに対応しているのはIzumoさんのコードしか見つけられませんでした。コード中のEXIF.getRawData(node)で参照しているデータはXMLHttpRequestのresponseBodyです。

考慮点

同じ画像を大量に表示するような場合は、テキストファイルに埋め込むよりも、アプリケーション側でLast-ModifiedCache-Controlヘッダーをちゃんと扱うようにするのが最も経済的です。

安易にCGIやらで組むと動的アプリケーションの特性としてCache-Control: no-cache付く事になるので、キャッシュが有効になりませんが、これに気をつけるだけでブラウザが適切に繰り返しの読み込みを効率化してくれます。

他には画像データのサイズが比較的大きい場合は、HTMLファイルのダウンロードに時間がかかる事になるので、最初の画面描画が遅くなる欠点があります。ここ最近のブラウザはレンダリング途中から表示が始まるので、問題にならないかもしれませんが、いずれにしろサイズの大きなファイルを大量に表示するのは、もともとの設計に問題があります。

画像データの埋め込みはあくまでもサムネイルや、HTMLメールなどの用途に限定するべきでしょう。 よくよく考えて使わないと、策に溺れる事になります。

2012/04/15

pubimg: 画像ファイルを外部に公開するためのユーティリティ

このブログのコンテンツ自身はテキストファイルで、独自にXMLスキーマをRelaxNGを使って定義して、Emacsのnxmlモードでバリデーションしながら書いています。

外部参照用のURLは外部のXML定義ファイルにまとめていて、コアの機能は固まっていますが、ラベル一覧やURL一覧に効率良くアクセスする手段は模索中だったりします。

ブログだけでなく外部に公開する画像ファイルを対象に、効率良くアップロードするためのユーティティを作成しました。

pubimg本体の動き

外部に画像ファイルを公開する時に必要な作業の流れは次のようになっています。

  • サイズなどを整えた画像ファイルを作成
  • 手元のPCのあるディレクトリにファイルをコピー
  • Apache AntのFtpTaskでバッチ的にファイルをアップロード

この時にブログなどの記事を書く方では、最終的なURLの形式が必要になるので、ファイルを登録するとURLを表示するようにしています。 ここら辺はホームディレクトリの~/.pubimg.confプロパティファイルを参照して、コピー先やURLのprefixの情報等をApache AntとRubyの両方で共有しています。

このプログラムで実行している様子は次の通りです。

端末でpubimgを実行した様子

pubimgスクリプトの特徴

このスクリプトは単純ですが、次のような機能を持っています。

  • コピー先のファイル名を別に指定
  • 日付情報をprefixに付与
  • ファイルサフィックス(いわゆる拡張子)をコピー元から自動的に判別
  • sha256チェックサムで同じファイルが存在した場合には、コピーせずに既にアップロードされているURLを表示

たとえば gnome-screenshot を使った場合は、ファイル名が "Screenshot-1.png"のような名前がデフォルトになります。公開用に作成した分けではない画像ファイルであれば、オリジナルから名前を変更することが必要になります。

そこでpubimgは、第2引数にコピー先のファイル名を指定して、次のような使い方ができます。

$ pubimg Screenshot-4.png terminal_exec_pubimg
http://www.yasundial.org/images/pubimg/20120415.0.terminal_exec_pubimg.png

内容が異なるファイルを既にあるURLにコピーしようとした場合は、日付後のindexがインクリメントします。

$ pubimg Screenshot-5.png terminal_exec_pubimg
http://www.yasundial.org/images/pubimg/20120415.1.terminal_exec_pubimg.png

うっかりヒストリにあるコマンドを実行したり、別の名前で登録しようとしても、重複して登録される事はありません。

$ pubimg Screenshot-4.png hogehgoe
http://www.yasundial.org/images/pubimg/20120415.0.terminal_exec_pubimg.png

プロパティファイル

Apache antとRubyで共有しているプロパティファイルの内容は次のようになります。

プロパティファイル

local.dir = ${user.home}/.pubimg/files
remote.dir = /images/pubimg/.

ant.home = ${user.home}/java/tools/ant
java.home = /usr/java/1.6.0

url.prefix = www.yasundial.org/images/pubimg/

Apache AntのFtpTask用の設定ファイルは別の場所にあり、プロパティで指定する事もできるようになっています。

もし複数のftpアップロード先が必要になれば、読み込むプロパティファイルを切り替える仕組みをpubimg側に入れるだけです。

さいごに

これだけだとライブラリ管理はできていないので、ファイルのリストや削除は別のアプリケーションを作成する事にします。また将来的にはWindowsからファイルをアップロードしたいので、pubimgの機能はRESTアプリケーションとしても動く事になるでしょう。

pubimg自身は複数の小さいrubyクラスから出来ているのでREST側からその機能を呼ぶのは難しくないはずです。どちらかというと権限管理とか、RESTサーバーアプリからファイルを直接コピーさせるわけにもいかないので、そういう手間が増えそうな予感はします。

自宅用のアプリだし、セキュリティの懸念を考慮しなくていいならすぐに動くかな…。

Ruby 1.9.3でfirebirdクライアントを使ってみた

FireBirdはファイルベースで扱えるのでFast-CGIなどで組む場合には非常に便利です。 SQLite3の機能では型が不足していたり、ストアドプロシージャーを使いたい場合などには良い代替案になると思います。

xstowで管理しているrubyのバージョンを1.9.3-p125に更新してから、firebirdに接続するためにfbパッケージをgemから導入しようとしたところエラーが発生しました。

Gitから最新版を入手しましたが、1.9.2で非推奨となっていたSTR2CSTRが本格的に使えなくなったので、コードを書き換えたのでログを残しておきます。

Githubからのコードの取得

作業環境がUbuntu 10.04 LTSなので、gitコマンドを使って作業領域にfbディレクトリを作成します。

$ git clone https://github.com/wishdev/fb.git
$ cd fb
$ ruby extconf.rb
$ make

ここでとりあえずコンパイラは通りますが、make installをしてrubyからDBを作成しようとするとエラーが出て作業を続ける事ができなくなります。

ここはfb.cからSTR2CSTRマクロを使っている個所を一括置換で修正しました。 そうすると今度は沢山あったワーニングの中にエラーが1つ増えて、途中でコンパイルが終了してしまいます。

コンパイル時のエラー

Githubからfbの最新版を取得して、STR2CSTRをStringValuePtrに機械的に置き換えてコンパイルしましたが、次のようなエラーが表示されました。

STR2CSTRをStringValuePtrに書き換えた後のコンパイルエラー

fb.c:2185: error: lvalue required as unary ‘&’ operand

解決策

ここのコードは以前でもSafeStringValueを通すべきなんじゃないかなと思いましたが、 STR2CSTRの他に、以下のような修正を1個所で行なえばruby 1.9.3でfirebirdが使えるようになります。

エラー発生個所の変更前のコード


    sql = StringValuePtr(rb_ary_shift(args));

エラー発生個所の変更後のコード


    VALUE r_sql = rb_ary_shift(args);
    SafeStringValue(r_sql);
    sql = StringValuePtr(r_sql);

2012/04/14

Xfce + マルチディスプレイ環境の片側のディスプレイでフォントが極端に小さ くなる現象への対応

少し時間に余裕が出来る事になってメインデスクトップのUbuntu LTS環境を見直していました。 そろそろ2年振りのLTSアップデートが迫ってきていますしね。

Window ManagerにXfceを使っていますが、:0.0と:0.1のマルチディスプレイ環境にして、ウィンドウがディスプレイ内に留まって互いのデスクトップを移動しないようにしています。 nvidia-settingsでいうところのSeparate X screenという設定です。

気がついたらフォントの大きさがそれぞれのディスプレイXfce上で違った大きさになっていました。Xfceの設定パネルでは「外観」→「Font」で設定が変更できますが、ディスプレイ毎の説明はないので、不可解な動きに戸惑ってしまいました。

環境について

いまのメインデスクトップ環境は次のような環境になっています。

  • CPU: AMD Phenom(tm) II X4 905e (4 core, 2.5GHz)
  • Motherboard: ASUS M5A97 PRO
  • Memory: 16GB (4GBx4)
  • Video Card: NVIDIA GPU GeForce GTS 450
  • Monitor0: EIZO FlexScan L567 (17inch.)
  • Monitor1: EIZO FlexScan L567 (17inch.)

Xfceのフォント設定

設定は「Xfce4設定マネージャー」(xfce4-settings-manager)から変更する事ができます。 フォントは「外観」の「Fonts」タブから変更することができます。

xfce4 Settingsウィンドウ

ディスプレイ毎にフォント設定がおかしくなる現状の解決について

Gnomeではフォントがおかしくならないので、Xfceに原因があるのかなと最初は考えました。

実際Xfceのオプションで改善する事ができたのですが、xdpyinfoはScreen毎に違うDPI設定を返していて、これが原因と考えるのが妥当そうです。

xdpyinfo |grep -i -e resol -e '#' の出力結果


screen #0:
  resolution:    95x96 dots per inch
screen #1:
  resolution:    75x75 dots per inch
解決策

Xfceでは「Fonts」タグの中に「Custom DPI Setting」チェックボックスがあるので、これにチェックを入れて問題は解決しました。

Custom DPI Settingチェックボックス

原因について

DPIが極端に大きかったり、小さかったりした場合に調整する「Custom DPI Setting」が必要な状況がよく分からなかったので、少し調べてみました。

ログを除いてみると、原因は同じディスプレイを使ってMulti Displayにしている事だと分かりました。

grep -C 1 DPI /var/log/Xorg.0.log 出力結果


(II) Apr 12 18:10:15 NVIDIA(0): Virtual screen size determined to be 1280 x 1024
(--) Apr 12 18:10:15 NVIDIA(0): DPI set to (95, 96); computed from "UseEdidDpi" X config
(--) Apr 12 18:10:15 NVIDIA(0):     option
--
(WW) Apr 12 18:10:15 NVIDIA(1): Cannot find size of first mode for Eizo L567 (DFP-0); cannot
(WW) Apr 12 18:10:15 NVIDIA(1):     compute DPI from Eizo L567 (DFP-0)'s EDID.
(==) Apr 12 18:10:15 NVIDIA(1): DPI set to (75, 75); computed from built-in default
(--) Depth 24 pixmap format is 32 bpp

このcannot compute DPIをキーワードに検索してみると、いくつかドキュメントがみつかります。 IgnoreEDIDオプションはうまく動かなかったので、今回はDisplaySizeを指定する事にしました。

xdpyinfoではうまく調整できている方のディスプレイサイズがわかります。 dimensions: 1280x1024 pixels (342x271 millimeters)

/etc/X11/xorg.confのバックアップを取ってから、MonitorセクションにDisplaySizeを追加しました。

xorg.conf変更後のMonitor1用設定

Section "Monitor"
    Identifier     "Monitor1"
    VendorName     "Unknown"
    ModelName      "Eizo L567"
    HorizSync       31.0 - 64.0
    VertRefresh     59.0 - 61.0

    Option         "DPMS"
    DisplaySize 342 270
EndSection

最初は342 271と、そのままの数値を書いていたのですが、342x274と計算されてしまったので、1つ小さい数を指定したところうまく数字が合いました。 ここの挙動はまったく不明ですが、今回はここら辺で終りにしようと思います。

最後に

最終的にはCustom DPI Settingの機能は使わずに動いています。 ここまでやらなくても実用上は問題なかったのですが、xdpyinfoレベルで同じであればアプリケーションの挙動が両方のディスプレイで同じになる裏付けになるので安心できます。

解決後の xdpyinfo | grep -C1 resolution 出力結果

  dimensions:    1280x1024 pixels (342x271 millimeters)
  resolution:    95x96 dots per inch
  depths (7):    24, 1, 4, 8, 15, 16, 32
--
  dimensions:    1280x1024 pixels (342x271 millimeters)
  resolution:    95x96 dots per inch
  depths (7):    24, 1, 4, 8, 15, 16, 32

2012/04/01

AndroidでJSONICライブラリを使ってみる

このブログを作成するシステムはgdataライブラリを利用して、いくつかの手製スクリプトで構成されています。

Subversionで履歴を管理していたのですが、実際にはほとんど使えないデータだったので履歴を切り離してGitに移行してしまいました。 まぁcommitしてきたログがあるので移行しても良かったのですが、気分的にはすっきりしていい感じです。

さて、新しい仕事を始めてからは.Net FrameworkやらAndroidやらiPhoneなんかのプログラミングを始めていますが、今回はJSONICというJavaでJSONデータを扱うライブラリについてです。

AndroidでのJSONデータの扱い

さて、Androidではorg.jsonパッケージがついてきて最低限のJSONデータを取り扱う環境があります。

送信用にJavaのデータ構造をJSONにする際には、特に問題なく扱えると思います。 反対に外部から受け取ったデータを扱う際にはローレベル過ぎて扱いが少しだけ面倒です。

今回はorg.jsonパッケージの上にライブラリを構築するのではなく、JSONICというJava言語用のJSONライブラリを利用する事にしました。

作成した成果物はjsonic4androidとしてGithubで公開しています。

JSONICを扱うメリット

基本的には無理に使う必要はありません。

自作の郵便番号検索システムから受け取ったJSONデータをJavaBeansとして扱うために使用しています。

この部分をorg.jsonパッケージを利用して取り出そうとして、面倒だったのは次のような点です。

  • UTF-8にエンコードするため、BufferedReaderを使い全データを変換する必要があった
  • 配列の中に各データのレコードを持っているため、要素名を指定してアクセスする処理が冗長で、JavaBeansにマッピングする必要があった
  • org.jsonパッケージを利用する自作変換クラスが安定して動作しなかった (これは自分の技量の問題です)

JSONのデータ構造は単純ですが項目数が多いため、各要素にアクセスするコードは繰り返しが多く、JavaBeansに変換してから扱うのがベストに思えたのですが、その変換コードを書くのがさらに面倒だったというのが実情です。

エントリ数が10以下で配列の中にさらにJSONデータ構造を持つような場合でなければ、org.jsonパッケージをそのまま使うのがお勧めです。

Android端末向けのプログラミングではライブラリを利用する事も作業量を減らす上では大切ですが、アプリ全体のパッケージサイズの圧縮が必要だったり、インタフェースを使わないなどのいくつかのプログラミング上のテクニックが存在します。

そういった事との兼ね合いで常にベストな選択をしてください。

パフォーマンス

jsonicでBufferedInputStreamからJavaBeansオブジェクト(JPostalBeanインスタンス)を生成した場合と、自前でInputStreamを開いてUTF-8に変換した文字列からJavaBeansオブジェクトを返すメソッドを作成して変換時間を計測して比較してみました。

jsonicを使ったコード


is = urlconn.getInputStream();
bis = new BufferedInputStream(is);
long beginTime = new Date().getTime();
ret = JSON.decode(bis, JPostalBean.class);
long endTime = new Date().getTime();

自前の変換メソッドを使ったコード (reqTextの生成時間は計測していません)


is = urlconn.getInputStream();
br = new BufferedReader(new java.io.InputStreamReader(is,"UTF-8"));
StringBuilder reqText = new StringBuilder();
...
long beginTime = new Date().getTime();
ret = parseJsonStream(reqText.toString());
long endTime = new Date().getTime();

合計で170件のデータを含むデータを処理しましたが、jsonicを利用した場合は約10%ほど高速でした。 自前ライブラリの場合には、BufferedReaderを使いUTF-8文字列を取得するところは省いていますが、それを含めると2倍近く処理速度の差が確認できました。

jsonicを使ったから特に処理が遅くなるという事ではなさそうなので、全体のバランスの中で選択すれば良いのかなと思います。

パッケージサイズの増加量

郵便番号検索アプリで作成したAPKファイルで比較するために、jsonicに依存するコードをコメントアウトしてjsonic4androidライブラリプロジェクトへのリンクを削除したAPKファイルを作成してみました。

$ ls -l JPostalSearch.*.apk
-rw-r--r-- 1 yasu yasu 469461 2012-04-01 10:38 /home/yasu/JPostalSearch.16e.without_jsonic.apk

jsonic4androidを含む、通常のAPKファイルは次のようになります。

$ ls -l JPostalSearch.*.apk
-rw-r--r-- 1 yasu yasu 499148 2012-04-01 10:45 /home/yasu/JPostalSearch.16e.apk

およそ30KB程度の増加量になり、配布について大きな障害ではなくなります。

jsonic4androidが対応するSDK(API)バージョン

作成しているアプリはAndroid 1.6 から(minSdkVersion="4")対応するようにエミュレータで動作を確認しています。

実機で確認しているのはAndroid 2.3.3 (SdkVersion="8")、及び、Android 3.2 (SdkVersion="13") です。

変更内容は次のセクションを参照してください。

jsonic4android - Eclipseプロジェクト形式での配布

今回扱ったJSONICのバージョンは1.2.10です。当初は1.2.9でしたがバージョンアップしたため、Githubのjsonic4androidも追随しました。

Githubに登録してあるのはEclipseに組み込む事を前提としたインポート可能なパッケージ形式です。

GithubのプロジェクトページのWikiにアクセスすると、画像で使い方が並んでいます。 gitでcloneしてから「Existing Projects into Workspace」を選ぶだけで、Eclipseの中で利用する事ができます。

jsonic4androidとして変更点

Eclipseパッケージとして組み込めるような形式になっている他の変更は次のような点です。

SDKのバージョンでいうと4、いわゆるAndroid 1.6以降で扱えるように、主にString::isEmptyメソッドを使わないように修正しています。

jsonicとproguardとの関係

リリースする際には署名をしますが、ここで通常はProguardを使う事になります。

開発環境ではうまく動くのに、署名したAPKファイルを配布すると問題になる場合がある原因の一つにはProguardがあります。

Proguardでの課題はテンプレートプログラミングを行なうためのGenericsとの相性です。

Proguardはパッケージ名やメソッドなどを変更してしまいますが、Java Reflection APIを扱うJSONICからはメソッドの名称が変更されてしまうと変数にアクセスする事ができなくなってしまいます。

鉤括弧でクラスを指定するGenericsの形式を使う場合には、明示的にproguard.cfgでそのオブジェクトを対象外にする必要があります。

今回はJPostalBeanクラスをjsonicのdecodeメソッドで処理しているため、proguard.cfgに次のような記述を追加しました。

proguard.cfgに追加したJSONICで処理するJavaBeans用の記述

-keep class net.yadiary.android.jpostal.beans.JPostalBean {
  <init>(...);
  *;
}

さいごに

JSONはクライアントとサーバーが1対1に対応するような軽量Webサービスを利用する場合の選択肢としては最有力だと思います。

SOAPでなければいけない場合は、ステートフルなセッションを行ない、メッセージボディを途中で分割して別々のサーバーに送信するSOPA対応Proxyを使用しなければいけない、限られた環境に限定されるでしょう。

Androidのようなスマートフォンで外部とのメッセージをやりとりする場合には自然に使う事になるJSONですが、アクセスするためにJSONICのようなデコーダとJavaBeansのデータ構造を利用しないと、JSONのデータ構造が変化する度にコードを追加、修正する手間が発生する事になります。

選択肢は沢山あるので、うまくライブラリを利用して見通しの良いコードを書くようにしたいものです。