2012/12/09

RacktablesでLDAP認証をかける時の注意点

Racktablesは簡単に導入ができて、ビジュアル的にもそこそこ見栄えが良く、ネットワーク屋さんがexcelやvisioを使って書くであろう絵の基本的な部分はカバーできるツールではないでしょうか。

PHPベースなので、あいかわらず拡張性が良いとは思えませんが、とりあえず動いているので使ってみる事にしました。

今回は不特定多数で使用する前提で、Ubuntu上で稼働するopenldapを使ったLDAP認証と連携させる事にしました。

racktablesのadminユーザーで、承認するユーザー名を個別に設定する場合には難しい点はないのですが、グループで認証しようとする場合には、Active Directory(AD)が想定されているので、Openldap用に少しだけ変更してあげる必要があります。

基本的なopenldapの設定

今回は手間をかけないために、ldap-account-manager (LAM)を併用しています。

LAMをUbuntuで使う場合は、openldapがデフォルトでドメイン名をみて基本的なディレクトリを作成しています。 sudo slapcatなどで確認して、変更したい場合には、$ sudo dpkg-reconfigure slapdなどで自分の管理するドメイン(dc=example,dc=com)を設定します。

racktablesの導入

ポイントはだいたい次のようなところでしょうか。

  • mysqlをあらかじめ導入しておく
  • wwwrootディレクトリをScriptAliasに設定するなどし、Webサーバーの実行ユーザーには読取権限だけを与えておく
  • inc/secret.phpファイルをWebサーバーの実行ユーザーに書き換えられるよう権限を変更する ($ sudo chown www-data:www-data wwwroot)
  • http://localhost/racktables/など配置した場所に応じたパスの/install.phpにアクセスし、インストール作業を終える

PHPが参照するファイルはWebサーバーのアクセス制限には縛られないので、secert.phpはシンボリックリンクなどにして、Webサーバーの設定としてはシンボリックリンク参照を無効にしておくのがお勧めです。

racktablesにLDAP認証を設定する

inc/secret.phpファイルに既に入っている雛型だけで、LDAP認証は十分です。 domain行は使わないのでコメントのまま無効にしておきます。

だいたい次のようなコードになっています。

secret.phpファイルの内容抜粋


$user_auth_src = 'ldap';
$require_local_account = FALSE;
$LDAP_options = array
(
        'server' => 'localhost',
#       'domain' => 'example.com',
        'search_attr' => 'uid',
        'search_dn' => 'ou=People,dc=x200,dc=example,dc=com',
# // The following credentials will be used when searching for the user's DN:
#       'search_bind_rdn' => NULL,
#       'search_bind_password' => NULL,
        'displayname_attrs' => 'sn',
        'group_attr' => 'gidnumber',
        'group_filter' => '/^([0-9]+)$/',
        'options' => array (LDAP_OPT_PROTOCOL_VERSION => 3),
#       'use_tls' => 2,         // 0 == don't attempt, 1 == attempt, 2 == require
);

group_attrは基本がAD認証なので、posixAccountで定義されているgidNumberをキーにすると、group_attrに対応するgroup_filterも書き直す必要があります。

PHPのldapsearchのマニュアルをみると、ldapsearchは大文字、小文字を意識しないけれど、戻り値として返される配列のキーは小文字になる記述があります。

そのためgroup_attrに指定する文字列は小文字でなければいけません。

また、group_filterの設定は数字が期待されているので、ここでは全ての数字を許可していますが、ドメインのポリシーに従って、10000番台だけを許可するような設定がここで可能です。

racktablesでのauthorization設定

認証されたユーザーがracktablesにアクセスできるかどうかは、Configurationのpermissions設定で行ないます。

Configuration -> Permissions設定


allow {$userid_1}
allow {lgcn_10000}

デバッグの方法について

secret.phpを編集してもうまく動かない場合、内部動作を確認する方法がないので、printfデバッグの手法を使います。 auth.phpファイルを編集する場合には、認証失敗画面に出力されるよう、$ret['memberof'][]に代入するのがお勧めです。

さいごに

ネットワーク管理用のアプリはいろんな形態が考えられますが、物理的なラックに着目した点で、ユニークなアプリなのかなと思っています。

認証済みユーザーに閲覧権限だけ付与するのに、default tabだけへのアクセスを許可するようなアプローチもできそうですが、それでは閲覧できない情報がいろいろあるので良い方法がないか探しています。

この手のアプリケーションをいろいろみていて、拡張性なんかが気になってしまいます。 mediawikiのWeb API的なアプローチが内部構造として採れないか、ちょっといろいろ考えています。

2012/10/14

「アシュリー事件」を読んで

別記事にまとめてはいましたが、改めて本を入手して読み終えたのでまとめておく事にしました。

ちなみにamazonでは2012/10/5の時点で「通常2~4週間以内に発送 (予定日:10月21日 - 10月31日)」というメールが届いていましたが、繰り上がり、10/13の午前には手に取る事ができました。

実はこの後で郡山市内のジュンク堂に在庫があった事が分かったのですが、場所は医学一般書。 在庫があると分かってからも、しばらく探してしまいました。

この本のポイント

最初の入口は、この事件の表層から賛成、反対、というリクアションを持つだろうとは思いますが、 この本全体では、そこからさらに深く入り、その行動や事象の背景をできるだけ明かにしようという姿勢が明確になっています。

著者が自身のブログで「アシュリー事件を語る方に」のエントリとして、娘さんとの日常を綴っている点からも、賛成、反対という二元論的な反応ではなく、当事者にもっと寄り添う形での、事実をもっと知ってから考えて欲しいという姿勢が感じられ、この本から受ける印象と結びついていきます。

改めて本書を読んで…。自分との距離感

私は何も特別な経験を持っているわけではありません。 当事者でもないのに…、といわれても返す言葉は持っていません。

それでも、ここ最近住民税の減税を掲げて当選したどこぞの市長が障害者向け手当の削減や事業の縮小を模索しているのは事実で、障害者に限らず各種セーフティーネットも見直しが行なわれていく流れになっています。

当然この流れには私自身も居て、この生活が誰かの犠牲や誰かへの皺寄せの上に成り立っているというのは気持ちの良いものではありません。

しかし、その罪意識が軽くなるように、排他的な方向でコントロールを効かせるというのは本末転倒でしょう。

といった感じで、利己的な観点で考えても、アシュリー事件での一連の流れを正当化するのは難しいと感じます。

自身の生存を認めてもらうために大多数に属するというではなく、この世の中は誰でもそのメインストリームから外れる可能性を持っているわけで、マイノリティを包含する社会を作っていくというのが、自身のためでもあるというのが日本なら、まだ共有可能な考え方だろうと思います。

とはいえ、いじめが社会問題として今年は大きく取り上げられましたが、この問題が一定の解決がなされずに、ことなかれ的に放置されるようなら、かなり暗い先行きでしょう。

根っこの部分では、異質な者をターゲットにするいじめの構造と、ことなかれ的に全体としての問題への対応を積極的にとってこなかった責任者の行動は、この本を読んで感じる印象と重なるところがあると思います。

震災後も福島県内に居住しているというだけで、遠く離れた人達からどう思われているんだろうなぁ、と考えない事もないわけで、そんなに遠い問題でもないというのが全体を読んでの本当にざっくりとした感想です。

勘違いしていたこと

この本の中でも繰り返し取り上げられていますが、アシュリーの両親が一連の"療法"を行なうにあたって、迷いがなかったという点については、当然葛藤があっただろうと考えていました。

実際どうだったのかはわかりませんが、少なくとも両親の行動からも、この両親が迷いなく我が子に医療的な処置を依頼していた様子が想像できます。 それは異様ではあり、その行動を当然のものとして捉えるために、その両親像について、いくつか残酷な偏見かもしれない想像をめぐらせている自分が少し嫌にはなります。

それでも世の中にはいろんな主義、主張の人がいるわけで、やはりこの両親を非難するというのは間違っている方向として考えるべきでしょう。

でも、そういう風に感じる人は少なくて、賞賛するか、非難するかの選択をするのが普通かもしれません。 ずっと昔に祖父とマインドコントロール化にある某教団の幹部インタビューをテレビでみてて、誰でもこうなる可能性があるといったら、「そんなことはない。こいつは馬鹿なのだ。」といい返されて喧嘩になりそうになった事を思い出します。

今年は生活保護の話題でも弱者の義務というか、自己責任について、報道が多くあったと思います。 生活保護については批判をかわすために、家賃の行政による直接振り込みや、食費のチケット制という話題もありました。

パチンコをやりたいなら、すればいいとは思うんですけど、そうさせないために選択肢を奪う事が成長には繋がらないと思うんですよね。かといって効果的な方法も少ないように思えます。

手を差し延べることはできても、その手を握るかどうは相手次第でしょうし、その手がどういう人の手かによって、握りたいと思うかどうかの自由ぐらいはあるはずです。

そういう答えの出ない問題に望む時には、基礎となる考え方を固めないといけないと思うんですが、そういう訓練のためにも、この本を読んで考える事で、自分の中にあるはいろいろな思いを引き出してくれると思います。

一番怖いのは、この事件を通しての一連の行動がいろいろ検討して影響を考慮した上でとられたものであればまだ救いはありそうですが、無自覚に当然のものとして新たなアシュリーが造られてしまう事でしょう。

2012/10/11

「アシュリー事件」を閲覧して

児玉真美さんが書かれた「アシュリー事件」を読みました、ではなく閲覧しました。 ちゃんと読もうと、amazonやら紀伊国屋で入手しようと予約中ですが、どこも取り寄せ状態で、出版社に在庫があってもしばらく時間がかかりそうです。

いままでの経験からは最悪、入手不可も有り得るんじゃないかなと危惧しています。

2012年10月11日 追記:
10/11の時点では、アマゾンと紀伊国屋のオンラインストアで入手可能と情報が変更されています。 無事に入手できそうです。

さて入手できないなら図書館で内容を確認しようと思いつつ、Webで串刺し検索が出来るのは非常に便利なのですが、福島県内の公立図書館では郡山女子大と福島市立飯館分館、南相馬市立の3個所にしかありません。

pandaboardが5V2Aでは安定して動かないという情報もあったので確認がてら、pandaboardとc920を車に積みつつ、飯館までドライブがてらいってきました。

ここで自分の意見を表明しておくと、こういう処置には反対で、本人の意思が確認できない状態であったとしても、生命の危機がない限り、こういう不可逆的な対応をするべきではありません。

その理由は、この本の中で引用されているいくつかのアシュリーの親に否定的な意見に含まれています。

ただ、一点だけ、私には介護の経験がないし、そのご苦労は想像する事もできません。 この文書を書くにあたり、個人を非難する気はなく、アシュリーとその家族を心からいたわりたいと思います。

しかし、この問題の構造については考えてみる事にします。

この本を読んだ理由

最近は科学の生い立ちやら疑似科学の定義を求めて哲学寄りの本を読んだりしているので、その影響もあって興味を持ちました。

でもアシュリー事件そのものについての情報を知ったのは完全に偶然で、もうよく覚えていません。 たぶん脳死による臓器移植を尊厳死に広げるとかいう、そういう話について検索していて、児玉さんのブログがひっかかったのだと思います。

そして、この子供に対して行なわれた治療なのか判然としない処置について、その効果が本当にあるのかどうかという点を確認するために本を閲覧しに行きました。

父親が肯定的に捉えている様子から効果があるのかなと思いつつ、年齢を考慮すると、訴えているような苦痛が発生するずっと前に対応しようとしている様子が異常に思えました。 「効果がないんじゃないのかなぁ…」と思い、この本で確認したいと思いました。

この本から得られる情報

著者である児玉真美さんの考えが全面に出ているはずなのに、書かれている内容は裁判官や哲学者、社会学者などの発言などを元に、様々な意見が掲載されて、バランス良くまとめられています。

調べたかった、この治療が効果的だったのか、という点については、年齢からまだ効果が不明ところがあり、また、エストロゲンの大量投与などの処置に対する否定的な意見がいくつか掲載されていました。

元々が医療上の必要から選択された理由ではありませんから、否定的な意見に説得力があるのも当然かもしれません。

これから起こるかもしれない不安に対する過剰なリアクションが、この問題を異様にし、また引き立てているのだと思います。

閲覧を終えて

この本を眺めて、私は知的障害を持つ人間に対する基本的人権や尊厳をどの観点で扱うかという事が論点だと理解しました。

論理的に説明をつけて論を展開するのは、もうすでにこの本の中で様々なものが取り上げられています。

主観的には、この障害者や両親の立場に自分の気持ちを投影して、それぞれの立場で考える事になるんだと思います。 「(本人の苦痛や気持ちが分からないのに処置をされて)かわいそう」、「(世話をする親が)つらそう」、等々、どちらの立場から考えるかによって結論は分かれる可能性が大きい話題だとは思います。

これが本人意思の表明が明確に伝われば尊重しましょう、という事になるんでしょうけれど、知的障害があるというところで、本人の意思が不明なら保護者である親の意見をまずは尊重しましょうという意見も成立しそうには思えます。

ただ通常であれば本人が成人するまで、あるいは判断能力を持つまで待ち、本人の意思を確認する事になるわけですが、それが望めない例であるわけです。

意思があるのかどうか、少なくとも現在の知見では、かなり動物的本能に近い判断能力しか持っていないと考えられる子供をどのように尊重するのか、その状態からの回復の見込みがほぼないという状況であれば、親が子を思って行なうその対応を当然だと考える人もいるでしょう。

しかし考えを進めていくと、どこかに境界があるように思えます。 人間が年齢をかさねて過去には知的な活動をしていたものの、幼児のような状態になり、高度な判断ができないと判断されるようになる人もいます。

アシュリーの事例に適用された考えを使えば、保護する者の意向が尊重されるべき事例ではないでしょうか。 もちろんアシュリーと同じ処置は必要ないですが、問題は処置の内容ではなく、QOL向上のために非医療的な処置を行なう事に説得力があるかどうか、という事になります。

こういう処置が許されるのであれば、その延長線上には、保護者というより庇護する者の意思が優先されるべきだという事になるのだと思います。

将来は延命治療の代りに尊厳死を与えてあげたりするような親切が待っているのかもしれません。 しかしアシュリーのケースは延命やQOLの向上が述べられていますから、尊厳死はいいすぎでしょう。
むしろ、その反対に徹底した延命治療が行なわれると考えるべきです。

しかし、それは良いことでしょうか?

どちらにしても人間としての扱いではないと思います。 たしかに本人の意思を察知、判断する事は超常的なテレパシー能力でもない限り難しいでしょう。

かといって、権利団体が主張するような、親ではない第三者機関が判断するような法整備が行なわれても、確認のしようがないのですから、将来の訴訟を恐れて不可逆的な対応を取る事はないと思われます。 少なくとも積極的な処置をするインセンティブは持たないと考えるのが妥当でしょう。

であれば、痛みや苦痛という状態に至った時点で医療を行なうという事になり、消極的な対応だと捉える事もできます。

しかし、意思を持った人間であれば拒否するかもしれないような過剰な処置を行なうべきでしょうか。 状況が違うというかもしれませんが、それでも現時点では必要がない行為を行なう事に正当性はあるでしょうか。

この本を読んで感じた事は、必要性の証明がおそらくできない行為を行なった事。それは人体実験に近い行為であったこと。医師たちに患者本人を尊重する気持ちがなかったこと。 そして、そういう行為が必要だと考えつくほどに家族が疲弊している現実です。

この本にありますが、家族たちの継続的なケアと本人が家族を失なった後もケアを受けられるという希望を持てる社会を望むべきで、実現のために動くべきなのだと思います。

脳は自分の行為を論理的に整合性を取るように働きます。 場合によってはしてしまった自分の人格と行為の間のギャップを埋めるために、いろんな記憶や感情を捏造します。 この状態であれば家族は行為について論理的に閉じるように、第三者からは容認できないような内容だとしても、理屈を考えるでしょう。

別にIT企業幹部でなくても、自身が生きている間に我が子の将来が保障されない現実を変えられないと悟れば、お手軽な一連の行為を思いついて、実現に全力を注ぐのも理解はできます。

しかし医師たちは家族に流されずに客観的に行動するべきでした。 徹底して患者本人に寄り添って尊重するという原則がこの医師たちにあれば、自分たちの判断に余るこの申し立てについて、裁判所への確認が行なわれただろうと思います。 イギリスの事例はアメリカで認められてしまった(かのようにみえる)事例のため、将来の訴えを恐れて事態が進展したのだろうと思います。

徹底的に非難されるべきはこの医師たちのみでしょう。 それでも自死については、それを選択した理由は分かりませんが、残念な事だと思います。

根源的な性質を持つこの問題は、哲学的な人たちの好奇心をかきたてるでしょう。 しかし一般の人達は、その胸の中に生まれた感情をうまく整理する事ができずに、様々なリアクションを行なうのだと思います。

本質的に解決するべきは、どんな状態で生まれて、どんな状態になっても、人間らしくケアされるだろうという希望のある社会を実現することなのだと悟り、そして絶望して、希望を失いそうになります。

2012/10/10

Ubuntu 12.04 LTS (precious)にVMWare CLIを導入してみる

空いているPCにIntel NICを追加して、VMWare vSphere Hypervisor (ESXi)を導入したので、手元のクライアントから操作できるようにコマンドラインクライアント(vCLI)を導入しました。

ちょっとした問題があったので、まとめておく事にします。

インストール手順

CLIのインストール手順はマニュアルのにあります。 必要な前提パッケージを導入すれば、$ sudo vmware-vsphere-cli-distrib/vmware-install.plを実行するだけなのですが、次のようなエラーメッセージが表示されました。

導入時のエラーメッセージ
...
Thank you.

http_proxy not set. please set environment variable 'http_proxy' e.g. export
http_proxy=http://myproxy.mydomain.com:0000 .

ftp_proxy not set. please set environment variable 'ftp_proxy' e.g. export
ftp_proxy=http://myproxy.mydomain.com:0000 .

$ 

正常終了したのかと思いましたが、マニュアルにはコマンドを配置するパス(デフォルト/usr/bin)を指定する段取りがあるはずで、案の定esxcliコマンドは導入されていませんでした。

スクリプトが途中で終了する理由

どういうわけか、プロキシー設定("export http_proxy=..." or "export ftp_proxy=...")を行なわないとスクリプトが終了する作りになっています。

vmware-vsphere-cli-distrib/vmware-install.plの該当個所の抜粋

...
      my $httpproxy =0;
      my $ftpproxy =0;

      if ( direct_command("env | grep -i http_proxy") ) {
         $httpproxy = 1;
      } else {
         print wrap("http_proxy not set. please set environment variable 'http_proxy' e.g. export ht
tp_proxy=http://myproxy.mydomain.com:0000 . \n\n", 0);
      }
      if ( direct_command("env | grep -i ftp_proxy") ) {
         $ftpproxy = 1;
      } else {
         print wrap("ftp_proxy not set. please set environment variable 'ftp_proxy' e.g. export ftp_
proxy=http://myproxy.mydomain.com:0000 . \n\n", 0);
      }

      if ( !( $ftpproxy && $httpproxy)) {
         uninstall_file($gInstallerMainDB);
         exit 1;
      }
...

プロキシーがなくとも外部ネットワークにアクセスできる環境であれば、この評価式全体をコメントアウトしてしまうのがお勧めです。最後のif文の評価式全体をコメントアウトしてあげると、プロキシーを設定することなしに導入する事ができます。

正常終了した時のメッセージ

最終的にインストールに成功した時のメッセージの最後の方は、次のようになっていました。

...

Please wait while configuring perl modules using CPAN ...

CPAN is downloading and installing pre-requisite Perl module "Archive::Zip" .

CPAN is downloading and installing pre-requisite Perl module
"Class::MethodMaker" .

CPAN is downloading and installing pre-requisite Perl module "SOAP::Lite" .

In which directory do you want to install the executable files?
[/usr/bin]

Please wait while copying vSphere CLI files...

The installation of vSphere CLI 5.1.0 build-780721 for Linux completed
successfully. You can decide to remove this software from your system at any
time by invoking the following command:
"/usr/bin/vmware-uninstall-vSphere-CLI.pl".

This installer has successfully installed both vSphere CLI and the vSphere SDK
for Perl.

The following Perl modules were found on the system but may be too old to work
with vSphere CLI:

UUID 0.03 or newer

Enjoy, 

--the VMware team

正式にサポートされているUbuntuは10.04なので、12.04 LTSでは少し前提となるパッケージが違うようですが、とりあえずは動いています。

何か不都合があるのかは、これから使ってみて確認していきたいと思います。

2012/10/09

シリアルから扱えるよう、Pandaboardのgettyを有効にしてみた

前回まででPandaBoardが起動するようにはなったのですが、シリアルの通信は途絶えてしまいシェルが起動するという事にはなりませんでした。

HDMIは使わないので、サーバーとして扱えるようにネットワークとシリアルを有効にするところまでです。

環境

ベースにしているrootfsは前回と同様にL24.9-PandaBoard_validation_environment.tar.gzです。

MLOとu-boot.imgをgitから準備をして起動するようにはなりました。

現状

で、起動はするものの、runlevelを聞いてきたところで数字は打つものの処理が止まってしまいます。

runleveの入力後に処理が停止する様子


...
Enter runlevel: 2
INIT: Entering runlevel: 2
INIT: no more processes left in this runlevel
...

まぁgettyを起動させれば良いのでしょうと、いうわけでSDカードをUbuntu 12.04 LTSに差し、認識された/dev/sdd2/mnt/mmc2にマウントして、/mnt/mmc2/etc/inittabファイルを編集したりしました。

ここら辺の強調したデバイス名やらは環境に依存して変化するので読み替えて下さい。

/etc/inittabを変更して、ちょっとだけ改善

ちょっとだけ進んだところ


...
Enter runlevel: 2
INIT: Entering runlevel: 2

.-------.                                           
|       |                  .-.                      
|   |   |-----.-----.-----.| |   .----..-----.-----.
|       |     | __  |  ---'| '--.|  .-'|     |     |
|   |   |  |  |     |---  ||  --'|  |  |  '  | | | |
'---'---'--'--'--.  |-----''----''--'  '-----'-'-'-'
                -'  |
                '---'

The Angstrom Distribution (none) ttyO2

Angstrom 2010.4-test-20100406 (none) ttyO2

(none) login: root
Login incorrect

よくよくみると、いろいろマニュアルに問題があるようなので、そこら辺をまとめておきます。

inittabファイルの書式について

Minimal-FSの/etc/inittabは次のようになっていました。

null::sysinit:/bin/mount -t proc proc /proc
null::sysinit:/bin/mount -t sysfs sysfs /sys
null::sysinit:/bin/mount -t usbfs usbfs /proc/bus/usb
null::sysinit:/bin/mount -o remount,rw /
null::sysinit:/etc/init.d/udev

ttyO2::respawn:/bin/sh

ここで最後の行でtty02tty2のように数字を除いて指定する必要がありました。

ここを修正すると起動後にrunlevelを入力させた後にシェルが起動します。

まぁMinimal-FSはapt-getもないので、本当にどうしようもないのですが、とりあえずこれでrootでログインする事が可能になります。

別の文書ではboot.scrでカーネルパラメータを指定するような指定もありましたが、デフォルトのkernelパラメータは次のようになっています。

kernelパラメータ

Kernel command line: console=ttyO2,115200n8 vram=16M root=/dev/mmcblk0p2 rw rootfstype=ext3 rootwait

もしこのパラメータを変更する必要があれば、mkimageコマンドを使ってboot.scrファイルを生成します。 今回は試したものの必要ないので、boot.scrファイルは使っていません。

FAQによれば、コマンドラインは次のようになっています。

http://omappedia.org/wiki/PandaBoard_FAQより抜粋


Step-1: create a boot.scr.txt like:
fatload mmc 0 0x80300000 uImage
echo Booting from mmc0 ...
setenv bootargs console=ttyO2,115200n8 noinitrd init=/sbin/init root=/dev/mmcblk0p2 rootwait rw loglevel=8
bootm 80300000

Step-2: run:
mkimage -A arm -T script -O linux -C none -a 0 -e 0 -n "boot.scr" -d boot.scr.txt boot.scr

mkimageコマンドは手元のUbuntu 12.04 LTS上で実行していますが、 ここら辺はuboot-mkimage-0.4当りをキーワードに検索すれば関連する情報が探せるかと思います。

さいごに

rootでログインができたので、必要そうな情報をコピーしておきます。

ネットワークカード
# ip addr
1: lo: <LOOPBACK> mtu 16436 qdisc noop 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: usb0: <BROADCAST,MULTICAST> mtu 1492 qdisc noop qlen 1000
    link/ether 22:17:88:b7:fc:59 brd ff:ff:ff:ff:ff:ff
/proc/cpuinfo

この情報をみるとOMAP4460としては認識されていないのかなと思えます。 あとでちゃんとしたkernelを使って検証することにしましょう。

# cat /proc/cpuinfo 
Processor       : ARMv7 Processor rev 10 (v7l)
processor       : 0
BogoMIPS        : 1396.94

processor       : 1
BogoMIPS        : 1363.33

Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc09
CPU revision    : 10

Hardware        : OMAP4430 Panda Board
Revision        : 0020
Serial          : 0000000000000000

さいごに

ここら辺でMinimal-FSは止めて、Ubuntu 12.04 (precise) を動作させてみようと思います。

Ubuntu 12.04 LTSでPandaBoard ES用のu-boot/MLOイメージを作成してみた

Pandaboard ESを入手したのですが、どうも配布されているu-boot.img/MLO/uImageではうまく起動させることができませんでした。

手順は公開されているので、Ubuntu 12.04 LTSをホストにしてクロスコンパイル環境を使ってARMEL用のバイナリを作成してみました。

環境

使用している機材などは次のようになっています。

  • 対象: PandaBoard ES Rev.B1
  • ホストOS: Ubuntu 12.04 LTS 64bit版
  • シリアルケーブル: Arvel SRC06-USB (FTDIチップ使用)
  • SDカード: TOSHIBA製 Class6 SDHC 4GB (いわゆる白東芝カード)
  • rootfsイメージ等: L24.9-PandaBoard_validation_environment.tar.gz
  • 差し替え用MLO/u-boot.imgファイル: origin/Linaro-u-boot-2012.04.2

基本的なインストールの流れ

本家からセットアップのインストラクションを探して、Linux Minimum (Pandaboard Minimal-FS)に辿り着きました。

L24.9-PandaBoard_validation_environment.tar.gzはここで入手できたのですが、X-Loaderはhangしてしまいました。

PandaBoard ES uboot howtoという文書に従ったのですが、少しだけ修正する必要がありました。

$ mkdir pandaboard-es
$ cd pandaboard-es
$ git clone git://git.linaro.org/boot/u-boot-linaro-stable.git
$ cd u-boot-linaro-stable
$ git checkout -b pandaboard-es origin/Linaro-u-boot-2012.04.2
$ export CROSS_COMPILE=arm-linux-gnueabi-
$ make omap4_panda_config
$ make

変更したのは、2011.12のブランチを取得するようになっていたところを最新の2012.04に変更し、CROSS_COMPILE環境変数に指定する文字列から-noneを削除しました。

これをしない場合に、makeが正常に終了しない問題が発生しています。

ARM用gccをUbuntu 12.04 LTSに導入する

手元のUbuntu 12.04 LTSにarm用のgcc環境を構築するには、次のコマンドを実行します。

$ sudo apt-get install gcc-arm-linux-gnueabi

arm用のgcc-4.6が導入されています。 これで、コンパイルに必要なものはほぼ揃うはずです。

成果物の場所

目立ちませんが、$ makeを実行したカレントディレクトリに必要なファイルが作成されています。

  • MLO
  • u-boot.img

この2つのファイルをSDカード上に作成したFAT32領域に書き込めば、カードをPandaboardに移してテストです。

そういえば、配布されているイメージでは、u-boot.imgではなく、u-boot.binが入っていたんですよね。

どっちにしろMLOが見つからないらしく、X-Loader 1.41がエラーを出すので、u-bootまで処理が進んでいないと思われるので、関係ありませんが…。

確認に必要な環境

HDMIで液晶ディスプレイなどに出力をするにしても、まずはシリアル経由で接続しないとX-Loaderやら起動時の画面出力やらが分かりません。

Linux上で動作するUSBシリアルケーブルは内部にFTDIのチップを積んでいるものがお勧めです。

pandaboardのシリアルに接続する場合の、kermitの設定

次の設定を~/.kermrcに入れていて、kermit起動後にC-Kermit> connectとタイプして問題なく動作しています。

Ubuntu 12.04 LTS上の~/.kermrcファイルの全体

set port /dev/ttyUSB0
set speed 115200
set carrier-watch off

手元のPandaboardからの出力は、こんな感じになっています。

起動時のコンソール出力の抜粋

U-Boot SPL 2012.04.01 (Oct 09 2012 - 13:49:33)
OMAP4460 ES1.1
OMAP SD/MMC: 0
reading u-boot.img
reading u-boot.img

U-Boot 2012.04.01 (Oct 09 2012 - 13:49:33)

CPU  : OMAP4460 ES1.1
Board: OMAP4 Panda
I2C:   ready
DRAM:  1 GiB
MMC:   OMAP SD/MMC: 0
Using default environment

In:    serial
Out:   serial
Err:   serial
Net:   No ethernet found.
checking for preEnv.txt
reading preEnv.txt

** Unable to read "preEnv.txt" from mmc 0:1 **
Hit any key to stop autoboot:  0
reading uEnv.txt

** Unable to read "uEnv.txt" from mmc 0:1 **
reading boot.scr

** Unable to read "boot.scr" from mmc 0:1 **
reading uImage

2665748 bytes read
Booting from mmc0 ...
## Booting kernel from Legacy Image at 82000000 ...
   Image Name:   Linux-2.6.35-g6d019da-dirty
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    2665684 Bytes = 2.5 MiB
   Load Address: 80008000
   Entry Point:  80008000
   Verifying Checksum ... OK
   Loading Kernel Image ... OK
OK

Starting kernel ...

Uncompressing Linux... done, booting the kernel.
Linux version 2.6.35-g6d019da-dirty (danders@ccd-dev) (gcc version 4.3.3 (GCC) ) #15 SMP PREEMPT Wed Sep 8 15:33:37 CDT 2010
CPU: ARMv7 Processor [412fc09a] revision 10 (ARMv7), cr=10c53c7f
CPU: VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
Machine: OMAP4430 Panda Board
Memory policy: ECC disabled, Data cache writealloc
OMAP4430 ES2.0
....

さいごに

まだgettyが最後に起動しないので、設定としてはいろいろ足りていないのですが、とりあえず起動するようにはなりました。

ロボットとか作っている人達をテレビでみるとこのカードを積んでたりしますよね。 サーボみたいに電流を流すものは扱わないのですが、ホームセンターなんかでバイク用の小型12Vバッテリーを眺めると、いろいろ夢は広がります。

まぁ12Vを安定的に得るのと充電するので、いろいろ大変そうですけどね…。

それでも携帯電話向けにソーラーパネルが付いたリチウム電池は普通に売っているので、そのままでは実用的に使えないでしょうけど、そんなに手間をかけなくてもモバイルでは運用できるのかな…。

2012/10/07

Ubuntu 12.04 (precious) amd64版にvCLIを導入しようとして、libtiff4関連のエラーに逢う

VMWare ESXi (VMWare vSphere Hypervisor 5.1)を導入しているマシンにリモートからアクセスするために、VMware-vSphere-CLIをダウンロードし、導入しようとしました。

導入方法はマニュアルのInstalling the vCLI Package on a Linux System with Internet Accessにあるのですが、ia32-libsを導入しようとしてエラーになってしまいました。

$ sudo apt-get -f install
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
依存関係を解決しています ... 完了
以下の特別パッケージがインストールされます:   
  libtiff4:i386
以下のパッケージが新たにインストールされます: 
  libtiff4:i386
アップグレード: 0 個、新規インストール: 1 個、削除: 0 個、保留: 0 個。
226 個のパッケージが完全にインストールまたは削除されていません。
142 kB 中 0 B のアーカイブを取得する必要があります。
この操作後に追加で 501 kB のディスク容量が消費されます。
続行しますか [Y/n]?
(データベースを読み込んでいます ... 現在 560449 個のファイルとディレクトリがインストールされています。)
(.../libtiff4_3.9.5-2ubuntu1.2_i386.deb から) libtiff4:i386 を展開しています...
dpkg: /var/cache/apt/archives/libtiff4_3.9.5-2ubuntu1.2_i386.deb の処理中にエラーが発生しました (--unpack):
 './usr/share/doc/libtiff4/README' is different from the same file on the system
dpkg-deb: error: subprocess ペースト was killed by signal (Broken pipe)
以下のパッケージの処理中にエラーが発生しました:
 /var/cache/apt/archives/libtiff4_3.9.5-2ubuntu1.2_i386.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)

解決方法はあっさりしたものだったのですが、不思議な現象に思えたのでメモを残す事にしました。

まずは対処方法から

前述のエラーへの対応方法は、とりあえずlaunchpadで、tiffを検索し、amd64版のlibtiff4パッケージをダウンロードして、手動で導入します。

$ sudo dpkg -i libtiff4_3.9.5-2ubuntu1.2_amd64.deb

続いてエラー原因を解消します。

$ sudo apt-get -f install

なんというか不思議なのですが、これで32bit版のlibtiff4:i386が導入され、対応は完了です。

原因について

結局、原因については不明です。

解決方法は、Ubuntu Forumに記述がありました。

この手の解決策は以前にもあったと思いますが、Ubuntu 12.04にアップグレードした際に何か問題があったのだと思います。

Ubuntu 12.04 amd64版をクリーンインストールした場合には問題はないだろうと思うのですが、エラーメッセージからは32bit版と64bit版で、READMEファイルの配置場所が重複している事が原因だとは思いながらも、どうすれば良いのか分からずしばらく悩んでしまいました。

これで本題のVMWare ESXi 5.1用のCLIが導入できますが、それはそれで問題があったので、別の記事にまとめる予定です。

2012/09/22

平成16年製デミオについて、2012年4月の車検から今月(2012年9月に)までにした こと

いろいろあって最近はブログの更新もいまいちだったので、4月に受けた車検から、今月、2012年9月、に交換した部品やら、その感想なんかを書いてみる事にしました。

別に特別な事をしたわけでもなくて、基本的にはオートバックスで購入したものを、そこのピットで取り付けてもらっています。そのため、オートバックスへの礼賛記事みたいになっているかもしれませんが、そんな気はありません。

かといって、事実を書いた部分が、多少批判的な印象を与えてしまうかもしれませんが、サービスや作業内容に問題があるとは思っておらず、他意はありませんのでご了承ください。

スペック

車の概要は次の通りです。

  • 形式:UA-DY3R
  • 購入年月:2008年4月 (ただし中古車として)
  • 登録年:2004年 (平成16年)
  • 現在までの走行距離:約9万4.3千キロ

中古車ですがディーラーの直営店で購入した事もあって、必要な部品の交換なんかは最初にいろいろやっていて、全体的には問題はなくてとても満足しています。

2012年4月:車検

いままでディーラーで受けていた車検ですが、いろんな状況から、今回始めてオートバックスでやってもらう事にしました。

見積り時の内容でやってもらったので、2010年にディーラーで車検を受けた時と比べて、だいたい半額以下ぐらいの値段になりました。

その代りブレーキ液やエアコンフィルターなんかも変えていないので、それなりに問題を抱えてはいます。

引き渡しの段階で、ブレーキ液やパワステ液の交換なんかを勧めてきて断わったのですが、車検の時にすれば手間が省けるだろうにと思った事を覚えています。

渡された書類にある、後で交換する事になるバッテリーやタイヤの溝なんかのデータは、次のようになっています。

  • 走行距離:約8万9.0千キロ
  • タイヤ溝の深さ
    • 前輪(左、右):(3.0, 2.9)[mm]
    • 後輪(左、右):(3.8, 3.5)[mm]
  • バッテリー診断結果
    • 容量:79%
    • 電圧:12.9V
  • 交換したオイル:マックスファクトリー SM0W20

夏休みの時期などは遠出をしたりした経緯もあって、若干走っていますが、だいたい一ヶ月毎に1,000kmぐらいづつ走っています。

車検から3ヶ月ほど経って、既に3,500kmほど走っていた事もあって、アクセルを踏んでも加速が悪く、坂道を上る時にはかなり大変な多いを感じるようになりました。

2012年9月:オイル交換 + 車検後の無料点検

このまま半年経過するまで待てなかったので、少し暑さがやわらいできた頃に無料点検のハガキが届いた事もあって、ついでにエンジンオイルを交換をする事に決めました。

普段は値段的に安い部分合成油で、オートバックスのPBオイル(Vintage 0W20)を入れていますが、 今回は同じ値段でセール対象になっていた Castrol の Magnatec 0W20 に交換しました。

交換してからは、夏の暑さが続く日でしたが、以前とは見違えるほど、エンジンが回転してダイレクトに前に進む感じが得られる印象です。 まぁエンジンオイルを変更するといつも感じる事ではありますが、これがどれくらい続くのかなぁ、と思っています。 Magnatec自体はいつもと同じ部分合成油なので、3,000km走った時にどんな感じか確認しようと思います。

結果として、特にエンジンに問題があるわけでもなく、車検の時に自動的に交換されたMax Factoryエンジンオイルの劣化の度合いがあまりにもひどかったという事になるんですが、3,000km走った後の印象が良ければ、次はPBオイルではなくて、5千円出しても全合成油系のオイルにしようかなと思います。

さて、このエンジンオイルの交換時に、改めてタイヤとパワステ液とバッテリーの交換を勧められました。 パワステの動きに問題は感じていなかったのと、バッテリーは75%, 12.9Vという事だったので、ここはスルーしました。

タイヤはスリップマーク(1.6mm)が出ていたので、さすがにやばいなぁ、という事で他のお店も周って交換する事に決めました。

ただ、車検から半年、3,5000キロ走って、1.5mmも削れたのか、ちょっと疑問ではありますが、 タイヤ自体が劣化していたはずで、むしろよくバーストしなかったと思います。

2012年9月:タイヤの交換

せっぱつまった状況でしたが、1週間ほど後に再びオートバックスのお世話になりました。 PBタイヤにしたい旨を店員に伝えると、扁平率55%のタイヤはありませんよと即答されましたが、価格表のファイルを見ながら、韓国製タイヤの次にPBタイヤをあたかも先ほどの会話がなかったかのように勧められました。

ちなみに、このデミオに適合するタイヤは二種類あります。

  • 175/65R14
  • 185/55R15

手持ちのタイヤは両方あって、14インチは買った時におまけで付けてもらったスタッドレスにアルミホイール、15インチは購入した時のまま純正アルミホイールに夏タイヤ、として使っています。

今回は15インチの方のタイヤを交換する事にしました。タイヤ舘などもいったのですが、目当てだったSNEAKERがモデルチェンジ前らしく生産中止かつ在庫なし、で残念でした。値段的には購入したオートバックスのPBタイヤとそれほど違わず問題なかったんですけどね。

そんなこんなで、在庫があった事もあって、オートバックスのPBタイヤ(MAXGRADE SIII)にしました。 いままでが悪すぎたため、新品だけあってびっくりするくらいに、取り回しが楽になりました。

以前は、カーブを曲る時にハンドルの戻りがつっかかるように感じたり、ロードノイズをじかに感じていましたが、タイヤを交換してからは、カーブを曲った時にハンドルから手を離すとスルスルと戻るし、静かという印象はないですけれど、エコタイヤというよりはコンフォートタイプといっているだけの事はあって、乗り心地はかなり改善されました。

直進性が増したとは感じませんし、タイヤの経年劣化がどれくらいのスピードで起るのか分かりませんが、想像以上に良くなっていると感じています。

この交換後に片道40〜70kmほどを合計400kmほど走りましたが、いままで良くて13km/lぐらいだった燃費が17km/lになり、オイル交換の直後と比べてもタイヤの交換が大きく寄与した結果だと思っています。

2012年9月:バッテリーの交換

マニュアルによると、UA-DY3Rに適合は46B24Lです。

交換前に使っていたものは2009年5月に、やはりオートバックスで購入しています。 オートバックスで46B24LというとPBの中でも水色の一番安いもので、バッテリー補充液のメンテナンスが必要なモデルです。

4万キロ/2年保障の製品を、3年半ぐらい使っていたわけですが、硫酸銅の結晶がマイナスターミナルに付着していて、4月に79%, 9月に75%となっていた事もあって、真冬に70%を切るのは自殺行為に思えたので交換する事にしました。

メンテナンスしながら短いサイクルでバッテリーを交換すればトータルでのコストは下がりそうですが、 自分には向かない事がよく分かったので、今回はメンテナンスフリーバッテリーが候補です。

55B24Lにしようかなと思っていたのですが、10%引きになる事もあって、65B24Lにしました。 どうせ4年もすれば交換とは思っているので、どっちでも良かったのですが、違いが何か実感できるかなぁぐらいの期待を込めています。

オートバックスで作業してもらった時に、時計やらガソリン交換してからのトリップメーターやらのメモリが消えてしまったのですが、まぁすぐに自分で戻せるし、前の値もだいたい覚えているので、気にしません。

ただ「影響は時計だけですが、すみません。」、と謝られただけで、何かダメージを与えるような事をしたとは説明されていませんが、 どうやってバッテリー交換時にメモリ内容を消したのか、けっこう気になったりしてます。

結晶を落す時に端子に繋っていた線が外れてしまったのかなぁ。

バッテリーを交換して音が良くなるものとは思いませんが、ヘッドライトは確実に明るくなったし、時計の時刻が狂う現象も軽減されるといいなぁというところです。

さいごに

オートバックスのPBエンジンオイルは、いまの時期はネットショップサイトから消えていて、店頭でも在庫らしきものはありましたけれど、山のようには積まれていませんでした。

また、PBバッテリーは、ブランド立ち上げ当初はパナソニックのOEMだった記述がありますが、交換したものはBOSCHのOEMでした。 ひょっとしたら容量別で混在しているのかもしれませんが、よく分かりません。

始めてディーラー以外で車検を受けましたが、中古車とはいえやっぱり定期点検と車検はディーラーで受けたいなぁというのが現時点での感想です。

自分で作業をするようになったわけではありませんが、今回はいろいろと勉強する機会になりました。

2012/09/03

BIND9 (named) が大量の"tmp-*"ファイルを掃き出した理由 - DNSSEC

気がついたら自分で管理しているDNSサーバーの/etc/bindにtmp-で始まるファイルが大量に掃き出されていました。

...
-rw-r--r--  1 bind bind  5267 Sep  3 08:35 tmp-0r4ItfPTMx
-rw-r--r--  1 bind bind  5267 Sep  3 08:47 tmp-s8Wik7LX69
-rw-r--r--  1 bind bind  5267 Sep  3 09:01 tmp-az3Ek2NMn7
...

結論:対応方法

DDNSやMaster/Slave構成などで.jnlファイルを使用しているBINDプロセスは定期的にジャーナルファイルの内容を書き出すので、zoneセクションでfileに指定しているファイルにも書き込み権限が必要になります。

解決策は簡単で、.jnlファイルとそれに対応するファイルの両方について、namedを実行しているbindユーザーの所有にすればエラーはなくなります。

変更後しばらく観察していると、tmp-*が作成される代りにzoneファイルが更新される事が分かるはずです。

本題:原因について

どうしてこういう事が発生したのかが問題です。 サーバーを構築した時は正しく権限を設定していて、動作も確認していました。

ここからの大前提として設定ファイルを格納している/etc/bindディレクトリは、root:bindの所有でtビットが立っています。

$ ls -ld /etc/bind
drwxrwsr-t 2 root bind 20480 Sep  3 11:59 /etc/bind

この設定によってnamedプロセスが上書きして欲くないファイルはroot:bindの所有として保護しています。 また動的に生成するファイルはbind:bindの所有となります。

DNSSEC固有の事情

zoneファイルの権限が変更された原因は、DNSSECを有効にしていたことに起因します。

エントリや鍵ファイルの更新のため定期的にzonesignerコマンドを実行していたのですが、 ここに少しばかり問題がありました。

操作の対象となるのは次の3つのファイルです。

対象ファイル

-rw-r----- 1 root bind  900 Sep  3 11:59 db.yadiary.net
-rw-r--r-- 1 bind bind 9731 Sep  3 11:59 db.yadiary.net.signed
-rw-r--r-- 1 bind bind 3489 Aug 30 05:23 db.yadiary.net.signed.jnl

db.yadiary.netは署名前のオリジナルファイルで、勝手に上書きされても困るので、rootの所有となっています。 この状態でzonesignerを実行すると次のようにパーミッションが変化します。

zonesigner実行後

-rw-r--r-- 1 root bind 9731 Sep  3 11:59 db.yadiary.net.signed

そうしたらsudoをbindユーザーで実行したらどうなるのでしょう。

sudo -u bind zonesigner -zone yadiary.net db.yadiary.net

zonesigner実行時のエラー

unable to open zone file db.yadiary.net at /usr/sbin/zonesigner line 841, <KEYREC> line 73.

namedが乗っ取られてコードを実行された場合を考えれば、DNSSECの署名に必要な鍵ファイルはbindユーザーに見られたくありません。 というわけでbindユーザーでは署名に必要なファイルが見つからないのでエラーとなって、これに直接対応する事はやめました。

とりあえずの対応策

zonesignerを実行するときのコマンドラインのメモの最後に、; chown bind db.yadiary.net.signedを加えました。

でも全体としてバランスが悪いので、他の解決策が用意されていないか探してみようと考えています。

まとめ

いろいろ調べる途中でみた設定例によると、/etc/bindディレクトリにスティッキービット(t)を立てずに紹介しているところもありました。

ファイルのセキュリティについては、chroot環境について触れているものもありました。 しかし、DNSサーバーとして不適切なエントリの追加を防止するためには、chrootだけでは十分ではありません。

namedのファイルパーミッションはいろいろ悩ましいところではあるので、少し試行錯誤してみたいと思います。

2012/08/16

さようならPS3 Linux。

いろいろ思うところがあったけれど、Playstaion 3のファームウェアを3.15から最新版の4.21にする事にしました。

まぁ普通の人なら当然するだけの事をしたんですが、これでHDDの中で10GBほどの領域を占有しているLinux領域にアクセスできなくなるわけです。

ときどき使っていたけれど、本気で使っていたわけじゃありません。 これからCellのプログラミングをする事もないだろうから、Other OSのサポートがなくなるのはしかたがなく受け入れる事にしました。

べつにこれで何か経済的に損失があるというわけでもないですし、ダウングレードしないといろいろできないのは納得したわけじゃないけれど、得られるメリットとのトレードオフなわけで、いま頃になってようやく分岐点を越えて決断に至った次第です。

まぁ自分に非がないのに当時の社長御自身がセールスポイントとしていた機能が削られた分のお金は返金して欲しいなぁとは思います。

2012/08/11

つめこみ教育からゆとり教育への転換で変わらなければいけなかったこと

いわゆる「ゆとり教育」は「つめこみ教育」からの反動として生まれたといわれています。 最近は多少の反省もあって学力低下を憂いて教科の内容は見直しが進められているらしい。

確かに、つめこみ教育自体には反対だけれど、それは落ちこぼれた場合の救済策がほとんどない事と、 学習塾や家庭教師などの学習環境による経済格差が学力に反映される可能性があるからです。

結論からいえば、正社員を20才前後で買って65才で退職金を付けてリリースするという制度を止めて、人材の流動性を高める事が必要だと考えています。そのためには労働者を解雇しやすくする仕組みがあって、その反面、労働者はより頻繁に雇用されて能力を示す機会を与えられる仕組みの構築が行なわれるべきだ、という内容になっています。

ゆとり教育でかわったこと

つめこみ教育は、6〜18才の期間限定でのスキル(知識・能力)獲得ゲーム(猶予期間付き)といえると思います。

最近は、経済力がある家庭は無駄にゲーム機を与えたり、子供を甘えさせたりする場合もあって、必ずしも環境の中で優位とはいえないし、教科の内容が簡単になれば、それなりに要求されるハードルが下がって子供の負担は軽減さえるかもしれません。

しかし、その本質を変えないままにゲームのルールを緩めてしまうと、その枠内でチート的行為を行なおうとするのは当然の帰結に思えます。 ルールがキツすぎるのが問題で、柔軟性を欠いたことでいろいろな問題を起した社会の反省が、ゆとり教育であったはずです。

ゆとり教育は「教科の時間・内容を減らし、経験重視型の教育方針」への転換だといわれています。 なにを経験させたいのかさっぱり分かりませんが、現実にはよりよい経験ができる学校の人気が出るといった学校間格差を生んだだけのように思えます。

この期間、日本の社会では正社員につけない人間は非正規雇用という位置付けを固定的に与えられる結果となり、よりおちこぼれは救済されない社会へと変貌を遂げました。

ゆとり教育においても、18才までにどれいくらいのスキルを身につけたかで判断がなされ、おちこぼれを救済するという結果にはつながっていないと思われます。

強いていうなら、ハードルが下がった事によって「(やらないけど)やればできるはず」と思い込む事が、より現実的になった、とはいえるのかもしれません。 「(やらないけど)やればできるはず」と思い込めれば、将来を悲観する事はなくなるのかもしれません。

社会で必要なのは理想と現実とのギャップを埋める能力

社会で働くというと、ルーチンワークで成り立つ単純労働もありますが、普通はそうやって作ったものを売る方法から考えなければいけないので、どんな仕事でも「現在達成できていない事」を「どうにかして達成させる能力」が求められる事になります。「空のケースがある状態」から「製品でケースが埋まった状態にする能力」ぐらいに考えれば、納得できるでしょうか。

就職活動というものは、「職がない状態」から「どうにかして職を得る能力」を示すものといえるかもしれません。

つまり、課題解決が基本的な仕事の単位と考えます。

以前は課題を解決するために「知っている人に聞けばいい」という状況が一般だったとすれば、いわゆる人脈が課題を解決する能力に等しかった時代もあったのかもしれません。

いまの社会は、いろいろ複雑で、とくにIT系では「課題を作って解決する能力」がないと、どんなにプログラミングが上手でも、他人の作ったライブラリの範囲から逃れる事ができなくて、その一生を下働きで終る事になるかもしれません。

プログラマーですがPHP以外の言語では仕事ができません、とかっていう信じられない状態が普通にあったりするわけです。

いままで書いてきたことは、環境要因もあるので、一概にその個人の能力のみを表現しているわけでもありませんが、それついて考慮するのは、ここの目的ではありません。

とにかく、社会で求められている能力というものは、18才までに習得するはずのスキルとは直接的には関係がないように思えるわけです。

つめこみ教育からの脱却で目指すべきだった社会の方向性

理想を「頑張った人が報われる」社会だとした場合に、一度はドロップアウトしても、後から十分なスキルを習得すれば、就職口があるようなケースが良いとされています。

これを勘違いして努力しなくても仕事がある状態が良いと思って、低賃金かつ単純労働な仕事をやたら誘致する自治体があったりするわけですが、そんな仕事は中国や東南アジアに移ってしまう

つまり常にその地域の特色を活かす必要があるんですが、そういう特色のある仕事をサポートするために、周辺に、単純労働的な仕事が存在するはずなのです。

核となる要素を作らずに、本来、その周辺にあるべきものを作っても、根付かずに終るのは目に見えて明かです。

ゆとり教育が本来目指すべきだったのは、教科の内容を減らす事自体が悪いとは思いませんが、課題を解決する能力の育成や、一度は落ちこぼれても救済可能な環境だと思います。

おちこぼれた人は、会社が準備した6畳一間のワンルームアパートに入って、毎日工場のどこかでその日限りの仕事をするような人材派遣会社に流れたりするわけで、中国や東南アジアからの勤勉な労働者に置き換えられる将来を憂う事になる可能性があります。

そういう最終的な出口に恐怖を感じるとすれば、つめこみ教育時代と何も変わっていないわけで、解決するべき問題は少なくとも教科内容の削減ではなかったと思います。そこは重要ではない。

人生を60年ぐらいのスパンで捉えて、各段階で一度はドロップアウトした人をどうやって再教育するのか、そういう意欲をどうやって持たせるのかが重要なはずです。

これは学校だけで解決するものでもないけれど、18才までの能力で判断することから、どんな年代、段階からでも社会参加できるような仕組みが一番良いもののはずです。

柔軟性を欠いたままの社会構造

一度、正社員として雇用されるとキャリアアップを除いて、なかなか会社を辞めるという事ができない環境があります。 また、基本的に会社は(正当な理由のハードルが高いため)リストラができず、お金を積んだり、単純に窓のない部屋に移動させるとかいう手法を使って、人員の整理を図る事になります。

何かがおかしいんですが、経済が好調な時代には表面化しなかっただけの矛盾を、「これで成功してきた」という間違った成功体験で正当化してしまう事象が発生しています。

ちなみに、主観的な体験を否定はしませんが、人間は脳の構造から、関連のない事象を結びつけて理解したり、本来みえていないものや聞いていないものを体験したと認識できる能力を持っていることもまた事実です。

話を戻すと、労働者の流動性と市場を作っていく事が肝要ではないでしょうか。

労働者も賃金が与えられる(べき)ものと思っているところがあるので、お互い様ですが、陰湿ないじめなしに解雇された方がすっきりするし、会社に20才からずっといる社員で占められる事がなくなれば、本来いうべき事をいう事もできるでしょう。

ただ現状を一気に解決する方法はないので、難しいのですが、少なくとも目指すべき方向性は政治家が示して、法律などのサポートを行なうべきです。

ゆとり教育が目的を達成していない理由

根本的な問題は、目指すべき方向性を誰も理解していない点にあります。あるいは人によって目標が違うというべきでしょうか。 ぼやんとした目標はあるんですが、ゆとり教育の成否が評価できない理由は、期間的な問題もありますが、目標がそもそもないので、何を指標とするのかから議論が始まるからだとも思います。

もっと柔軟な、人に優しい社会にするのだと思っていたのですが、年寄がシステムを変えてくれないので柔軟性はないままで、若くて優しい人たちが(無関心によって)残酷な社会を作っている様子が普通に怖いです。

例えば、教師は自分の給与が社会から与えられていると認識して、将来の雇用主を育てるために、自立して使える人材を作る事に、もっと真剣になるべきです。 そう考えれば、そのためには教科の時間も足りないし、18才までに年刻みで教える事の難しさが見えてくると思います。

そういった必要から社会を変えていく事が必要で、イデオロギーや教条的な思い込みを原動力とする変化はうまくいかないでしょう。

さいごに

社会に出て学んだ事は「現実とのギャップを埋めるために与えられた環境で全力を尽す」という事です。

この社会は不公正で、各人は、ハンディの付いたそれぞれ違うスタート地点から同じゴールを目指すマラソンをしているようなものです。

理想が与えられるべきものだなどという考えは、社会主義を通り越してカルト的な信仰です。 とはいえ、それを羨んでも何も良い事は起きません。

違いはあるし、各人は違う人生を歩いてきた事を認め合って、それぞれが違うスタート地点に立って協調して動く事ができなければ、より大きな仕事を達成する事はできません。

気に入らないからと、足をひっぱる事は全体を停滞させるだけで、より大きな問題を引き起しています。

排他的な群の文化では、この時代において「全滅 or (ひとときの)繁栄」となる事は自明に思えます。

基本的にこれらの柔軟性を欠いている原因は「恐怖心」です。 人間の可能性を潰して、萎縮させて低い能力しか発揮できない原因は、この恐怖心にあります。

学校でのいじめが問題になっていますが、いじめる側がいじめられる側にならないためには、当事者よりも周囲に徹底的に力を誇示する必要があって、それによってエスカレートする宿命を負っているように感じます。

つきつめていけば、この社会が克服しなければいけないのはこの恐怖心ですが、戦前からこの社会に影を落しているものでもあり、この事を認識できるかどうかから試されているように思えてなりません。

日本人が恐怖心から逃れるために作ってきた神話や伝承、社会基盤といった代償的行為を思えば、ストレートに現状を認識することはとても悲観的に思えます。

これを克服するためには、各個人がかなり分析的にこの社会や現代社会の有り様を捉える必要があると思うのですが、哲学のないゆとり教育やそれに続く教育改革がこれに寄与する事ができるのか、この観点ではかなり懐疑的です。

外部の目や、他人の意見に左右されたり、過去の経験や誰かに付いていけばいい、そんな程度の認識ではだめで、もっとあるべき論から議論ができなければいけないのですが、議論にならないのは教育のシステムにも原因があるのでしょう。

何をやっても、全体を包括するような哲学やゴールがはっきりしない、当事者に認識されない間はうまくいかないでしょう。日本のシステムの悪いところは、その必要性や目指すところを「聞くな、感じろ」と強制するところにもあるように思えます。

これまで暗黙のうちに語られなかった事を、シンプルに口に出して明示的に話すだけで、ずいぶん変わる気がするんですけどね。あまりにも黒い理由があり過ぎて口に出せないんでしょうか。でもそれはもっと大きな別の問題なんですよ。

Postscript: 書きながらフォーカスを失なった短文たち

なぜか地方の経済は地方公務員の収入で支えられているところがあります。 周り周っていくお金ですが、基本的には、その収入は公務員以外の産業から埋まれた税金で賄っているはずです。お札を刷れば別ですけれど。

なぜか地方経済というエコシステムの頂点に公務員があるのは食物連鎖的に異常だっていう認識が普通になかったりして、地方公務員が勝ち組みたいな受け止め方をされたりします。

日本はユーロ危機にある現代のギリシャみたいにはならないと思いますが、19世紀に勃興したポルトガルの大航海時代後みたいな位置付けにはなると思います。いまのポルトガルには失礼ですが、将来の日本の姿はここら辺じゃないでしょうか。

ここでは、ゆとり教育が失敗だとはいっていません。 柔軟な人材を育む事に成功している可能性はあります。

しかし、うまくいったとしても、その育んだ人材を受け入れる社会システムが変化しないのであれば、前段のみを変革して、問題のある後段をそのまま維持しようとしているシステムの歪みが次の問題になるでしょう。

学力の低下を持って余りあるメリットが享受できれば良いのですが、それも難しそうです。

震災の後もここまで必死になってバブル期に最大限機能した日本のシステムを維持しつづけようとするとは思いませんでした。 今度こそ変わるかなって思ったんですけどね。意識の問題ではありますが、残念です。

2012/07/29

Linux環境でのbin/neo4j installコマンドの挙動

グラフDBの一つであるNeo4Jをいろいろ試す中で、 サーバースクリプトの引数にinstallオプションがある事に気がつきました。

いろいろ勝手にやってくれるのは便利なのですが、 それはそれで動きが分からないと不便なので、調べたことをメモしておきます。

環境はUbuntu 12.04 LTS 64bit版、JVMはOracle版のJava 1.6.0_33です。

マニュアルの説明

セクション18.1 Server InstallationではWindows, Linux, MacOSX毎の説明が書かれています。 ここではLinux用の記述について説明しています。

まず、導入から起動までの基本的なステップとして次のように書かれています。

  • $ bin/neo4j install
  • $ service neo4j-service status
  • $ service neo4j-service start

そして、導入ステップの中で次のような情報を入力や作業が行なわれるとされています。

  • ユーザー名 (デフォルトは"neo4j") の入力
  • ユーザーが存在しない場合の作成をスクリプトが行なう
  • dataディレクトリ以下のファイル所有者をユーザー名に変更

さらにセクション 22.9 にあるLinux用のパフォーマンスチューニングを実施するよう勧めています。

スクリプトの挙動

サービスとして起動させるために、何をするのかの説明はないので、スクリプトを普通に眺めていきます。

bin/neo4jスクリプトの中では、installservice()シェル関数の中で処理が行なわれています。

1. OS、JVMの判別

bin/utilsで定義されているdetectos, findjavaシェル関数でそれぞれOSとJVMの判別が行なわれています。

detectosの中では$ uname -sコマンドの出力をベースにDIST_OS変数に"linux"(solaris,cygwinなどの場合には正規化した値)が入るようになっています。

findjava関数はいろいろ泥くさい処理をしていて、例えばgentoo linuxの場合には$ java-config --jre-homeの出力をJAVA_HOME変数に設定するなどの処理をしています。 最終的にはJAVACMD変数にjavaコマンドのパスを格納して、それを後続の処理で使うようになっています。

2. /etc/init.d/neo4j-serviceファイルの配置

/etc/init.dディレクトリがある場合には、/etc/init.d/neo4j-serviceファイルはbin/neo4jへのシンボリックリンクとして作成されます。

その後で、$ update-rc.d neo4j-service defaults$ chown -R neo4j: data confのコマンドが実行されています。ユーザー名neo4jは適宜入力された名前に変更されて、ドキュメントにある通りの設定がされています。

グループ名の指定がないのがポイントと、いくつかドキュメントにない暗黙の設定があります。

インストール処理の自動化について

falseに設定されているHEADLESS変数は質問があるところで参照されて、もしtrueだと全ての設問にy(yes)を選択した挙動になります。 これを使う場合、外部から変数にtrueを設定する仕組みはないので、スクリプト自体を上書きする必要があります。

またwrapper_user変数は、作成するユーザー名の指定があって、通常は$ id -unから実行時のUIDの値が入りますが、$ sudo env wrapper_user=neo4jsrvr bin/neo4j installのようにするとコマンドラインからデフォルトのユーザーIDが指定できます。

この他にenvコマンドで同様に操作できるパラメータには次の変数がありました。

  • org_neo4j_server_webserver_port - ポート番号 (既存プロセスのステータス確認に使用)
  • wrapper_user - サーバーを実行するユーザーID
  • wrapper_ntservice_name - /etc/init.d/直下に作成するシンボリックリンクのファイル名 (serviceコマンドの引数に指定する名前)

ユーザーIDと/etc/init.d以下に配置されるファイル名の変更であれば、手動でHEADLESS=falseをtrueに変更して、envコマンドの引数でパラメータを指定すれば、対話的な処理なしにインストール作業を進めることができます。

org_neo4j_server_webserver_port は導入時のプロセスチェックにのみ使われて、confファイルは変更されません。 conf/neo4j-server.propertiesファイルを直接変更する必要があります。

上書きされるファイルについて

変更されたユーザー名については、conf/neo4j-wrapper.propertiesファイルが変更されます。 これによって$ sudo /etc/init.d/neo4j-service startのように実行すると、自動的に指定したユーザーIDでneo4jプロセスが起動します。

これ以外のパラメータは、confファイルを変更する必要があります。

さいごに

installオプションを実行すると、ファイルが展開されているディレクトリがそのまま使われます。 マニュアルにもありますが、/opt以下などにtar.gzを展開する必要があるでしょう。

その反面 data, conf ディレクトリは展開されたディレクトリに含まれているので、本番で運用するのであれば、これらデータ本体の配置は考慮する必要があります。

アップグレード時は、初回起動時にデータベース構造の更新処理が走る事になっているので、tar.gzを展開して、conf, dataを入れ替えるか、シンボリックリンクで適当なディレクトリを参照するようにしてあげるぐらいで良いようです。

その他に本番運用にはクラスタリングやオンラインバックアップなど、まだ考慮しなければいけない要素があるので、これから検証していく予定です。

2012/07/25

Ubuntu 12.04 LTSでmrubyを試してみた

組み込み用途に特化した軽量版Rubyの実装として、mrubyがリリースされています。

これの使いどころを考えてみたのですが、Tech-Onのインタビュー記事では、一例としてC言語で記述が難しい処理をmrubyにオフロードするとありました。

個人的には静的なコードはC言語で書くだろうと考えていて、動的なランタイムの変更をmrubyでユーザーに開放するんだろうなと思っています。 具体的には設定ファイルの記述やプラグインをmrubyスクリプトで作成することになるでしょう。

この使い方だけならluaなんかでも良いんですが、操作可能な要素をクラス単位でまとめる事ができるのは、実際のところ名前空間があるかどうかの違いぐらいしかありませんが、実用上の使い勝手は表面的な違い以上のものがあると感じています。

まぁ、ここら辺はいまのところ信念の問題なのですが、今回はmrubyを使って設定要素をクラスにまとめる場合を想定して、ちょっとしたサンプルを作ってみる事にしました。

テストした環境とmrubyのバージョンは次のようになっています。 特定のmrubyのバージョンには依存していないと思いますが、Webで調べたコードの中の関数の引数の取り方が違うものもあったので、念のため書いておきます。

  • OS: Ubuntu 12.04 LTS x86_64版
  • コンパイラ: gcc 4.6.3
  • mruby git commit: 8b6f6faf1e3771c04a1e2a58b1bbc84fe7d5c1e2

ちなみに、commit:のハッシュを使って、このテストしたmrubyと同じソースコードをbranch名"20120723.083824"で入手するには次のようにします。

$ git clone https://github.com/mruby/mruby.git
$ git checkout -b 20120723.083824 8b6f6faf1e3771c04a1e2a58b1bbc84fe7d5c1e2

mrubyのバージョンや日付によってメソッドの呼び出しシグネチャが違う事がありますが、だいたいそのまま読めると思います。

参考資料

参考にしたのは、主に手探りでおぼえるmruby その1:クラスを定義する、メソッドを定義するで、RICOHの方がまとめた記事もありましたがコードがGPLだったりするので、考え方などを参考にするに留めています。

とりあえずmrubyについては使い方よりも、生み出された背景や、中間コンパイラとしての挙動について、mrbcコマンドの動きを抑えておくのがお勧めです。

実証実験に参加した各種企業が公開しているドキュメントが参考になるでしょう。

mrubyのサイズ

x86_64環境でライブラリをenv COMPILE_MODE=release makeで作成すると、libmruby.a, libmruby_core.aの各ファイルはそれぞれ790KB前後のサイズになります。(strip後は470KB前後)

ruby-1.9.3-p194のコードをビルドすると、librucy-static.aのサイズはstrip後も2.1MBほどになります。

ライブラリの全てのシンボルとリンクする分けでもないので、静的にリンクした実行ファイルのサイズはもっと小さくなるはずですですが、CRubyのライブラリサイズの大きさは少し大き過ぎるなぁと感じるところです。

mrubyに求める機能

既に説明していますが、アプリケーションが提供するクラスを主体として、そのオブジェクトを組み込みメソッドを組み合せて操作できるところがメリットだろうと思っています。

普通のアプリケーションでは設定ファイルに固定文字列しか書けないのが普通ですが、これはセキュアだとは思うものの、記述できる語彙が少な過ぎるとも感じています。

luaはちょっとローレベルに過ぎる印象があってオブジェクト的に構造体の中に値と操作用関数をまとめる事はできますが、その環境をセットアップするのは少し面倒な印象です。

今回のゴール

mrubyを通してユーザーは日時情報や外部ファイルに記述されている内容などの情報に応じて振舞いを変化させる事ができるようになるといいなぁというわけですが、サンプルなので今回の範囲は一組のsetter/getterをラップしてみようと思います。

具体的に何をするのか、図にしてみた

図にしてみると次のようなプログラムを作成してみる事になります。

C言語レベルではConfigクラスに相当する設定可能な要素を構造体で定義します。

アプリからは直接構造体を操作せず、操作用のsetter/getterメソッドをConfigControllerクラスとしてまとめています。mrubyからはWrapperクラスを通してConfigControllerファイルでまとめているメソッドにアクセスしています。

実際のコード

いろいろ大風呂敷を広げましたが、ここから先はしょぼいコードが並びます。

ただGoogleで検索にヒットしたページは、やはりmrubyにコードをオフロードする仕組みやmrbcを使って中間コードを実行する方法に特化していたりしたので、もうちょっと泥くさい使い方を考えてみました。

conf.h, conf.c

実際にはConfとConfControllerに相当する機能はconf.c, conf.hにまとめました。 でも何かするわけではなくて、デバッグモードかどうかを動的に切り替えられるというだけです。

内容にはまったく意味がないんですが、気にしないでください。

conf.hファイル

#ifndef YA_CONF_H
#define YA_CONF_H 1

#include <mruby.h>

struct _conf {
  int is_debug;
} conf;

int is_debug(void);
void set_is_debug(int d);

#endif

conf.cファイル

#include "conf.h"
int is_debug(void) {
  return conf.is_debug;
}
void set_is_debug(int d) {
  conf.is_debug = d;
}
wrapper.h, wrapper.c

wrapper.hファイル

#ifndef YA_WRAP_H
#define YA_WRAP_H 1

#include <mruby.h>

struct RClass* yamrb_class;

mrb_state* yamrb_init(void);
mrb_value yamrb_is_debug(mrb_state* mrb, mrb_value self);
mrb_value yamrb_is_debug_equal(mrb_state* mrb, mrb_value self);

#endif

wrapper.cファイル

#include "conf.h"
#include "wrapper.h"

#include <mruby.h>
#include <mruby/numeric.h>

mrb_state* yamrb_init() {
  mrb_state* mrb = mrb_open();
  yamrb_class = mrb_define_class(mrb, "YaConf", mrb->object_class);
  mrb_define_method(mrb, yamrb_class, "is_debug", yamrb_is_debug, ARGS_NONE());
  mrb_define_method(mrb, yamrb_class, "is_debug=", yamrb_is_debug_equal, ARGS_REQ(1));

  return mrb;
}

mrb_value yamrb_is_debug(mrb_state* mrb, mrb_value self) {
  mrb_value ret = mrb_fixnum_value(is_debug());
  return ret;
}

mrb_value yamrb_is_debug_equal(mrb_state* mrb, mrb_value self) {
  mrb_int arg_debug;
  int argc = mrb_get_args(mrb, "i", &arg_debug);
  if(argc == 1) {
    set_is_debug(arg_debug);
  }
  return self;
}
main.c
#include <stdio.h>
#include "conf.h"
#include "wrapper.h"
#include <mruby.h>
#include <mruby/proc.h>
#include <mruby/compile.h>

mrb_state *mrb;
int gen_code_num;
mrbc_context *mrbc_ctx;

void init() {
  mrb = yamrb_init();
  FILE *fp = fopen("main.rb","r");
  mrbc_ctx = mrbc_context_new(mrb);
  struct mrb_parser_state* st = mrb_parse_file(mrb, fp, mrbc_ctx);
  fclose(fp);
  gen_code_num = mrb_generate_code(mrb, st->tree);
  mrb_pool_close(st->pool);
}

int main(int argc, char** argv) {
  init();
  
  // first run
  printf("current is_debug: %d\n", is_debug());
  mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[gen_code_num]), mrb_nil_value());

  printf("current is_debug: %d\n", is_debug());
  set_is_debug(1);
  printf("new is_debug: %d\n", is_debug());

  // second run
  mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[gen_code_num]), mrb_nil_value());

  // close
  mrbc_context_free(mrb, mrbc_ctx);
  mrb_close(mrb);
}

main.rb

print "-- begin --\n"
y = YaConf.new()
p y.is_debug()
y.is_debug = 2
p y.is_debug()
print "----\n"

Makefileファイル

INC = ./src/include
LIB = ./src/lib

main: conf.o wrapper.o main.c
	gcc -std=gnu99 -I. -I$(INC) -o main main.c conf.o wrapper.o -L$(LIB) -lmruby -lm

conf.o:	conf.c conf.h
	gcc -std=gnu99 -I. -I$(INC) -c conf.c 

wrapper.o: wrapper.c wrapper.h
	gcc -std=gnu99 -I. -I$(INC) -c wrapper.c 

気になったこと

Rubyの拡張ライブラリの知識は、いろんな意味で役に立ちますが、README.EXT.jaに対応するまとまったドキュメントがないので、いろいろ混乱するかもしれません。

FIX2INTなどの型変換マクロがない

Rubyの拡張ライブラリでは標準的なC言語の型との変換はマクロで実現していましたが、 mruby.hでは静的な関数が準備されています。

  • static mrb_value mrb_fixnum_value(mrb_int)
  • static mrb_value mrb_float_value(mrb_float)
  • などなど

mrb_intは標準のint型とtypedefされているだけで、直接に代入できます。 mrb_floatはmrbconf.hで定義されていますが、手元では8byteで通常はdouble型に紐付くようです。 文字列はmruby/string.hに定義されているような、組み込みString型用の関数を使う事になります。

mruby.hに定義されていなければ、操作対象の型に応じてmruby/以下のヘッダーファイルを眺める事になります。

mrb_generate_code()が返すint型の番号を管理する方法が欲しい

これはmrubyが準備する話しではないのですが、今回は処理が単純なのでmrbも全体で共有するような作りにしました。少し複雑になってきても、必要な都度、必要なコードをmrb_run()で呼びますが、この場合には対象のコードをmrb_generate_code()の戻り値で指定する必要がでてきます。

この管理方法が、まぁ対象のアプリに依りますが、どうなるかなぁと思っているところです。

まとめ

rubyはいろいろ機能が増えすぎて特定のバージョンとコードを密接に管理する必要があるので、mrubyはシンプルに保って欲しいなぁと思っています。まぁコンパイル時のオプションで調整することもできる部分もありますが。

C言語でアプリを組む時に機能の一部をオフロードする目的だと内蔵クラスや機能が多い方が楽になるわけですが、アプリケーションのプラグイン的にユーザーに開放する場合には、むしろ機能や組み込みクラスはないぐらいの方がセキュアになります。

mrubyは現状ミニマムで、これからスタンダード、フルといった主にクラスが追加される形のバージョンが出てくる事になっています。 とはいえ、ミニマムしかない現状でアプリの拡張をmrubyで行なおうとすると物足りない印象はあるので、前倒しでミニマウなmrubyが拡張されるような可能もあるのかなぁと思っています。 クラスセットについては、決まっているようですけれど、環境としてはまだまだ機能の追加が続いていますしね。

どうなるか、まだよくわかりませんが、アプリのコアエンジンとして安定してくれるといいなぁと思います。

2012/07/11

Google Chrome拡張を{ "manifest_version": 2 }に対応させてみる

気がついたらGoogleから「マニフェストのバージョンが2になったから対応よろしくね」、という旨のメールが届いていた。 まぁ8月中旬から新規の登録受付を中止して、年内には更新の受け付けができなくなるスケジュールが引かれていたので、 使うだけのユーザーなら影響がでるのは来年以降ですね。

今回はmanifest.jsonファイルを変更して気になった点をまとめていきます。

ちなみに変更は動作確認が終り次第、gitoriousにあるコード(Japan Postal Code Search, Open PinnedTab Link, etc...)に反映させます。

参考にしたページ

Googleのガイドはとてもまとまっているので、あまり他のサイトを調べる必要性を感じないのですが、 さすがに今回はStackOverflowなどのサイトにお世話になりました。

とはいえ、まず抑えておかなければいけないのはGoogleのmanifest.jsonの"manifest_version"の説明ページです。

ここで、全体のスケジュールと全体の変更の概要が書かれています。 しかし、ここだけでは具体的にアプリケーションにどんな変更が必要か把握するのは難しかったです。

具体的な変更作業

とりあえず作成しているOpen PinnedTab LinkJapan Postal Code Searchの2つは、比較的簡単なアプリケーションです。

このアプリケーションを修正した際に必要な修正は以下のようになりました。 まだ稼働確認が終っていないので、追加で必要なものもありそうですが、とりあえずまとめます。

HTML中にonclick, onload属性は書けなくなった

scriptタグを使ったjQueryの$(function() {...});表記などは、そのまま使えますが、(x)HTML上のタグにあるonclick="..."やonload="..."の表記を埋め込む事ができません。

基本的なコードはGoogle ChromeガイドのContent Security Policy (CSP)ページに記載されています。

onloadについは書かれていませんが、onclickと同様にdocument.addEventListener('DOMContentLoaded', function () { ... };の...部分にonloadで行なっている関数呼び出しなどをコピーすれば動きます。

ここで問題になったのはJavaScriptのコード中で、明示的に文字列としてonclick属性を追加している場合でした。

変更前のコード:文字列としてonclick属性を追加している例

...
td4_span.setAttribute("onclick","showMapImg('" + center + "')");
...

変更後のコード:addEventListenerに書き換えた例

...
td4_span.addEventListener('click', function(){showMapImg(center);});
...

元々変更後のように組むべきだったとは思います。 とはいえ文字列で解決するのは単純で簡単だったので、使っている方もいそうです。

動的なアクションはすべからくaddEventListener関数で指定する事になると思われます。

background処理をhtmlからjsファイルへ移動

manifestのbackgroundで指定する対象がhtml("page":文字列)とjavascript("scripts":文字列配列)の2つが準備されています。

これまではhtmlで記述していたので、manifest.jsonを書き換えて、そのまま流用すれば良いと思っていたのですが、どういう分けか、うまく動きませんでした。

変更前:version 1のmanifest.jsonから抜粋

...
  "background_page": "background.html",
...

元々background.htmlは本文のない、scriptタグの中に処理が記述されているだけだったので、その中身をjsスクリプトファイルに分割して、manifest.jsonの表記を書き換えました。

変更後:version 2のmanifest.jsonから抜粋

...
  "background": { 
     "scripts": ["scripts/background.js"]
  },
...

jsファイルを指定した場合でも、内部では空のhtmlファイルが生成されて組み込まれているだけのようなので、今回の挙動はおかしいと思います。 おそらくhtmlファイルのまま移行できるように作られているはずなので、早晩このワークアラウンドは不要になるでしょう。

まとめ

いろいろ書こうと思ったのですが、うまく動かない処理を発見したりして別の記事にまとめる事にして、とりあえず今回はここまでで終りです。

今回取り上げなかったのですが、外部サイトからのJavaScriptファイルのロードなどもデフォルトではできなくなっています。 オプションページのためにjQueryのコードをCDNからダウンロードしたいような場合にも対応が必要そうです。

単純なmanifest.jsonの書き換えで対応できるアプリケーションは少ないのではないでしょうか。

とはいえ、変更内容を確認する限り、機能が制限されているものはないので、既存の"manifest_verison":1アプリは全て、ちゃんと書き換えれば動くはずです。

この「ちゃんと」という部分が曲者ですが、今回の経験からは、新ルールに矯正された事によってアプリケーションのコード全体は良くなっていると思います。

Chrome拡張のAPIについてはいろいろ疑問に思うところもあったので、これから始める方々には、面倒は増えるでしょうが、よりよいコードになっているとは思います。

これがどれくらい面倒かは…、なんともいえませんが、少なくとも既存の開発者は、どこに問題があるか調べるだけで面倒そうです。

2012/07/04

SK17iをICSにして撮影した画像のファイルサイズが増えている件について

ExifPMというAndroidアプリを作成している事もあって、Xperia Mini Pro (SK17i) で撮影した写真データのExifデータをチェックしていると、ICSへのアップデート前後でファイルサイズが違っている事に気がつきました。

Exifデータを比較してみると、画素データのサンプリング方法がYCbCr420からYCbCr422へと変更されていた事が原因でした。

元々500万画素のカメラなのに、生成される写真データは700[KB]から900[KB]程度のファイルサイズだったので、 カメラアプリの不具合かなぁと思っていたのですが、これはデータをYCbCr420で処理していた事が原因でした。

色データの扱いは対象の特性によって異っていて、ディスプレイへの出力はRGBで考えて、印刷をする時にはマゼンダなどの中間色を使ったCMYKが使われます。JPEGの場合にはYCbCrやYCbCrと呼ばれるような、輝度と色差のデータを使う事になります。

基本的にはYCbCr420は縦横4ピクセルで色差情報を共有するので、横並びの2ピクセルで色差情報を共有するYCbCr422と比較してデータ量が落ちるため、画質が落ちるといわれています。

データ量が少ない分だけ劣化するのは事実ですが、まぁそんなに単純な話しでもないので、 サンプリング方法の詳しい説明はGoogleでいくつかのサイトの記述を確認されるのがお勧めです。

問題はICSにアップデートしたSK17iではカメラの画質が上がっているのだろうか、という疑問に対する答えがあるのかどうかです。

結論からいえば情報不足でよく分からないという事になるのですが、とりあえずまとめたところをメモにしておきます。

撮影した画像の見た目の違い

残念ながら同一条件で、同一被写体を撮影した画像がないので、正確な比較はできません。

撮影する時間帯はずれているので照明の条件は同じになりませんが、似たようなアングルで撮影した画像をみてみると、古い画像は絹のような質感が感じられて何か靄のようなもので覆われている印象があります。それと比較するとICS以降に撮影した画像は鮮明に感じられます。

画像をみてICSで撮影された画像かどうか区別できるものもあれば、難しいと感じるものもあって、単純に画質が向上したというのは難しいかなと感じています。

レンズの汚れや撮影環境の条件をちゃんと揃えないと、見た目の判断だけでは何ともいえないところです。

画像ファイルサイズの違い

対象によってファイルサイズは変化しますが、室内・屋外で撮影した手元の画像を眺めると、だいたいファイルサイズは次のようになっています。

  • Android 2.3: 500[KB]〜900[KB]
  • Android 4.0: 1.2[MB]〜1.8[MB]

画質の違いは別にして、ファイルサイズは確実に増えています。

APIからみた画質の変化について

自作アプリを作った時にカメラAPIから渡されたデータは、YUV420SPという形式でした。 ちょっと調べたところ、このAPIに変化はなさそうです。

Androidのカメラアプリを作って気になったのは、カメラからのRAWデータにアクセスする方法がないところでもありました。APIとしては準備されているんですが、あれが有効なデータを返すデバイスを寡聞にして聞いた事がありません。

使えたとして、大抵のデバイスでは、ほぼ確実にヒープ領域が確保できないでしょうから、処理を始める前にアプリが落ちるんでしょうけれど。

カメラ固有のAPIからデータを取得してYCbCr422データを作らない限りは、YCbCr422のデータをYUV420SPをYCbCr422に変換してもデータサイズが大きいだけで画質の向上は見込めない事になります。

現状では満足していますが、画質が向上したのかという疑問にははっきりとした答えが出せないでいます。

さいごに

SK17iを入手して比較的すぐにICSにアップデートしてしまったので、カメラアプリの挙動の違いが本当に正しいのか、設定の違いなんじゃないのか、という点について良くわかっていません。

体験として嘘は書いてませんが、十分に調査したとはいえないので、ひょっとすると勘違いが含まれているかもしれません。その際はご指摘頂ければ幸いです。

ただICSにアップデートした事自体は公開していません。Xperiaのドコモ端末の中でRAM 512MB未満はICSへのアップデートが不適とされて行なわれない事になりました。

理由がRAMサイズにあるという事ですが、それで何が問題なのかという点はよく分かっていません。

ただしばらくSK17iを使っていて、落ち着いて使っている時は良いのですが、ソフトウェアキーボードを急いで操作する時には、ひっかかるように感じる時があります。

その反面、ハードウェアキーボードを使ってメールやtwitterを使う場面で、何か問題が起こった事はないので、細かい違いを気にするのであれば、ICSへのアップデートは避けた方がいいのかもしれません。

それを除けば、いまのところICSだから問題だという現象には遭遇していないところです。 カメラの件は裏の仕組みがどうなっているか分からないので、良いのかどうか分かりませんが、基本的に新しいものが好きなのでICSにして満足しています。

2012/06/29

バージョンアップを続けるAndroidアプリでのDBスキーマの更新方法

さて、AndroidアプリケーションにはSQLite3への接続を管理するためのクラスとしてandroid.database.sqlite.SQLiteOpenHelperクラスがあります。

通常はこのクラスを継承して、子クラスを自分のアプリケーションで定義するわけですが、今回はスキーマをバージョンアップするためのonUpgradeメソッドの書き方についてです。

よくあるサンプルは直前のバージョンと自分のバージョンを比較して、バージョンアップ用のコードを定義していますが、アプリを使うユーザーが次のようなシナリオに遭遇する場合を想定しているでしょうか。

  • Version 1.0のアプリをダウンロードして使う
  • Version 2.0がリリースされるが、ユーザーは更新を行なわなかった
  • Version 3.0がリリースされ、ユーザーはアプリを更新した

アプリは毎回のバージョンアップで、ALTER TABLE ... ADD COLUMN ...を行なっているとします。

SQLiteOpenHelperクラスを紹介するWebサイトはいろいろあったのですが、こういう使い方を説明しているところはなかったのでまとめる事にしました。

まぁ良く考えれば分かる事ですが、いまのところの自分のベストプラクティスをメモしておきます。

解決するべき課題

前提として毎回ユーザーはアプリを更新しない、 Version 1.0からVersion 20.0へアップグレードしても、アプリケーションの内部データは一貫性を保ちたい、という要求があるとします。

これ自体は日本的というか、こういうのは諦めて、onUpgradeでDROP TABLE 〜 CREATE TABLEを行なってデータを初期化してしまうのも、まま、見ることです。

ここでは敢えて、この課題を解決する方法を目指します。

定石1:バージョン毎にスキーマやデータを変更するメソッドを定義する

DBのバージョンは1から始まりますが、この時のDB定義はonCreate(SQLiteDatabase db)メソッドの中でdbオブジェクトにSQLを発行して定義します。

バージョン2からは例えば、次のようなメソッドを用意してあげます。 これは直前のバージョン1からのアップグレードだけを前提にしています。

ExifPMで使用しているversion 2、3用メソッドの例

    private void updatedb2(SQLiteDatabase db) {
        Log.d(this.toString(), "upgrading db 1 to 2");
        StringBuilder sql = new StringBuilder();
        ...
        db.execSQL(sql.toString());
    }
    private void updatedb3(SQLiteDatabase db) {
        ...
    }

定石2:onCreate、onUpgradeメソッドに一度記述した内容は消してはいけない

もちろんこれは内容を変更しないという意味ではありません。

定石1で作成したバージョン毎のメソッドを追記する操作だけを許可するというルールです。

ExifPMで使用しているonUpgradeメソッドの全体


    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if(oldVersion < 2 && 2 <= newVersion) {
            updatedb2(db);
        }
        if(oldVersion < 3 && 3 <= newVersion) {
            updatedb3(db);
        }
    }

よくみる例はここで、if (oldVersion == 1 && newVersion == 2) {...}みたいにしていますが、(oldVersion, newVersion) = (1,3)の組み合せもあるわけで、まぁそれは破綻するわけです。

ちなみにonCreateメソッドの方は初期化時に1回しか呼ばれないためもっと簡単です。

    @Override
    public void onCreate(SQLiteDatabase db) {
         ...
         updatedb2(db);
         updatedb3(db);
    }

念のためにコンストラクタの書き方

SQLiteOpenHelperのサブクラスとしてSQLDBHelperというクラスを定義していて、この中で親クラスのコンストラクタを呼び出し、DBバージョンを指定しています。

コンスタクタの例


    public SQLDBHelper(Context context) {
        super(context, "appdb", null, 3);
    }

実際にはバージョン番号はアプリケーション全体で定数を管理しているクラスの中でfinal static intで定義されていますが、こんな感じでSQLDBHelperを使う側はDBバージョンを意識する必要がなくなっています。

さいごに

この方法でDBバージョン Nに対して、N-1との差分を記述していって、場合によってはデータメンテナンスも行ないます。

まぁ初期化した方が楽だったりもするんですが、ユーザーデータをできるだけ開発側の理由で、消したくないという思いもあるので、こういう方法を採用しています。

あんまりこういう事が検索に引っかからないのは、もっと良い方法があるからなのか、かなり気になっています。 なにか理由があるのかなぁ…。

リーダブルコードを読んでみて気になったこと

オライリー(O'Reilly)から出版されたリーダブルコードについてです。 ほとんどいいがかりみたいですが、読んで気になったところをメモしておきます。

これから文句みたいなことを書きますが、この本はいろいろな発見があって、Web専業といった特定の業務に特化していて、いろいろなシステムに触れる機会が少ないような方には特にお勧めです。

最近Androidアプリを作ってJavaばかり触っているので、特にこの観点から気になった点をまとめました。

「関数の戻り値にretvalを使うな」について

型のないいわゆるLLと呼ばれるようなスクリプト系言語を扱う場合には、tmp/retvalのような名称は使うべきではないと強く思います。

しかし型のある言語の場合には、メソッドの先頭で戻り値のオブジェクトを格納する変数を用意しておき、最後にreturn ret;とするのは悪い習慣だとは思いません。

問題なのはメソッドの戻り値になるオブジェクトを格納する変数を、メソッドの途中に宣言をする事です。 読み手にゴールを提示せずに書き始め、そのゴールを途中にそっと忍び込ませるような真似は、決してするべきではないと思います。

C言語のように全ての変数をメソッド/スコープの先頭で宣言するべきとは思いませんが、処理の全体に関連する変数、定数の宣言はメソッドの先頭で行ない、それが処理全体の主人公であるという見通しを読者に伝えるべきです。

  public String findUniqueNameFromIndex(int index) {
     String ret = "";
     ...
     return ret;
  }

すべてのメソッド定義でこのような形式に沿った記述をしていれば、その処理がやや冗長になっても途中でターゲットを見失わずに済みます。

処理の途中で使用する変数は、そのスコープにもっとも近い場所で宣言をすることで、読者にそのスコープに関連する事を強く印象づける事ができると考えています。

大切な事は、全ての記述を特定の形式に収める事で、読者の興味、関心を他に移さずにコードに集中させる事です。

Syntactic Sugarであるswitch文を排していない

読んだ限りではswitch文について言及している部分はありませんでした。 まぁこれは趣味な部分も含んでいるので、こうでなきゃいけないというコンセンサスは得られないと思います。

ただ、switch文は単一スコープの中に記述する形式になっているので、一度あるcase文の中で宣言した変数は、他の個所で使う事はできません。break文を忘れて、意図せず変数に代入が行なわれて気がつかずに特定の処理でバグが入るといった可能性があります。

一般にswitch文はif-else文で置き換えが可能で、スコープが明確に分かれているif-else文を使うのが個人的にはお勧めです。少なくともbreak文を抜かすような真似はif-else文ではできません。

まぁswitch文は便利ですし、組み込み系のプログラミングで8bitの入力を処理するのに255個のcase文があるコードを見た事もあるので、環境によって流儀はあると思いますが、あえてswitch文は止めようといっておきます。

まとめ

リーダブルコードは読者を意識したコーディングを行なうための、とても良いガイドになっていると思います。

昔から特定の言語を対象にした、「べき・べからず本」というのはありましたが、どうもTipsが命令的で柔軟性にやや欠けるような印象がありました。

リーダブルコードのテーマは昔から出版されている本と比較して、とても斬新というわけではありません。

しかし、押し付けようとせずに、プロ・アマ問わずに誰でも読める記述になっている事はお勧めのポイントとして、とてもとても高く評価しています。

この本は誰が読んでも参考になりますが、できれば10代で、この本を読んで、最初から読み手を意識したコーディングをする方が増える事を願っています。

最後に、深夜に書き始めたせいか、いつもどおり、そしていつもにも増して上から目線になってしまいました。 ごめんなさい。

おまけ:自分が書くコードはリーダブルか (小学生読書感想文調)

私は、ずっとこういう事をテーマにしているはずなのに、いまだに最初からリファクタリングが不要なコードを書く事ができていません。

これは小説家が何枚も原稿用紙を破るように、当たり前の事なんだと思ってきました。 でも、リーダブルコードを読んで、プロの人達はこんな事はしなくても経験を積めばリーダブルで高可用性なコードが最初から書けて、こんな素晴しい本まで書けるようになるんだと知りました。

私は以前に同じようなコードを書いた事がなければ、いろいろ設計らしき事をしてから作り始めても、一つのメソッドが100行くらいになってしまう事は、たまに起ります。

1つのスコープでそんなに長いコードを書いているので、tmpという変数名を使ってしまうと、後でtmp2のような変数名を使わなくてはいけなくなるので、結果として説明的な変数を使うようになります。

それから、処理の内容をできるだけ分割して、意味のある単位で外部のメソッドとして括り出して、その処理に責任を負うのに相応しいクラスに処理を移します。

そんな作業を何回も繰り返して、やっとそれなりに適当な粒度のメソッドに分割されたコードを作る事ができた、と思う事ができるようになります。

そして、いくつかの責務を負っているクラスを分割したくなります。 この衝動はどうしても抑える事ができないのですが、よくよく考えずにやってしまって、最終的にgit checkout -fをしなければいけなくなる時があります。

きっとプログラミングが仕事の人達は、最初から見通しをつけて空のメソッドをどんどん書いて、後から、その中に処理を書いていく事ができるんだと思います。何年も仕事をしていて、いまだにこんな事をしているのは自分だけに違いないと思うと情けないけれど、負けずに頑張っていきたいと思います。

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

2012/06/27

CursorLoaderとAsyncTaskLaoderを使ったアプリのソースコードを公開してみた

最初は「Android 3.0以前でのFragmentとLoaderによるプログラミングのすゝめ」というタイトルにしたのですが、説明的すぎたので止めました。

さて、本屋さんでは、あんざい ゆきさんの本などを除いて、初心者向けのAndroid本のほとんどで、FragmentやLoaderについての記述がある本をみることはありません。

初心者向け本が備えるべき要件をここで書くつもりはないので、何が初心者向けなのかは議論の余地があると思いますが、網羅的な本を読むよりは、多少偏ってもアプリケーションを一つ作って、その周辺知識を増やしていく方が学習方法としては効果的じゃないかなと考えています。

その点では良い土台が必要なんじゃないかなと思うわけですが、そのうちAndroid 2.3を対象にしたアプリケーションでもFragmentやLoaderを使ったプログラミングを推奨する「やり直し本」が出てくるんじゃないかなと期待しているんですが、どうするべきなんでしょうね。

Android 1.6の頃はThreadを生成して、Handlerオブジェクトに処理を実行させるコードを書いた事もあったかもしれませんが、アプリケーションを見通し良く開発していくためには、新しい方法を取り入れて進んでいくしかないと思っています。

互換ライブラリを使ったアプリケーションの開発

CursorLoaderはContentProviderに対するwrapperのように使えば良いのですが、そういう説明をみなかったので実際にアプリケーションを作ってみました。

アプリケーションのベースは以前作成した「日本の郵便番号検索 Free」で、郵便番号の3桁+4桁の数字のみを入力するようにして、内部のコードはシンプルに保っています。

作成したアプリケーションのスクリーンショット

内部構造や使う仕組みは一部は流用していますが、ほぼ新規に作成しています。 ログの出力用メソッドに対する工夫など、役に立ちそうな部分は極力反映したつもりです。

このアプリケーション「Yamaneko」のソースコードはApache License 2.0でGithubにて公開してます。

アプリケーションの導入や内部構造について

使うためのEclipseの操作方法はスクリーンショットを交えて説明しているので、内部構造の説明を含めてgithubのwikiを参照して下さい。

データフロー概要

実際にアプリケーションを作って感じたこと

「動けばいい」というのではなくて、内部構造をシンプルに保ちつつ見通しのよいコードを書くために、普通はフレームワークを開発します。

Android 3.0やCompatibility Packageで提供されている新しいクラスは、そういう新しい(抽象度は低めかもしれませんが)フレームワークに相当する処理で、開発元が公式に提供しているものとなります。

その点では独自になにか仕組みを作るよりは、馴染むまでに時間がかかるかもしれませんが、できるだけ使った方が良いスタイルを強制できる事になります。

初心者向けの雛型アプリケーション

GNU公式のGNU Helloアプリのソースコードを雛型に開発をしようとして、その完成度の高さのあまりに挫折した人も多いのではないでしょうか。

まぁ、GNU Helloはほとんどネタですけれど、Androidでも似たようなアプリケーションが必要なのかもしれません。

本屋さんでAndroidの初心者向け開発本を眺めた印象では、初心者向けの良い雛型アプリケーションを提供する本がないなぁという印象を受けました。

内部構造を知るためにAndroidのframeworks/base/coreディレクトリにあるソースコードを見るのは普通だと思いますが、アプリケーションを開発するための土台としては、何か参考になるか聞かれて「GNU Helloみてみたら」みたいな定番があった方が便利じゃないでしょうか。

ApiDemoアプリも良いんですけれど、単品の機能だけなので、連携が分からないところがネックかなと思っています。

非同期処理のための他の方法との比較

メインUIやContentProvider, Serviceプロセスをブロックしないためには、HandlerやAsyncTaskクラスを使えば、AsyncTaskLoaderを使う必要は必ずしもありません。

仕組なくともProgressViewでアップデートするためにはAsyncTaskLoaderは適切ではなくて、AsyncTaskクラスの方が便利でしょう。

今回、CusrorLoaderとAsyncTaskLoaderを使った印象は、自前でAsyncTaskを管理してContentProviderの内部からタスクを起動するよりも、AsyncTaskLoaderでネットワークアクセスのみを扱い、ListViewへの出力はCursorLoaderと平行に起動できるのは内部構造としては見通しが良く、管理もしやすかったです。

外部のWebサービスから結果を取得して表示するアプリケーションでは、今回の構造がいまのところベストかなと思います。

表示するデータをContentProviderに管理させる構成について

直感的には自分のアプリケーションで表示するデータをDBに入れるだけではなくて、そのためにContentProviderのサブクラスを作成して、AndroidManifest.xmlに登録するというのは冗長に感じます。

とはいえ、twitterのタイムラインのように、ListViewやGridViewに表示する項目数が想定できないような場面では、データベースを中継してCursorオブジェクトでListViewやGridViewに表示する方法がベストです。

多少面倒でも、SimpleCursorAdapterとCursorLoaderを組み合せるのは良い方法だと思います。 パフォーマンスが懸念される場面でListAdapterを使うというのは、いまのところ納得していません。

また、副次効果としてSQLiteDatabaseオブジェクトをContentProviderクラスだけが扱うことになるのは良い構造だと思います。

さいごに

自分のコードが最善だとは到底思えないので、アプリケーションのコードを公開するのは気が重いです。

Androidアプリのソースコードは、あまりみないので、公開しようかなと思ったのですが、 Apache License 2.0やGPLv3などのライセンス別にまとめるサイトがあってもいいかもしれません。

いろいろ叩かれるとしても、それを乗り越えられるなら、コードを隠しても良い事はほとんどないですからね。

次に勤める会社があるなら、その会社との契約に縛られない限りは、 まとまった処理単位での動きが確認できるアプリケーションのコードはこれからも公開していきます。

2012/06/23

Pythonスクリプトの出力をリダイレクトしたらエラーになった

このブログサイトに登録してあるキーワードを出力するスクリプトをgdataライブラリを使って、pythonで書いているのですが、出力結果をファイルにキャッシュしておこうとしてラップしているbashスクリプトに../list_labels.py | tee labels.txt を追加したところ、次のようなエラーが発生しました。

  File ".../python/list_labels.py", line 133, in run
    self.list_labels()
  File ".../python/list_labels.py", line 125, in list_labels
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-7: ordinal not in range(128)

問題になったのは次のようなコードの中で、print labelをしているところです。

問題になった該当コード周辺

...
    l = []
    for label in set(labels):
      l.append(label)
      pass
    for label in sorted(l):
      print label
      pass
    pass

Googleでとりあえず検索してみた結果

いくつかの方法がありましたが、sys.setdefaultencodingにencodingを記述するといった方法がありました。

import sysをしてからdir(sys)でみると、少なくとも手元のpython 2.7.3にはそんなメソッドは定義されていません。

次にヒットしたのはunicodeに文字列を変換するものですが、どちらかというとスクリプト中に埋め込んだutf-8文字列を画面に出力するための方法を解説しているもので、リダイレクトの時に問題が発生している事については触れられていませんでした。

pythonのバージョンに依るのかもしれませんが、この他の方法もいまいち決定的なものはありませんでした。

unicodeに変換するのではなく、リダイレクトの時の正しいencodeを指定する

解決策は次のようにencode()関数を追加して'utf-8'に変換しました。

変更後の該当コード個所の抜粋

    l = []
    for label in set(labels):
      l.append(label)
      pass
    for label in sorted(l):
      print label.encode('utf-8')
      pass
    pass

リダイレクトの時にLANGなどをみて標準的なlocaleに変化してくれれば良いのですが、リダイレクトの時にencodeが設定されないというのは、かなり悩ましい挙動です。

暗黙の挙動だから不定になるのは当然という意見もありそうですが、リダイレクトの時に挙動が切り替わるのは、やっぱり謎仕様です。rawデータとして何の処理もせずにbyte列を掃き出すなら、まだ分かるんですけどね。

ともかく無事に終わったので部屋の整理をして今日は休もうと思います。

Xperia Mini Pro (SK17i)をIce Cream Sandwich (ICS) 4.0.4にしてみた

HVGA(320x480)サイズの開発機として海外から購入したXperia Mini Pro (SK17i)のOSをIce Cream Sandwich (ICS) 4.0.4に変更しました。

これで手元に実機環境は画面サイズがHVGA, WVGA, WXGA, OSが2.3, 3.2, 4.0と、だいたいメジャー所が揃った事になります。

お金があれば未使用中古品のGalaxy Nexusも欲しいんですけどね。 SC-04Dはちゃんと技適の通った端末ですし、b-mobileの各種SIMカードも使えるはずですし。

twitterやメールの読み書きぐらいはHVGA端末でまったく問題ないですが、PDFビューアーとして使うなら一辺1024ピクセル以上は欲しいです。960x640なiPod Touchの画面は3.5"あって綺麗ですが、PDFファイルを見ようという気にはなりませんから。

さて、今回はICSにアップデートして気がついた点を書いていきます。Xperia固有の事もありますが、ICSの特徴と区別していません。

【2012年6月30日追記】
ここでの対象はソニーが配布しているオフィシャルなICSについてです。 ROMを書き換える方法の方が対応が早いので、検索エンジンにもよくヒットするとは思いますけれど…。
携帯端末についていえば、ROMを書き換えるような方法は一般にお勧めできる方法ではありません。

【2012/6/30追記】アップデートの方法について

設定画面のアップデートの確認をしてもICSへのアップグレードはできません。事前にユーティリティをインストールしたWindowsか、Macに接続する必要があります。

更新情報は自動的に通知されるので、パソコンを使えば特に問題なくICSへのアップグレードできるはずです。

内部ストレージの増加

正確に画面ダンプを持っているわけではないのですが、以前のブログを書いた時には空き容量が170MB程度でした。

ICSを導入した後は、内部ストレージ全体は420MBほどあって、使用領域が200MB、空き領域が220MB程度になっています。

スクリーンショットが標準機能に

電源ボタン長押しでメニューにスクリーンショットを取るためのボタンが表示されましたが、このボタンはなくなっています。 ICSからは標準機能として電源ボタンとボリュームのマイナスボタンの同時長押しでできるようになっています。

撮影したスクリーンショットはSDカード上のPitures/ScreenshotsフォルダにPNGファイルとして保存されています。

標準カメラ機能

購入直後は撮影した画像がサムネイルみたいで残念な感じだったのですが、ICSにアップデートしてからはちゃんと撮影できるようになっています。

開発者向けオプションの充実

ICSは標準で開発者向けオプションがいろいろ提供されているはず…、だと思っていたので不思議でもなんでもなかったのですが、Acer Iconia Tab A100では、この項目が極端に少ないといった情報があって、本当だとすればXperiaの項目はちゃんと揃っている方だと思います。

「アクティビティを保持しない」のオプションを使えば、検索結果をクリックして地図を表示するActivityに移動してから戻ってきたらActivityが再起動して画面がクリアされている、といったメモリが余っている開発機では発生しない現象の確認もできます。

Activityのライフサイクルをちゃんと理解するにも役立ちますし、個人的にはこのオプションが気に入っています。

まとめ

ICSにしたから特に動きが機敏になったという印象はありません。 ランチャーも引き続き使え、使い勝手も特に変化はありません。

とはいえ、タブを使っているアプリケーションではアイコンが消えてしまったり、いろいろ細かい点ではUIが違っています。

ICSになって劣っている点があるとは思えなくて、個人的にはユーザーは積極的にICSにアップデートして、開発者も追随するべきだと思います。これはアプリ開発者としての意見です。

不満がなければアップデートするなというのは、システム屋からの意見として、まっとうだと思いますけどね。

これでICSでの動作確認も実機でできるようになりました。 だからといって特に新しい機能「だけ」を使ったアプリを作るわけじゃないですけどね。

何か参考になるかと思って、本屋さんで「Android SDK 4対応」とか意味不明な売り文句の本を眺めてきたのですが、 フラグメントについてはまったく触れられていませんでした。互換パッケージについても同様です。

まぁ現状のAndroidは安定期とはとてもいえなくて、活発に変化が起こっているおもしろい時期でもあるし、変化に追従しなければいけない時期でもあります。この時期はいろいろな事がおこって楽しいですね。過去に正しかったものが、時間を経て決してベストとは呼べなくなったり、本当に大変ですけれど。

Android In-app Billingのサンプルを自前アプリに組み込んでみた

AndroidのIn-app Billingは、無料アプリケーションの中で課金アイテムを販売するための仕組みです。 Application Licensingが有料アプリケーションのみを対象としているのと対照的です。

無料と有料とを別々に販売する方法は、アプリ内広告のためのライブラリを省くなどなど、アプリケーションサイズを小さくすることなどはできますが、その反面、コードのメンテナンスやテストはいろいろと面倒だったりします。

無料アプリをそのままにして、有料アプリを頻繁にバージョンアップさせる方法もありますが、 あまり良いインタフェースではなさそうに思えたので、今回は単一のアプリケーションイメージを無料で配布して、 その中で一部機能のロックを解除するツールを販売する方法にしてみました。

このIn-app Billingのサンプルアプリケーションを動かすのも、Androidプログラミング自体が始めての場合には いろいろと面倒そうですが、手順はいろいろ出回っていて、そもそもDev Guideの手順が一番充実していたりするので、 今回はこのサンプルのコードを自分のアプリケーションに組み込んだ時のログを残す事にしました。

Dungeonsアプリについて

Android Dev. Guideに従って作業を進めると、Google PlayにAPKファイルをアップロードするところまでは簡単に進むと思います。

アップロードしてからすぐに課金アイテムの作成などを行なう事ができるようになりますが、 アプリケーションから正しく処理ができるようになるまでは30分ほどかかりました。

うまく動かない場合には、アイテムを増やしたりするよりも、しばらく時間を置くのが良さそうです。

非署名アプリからの購入テスト

一度署名アプリをGoogle Playに登録して、課金アイテムを登録すると、emacsからアプリを起動した時のように署名をしていないapkから起動したアプリも正常に動き、"android.test.purchased"の購入などのテストができるようになります。

作業の進め方 (方針)

Dungeonsアプリを下敷に、自分のアプリケーションからアイテムの購入ができるようにする方法を考えます。

今回はDungeonsをライブラリに変更せずに、自作アプリケーション配下のパッケージに導入します。

ManagedアイテムとUnManagedアイテム

一回購入すると購入者情報と紐付いて、アプリケーションをアンインストールしようが購入履歴がGoogleに保存されて、再インストールしたアプリにも引き継がれる、それがManagedアイテムです。

よくよくアプリ課金で問題になるコインや武器といった繰り返し購入可能なアイテムはUnmanagedと呼ばれていますが、今回は対象としては考えません。

課金(Billing)機能の組み込みについて

基本的にはDungeonsアプリのDungeons.javaファイルを除いて、全てのファイルを自分のアプリケーションにコピーします。

Eclipse上のPackage Explorerでは次の画像のように、自分のアプリケーションのパッケージの中にbillingサブパッケージを追加して全てのクラスをコピーしてきました。

Eclipse上のPackage Explorerの様子

この他にsrcフォルダに "com.android.vending.billing" パッケージを作成し、IMarketBillingService.aidl ファイルをコピーしておきます。

また"com.example.dungeons.util"パッケージのBase64.javaとBase64DecoderException.javaもコピーしてきます。

参照関係はEclipseのエラーを確認しながら修正していきます。

Dungeonsアプリとの差分について

参照関係を解決しても、Dungeonsアプリ固有のUIを操作している部分は削除する事になります。

パッケージ名やコメント内の重要ではない部分を省いた差分は、概ね次のようになります。

BillingService.java

-    class RequestPurchase extends BillingRequest {
+    public class RequestPurchase extends BillingRequest {

-    class RestoreTransactions extends BillingRequest {
+    public class RestoreTransactions extends BillingRequest {

Security.javaの差分 (セキュリティ上の理由から省略)

-            String base64EncodedPublicKey = "...";
+            String base64EncodedPublicKey = "...";

RequestPurchaseをpublicに変更したのはパッケージnet.yadiary.android.exifpc.billingの外部からアクセスする必要があるからです。

Dungeonsでは単一のパッケージの中に含まれるので意識しない部分ですが、それを除いてもかなり使い周せるように考えられて作られていると思います。

自分のアプリからbillingパッケージにコピーしたクラスを使う

いよいよメインの作業に入っていきますが、Dungeons.javaを参考にしていきます。

購入アイテムのリストを作成する

クラス変数としてCATALOGのエントリを作成します。 今回のアプリでは画面に表示する名称は別途管理するので、このCatalogEntryの第二引数は実際には使っていません。

mBillingServiceインスタンスは外部から操作する必要があるのでアクセスできるよう、getterのみ定義しています。

MainActivityのライセンス関連変数


        /*
	 * ライセンス用設定
	 */
	private static final String TAG = "ExifPM";
	private static final String DB_INITIALIZED = "db_initialized";

	private Handler mHandler;
	private BillingService mBillingService;
	public BillingService getBillingService() {
		return mBillingService;
	}
	private ExifPMPurchaseObserver mExifPMPurchaseObserver;

	/**
	 * カタログ情報はここにまとめ、各Fragmentが参照する
	 */
	public static final CatalogEntry[] CATALOG = new CatalogEntry[] {
			// primary selling tools
			new CatalogEntry("exifpm_purchases_item", R.string.billing_item_hiddenads, CatalogEntry.Managed.MANAGED),
			// debug items
			new CatalogEntry("android.test.purchased", R.string.billing_item_hiddenads, CatalogEntry.Managed.MANAGED),
			new CatalogEntry("android.test.canceled", R.string.billing_item_hiddenads, CatalogEntry.Managed.MANAGED),
			new CatalogEntry("android.test.refunded", R.string.billing_item_hiddenads, CatalogEntry.Managed.MANAGED),
	// please add other stuffs under this line.
	};
	private PurchaseDatabase mPurchaseDatabase;
onCreateメソッドでのライセンス関連設定

onCreateメソッドでのライセンス関連設定


                mHandler = new Handler();
		mExifPMPurchaseObserver = new ExifPMPurchaseObserver(mHandler);
		mBillingService = new BillingService();
		mBillingService.setContext(this);
		ResponseHandler.register(mExifPMPurchaseObserver);
		if (mBillingService.checkBillingSupported(null) == false) {
                        // 必要に応じてエラーメッセージを表示する
			Toast.makeText(this, R.string.billing_toast_notsupported, Toast.LENGTH_LONG).show();
		}

最後のToast文は課金がサポートされていない事を通知するためのメッセージです。 Emulatorなどで実行すると、ここでメッセージが表示されるはずです。

onDestroyでの後始末

課金サービスに限らずServiceに定義したインスタンスはunbindしないと、バックグラウンドで稼働してバッテリーを消費します。 定義されているだけではServiceは起動しませんが、BillingServiceのようにアクセスされ、起動したServiceは、いわゆるlong-runningプロセスとしてシステムが管理します。 GCが動くとか妄想は止めて、必ず停止するようにしましょう。

onDestroyメソッド全体


        @Override
	protected void onDestroy() {
		mBillingService.unbind();
		super.onDestroy();
	}
PurchaseObserverサブクラスの作成

Activityクラス内にPurchaseObserverのサブクラスを定義します。

ここでは変数として宣言されていた、mExifPMPurchaseObserverを作成していきます。 参考までに作成されたExifPMPurchaseObserver全体は次のようになっています。

ExifPMPurchaseObserverクラス全体 (一部省略)


	private class ExifPMPurchaseObserver extends PurchaseObserver {
		public ExifPMPurchaseObserver(Handler handler) {
			super(MainFragmentActivity.this, handler);
		}
		@Override
		public void onBillingSupported(boolean supported, String type) {
			if (type == null || type.equals(Consts.ITEM_TYPE_INAPP)) {
				if (supported) {
					restoreDatabase();
				}
			} else if (type.equals(Consts.ITEM_TYPE_SUBSCRIPTION)) {
				// This type is not essential of this application
			} else {
				// not supported state, do nohing.
			}
		}
		/**
		 * キャンセル、リファンド通知はこのメソッドのpurchaseStateで判断する
		 */
		@Override
		public void onPurchaseStateChange(PurchaseState purchaseState, String itemId, int quantity, long purchaseTime, String developerPayload) {
			if (purchaseState == PurchaseState.PURCHASED) {
				if (itemId.equals(CATALOG[0].sku) || itemId.equals(CATALOG[1].sku)) {
					if (adView != null) {
						adView.setVisibility(View.GONE);
					}
				}
			} else if (purchaseState == PurchaseState.CANCELED) {
			        // do nothing
			} else {
				// refunded state
				if (itemId.equals(CATALOG[0].sku) || itemId.equals(CATALOG[2].sku) || itemId.equals(CATALOG[3].sku)) {
					AppConfig.isFreeEdition = false;
				}
			}
		}

		@Override
		public void onRequestPurchaseResponse(RequestPurchase request, ResponseCode responseCode) {
			if (responseCode == ResponseCode.RESULT_OK) {
				if (Consts.DEBUG) {
					Log.i(TAG, "purchase was successfully sent to server");
				}
			} else if (responseCode == ResponseCode.RESULT_USER_CANCELED) {
				if (Consts.DEBUG) {
					Log.i(TAG, "user canceled purchase");
				}
			} else {
				if (Consts.DEBUG) {
					Log.i(TAG, "purchase failed");
				}
			}
		}

		@Override
		public void onRestoreTransactionsResponse(RestoreTransactions request, ResponseCode responseCode) {
			AppConfig.sendMessage("called with ResponseCode=" + responseCode);
			if (responseCode == ResponseCode.RESULT_OK) {
				if (Consts.DEBUG) {
					Log.d(TAG, "completed RestoreTransactions request");
				}
				// Update the shared preferences so that we don't perform
				// a RestoreTransactions again.
				SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
				SharedPreferences.Editor edit = prefs.edit();
				edit.putBoolean(DB_INITIALIZED, true);
				edit.commit();
			} else {
				if (Consts.DEBUG) {
					Log.d(TAG, "RestoreTransactions error: " + responseCode);
				}
			}
		}
	}

ここではコンストラクタを除くと4つのメソッドが定義されています。 全てのメソッドの基本的な構造はDungeonsクラスから、そのまま引き継いでいます。

前半のonBillingSupportedとonPurchaseStateChangeは

後半のonRequestPurchaseResponseとonRestoreTransactionsResponseはアプリケーションの動きに応じて、例えばリセットされたアプリケーション起動時にステータスを回復したタイミングで「購入情報を更新中です」みたいなメッセージを表示する事ができます。

restoreDatabaseの動き

バッサリ省略したrestoreDatabaseメソッドは、キャッシュクリアされたアプリを起動した場合などに、購入済み情報を取得するためのメソッドです。

実際の取得処理はmBillingServiceのrestoreTransactionメソッドを呼び出します。

実際の購入処理

このActivityの管理下にあるFragmentから実際の購入処理を呼び出す事になります。

ボタンなどをクリックした時にActivityのBillingServiceインスタンスに対して、 requestPurchaseメッセージを送信します。

この処理は簡単なので省略します。

まとめ

課金処理を追加する事自体でアプリケーションコードは、ほとんど増えませんし、パーミッションも明示的なcom.android.vending.BILLING 1つだけなので、良いソリューションだと思います。

反面、課金処理は簡単ですが、ゲームなんかでコインを購入させるのは、どうかなぁと思います。

単純に時間短縮のためにコインを購入オプションがあって、時間さえかければ先に進めるようなものは良いのですが、ゲームバランスが極端に悪いものは遊んでいてあまりおもしろいとは思いません。

じゃぁなんで課金機能を追加したのかと言われれば、日本の法律では寄付の受付は禁止ですから、アプリケーションを気に入ってもらったり、このブログが参考になったりした場合に、代りにアイテムを購入して頂ければ幸いです。

Ubuntu 12.04 LTSマルチディスプレイ環境下での不具合?

数年前に新品パネルに交換した事もあって、いまだに17インチディスプレイ(Nanao FlexScan L567)を使っています。 これを2枚並べてマルチディスプレイ環境にしているのですが、拡大表示時の課題などもあってディスプレイを仮想的に接続せずにDISPLAY環境変数でいうところの:0.0と:0.1として使っています。

このディスプレイが接続されているマシンはUbuntu 12.04 LTS専用機で、Androidアプリの開発やらメール書きやらのメインマシンとして使っています。

タイトルに?をつけてスポーツ新聞の見出しみたいですが、今回はいままで気がついたものの、コード上でまだ原因が特定できていない、おそらくマルチディスプレイ環境が原因と思われる症状をメモしておきます。

環境について

OSよりもWindowManagerの影響は大きいと思われて、その他のGNOME, KDEなどでは現象が発生しないかもしれません。

  • OS: Ubuntu 12.04 LTS x86_64版
  • WindowManager: xfwm4 (デスクトップ環境はXFCE)

考慮点

ここでまとめた内容は、まだコード上でマルチディスプレイが原因と特定できていない現象を含んでいます。 そのため現象としては正しいが、原因はマルチディスプレイ環境にないものも含まれている可能性があるのでご注意下さい。

なおアプリケーションをDIPSLAY=:0.0設定下で稼働した場合には不具合は起っていません。

其の壱:Android AVD(qemu)が終了しない

EclipseからAVD(qemuによるandroid emulator)を起動して、デバッグが終ったところでウィンドウを閉じて終了しようとしても、AVDプロセスが終了しません。

KILLHUPシグナルは受け付けないので、この場合の対処法はkill -9で該当プロセスを強制終了させるのみです。

其の弐:Firefoxのプルダウンメニューが開かない

住所欄の都道府県などはFormのプルダウンメニューで選択可能になっている場合があります。 このプルダウンをクリックしても選択肢が表示されません。下矢印キーで選択はできるので実用上は致命的とまではいえません。

其の参:PiTiViビデオエディタで動画のExport処理が完了しない

最終的に完成した動画をExportするわけですが、この処理が永遠に終わりません。

この他にも動画関連の不具合はあって、ffmpegやmpeg2enc辺りが怪しいんですが、追求していません。

其の肆:gtk-recordmydesktopが撮影を終了することができなくなる

やっぱり動画周りの不具合ですが、これは通知エリアを利用するアプリケーションで、DISPLAY=:0.0に通知ウィンドウがある場合に、DISPLAY=:0.1で動かしたgtk-recordmydesktopが隠れたまま、操作不可になってしまう事が原因です。

この場合、gtk-recordmydesktopを端末から起動していてC-cで終了する事はできますが、recordmydesktopプロセスは残ってしまいます。手動でKILLHUPを送信すれば停止しますが、注意が必要です。

当然、gtk-recordmydesktopを使わず端末からrecordmydesktopを直接動かせば何の問題もありません。

まとめ

これからKDE,GNOMEで動かしてみて、どんなになるか確認してみます。 とはいえ作業中なので環境全体をシャットダウンして試すことはすぐにはできないんですよね。

最近は1920x1200を越えるディスプレイも簡単に購入できて、27インチで2560x1440ともなれば、1280x1024の2枚(2560x1024)よりも下に広くなります。でもこのFlexScan L567の発色は良くて満足しています。 Mac/Windows用に安い1920x1080なIPS液晶ディスプレイもあるんですが、表面処理が荒くてオブラートみたいな薄い膜が一枚あるみたいなのが残念です。

IPSが高級品だった時代は過ぎさってしまったので、次に購入する時は実物も眺めつつ決めたいと思います。

2012/06/22

HVGA,タブレット両対応の郵便番号アプリをViewPagerとFragmentで作りかえてみ た

以前作成した郵便番号アプリはSlidingDrawerを使って同一レイアウトXMLに全てのViewを記述しつつ、 入力部分の描画を切り替えるようにしていました。 タブレットではSlidingDrawerを使わずにViewを配置する事で、画面サイズの違いに対応していました。

今回はViewPagerを使う事で、SlidingDrawerのツメ部分の描画が省略されるなど、若干シンプルになりました。 タブレットではViewPagerを使わずにレイアウトXMLを記述しています。 まったくの同一コードという分けにはいかないので、MainActivity側でViewPagerインスタンスが入手できない事を検出して、Fragmentへのアクセス方法を切り分ける事で、同一コードで対応しました。

今回はViewPagerならではの部分についてまとめていきます。

ViewPagerを使用した画面イメージ Tablet用画面イメージ

対応するバージョン、使用したAPI等々

今回のアプリケーションはAndroid 1.6以降、Android 4.0までをターゲットにしています。 検証のために次のような機器を使用しています。

  • Acer Liquid MT (2.3 800x480)
  • Iconia Tab A500 (3.2 1280x800)
  • Sony Xperia Mini Pro (4.0 320x480)

内部ではFragmentを使うために、android.support.v4パッケージを使用しています。 参考までにActivityクラスのimport文は、次のようになっています。

Activityクラスのimport文抜粋

import net.yadiary.android.actionbarcompat.ActionBarActivity;
...
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.CursorAdapter;
...

ViewPagerがある場合とない場合の切り替え方法

レイアウトXMLにViewPagerの記述があれば、findViewById(R.id.pager)のようなメソッドでインスタンスが取得できるはずです。

課題はFragmentのインスタンスにどのようにアクセスするのかという事です。 通常はFragmentManagerインスタンスを経由して、Fragmentにアクセスしますが、 ViewPagerが設定するFragment Tag名は外部からは(一応)分かりません。

Activity(実際にはFragmentActivityをベースにしたActionBarActivityの子クラス)中のコードは次のようになっています。

onCreateメソッドの抜粋

	protected void onCreate(Bundle state) {
		super.onCreate(state);
		setContentView(R.layout.main);
		viewPager = (ViewPager) findViewById(R.id.pager);
		if (viewPager != null) {
			myAdapter = new MyAdapter(getSupportFragmentManager());
			viewPager.setAdapter(myAdapter);
		}

viewPagerの中に配置するFragmentはMyAdapterクラスのgetItem(int position)メソッドの中でインスタンスを生成しています。 ここら辺はオフィシャルのFragmentPagerAdapterリファレンスを参照してください。

FragmentにアクセスするためのサポートメソッドをActivity中に追加しています。 ViewPagerを使う場合は、アダプターのinstantiateItem(viewPager, position)を使用しています。

FragmentはレイアウトXMLで記述したのでR.id経由で指定していますが、 ViewPagerのインスタンスがnullの場合に、必要な場面は想像できませんが、動的にFragmentを定義する事もできます。

Activityクラスに追加したFragment取得用サポートメソッド

	public Fragment getFragment(int position) {
		Fragment ret = null;
		if (viewPager != null) {
			ret = (Fragment) myAdapter.instantiateItem(viewPager, position);
		} else {
			FragmentManager fm = getSupportFragmentManager();
			if (position == PAGER_PAGE_INPUT) {
				ret = fm.findFragmentById(R.id.fragmentInput);
			} else {
				ret = fm.findFragmentById(R.id.fragmentListView);
			}
		}
		return ret;
	}
レイアウトXML中でのViewPager, Fragmentの記述方法

android.support.v4パッケージを使っている事で、レイアウトXMLの具体的な書き方は、Android 3.0以降に対応したものとは少し変わっています。

ここら辺の書き方で困る場合もありそうなので、該当個所の抜粋だけ載せておきます。

android.support.v4のViewPager, FragmentレイアウトXML記述例

...
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0px"
        android:layout_weight="0.9" >
    </android.support.v4.view.ViewPager>

...

        <fragment
            android:id="@+id/fragmentInput"
            android:name="net.yadiary.android.jpostal.InputFragment"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
        </fragment>

...

要素名をパッケージで指定したり、大文字が小文字だったり、知っていれば何でもないんですけどね。

まとめ

ViewPagerを使う事で画面はシンプルになりましたが、ActionBarを使っているので20〜30ピクセルほどは以前よりも画面を占有するようになりました。

とはいえ、Fragmentに分けた事で内部構造は分割統治が可能になりシンプルになりました。

以前のコードはサポートクラスに処理を切り出したりはしていましたが、ステータス管理の面からは巨大なActivityクラスでした。 android.support.v4パッケージとFragmentを使う事で、互換性を維持しつつ、よりレイアウトXMLを中心としたアプリケーション開発ができるでしょう。

正直なところFragmentを始める前は、解説書を読んでもどういう風に処理を分けたらいいのか、イメージを持つ事が難しかったです。

まずは新規で簡単なアプリを作って慣れてから、古いアプリのActivityをFragment対応にする場合には、レイアウトXMLに対応する新しいFragmentクラスを作って、Activityの処理をFragmentクラスに移すようにするべきでしょう。

いろいろなデバイスに対応するのは面倒ですが、参考になれば幸いです。