<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>ウノウラボ Unoh Labs</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/" />
    <link rel="self" type="application/atom+xml" href="http://labs.unoh.net/atom.xml" />
    <id>tag:labs.unoh.net,2006://2</id>
    <link rel="service.post" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2" title="ウノウラボ Unoh Labs" />
    <updated>2010-09-01T07:14:01Z</updated>
    <subtitle>ウノウ株式会社のデモ版サービスやTIPSなどの情報</subtitle>
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type  4.261</generator>
 

<entry>
    <title>アジャイルな開発をチームでやってみた(2010年版)  - その2</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/09/acts_as_agile2.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1860" title="アジャイルな開発をチームでやってみた(2010年版)  - その2" />
    <id>tag:labs.unoh.net,2010://2.1860</id>
    
    <published>2010-09-01T07:02:24Z</published>
    <updated>2010-09-01T07:14:01Z</updated>
    
    <summary>テストコード書いてますか？　HIROKIです。 murahashiに続いて、テストファーストを導入してみての振り返りをします。 まず、どうやってチームにテストファーストのスタイルを持ち込んだのか。 1．テストが重要だという共通認識を持つ。 前のプロジェクトではテストコードは、ほとんどありませんでした。 その中で、開発になれていない人や新たに人が投入され、 極少数ですが、デグレーションが起きました。 その経験を元にテストが重要だという共通認識を持つことができました。 2．プロジェクト開始時からテストファーストに踏み切る気持ちが必要 テストコードを書かなければコミットさせない。ぐらいの気持ちが必要です。 実際に何度も、本格的な実装が始まる前から口にしていました。 「うちのチームはテストを書かなければコミットを許さない。」と。 3．でも、テストコード書いたことないよ？ テストコードを書いたことの...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
        <category term="symfony" />
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[<p>テストコード書いてますか？　<a href="http://hiroki.jp">HIROKI</a>です。</p>

<p><a href="http://labs.unoh.net/cgi-bin/mt-search.cgi?tag=murahashi&blog_id=2">murahashi</a>に続いて、テストファーストを導入してみての振り返りをします。
まず、どうやってチームにテストファーストのスタイルを持ち込んだのか。</p>

<h3>1．テストが重要だという共通認識を持つ。</h3>

<p>前のプロジェクトではテストコードは、ほとんどありませんでした。
その中で、開発になれていない人や新たに人が投入され、
極少数ですが、デグレーションが起きました。</p>

<p>その経験を元にテストが重要だという共通認識を持つことができました。</p>


<h3>2．プロジェクト開始時からテストファーストに踏み切る気持ちが必要</h3>

<p>
テストコードを書かなければコミットさせない。ぐらいの気持ちが必要です。
実際に何度も、本格的な実装が始まる前から口にしていました。
「うちのチームはテストを書かなければコミットを許さない。」と。
</p>

<h3>3．でも、テストコード書いたことないよ？</h3>
<p>
テストコードを書いたことのないメンバーもいるかと思います。
そんな人は下記の参考書を写経することから始めましょう。
</p>
<iframe src="http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=unoh-22&o=9&p=8&l=as1&m=amazon&f=ifr&md=1X69VDGQCMF7Z30FM082&asins=4894717115" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>

<p>symfonyを使うことが前提だったので、上記の本をlimeで置き換えて写経しました。</p>
<p>また、下記のような動画もとても参考になります。</p>

<ul>
	<li>ruby札幌 スはスペックのス〜RSpecによるテスト駆動開発の実演〜 <a href="http://www.nicovideo.jp/watch/sm2370458">その1</a> <a href="http://www.nicovideo.jp/watch/sm2370872">その2</a> <a href="http://www.nicovideo.jp/watch/sm2371011">その3</a></li>


<li><a href="http://gihyo.jp/dev/serial/01/tdd">［動画で解説］和田卓人の"テスト駆動開発"講座</a></li>

</ul>
<h3>4．そんなこと言っても、自主的に学習する人ばかりでないよ？</h3>

<p>初日にペアプロしちゃいましょう。</p>
<p>
そのチームでTDDに一番詳しい人と、TDD初心者な人をペアにして、
実装を始める初日かその前（できるだけはやく）にペアプロをしましょう。
</p>

<p>
ペアプロは1モジュール書き上げるくらいが良いかと思います。
そのチームで利用する開発環境の基本的なテストコードの書き方として後に参考にします。
</p>

<p>
私のチームではsymfony+memcached+lime+hudsonという環境でしたので、
どのタイミングでfixtureを流してmemcachedをクリアして、
どういったテストの書き方をしていくのか参考になるものを1つ作り上げました。
(細部に関してはチームみんなの意見をあつめて、検討してつくりました。)
</p>

<p>これができれば他のモジュールにも適応して開発していくだけです。</p>

<h3>5．じゃあ、やってみた経験から為になること教えてよ？</h3>

<p>symfonyでテストファーストを実施してみた経験から具体例をあげてみます。<p>

<h3>fixtureを理想的なものを最初につくっておく</h3>
<p>
テーブルの定義だけではなくて、
テーブルの中身のサンプルを全部のテーブル用意しておくこと。
データ自体は簡単なデータでよい。
</p>
<p>
モジュールをつくる度にあらたなfixtureをつくることを避けられる。
fixtureをつくるのが結構めんどくさい。
テストを書きたいのにデータがないというフラストレーションがたまる。
結局、つくらないといけないけど、他のモジュールで使っているfixtureと整合性がとれているのか？
</p>

<p>規模が大きくなればなるほど、面倒なことになります。</p>

<h3>limeのOKメソッドは使うな</h3>
<p>bool値をチェックするメソッドとしてokメソッドがLimeには用意されていますが、
これを使用することを非推奨としました。</p>

<p>たとえば、テストが失敗したケースを考えると。</p>
<pre class="code">$test->ok(false);</pre>

<p>実行画面</p>

<pre class="code">not ok 21
#     Failed test (./hogeTest.php at line 34)
</pre>


<p>という感じで返ってきます。ですが、isメソッドで書けば</p>

<pre class="code">$test->is(false,true,trueが返ってくるはず);</pre>

<pre class="code">not ok 22 - trueが返ってくるはず
#     Failed test (./hogeTest.phpat line 35)
#            got: false
#       expected: true</pre>

<p>という感じで出力が返ってくる。コメントもあるので、わかりやすい。</p>

<p>okメソッドだけだと情報が少なくて苦労するケースがあるので、面倒でもisメソッドで書くことにしました。</p>

<h3>コメントではなくて、コードに含めろ</h3>

<p>非推奨</p>
<pre class="code">// trueが返ってくるはず
$test->is($foo->bar(),true);</pre>
     
<pre class="code">not ok 22
#     Failed test (./hogeTest.phpat line 35)
#            got: false
#       expected: true</pre>

<p>推奨</p>
<pre class="code">$test->is($foo->bar(),true,trueが返ってくるはず);</pre>

<pre class="code">not ok 22 - trueが返ってくるはず
#     Failed test (./hogeTest.phpat line 35)
#            got: false
#       expected: true</pre>

<p>コメントを元にテストの場所や書いたテストの意図がわかるので、メソッドに含めましょう。</p>

<h3>まとめ</h3>
<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/09/build_status-212.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/09/build_status-212.html','popup','width=500,height=400,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/09/build_status-thumb-200x160-212.png" width="200" height="160" alt="build_status.png" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></a></span>
<p>このグラフがhudsonが1回目のビルドから529回目のビルドまでのテストにかかった時間と成功・失敗の結果です。</p>
<p>チーム全体で常にすべてのテストをパスしている状態を保つという意識とそれに伴う行動の結果です。</p>

<p>Files=59, Tests=3152  </p>
<p>現時点で59ファイル、3152のテストをパスしています。</p>
<p>赤色の部分も目立ちますが、テストが成長していることが目で見てわかります。
こうやって、テストに支えられていることも再認識することも大切なことなのかな。とも思います。</p>

<p>今回は、チームでテストファーストをやるためのステップと、少し具体的な例を紹介してみました。</p>
]]>
        

    </content>
</entry>

<entry>
    <title>アジャイルな開発をチームでやってみた(2010年版)</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/acts_as_agile.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1858" title="アジャイルな開発をチームでやってみた(2010年版)" />
    <id>tag:labs.unoh.net,2010://2.1858</id>
    
    <published>2010-08-26T00:50:00Z</published>
    <updated>2010-08-26T00:58:54Z</updated>
    
    <summary>こんにちは murahashi です。 アジャイルな開発をチームでやってみている(2010年)のですが、いざやってみると結構ハマリどころがありました。やってみたことを共有しておこうと思います。 かたちから入ろう 朝会 アジャイルな開発と言えば朝会なので、朝会から始めました。 開始時刻をメンバーで決めて、それぞれが昨日やったこと、今日やること、おしらせ、困っていること、を共有しました。 さらに、朝会前に社内wikiにメモ書き程度の項目を書いておきます。これにあらかじめ目を通すことで、一番の課題に時間を集中することができました。 アンチパターン・決めた時刻を守らない 11時から朝会始めようと決めたのに、11時過ぎに汗だくで飛び込んできて「遅れてすみません」「wiki書いてません」「wiki読んでません」というのは、チームの空気を悪くするだけでなく、単純に全員の時間を無駄にしてしまいます。 時刻...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
        <category term="symfony" />
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[こんにちは murahashi です。<br />
アジャイルな開発をチームでやってみている(2010年)のですが、いざやってみると結構ハマリどころがありました。やってみたことを共有しておこうと思います。<br /><br style="clear:left" />

<h4>かたちから入ろう</h4>
<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="acts_as_agile_armor_600_457.jpg" src="http://labs.unoh.net/acts_as_agile_armor_600_457.jpg" width="600" height="457" class="mt-image-none" style="" /></span>
<h5>朝会</h5>
アジャイルな開発と言えば朝会なので、朝会から始めました。<br />
開始時刻をメンバーで決めて、それぞれが昨日やったこと、今日やること、おしらせ、困っていること、を共有しました。<br />
さらに、朝会前に社内wikiにメモ書き程度の項目を書いておきます。これにあらかじめ目を通すことで、一番の課題に時間を集中することができました。<br /><br style="clear:left" />

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="acts_as_agile_anti_pattern_50_67.jpg" src="http://labs.unoh.net/acts_as_agile_anti_pattern_50_67.jpg" width="50" height="67" class="mt-image-left" style="float: left; margin: 0 20px 20px 0;" /></span><h5>アンチパターン・決めた時刻を守らない</h5>
11時から朝会始めようと決めたのに、11時過ぎに汗だくで飛び込んできて「遅れてすみません」「wiki書いてません」「wiki読んでません」というのは、チームの空気を悪くするだけでなく、単純に全員の時間を無駄にしてしまいます。<br />
時刻を守るか、守れるルールに変えましょう。ごめんなさい。<br /><br style="clear:left" />

<h5>できることからやろう</h5>
組み込めそうなものからとりいれてみました。<br />
具体的には、『達人プログラマー』で言う三本柱（テスト・自動化・早期デプロイ）、朝会、継続的な統合(CI)、テスト駆動開発(TDD)、テスト用データベースの独立、分散VCSを使う、などです。<br />
CI, git, TDDは一気に導入しました。テスト用データベースの独立は必要になってから取り入れました。<br /><br style="clear:left" />

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="acts_as_agile_anti_pattern_50_67.jpg" src="http://labs.unoh.net/acts_as_agile_anti_pattern_50_67.jpg" width="50" height="67" class="mt-image-left" style="float: left; margin: 0 20px 20px 0;" /></span><h5>アンチパターン・あれもこれも</h5>
一個一個見ればそれはあったほうがいいんだろうなと思いますが、きりがありません。多すぎて身構えてしまうし、開発が乗ってくるまでにどれだけかかるんだよと。<br />
かといって完全マスターしないと増やさないポリシーも意味がありません。要はバランスです。<br />
今思えばgitの導入がかなり説明不足でした。その時はsubversionをフル活用した社内ライブラリとの接合をとるのでいっぱいいっぱいでした。<br /><br style="clear:left" />

<h5>チームでやろう</h5>
今までもタスクが個人レベルまで分解されたあとは、アジャイルというかXPというか"ぼくのかんがえたTDD"でやってみてました。これが結構できるようになったなーと思ってチームでやるようになったのですが、ひとりxpとチームxpのあいだの一歩は思ったより大きかったです。<br />
逆説的ですが、ひとりxpが完璧にできるようになるのを待つ必要はないと思います。最終的にチームでやらない意味がわからないので、できることからさっさとチームで始めるのが良いと思います。<br /><br style="clear:left" />

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="acts_as_agile_anti_pattern_50_67.jpg" src="http://labs.unoh.net/acts_as_agile_anti_pattern_50_67.jpg" width="50" height="67" class="mt-image-left" style="float: left; margin: 0 20px 20px 0;" /></span><h5>アンチパターン・チームで決めた方針は変えない</h5>
合わなかったら、そして合理的な理由があればさっさと方針を変えましょう。<br />
はじめのルールのままテストがどんどん遅くなっていっているのでどうにかしたいです。<br /><br style="clear:left" />

<h5>テスト駆動で開発しよう</h5>
テストコードを先に書く事で、テスト可能な設計になります。むしろテスト可能な設計でしか書けません。テストコードが存在することで、テストコードの範囲内で正しく動くっぽいことが確認できます。テストコードがなければ、正しく動くっぽいことの確認すらできません。<br />
また、テストを壊してしまったら、CIサーバのhudsonが容赦なく "Build faild in Hudson" なるメールをガッシュガッシュ投げつけてくるので、想定の範囲内でデグレードを防ぐことができます。<br /><br style="clear:left" />

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="acts_as_agile_anti_pattern_50_67.jpg" src="http://labs.unoh.net/acts_as_agile_anti_pattern_50_67.jpg" width="50" height="67" class="mt-image-left" style="float: left; margin: 0 20px 20px 0;" /></span><h5>アンチパターン・テストを書かなければ失敗しない</h5>
テストを書かなければ失敗しないのですが、それでは意味がありません。また、通らなくなったテストをコメントアウトしたり、消したりしてテストを通してはいけません。<br />
さらに「いけません」とチームに言ってるのに「他の作業するところで影響でるから」と実装だけ修正したりすると、「なんであいつだけ」となるので以後気をつけます。<br />
ただし"たまに落ちるテスト"はどうしていいかいまだによくわかりません。<br /><br style="clear:left" />

<h5>イディオムに従う</h5>
symfonyのイディオムはjobeetなので、jobeetに従っておくと共通認識をつくりやすいです。php, symfony, lime, jobeetのイディオムから外れるときは明確に意図を持って外れるようにしましょう。<br />
「正しいunit testでは」みたいな話ばかりをしていても仕方がないので。しましたけど。﻿<br /><br style="clear:left" />

<h4>そんなこんなやってみた構成</h4>
<ul>
<li>php5.3, symfony1.4, mysql5.1, memcache, flashlite1.1</li>
<li>テスティングフレームワーク lime</li>
<li>BTS trac</li>
<li>VCS git</li>
<li>CI hudson</li>
<li>デプロイ capistrano</li>
</ul>
だいたいそんなかんじ<br /><br style="clear:left" />

<h4>やってみてのハマりどころ</h4>

<h5>アジャイルな開発のスイートスポットは新規開発?</h5>
不慣れなチームだったので別にスイートスポットではありませんでした。

<h5>ケータイ向けアプリのflash</h5>
flashが重要な役割を占めるけど、limeでテスト駆動開発...そこは割り切って開発することにしました。<br />
デプロイするcapistranoのレシピは早々に書いて動かしてたのですが、flashとphpが出来上がってきたところでがっちゃんこになりました。そして、がっちゃんこしたら結局わりきった接合面でなんでか動きません。TDDでmodelを厚く作ったのに...<br />
だいたい原因は、symfony にも不慣れな人ばかりだったので、そんなに厚くないcontroller部分の値の受け渡し部分なことが多かったです。

<h5>ケータイ向けopensocialに特有の部分</h5>
テスト用擬似ブラウザsfBrowserのclickとかredirectとか(NDAかもしれないので省略されました)<br /><br style="clear:left" />
<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="acts_as_agile_stone_wall_600_569.JPG" src="http://labs.unoh.net/acts_as_agile_stone_wall_600_569.JPG" width="600" height="569" class="mt-image-none" style="" /></span>
<h5>fixtureの硬直化がDBのschemaの硬直化を呼ぶ</h5>
fixtureをyamlで書いてそれを毎回DBにロードしてるので、スキーマ変更するたびにyamlの編集が必要になりました。テスト書き換えるだけでもしんどいとおもうひとばかりなのに、yamlをえんえん編集してるのはもっとひどくてかなりダメージを受けてました。赤→緑→リファクタリングのサイクルで脳汁が出る人ばかりではありません。自然と、 DBのschemaが硬直してしまいました。<br />
factory_girlはやくきてくれー。

<h5>全体的に</h5>
やりたいやりたい言ってる私がコーチになるどころかそんなに役に立ててないのが割と原因の一つです。でも私もわかんないよ。手探り手探りです。

<h4>あわせてよみたい</h4>
<ul>
<li><a href="http://alpha.mixi.co.jp/blog/?p=1738">mixi Engineers' Blog » いまからでも間に合う開発者テスト</a></li>
<li><a href="http://d.hatena.ne.jp/Yoshiori/20100321/1269171882">デブサミのアンケートに感動したので全部返事書いてみた 2010</a><br />
このバーンダウンチャートいいなー</li>
<li>達人プログラマー　黒い方 4-89471-274-1</li>
<li>Head First ソフトウェア開発 978-4-87311-392-0</li>
</ul>
<h4>これから</h4>
問題にぶつかったなかで、今一番多い解決策は"とりあえずわりきって前に進む"であることがおおいので、もうちょっとましな解決tipsのエントリを挙げられるといいなあと思います。<br />

以上デース]]>
        
    </content>
</entry>

<entry>
    <title>リビドーに赴くままlibmemcachedをPHPから使ってみる</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/keita.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1857" title="リビドーに赴くままlibmemcachedをPHPから使ってみる" />
    <id>tag:labs.unoh.net,2010://2.1857</id>
    
    <published>2010-08-25T11:26:14Z</published>
    <updated>2010-08-25T15:03:55Z</updated>
    
    <summary>Keita です。 浴衣姿の女性は人類の宝だと思います。 さて、phpのpeclのmemcache&apos;d&apos;ってご存知でしょうか。 長い歴史をもつpecl memcache(名前近過ぎだ）はその拡張の中でmemcachedプロトコルの実装をしていますが、pecl memcachedはlibmemcahcedにその実装を依存を任せています。 利用するときの違いとしては memcache: インストール時に他のライブラリに依存しない 長いこと使われてるライブラリなので安定性はある memcached memcacheよりも利用できる機能が多い 今後もプロトコルの拡張があったときにlibmemcachedに追従するだけなので対応が早いことが予測される という点が挙げられます。(まだまだ一杯ありますが・・） memcacheよりも利用できる機能が多いというのはかなり大きく、具体的に言うと、set時に比...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[Keita です。
<p>
浴衣姿の女性は人類の宝だと思います。
</p>
<p>さて、phpの<a href="http://pecl.php.net/package/memcached">peclのmemcache'd'</a>ってご存知でしょうか。
長い歴史をもつ<a href="http://pecl.php.net/package/memcache">pecl memcache</a>(名前近過ぎだ）はその拡張の中でmemcachedプロトコルの実装をしていますが、pecl memcachedはlibmemcahcedにその実装を依存を任せています。</p>

<p>
利用するときの違いとしては<br />

memcache:
<ul>
<li>インストール時に他のライブラリに依存しない</li>
<li>長いこと使われてるライブラリなので安定性はある</li>
</ul>
memcached
<ul>
<li>memcacheよりも利用できる機能が多い</li>
<li>今後もプロトコルの拡張があったときにlibmemcachedに追従するだけなので対応が早いことが予測される</li>
</ul>
という点が挙げられます。(まだまだ一杯ありますが・・）
</p>
<p>
memcacheよりも利用できる機能が多いというのはかなり大きく、具体的に言うと、set時に比較して他のプロセスが更新されてないかどうかを確認してから更新するという処理をアトミックにできるMemcached::casは、揮発性の高い本家のmemcached(デーモンの方）はともかく、最近の永続的なストレージとして、memcachedプロトコル互換をうたうKVSを使う時には是非とも使いたい機能だと思います。
</p>
<p>
追記:<br />
kamipoさんより、はてブのご指摘でPECL::memcacheも<a href="http://pecl.php.net/package-info.php?package=memcache&version=3.0.0">3.0.0(alpha)</a>より、casをサポートしているというご指摘いただきました。<br />
よくよく見てみると確かにCasに限らずappendやバイナリプロトコルのサポートなどいままでほしかった機能が実装されています。（どんだけやる気なんだと作者に対して強く尊敬の念をいだきました）安定性やなどかんがみると、PECL::memcacheは非常に有力な候補となると個人的には思いますのでこれは別途調査してみたいとおもいますし皆様も選択の際はぜひ頭の隅にいれていただければ幸いです。
</p>

<p>
前置きが長くなりましたが、libmemcachedとpecl memcachedの構築に本来は簡単なのになんだかとっても、手間取ったので記録を残したいと思います。
</p>

前提としては、PHP5.3.3の環境でRPMを作成する環境は整っているという前提です。

とりあえずrpmmacrosをの設定
<pre class="code">
cd ~/
echo "%_topdir `pwd`/rpmbuild" > ~/.rpmmacros
mkdir -p ~/rpmbuild/{SRPMS,RPMS/x86_64,SOURCES,SPECS,BUILD}
</pre>

libmemcachedのrpmを作成
<pre class="code">
tar zxvf libmemcached-0.39.tar.gz 
cd libmemcached-0.39
./configure
make rpm
</pre>

インストール
<pre class="code">
$ cd ~/rpmbuild/RPMS/x86_64/
$ rpm -ivh libmemcached-devel-0.39-1.x86_64.rpm libmemcached-0.39-1.x86_64.rpm 
</pre>

これで、limemcachedがインストールされました。
そのあとpeclのRPMを作るのですが、会社のRPMだとラボブログに書くちょっと癖があったりするので、ここでは、<a href="http://rpms.famillecollet.com/">Les RPM de Remi - Packages</a>からSRPMを拝借することにします。

libmemcachedも含めてここにRPMが公開されてるのでそちらをつかってさくっと入れてしまうのが楽だとは思いますが、RPM作るのに手間取ったのでその手順を書くというのが本記事の主旨なのでとりあえずこのまま進めます。

<pre class="code">
# PHPはすでに入っていて、phpizeが使える前提で・・・
$ wget http://rpms.famillecollet.com/SRPMS/php-pecl-memcached-1.0.2-1.remi.src.rpm
$ rpm -i php-pecl-memcached-1.0.2-1.remi.src.rpm
$ cd ~/rpmbuild/SPECS
$ rpmbuild -ba php-pecl-memcached.spec
$ cd ~/rpmbuild/RPMS/x86_64/
$ rpm -ivh php-pecl-memcached-1.0.2-1.x86_64.rpm
</pre>
ちなみに何を手間取ったかというと、libmemcached-0.39で、pecl-memcached-1.0.1を動かそうとしていたからずーっと動かなかったりしていたのですが。
<a href="http://pecl.php.net/package-changelog.php?package=memcached&release=1.0.2">Change Log</a>にしっかり書いてありました。
それに気がついた時いろいろ雄たけびあげました。

何はともあれ、これで安心してCasが使えます。動作としてはこんな感じのイメージです。
<pre class="code">
&lt;?php
$memcached = new memcached();
$memcached->addServer('localhost', 11211);

$key = 'hoge';
$value1 = 'fuga';
$value2 = 'mote';

$memcached->set($key, $value1);
$cas = null;
$resultValue = $memcached->get($key, null, $cas);
assert($resultValue === $value1); //fugaが返される

$success = $memcached->cas($cas, $key, $value2);
$resultValue = $memcached->get($key, null, $cas);

assert($success === true); //更新は成功
assert($resultValue === $value2); //更新されmoteが返される


$memcached->set($key, $value1);
$resultValue = $memcached->get($key, null, $cas);

$memcached->set($key, $value1); // 取得したあとに誰かがもう一度セットしたとする

//更新しようとする
$success = $memcached->cas($cas, $key, $value2);

assert($success === false); //更新失敗
assert($resultValue === $value1); //更新されずhogeが返される
</pre>


そんなわけで、どうでもいいですが、僕は、秋祭り、一緒に浴衣でいってくれる女子を募集中です。
記事ともどもご参考になれば幸いです。]]>
        
    </content>
</entry>

<entry>
    <title>線画のゴミ除去方法</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/post_149.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1853" title="線画のゴミ除去方法" />
    <id>tag:labs.unoh.net,2010://2.1853</id>
    
    <published>2010-08-19T05:53:25Z</published>
    <updated>2010-08-19T07:11:36Z</updated>
    
    <summary>初めまして。ウノウでデザイナーをしているyamamotoです。最近は色々と便利な本が出ているので、当たり前の技術になっていますが知っているとチョビット便利なフォトショップ上でのゴミの取り方をご紹介します。画像データはフォトショップ上にて直接描いてしまったサンプルですが実際は紙に描いたものをスキャニングしたものだと仮定して下さい。スキャニングし線を抽出するとどうしてもゴミがついてきてしまいます。そこで便利なゴミの取り方のご紹介です。１：線画部分だけを抽出した状態レイヤー部分をダブルクリックしレイヤースタイルを表示します※フォトショップ上レイヤータブからもレイヤースタイルを選択できます。この時、境界線を選択し、塗りつぶしのタイプをカラーにしカラーをわかりやすい色にします。ここでは赤を選択しています。描画モードを通常にし位置を外側にします。※大抵はカラー以外はデフォルト設定で大丈夫だと思います。...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[初めまして。ウノウでデザイナーをしているyamamotoです。<br /><br />最近は色々と便利な本が出ているので、当たり前の技術になっていますが<br />知っているとチョビット便利なフォトショップ上でのゴミの取り方をご紹介します。<br /><br />画像データはフォトショップ上にて直接描いてしまったサンプルですが<br />実際は紙に描いたものをスキャニングしたものだと仮定して下さい。<br /><br />スキャニングし線を抽出するとどうしてもゴミがついてきてしまいます。<br />そこで便利なゴミの取り方のご紹介です。<br /><br /><br />１：線画部分だけを抽出した状態<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/%E3%83%A9%E3%83%9C%E3%83%96%E3%83%AD%E3%82%B0001-193.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/ラボブログ001-193.html','popup','width=681,height=488,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/%E3%83%A9%E3%83%9C%E3%83%96%E3%83%AD%E3%82%B0001-thumb-200x143-193.jpg" alt="ラボブログ001.jpg" class="mt-image-none" style="" height="143" width="200" /></a></span><br /><div><br /><br />レイヤー部分をダブルクリックしレイヤースタイルを表示します<br />※フォトショップ上レイヤータブからもレイヤースタイルを選択できます。<br /><br />この時、境界線を選択し、塗りつぶしのタイプをカラーにし<br />カラーをわかりやすい色にします。ここでは赤を選択しています。<br /><br />描画モードを通常にし位置を外側にします。<br />※大抵はカラー以外はデフォルト設定で大丈夫だと思います。<br /><br />自分の見やすいサイズを選択しOKを押します。<br /><br />２：レイヤースタイルを表示した状態<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/%E3%83%A9%E3%83%9C%E3%83%96%E3%83%AD%E3%82%B0002-196.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/ラボブログ002-196.html','popup','width=681,height=488,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/%E3%83%A9%E3%83%9C%E3%83%96%E3%83%AD%E3%82%B0002-thumb-200x143-196.jpg" alt="ラボブログ002.jpg" class="mt-image-none" style="" height="143" width="200" /></a></span><br /><br />上記の行程を行うと線画に赤い線がつきます<br />また、キャラクター以外のヌキの部分のゴミにも同様に<br />赤い線がつくのでわかりやすいと思います。<br /><br />後は消しゴムツールでゴミの部分を消してあげると奇麗になりますが<br />取りきれない部分もありますのである程度は自分で確認する必要もあります。<br /><br /><br />３：境界線が選ばれている状態<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/%E3%83%A9%E3%83%9C%E3%83%96%E3%83%AD%E3%82%B0003-199.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/ラボブログ003-199.html','popup','width=681,height=488,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/%E3%83%A9%E3%83%9C%E3%83%96%E3%83%AD%E3%82%B0003-thumb-200x143-199.jpg" alt="ラボブログ003.jpg" class="mt-image-none" style="" height="143" width="200" /></a></span><br /><br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/%E3%83%A9%E3%83%9C%E3%83%96%E3%83%AD%E3%82%B0004-202.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/ラボブログ004-202.html','popup','width=681,height=488,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/%E3%83%A9%E3%83%9C%E3%83%96%E3%83%AD%E3%82%B0004-thumb-200x143-202.jpg" alt="ラボブログ004.jpg" class="mt-image-none" style="" height="143" width="200" /></a></span><br /><br />この後は設定したレイヤースタイルをそのままドラッグして<br />
ゴミ箱に捨てれば完成です。<br /><br /><br /><br /><br /><br /><br /><br />３：線画完成！<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/%E3%82%89%E3%81%BC%E3%81%B6%E3%82%8D-205.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/らぼぶろ-205.html','popup','width=952,height=1304,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/%E3%82%89%E3%81%BC%E3%81%B6%E3%82%8D-thumb-200x273-205.jpg" alt="らぼぶろ.jpg" class="mt-image-none" style="" height="273" width="200" /></a></span><br /><br />あとはリビドーのままに塗ってください！<br />おつかれさまでした！<br /><br /><br /></div><div><br /><br /><br /></div>]]>
        
    </content>
</entry>

<entry>
    <title>PNG画像書き出しの「あとちょっと軽くできたら...」を可能にするファイル作成TIPS</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/pngtips.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1851" title="PNG画像書き出しの「あとちょっと軽くできたら...」を可能にするファイル作成TIPS" />
    <id>tag:labs.unoh.net,2010://2.1851</id>
    
    <published>2010-08-19T02:22:04Z</published>
    <updated>2010-08-19T02:36:53Z</updated>
    
    <summary>こんにちは。ウノウでデザイナーをしているChristelです。 銀色の全身スーツを着、透明チューブの中を走る車で移動する21世紀で暮らす私たち。夢のクラウド化が囁かれる昨今ですが、未だ携帯電話関連の画像作成には、スカイツリーよりも高い容量の壁があります。「Flashの3階層メニューで文字も画像で100kb以内でよろしく」とか「あれとこれとそのファイル合わせて一回で表示するのは20kb以内でね」など、関係者の皆さんは、1バイトに泣き、1バイトに笑う日々を送っておられることでしょう。 今日はそんな皆さんに「ビットマップデータを透過PNGで書き出すとき、クオリティを下げずにサイズをちょっとだけ減らせる画像作成のコツ」 をご紹介します。ニッチ過ぎて申し訳ないです。 １）まずPhotoshopで画像を作成 ↑ 一世を風靡した感のあるWEB2.0風。画像の痛さには目を瞑ってください。 ２）そのまま背景...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
        <category term="Tips" />
    
        <category term="ネタ" />
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[こんにちは。ウノウでデザイナーをしているChristelです。<br />

<p>銀色の全身スーツを着、透明チューブの中を走る車で移動する21世紀で暮らす私たち。<br />夢のクラウド化が囁かれる昨今ですが、未だ携帯電話関連の画像作成には、スカイツリーよりも高い容量の壁があります。<br />「Flashの3階層メニューで文字も画像で100kb以内でよろしく」とか<br />「あれとこれとそのファイル合わせて一回で表示するのは20kb以内でね」など、<br />関係者の皆さんは、1バイトに泣き、1バイトに笑う日々を送っておられることでしょう。</p>

<p>今日はそんな皆さんに<br />「ビットマップデータを透過PNGで書き出すとき、クオリティを下げずにサイズをちょっとだけ減らせる画像作成のコツ」<br />
をご紹介します。</p><p>ニッチ過ぎて申し訳ないです。</p><p><br /></p>

<p><font class="Apple-style-span" style="font-size: 1.25em; "><b>１）まずPhotoshopで画像を作成</b></font></p>

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_1-163.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_1-163.html','popup','width=273,height=192,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/miyakami_1-thumb-200x140-163.jpg" width="200" height="140" alt="miyakami_1.jpg" class="mt-image-none" style="" /></a></span>

<br />↑ 一世を風靡した感のあるWEB2.0風。画像の痛さには目を瞑ってください。<p><br /></p>

<p><font class="Apple-style-span" style="font-size: 1.25em; "><b>２）そのまま背景透過のPNG24で書き出します</b></font></p>

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_2-166.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_2-166.html','popup','width=332,height=128,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/miyakami_2-thumb-200x77-166.jpg" width="200" height="77" alt="miyakami_2.jpg" class="mt-image-none" style="" /></a></span>

<br />↑背景透過、マットなし、のPNG24です<br /><br />

15.3kbのPNG画像ができました<br />

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_3-169.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_3-169.html','popup','width=436,height=481,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/miyakami_3-thumb-200x220-169.jpg" width="200" height="220" alt="miyakami_3.jpg" class="mt-image-none" style="" /></a></span><div><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_3-169.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_3-169.html','popup','width=436,height=481,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"></a></span><br />

<p>...と、ここまではフツー。</p><p><br /></p>

<p><font class="Apple-style-span" style="font-size: 1.25em; "><b>３）それではここでFireworksを立ち上げましょう</b></font></p>

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_4-172.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_4-172.html','popup','width=185,height=167,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/miyakami_4-thumb-200x180-172.jpg" width="200" height="180" alt="miyakami_4.jpg" class="mt-image-none" style="" /></a></span>

<br />私たちの未来を明るく照らしだすFireworks先生。ゴールドカラー（きいろとも言う）がまばゆい。<p><br /></p>

<p><font class="Apple-style-span" style="font-size: 1.25em; "><b>４）先ほどのPSDファイルを読み込み、そのまま背景透過のPNG32で書き出します</b></font></p>

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_5-175.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_5-175.html','popup','width=241,height=97,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/miyakami_5-thumb-200x80-175.jpg" width="200" height="80" alt="miyakami_5.jpg" class="mt-image-none" style="" /></a></span>

<br />背景透過、マットなし、のPNG32です<p><br /></p>

<p><font class="Apple-style-span" style="font-size: 1.25em; "><b>５）ではいま作成したファイルサイズを見てみましょう</b></font></p>

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_6-178.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_6-178.html','popup','width=435,height=480,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/miyakami_6-thumb-200x220-178.jpg" width="200" height="220" alt="miyakami_6.jpg" class="mt-image-none" style="" /></a></span>

<br />14.7kbのPNGファイルができました<p><br /></p>

<p><big><strong><font color="yellow">...なんということでしょう～!!</font></strong></big></p><p>Photoshop書き出し15.3kb<br />
Fireworks書き出し14.7kb</p>

<p>Photoshopで作成したファイルより軽い♪</p>

<p>二つのファイルを比べてみてもほとんど遜色ありません。</p>

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_7-181.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_7-181.html','popup','width=614,height=271,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/miyakami_7-thumb-200x88-181.jpg" width="200" height="88" alt="miyakami_7.jpg" class="mt-image-none" style="" /></a></span><div><br /></div><div>

<p><font class="Apple-style-span" style="font-size: 1.25em; "><b>ありがとう、ありがとうFireworks先生！！</b></font></p><p><br /></p>

<p>...端的に申し上げると「Fireworksを使え」という事ですが、意外と皆さんご存知ないようです。<br />大量のPNGを使用するFlashや携帯サイト等では差が歴然です。<br />小さな差異を笑ってはいけません。<br />また、GIFの場合もFireworksを使うと若干軽くなる場合が多いです。</p><p><br /></p>

<hr>

<p>★☆ ↓↓おまけ↓↓ ☆★</p>

<p><font class="Apple-style-span" style="font-size: 1.25em; "><b>◆◆試してみよう～...その1◆◆</b></font></p><p>ちなみにFireworksによるJpeg画像の書き出しではどうでしょうか。<br />同じ写真をPhotoshopとFireworksで同じ設定で書き出してみました。</p><p><br /></p>

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_8-184.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_8-184.html','popup','width=200,height=300,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/miyakami_8-thumb-200x300-184.jpg" width="200" height="300" alt="miyakami_8.jpg" class="mt-image-none" style="" /></a></span>

<br />元の写真。みかんゼリーおいしい。<p><br /></p>

画質80、最適化チェック、マットなしのJpeg書き出しです<br />

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_9-187.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_9-187.html','popup','width=838,height=646,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/miyakami_9-thumb-200x154-187.jpg" width="200" height="154" alt="miyakami_9.jpg" class="mt-image-none" style="" /></a></span>

<p>プロパティを見てみると</p><p>左：Photoshop書き出し19.2kb<br />右：Fireworks書き出し10.6kb</p><p><br /></p>

<p>Fireworks書き出しの方が俄然軽いですが、やはり画像が荒くなっています。<br />しかしながら目を覆うほどの劣化ではないので、臨機応変に使い分けて頂ければ良いでしょう。</p><p><br /></p>

<hr>

<p><font class="Apple-style-span" style="font-size: 1.25em; "><b>◆◆試してみよう～...その2◆◆</b></font></p><p>では更に同じ写真をPNGで書き出したらどうでしょうか。</p><p>Photoshop 背景透過、マットなし、のPNG24<br />Fireworks 背景透過、マットなし、のPNG32<br />
で書き出し。</p>

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/miyakami_10-190.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/miyakami_10-190.html','popup','width=908,height=726,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/miyakami_10-thumb-200x159-190.jpg" width="200" height="159" alt="miyakami_10.jpg" class="mt-image-none" style="" /></a></span>

<p>左：Photoshop書き出しのファイル<br />右：Fireworks書き出しのファイル</p><p><br /></p><p>とってもきれいでどちらも遜色ありません。</p>

<p>さてファイルのサイズはというと...&nbsp;</p><p>Photoshop書き出し75.2kb<br />Fireworks書き出し84.2kb</p>

<p>と非常に残念な結果に...。</p><p><br /></p><p>書き出しは用途を考えて正しく行いましょう～！</p><p><br /></p>

<p>では、素敵な毎日を。。。</p><p><br /></p><p><br /></p>
</div></div>]]>
        
    </content>
</entry>

<entry>
    <title>MySQLのチューニングのためのデータの集め方</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/how-to-collect-queries-for-tuning-mysql.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1849" title="MySQLのチューニングのためのデータの集め方" />
    <id>tag:labs.unoh.net,2010://2.1849</id>
    
    <published>2010-08-13T06:17:23Z</published>
    <updated>2010-08-13T15:13:58Z</updated>
    
    <summary>いつの間にか会社で古株になったyamaokaです。 webアプリケーションのバックエンドにMySQLを使っている場合、 クエリ（SQL）のチューニングをする必要がありますよね。 皆さんはチューニングの計画をどのように立てていますか。 もちろん、既に明らかに重いことが想定されているページがあれば、 その処理で使われているクエリを中心にEXPLAINなどを使って解析していけばいいと思います。 でもそうではなく、全体的にクエリの見直しやチューニングを行いたい場合は 実際に実行されているクエリを確認していくという作業が必要です。 そこで使うことができる3つの方法について書きたいと思います。 遅いクエリを記録する MySQLにはスロークエリログといって、 実行に時間がかかったクエリを記録する機能が最初から付いています。 /etc/my.cnfに次のように設定を書けば実行時間が1秒を超えたクエリが出力...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
        <category term="Tips" />
    
        <category term="mysql" />
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[<p>いつの間にか会社で古株になったyamaokaです。</p>

<p>
webアプリケーションのバックエンドにMySQLを使っている場合、
クエリ（SQL）のチューニングをする必要がありますよね。
皆さんはチューニングの計画をどのように立てていますか。
</p>

<p>
もちろん、既に明らかに重いことが想定されているページがあれば、
その処理で使われているクエリを中心にEXPLAINなどを使って解析していけばいいと思います。
</p>

<p>
でもそうではなく、全体的にクエリの見直しやチューニングを行いたい場合は
実際に実行されているクエリを確認していくという作業が必要です。
そこで使うことができる3つの方法について書きたいと思います。
</p>

<h3>遅いクエリを記録する</h3>
<p>
MySQLにはスロークエリログといって、
実行に時間がかかったクエリを記録する機能が最初から付いています。
/etc/my.cnfに次のように設定を書けば実行時間が1秒を超えたクエリが出力されるようになります。
<pre class="code">slow_query_log = 1
slow_query_log_file = /path/to/mysql-slow.log
long_query_time = 1</pre>
オンラインでset globalを使って変更する場合は次のようにします。
<pre class="code">set global slow_query_log = 1;
set global slow_query_log_file = '/path/to/mysql-slow.log';
set global long_query_time = 1;
</pre>
</p>
<p>
出力されたログファイルをMySQLに付属しているmysqldumpslowというツールで解析すると便利です。次のように実行すれば、平均の実行時間が長い順にソートして表示してくれます。
<pre class="code">mysqldumpslow -s at /path/to/mysql-slow.log</pre>
クエリのパラメータは数値はN、文字列はSに置換して表示してくれるので
同じクエリをまとめてチェックすることができます。
</p>

<h3>実行回数の多いクエリを記録する</h3>
<p>
実行時間は短いけれど多くの回数実行されるクエリというのもあります。
これらは通常のスローログには出てこないのですが、
実は負荷の大部分を占めている、ということもありえます。
キャッシュなど別の方法を考えることで
アプリケーションの負荷を減らすことができるかもしれません。
</p>
<p>
従来MySQLではlong_query_timeに1秒以上の値しか設定できませんでしたが、
MySQL 5.1から1秒未満の値も設定できるようになりました。
つまり、次のように/etc/my.cnfで「0」を設定すれば
全てのクエリが記録できることになります。
<pre class="code">slow_query_log = 1
slow_query_log_file = /path/to/mysql-slow.log
long_query_time = 0</pre>
</p>
<p>
もちろん、全てのクエリの記録は負荷も大きくかかることを
理解してから設定を行うようにしてください。
また、ログの容量も大きくなるので、ディスクの空き容量にも注意が必要です。
オンラインでset globalを使って一時的に値を0に変更し、すぐに元の値（1など）に戻すのがオススメです。
<pre class="code">set global slow_query_log = 1;
set global slow_query_log_file = '/path/to/mysql-slow.log';
set global long_query_time = 0;
# 後で set global long_query_time = 1; で元に戻す</pre>
</p>
<p>
出力されたログファイルをmysqldumpslowで次のように解析することで、
実行回数の多い順に表示することが可能です。
<pre class="code">mysqldumpslow -s c /path/to/mysql-slow.log</pre>
</p>
<p>
mysqldumpslowは他にもいろいろ機能を持っているので、「--help」を付けて実行して
一度オプションを確認してみるといいかもしれません。
</p>
<p>
追記: MySQLでは/etc/my.cnfに次のような設定をすることで全てのクエリを記録することが可能です。
最初から全ての記録を保存したい場合はこちらの方法もいいかもしれません。
<pre class="code">log = /path/to/mysql-query.log</pre>
</p>

<h3>インデックスを使っていないクエリを記録する</h3>
<p>
多くの場合、インデックスを使用しないクエリは遅いです。
インデックスの設計は計画的に行う必要があると思いますが、
今現在インデックスを使用していないクエリはどれなのか知りたい場合があると思います。
次のように/etc/my.cnfに記述することでインデックスを使用していないクエリを
スロークエリログに記録することができるようになります。
<pre class="code">slow_query_log = 1
slow_query_log_file = /path/to/mysql-slow.log
long_query_time = 5
log_queries_not_using_indexes = 1</pre>
オンラインでset globalを使って設定する場合は次のようにします。
<pre class="code">set global slow_query_log = 1;
set global slow_query_log_file = '/path/to/mysql-slow.log';
set global long_query_time = 5;
set global log_queries_not_using_indexes = 1;</pre>
</p>
<p>
出力されたログファイルは今までと同じように
mysqldumpslowを使って解析していくことになると思います。
</p>

<h3>終わりに</h3>
<p>
最近は、開発するときにフレームワークに付属のORマッパーを使ったりして
実際に発行されるクエリを意識しないことが多くなっていると思います。
もちろんそれはメリットだと思うのですが、実際に実行されるのはクエリ（SQL）である以上、
完全に意識しないで済むということはありません。
</p>
<p>
実際に発行されているクエリを眺めつつ、
少しでもパフォーマンスのよいwebアプリケーションを作っていけたらいいと思います。
</p>

<p>追記: オプションの変数名がMySQL 5.1基準のものになっていなかったのでMySQL 5.1をベースに修正しました。</p>
]]>
        
    </content>
</entry>

<entry>
    <title>JavaライブラリでOAuth認証</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/javaoauth.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1842" title="JavaライブラリでOAuth認証" />
    <id>tag:labs.unoh.net,2010://2.1842</id>
    
    <published>2010-08-12T10:28:17Z</published>
    <updated>2010-08-12T09:01:02Z</updated>
    
    <summary>ライブラリをつかってJava環境でOAuth認証を行う方法</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
        <category term="Java" />
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[<p>みなさん、こんにちは。
７月にウノウに入社しました細川です。</p>

<p>私は、これまでいろいろなオープンソースの恩恵にあずかってきましたが、こちらから貢献をしたことは、ほとんどありませんでした。この記事がお役に立てれば幸いです。</p>

<p>ウノウ入社前にはJavaを主に使っていましたので、今回は、JavaのOAuthライブラリを使う方法について書いてみたいと思います。</p>

<p><font class="Apple-style-span" style="font-size: 1.25em; "><font class="Apple-style-span" style="font-size: 1.25em; ">OAuthとは</font></font></p><p><a href="http://ja.wikipedia.org/wiki/OAuth">OAuth</a>は、セキュアな認証手段として多く使われてきています。
twitterもweb APIの認証手段として採用しています。
OAuthを使った認証を行うことで、ユーザーはアカウントやパスワードを知られることなく、第三者サービスにAPIの使用を許可することができます。また、その認証は第三者サービスに関係なく取り消すことができます。</p>

<p>OAuth Community&nbsp;<a href="http://oauth.net/" style="text-decoration: underline; ">http://oauth.net/</a></p>

<p>OAuthコミュニティが各種プラットフォーム向けのライブラリを公開していますので、今回は、Javaのライブラリをビルドして使用します。</p>

<p>Code - OAuth&nbsp;<a href="http://oauth.net/code/">http://oauth.net/code/</a></p>

<p></p><p>Repository&nbsp;<a href="http://oauth.googlecode.com/svn/code/">http://oauth.googlecode.com/svn/code/</a></p><p><font class="Apple-style-span" style="font-size: 1.25em; "><font class="Apple-style-span" style="font-size: 1.25em; ">OAuth Library をビルドする</font></font></p><p>Eclipseを使ったビルドを紹介します。</p><p>新規プロジェクトを生成し、ショートカットメニューから「インポート」を選択します</p><p></p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/import_project_from_svn-125.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/import_project_from_svn-125.html','popup','width=470,height=550,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/import_project_from_svn-thumb-200x234-125.jpg" width="200" height="234" alt="import_project_from_svn.JPG" class="mt-image-none" style="" /></a></span><p></p><p></p><div>SVNからプロジェクトを選択」を選択し、次へ</div><div><br /></div><div><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/check_out_from_svn-128.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/check_out_from_svn-128.html','popup','width=499,height=550,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/check_out_from_svn-thumb-200x220-128.jpg" width="200" height="220" alt="check_out_from_svn.JPG" class="mt-image-none" style="" /></a></span></div><div>ロケーションに<a href="http://oauth.googlecode.com/svn/code/" style="text-decoration: underline; ">http://oauth.googlecode.com/svn/code/</a>&nbsp;を指定します。</div><div><br /></div><div><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/select_check_out_folder-131.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/select_check_out_folder-131.html','popup','width=499,height=550,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/select_check_out_folder-thumb-200x220-131.jpg" width="200" height="220" alt="select_check_out_folder.JPG" class="mt-image-none" style="" /></a></span></div><div>フォルダ「java」を選択します。</div><div><br /></div><div><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/select_check_out_option-134.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/select_check_out_option-134.html','popup','width=499,height=550,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/select_check_out_option-thumb-200x220-134.jpg" width="200" height="220" alt="select_check_out_option.JPG" class="mt-image-none" style="" /></a></span></div><div>チェックアウトオプションは上画像のように選択してください。</div><div><br /></div><div>チェックアウトできましたら、次にプロジェクトの設定を行います。</div><div><a href="http://labs.unoh.net/assets_c/2010/08/build_path_source-thumb-200x160-137-138.html" style="text-decoration: underline; "><img src="http://labs.unoh.net/assets_c/2010/08/build_path_source-thumb-200x160-137-thumb-200x160-138.jpg" width="200" height="160" alt="build_path_source.JPGのサムネール画像" class="mt-image-none" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-style: initial; border-color: initial; " /></a></div><div>Default output folderに「oauth/bin」を設定します。</div><div>また、Sourceタブから上画像のようにフォルダをパスに設定します。</div><div><br /></div><div><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/build_path_libraries-140.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/build_path_libraries-140.html','popup','width=677,height=543,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/build_path_libraries-thumb-200x160-140.jpg" width="200" height="160" alt="build_path_libraries.JPG" class="mt-image-none" style="" /></a></span></div><div>LibrariesタブからSVNからチェックアウトしたlibフォルダ以下にあるライブラリをビルドパスに追加します。</div><div>これで、ライブラリがビルドされるはずです。</div><div><br /></div><div>このままですと、classファイルがばらばらになっている状態ですので、Fat Jar Eclipse Plug-Inを使って、Jarファイルにアーカイブしましょう。インストールは<a href="http://sourceforge.net/projects/fjep/">こちら</a>から。</div><div><br /></div><div>Fat-Jarは、実行形式のJar ファイルを簡単に作れたり、参照する外部JarファイルライブラリもJar内に配置できたりする優れものですが、今回は、単純なアーカイブを作成します。</div><p></p><div>Fat-Jarをインストールしたら、プロジェクト上でショートカットメニューを開き、「Build Fat Jar」を選択します。</div><div><br /></div><div><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/configure_fat_jar-145.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/configure_fat_jar-145.html','popup','width=469,height=492,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/configure_fat_jar-thumb-200x209-145.jpg" width="200" height="209" alt="configure_fat_jar.JPG" class="mt-image-none" style="" /></a></span></div><div>Jarアーカイブの名前を設定して「Finish」で作成されます。</div><div><br /></div><div><font class="Apple-style-span" style="font-size: 1.25em; "><font class="Apple-style-span" style="font-size: 1.25em; ">OAuthライブラリを使う</font></font></div><div>今回は、Twitterを例にとって、サーブレットから認証、APIアクセスを行うこととします。</div><div><br /></div><div>Twitterのアカウントをもっている方なら、<a href="http://twitter.com/apps">Twitterアプリケーション</a>&nbsp;からアプリケーションを登録することができますので、実際に試したい場合には登録してください。</div><div>登録が完了すると、アプリケーションがOAuth認証に必要とする2つのキーと認証のためにアクセスする３つのURLが手に入ります。</div><div><br /></div><div>これらの提供された値とOAuthライブラリの主に4つのクラスを用いて認証を行い、Twitter APIにアクセスしてみましょう。</div><div><br /></div><div><font class="Apple-style-span" style="font-size: 1.25em; ">４つのクラス</font></div><div><div>net.oauth.client.OAuthClient</div><div>通信を行うクラス</div><div>実際に使用されるHTTP通信の実装をラップします。</div><div><br /></div><div>net.oauth.OAuthServiceProvider</div><div>プロバイダを定義しているクラス</div><div>プロバイダから提供されるURLをラップします。</div><div><br /></div><div>net.oauth.OAuthConsumer</div><div>コンシューマ(第三者サービス)を定義しているクラス</div><div>コンシューマ・キー、コンシューマ・シークレット、コールバックURLなど、コンシューマに結びつく値を扱います。</div><div><br /></div><div>net.oauth.OAuthAccessor</div><div>アクセスを定義しているクラス</div><div>アクセス・トークン、リクエスト・トークン、トークン・シークレットなど、個々の認証に関わる値を扱います。</div></div><div><br /></div><div><font class="Apple-style-span" style="font-size: 1.25em; ">認証URLを生成する</font></div><div>OAuth認証において認証はTwitter（プロバイダ）が行い、その結果がリダイレクトによりサイト（コンシューマ）に通知されます。</div><div>ですから、まず、認証先へのURLを生成し、それをユーザーにクリックしてもらうか、リダイレクトして認証先に移動させる必要があります。以下に、リダイレクトにより認証先に飛ばすサーブレットのdoGetメソッドを示します。</div>

<pre class="code">protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	
	String destUrl = request.getParameter("dest");

	OAuthClient client = new OAuthClient( new URLConnectionClient());
	OAuthServiceProvider provider = new OAuthServiceProvider( REQUEST_TOKEN_URL, AUTHORIZE_URL, ACCESS_TOKEN_URL);
	OAuthConsumer consumer = new OAuthConsumer( CALL_BACK_URL,
			CONSUMER_KEY, COMSUMER_SECRET, provider);
	OAuthAccessor accessor = new OAuthAccessor( consumer);
		
	String redirectTo = null;
	try{
		try {
 			//get request token first from Twitter.com
 			HashMap<string,string> params = new HashMap<string, string="">();
			params.put( "oauth_callback", 
					OAuth.addParameters(accessor.consumer.callbackURL,
							"dest", destUrl));
			//get request token first from Twitter.com
			client.getRequestToken(accessor, null, params.entrySet());
		} catch (OAuthException e) {
			throw new OperationFailedException( "It failed to authenticate Twitter account", e);
		} catch (URISyntaxException e) {
			throw new OperationFailedException( "It failed to authenticate Twitter account", e);
		}
			
		//build redirect path to twitter authentication page
		redirectTo = OAuth.addParameters(
				accessor.consumer.serviceProvider.userAuthorizationURL,//auth URL(twitter.com)
				"oauth_token", accessor.requestToken//
				);
	} catch (IOException e) {
		throw new RuntimeException( "It failed to authenticate Twitter account", e);
	}

	response.sendRedirect( redirectTo);
}
</string,></string,string></pre>最初の部分ですが、ここでは、OAuthClientをもっとも単純なURLConnectionクラスを使うように初期化しています。
<pre class="code">OAuthClient client = new OAuthClient( new URLConnectionClient());</pre>
Jakarta Commons HttpClientのv3やv4を使用することもできます。その場合にはそれぞれnet.oauth.client.HttpClient3、net.oauth.client.HttpClient4クラスを使って初期化します。しかし、Google App Engine for Java環境では、スレッドが使えない関係で、URLConnectionClientしか動作しません。&nbsp;
<div><br /></div><div>次に、OAuthライブラリの各クラスをTwitter（プロバイダ）から提供されたパラメータを使って初期化しています。
<pre class="code">	OAuthServiceProvider provider = new OAuthServiceProvider( REQUEST_TOKEN_URL, AUTHORIZE_URL, ACCESS_TOKEN_URL);
	OAuthConsumer consumer = new OAuthConsumer( CALL_BACK_URL,
			CONSUMER_KEY, COMSUMER_SECRET, provider);
	OAuthAccessor accessor = new OAuthAccessor( consumer);
</pre>アプリケーション登録で手に入れたパラメータを使って各クラスを初期化しています。</div><div><br /></div>

次のパートでは、パラメータを積んでTwitter（プロバイダ）と通信を行い、リクエストトークンを受け取ります。
<pre class="code"> 			//get request token first from Twitter.com
 			HashMap<string,string> params = new HashMap<string, string="">();
			params.put( "oauth_callback", 
					OAuth.addParameters(accessor.consumer.callbackURL,
							"dest", destUrl));
			//get request token first from Twitter.com
			client.getRequestToken(accessor, null, params.entrySet());
</string,></string,string></pre>
リクエストトークンは認証をリクエストする際に必要になります。&nbsp;<div>パラメータ"oauth_callback"は、リダイレクトによるコールバックが呼び出すURLを指定します。アプリケーション登録でコールバックURLは登録しているのですが、ここではそのURLにパラメータ"dest"を追加するために使っています。"oauth_callback"を指定しない場合、アプリケーション登録で登録したURLにリダイレクトされます。</div><div><br /></div><div>&nbsp;次のパートでは、getRequestTokenでTwitter（プロバイダ）から取得されたリクエストトークンを取り出し、認証先URLを生成しています。
<pre class="code">		//build redirect path to twitter authentication page
		redirectTo = OAuth.addParameters(
				accessor.consumer.serviceProvider.userAuthorizationURL,//auth URL(twitter.com)
				"oauth_token", accessor.requestToken//
				);
</pre></div>

うまくいけば、ユーザーは以下のようなTwitterのページに誘導されます。Twitterと連携するWebアプリケーションを使っている方なら、見たことがあるのではないでしょうか。


<div><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/twitter_auth_confirmstion-156.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/twitter_auth_confirmstion-156.html','popup','width=798,height=553,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/twitter_auth_confirmstion-thumb-200x138-156.jpg" width="200" height="138" alt="twitter_auth_confirmstion.JPG" class="mt-image-none" style="" /></a></span></div><div>ユーザーが「許可する」もしくは「拒否する」をクリックすると、<span class="Apple-style-span" style="font-family: monospace; white-space: pre; ">"oauth_callback"で指定したURLにリダイレクトされます。</span></div><div><span class="Apple-style-span" style="font-family: monospace; white-space: pre; ">次にコールバック先での処理を見てみましょう。</span></div><div><span class="Apple-style-span" style="font-family: monospace; white-space: pre; "><br /></span></div><div><font class="Apple-style-span" style="font-size: 1.25em; ">コールバックでの処理</font></div>
<pre class="code">protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	
	String destUrl = request.getParameter( "dest");
	
	String requestToken = request.getParameter(OAuth.OAUTH_TOKEN);
	if( requestToken == null){
		//rejected by USER
		response.sendRedirect( destUrl);
		return;
	}
	
	String verifire = request.getParameter(OAuth.OAUTH_VERIFIER);
	if( verifire == null){
		//rejected by USER
		response.sendRedirect( destUrl);
		return;
	}
	
	OAuthClient client = new OAuthClient( new URLConnectionClient());
	OAuthServiceProvider provider = new OAuthServiceProvider( REQUEST_TOKEN_URL, AUTHORIZE_URL, ACCESS_TOKEN_URL);
	OAuthConsumer consumer = new OAuthConsumer( CALL_BACK_URL,
			CONSUMER_KEY, COMSUMER_SECRET, provider);
	OAuthAccessor accessor = new OAuthAccessor( consumer);
	
	accessor.requestToken = requestToken;
	
	try {
		HashMap<string,string> params = new HashMap<string, string="">();
		params.put( OAuth.OAUTH_VERIFIER, verifire);
		//get access token and secret from twitter.com
		client.getAccessToken(accessor, null, params.entrySet());
	} catch (OAuthException e) {
		throw new RuntimeException( "It failed to authenticate Twitter account", e);
	} catch (URISyntaxException e) {
		throw new RuntimeException( "It failed to authenticate Twitter account", e);
	}
	
	
	//Retrieve user's information
	OAuthMessage oMessage = null;
	try {
		oMessage = accessor.newRequestMessage("GET", "http://api.twitter.com/1/account/verify_credentials.json", null);
	} catch (OAuthException e) {
		throw new RuntimeException( "It failed to authenticate Twitter account", e);
	} catch (URISyntaxException e) {
		throw new RuntimeException( "It failed to authenticate Twitter account", e);
	}

	OAuthResponseMessage rMessage = client.access(oMessage, ParameterStyle.AUTHORIZATION_HEADER);
	int status = rMessage.getHttpResponse().getStatusCode();
	if( status == HttpResponseMessage.STATUS_OK){
		String jsonStr = rMessage.readBodyAsString();
		JSONObject jObj = JSONObject.fromObject(jsonStr);
		String userId = jObj.optString("id");
		accessor.setProperty( "id", userId);
	}else{
		throw new RuntimeException( "It failed to authenticate Twitter account STATUS CODE:"+status);
	}
	
	//Store access token and secret to Cookie
	TwitterAPIUtil.storeTokenToCookie(accessor, response);
	
	response.sendRedirect( destUrl);
}  	
</string,></string,string></pre>
まず、認証によりTwitter（プロバイダ）から付け加えられたパラメータを取得しています。
<pre class="code">	String requestToken = request.getParameter(OAuth.OAUTH_TOKEN);
	if( requestToken == null){
		//rejected by USER
		response.sendRedirect( destUrl);
		return;
	}
	
	String verifire = request.getParameter(OAuth.OAUTH_VERIFIER);
	if( verifire == null){
		//rejected by USER
		response.sendRedirect( destUrl);
		return;
	}
</pre>
これらがセットされていなかった場合、ユーザーが認証を拒否したと考えられますので、あらかじめコールバックURLに追加したパラメータに積んでおいた飛び先URLにリダイレクトさせます。<div><br /></div><div>先の処理と同様にOAuthのクラスを初期化後、認証により手に入ったリクエストトークン、ベリファイアを使ってTwitter（プロバイダ）からアクセストークンとトークンシークレットを取得します。
<pre class="code">	accessor.requestToken = requestToken;
	
	try {
		HashMap<string,string> params = new HashMap<string, string="">();
		params.put( OAuth.OAUTH_VERIFIER, verifire);
		//get access token and secret from twitter.com
		client.getAccessToken(accessor, null, params.entrySet());
	} catch (OAuthException e) {
		throw new RuntimeException( "It failed to authenticate Twitter account", e);
	} catch (URISyntaxException e) {
		throw new RuntimeException( "It failed to authenticate Twitter account", e);
	}
</string,></string,string></pre>アクセストークンとトークンシークレットはAPIをコールするために必要になります。
うまく取得できた場合、すでにOAuthAccessorオブジェクトにアクセストークンとトークンシークレットは格納されていますので、Twitter APIを呼び出すことができます。

<pre class="code">	//Retrieve user's information
	OAuthMessage oMessage = null;
	try {
		oMessage = accessor.newRequestMessage("GET", "http://api.twitter.com/1/account/verify_credentials.json", null);
	} catch (OAuthException e) {
		throw new RuntimeException( "It failed to authenticate Twitter account", e);
	} catch (URISyntaxException e) {
		throw new RuntimeException( "It failed to authenticate Twitter account", e);
	}

	OAuthResponseMessage rMessage = client.access(oMessage, ParameterStyle.AUTHORIZATION_HEADER);
	int status = rMessage.getHttpResponse().getStatusCode();
	if( status == HttpResponseMessage.STATUS_OK){
		String jsonStr = rMessage.readBodyAsString();
		JSONObject jObj = JSONObject.fromObject(jsonStr);
		String userId = jObj.optString("id");
		accessor.setProperty( "id", userId);
	}else{
		throw new RuntimeException( "It failed to authenticate Twitter account STATUS CODE:"+status);
	}
</pre>
ここでは、認証済みユーザー自身のユーザー情報を返す <a href="http://dev.twitter.com/doc/get/account/verify_credentials">account/verify_credentials</a> APIを呼び出しています。レスポンスのJSONを扱うのには、<a href="http://json-lib.sourceforge.net/">Json-lib</a> ライブラリを使用しています。<div><br /></div><div>最後に認証情報をクッキーに保存しています。</div>
<pre class="code">	//Store access token and secret to Cookie
	TwitterAPIUtil.storeTokenToCookie(accessor, response);
</pre>
このメソッドの中身は以下のようになっています。
<pre class="code">public static void storeTokenToCookie( OAuthAccessor accessor, HttpServletResponse response, int maxAge){
	Cookie cookie = new Cookie( accessor.consumer.consumerKey+"_requesttoken", accessor.requestToken);
	cookie.setPath( "/" );
	cookie.setMaxAge( maxAge);
	response.addCookie( cookie);
	
	cookie = new Cookie( accessor.consumer.consumerKey+"_accesstoken", accessor.accessToken);
	cookie.setPath( "/" );
	cookie.setMaxAge( maxAge);
	response.addCookie( cookie);

	cookie = new Cookie( accessor.consumer.consumerKey+"_secret", accessor.tokenSecret);
	cookie.setPath( "/" );
	cookie.setMaxAge( maxAge);
	response.addCookie( cookie);
	
	String userId = (String)accessor.getProperty("id");
	if( userId == null){
		userId = "";
	}
	cookie = new Cookie( accessor.consumer.consumerKey+"_id", userId);
	cookie.setPath( "/" );
	cookie.setMaxAge( maxAge);
	response.addCookie( cookie);
}
</pre><div>認証情報は、もちろんデータベースなどのストレージに保存することも可能です。しかし、これらの認証情報は、Twitter（プロバイダ）のサイトでユーザーが一方的に無効にできることには留意しておく必要があるでしょう。</div><div><br /></div>
別の場所で認証情報を使ってAPIを呼び出すには、以下のようにします。
<pre class="code">	.........
	OAuthClient client = new OAuthClient( new URLConnectionClient());
	OAuthServiceProvider provider = new OAuthServiceProvider( REQUEST_TOKEN_URL, AUTHORIZE_URL, ACCESS_TOKEN_URL);
	OAuthConsumer consumer = new OAuthConsumer( CALL_BACK_URL,
			CONSUMER_KEY, COMSUMER_SECRET, provider);
	OAuthAccessor accessor = new OAuthAccessor( consumer);
	
	//try to retrieve token
	Cookie[] cookies = request.getCookies();
	for( int i = 0; i &lt; cookies.length; ++i){
		Cookie cookie = cookies[i];
		if( cookie.getName().equals( accessor.consumer.consumerKey+"_accesstoken")){
			accessor.accessToken = cookie.getValue();
		}else if( cookie.getName().equals( accessor.consumer.consumerKey+"_requesttoken")){
			accessor.requestToken = cookie.getValue();
		}else if( cookie.getName().equals( accessor.consumer.consumerKey+"_secret")){
			accessor.tokenSecret = cookie.getValue();
		}else if( cookie.getName().equals( accessor.consumer.consumerKey+"_id")){
			accessor.setProperty( "id", cookie.getValue());
		}
	}
	.........
</pre>非常に駆け足になってしまいましたが、どのような印象を受けましたか？ライブラリを使用することで、かなりOAuthへの敷居が下がったと感じた方もおられるのではないでしょうか？</div><div><br /></div><div>この記事がちょっとでもだれかのお役に立てば幸いです。</div>]]>
        <![CDATA[<p><br />
</p>]]>
    </content>
</entry>

<entry>
    <title>今からはじめるCassandra入門 </title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/cassandra.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1848" title="今からはじめるCassandra入門 " />
    <id>tag:labs.unoh.net,2010://2.1848</id>
    
    <published>2010-08-12T02:48:04Z</published>
    <updated>2010-08-14T04:26:37Z</updated>
    
    <summary> こんにちわ、7月に入社したばかりの@emorinsです。 題名の通りですが分散データベース『Apache Cassandra』を紹介したいと思います。 少し前はHadoop(とHBase)と比較されることの多かったCassandraですが、最近はHadoopの人気に押されつつあるようにも感じます。しかし、CassandraとHadoopは特徴が異なり、よく言われるのがCassandraはリアルタイム処理に向き、一貫性のかわりに可用性を重視し、またHadoopとは違って単一障害点もありません。 今日はそんなHadoopとは違った魅力のある分散データベース『Apache Cassandra』をはじめてみましょう。 目次 Cassandraとは アーキテクチャ Cassandraの特徴 コンシステンシレベル データモデル MemtableとSSTable セットアップ storage-conf...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[<div><img alt="emori01.gif" src="http://labs.unoh.net/emori01.gif" width="494" height="124" /></div>

<p>こんにちわ、7月に入社したばかりの<a href="http://twitter.com/emorins" target="_blank">@emorins</a>です。</p>

<p>題名の通りですが分散データベース『<a href="http://cassandra.apache.org/">Apache Cassandra</a>』を紹介したいと思います。</p>
<p>少し前はHadoop(とHBase)と比較されることの多かったCassandraですが、最近はHadoopの人気に押されつつあるようにも感じます。<br>しかし、CassandraとHadoopは特徴が異なり、よく言われるのがCassandraはリアルタイム処理に向き、一貫性のかわりに可用性を重視し、またHadoopとは違って単一障害点もありません。</p>

<p>今日はそんなHadoopとは違った魅力のある分散データベース『Apache Cassandra』をはじめてみましょう。</p>

<h2>目次</h2>
<ul>
<li>Cassandraとは</li>
<li>アーキテクチャ
<ul>
<li>Cassandraの特徴</li>
<li>コンシステンシレベル</li>
<li>データモデル</li>
<li>MemtableとSSTable</li>
</ul>
</li>
<li>セットアップ</li>
<li>storage-conf.xmlの設定
<ul>
<li>Keyspace</li>
<li>ColumnFamily</li>
<li>MemtableThroughputInMB</li>
<li>MemtableFlushAfterMinutes </li>
</ul>
</li>
<li>Cassandra-CLIの使い方</li>
<li>ThriftAPIによるクライアントからの操作
<ul>
<li>Thriftとは</li>
<li>Thriftのインストール</li>
</ul>
</li>
<li>PHPによるクライアントアプリケーションを作る
<ul>
<li>データを取得する簡単なプログラム</li>
<li>ThriftAPI </li>
</ul>
</li>
<li>最後に
<ul>
<li>本エントリで取り上げなかったこと</li>
<li>参考</li>
</ul>
</li>
</ul>
<hr>

<h2>Cassandraとは</h2>
<div><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="emori02.gif" src="http://labs.unoh.net/emori02.gif" width="408" height="348" class="mt-image-none" style="" /></span></div>
<p>そもそもCassandraとは何でしょうか。</p>
<p>長い間データベースと言えばRDBMS(MySQL、PostgreSQL、Oracle Databaseなど)と、それを操作するSQL言語を使用するのが主流でした。しかし、大規模データ処理を必要としたGoogleやAmazonは、リレーショナルデータベースより(ペタバイトクラスの膨大なデータ処理に対して)高速で可用性のある、スケールアウトしやすいデータベースを必要としました。そこで生まれたのがGoogleのBigtableと、AmazonのDynamoです。</p>

<p>Cassandraは、そんなBigtableやDynamoを参考にFacebookが開発したNoSQLデータベースです。現在はApacheソフトウェア財団に寄贈され、オープンソースとして開発が進められています。</p>

<h2>アーキテクチャ</h2>
<h3>Cassandraの特徴</h3>
<div>Cassandraは、<a href="http://www.hyuki.com/yukiwiki/wiki.cgi?BrewersCapTheorem" target="_blank">CAP定理</a>のうちAP(可用性:Availabilityと分割耐性:Partition Tolerance)を重視しています。また一貫性:Consistencyについては、どのレベルの整合性にするか調整可能です(後述)。</div>
<div>
Cassandraの主な特徴として
<ul>
<li>高可用性</li>
<li><a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%99%E3%83%B3%E3%83%81%E3%83%A5%E3%82%A2%E3%83%AB%E3%83%BB%E3%82%B3%E3%83%B3%E3%82%B7%E3%82%B9%E3%83%86%E3%83%B3%E3%82%B7%E3%83%BC">イベンチュアル・コンシステンシー</a></li>
<li>一貫性と遅延のトレードオフを調整可能</li>
<li>単純なKey/Valueではないデータモデル</li>
<li>単一障害点(SPOF)がない</li>
</ul>
などが言えます。<br>
特に可用性の高さとSPOFが無いのが特筆すべき点です。
</div>

<h3>コンシステンシレベル</h3>
<div>Cassandraは可用性の代わりに一貫性を犠牲にしていますが、遅延とのトレードオフで一貫性のレベル設定ができます。</div>
<ul>
<li>W=書き込みを保証するレプリケート数</li>
<li>R=読み込むレプリケート数</li>
<li>N=レプリケートしているノード数(storage-conf.xml内の&lt;ReplicationFactor&gt;)</li>
<li>Q=QUORUM (Q = N / 2 + 1) </li>
</ul>
<pre class="code">R + W > N</pre>
<div>の条件を満たすとき、一貫性があると言えます。</div>
<div>
設定できるレベルは以下の4つです。
<ul>
<li>Zero：保証なし</li>
<li>One：W=1,R=1
<ul>
<li>[書き込み]１つのノードの書き込みを保証</li>
<li>[読み込み]最初に読み込んだノードから返す</li>
</ul>
</li>
<li>Quorum：w=Q,R=Q
<ul>
<li>[書き込み](N+1)/2数のノードに書き込まれたことを保証</li>
<li>[読み込み](N+1)/2数のノードから読み込み、最新を返す</li>
</ul>
</li>
<li>All：w=N,R=N
<ul>
<li>[書き込み]N個のノードに書き込まれたことを保証する</li>
<li>[読み込み]N個ののノードから読み込み、最新を返す</li>
</ul>
</li>
</ul>
</div>
<h3>データモデル</h3>
<div>基本はKey/Value型ですが、階層的な構造を持つ4次元or5次元のデータモデルになっています。単純なKVSよりも高機能かつ表現力豊かなデータをモデリングできます。</div>
<div>それぞれのデータ構造をRDBMSと照らし合わせながら見ていきましょう。</div>
<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="emori03.gif" src="http://labs.unoh.net/emori03.gif" width="588" height="615" class="mt-image-none" style="" /></span>
<dl>
<dt>キースペース</dt>
<dd>RDBMSでいうデータベースに当たるものです。Cassandraデータモデルの一次元目で、カラムファミリのコンテナになります。一般に1アプリケーションに1キースペースとされます。</dd>
<dt>カラムファミリ</dt>
<dd>RDBMSでいうテーブルに当たるものです。カラムのコンテナです。カラムファミリは後述するstorage-conf.xmlで設定して作成します。複数の行(Row)から構成されます。</dd>
<dt>行(Row)</dt>
<dd>キーと複数のカラムを持つ。RDBMSでいえば、行はレコード、キーは主キーのイメージです。</dd>
<dt>カラム</dt>
<dd>Cassandraでの最小のデータ構造。名前(name)、値(value)、タイムスタンプ(timestamp)を持ちます。</dd>
<dt>スーパーカラム</dt>
<dd>値(value)にソート済みの複数のカラムのリストを持つ。スーパーカラムは必ずしも必要ではありません。</dd>
</dl>

<div>分かりにくいと思うので、以下のCassandra-CLIでのデータ挿入の命令文を、少し無理にSQL文に置き換えてみましょう(Cassandra-CLIの使い方については後述します)。</div>

<ul>
<li>Cassandra-CLIでのデータ挿入<br>
<pre class="code">set Keyspace1.Standard2['jsmith']['job']='developer'</pre>
</li>
<li>SQLによるINSERT文(※syntax error)<br>
<pre class="code">INSERT INTO Keyspace1.Standard2 (job) VALUES('developer') WHERE id='jsmith';</pre>
</li>
</ul>

<div>この場合、Keyspace1がキースペース、Standard2がカラムファミリ、['jsmith']が行(キーは'jsmith')、そして['job']がカラムで'job'が名前(name)です。</div>

<h3>MemtableとSSTable</h3>
<p>ハードウェア上のデータの流れはどうなっているでしょうか。<br>
まずCassandraに書き込み操作がされるとCommitLogに書き込まれます。その後、カラムファミリ毎にMemtableというメモリ空間に対して書き込まれます。Memtableが一杯になると、ディスク上にSSTableという形式で保存(フラッシュ)されます。</p>

<p>MemtableではSSTableにフラッシュする際に使用するキーを設定してソートしておくので、MySQLでのランダムアクセスとは違い、HDDにシーケンシャルに書き込みむので、高速です。</p>

<p>ここまで話を聞くと、MemtableからSSTableにフラッシュする回数を極力減らすため、Memtableの容量を設定したくなるかと思いますが、もちろん可能です。後述するstorage-conf.xmlという設定ファイルで、Memtableに使うメモリ容量や保存時間などが指定できます。</p>

<h2>セットアップ</h2>
<div>今回使用した環境は以下です。<br>
<ul>
<li>FreeBSD 8.1</li>
<li>JDK 1.6</li>
<li>Cassandra 0.6.3</li>
<li>PHP 5.3.2</li>
</ul>
</div>
<div>まずはCassandraを動かすために必要なJava環境を作るため、JDK 1.6をインストール。</div>
<pre class="code"># wget http://www.FreeBSDFoundation.org/cgi-bin/download?download=diablo-caffe-freebsd7-i386-1.6.0_07-b02.tar.bz2
# make install clean</pre>
<div>Cassandraのインストール。</div>
<pre class="code"># cd /usr/ports/databases/cassandra
# make install clean</pre>
<div>続いて初期設定。取りあえずサンプルで用意されているlog4j.properties.sampleとstorage-conf.xml.sampleをリネームして作成。設定ファイルの説明は後述します。</div>
<pre class="code"># cd /usr/local/share/cassandra
# cp conf/log4j.properties.sample conf/log4j.properties
# cp conf/storage-conf.xml.sample conf/storage-conf.xml</pre>
<div>準備完了なので、起動します。これでシングルノードではありますがCassandraクラスタのできあがりです。</div>
<pre class="code"># /usr/local/share/cassandra/bin/cassandra</pre>
<div>起動オプション
<ul>
<li>-f:フォアグラウンドで起動</li>
<li>-h:使用できるオプションを表示</li>
<li>-p <pidfile>:PIDを<pidfile>に出力。</li>
</ul>
</div>

<h2>storage-conf.xmlの設定</h2>
<p>Cassandraの最も重要な設定ファイルがstorage-conf.xmlです。このファイル内で、クラスタの各設定や、カラムファミリの作成、Memtableの容量など設定できます。</p>
<p>全ては紹介できませんが、いくつか重要な設定箇所をピックアップします。詳しい情報や設定例はstorage-conf.xml.sampleを見てください。</p>

<h3>Keyspace</h3>
<p>キースペースを作成します。Name属性を指定して、カラムファミリはこのKeyspaceタグ内に記述します。<br>
今回の設定例:</p>
<pre class="code">&lt;Keyspace Name="Keyspace1"&gt;
...
&lt;/Keyspace&gt;</pre>

<h3>ColumnFamily</h3>
<div>カラムファミリの設定です。下記のような属性を指定できます。</div>
<ul>
<li>Name:カラムファミリ名</li>
<li>ColumnType:カラムタイプ。デフォルトは"Standard"。スーパーカラムを持ちたい場合は"Super"にする。</li>
<li>CompareWith:カラムのソート方法</li>
<li>KeysCached:キーをキャッシュする個数(%をつけると割合)</li>
</ul>
<div>今回の設定例:</div>
<pre class="code">&lt;ColumnFamily Name="Standard2" CompareWith="UTF8Type" KeysCached="100%"/&gt;</pre>

<h3>MemtableThroughputInMB</h3>
<div>Memtableのデータ容量</div>

<h3>MemtableFlushAfterMinutes</h3>
<p>フラッシュまでの最大時間。この時間を超えると強制的にフラッシュする。</p>
<p>その他詳しい設定例などは以下をご参考ください。<br>
<a href="http://wiki.apache.org/cassandra/StorageConfiguration" target="_blank">StorageConfiguration - Cassandra Wiki</a></p>

<h2>Cassandra-CLIの使い方</h2>
<p>データモデルの項でチラっと出てきましたが、Cassandra-CLIというクライアントツールを使って、Cassandraのデータを操作してみましょう。</p>
<p>Cassandraを起動した状態で、Cassandra-CLIを立ち上げます。</p>
<pre class="code">/usr/local/share/cassandra/bin/cassandra-cli --host localhost --port 9160</pre>
<div>すると</div>
<pre class="code">Connected to: "Test Cluster" on localhost/9160
Welcome to cassandra CLI.

Type 'help' or '?' for help. Type 'quit' or 'exit' to quit.
cassandra&gt; </pre>
<div>と、Cassandra-CLIのコンソールに入ります。</div>
<div>では、手始めにデータを書き込んでみましょう。今回はKeyspace1のStandard2カラムファミリを利用します。Keyspace1と Standard2自体はstorage-conf.xml.sampleに初めから定義されているキースペース・カラムファミリですので、特別なことをしなくてもそのまま利用できるはずです。</div>
<pre class="code">cassandra&gt; set Keyspace1.Standard2['unoh']['age']='25'</pre>

<p>set文で、Standard2カラムファミリに'unoh'キーを追加した上で、'age'という名前のカラムに25という値を挿入しました。</p>

<p>実際にちゃんとデータの追加ができているかget文で確認してみます。</p>

<pre class="code">cassandra&gt; get Keyspace1.Standard2['unoh']            
=&gt; (column=age, value=25, timestamp=1281559772819000)
Returned 1 results.</pre>

<div>Standard2カラムファミリの'unoh'キーにあるカラムが取得できました。カラム名、カラム値、タイムスタンプがきちんと挿入されているのが確認できます。<br>
さらに追加してみましょう。</div>

<pre class="code">cassandra&gt; set Keyspace1.Standard2['unoh']['phone']='03-5766-3911'
Value inserted.
cassandra&gt; set Keyspace1.Standard2['unoh']['web']='www.unoh.net'  
Value inserted.</pre>

<div>で、再度取り出してみる。</div>

<pre class="code">cassandra&gt; get Keyspace1.Standard2['unoh']                        
=&gt; (column=web, value=www.unoh.net, timestamp=1281560104883000)
=&gt; (column=phone, value=03-5766-3911, timestamp=1281560057868000)
=&gt; (column=age, value=25, timestamp=1281559772819000)
Returned 3 results.</pre>

<div>完璧ですね。</div>

<pre class="code">cassandra> help </pre>

<div>とすれば、実行可能なコマンド一覧がでますので、各自試してみてください。Cassandra-CLIはこれくらいにして次に進みます。</div>


<h2>ThriftAPIによるクライアントからの操作</h2>
<div>Cassandraをクライアントプログラムから操作するときには、RPCフレームワークである『Thrift』が利用できます。</div>

<h3>Thriftとは</h3>
<p>Thriftは、同じくFacebookが開発した言語間サービス開発のためのRPCフレームワークです。C++、C#、Java、OCaml、Perl、Python、PHP、Rubyなどで書かれたプログラム間を通信可能にしてくれます。</p>
<p>CassandraはJavaで書かれているわけですが、Thriftを使うことで、異なる言語からCassandraを操作することができるようになります。CassandraにはThiftインターフェースがあらかじめ用意されているので、Thrift（とCassandra）がサポートしている言語ならどの言語のクライアントコードからでも操作可能です。今回はタイトル通りPHPから利用してみましょう。</p>
<p>※CassandraのAPI/RPC/Thriftポートは9160番です。</p>

<h3>Thriftのインストール</h3>
<div>Thriftの大まかな使い方の流れは、
<ol>
<li>Thriftインターフェース（.thrift）の作成</li>
<li>そのインターフェースに沿った各言語のスケルトンを生成</li>
<li>スケルトンを元にサーバー・クライアントのコーディング</li>
</ol>
という流れになります。Thriftインターフェース（.thrift）は、Cassandraのインストール先ディレクトリ下のinterfaceディレクトリに"cassandra.thrift"ファイルを利用するので、1の作業は必要ありません。また、2,3のサーバー(Cassandra)側のコーディングも必要ありません。</div>
<div>では、Thriftをインストールします。</div>

<pre class="code"># cd /usr/ports/devel/thrift/
# make install clean</pre>

<div>インストールが完了したら、まずはThriftプロジェクト用のディレクトリを適当な場所に作成しましょう。</div>

<pre class="code"># mkdir ~/thrift</pre>

<div>次に、Thriftプロトコルが実装されたPHPコードをThriftパッケージ内からコピーしてきます。このコードはPHPからThriftを扱うために必要な全プロジェクトで共通して使うライブラリです。</div>

<pre class="code"># wget http://ftp.riken.jp/net/apache/incubator/thrift/0.2.0-incubating/thrift-0.2.0-incubating.tar.gz
# tar zxvf thrift-0.2.0-incubating.tar.gz
# cp -R thrift-0.2.0/lib/php/src ~/thrift</pre>

<div>次に"cassandra.thrift"を使ってCassandra用Thriftクライントを生成します。サーバーごとのThriftクライントはsrcディレクトリ内のpackagesディレクトリに分けて入れておきましょう。</div>

<pre class="code"># cd thrift
# mkdir src/packages
# cp /usr/local/share/cassandra/interface/cassandra.thrift ~/thrift/cassandra.thrift
# thrift --gen php cassandra.thrift
# mv -R gen-php/cassandra src/packages</pre>

<div>これでCassandraをThriftから扱うための準備が整いました。ディレクトリ構成を以下にまとめます。</div>

<pre class="code">/thrift
  ├ /src・・・Thriftプロトコルの実装ファイル
　　　├ Thrift.php
      ├ autoload.php
      ├ /ext
      ├ /packages・・・各サーバごとのThriftクライアント(gen-phpとして生成されたコード)
           └ /cassandra・・・cassandra用Thriftクライアントが入ったディレクトリ
                ├ Cassandra.php
                ├ cassandra_constants.php
                └ cassandra_types.php
      ├ /protocol
      └ /transport
  └ /unoh・・・プロジェクトディレクトリ
      └sample.php・・・クライアントアプリケーション(ここで具体的にCassandraを操作するコードを書きます)</pre>

<h2>PHPによるクライアントアプリケーションを作る</h2>
<div>では、いよいよPHPでCassandraを使ったアプリケーションを書いていきましょう。</div>

<h3>データを取得する簡単なプログラム</h3>
<div>・~/thrift/unoh/sample.php</div>
<pre class="code"> &lt;?php
 $GLOBALS['THRIFT_ROOT'] = '../src';
 require_once $GLOBALS['THRIFT_ROOT'].'/packages/cassandra/Cassandra.php';
 require_once $GLOBALS['THRIFT_ROOT'].'/packages/cassandra/cassandra_types.php';
 
 require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php';
 require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';
 require_once $GLOBALS['THRIFT_ROOT'].'/transport/TFramedTransport.php';
 require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';
 
 try {
   // Thriftの接続用インスタンスの作成                 
   $socket = new TSocket('localhost', 9160);
   $transport = new TBufferedTransport($socket, 1024, 1024);
   $protocol = new TBinaryProtocolAccelerated($transport);
 
   //Cassandraクライアントの作成                        
   $client = new CassandraClient($protocol);
 
   //接続開始                                            
   $transport-&gt;open();
 
   // ColumnFamilyの指定                                
   $columnParent = new cassandra_ColumnParent();
   $columnParent-&gt;column_family = "Standard2";
 
   //スライスの指定。切り出すColumnの範囲を指定できる。
   $predicate = new cassandra_SlicePredicate();
   $sliceRange = new cassandra_SliceRange();
   //例えば、'age'から'phone'までのColumnを切り出す(Columnは昇順でソートされる)
   $sliceRange-&gt;start = "age";
   $sliceRange-&gt;finish = "phone";
   $predicate-&gt;slice_range = $sliceRange;
 
   //クエリを投げる
   $result = $client-&gt;get_slice('Keyspace1', 'unoh', $columnParent,$predicate, cassandra_ConsistencyLevel::ONE);
   //中身を出力してみる
   var_dump($result);
 
   //接続終了
   $transport-&gt;close();
 }
 catch (TException $tx) {
   print 'TException: '.$tx-&gt;why. ' Error: '.$tx-&gt;getMessage() . "\n";
 }</pre>

<div>出力結果</div>

<pre class="code"># php ~/thrift/unoh/sample.php</pre>

<pre class="code">array(2) {
  [0]=&gt;
  object(cassandra_ColumnOrSuperColumn)#9 (2) {
    ["column"]=&gt;
    object(cassandra_Column)#10 (3) {
      ["name"]=&gt;
      string(3) "age"
      ["value"]=&gt;
      string(2) "25"
      ["timestamp"]=&gt;
      float(1.281559772819E+15)
    }
    ["super_column"]=&gt;
    NULL
  }
  [1]=>
  object(cassandra_ColumnOrSuperColumn)#11 (2) {
    ["column"]=&gt;
   object(cassandra_Column)#12 (3) {
      ["name"]=&gt;
      string(5) "phone"
      ["value"]=&gt;
      string(12) "03-5766-3911"
      ["timestamp"]=&gt;
      float(1.281560057868E+15)
    }
    ["super_column"]=&gt;
    NULL
  }
}</pre>
<div>Cassandraからデータを取り出すだけの簡単なコードですが、Cassandraとうまく連携できているのが確認できました。</div>

<h3>ThriftAPI</h3>
<div>先程のコード中でnew cassandra_SliceRange()を使ってスライスを作成したり、$client-&gt;get_slice()でクエリを投げて結果を受け取ったりしていました。これらはThriftAPIで利用できます。</div>
<div>利用可能なThriftAPI一覧は以下のページを参照してください。<br>
<a href="http://wiki.apache.org/cassandra/API" target="_blank">API - Cassandra Wiki</a></div>

<h2>最後に</h2>
<h3>本エントリで取り上げなかったこと</h3>
<div>かけ足になりましたが、Cassandraの概要とシングルノードによる基本的な操作をご紹介しました。マルチノードクラスタ、ノードの追加・削除方法、チューニング、モニタリング、実用性など、また次の機会に突っ込めたらと思います。</div>

<h3>参考</h3>
<ul>
<li><a href="http://wiki.apache.org/cassandra/FrontPage_JP" target="_blank">FrontPage_JP - Cassandra Wiki</a></li>
<li><a href="http://gihyo.jp/dev/serial/01/cassandra" target="_blank">連載：Cassandraのはじめ方─手を動かしてNoSQLを体感しよう｜gihyo.jp ... 技術評論社</a></li>
<li><a href="http://www.ne.jp/asahi/hishidama/home/tech/apache/cassandra/" target="_blank">Cassandraメモ(Hishidama's Apache Cassandra Memo)</a></li>
</ul>

]]>
        
    </content>
</entry>

<entry>
    <title>レディの嗜みについて</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/post_148.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1847" title="レディの嗜みについて" />
    <id>tag:labs.unoh.net,2010://2.1847</id>
    
    <published>2010-08-11T12:25:33Z</published>
    <updated>2010-08-12T02:53:37Z</updated>
    
    <summary> はじめまして 6月にウノウ株式会社改め Zynga Japan※謎 に入社致しました mayutanです。 社内全体 MTG で flickr のプレミアムユーザであることを dis られたので 弊社のサービスでありますフォト蔵の API を使って皆さん(誰？)に許していただこうと思います。 Android 端末を見たことも触ったこともなかったので、後学のためにアプリを作ってみることにしました。 タグ的な何かを選択したらそのイメージに合った写真を一覧表示するアプリを作ってみましょう。 Aandroid アプリの開発環境はこちらを参考に整えました　http://developer.android.com/sdk/installing.html ここから直感的にアプリをデザインしてみましょう！ res ディレクトリの layout/main.xml を開いてエディター下部にあります、「レイア...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
        <category term="Java" />
    
        <category term="android" />
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[
はじめまして
6月にウノウ株式会社<strike>改め Zynga Japan</strike>※謎 に入社致しました <a href="http://twitter.com/mayutan">mayutan</a>です。

社内全体 MTG で flickr のプレミアムユーザであることを dis られたので
弊社のサービスでありますフォト蔵の API を使って皆さん(誰？)に許していただこうと思います。

Android 端末を見たことも触ったこともなかったので、後学のためにアプリを作ってみることにしました。
タグ的な何かを選択したらそのイメージに合った写真を一覧表示するアプリを作ってみましょう。

Aandroid アプリの開発環境はこちらを参考に整えました　http://developer.android.com/sdk/installing.html

ここから直感的にアプリをデザインしてみましょう！

res ディレクトリの layout/main.xml を開いてエディター下部にあります、「レイアウト」というタブをクリックしますと・・・！

<strong><big>なんということでしょう！！！！！！！！！！！！！！！</big></strong>

なんかデザインできそうなふいんき(なぜかへんかんできない)です。

黒い画面の上で、右クリック→追加を選択すると、ダイアログが出てくるので、
無難に ListView を追加します。

「VB みたいにぐいぐいーとかそれそれーとかできるんだろうな♪」
とか思ったらできねー・・・

今回は時間がないので、仕方ないのでコードで書きます。
コードはこちらお暇な方はどうぞ→<span class="mt-enclosure mt-enclosure-file" style="display: inline;"><a href="http://labs.unoh.net/2010/08/11/Photozoroid.java">Photozoroid.java</a></span>

アンドロイドはまだまだ直感的には開発できないですね。がんばれアンドロイド

さて、layout/main.xml を見てみますと、ListView を追加したことにより、新しい記述も追加されていますね。
ここにある android:id="@+id/ListView01" というのが、このリストの ID です。

実際には ListView にどうやって表示させたいオブジェクトを渡すのかといいますと、<a href="http://developer.android.com/reference/android/widget/ListView.html#setAdapter(android.widget.ListAdapter)">setAdapter</a>　メソッドを使います。
今回は表示するリストは固定ですので、中から指定しちゃいます／／／

onCreate は必ず呼ばれるメソッドで必ず実装しなければいけません。
setContentView で、引数に渡した画面が呼ばれます。
ここまでくればピン！ときますね。 main.xml に記述されているやつらです。

リストを表示させるならこれ！
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
adapter.add("girl");
adapter.add("food");
・・・
ListView listView = (ListView) findViewById(R.id.ListView01);
listView.setAdapter(adapter);

そして実行すると・・・！<big>なんということでしょう・・・！</big>

<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/6-148.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/6-148.html','popup','width=378,height=570,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/6-thumb-200x301-148.jpg" width="200" height="301" alt="6.jpg" class="mt-image-none" style="" /></a></span>

じゃーん。リストが出ましたね。

次はこれに onclick イベントとか実装しちゃいましょう！
それでは onClick イベントはどう実装すればいいのでしょうか。
API を見ると、どうやら AdapterView クラスが持っているこの関数を実装してやればいいらしい。

<a href="http://developer.android.com/guide/topics/ui/binding.html">listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {</a>
・・・

ここで引数にしているのは AdapterView クラスが持っているインナークラスのメソッドで、
外から呼ぶにはこうしてやります。
中見てないので適当に予想しますが、おそらく View クラスはたくさんのインターフェースを持っているのでしょう。たぶん。

次に、OnItemClickListener#onItemClick で実際にクリックされた場合の処理を実装します。

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
		    @Override
		    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
		        ListView listView = (ListView) parent;
		        String searchKeyWord = (String) listView.getItemAtPosition(position);
		    }
		});

そして、これをキーワードにして API より取得したデータから画像を別画面に表示してみましょう。
遷移画面用の XML ファイルを res/layout に作成します。
そして、AndroidManifest.xml にこの View を登録。

次画面に遷移するために Intent クラスを使うようですね。そして、渡したいオブジェクトを putExtra してやります。
渡すデータは Serializable である必要があります。

次画面でデータを受け取り、API でキーワードから検索します。
コードはこちら→<span class="mt-enclosure mt-enclosure-file" style="display: inline;"><a href="http://labs.unoh.net/PhotozoViewer.java">PhotozoViewer.java</a></span>
このとき urlconn.connect で、"java.net.SocketException: Permission denied"　が発生すると思います。
Manifest.xml に 
uses-permission android:name="android.permission.INTERNET"
を追加して、自分の Android アプリからの通信を許可してあげましょう。。


さていよいよ画像の表示です。こちらを参考に作りました。
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/Grid2.html
ほとんどサンプルのままですが、ここでは URL から読み込んだ画像 stream を Bitmap に変換して表示してみました。


<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://labs.unoh.net/assets_c/2010/08/last-151.html" onclick="window.open('http://labs.unoh.net/assets_c/2010/08/last-151.html','popup','width=384,height=562,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://labs.unoh.net/assets_c/2010/08/last-thumb-200x292-151.jpg" width="200" height="292" alt="last.jpg" class="mt-image-none" style="" /></a></span>

じゃーん！

getView はいつ呼ばれるの？と思うかもしれませんが、これは View が表示されるたびに呼ばれます。

こんな感じで簡単に Web サービスと連携させたクライアントアプリケーションが作れてしまう Android には
とても可能性を感じますね！これからも色々いじってみます。



!!!まったく関係ありませんが <a href="http://blog.livedoor.jp/theclovers/">the clovers</a> もよろしくお願いいたします!!!]]>
        
    </content>
</entry>

<entry>
    <title>DBの選択について</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/db.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1844" title="DBの選択について" />
    <id>tag:labs.unoh.net,2010://2.1844</id>
    
    <published>2010-08-11T04:02:14Z</published>
    <updated>2010-08-12T01:58:29Z</updated>
    
    <summary>皆様、はじめまして。 7月入社のトクヤマと申します。 以後、お見知りおき頂ければ幸いです。 私は以前、別の会社の仕事でPostgreSQL,MySQL,Oracle,SQLSeverを同時に運用していたことがありました。 また現在はMySQLとPostgreSQLを運用しています。 今回はそれぞれのDBを、どのようなWEBサイトやオープン系の開発を行う時に選ぶべきなのかという事をお話したいと思います。 なおOracle、SQLServerは現在はWEBサイトやオープン系の開発では使われなくなっていますのでPostgreSQLとMySQLに絞りたいと思います。 「定説」として以前はPostreSQLは多機能で遅い、MySQLは機能は少ないが検索が速いと言われていました。 しかし、それぞれバージョンアップを繰り返すうちに現在は、どちらもお互いの特徴をカバーしてきています。 それではどの面をみて...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        皆様、はじめまして。

7月入社のトクヤマと申します。
以後、お見知りおき頂ければ幸いです。

私は以前、別の会社の仕事でPostgreSQL,MySQL,Oracle,SQLSeverを同時に運用していたことがありました。
また現在はMySQLとPostgreSQLを運用しています。

今回はそれぞれのDBを、どのようなWEBサイトやオープン系の開発を行う時に選ぶべきなのかという事をお話したいと思います。
なおOracle、SQLServerは現在はWEBサイトやオープン系の開発では使われなくなっていますのでPostgreSQLとMySQLに絞りたいと思います。

「定説」として以前はPostreSQLは多機能で遅い、MySQLは機能は少ないが検索が速いと言われていました。
しかし、それぞれバージョンアップを繰り返すうちに現在は、どちらもお互いの特徴をカバーしてきています。

それではどの面をみて選択するのかということですが、それぞれのデメリットを検討して決めてるべきではないかと思います。
私が個人的にベンチマークや運用感を見て感じたデメリットや適用例は下記のようなものがあると考えています。

デメリット：
●PostgreSQL
・チューニング次第でかなり性能が変わる。また細かいチューニングを行わないと遅い。
・レプリケーション機能はサードパーティのアプリケーションで実現できるが設定やデータ不整合対策がMySQLより手間がかかる。(*1)
・単純検索ではMySQLの方がまだ速い。(チューニングを行うとMySQLよりも早くなる場合もあります)
*1...PostgreSQL9.0でようやくレプリケーション機能が本体に実装されますがMySQLと比較すると少し不便さを感じます。

●MySQL
・単純検索は速いがJOINやORDER BY、GROUP BY、または一部サブクエリーを使用した複雑な検索では遅くなる。
・データ量が多くなると性能の劣化が比較的大きい。
・PostgreSQLと比較して同時接続負荷に弱い。


適用例：
●PostgreSQL
・SNSなどのコミュニケーションサイト
・更新や検索が複雑に絡みあるゲーム系ソーシャルアプリ
・その他:データ量が多くなる、同時アクセス数が多くなる。また各テーブルが複雑に連携しあうようなスキーマ設計を行う場合。

●MySQL
・占いや着うたサイトなどの会員やコンテンツデータ、履歴管理程度を行うサイト
・検索や課金系のサイト
・その他:サイトの開発期間が短いなどの場合

設定の難しさはありますが設計や運用のし易さで言えばPostgreSQLに分があると思います。ネックはレプリケーション機能の弱さです。
しかしMySQLのレプリケーション機能が絶対に不整合を起こさないかと言えばそんなことはありません。
設計次第でどちらも不整合を減らし無くすこともできます。

またMySQLは(あくまでも比較として)同時接続負荷の弱さが一つとしてありますがこれはレプリケーションの取り扱いの簡易さから簡単なDBの追加で対応が可能です。
ネックといえば今後の先行きが多少不明な点でしょうか。MySQLの権利がOracle社に移ったことはご存じかとは思いますがOracleとの兼ね合いでMySQLが今後の開発がどうなるのか、まだ方針が明確にはなっていません。
(Oracle社はMySQLへの投資を続けるとは言っていますが...)

以上、勝手なことを申し上げてきましたが、これらはあくまでも目安となります。
現在はmemcacheなどメモリキャッシュの使用も前提としてWEBサイトは設計されますし、
サーバーインフラやDBスキーマの設計次第の部分もありますから、
一概に特定のWEBサイト開発では特定のDBでしか開発・運用できないと言うことはありません。

結論として、あまりまとめきらずに恐縮ですが、
それぞれのDBに特徴や弱点があるとはいえ「派閥」というべきか、どちらか一方のDBだけを押す声が多いなか
一つのDBだけで考えるのではなく複数ある中から柔軟に使用するDBを選択していきたいものです。

※もちろん、同じ会社のなかでシステム毎にDB違うのも考え物ですが...以前、私はそれで苦労しました。。。
        
    </content>
</entry>

<entry>
    <title>仮想マシンとLVM2で簡単サーバ構築</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/lvm2.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1845" title="仮想マシンとLVM2で簡単サーバ構築" />
    <id>tag:labs.unoh.net,2010://2.1845</id>
    
    <published>2010-08-11T03:56:00Z</published>
    <updated>2010-08-12T02:14:12Z</updated>
    
    <summary>はじめまして、7月に入社いたしましたsatoshiと申します。 会社でも自宅でもPCをMacにして、Linuxと同じ操作感で使用できて快適な今日この頃ですが、皆様いかがお過ごしでしょうか? 今回、KVM(Kernel-based Virtual Machine)とLVM(Logical Volume Manager)を使用して、サーバ構築すると便利なケースを紹介したいと思います。 新しくサーバを構築する場合、OSインストールイメージをDVDに焼いて、DVDをドライブに挿入して、手順の通り選択して...となると思います。 (中には、Cobblerを使用しているという先進的な方もいらっしゃるかもしれませんが) 仮想マシンでは新たにサーバ環境を構築する場合、既存の仮想ディスクをコピーして使い回せるのでインストールの手間を簡略化できます。 さらに、一歩進んでLVMで作成したスナップショットLVを使...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
        <category term="Linux" />
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[<p>はじめまして、7月に入社いたしましたsatoshiと申します。</p>
<p>会社でも自宅でもPCをMacにして、Linuxと同じ操作感で使用できて快適な今日この頃ですが、皆様いかがお過ごしでしょうか?</p>
<p>今回、KVM(Kernel-based Virtual Machine)とLVM(Logical Volume Manager)を使用して、サーバ構築すると便利なケースを紹介したいと思います。</p>
<p>新しくサーバを構築する場合、OSインストールイメージをDVDに焼いて、DVDをドライブに挿入して、手順の通り選択して...となると思います。
(中には、Cobblerを使用しているという先進的な方もいらっしゃるかもしれませんが)</p>
<p>仮想マシンでは新たにサーバ環境を構築する場合、既存の仮想ディスクをコピーして使い回せるのでインストールの手間を簡略化できます。
さらに、一歩進んでLVMで作成したスナップショットLVを使用すると消費するディスク領域を大幅に節約できます。</p>
<p>今すぐにデモ環境を用意したいとか、Webサーバは共通のものを使いたいが、アプリケーションがPHPとPythonって環境が異なるとか
OSをアップグレードしたらどうなるかチェックしたいとか...等々いろいろ利用ケースを考えてみました。</p>
<div class="section" id="id1">
<h2>前提環境</h2>
<p>作成する仮想マシンは、ベース環境となる仮想マシンを必要としますのであらかじめ作成しておく必要があります。
さらに、スナップショットLVを利用した仮想マシン使用時は、元の仮想マシン(ベース環境)を停止しておきます。
また、インストール時のパラメータを変更すれば、XenやほかのLinuxディストリビューションでも流用可能だと思います。</p>
<p><strong>物理マシン(ホスト環境)</strong></p>

<p>物理マシン(サーバ機)にインストールした環境をホスト環境と呼ぶことにします。</p>
<p>KVMを仮想マシンに使用する場合は、ほぼ強制的にCPU仮想化支援機能が有効化されていることが必要です。</p>
<dl class="docutils">
<dt>CPU仮想化支援機能</dt>
<dd>IntelVT-x OR AMD-V</dd>
<dt>OS</dt>
<dd>CentOS5.5</dd>
<dt>仮想マシンソフトウェア</dt>
<dd>KVM</dd>

<dt>仮想マシン管理ライブラリ</dt>
<dd>Libvirt</dd>
<dt>ボリューム管理</dt>
<dd>LVM2</dd>
</dl>
<p><strong>仮想マシン(ベース環境,クローン環境)</strong></p>
<p>複製元となる環境をベース環境と呼ぶことにします。</p>
<p>複製元からデータをコピーして作成した環境をクローン環境と呼ぶことにします。</p>
<dl class="docutils">
<dt>OS</dt>

<dd>CentOS5.5</dd>
<dt>仮想ディスク</dt>
<dd>LVM2のLV(Logical Volume)</dd>
</dl>
<p><strong>仮想マシン(オンデマンド環境)</strong></p>
<p>ベース環境のLVから作成したスナップショットLVを利用した環境をオンデマンド環境を呼ぶことにします。</p>
<dl class="docutils">
<dt>OS</dt>
<dd>CentOS5.5</dd>
<dt>仮想ディスク</dt>

<dd>LVM2のスナップショットLV</dd>
</dl>
</div>
<div class="section" id="id2">
<h2>手順</h2>
<div class="section" id="id3">
<h3>事前準備</h3>
<p>まずは、ベース環境の仮想マシンを作成します。
diskオプションに指定しているpoolは、VG(Volume Group)です。このように指定するとVGからLVが自動的に作成されます。
(LV名は、nameオプションで指定した名称+.img)</p>
<p><em>ベース環境作成</em>:</p>
<pre class="code">
# VGをlibvirtで管理
$ sudo virsh pool-create-as ServiceVG00 --type=lvm2 --target=/dev/ServiceVG00
# ベース環境の仮想マシンを作成
$ sudo virt-install \
--location http://ftp.riken.jp/Linux/centos/5/os/x86_64/ \
--accelerate \
--name basevm \
--ram 1024 \
--vcpus 2 \
--noacpi \
--os-variant rhel5.4 \
--network bridge:br0 \
--extra-args console=ttyS0,115200n8 \
--disk pool=ServiceVG00,bus=virtio,size=48,cache=writethrough \
--noreboot \
--keymap=ja

#インストーラが起動するので、OSのインストールを行います。
インストールを開始しています...
(...)
Guest installation complete... you can restart your domain
by running 'virsh start basevm'

</pre>
<p>インストールが完了したら、ホストOSにて仮想マシンの時刻設定を行います。</p>
<p><em>仮想マシンの時刻設定</em>:</p>
<pre class="code">
# 仮想マシンの時刻設定変更
$ sudo virsh edit basevm
&lt;domain type='kvm'&gt;
  &lt;name&gt;basevm&lt;/name&gt;
  &lt;uuid&gt;bd55f202-cfd4-3d8b-ef23-d9e4d37c4711&lt;/uuid&gt;

(...)
  &lt;/features&gt;
  &lt;clock offset='localtime'/&gt;     # &lt;= 'utc'を'localtime'に変更
  &lt;on_poweroff&gt;destroy&lt;/on_poweroff&gt;
(...)
&lt;/domain&gt;
</pre>
<p>後は、いつも通りOSの初期設定を行います。
(時刻設定、ネットワーク設定、パッケージインストール・更新等)</p>

<p><em>ベース環境初期設定</em>:</p>
<pre class="code">
# 仮想マシンの起動
$ sudo virsh start basevm
# ゲストOSにログインして初期設定
$ sudo virsh console basevm
connect to domain basevm
Escape character is ^]

(...)
CentOS release 5.5 (Final)
Kernel 2.6.18-194.8.1.el5 on an x86_64

basevm login:~root
Password:
Last login: Sun Aug  1 19:34:34 2010 from 192.168.100.8
[root&#64;basevm ~]#
</pre>
</div>
<div class="section" id="id4">
<h3>ケース1: 開発環境を複製する</h3>
<p>個人ごとの開発環境を複製して、個別に環境をカスタマイズしたいという場合、ベース環境に必要なパッケージをインストールし、そのベース環境のLVを元にスナップショットLVを作成し、オンデマンド仮想マシンを作成すれば簡単に作成できます。
また、スナップショットLVに割り当てるディスク容量はデータが変更される分のみ見積もればよいので、ディスク容量を大幅に節約できます。</p>
<p><strong>手順</strong></p>
<ol class="arabic">
<li><p class="first">ベース環境のゲストOSで必要なパッケージをインストール</p>

</li>
<li><p class="first">仮想マシン(ベース環境)をシャットダウン</p>

</li>

<p><strong>必要な分だけ以下の操作を繰り返す</strong></p>

<li><p class="first">ベース環境用LVのスナップショットLVを作成</p>
<blockquote>
<p><em>スナップショットLVの作成</em>:</p>
<pre class="code">
# ベース環境LVのパスを確認
$ sudo virsh dumpxml basevm | grep -A5 '&lt;disk'
    &lt;disk type='block' device='disk'&gt;

      &lt;driver name='qemu' cache='writethrough'/&gt;
      &lt;source dev='/dev/ServiceVG00/basevm.img'/&gt; #&lt;= LVのパス
      &lt;target dev='vda' bus='virtio'/&gt;
    &lt;/disk&gt;
    &lt;interface type='bridge'&gt;
# スナップショットLVの作成(ベース環境に対してディスク容量を1/48で設定)
$ sudo lvcreate -s -L 1G -n snapvm.img /dev/ServiceVG00/basevm.img

</pre>
</blockquote>
</li>
<li><p class="first">作成したスナップショットLVを使用し、オンデマンド仮想マシンを作成</p>
<blockquote>
<p>OSのインストールはスキップできるので一瞬で仮想マシンの作成は完了します。
diskオプションに指定しているvolはスナップショットLVを指定しますが、指定方法は&quot;Pool名/Volume名&quot;となります。
(Libvirtで管理されているPoolからLVを作成すると自動的にLibvirtの管理下に入ります)</p>
<p><em>オンデマンド仮想マシンの作成</em>:</p>
<pre class="code">
$ sudo virt-install \
    --import \
    --accelerate \
    --name snapvm \
    --ram 1024 \
    --vcpus 2 \
    --noacpi \
    --os-variant rhel5.4 \
    --network bridge:br0 \
    --extra-args console=ttyS0,115200n8 \
    --disk vol=ServiceVG00/snapvm.img,bus=virtio,cache=writethrough \
    --noreboot \
    --keymap=ja

インストールを開始しています...
Guest installation complete... you can restart your domain
by running 'virsh start snapvm'
</pre>
</blockquote>

</li>
<li><p class="first">オンデマンド環境の仮想マシンに展開されたOSの各種調整(主にネットワーク設定)</p>
<blockquote>
<p>オンデマンド環境の仮想マシンで動作するOSは、元のベース環境とほぼ同じですが、MACアドレスの書換等ネットワーク周りの設定が自動的に変更されています。
環境依存の部分(ホスト名、ユーザパスワード)の変更を行えば、ベース環境のデータを共有しつつも、それぞれ別の開発環境として使用することが可能になります。</p>
</blockquote>
</li>
<li><p class="first">個別の環境作成</p>
<blockquote>
<p>後は、アプリケーションのデプロイをするだけで専用の開発環境が作成できます。</p>
</blockquote>
</li>
</ol>
<p>必要がなくなれば、仮想マシンを停止しスナップショットLVを削除するだけです。</p>

<blockquote>
<hr />
<div class="tip">

<p class="first admonition-title">Tip</p>
<dl class="docutils">
<dt>スナップショットLVの拡張方法</dt>
<dd>スナップショットLVは通常のLVと同じく拡張が可能ですので、当初の見積もりよりデータの変更量が大きかった場合、下記のコマンドによりディスク容量の拡張できます。</dd>
</dl>
<p><em>LVのディスク容量の拡張</em>:</p>
<pre class="last code">
# さらに+1G拡張する
$ sudo lvextend -L +1G /dev/ServiceVG00/snapvm.img
</pre>
</div>
<hr />
<blockquote>

</div>
<div class="section" id="id5">
<h3>ケース2: オンデマンド環境の構成を実稼働環境として使用</h3>
<p>オンデマンド環境のスナップショットLVをバックアップすると、ベース環境のデータとスナップショットに書き込まれたデータが取得できるので、それを新たに作成したLVに書き込みます。
これにより、ベース環境ともオンデマンド環境とも独立したサーバ環境を構築することができます。
オンデマンド環境の場合、大半のデータをベース環境と共存していましたが、この環境の場合は、すべてのデータをコピーしたクローン環境ですので、全く別の環境として構築することが可能です。</p>
<p><strong>手順</strong></p>
<ol class="arabic">
<li><p class="first">仮想マシンを停止</p>
</li>
<li><p class="first">新規仮想マシンを稼働させるLVを作成</p>
<blockquote>
<p>作成するLVのサイズはベース環境のLVと同等以上にします。</p>

<p><em>LVの作成</em>:</p>
<pre class="code">
$ sudo lvcreate -L 48G -n prod01.img ServiceVG00
</pre>
</blockquote>
</li>
<li><p class="first">スナップショットLVを利用して仮想イメージを新規LVにコピー</p>
<blockquote>
<p>ベース環境のデータとスナップショットに書き込まれたデータを新たに作成したLVにコピーします。
(ディスク容量が大きければそれだけ時間がかかります。)</p>
<p><em>仮想イメージをLVにコピー</em>:</p>
<pre class="code">

$ sudo dd if=/dev/ServiceVG00/snapvm.img of=/dev/ServiceVG00/prod01.img bs=8192
</pre>
</blockquote>
</li>
<li><p class="first">仮想マシンを起動し、ゲストOSの設定を修正</p>
<blockquote>
<p>手順は、ケース1の4.以降と同様です。</p>
</blockquote>
</li>
</ol>
</div>
<div class="section" id="os">
<h3>ケース3: 仮想サーバのOSパッケージを実際にインストール・アップグレードした場合の影響が知りたい</h3>
<p>yumコマンド等を使用すればいろいろそのパッケージの依存関係とか教えてくれますが、実際にアップグレードしてみないとわからないことも多々あります。
同じ環境の仮想サーバを複数台用意していたとして、そのスペアをアップグレードを試す環境として使うことが多いと思いますが、もし失敗した場合、スペアを破棄しなければならず再インストールということになってしまいます。
実際に試す環境として、スペアから作成したスナップショットLVを使用したオンデマンド仮想マシンを利用することもできます。
これなら失敗したとしてもスペアの環境には影響せず、かつ何度もアップグレードを試すことができます。</p>

<p><strong>手順</strong></p>
<ol class="arabic">
<li><p class="first">仮想マシンの一時停止(virsh suspend)</p>
<blockquote>
<p><em>仮想マシンの一時停止</em>:</p>
<pre class="code">
$ sudo virsh suspend basevm
</pre>
</blockquote>
</li>
<li><p class="first">仮想マシンLVのスナップショットLVを取得</p>
</li>

<li><p class="first">スナップショットLVを使用して、新規オンデマンド仮想マシンを作成</p>
</li>
<li><p class="first">オンデマンド環境のOSにログインし、実際にパッケージのアップグレード</p>
</li>
<li><p class="first">問題なかったら、オンデマンド環境を停止し削除</p>
</li>
<li><p class="first">一時停止状態の既存仮想マシンを再開し、パッケージのアップグレード</p>
<blockquote>
<p><em>仮想マシンの再開</em>:</p>
<pre class="code">
$ sudo virsh resume basevm

</pre>
</blockquote>
</li>
</ol>
<p>インストール・アップグレードで失敗しても、2-5の手順を何度でもやり直すことができます。</p>
</div>
</div>
<div class="section" id="id6">
<h2>最後に</h2>
<p>今回使用したKVMは、Kernel-based...と名前の通りKernelに組み込まれ、標準のOSをそのままに動作させることが可能になっています。
KernelがバージョンアップするごとにKVMも機能強化されていくのでさらに使いやすくなっていくことでしょう。</p>
<p>コマンドラインが扱いづらいという方は、GUIデスクトップベース・Webベースのツールがリリースされていますのでそちらを使えばよいでしょう。</p>
<p>GUIデスクトップクライアント</p>

<ul class="simple">
<li>virt-manager( <a class="reference external" href="http://virt-manager.et.redhat.com/">Virtual Machine Manager</a> )</li>
</ul>
<blockquote>
<hr />
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">今回使用したコマンド <tt class="docutils literal"><span class="pre">virt-install</span></tt> も上記と同一プロジェクトの成果物です。</p>
</div>
<hr />
</blockquote>
<p>Webアプリケーション</p>

<ul class="simple">
<li><a class="reference external" href="http://karesansui-project.info/">Karesansui</a></li>
</ul>
<p>大規模VM管理プラットフォーム</p>
<ul class="simple">
<li><a class="reference external" href="https://fedorahosted.org/ovirt/">oVirt</a></li>
</ul>
</div>
<div class="section" id="id7">
<h1>参考</h1>
<p>仮想化およびLVMについては下記が参考になります。</p>

<p><strong>Virtualization</strong></p>
<p><a class="reference external" href="http://www.redhat.com/docs/ja-JP/Red_Hat_Enterprise_Linux/5.4/html/Virtualization_Guide/index.html">仮想化ガイド</a></p>
<p><strong>KVM</strong></p>
<p><a class="reference external" href="http://www.jp.redhat.com/magazine/jp/201006/rhel.html">レッドハット・ニュースレター Vol.45 |</a></p>
<p><a class="reference external" href="http://www.atmarkit.co.jp/flinux/rensai/kvm02/kvm02a.html">＠IT：KVMの導入と基本的な使い方（1/3）</a></p>
<p><strong>LVM</strong></p>
<p><a class="reference external" href="http://www.itmedia.co.jp/enterprise/0307/11/epn01.html">エンタープライズ：特集：LVMによるディスクパーティションの動的化（前編） (1/7)</a></p>
<p><a class="reference external" href="http://tldp.org/HOWTO/html_single/LVM-HOWTO/">LVM HOWTO</a></p>
<p><a class="reference external" href="http://www-06.ibm.com/jp/domino01/mkt/cnpages7.nsf/page/default-000B6F74">IBM Linux at IBM | Linux LVM Survival Guide (LVM2)</a></p>

<p><strong>Libvirt</strong></p>
<p><a class="reference external" href="http://www.ibm.com/developerworks/jp/linux/library/l-libvirt/">libvirt 仮想化ライブラリーの徹底調査</a></p>
<p><a class="reference external" href="http://www.jp.redhat.com/magazine/jp/201002/rhel.html">レッドハット・ニュースレター Vol.41 | 今さら聞けないlibvirt、はじめの一歩</a></p>
</div>]]>
        
    </content>
</entry>

<entry>
    <title>Androidアプリ開発環境を構築する</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/08/android_1.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1838" title="Androidアプリ開発環境を構築する" />
    <id>tag:labs.unoh.net,2010://2.1838</id>
    
    <published>2010-08-02T05:32:29Z</published>
    <updated>2010-08-03T11:40:17Z</updated>
    
    <summary>MacでAndroidアプリ開発環境構築</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[<h2>はじめに</h2>
<div>
４月にウノウ入社しました、takaと申します。
ウノウではまだまだなAndroidですが、敢えてMacでAndroidアプリ開発環境を整えてみようと思います。元々はWindowsユーザでしたがウノウではMacを使って作業していますので、慣れる目的もあります。AndroidアプリはJava、Eclipseを用いた開発が主流だと思いますのでWindowsでもほぼ同様の手順で構築可能なはずです。
</div>

<h2>JDK6</h2>
<ul>
	<li>Mac OS Xでは標準で入っていたので特に作業無し。</li>
</ul>


<h2>Eclipse 3.6 Helios</h2>
<ul>
	<li>http://www.eclipse.org/</li>
	<li>http://www.eclipse.org/downloads/</li>
</ul>


<h2>Pleiades</h2>
<ul>
	<li>http://mergedoc.sourceforge.jp/</li>
	<li>Windowsの場合は「Pleiades All in One」など使用すると各種プラグインも入っていて楽かもしれない。</li>
	<li>Eclipse を日本語ローカライズするためのプラグイン。 </li>
	<li>Eclipse 3.6 Helios では上手く使えなかったので今回は保留。 </li>
</ul>


<h2>Eclipse-ADT</h2> 
<ul>
　　　　　　　<li>Android の Eclipse Plugin。</li>
	<li>Android DDMS	0.9.7.v201005071157-36220</li>
	<li>Android Development Tools	0.9.7.v201005071157-36220	</li>
</ul>


<h2>Eclipse のインストール</h2>
<ol>
	<li>Eclipse Helios (3.6) Packages for Mac OS X(Cocoa)</li>
	<li>Eclipse IDE for Java Developers の Mac OS X 64 Bitをダウンロード。</li>
	<li>Eclipse を起動。</li>
	<li>Workspace を適当に設定。デフォルトでOK。</li>
	<li>メニューから Help - Install New Software。</li>
	<li>Add ボタンを押すと入力ボックスダイアログが表示される。</li>
	<li>Name: に適当な名前を入力する。例）Google Android Plugin</li>
	<li>Location: に https://dl-ssl.google.com/android/eclipse/</li>
	<li>Developer Tools という項目が表示されるのでチェックを入れて Next。</li>
	<li>Install Details で以下のインストール項目が表示されるのを確認して Next。<br>
	-Android DDMS<br>
	-Android Development Tools</li>
	<li>I accept the term of th license agreements 同意して Finish。</li>
	<li>インストール中に Security Warning が出る事があるので OK。</li>
	<li>再起動してプラグインを有効にする必要があるので Restart Now。</li>
	<li>Eclipse - Preferences - Java - Compiler - Compiler compliance settings は 1.6。<br>
	-環境にはよっては1.5となっている事があるようで、大量のエラーが出るので要確認。<br>
	-JDK1.5と1.6ではアノテーションの仕様が変わっています。</li>
</ol>


<h2>Android SDKのインストール</h2>
<ol>
	<li>http://developer.android.com/sdk/index.html</li>
	<li>Mac OS X (intel)  	 android-sdk_r06-mac_86.zip</li>
	<li>ダウンロードして任意の場所に展開。</li>
	<li>Eclipse - 環境設定 - Android - SDK Location に展開したSDKの場所を指定。</li>
	<li>Send usage statistics to Google.はチェックを外して良い。</li>
</ol>


<h2>Android SDK Libraryのインストール</h2>
<ol>
	<li>Window - Android SDK and AVD Manager - Available Packages より全てチェック。<br>
        ダウンロードとインストールに時間が掛かるので、例えば、開発ターゲットを絞るのであれば SDK Platform Android 2.1 だけ選択もアリ。ただ、Android1.6で試したい時もあると思うので全部入れる方が楽。</li>
	<li>Install Selected より全てインストール。</li>
	<li>All Accept して Install。</li>
	<li>インストールが完了すると ADB Restart で Yes として再起動。<br>
	Updated ADB to support the USB devices declared in the SDK add-ons.<br>
	'adb kill-server' succeeded.<br>
	'adb start-server' failed.<br>
	と表示されるが気にせず Close。</li>
	<li>Eclipse 自体を再起動。</li>
</ol>


<h2>エミュレータ端末の設定</h2>
<ol>
	<li>Window - Android SDK and AVD Manager - Virtual Devices より New。</li>
	<li>Name: 任意の端末名。例：HT-03aAndroid2.2-HVGAなど判り易い名前。</li>
	<li>Target: 任意のPlatform。例：Android 2.2 - API Level 8<br>
        -Googleのサービスを使う場合は Google APIs (Google inc.) - API Level 8 など。</li>
	<li>SD Card: 任意の容量。 例：Size: 100 MiB<br>
	-ファイルイメージとしてマウントしたい場合にはFileに指定。</li>
	<li>Skin: 各解像度に合わせて設定。Default(HVGA)</li>
	<li>Hardware: GPS、タッチスクリーン、トラックボールなどの各種デバイスのサポート指定。</li>
	<li>Create ADV で端末作成。</li>
	<li>作成した端末名を選択して Start でエミュレータ端末が起動する。</li>
	<li>起動する際に、Launch Options で表示スケールを設定することもできる。</li>
	<li>作成したエミュレータ端末は詳細が見れるが変更不可能で、変更する場合は作成しなおす。</li>
</ol>


<h2>エミュレータ端末起動後画面</h2>
<ol>
	<li>左側にHOME画面。</li>
	<li>右側にデバイスキー。</li>
	<li>マウスやキーボードを使って操作可能。</li>
	<li>Window名に リスンポート番号:端末名 と表示。</li>
	<li>HOMEからMENU - Setting - Language よりUIを日本語選択可能。</li>
</ol>


<h2>エミュレータ端末でのショートカットキー</h2>
<table>
<tr><td>画面の縦横切り替え：</td><td>control+F11</td></tr>
<tr><td>Google検索ボックス：</td><td>F5</td></tr>
<tr><td>トラックボールモード：</td><td>F6</td></tr>
<tr><td>MENU/画面ロック解除：</td><td>F2</td></tr>
<tr><td>受話UP：</td><td>F3</td></tr>
<tr><td>受話DOWN/画面ロック：</td><td>F4</td></tr>
<tr><td>戻る：</td><td>ESC</td></tr>
</table>


<h2>プログラムの作成</h2>
<ol>
	<li>File - New - Project - Android - Android Project</li>
	<li>Project name: HelloAndroid</li>
	<li>Contents - Create new project in workspace<br>
	-任意適当に設定</li>
	<li>Build Target<br>
	-エミュレータ端末の設定と同様にPlatformの選択。Android2.2など。<br>
	-Googleのサービスを使う場合は Google APIs の方を選ぶなど。</li>
	<li>Application name: HelloAndroid</li>
	<li>Package name: net.unoh.helloandroid</li>
	<li>Create Activity: HelloAndroidActivity</li>
	<li>Min SDK Version: 8<br>
	-必須Androidバージョンを指定。例だと 8 と指定しないと Warning が出る。</li>
	<li>Finish プロジェクトが作成される。</li>
	<li>gen にリソースファイルが自動ジェネレートされる</li>
	<li>src にソーススケルトン。</li>
	<li>エラーとなる場合には Project - Properties - Java Build Path を確認。</li>
	<li>Java Build Path - Order and Export の Android 2.2 にチェックし、OK。</li>
	<li>assets</li>
	<li>res</li>
	<li>AndroidManifest.xml</li>
	<li>default.properties</li>
	<li>ディレクトリとファイルが生成されているのを確認。</li>
	<li>Run - Run - Run As - Android Application で OK。</li>
	<li>エミュレータ端末の画面に HelloAndroid が表示。</li>
	<li>Hello World, HelloAndroidActivity!</li>
</ol>


<h2>最後に</h2>
MacでもWindowsと同様に開発環境を構築することができました。メニューの位置が変わっているなど違いはあれどツールとしては同じものを使用しているので開発環境に差は無いでしょう。予めJDK6が入っている分、導入は楽なように思います。
Enjoy! Android!

]]>
        
    </content>
</entry>

<entry>
    <title>エンジニアが買い物をするために</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/07/post_146.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1837" title="エンジニアが買い物をするために" />
    <id>tag:labs.unoh.net,2010://2.1837</id>
    
    <published>2010-07-30T10:51:53Z</published>
    <updated>2010-07-30T12:10:48Z</updated>
    
    <summary>OSIの参照モデル、って知ってますか？ ハジメマシテ、低レベルエンジニアのnsatohです。 そうそう、あの「Layerほげがフガだから、」とかスーツ来たIT系がよく言うキーワードのアレです。 でも、インターネット的な、ゲームとかアプリケーションとかを提供するには、たしかにOSI参照モデルを知っていると前提しての会話が楽なので、知らなかった人は是非、知っておいてください。いわゆるバズワードです。 で、OSI参照モデルにはLayer1からLayer7が定義されていますが、まことしやかに「あれって10階層じゃね？」と、言われること幾久しくです。 Layer0の土建層、Layer8の金銭層、Layer9の政治層とかとか、バグフィックスがハゲシクメンドくちゃいけれど、あきらめましょう的なものがあります。あとはLayer10に宗教層、いわゆるエンジェリック・レイヤーを入れる向きもありますが、ともかく...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
        <category term="ネタ" />
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[<strong><big>OSIの参照モデル、って知ってますか？</big></strong>
ハジメマシテ、低レベルエンジニアのnsatohです。

そうそう、あの「Layerほげがフガだから、」とかスーツ来たIT系がよく言うキーワードのアレです。
でも、インターネット的な、ゲームとかアプリケーションとかを提供するには、たしかにOSI参照モデルを知っていると前提しての会話が楽なので、知らなかった人は是非、知っておいてください。いわゆるバズワードです。

で、OSI参照モデルにはLayer1からLayer7が定義されていますが、まことしやかに「あれって10階層じゃね？」と、言われること幾久しくです。

Layer0の土建層、Layer8の金銭層、Layer9の政治層とかとか、バグフィックスがハゲシクメンドくちゃいけれど、あきらめましょう的なものがあります。あとはLayer10に宗教層、いわゆるエンジェリック・レイヤーを入れる向きもありますが、ともかく、OSI参照モデル本来の7層以外は、意思とか趣向が大きく影響します。

で、その辺をふまえて、僕は一応、「<strong>低レイヤーの技術者です。</strong>」と名乗ります。

どの辺が僕の守備範囲かといえば、Layer0からLayer4辺りです。
でも、ウノウはAS持ってるわけでもないですし、特別なネットワークを構成しているわけでもないので、なんていうか、社内では一匹狼（と書いて、<em>Bocchi</em> と読む）です。
でも、低レイヤーのシゴトばかりしていると、どうしても、Layer4、5、6、7、8と手を出さないと立ち行かなくなるので、手を出します。
そうこうしているうちに、気がついたら<strong>なんとなく</strong>購買<strong>的な立ち位置のような雰囲気</strong>が出てきたりするのも良くある事。
「エンジニアの肩書きなのに彼って打合せかメールばかりだよねー」
「メールと打合せでシゴトしたふりしてていいのは小学生までだよねー、キャハハ」
と、すっかり<strong>社内的にも昼行灯</strong>と思われがちな僕の保身のためにも、サーバとかデータセンタとか、そういうのを買うときのあれこれなんかを、まぁ夏の小話程度でいいので聞いてくれると助かります。

<strong><big>購買、っていう作業</big></strong>
わりと定型。で、わりと不定形。
フツーの会社だったら当然やってるんじゃね？と思う感じの手順を並べます。

<em>1、雰囲気作り
2、見積もり
3、お伺いをたてる
4、買う！
5、届く。
6、事後処理</em>

まぁ、当たり前の手順ですよね。

<strong><big>1、雰囲気作り</big></strong>
いきなり「俺はサーバ500台、買う！」って言っても、<big><strong>"まぁもちつけ。"</strong></big>と言われて終わりです。あなたの中にある、"それを買うべき理由"を会社に伝染させましょう。
A4ペラ1枚でいいですから、
・目的はなにか。あなたはなにがしたいのか。
・目的のために、どんなものが必要なのか。
・あなたが欲しいものは、それをみたすのに最適なものなのか。
・あなたがが欲しいそれ以外で、目的を達成する方法を無理矢理考える。
の4つを書きましょう。
いやぁ、これを書くと、実は「<strong><em>他のモノの買った方がいいんじゃね？</em></strong>」となるんだわ、大抵の場合。

　ん？めんどくさい？　だよねー、書面書くとか、めんどくさいよねー。

けれど、あなたが今、買おうとしているモノの代金は、あなたが給料をもらう会社の、同じおサイフから出てるんですよ？隣にいる同僚の、その家族をたち養うためにも使われる同じおサイフのお金なんですよ？それをめんどくさいなんて！まったくあなたはひどい人ですね、マジでソンＫ〜。
とか、そういうことですのよね、つまるところ。
ですので、あなたがほしいそれを買うことが、「みんなの幸せに繋がるんだ！」と、そういう雰囲気を作る事はとても重要なオシゴトです。


<strong><big>2、見積もり</big></strong>
ここで購入金額が決まります。なのでガンバろう！
ここでいくつかTIPSがあって、

<em>・営業さんと仲良くやろう
・見積もりは3カ所からとうろう
・決定権はあなたにはない！と自覚しよう。
・見えない金額に気をつけろ！</em>

この4つは、きっと重要。じゃないかなー、とか思ったり。


<strong><em>おやくそく1：営業さんと仲良くやろう！</em></strong>
僕もそうですが、技術者は友達が少ない（偏見ですネ）ので、他人と関わるよりちゃっちゃとWebで値段だけ、見積もったりします。
で、絶対時間的にはそっちが早く見積もれたりする、とか思ってますよね。

<big><strong>いや、それはシゴトできない人のダメな考えです。</strong></big>

僕ならざっくりとした要件だけ伝えて、最後に「で、納期と金額を見積もっていただけますか。」と。

だいたい、要件すら他人に伝えられないって、自分が欲しいものが自分でもわかってないってことだよねぇ？そんなんで買い物しちゃ、あかん！それはお金を捨ててるのと一緒だと、自覚するべきだよ。

で、営業の人ががんばって見積ってくれてる間、僕は別な仕事してます。
そもそも、それぞれのメーカ別の商品の詳細仕様とか、あなたには不要です、まったく不要です。そんな細かい話は忘れて、あなたはもっと応用の効く知識を、そうですね、ARPとL2スイッチの学習とか、ルーティングとMATとロードバランサでのパケット加工とか、分散KVSとコンシステントハッシュ、あるいは購買が好きなら各メーカの製品ラインアップがどんな方針で区分けされてるのか、とかそんなことを知っているほうが重要です。
なので、細かい構成とか金額算出とか営業さんに任せて、見積書が出来るまで別なことをするのが出来るSE！ってヤツです。

<strong><em>おやくそく2：見積もりは3カ所からとろう！</em></strong>
複数から見積もりをとると安くなる、というよりは、ボられずに済む、という感じです。
全く同じものがターゲットだったとしても、メーカ直販以外の、日商とか丸紅とか立花エレとかダイワボウとかetc、と、なんでも扱える商社を通して見積もってみましょう。
で、どこから見積もっても同じような値段だったら、それはその価格が適正なんです。

複数から見積もりをお願いするときの商品名指定は善し悪しです。品名指定したら値引き合戦しか出来ませんから、僕は「こんな感じで、こんな仕様の...」と要件を伝えるようにしています。
だいたい商品知識は営業さんに負けるのが基本ですから、彼らの知識を借りましょう。シゴトなんて他人の力を借りなきゃなんもできんのです、特にIT系。
要件で指定して見積もると、ともすれば品名指定では検討すら出来なかった上位バージョンが、うっかり予算内で手にはいったりします。
まれに、ですがキャンセル品とか、決算月とか、事業部のノルマ、とかでうっかり（以下自粛
なんていうか、ぶっちゃけ、<strong><big>日 本 の 商 社 最 強　!</big></strong>。
メーカと商社は持ちつ持たれつなので、（以下自粛
信頼関係が良好ならば、納期とか金額とか取引条件とか、いろいろと融通が聞くのが営業経由での購入のいいところです。具体例は言えませんけど、強烈な提案とかまぁ、ありますよ。

たぶん、なじみの営業さんとかできると、それは転職してもお付き合いできるあなたの資産の一つになっているはずです。
たぶんここを見ているエンジニアって、どこにいったって結局サーバとかデータセンタとか使うんだよねぇ？

<strong><em>おやくそく3：決定権はあなたにはない！と自覚しよう。</em></strong>
あと購入の決定権はいつだって上司のもので、自分は上司に「これかったらいいと思います。」と畏込み申し奉るだけの民草にすぎない、と自覚すべきです。
押しの強い営業トークとかは「いやぁ、でも僕は上申するだけですから...」で逃げたりしましょう。これは緩い「お断り」の意味です。それでも食い下がってきたときはそこから買わない方がいいです、経験上。
あとは「いついつまでなら安いんです！」も悩み深いところ。ほんとに善意でそういってくれる営業さんもいるのですが、単に早く決めてほしいから、ってだけの人もいて、ここは空気を読む能力を磨いてください。ちなみに僕は期限切ってきたらほぼ買わない（笑

また、リースやレンタルっていうファイナンスの仕組みについても、最低限の知識はもっていたほうがいいです。いや僕も全然しらないんで、自戒の意味で書いてますが。
金額が100万超えそうなときは購入が現金かリースか別な方法か、上司に相談してみましょう。
このとき一緒にリースならリース条件なんかも確認出来るならしておきましょう。おつきあいのあるリース会社があればそこに、そうでないなら営業さんに相談してみてください。ファイナンス会社を紹介してくれます。
見積もり金額は普通はリースも現金も変わる事はありませんが、リース会社の利益がそこにのりますので、あなたの会社の総支払い額はけっこう上がります。

<strong><em>おやくそく4：見えない金額に気をつけろ！</em></strong>
また書面には明記されてない金額、というのにも気を配った方がいいです。
消費税はもちろんですが、送料も馬鹿になりません。リースやレンタルの購入であればその利率も気にしてください。
同じ値段のサーバでも片方はセットアップ済み、もう片方はOS未導入、とかなると、OS導入の人件費であっという間にトータルの費用に差が出ます。
あなたはあなたが思う以上に、高い費用が発生しているのです！
ふつー、1人の人間をどっかの会社で使うとしたら、100万/月ってのはぶっちゃけ劇安です。100万の中からオフィス代、電気代、交通費、パソコン代、etcって考えたら給料に出せるのがいくらか、精々30万でしょう。人間が一番高い！ってのは概ね、正しいのです。

あー、あれだ、ケータイ買うときの充電器が！登録料が！っていうアレ、アレの感覚で居ればいいんじゃね？　なんていうか想定外の出費って意味で。

で、見積もりが来たら内容をちゃんと確認しましょう。
僕も以前、全部で3千万のシステムを購入するのに、400万のロードバランサーが2セット入っていて、購入してから途方に暮れたことがあります。このときもそうですが、2台でActive/Standbyクラスタをくむとき、2台と2セットで間違うとか、良くあるんです。
まー、別件に使って元はとったけどネ！！

<strong><big>3、お伺いをたてる</big></strong>
さて見積もりも2つか3つ、手元に出来た！さぁ一番安いのを買うぞ！ってそれはちょっとマテ。
ほんとにそれは安いの？ってのは重要な事です。
書面にない金額の発生は、どうせあるので、総額を概算するのはあなたの仕事です。1割程度は高くなると思っておく方がいいよ。
納期は大丈夫ですか？
<em><strong>1年後にチロルチョコ100個届けてやるから、今日500円振り込めよ！</strong></em>って詐欺だよねぇ？
海外からの輸入品は船便なら納品が二ヶ月三ヶ月は当たり前です。
この辺の国内在庫を見極めたりも、営業さんときちんと関係が出来てれば見積もりの時点で解決出来ている問題になってたりするのです。こんなのまでいちいち自分で市場在庫（ってか扱ってる通販サイトで在庫ありを探す）を探すとか、時間の無駄だし、蛇の道は蛇なのですよ、こういうのは。
納期も鑑み、値段も鑑み、性能もOKだとなったら、会社に「これ、こうてくれやぁー！」と稟議を申請します。きっとあなたは一番安い見積もりではない別な見積もり書で「こっちをこうてくれやー！」と言っている事でしょう。

<strong><big>4、買う！</big></strong>
実際に買う！　って実はあんまり楽しくないです。
見積もり書を会社に示したあと、そっからさきはただただ面倒な書類の作成が続くのです。
売買契約書はもちろんですが、銀行振込のための書類やら、注文書やら、リースとかを使うとなればリース申し込み書もそうですし、購入する機械によってはNDAも必要になったりします。これらの書類に会社のハンコをもらわないとイケマセン。
いやぁハンコ無しでも発効しちゃったりするんですが、けど、ハンコもらっとけば「たしかに会社の判断じゃんか！」と勝手にあなたが行った事じゃないですよ、と体面がたちます。
だいたい、現金で買う訳はないですし、ただ数字だけが口座間を移動する、それだけのための書類なのです。

なんかこー、買う段になってくると、もうね、なんていうか、飽きちゃってる感じ。
何が出来るもので値段いくらでオプションがこうで、とかとか詳細に知りすぎてて、内心、次のシゴトに浮気し始める頃。

購入手続きで最悪なのは携帯電話。
同一機種のまとめ買いならともかく、検証用にばらばらに買うと申し込み書を書くのに1日仕事。
「手が、手がー！」あるいは「ふはは、書き損じがゴミのようだ！」（いや、ゴミですが、実際）となります。
犯罪に悪用されるのを防ぐためとはいえ、いろいろと面倒です。

あと、忘れちゃ行けないのはファイナンス。
これの事務手続は社の信用によってはいろいろとあると思います。

<strong><big>5、届く</big></strong>
モノが届きます。届きますが正直、めんどくさいです。
段ボールの処分だってタダじゃないんです、企業が出すゴミは産業廃棄物です。ふつーの清掃工場じゃ引き取ってくれません。お金払って処理してもらうんですよ？
<em><strong>まぁ値段交渉中に「段ボールの処分はお願いしますねー」とか、僕は言っちゃいますが</strong></em>諸刃の剣、素人にはおすすめできない。それは今までの取引の経緯があればこそ。ふつーは「産廃業者のお見積りもできますよ」と返されます。
100余台、30kgの物体を開梱して、内容確認して、まぁ2日くらいかかりますね！

ぶっちゃけ、購買ずっとやってると、見積もり完了したあたりで大満足。届く頃には購入対象物にもう飽きがきちゃってるとかが基本。なんていうか、感動とか、無い。この仕事で一番困るのは、「モノを買っても感動しなくなる。」

あと、同じものを個人で買うと割高なことを知ってしまう事、とかかな...
携帯電話とかもうね、ずっとP504だったもの...。

で、<em><em>ここ3ヶ月で携帯電話を40回線買った僕が今使ってるのはxperia。</em></em>だって、ほら、ねぇ？（笑

<strong><big>6、事後処理</big></strong>
この事後処理はわりと忘れてしまいがちです。
検品書、ってやつがくるので検品が済んでいるなら、会社ハンコを押して返送してあげましょう。
納品書、領収書は経理か総務か、それっぽい部署の人に渡しましょう。
領収書や納品書はふつうの会社組織ならとても重要な書類のはずです。
でも、納品された現物にどうしても気が取られますので、うっかり捨ててしまったり、しがちです。でも再発行してもらえない書類です。
段ボールについてる納品書とか、開梱作業をヘルプしてくれた人にちゃーんと説明しておかないと、まぁ、ほぼ捨てられるね！

事後処理で事務処理が続くと、<em><strong>ほんと経理とか総務とか「スゲー！」って感動する。</strong></em>だって彼ら、日々この事務処理やってるんだよ？同じ事、出来る？？とか思うと、なんていうか、バックオフィス様に足を向けては寝れないよね。

と、当たり前の話を書いてみました。
でも、きっと、事務方のみなさんからすれば邪道な買い物だ！とか言われそうで怖いですが、まぁ、そんな感じで。

最後に
「　〜前略〜はみんなイチコロさ。ハッタリかまして、ブラジャーからミサイルまで、何でもそろえてみせるぜ。」]]>
        
    </content>
</entry>

<entry>
    <title>Vim: ちょこっと設定、ちょこっとプラグイン。</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/07/vim.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1831" title="Vim: ちょこっと設定、ちょこっとプラグイン。" />
    <id>tag:labs.unoh.net,2010://2.1831</id>
    
    <published>2010-07-29T06:37:37Z</published>
    <updated>2010-08-03T09:44:39Z</updated>
    
    <summary><![CDATA[ども。6月入社のjhoshinaです。 まだ入社したてのつもりだったのに、ラボブログ執筆がまわってきてあたふたしています。 何を書こうか迷ったのですが、環境も新しくなったことですし、設定しなおしたVimの設定・プラグインなどをちょこっと見ていこうかと思います。phpとsymfony関係を中心に見ていきます。 雑多な設定 特に変わったところもないのですが一応。 ~/.vimrc set nocompatible set showmatch showmode showcmd set laststatus=2 set statusline=%&lt;%f\ %m%r%h%w%{'['.(&amp;fenc!=''?&amp;fenc:&amp;enc).']['.&amp;ff.']'}%=%l,%c%V%8P syntax on filetype on filetype indent on f...]]></summary>
    <author>
        <name>unoh</name>
        
    </author>
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[<p>ども。6月入社のjhoshinaです。</p>

<p>まだ入社したてのつもりだったのに、ラボブログ執筆がまわってきてあたふたしています。</p>

<p>何を書こうか迷ったのですが、環境も新しくなったことですし、設定しなおしたVimの設定・プラグインなどをちょこっと見ていこうかと思います。phpとsymfony関係を中心に見ていきます。</p>


<h3 style="color:#ff9933;">雑多な設定</h3>

<p>特に変わったところもないのですが一応。</p>


<h4>~/.vimrc</h4>

<pre class="code">
set nocompatible
set showmatch showmode showcmd
set laststatus=2
set statusline=%&lt;%f\ %m%r%h%w%{'['.(&amp;fenc!=''?&amp;fenc:&amp;enc).']['.&amp;ff.']'}%=%l,%c%V%8P

syntax on
filetype on
filetype indent on
filetype plugin on
</pre>


<h4>~/.vim/ftplugin/php.vim</h4>

<pre class="code">
setlocal shiftwidth=4 tabstop=4 expandtab nowrap

let php_sql_query = 1
let php_baselib = 1
let php_htmlInStrings = 1
let php_noShortTags = 1
let php_parent_error_close = 1
let php_parent_error_open = 1
&quot;let php_folding = 1
&quot;let php_sync_method = x

&quot; for QuickFix
setlocal makeprg=php\ -l\ %
setlocal errorformat=%m\ in\ %f\ on\ line\ %l
</pre>

<p>自分の場合、見通しのよさと影響を重視して、ファイルタイプ別の設定は極力~/.vim/ftplugin/*.vimにsetlocalで書くようにしています。</p>

<p>設定読み込みの順番はruntimepathが示すように、 ~/.vim -> (Vim標準ディレクトリ) -> ~/.vim/after となっているので、各種プラグインなどが読み込まれた後に設定が必要であれば ~/.vim/after 以下に設定してます。</p>


<h3 style="color:#ff9933;">matchit.vim</h3>

<p>PHPと直接関係ないのですが、対応する括弧に移動するコマンド「%」を拡張するマクロです。</p>

<p>HTMLの対応タグにジャンプできるのはもちろんのことで、例えば、</p>

<pre class="code">
&lt;?php if ($foo): ?&gt;
Hello World!
&lt;?php else: ?&gt;
Oh..
&lt;?php endif ?&gt;
</pre>

<p>というコードがあった場合、ifの上で%を押すことによって、else、endifの場所に移動してくれます。</p>

<p>matchit.vimはVimの標準配布物に含まれているため、</p>

<pre class="code">
source $VIMRUNTIME/macros/matchit.vim
</pre>

<p>と、~/.vimrcに記述するだけで有効になります。もちろん、~/.vim/pluginにコピーしても動きます。</p>


<h3 style="color:#ff9933;">symfony.vim</h3>

<ul>
<li><a href="http://github.com/soh335/vim-symfony">soh335's vim-symfony at master - GitHub</a></li>
</ul>

<p>Symfonyでファイル間の移動などを行うプラグインです。</p>

<p>Symfonyで開発をしていると、Controller-View-Model間の移動が頻繁に行われますが、このプラグインを使うと移動が楽になります。</p>
<p>例えば、ControllerのexecuteIndex()内にカーソルがある状態で「:Sview」と打つと対応するindexSuccess.phpを、逆にindexSuccess.php上で「:Saction」と打つとアクション内の対応メソッドへとジャンプしてくれます。もちろんモデル名の上で「:Smodel」と打てば対応するモデルへと移動してくれます。</p>

<p>また、symfonyコマンドを発行することも可能で、</p>

<pre class="code">
:Symfony generate:module frontend dashboard
:Symfony cc
</pre>

<p>などと、シェルに降りるまでもなく作業が続けられます。</p>


<h3 style="color:#ff9933;">snipMate</h3>

<ul>
<li><a href="http://www.vim.org/scripts/script.php?script_id=2540">snipMate : TextMate-style snippets for Vim</a></li>
</ul>

<p>TextMate<http://macromates.com/>にあるようなsnippetの機能を提供するプラグインです。</p>

<p>同種のプラグインに<a href="http://www.vim.org/scripts/script.php?script_id=1318">snippetsEmu</a>がありますが、snippetの書きやすさからsnipMateを使っています。</p>

<p>ife&lt;TAB&gt;と入力すると</p>

<pre class="code">
if (/* condition */) {
    // code...
} else {
    // code...
}
</pre>

<p>と展開されます。「/* condition */」の部分が選択された状態になっており、入力が終わったらTabを押すことによって、「// code...」の部分に移動できます。</p>

<p>前述のsymfony.vimをいれてあると、以下の設定でバッファにSymfonyのファイルタイプを仕込むことができるので、symfonyのファイルを開いた場合だけに適用するsnippetを定義することもできます。</p>

<pre class="code">
autocmd User SymfonyBufInit setlocal filetype+=.symfony
</pre>

<p><a href="http://github.com/soh335/vim-symfony-snipmate">soh335's vim-symfony-snipmate at master - GitHub</a>にあるものを自分向けにいじって使っています。これで「requ&lt;TAB&gt;」と打つと「$this->getRequestParameter(&quot;&quot;);」なんて展開が可能になります。「Symfonyってメソッド名長いよね」と常々思っているので手放せません。


<h3 style="color:#ff9933;">その他</h3>

<p>他に最近ではctagsからcscopeに移行したり、project.vimをやめてNERDTreeにしたりしたのですが、まだ十分に使いこなせていない感じもあるので、このあたりはまた次の機会にでも。</p>

<p>では！</p>]]>
        
    </content>
</entry>

<entry>
    <title>symfonyのsfBrowserを使ってコンソールツールを作成してみました</title>
    <link rel="alternate" type="text/html" href="http://labs.unoh.net/2010/07/symfonysfbrowser.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://www.unoh.net/mt32/mt-atom.cgi/weblog/blog_id=2/entry_id=1832" title="symfonyのsfBrowserを使ってコンソールツールを作成してみました" />
    <id>tag:labs.unoh.net,2010://2.1832</id>
    
    <published>2010-07-29T05:46:35Z</published>
    <updated>2010-07-29T10:28:56Z</updated>
    
    <summary>はじめまして、4月に入社しました、はなだと申します。 以後よろしくお願いいたします。 私自身は、これまでにJava/C#/C++/Perlなどを使った開発を行って来ましたが、現在は（はじめての！）PHPを使ったソーシャルアプリの開発を行っています。 これまでは、新しい言語を学習する際に、サンプルやチュートリアルを終えたあとで、ちょっとした作業を簡略化するためのツールをチョコチョコとつくっていました。今回は、はじめてのPHPということで、symfonyのsfBrowserを使った、コンソールツールを作ってみました。 このツール開発を通じてやりたいことは次のような内容です。 開発しているsymfonyのURLを指定して、コンソールベースのツールでリンクを遷移させたい できればUser-Agentとして携帯電話に対応させたい さらに　 symfonyの内部構造を理解したい PHPの言語仕様やライ...</summary>
    <author>
        <name>unoh</name>
        
    </author>
    
        <category term="symfony" />
    
    <content type="html" xml:lang="ja" xml:base="http://labs.unoh.net/">
        <![CDATA[<p>はじめまして、4月に入社しました、はなだと申します。<br />
以後よろしくお願いいたします。</p>
<br/>
<p>私自身は、これまでにJava/C#/C++/Perlなどを使った開発を行って来ましたが、現在は（はじめての！）PHPを使ったソーシャルアプリの開発を行っています。<br/>
これまでは、新しい言語を学習する際に、サンプルやチュートリアルを終えたあとで、ちょっとした作業を簡略化するためのツールをチョコチョコとつくっていました。今回は、はじめてのPHPということで、<a href="http://www.symfony-project.org/">symfony</a>の<a href="http://www.symfony-project.org/api/1_4/sfBrowser">sfBrowser</a>を使った、コンソールツールを作ってみました。
</p>
<p>このツール開発を通じてやりたいことは次のような内容です。
<ul>
<li>開発しているsymfonyのURLを指定して、コンソールベースのツールでリンクを遷移させたい</li>
<li>できればUser-Agentとして携帯電話に対応させたい</li>
</ul>
<p>さらに　</p>
<ul>
<li>symfonyの内部構造を理解したい</li>
<li>PHPの言語仕様やライブラリに慣れたい</li>
</ul>
というものがありました。
</p>
<p>ところで、携帯電話対応のWebアプリに対する動作確認を行う場合には、<a href="http://firemobilesimulator.org/">FireMobileSimulator</a>を使うのが一般的だと思いますが、2台以上の携帯電話で相互に影響を及ぼす機能を確認する際には、いちいち端末を切り替えるよりも、コンソールからお手軽にリンクをクリックさせたいと思ったのが作成動機です。</p>
<p>コードは以下のようになります。</p>

<pre class="code">
&lt;?php
// ツールの初期化関数
function initialize()
{
    // symfonyプロジェクトの設定情報を読み込む
    require_once dirname(__FILE__).'/config/ProjectConfiguration.class.php';
    // アプリ名と環境を指定して設定情報を取得
    $configuration = ProjectConfiguration::getApplicationConfiguration('アプリ名', 'test', true);
    // コンテキストを作成
    sfContext::createInstance($configuration);
    // remove all cache
    sfToolkit::clearDirectory(sfConfig::get('sf_app_cache_dir'));

    // sfBrowserインスタンスを作成
    $browser = new sfBrowser();
    // 必要であれば携帯電話のUser-Agentと固有IDを設定
    $browser
        -&gt;setHttpHeader('User-Agent', 'DoCoMo/2.0 N02A(c100;TB;W24H16)')
        -&gt;setHttpHeader('X-DCMGUID', '携帯電話の固有ID');
    return $browser;
}

// sfBrowserインスタンスとURLを指定して、anchorのリストを表示
function process($browser, $url)
{
    echo "Url: [$url] \n";
    // セレクタ取得
    $selector = $browser-&gt;get($url)-&gt;getResponseDomCssSelector();
    echo 'Title: ' . $browser-&gt;getResponse()-&gt;getTitle() . "\n";
    // aタグノードの一覧を取得
    $anchors = $selector-&gt;matchAll('a')-&gt;nodes;
    foreach($anchors as $key=&gt;$anchor) {
        echo "$key: " . $anchor-&gt;nodeValue . "\n";
    }
    echo "q:quit, 0-:click link\n&gt; ";
    return $anchors;
}

function main($url)
{
    $browser = initialize();
    // 標準入力をopen
    $fp = fopen('php://stdin', 'r');
    if (!$fp) {
        exit("Failed to open STDIN\n");
    }

    // ループを回す
    while(!feof($fp)) {
        $anchors = process($browser, $url);
        $command = fgets($fp, 256);
        $command = trim($command);
        if ($command === 'q') {
            // コマンドが'q'の場合には終了
            break;
        } else if (is_numeric($command)) {
            // コマンドが数字の場合にはanchorの配列から取得
            $index = intval($command);
            if ($index &lt; count($anchors)) {
                // href属性からURLを取得
                $url = $anchors[$index]-&gt;getAttribute('href');
            } else {
                echo "Out of range. [$index]\n";
            }
        } else {
            echo "Unknown command. [$command]\n";
        }
    }
    fclose($fp);
    echo("\nQuit.\n");
}

if ($argc &lt; 2) {
    exit("Usage:&gt;php $argv[0] [url]\n");
}
main($argv[1]);
?&gt;</pre>

<p>例えばこれをsymfonyのプロジェクトルートディレクトリ直下にConsoleBrowser.phpという名前で保存します。<br/>
ルートパス（/）をオープンするには、次のようなコマンドを実行します。
</p>
<pre class="code">$ cd myprj
$ php ConsoleBrowser.php /
</pre>

<p><strong>"q"</strong>コマンドでツールを終了できます。読み込んだコンテンツにaタグ（anchor）がある場合には、数字付きリストで表示されるので、その番号を入力すれば、リンク先に遷移します。</p>

<p>基本的な実装しかしておりませんので、formタグに対応させる、内容を表示させる、postで送信する、パラメータの入力を行う、などの拡張は皆様の手で是非とも追加してみてください。</p>
]]>
        
    </content>
</entry>

</feed> 

