<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ロジカルにアナログで。</title>
	<atom:link href="http://ohnaka.jp/blog/feed" rel="self" type="application/rss+xml" />
	<link>http://ohnaka.jp/blog</link>
	<description>大中邦彦の雑記Blog</description>
	<lastBuildDate>Wed, 07 Dec 2011 04:13:43 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>ソーヤー効果</title>
		<link>http://ohnaka.jp/blog/2011/12/561</link>
		<comments>http://ohnaka.jp/blog/2011/12/561#comments</comments>
		<pubDate>Wed, 07 Dec 2011 04:13:43 +0000</pubDate>
		<dc:creator>くにちこ</dc:creator>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[日記]]></category>

		<guid isPermaLink="false">http://ohnaka.jp/blog/?p=561</guid>
		<description><![CDATA[最近「ソーヤー効果」というものをいろんなところで見かけて気になっています。 ソーヤー効果についてはこちらのブログを見ると分かりやすいかも。 つまり、同じ作業であっても、 「報酬をあげるから、この作業をやってくれません？」 [...]]]></description>
			<content:encoded><![CDATA[<p>最近「ソーヤー効果」というものをいろんなところで見かけて気になっています。</p>
<p>ソーヤー効果については<a href="http://core-room.sblo.jp/article/45006005.html" target="_blank">こちらのブログ</a>を見ると分かりやすいかも。</p>
<p>つまり、同じ作業であっても、</p>
<p>「報酬をあげるから、この作業をやってくれません？」</p>
<p>という見せ方と、</p>
<p>「この作業はすごく楽しい。お金払ったらさせてあげるよ」</p>
<p>という見せ方の２通りがあるということです。</p>
<p><span id="more-561"></span></p>
<p><span style="color: #0000ff;">前者が「仕事」であり、後者が「遊び」なわけ</span>です。ところが、全く同じ作業であっても「仕事だ」とおもってやっているとたのしく無いし、モチベーションも上がらない一方、「遊びだ」と認識できるととたんにモチベーションが上がってしまったりします。</p>
<p>うちの会社の開発業務でも取り入れられないかなぁなんて思ったのですが、例えばSubversionのリポジトリを監視していて、日々コミットしたソースコードの行数をレポートするような簡単なロボットがあって、「月間MVP」みたいなのが出てくるとか。</p>
<p>それは人事考課に影響するものではなくて、<span style="color: #0000ff;">単なる「遊び」</span>です。</p>
<p><span style="color: #ff0000;"><strong>だれかそういうの作ってませんかね？</strong></span>Subversionのリポジトリを監視するくらいのものであればコストもかからず簡単に導入できそうですし〜。</p>
<p>&nbsp;</p>
<p>ちなみに、プログラムの価値は行数で評価されるものではないので、そんなもので給与が決まったらたまりませんが、遊びであれば問題ありません。逆に<span style="color: #0000ff;">単なる行数</span>なので、若手が上司に勝てたりする余地もあります。</p>
<p>もちろん上位になるために無駄なものをコミットしたりしてはいけません。そんな事をしたら「ダメなコードを書く人」として人事考課に影響します。そういうガードはかかっているので、暴走する危険はなさそうです。</p>
<p>&nbsp;</p>
<p>こういった話は「ゲーミフィケーション」というネタで最近話題になっているようなので、しばらくウォッチしてみたいとおもいます。</p>
]]></content:encoded>
			<wfw:commentRss>http://ohnaka.jp/blog/2011/12/561/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>今年もウェブポやります!</title>
		<link>http://ohnaka.jp/blog/2011/10/556</link>
		<comments>http://ohnaka.jp/blog/2011/10/556#comments</comments>
		<pubDate>Mon, 24 Oct 2011 07:53:25 +0000</pubDate>
		<dc:creator>くにちこ</dc:creator>
				<category><![CDATA[日記]]></category>

		<guid isPermaLink="false">http://ohnaka.jp/blog/?p=556</guid>
		<description><![CDATA[早いものでもう10月も下旬。あと２ヶ月もしたら今年も終わりです。 そんな訳で必要なのが年賀状。我が社の「ウェブポ」ですが、今年もやります。 http://webpo.jp/top/ 例年ご要望の多かった喪中はがきの印刷に [...]]]></description>
			<content:encoded><![CDATA[<p>早いものでもう10月も下旬。あと２ヶ月もしたら今年も終わりです。</p>
<p>そんな訳で必要なのが年賀状。我が社の「ウェブポ」ですが、今年もやります。</p>
<p><a title="年賀状のぜんぶをオンラインで。ウェブポ。" href="http://webpo.jp/top/">http://webpo.jp/top/</a></p>
<p>例年ご要望の多かった喪中はがきの印刷にも対応する予定です。</p>
<p>受付は11/1から。</p>
<p>乞うご期待！</p>
]]></content:encoded>
			<wfw:commentRss>http://ohnaka.jp/blog/2011/10/556/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apache ThriftのJavaサーバにC# (.NET)クライアントから接続する</title>
		<link>http://ohnaka.jp/blog/2011/09/538</link>
		<comments>http://ohnaka.jp/blog/2011/09/538#comments</comments>
		<pubDate>Fri, 02 Sep 2011 05:37:08 +0000</pubDate>
		<dc:creator>くにちこ</dc:creator>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[.NET Framework]]></category>
		<category><![CDATA[Apache Thrift]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[HttpWebRequest]]></category>
		<category><![CDATA[ServicePointManager]]></category>
		<category><![CDATA[SSL]]></category>
		<category><![CDATA[THttpClient]]></category>
		<category><![CDATA[Visual Studio]]></category>

		<guid isPermaLink="false">http://ohnaka.jp/blog/?p=538</guid>
		<description><![CDATA[前回までの記事で、Apache Thriftを使ったJavaサーバとそれに接続するJavaクライアントを作りました。構成としては、 Apache Thrift 0.6.1〜0.7.0 サーバサイドは Tomcat上で S [...]]]></description>
			<content:encoded><![CDATA[<p>前回までの記事で、Apache Thriftを使ったJavaサーバとそれに接続するJavaクライアントを作りました。構成としては、</p>
<ul>
<li>Apache Thrift 0.6.1〜0.7.0</li>
<li>サーバサイドは Tomcat上で ServletとしてThriftの要求を受付</li>
<li>トランスポート層はHTTPで、TBinaryProtocolを使用</li>
</ul>
<p>という感じです。<strong><span style="color: #0000ff;">このJavaのサーバにC#のクライアントから接続できるかどうか</span></strong>、試してみたいと思います。<br />
<span id="more-538"></span></p>
<h2>.NET版のThrift DLLを作る</h2>
<p>MacOS X 上でビルドした Thriftコンパイラは、C#のコードを吐く事ができます。ただこれはあくまで自動生成部分です。Javaも ThriftのJarを Mavenで取ってきていましたが、.NET版の実際の動作にはThriftのDLLを準備する必要があります。</p>
<p>どこからかダウンロードできると良いのですが、公式サイトにはソースコードしかありません。Windows用のThrift Compilerは .exeが置いてあるんですがね、、、。</p>
<p>そんなわけで Visual Studio 2010を使ってソースからDLLをビルドします。</p>
<h3>ソースを展開する</h3>
<p><a href="http://thrift.apache.org/download/">http://thrift.apache.org/download/</a> からソースコードをダウンロードします。執筆時点での最新版はthrift-0.7.0.tar.gz です。これを展開すると、いろいろファイルが出てきますが、目的のDLLのソースは、lib/csharp/src の中にあります。</p>
<p>このフォルダに Thrift.sln という VisualStudioのソリューションファイルがあるのですが、なぜかうまく開けません。<span style="color: #ff0000;"><strong>「選択されたファイルは有効なソリューションファイルではありません。」</strong></span>というエラーが出てしまいます。</p>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-error.png"><img class="aligncenter size-medium wp-image-540" title="vs-error" src="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-error-300x121.png" alt="" width="300" height="121" /></a></p>
<p>thrif-0.6.1でも thrift-0.7.0でもだめでした。海外のサイトにはあまり情報がないようなのですが、日本語のVisual Studio特有の問題なのでしょうか？</p>
<h3>Visual Studioでビルドする</h3>
<p>仕方がないので空のソリューションを作り、そこにソースを突っ込んでビルドします。手順は以下の通りです。</p>
<ul>
<li>VS2010を立ち上げ、「ファイル」-「新規作成」-「プロジェクト&#8230;」を選択します。</li>
<li>新規プロジェクトダイアログで「クラスライブラリ」を選択します。</li>
</ul>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-newproj.png"><img class="aligncenter size-medium wp-image-541" title="vs-newproj" src="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-newproj-300x207.png" alt="" width="300" height="207" /></a></p>
<ul>
<li>プロジェクト名は Thrift-0.7.0 にしてみました。</li>
<li>プロジェクトができたら、ソリューションエクスプローラの上でThrift-0.7.0プロジェクトを右クリックし、プロパティを開きます。</li>
<li>プロジェクトのプロパティで、使用する .NETフレームワークのバージョンを選択します。僕は「 .NET Framework 4.0」を選びました。「.NET Framework 4.0 Client Profile」というのもあるのですが、こちらだと、System.Webパッケージが使用できないため、Thriftをビルドできませんでした。</li>
<li>プロジェクトを作成したら、thriftのlib/csharp/src の中身をドラッグ＆ドロップするなどしてプロジェクトに追加します。</li>
<li>「参照設定」を右クリックして「参照の追加&#8230;」メニューを開きます。</li>
<li>参照の追加で、.NETの System.Webを追加します。</li>
<li>ここまでの状態で、プロジェクトは以下のようになっているはずです。</li>
</ul>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-reference.png"><img class="aligncenter size-medium wp-image-542" title="vs-reference" src="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-reference-300x293.png" alt="" width="300" height="293" /></a></p>
<ul>
<li>「ビルド」-「ソリューションのビルド」を選択してビルドします。設定に間違いがなければビルドに成功するはずです。</li>
<li>ビルドに成功したら、プロジェクトディレクトリの中に obj/Debug/Thrift-0.7.0.dll が出来ています。今後はこのファイルをコピーして利用します。</li>
</ul>
<h2>CardServiceのクライアントプロジェクトを作成する</h2>
<p>DLLが出来たところで、JavaのCardServiceサーバに接続するクライアントプログラムをC#で作ってみます。VS2010で「ファイル」-「新規作成」-「プロジェクト&#8230;」を選択し、今度は「コンソールアプリケーション」を選びます。GUIが得意な人は「Windowsフォームアプリケーション」などでも構いません。適宜読み替えてください。</p>
<p>プロジェクト名は「CardServiceClient」にしてみました。</p>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-csclient.png"><img class="aligncenter size-medium wp-image-543" title="vs-csclient" src="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-csclient-300x207.png" alt="" width="300" height="207" /></a></p>
<h3>ThriftのDLLを追加する</h3>
<p>プロジェクトが出来上がったら、先ほど作成したDLLをプロジェクトに追加します。プロジェクトのトップに lib フォルダを作り、そこにコピーします。</p>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-importdll.png"><img class="aligncenter size-medium wp-image-544" title="vs-importdll" src="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-importdll-300x201.png" alt="" width="300" height="201" /></a></p>
<p>これだとファイルをコピーしただけなので、実際にThrift-0.7.0.dllが使われるように、参照設定を変更します。「参照設定」を右クリックして「参照の追加&#8230;」を選択します。</p>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-setdll.png"><img class="aligncenter size-medium wp-image-545" title="vs-setdll" src="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-setdll-300x253.png" alt="" width="300" height="253" /></a></p>
<p>ダイアログが開いたら「参照」タブを選択して、今プロジェクトにコピーした lib/Thrift-0.7.0.dll を選択します。</p>
<p>これでプログラムをビルドする準備が整いました。</p>
<h3>C＃用のソースコードを生成する</h3>
<p>Thriftコンパイラ(thriftコマンド)を使って CardServiceのC#用ソースコードを生成します。Javaのサーバ側を作ったときと同じ CardService.thriftファイルを用います。</p>
<h3>CardService.thrift</h3>
<pre>#!/usr/bin/thrift

namespace java jp.ohnaka.thrift
namespace csharp Ohnaka.Thrift

struct Card {
  1: string id;
  2: string name;
}

exception TIllegalArgumentException
{
    1: string message
}

service CardService
{
        void addCard(1: Card card) throws (1:TIllegalArgumentException e);
        list&lt;Card&gt; getCards();
        Card getCardById(1: string id) throws (1:TIllegalArgumentException e);
}</pre>
<p>ただし、Javaのコードを生成した時から一行だけ追記しています。C#用のネームスペースを宣言する部分です。</p>
<pre>namespace csharp Ohnaka.Thrift</pre>
<p>今回は、Ohnaka.Thriftとしてみました。お好みで書き換えてください。準備ができたら C#のコードを生成します。thriftコンパイラは MacOS X 上で作ったものをつかってみましたが、Windows用のThriftコンパイラ(thrift.exe)をダウンロードしてきてもOKです。</p>
<pre># thrift --gen csharp CardService.thrift</pre>
<p>Javaのコードと同時に生成するのであれば、</p>
<pre># thrift --gen java --gen csharp CardService.thrift</pre>
<p>としてください。</p>
<p>うまくいけば、gen-csharpというフォルダが作られますので、その中身をVisual Studioの CardServiceClientプロジェクトに追加します。</p>
<h3>プロジェクトの設定を変更する</h3>
<p>CardServiceClientプロジェクトの設定を少し変更する必要があります。というのも、Thrift-0.7.0.dllは、.NET Framework 4.0 を使ってつくられました。しかし、今作ったCardServiceClientはデフォルトで .NET Framework 4.0 Client Profieというものを使うように設定されています。<strong><span style="color: #0000ff;">ThriftのDLL は System.WebというClient Profileには入っていない namespaceのライブラリを使っている</span></strong>ため、そのままでは動かないのです。</p>
<p>プロジェクトを右クリックして、プロパティを表示し、フレームワークを「.NET Framework 4.0」に変更してください。変更が終わったら、「参照設定」を右クリックし「参照の追加&#8230;」から System.Webを追加してください。</p>
<p>次のような画面になればOKです。</p>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-importsrc1.png"><img class="aligncenter size-medium wp-image-549" title="vs-importsrc" src="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-importsrc1-300x201.png" alt="" width="300" height="201" /></a></p>
<h3>サーバを呼び出すコードを記述してみる</h3>
<p>ひな形の mainメソッドがかかれた Program.cs というファイルがありますので、これを直接いじって、main()メソッドにコードを書いてみます。</p>
<pre class="brush:csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Thrift.Transport;
using Thrift.Protocol;
using Ohnaka.Thrift;

namespace CardServiceClient
{
    class Program
    {
        static void Main(string[] args)
        {
            // 接続先のURLを指定
            THttpClient transport = new THttpClient(new Uri("http://localhost:8080/thrift-test-server/card"));
            // バイナリプロトコルを使用（サーバ側と合わせる）
            TProtocol protocol = new TBinaryProtocol(transport);
            // クライアントスタブを作成
            CardService.Client client = new CardService.Client(protocol);

            try
            {
                transport.Open();

                // サーバ側のaddCardメソッドを呼び出す
                Card card1 = new Card();
                card1.Id = "1000";
                card1.Name = "ohnaka";
                card1.Test = new Test();
                client.addCard(card1);

                // もう一個カードを作ってみる
                Card card2 = new Card();
                card2.Id = "1001";
                card2.Name = "kuni";
                client.addCard(card2);

                // サーバ側のgetCardsメソッドを呼び出す
                List&lt;Card&gt; cards = client.getCards();
                System.Console.WriteLine("Num of cards: " + cards.Count);

                // サーバ側のgetCardメソッドを呼び出す
                Card retrievedCard = client.getCardById("1000");
                System.Console.WriteLine("Retrieved card id   = " + retrievedCard.Id);
                System.Console.WriteLine("Retrieved card name = " + retrievedCard.Name);

                // 不正な引数を渡してみる
                try
                {
                    client.addCard(null);
                }
                catch (Exception ex)
                {
                    // どんな例外が飛ぶか調べる
                    System.Console.WriteLine(ex.Message);
                }
                // IDが null なCardを渡してみる
                try
                {
                    client.addCard(new Card());
                }
                catch (Exception ex)
                {
                    // どんな例外が飛ぶか調べる
                    System.Console.WriteLine(ex.Message);
                }
            }
            catch (Exception ex)
            {
                System.Console.WriteLine(ex.Message);
                return;
            }
            finally
            {
                System.Console.WriteLine("End. Please Hit Any Key.");
                System.Console.ReadLine();
            }
        }
    }
}</pre>
<p>Javaで書いたクライアントのコードをほとんどそのままコピペした感じです。ここまで同じに書けるとは思いませんでした。</p>
<p>実行結果はこんな感じ。</p>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-console.png"><img class="aligncenter size-medium wp-image-551" title="vs-console" src="http://ohnaka.jp/blog/wp-content/uploads/2011/09/vs-console-300x218.png" alt="" width="300" height="218" /></a></p>
<p><strong><span style="color: #0000ff;">うまくJavaのサーバと通信する事ができました！</span></strong>例外もきちんと飛んできています。</p>
<h2>C#版のバグ？</h2>
<h3>リクエストがタイムアウトする</h3>
<p>実は最初に試した時にTHttpClientのリクエストがタイムアウトしてエラーになってしまうという問題に悩まされました。色々調べてみたところ、次のバグ報告に行き着きました。</p>
<p><a href="https://issues.apache.org/jira/browse/THRIFT-1260">https://issues.apache.org/jira/browse/THRIFT-1260</a></p>
<blockquote><p>When calling the server more than 16000 times, an exception is being thrown &#8211; &#8220;An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full&#8221; its because the stream is not being closed.</p>
<p>streamがクローズされていない正で、サーバを16000回以上呼び出した時に「バッファ領域が不足しているか、キューがいっぱいでソケット上の操作が完了できません」という例外がスローされる。</p></blockquote>
<p>僕が遭遇したのはこの現象ではなかったのですが、streamがクローズされていないというのはかなり怪しい感じがします。試しこのページに添付されていたTHttpClient.csをダウンロードして、thrift-0.7.0.dllを作り直してみました。すると無事にエラーが起こらなくなりました。</p>
<p>この手のバグに簡単に遭遇したというところからして、ThriftはあまりC#上で使われていないのではないかという気がします。Thriftがサポートしているプログラミング言語は多岐に及びますが、枯れている言語と枯れていない言語がありそうで、注意が必要です。</p>
<h3>SSL接続がタイムアウトする</h3>
<p>試しに接続先を https://〜 にして、SSLでの接続を試みたのですが。見事に失敗しました。サーバに接続しようとしても何も応答が返ってこず、タイムアウトしてしまったのです。</p>
<p>原因を調べていたら以下のサイトが見つかりました。</p>
<p><a href="http://stackoverflow.com/questions/5653868/what-makes-this-https-webrequest-time-out-even-though-it-works-in-the-browser">http://stackoverflow.com/questions/5653868/what-makes-this-https-webrequest-time-out-even-though-it-works-in-the-browser</a></p>
<blockquote><p>Using Microsoft Network Monitor, I found that <code>HttpWebRequest</code> would get stuck at a stage where it&#8217;s supposed to send back a client key exchange. It simply didn&#8217;t. The server duly waited for it, but it never came.</p>
<p>Microsoft Network Monitorを使って調べたところ、HttpWebRequestがクライアント鍵交換を行なう段階でスタックする事を発見しました。単に何もしていないのです。サーバは正しくそれを待っているのですが、返事は来ません。</p></blockquote>
<p>この問題は、次の方法で回避できるそうです。</p>
<blockquote><p>What fixed it was forcing HttpWebRequest to use SSL3 instead of TLS (even though TLS is supposed to automatically turn into SSL3 if necessary)</p>
<p>HttpWebRequestがTLSではなくSSL3を使うように強制するとこの問題が直ります（TLSは本来必要があれば自動的にSSL3になるはずなのですが)</p></blockquote>
<p>この方も良く理由は分からないらしいのですが、TLSを使わずにSSL3を使う事を強制するとうまく接続できるようになるそうです。そのためには、接続前に以下のコードを実行すればOKです。</p>
<pre><code> ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3; </code></pre>
<p><span style="font-family: monospace;">これはどうやらグローバルな設定のようなので、どこかで一回やればOKなんだと思いますが、念のため、THttpClientのCreateRequestメソッドの頭に突っ込んでみました。</span></p>
<pre>        private HttpWebRequest CreateRequest()
        {
            // TLSを使わずに強制的にSSLを使うように指示。そうしないとタイムアウトしてしまう
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
            HttpWebRequest connection = (HttpWebRequest)WebRequest.Create(uri);</pre>
<p>うーむ。いろいろありますね。</p>
<h3>自己署名の証明書を持つサーバにSSL接続する場合</h3>
<p>Javaでもよくありますが、第三者に署名されていないいわゆる「オレオレ証明書」のサーバに接続する場合、「証明書が署名されてないぞ！」とエラーになってしまいます。僕はちゃんとした証明書を持っているのでこの問題にはあたりませんでしたが、もしこの問題にあたった方は以下の方法で回避できるそうです。</p>
<p><a href="http://stackoverflow.com/questions/560804/how-do-i-use-webrequest-to-access-an-ssl-encrypted-site-using-https">http://stackoverflow.com/questions/560804/how-do-i-use-webrequest-to-access-an-ssl-encrypted-site-using-https</a></p>
<p>やはり、ServicePointManagerに手を入れるようです。</p>
<p>ご参考まで。</p>
]]></content:encoded>
			<wfw:commentRss>http://ohnaka.jp/blog/2011/09/538/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apache ThriftとSpringを組み合わせて例外のマッピングを実現する</title>
		<link>http://ohnaka.jp/blog/2011/08/529</link>
		<comments>http://ohnaka.jp/blog/2011/08/529#comments</comments>
		<pubDate>Thu, 25 Aug 2011 08:11:29 +0000</pubDate>
		<dc:creator>くにちこ</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[技術]]></category>
		<category><![CDATA[Apach Thrift]]></category>
		<category><![CDATA[Spring]]></category>

		<guid isPermaLink="false">http://ohnaka.jp/blog/?p=529</guid>
		<description><![CDATA[最近Apache Thriftの調査をしているのですが、全体像がようやく見えてきました。ただ、実際にプロダクトに組み込もうとすると、「コード生成」というものをどのように扱うべきかちょっと悩んだりします。 プラットフォーム [...]]]></description>
			<content:encoded><![CDATA[<p>最近<a title="【まとめ】Apache Thrift調査" href="http://ohnaka.jp/blog/%e3%80%90%e3%81%be%e3%81%a8%e3%82%81%e3%80%91/%e3%80%90%e3%81%be%e3%81%a8%e3%82%81%e3%80%91apache-thrift%e8%aa%bf%e6%9f%bb">Apache Thriftの調査</a>をしているのですが、全体像がようやく見えてきました。ただ、実際にプロダクトに組み込もうとすると、「コード生成」というものをどのように扱うべきかちょっと悩んだりします。</p>
<p>プラットフォームを跨いでRPCを行なう場合、各プラットフォーム上で値クラスや例外を定義するのが煩雑になります。Apache Thriftの良いところはこれらのコードまでふくめて、各プラットフォームのRPC層のソースコードを自動生成してくれるところにあります。</p>
<p>ただ、<span style="color: #ff0000;"><strong>Thriftが生成したクラスをビジネスロジック層で使ってしまうのはいささか抵抗があります</strong></span>。<br />
<span id="more-529"></span></p>
<h2>Thriftが生成したクラスはどの範囲で使うべきか</h2>
<p><a title="Apache Thriftでクラス(struct)、例外(exception)を使ってみる" href="http://ohnaka.jp/blog/2011/08/518">過去の調査</a>で「CardService」というRPCを題材として用いてThriftを動かしてきました。CardServiceの例では、Thriftで  Cardクラスという値クラスを定義し、そのオブジェクトをクライアントとサーバの間でやりとりしています。また、サーバ上で発生した例外をクライアントにも伝わるように、TIllegalArgumentExceptionという例外を定義し、それを throwしてみるテストを行ないました。</p>
<p>しかし、このCardクラスと TIllegalArgumentException例外は、どのレイヤまで利用して良いのでしょうか？</p>
<ul>
<li>CardをDBに永続化する場合、永続化層に渡す引数として、Thriftが生成したCardクラスを渡しても良いものか？</li>
<li>ThriftによるRPC以外に、JAX-RSなどでRESTfulインターフェースも平行して提供したい場合、JAX-RS層でもThriftが生成したCardクラスを使っても良いか？</li>
<li>ビジネスロジック層で引数のエラーが発生した時に、TIllegalArgumentExceptionを throwしてもよいか？つまり、ビジネスロジック層のメソッドに throws TIllegalArgumentExceptionが現れても良いか？</li>
</ul>
<p>「レイヤ間の依存性を極力下げる」という考えからすれば、これらは<span style="color: #ff0000;"><strong>全てNG</strong></span>です。つまり、Thriftが自動生成してくれたクラスや例外は、あくまでRPC層のみで用い、他のレイヤに渡す時は別のオブジェクトに変換してやる必要がありそうです。</p>
<p>これは嬉しいような嬉しく無いような、、。</p>
<h2>それでもThriftはメリットがある</h2>
<p style="padding-left: 30px;"><span style="color: #0000ff;"><strong>「自動生成されたコードが限定的にしか使えないなんて、それってお自動生成した意味あるの？」</strong></span></p>
<p>という気持ちになってきますが、僕は「<strong><span style="color: #0000ff;">意味はある</span></strong>」と思っています。理由は２つあります。</p>
<p>まず<strong>一つ目の理由</strong>。例えば、ちょっとしたメンテナンスツールを作りたい場合などに、使い慣れた環境から簡単にRPCを呼び出せると嬉しい場合があります。このクライアントコードは半ば使い捨てのようなものなので、レイヤ間の依存性などをいちいち気にする必要がない場合が多いでしょう。このようなツールを作る場合に、値クラスや例外が自動生成されるのはとても嬉しいです。</p>
<p><strong>もう一つの理由</strong>は、「XMLやJSONをパースするよりは簡単にコードが書ける」というものです。自動生成された値クラスが無い場合はどうしていたかというと、サーバからXMLが帰ってきて、それをパースしてビジネスロジック層のオブジェクトに詰め込んでいたのです。つまり、Thriftが生成したCardクラスをTCard、ビジネスロジック層のCardクラスをBCardとすると、</p>
<p><strong>今まで：</strong></p>
<pre style="padding-left: 30px;">XML ---パース---&gt; BCard</pre>
<p><strong>Thrift：</strong></p>
<pre style="padding-left: 30px;">TCard ---コピー---&gt; BCard</pre>
<p>というように対比できます。<span style="color: #0000ff;">前者のコードより、後者のコードの方が圧倒的に簡単</span>です。うまくプロパティ名などを合わせれば、全自動でコピーを行なう事もできます。</p>
<p>実際、<span style="color: #0000ff;">僕はReflectionを使って TCardから BCardへプロパティをコピーするトランスレータを自作しました</span>が、とてもうまく動いています。</p>
<h2>例外はどうする？</h2>
<p>ThriftはRPCなので、例外は一方通行、つまりサーバ側で発生した例外がクライアントに渡る方向しかありません。メソッドの引数に問題があった時にArgumentExceptionを送出する、というシチュエーションを例にして図示してみます。クライアントにビジネスロジックがある場合も考えると、<strong><span style="color: #0000ff;">例外は次のように変換されるべき</span></strong>です。</p>
<pre>クライアントビジネスロジック層            クライアントRPC層                 サーバRPC層                  サーバビジネスロジック層
      BCArgumentException  &lt;=変換= TArgumentException &lt;==Thrift/HTTP== TArgumentException &lt;=変換=  BSArgumentException</pre>
<p>これをベタにやろうとすると、例えばサーバRPC層とサーバビジネスロジック層との間で次のようなコードを書く事になります。</p>
<pre class="brush:java">try {
    bService.someMethod(arg1, arg2);
} catch( BSArgumentException e) {
    throw new TArgumentException(e.getMessage);
}</pre>
<p class="brush:java">あー、、、。こういうコードを全てのメソッドの呼び出しに書くのは骨が折れます。</p>
<p class="brush:java">実は、こういった「<strong>全てのメソッドの呼び出しに◯◯する</strong>」というのはアスペクト指向プログラミング(AOP)という考え方を使うとうまく解決できます。Javaは言語仕様としてはメソッドの呼び出しをフックする仕組みを持っていませんが、Proxyクラスを使うと実現できます。また、Javaassistやcglibというツールを使う方法もあります。</p>
<p class="brush:java">今回僕はサーバサイドにSpringフレームワークを使っていたので、<span style="color: #0000ff;"><strong>SpringのAOP機能を使って例外を変換する</strong></span>方法を紹介します。</p>
<h2 class="brush:java">TServletの使い方を少し変えて、Springを利用する</h2>
<p class="brush:java">Thriftに付いてくる TServletというクラスを用いると、ThriftのRPCをJavaのサーブレットとして動かす事ができます。前回紹介した時は次のようなコードにしました。</p>
<pre class="brush:java">import jp.ohnaka.thrift.CardService;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServlet;

/**
 * CardServiceサービスへの呼び出しを受け付けるサーブレット
 */
public class CardServiceServlet extends TServlet {
    private static final long serialVersionUID = 1L;

    public CardServiceServlet() {
        // Binary Protocolを扱うサーブレットとして初期化する
        super(new CardService.Processor(new CardServiceHandler()), new TBinaryProtocol.Factory());
    }
}</pre>
<p class="brush:java">このコードでは、CardServiceの実装クラスであるCardServiceHandlerを直接 new していますが、Springから取得するように変更してみます。</p>
<pre class="brush:java">import jp.ohnaka.thrift.CardService;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServlet;

/**
 * CardServiceサービスへの呼び出しを受け付けるサーブレット
 */
public class CardServiceServlet extends TServlet {
    private static final long serialVersionUID = 1L;

    private CardService.Iface getHandler() {
        return (CardService.Iface) applicationContext.getBean("cardServiceHandler");
    }

    public CardServiceServlet() {
        // Binary Protocolを扱うサーブレットとして初期化する
        super(new CardService.Processor(getHandler()), new TBinaryProtocol.Factory());
    }
}</pre>
<p class="brush:java">applicationContextは SpringのApplicationContextのインスタンスです。このオブジェクトはSpringの初期化時に作ったものですが、環境によって取り方はいろいろあります。ひとまずどこかに保存しておいた者を取ってきたものと思ってください。</p>
<p class="brush:java">このようにする事で、new CardServiceHandler() となっていた部分が無くなり、Springによってインスタンスを作るようになります。</p>
<h2 class="brush:java">Springの設定をする</h2>
<p class="brush:java">springの設定ファイルには次のような記述を書きます。</p>
<pre class="brush:xml"> &lt;!-- CardServiceのハンドラ。例外変換の為に Porxyを使って ThriftExceptionMapperAdviceをinjectしている --&gt;
 &lt;bean id="cardServiceHandler" class="org.springframework.aop.framework.ProxyFactoryBean"&gt;
  &lt;property name="proxyInterfaces" value="jp.ohnaka.thrift.CardService$Iface"/&gt;
  &lt;property name="interceptorNames"&gt;
   &lt;list&gt;
    &lt;value&gt;thriftExceptionMapperAdvice&lt;/value&gt;
   &lt;/list&gt;
  &lt;/property&gt;
  &lt;property name="target"&gt;
   &lt;bean class="jp.ohnaka.thrift.CardServiceHandler"/&gt;
  &lt;/property&gt;
 &lt;/bean&gt;

 &lt;bean id="thriftExceptionMapperAdvice" class="jp.ohnaka.thrift.ThriftExceptionMapperAdvice"/&gt;</pre>
<p>通常のBeanであれば、</p>
<pre>&lt;bean id="cardServiceHandler" class="jp.ohnaka.thrift.CardServiceHandler"/&gt;</pre>
<p class="brush:xml">と書くだけなのですが、なんだか長ったらしくなっています。実は、Springの ProxyFactoryBeanというものを使って、CardServiceHandlerに<strong>ちょっとした仕掛けを仕込める</strong>ようにしています。これが<span style="color: #0000ff;"><strong>「アスペクト指向プログラミング」の考え方</strong></span>です。<br />
では、プロパティの意味を順に説明します。</p>
<h3>proxyInterfaces</h3>
<p>このBeanの持っているインターフェースを列挙します。今回のCardServiceHandlerは CardServiceクラスの内部インターフェース であるIfaceインターフェースを実装していますので、&#8221;jp.ohnaka.thrift.CardService$Iface&#8221; としています。なお、複数列挙する時は&lt;list&gt;を使えばいいみたいです。</p>
<h3>interceptorNames</h3>
<p>proxyInterfacesで定義したインターフェースの各メソッドを呼び出す時にどういう「仕掛け(Proxy)」を仕込むのかを定義しています。ここでは thriftExceptionMapperAdviceというものを指定します。これは後ろで定義しているBeanの idと対応しています。</p>
<h3>target</h3>
<p>この「仕掛け」をかます対象のBeanです。refで外部の Beanを参照しても良いですが、ここではその場で定義しています。定義している Beanは今までソースコード内で newしていた jp.ohnaka.thrift.CardServiceHandlerです。</p>
<h2 class="brush:xml">これで何が起こるの？</h2>
<p class="brush:xml">このようにして作られた cardServiceHandlerというIDの Beanは、CardService$Ifaceの各メソッドを呼び出す時に、thriftExceptionMapperAdviceを経由して呼び出されるようになります。この ThriftExceptionMapperAdviceは、次のようなクラスです。</p>
<pre class="brush:java">package jp.ohnaka.thrift;

import org.springframework.aop.ThrowsAdvice;

/**
 * サービス層で発生したIllegalArgumentExceptionを Thrift層の TIllegalArgumentExceptionに変換する。
 * SpringのAOPを使って、CardServiceHandlerの各メソッドにこの Adviceを injectしている。
 * @author ohnaka
 */
public class ThriftExceptionMapperAdvice implements ThrowsAdvice {
    public void afterThrowing(IllegalArgumentException e) throws Throwable {
        throw new TIllegalArgumentException(e.getMessage());
    }
}</pre>
<p>ThriftExceptionMapperAdviceは Springの ThrowsAdviceというインターフェースを実装したクラスです。ThrowsAdviceは「メソッドの呼び出しで例外が発生したときに処理を仕込みたい」という事をSpringに教える為のマーカインターフェースです。</p>
<p>ThrowsAdviceをマーカインターフェースをして実装した上で、afterThrowing()というメソッドを定義します。この例のように afterThrowing(IllegalArgumentException e) と定義すると、CardServiceHandlerで IllegalArgumentExceptionが発生した時に、呼び出し元に例外が渡る前にこのメソッドを呼び出してくれるようになります。</p>
<p>つまり、<strong><span style="color: #ff0000;">CardServiceHandlerの全てのメソッドの呼び出しに</span></strong>、</p>
<pre>try {
} catch(IllegalArgumentException e) {
   throw new TIllegalArgumentException(e.getMessage());
}</pre>
<p><span style="color: #ff0000;"><strong>というコードを付け足したのと同じ効果が得られるわけです</strong></span>。afterThrowing(HogeHogeException e) というのを定義すれば、別の例外 HogeHogeExceptionに対してフックを仕掛けることもできます。</p>
<p>ちなみに、afterThrowing()メソッドの中で、「どのメソッドが呼び出されたのか」を知りたい場合は次のような書き方もできます。</p>
<pre>public void afterThrowing(Method m, Object[] args, Object target, IllegalArgumentException ex)</pre>
<p>これで<strong>ThriftのRPC層とビジネスロジック層で例外を変換できるようになりました</strong>。めでたしめでたし。</p>
<h2>補足</h2>
<p>本文中では適当に飛ばしてしまった Springの ApplicationContextの取り方ですが、僕はこんな風にしています。</p>
<p>まず、web.xmlで Springの SpringContextLoaderListenerというリスナを定義します。そのリスナの後に、自作のContextListenerを定義します。</p>
<p>web.xml</p>
<pre class="brush:applescript"> &lt;listener&gt;
  &lt;listener-class&gt;org.jboss.resteasy.plugins.spring.SpringContextLoaderListener&lt;/listener-class&gt;
 &lt;/listener&gt;

 &lt;listener&gt;
  &lt;listener-class&gt;jp.ohnaka.web.MyContextListener&lt;/listener-class&gt;
 &lt;/listener&gt;</pre>
<p class="brush:applescript">順番は必ずSpringContextLoaderListenerが先にくるようにしてください。</p>
<p class="brush:applescript">MyContextListenerは次のようにします。</p>
<pre class="brush:java">public class MyContextListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent arg0) {
        WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(arg0.getServletContext());
        MyApplicationContext.getInstance().setApplicationContext(wac);
    }
}</pre>
<p>このようにすると、SpringContextLoaderListenerが先に起動するので、MyContextListenerの contextInitializedが呼ばれた時には、Springの初期化が終わっており、Servlet Contextに ApplicationContextがセットされています。そのApplicationContextは WebApplicationContextUtilsを使って取得できます。</p>
<p>WebApplicationContextUtilsのgetRequiredWebApplicationContextメソッドは、staticメソッドなのでどこからでも呼べるのですが、引数に ServletContextが必要なので、先ほどの CardServiceHandlerなどからは呼び出せません。そこで MyContextListenerの contextInitializedメソッドの中で呼び出しておき、自作のシングルトンクラス(MyAppliationContext)にセットしておいて、どこからでも取れるようにしています。</p>
<p>こうすると、CardServiceHandlerでは、</p>
<pre>MyApplicationContext.getInstance().getApplicationContext();</pre>
<p>というような感じで、AppliationContextを取得できるようになります。</p>
]]></content:encoded>
			<wfw:commentRss>http://ohnaka.jp/blog/2011/08/529/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apache Thriftでクラス(struct)、例外(exception)を使ってみる</title>
		<link>http://ohnaka.jp/blog/2011/08/518</link>
		<comments>http://ohnaka.jp/blog/2011/08/518#comments</comments>
		<pubDate>Wed, 17 Aug 2011 03:20:51 +0000</pubDate>
		<dc:creator>くにちこ</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[技術]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[exception]]></category>
		<category><![CDATA[struct]]></category>
		<category><![CDATA[Thrift]]></category>

		<guid isPermaLink="false">http://ohnaka.jp/blog/?p=518</guid>
		<description><![CDATA[前回までの記事で、Apache Thriftを使ったRPC呼び出しを試してきました。 ただ、前回のものだとXML-RPCなどの他の言語非依存のRPC技術と大差のない単なるメソッド呼び出しになってしまっていました。 Apa [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Apache ThriftのクライアントサイドをJavaで動かす" href="http://ohnaka.jp/blog/2011/08/511">前回までの記事</a>で、Apache Thriftを使ったRPC呼び出しを試してきました。</p>
<p>ただ、前回のものだとXML-RPCなどの他の言語非依存のRPC技術と大差のない単なるメソッド呼び出しになってしまっていました。</p>
<p>Apache Thriftの真骨頂は、intや stringといったプリミティブ型だけでなく、<span style="color: #ff0000;"><strong>構造体(クラス)や例外までもがプラットフォームを超えて共用できる</strong></span>というところにあります。</p>
<p>今回は構造体や例外も含めて、もう少し実際のサービスに近いレベルでテストをしてみたいと思います。</p>
<p><span id="more-518"></span></p>
<h2>Cardの管理を行なうサービスを定義する</h2>
<p><a title="【まとめ】JavaでRESTfulなWebServiceを作る" href="http://ohnaka.jp/blog/%e3%80%90%e3%81%be%e3%81%a8%e3%82%81%e3%80%91/java-restful-webservice">JAX-RSでRESTfulサービスを定義する記事</a>と同じく、名刺(Card)を追加したり一覧を取ったりする事のできるサービスを考えてみます。</p>
<p>メソッドとしては次の３つを考えました。</p>
<h3>CardService.thrift</h3>
<pre>service CardService
{
        void addCard(1: Card card);
        list&lt;Card&gt; getCards();
        Card getCardById(1: string id);
}</pre>
<p>addCard()はCardを追加するメソッド、getCards()は全てのCardを取得するメソッド、getCardByIdはIDを指定してCardを取得するメソッドです。ここで、前回までの記事では出てこなかった要素が２つあります。</p>
<p><span style="color: #000000;"><strong>１つ目</strong></span>は、list型です。Thriftでは複数の要素を扱う入れ物(Container)として、list, set, mapがデフォルトで用意されています。これらはJavaのGenerics、C++のTemplateと同じような表記で、型の指定が可能です。</p>
<p>詳細を<a href="http://wiki.apache.org/thrift/ThriftTypes">公式サイト</a>から引用します。</p>
<ul>
<li><strong><tt>list&lt;</tt><em>type</em></strong><strong><tt>&gt;</tt></strong></li>
<ul>
<li>順序の決まった要素のリストです。C++ではSTLのvectorクラス、JavaではArrayList、その他のスクリプト言語では標準の配列にマッピングされます。</li>
</ul>
<li><strong><tt>set&lt;</tt><em>type</em></strong><strong><tt>&gt;</tt></strong></li>
<ul>
<li>順序の決まっていない、ユニークな要素の集合です。C++ではSTLのsetクラス、JavaではHashSet、Pythonではsetクラスにマッピングされます。PHPはsetをサポートしないため、Listのように扱われます。</li>
</ul>
<li><strong><tt>map&lt;</tt><em>type1</em><tt>,</tt><em>type2</em></strong><strong><tt>&gt;</tt></strong></li>
<ul>
<li>厳密にユニークなキーとそれに対応する値のマップです。C++ではSTLのmap、JavaではHashMap,PHPでは連想配列、PythonやRubyでは dictionaryにマッピングされます。</li>
</ul>
</ul>
<p><strong>２つ目</strong>は引数や返り値の型として使っているCardというクラスです。これはThriftがデフォルトで持っている者ではありません。自分で定義する必要があります。CardService.thriftに以下の定義を追加しましょう。</p>
<h3>CardService.thrift</h3>
<pre>struct Card {
  1: string id,
  2: string name
}</pre>
<p>まさにC言語の構造体です。ですが、フィールドの先頭に1: や 2: という数字が付いています。これは<span style="color: #0000ff;"><strong>Field IDというもの</strong></span>で、フィールドを一意に指定するIDとして利用されます。<a href="http://wiki.apache.org/thrift/ThriftIDL">IDLの定義としてはオプション</a>なので、無くてもコンパイルはできるのですが、<strong>「下位互換性を保つ為には定義しておいた方が無難だよ」</strong>という<span style="color: #ff0000;">warning</span>が出ます。</p>
<p>たとえば、リリース後に nameというフィールドを firstname に変更したい場合があったとします。そのような場合でもフィールドIDを同じにしておけば古いクライアントともきちんと通信できるようになるのだろうとおもいます（試してません）。</p>
<p>外部に公開するAPIを考えると下位互換性の問題は避けて通れません。注意して設計するようにしましょう。</p>
<p>ちなみに、Cの構造体やJavaのクラスのように、フィールドの区切りをコンマ(,)の代わりにセミコロン(;)にする事もできます。<a href="http://wiki.apache.org/thrift/ThriftIDL">IDL上</a>はどちらでも良いようです。</p>
<h3>CardService.thrift (セミコロンバージョン)</h3>
<pre>struct Card {
  1: string id;
  2: string name;
}</pre>
<h2>パッケージの宣言を追加する</h2>
<p>このままThriftでコード生成をしても良いのですが、Javaのクラスがデフォルトパッケージの(package宣言が無い)クラスとして生成されてしまうのはちょっとかっこわるい感じがありました。</p>
<p>実は thriftファイルにはパッケージ名を指定する方法がちゃんとあります。この宣言を頭に足しておいてください。</p>
<h3>CardService.thrift</h3>
<pre>namespace java jp.ohnaka.thrift</pre>
<p>この宣言は、<strong>「Javaのコードを生成する時は jp.ohnaka.thriftパッケージにしてね」</strong>という意味になります。もちろんC++や他の言語でも個別に指定できます。</p>
<p>ただし、一つのthriftファイル内では namespace宣言は１つしかする事はできません。すなわちCardクラスとCardServiceクラスは必ず同じパッケージになってしまうという事です。まあ仕方ないんでしょうかね。</p>
<h2>CardServiceのサーバサイド実装</h2>
<p>それでは実際にCardServiceクラスを実装してみます。</p>
<p>詳細は以前説明したもの同じなので省略しますが、ビジネスロジックを実装するCardServiceHandlerと、Servlet化するためのCardServiceServletを実装すればOKです。</p>
<h3>CardServiceHandler.java</h3>
<pre class="brush:java">import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jp.ohnaka.thrift.Card;
import jp.ohnaka.thrift.CardService;

import org.apache.thrift.TException;

/**
 * CardServiceのサーバサイド実装
 */
public class CardServiceHandler implements CardService.Iface {

    static private Map&lt;String, Card&gt; cardDb_ = new HashMap&lt;String, Card&gt;();

    @Override
    public void addCard(Card card) {
        cardDb_.put(card.getId(), card);
    }

    @Override
    public List&lt;Card&gt; getCards() {
        return new ArrayList&lt;Card&gt;(cardDb_.values());
    }

    @Override
    public Card getCardById(String id) {
        return cardDb_.get(id);
    }

}</pre>
<p class="brush:java">DBの代わりにstaticな Map&lt;String,Card&gt;を用意して、そこにストアするようにしてあります。本当のサービスであればMyBatisなどを使ってDBにストアするようなイメージです。</p>
<h3>CardServiceServlet.java</h3>
<pre class="brush:java">import jp.ohnaka.thrift.CardService;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServlet;

/**
 * CardServiceサービスへの呼び出しを受け付けるサーブレット
 */
public class CardServiceServlet extends TServlet {
    private static final long serialVersionUID = 1L;

    public CardServiceServlet() {
        // Binary Protocolを扱うサーブレットとして初期化する
        super(new CardService.Processor(new CardServiceHandler()), new TBinaryProtocol.Factory());
    }
}</pre>
<p class="brush:java">前回の記事と同じで、TServletを継承して使用するプロトコルを明示するだけです。</p>
<h3>web.xml</h3>
<p>サーブレットを /card に配置するために、web.xmlは以下のようにしました。</p>
<pre class="brush:xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"&gt;
  &lt;display-name&gt;thrift-test-server&lt;/display-name&gt;

  &lt;servlet&gt;
  &lt;servlet-name&gt;CardServiceServlet&lt;/servlet-name&gt;
  &lt;servlet-class&gt;CardServiceServlet&lt;/servlet-class&gt;
  &lt;/servlet&gt;

  &lt;servlet-mapping&gt;
  &lt;servlet-name&gt;CardServiceServlet&lt;/servlet-name&gt;
  &lt;url-pattern&gt;/card&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;
&lt;/web-app&gt;</pre>
<p class="brush:xml">これで完成です。</p>
<h2 class="brush:xml">明示的に例外を投げるようにする</h2>
<p>CardServiceHandlerの実装では引数のチェックなどを一切していません。addCard()に nullが渡されたり、Cardオブジェクトのidがnullだったりした場合に、内部でRuntimeExceptionが発生してしまいます。NullPointerExceptionなどのRuntimeExceptionが発生すると、HTTPの<span style="color: #ff0000;"><strong>500 Internal Server Error</strong></span>となってクライアント側に問題が発生した事が伝えられます。</p>
<p>そういう仕様でもいいのですが、クライアントからするといったいなぜ<strong><span style="color: #ff0000;">500 Internal Server Error</span></strong>になったのか原因を特定する事ができず困ってしまいます。</p>
<p>そこで、TIllegalArgumentExceptionという例外を定義して、明示的に例外を投げるようにしてみます。Javaであればjava.lang.IllegalArgumentExceptionを投げるのが一般的ですが、他の言語で理解できない例外なので、<strong><span style="color: #ff0000;">例外もきちんと Thrift上で定義してあげる必要があります</span></strong>。</p>
<p>ちょっと細切れになってきたので、CardService.thriftの全体を示します。</p>
<h3>CardService.thrift</h3>
<pre>#!/usr/bin/thrift

namespace java jp.ohnaka.thrift

struct Card {
  1: string id;
  2: string name;
}

exception TIllegalArgumentException
{
    1: string message
}

service CardService
{
        void addCard(1: Card card) throws (1:TIllegalArgumentException e);
        list&lt;Card&gt; getCards();
        Card getCardById(1: string id) throws (1:TIllegalArgumentException e);
}</pre>
<p>exception TIllegalArgumentExceptionというところが例外を定義しているところです。文法は構造体の定義と同じで、<strong>structの部分を exceptionに変えただけ</strong>です。</p>
<p>このように例外を定義すると、サービスのメソッドにthrows節を書けるようになります。throwsの意味はJavaと同じですが、1: というフィールドIDと例外変数の名前として&#8221;e&#8221;という名前を明示する必要があるところがちょっと変わっています。詳細は未調査ですが、これも後方互換性のためでしょうか。</p>
<p>例外を追加したので、CardServiceHandlerも以下のように修正します。</p>
<h3>CardServiceHandler.java</h3>
<pre class="brush:java">import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jp.ohnaka.thrift.Card;
import jp.ohnaka.thrift.CardService;
import jp.ohnaka.thrift.TIllegalArgumentException;

/**
 * CardServiceのサーバサイド実装
 */
public class CardServiceHandler implements CardService.Iface {

    static private Map&lt;String, Card&gt; cardDb_ = new HashMap&lt;String, Card&gt;();

    @Override
    public void addCard(Card card) throws TIllegalArgumentException {
        if (card == null) {
            // クライアントサイドに飛ばす例外にはjava.lang.IllegalArgumentExceptionは使えない
            // ので、独自に定義した例外を利用する
            throw new TIllegalArgumentException("Argumnent shouldn't be null");
        }
        if (card.getId() == null) {
            // クライアントサイドに飛ばす例外にはjava.lang.IllegalArgumentExceptionは使えない
            // ので、独自に定義した例外を利用する
            throw new TIllegalArgumentException("Card ID shouldn't be null");
        }
        cardDb_.put(card.getId(), card);
    }

    @Override
    public List&lt;Card&gt; getCards() {
        return new ArrayList&lt;Card&gt;(cardDb_.values());
    }

    @Override
    public Card getCardById(String id) throws TIllegalArgumentException {
        if (id == null) {
            throw new TIllegalArgumentException("Argumnent shouldn't be null");
        }
        return cardDb_.get(id);
    }

}</pre>
<h2 class="brush:java">クライアントサイドを実装する</h2>
<p>サーバサイドの準備ができたので、これを呼び出すクライアントサイドを実装してみます。</p>
<h3>Client.java</h3>
<pre class="brush:java">import java.util.List;

import jp.ohnaka.thrift.Card;
import jp.ohnaka.thrift.CardService;

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.THttpClient;

public class Client {

    public static void main(String[] args) throws Exception {
        // 接続先のURLを指定
        THttpClient transport = new THttpClient("http://localhost:8080/thrift-test-server/card");
        // バイナリプロトコルを使用（サーバ側と合わせる）
        TProtocol protocol = new TBinaryProtocol(transport);
        // クライアントスタブを作成
        CardService.Client client = new CardService.Client(protocol);

        try {
            transport.open();

            // サーバ側のaddCardメソッドを呼び出す
            Card card1 = new Card();
            card1.setId("1000");
            card1.setName("ohnaka");
            client.addCard(card1);

            // もう一個カードを作ってみる
            Card card2 = new Card();
            card2.setId("1001");
            card2.setName("kuni");
            client.addCard(card2);

            // サーバ側のgetCardsメソッドを呼び出す
            List&lt;Card&gt; cards = client.getCards();
            System.out.println("Num of cards: " + cards.size());

            // サーバ側のgetCardメソッドを呼び出す
            Card retrievedCard = client.getCardById("1000");
            System.out.println("Retrieved card id   = " + retrievedCard.getId());
            System.out.println("Retrieved card name = " + retrievedCard.getName());

            // 不正な引数を渡してみる
            try {
                client.addCard(null);
            } catch (Throwable t) {
                // どんな例外が飛ぶか調べる
                t.printStackTrace();
            }
            // IDが null なCardを渡してみる
            try {
                client.addCard(new Card());
            } catch (Throwable t) {
                // どんな例外が飛ぶか調べる
                t.printStackTrace();
            }
        } catch (TException te) {
            te.printStackTrace();
        }
    }

}</pre>
<h2 class="brush:java">実行結果</h2>
<p>クライアントコードを実行した結果です。</p>
<pre>Num of cards: 2
Retrieved card id   = 1000
Retrieved card name = ohnaka
TIllegalArgumentException(<span style="color: #ff0000;">message:Argumnent shouldn't be null</span>)
	at jp.ohnaka.thrift.CardService$addCard_result.read(CardService.java:972)
	at jp.ohnaka.thrift.CardService$Client.recv_addCard(CardService.java:110)
	at jp.ohnaka.thrift.CardService$Client.addCard(CardService.java:85)
	at Client.main(Client.java:47)
TIllegalArgumentException(<span style="color: #ff0000;">message:Card ID shouldn't be null</span>)
	at jp.ohnaka.thrift.CardService$addCard_result.read(CardService.java:972)
	at jp.ohnaka.thrift.CardService$Client.recv_addCard(CardService.java:110)
	at jp.ohnaka.thrift.CardService$Client.addCard(CardService.java:85)
	at Client.main(Client.java:54)</pre>
<p>カードを２つ作ったので、Num of cardが 2と帰ってきています。また、IDを指定して正しくCardオブジェクトを取得する事ができています。</p>
<p>addCard()メソッドに不正な引数をあたえた場合も、TILlegalArgumentExceptionがスローされている事がわかります。エラーになった理由がメッセージに含まれていて理由もよくわかりますね。</p>
<h2>気になるポイント</h2>
<p>なかなか強力ですね。いままでRPC上に構造体を流すのに苦労していたのが嘘のようです。Apache Thriftの強力なコード生成能力は構造体や例外を定義してこそと言えるかもしれません。</p>
<p>ただ、ちょっと気になる記述を公式サイト(<a href="http://wiki.apache.org/thrift/ThriftTypes">http://wiki.apache.org/thrift/ThriftTypes</a>)で見つけてしまいました。</p>
<p>&nbsp;</p>
<blockquote>
<h1 id="Structs">Structs</h1>
<p>Thrift structs define a common object &#8212; they are essentially equivalent to classes in OOP languages, <span style="color: #ff0000;">but without inheritance.</span> A struct has a set of strongly typed fields, each with a unique name identifier. Fields may have various annotations (numeric field IDs, optional default values, etc.) that are described in the <a href="http://wiki.apache.org/thrift/ThriftIDL">ThriftIDL</a>.</p></blockquote>
<p>&#8221; but without inheritance&#8221;の部分です。継承が利用できないという事のようですが、これが本当だとすると、Cardクラスを継承したJapaneseCard, EnglishCardなどを作って addCard()メソッドで統一的追加処理を扱う事ができません。</p>
<p>もう少し調査が必要なようです。</p>
]]></content:encoded>
			<wfw:commentRss>http://ohnaka.jp/blog/2011/08/518/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apache ThriftのクライアントサイドをJavaで動かす</title>
		<link>http://ohnaka.jp/blog/2011/08/511</link>
		<comments>http://ohnaka.jp/blog/2011/08/511#comments</comments>
		<pubDate>Thu, 11 Aug 2011 05:49:22 +0000</pubDate>
		<dc:creator>くにちこ</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[技術]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[Thrift]]></category>

		<guid isPermaLink="false">http://ohnaka.jp/blog/?p=511</guid>
		<description><![CDATA[前回と前々回の記事で、Apache ThriftでRPCを定義し、コードを生成してサーバを立てるところまで行きました。 今回はクライアントサイドです。こちらもJavaで動かしてみたいと思います。 ThriftでJavaの [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Apache ThriftのサーバサイドをJava Servletとして動かす" href="http://ohnaka.jp/blog/2011/08/503">前回</a>と<a title="Apache Thrift を MacOS X 上で試す" href="http://ohnaka.jp/blog/2011/08/493">前々回</a>の記事で、Apache ThriftでRPCを定義し、コードを生成してサーバを立てるところまで行きました。</p>
<p>今回はクライアントサイドです。こちらもJavaで動かしてみたいと思います。</p>
<p><span id="more-511"></span>ThriftでJavaのコードを生成するところは、前回でも前々回でも解説したので、今回は省略します。</p>
<pre># thrift --gen java TinyCalc.thrift</pre>
<p>ってやって、TinyCalc.javaが生成できているつもりで読んでください。</p>
<h2>Eclipseのプロジェクトを作る</h2>
<p>今回もEclipseのプロジェクトとして作ります。通常のJavaアプリケーションプロジェクトを作ってください。プロジェクト名は<strong>thrift-test-client</strong>としました。</p>
<p>毎度ですが僕はMavenを使っているので、以下のpom.xmlを用意しました。</p>
<h3>pom.xml</h3>
<pre class="brush:xml">&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
 &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

 &lt;groupId&gt;jp.ohnaka.thrift&lt;/groupId&gt;
 &lt;version&gt;1.0.0-SNAPSHOT&lt;/version&gt;
 &lt;artifactId&gt;thrift-test-client&lt;/artifactId&gt;
 &lt;name&gt;Apache Thrift test Server&lt;/name&gt;

 &lt;dependencies&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;org.apache.thrift&lt;/groupId&gt;
   &lt;artifactId&gt;libthrift&lt;/artifactId&gt;
   &lt;version&gt;0.6.1&lt;/version&gt;
  &lt;/dependency&gt;

  &lt;!-- Logging --&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
   &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
   &lt;version&gt;1.6.1&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
   &lt;artifactId&gt;logback-core&lt;/artifactId&gt;
   &lt;version&gt;0.9.16&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
   &lt;artifactId&gt;logback-classic&lt;/artifactId&gt;
   &lt;version&gt;0.9.16&lt;/version&gt;
  &lt;/dependency&gt;

  &lt;!-- Testing --&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;junit&lt;/groupId&gt;
   &lt;artifactId&gt;junit&lt;/artifactId&gt;
   &lt;version&gt;4.8.1&lt;/version&gt;
   &lt;scope&gt;test&lt;/scope&gt;
  &lt;/dependency&gt;
 &lt;/dependencies&gt;

 &lt;build&gt;
  &lt;plugins&gt;
     &lt;plugin&gt;
    &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
    &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
    &lt;version&gt;2.1&lt;/version&gt;
    &lt;configuration&gt;
     &lt;encoding&gt;UTF-8&lt;/encoding&gt;
     &lt;source&gt;1.6&lt;/source&gt;
     &lt;target&gt;1.6&lt;/target&gt;
    &lt;/configuration&gt;
   &lt;/plugin&gt;
  &lt;/plugins&gt;
 &lt;/build&gt;
&lt;/project&gt;</pre>
<p>Thrift本体のjarと、slf4jのライブラリなどを足しています。依存ライブラリとして httpclientなども入ります。最終的にMavenが解決したライブラリは以下のようになりました。</p>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/08/thrift-client.png"><img class="aligncenter size-medium wp-image-512" title="thrift-client" src="http://ohnaka.jp/blog/wp-content/uploads/2011/08/thrift-client-237x300.png" alt="" width="237" height="300" /></a>プロジェクトが生成できたら、Thriftが生成した TinyCalc.javaをソースフォルダにコピーしてください。</p>
<h2>クライアントサイドからサービスを呼び出す</h2>
<p>それでは早速、クライアントからサーバサイドのサービスを呼び出すコードを書いてみます。</p>
<h3>Client.java</h3>
<pre class="brush:java">import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.THttpClient;

public class Client {

    public static void main(String[] args) throws Exception {
        // 接続先のURLを指定
        THttpClient httpClient = new THttpClient("http://localhost:8080/thrift-test-server/calc");
        // バイナリプロトコルを使用（サーバ側と合わせる）
        TProtocol protocol = new TBinaryProtocol(httpClient);
        // クライアントスタブを作成
        TinyCalc.Client client = new TinyCalc.Client(protocol);

        try {
            httpClient.open();

            // サーバ側のsumメソッドを呼び出す
            double sum = client.sum(1.0, 2.2);
            System.out.println("1.0 + 2.2 =" + sum);
            // サーバ側のsubtractメソッドを呼び出す
            double sub = client.subtract(5.3, 3.4);
            System.out.println("5.3 - 3.4 =" + sub);
            httpClient.close();

        } catch (TException e) {
            e.printStackTrace();
        }
    }

}</pre>
<p class="brush:java">まずトランスポート層として、THttpClientを作ります。その際、接続先URLとして、サーバサイドで作成したサーブレットのURLを指定する必要があります。今回は http://localhost:8080/thrift-test-server/calc となります。</p>
<p class="brush:java">次に、THttpClientのオブジェクトをプロトコルハンドラに渡します。今回は<span style="color: #ff0000;"><strong>サーバサイドをTBinaryProtocolにしたので、クライアント側もTBinaryProtocolを使って作成</strong></span>しています。</p>
<p class="brush:java">最後にThriftが生成した TinyCalc.Clientをnewします。</p>
<p class="brush:java">生成されたTinyCalc.ClientオブジェクトはTinyCalc.Ifaceを実装したクラスなので、サーバサイドで定義したTinyCalcHandlerと同じ sum()メソッドと subtract()メソッドを持っています。もうお分かりだと思いますが、<strong><span style="color: #0000ff;">クライアント側のsum()メソッドを呼び出すと、サーバ側のsum()メソッドが呼び出されるわけ</span></strong>です。</p>
<p class="brush:java">なお、実際に呼び出す為には、THttpClientのopen()メソッドを呼んでおき、必要がなくなったらclose()する必要があります。</p>
<h2 class="brush:java">実行結果</h2>
<p class="brush:java">Eclipseの実行メニューから Clinet.javaをJavaアプリケーションとして起動してください。</p>
<p class="brush:java"><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/08/thrift-client-output.png"><img class="aligncenter size-medium wp-image-513" title="thrift-client-output" src="http://ohnaka.jp/blog/wp-content/uploads/2011/08/thrift-client-output-300x264.png" alt="" width="300" height="264" /></a>コンソールに<strong><span style="color: #ff0000;">足し算と引き算の結果が表示されました！</span></strong></p>
<p class="brush:java">めでたしめでたし。</p>
<h2 class="brush:java">感想</h2>
<p class="brush:java">動かしてみた感想ですが、導入にビルドが必要だったり、ちょっと敷居が高いかなという印象はあります。しかし、今後、さまざまな有名サービスが、「うちのAPIはこれです」といって Thriftの定義ファイルを公開するのがトレンドになるかもしれません。</p>
<p class="brush:java">もしそういう時代になったら、「<span style="color: #0000ff;"><strong>エンジニアが手元に Thriftをインストールしておくのは常識</strong></span>」と言われるようになるかもしれませんね。</p>
<p class="brush:java">僕は今までRPCを作る時は、XML-RPCを使う事が多かったです。XML-RPCもプラットフォーム非依存なRPCなのですが、実際にはXML−RPCの拡張モードを使わないと、オブジェクトや例外の伝送ができません。僕はクライアントもサーバもJavaだったということもあって、 拡張モードを使ってしまっていたのですが、これだと他の言語から呼び出すのは難しくなります。</p>
<p class="brush:java">Thriftではプリミティブ型だけでなく、構造体も定義できるようなので、互換性を失わずにXML-RPCの拡張モードのような事ができます。また、コード生成のサポートがあるので、構造体に対応した値クラスの定義などを言語ごとにいちいち作る必要もありません。</p>
<p class="brush:java">次回はこの構造体のあたりや例外の扱いを試してみたいと思います。</p>
]]></content:encoded>
			<wfw:commentRss>http://ohnaka.jp/blog/2011/08/511/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apache ThriftのサーバサイドをJava Servletとして動かす</title>
		<link>http://ohnaka.jp/blog/2011/08/503</link>
		<comments>http://ohnaka.jp/blog/2011/08/503#comments</comments>
		<pubDate>Thu, 11 Aug 2011 02:57:28 +0000</pubDate>
		<dc:creator>くにちこ</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[技術]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[Thrift]]></category>
		<category><![CDATA[Tomcat]]></category>

		<guid isPermaLink="false">http://ohnaka.jp/blog/?p=503</guid>
		<description><![CDATA[前回の記事で、Apache  Thriftを使ってRPCコードを自動生成する方法を解説しました。 今回は実際に生成されたコードを動かしてみたいと思います。Thriftを使えばいろんな言語のコードが生成できるのですが、まず [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Apache Thrift を MacOS X 上で試す" href="http://ohnaka.jp/blog/2011/08/493">前回の記事</a>で、Apache  Thriftを使ってRPCコードを自動生成する方法を解説しました。</p>
<p>今回は実際に生成されたコードを動かしてみたいと思います。Thriftを使えばいろんな言語のコードが生成できるのですが、まずは僕のメイン言語であるJavaのコードを生成して、サーバとして動かしてみたいと思います。</p>
<p><span id="more-503"></span>ちなみにThriftを解説したページを探すと、JavaサーバとしてTomcatなどのサーブレットコンテナを使わず、<strong>Thriftが用意しているTThreadPoolServerクラスなどを使ってスタンドアロンアプリとして動かす</strong>方法が多く見受けられます。</p>
<p>ちょっとしたテストには良いと思うのですが、本番サービスで使うには運用面などで不安があります。SSLにしたい場合はどうすればいいんでしょう？証明書は？できるのかもしれませんが、蓄積した運用ノウハウが行かせません。</p>
<p>それに、専用のポートをThriftのサーバで占有されるのも困ります。ロードバランサなども個別に設定する必要が出てきます。</p>
<p>本記事では、<span style="color: #0000ff;"><strong>ThriftのサーバサイドコードをServletとして動かす方法を解説します</strong></span>。</p>
<h2>Javaのコードを生成する</h2>
<p>前回の記事の最後の部分ですが、おさらいです。</p>
<p>まずは、Thriftの定義ファイルを書きます。前回同様、木村俊也氏のブログ記事「<a href="http://blog.broomie.net/index.cgi?id=36">Thriftが便利すぎる</a>」からお借りします。</p>
<h3>TinyCalc.thrift</h3>
<pre>#!/usr/bin/thrift

service TinyCalc
{
        double sum(1: double param1, 2: double param2)
        double subtract(1:double param1, 2:double param2)
}</pre>
<p>メソッド名を見てお分かりの通り、２つの実数の「加算」と「減算」をするサービスを定義しています。</p>
<p>ではThriftでJavaのコードを生成します。</p>
<pre># thrift --gen java TinyCalc.thrift</pre>
<p>上記コマンドを実行すると、gen-javaというサブディレクトリが生成され、中にTinyCalc.javaというファイルが生成されます。</p>
<h2>Eclipseのプロジェクトを作る</h2>
<p>たぶんみなさんEclipseを使っていると思うので、Eclipse上で動かしてみましょう。まずは、Dynamic Web Projectを生成します。プロジェクト名は thrift-test-serverとしました。</p>
<p>僕はEclipseのプロジェクトもMavenで管理するのが好きになってしまったので、次のようなpom.xmlをプロジェクトのルートに置き、Dependency ManagementをONにします。</p>
<h3>pom.xml</h3>
<pre class="brush:xml">&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
 &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

  &lt;groupId&gt;jp.ohnaka.thrift&lt;/groupId&gt;
 &lt;version&gt;1.0.0-SNAPSHOT&lt;/version&gt;
 &lt;artifactId&gt;thrift-test-server&lt;/artifactId&gt;
 &lt;packaging&gt;war&lt;/packaging&gt;
 &lt;name&gt;Apache Thrift test Server&lt;/name&gt;

 &lt;dependencies&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;org.apache.thrift&lt;/groupId&gt;
   &lt;artifactId&gt;libthrift&lt;/artifactId&gt;
   &lt;version&gt;0.6.1&lt;/version&gt;
  &lt;/dependency&gt;

  &lt;!-- Servlet --&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
   &lt;artifactId&gt;servlet-api&lt;/artifactId&gt;
   &lt;version&gt;2.5&lt;/version&gt;
  &lt;/dependency&gt;

  &lt;!-- Logging --&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
   &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
   &lt;version&gt;1.6.1&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
   &lt;artifactId&gt;logback-core&lt;/artifactId&gt;
   &lt;version&gt;0.9.16&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
   &lt;artifactId&gt;logback-classic&lt;/artifactId&gt;
   &lt;version&gt;0.9.16&lt;/version&gt;
  &lt;/dependency&gt;

  &lt;!-- Testing --&gt;
  &lt;dependency&gt;
   &lt;groupId&gt;junit&lt;/groupId&gt;
   &lt;artifactId&gt;junit&lt;/artifactId&gt;
   &lt;version&gt;4.8.1&lt;/version&gt;
   &lt;scope&gt;test&lt;/scope&gt;
  &lt;/dependency&gt;
 &lt;/dependencies&gt;

 &lt;build&gt;
  &lt;plugins&gt;
     &lt;plugin&gt;
    &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
    &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
    &lt;version&gt;2.1&lt;/version&gt;
    &lt;configuration&gt;
     &lt;encoding&gt;UTF-8&lt;/encoding&gt;
     &lt;source&gt;1.6&lt;/source&gt;
     &lt;target&gt;1.6&lt;/target&gt;
    &lt;/configuration&gt;
   &lt;/plugin&gt;
   &lt;plugin&gt;
    &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
    &lt;artifactId&gt;maven-war-plugin&lt;/artifactId&gt;
   &lt;/plugin&gt;
  &lt;/plugins&gt;
 &lt;/build&gt;
&lt;/project&gt;</pre>
<p>ようは Thrift本体のjarと、Thriftによって生成されたコードが使用している slf4jのライブラリを追加しているだけです。Mavenを使っていない人はそれらの jarを手動でプロジェクトに追加してください。</p>
<p>プロジェクトの設定が終わったら、生成されたTinyCalc.javaをソースフォルダにいれます。生成されたコードはディフォルトパッケージに所属しているので、<strong>必要があればリファクタリング</strong>しましょう。今回はそのままにしておきます。</p>
<p><span class="Apple-style-span" style="font-size: 20px; font-weight: bold;">サーバサイドの実装を記述する</span></p>
<p>Apache Thriftがコードを自動生成してくれるといっても、あくまでインターフェースや通信制御の部分であって、<span style="color: #0000ff;"><strong>実際の処理は自分で書く</strong></span>必要があります。</p>
<p>Thriftが生成してくれたTinyCalcというJavaクラスの中には、TinyCalc.Ifaceという内部インターフェースが定義されています。サーバサイドの処理は、このインターフェースを実装したクラスに書きます。</p>
<p>ここでは、TinyCalcHandlerというクラスにします。</p>
<h3>TinyCalcHandler.java</h3>
<pre class="brush:java">import org.apache.thrift.TException;

/**
 * TinyCalcのサーバサイド実装
 */
public class TinyCalcHandler implements TinyCalc.Iface {

    /**
     * 2つの引数を足してその値を返します。
     */
    @Override
    public double sum(double param1, double param2) throws TException {
        return param1 + param2;
    }

    /**
     * 1つ目の引数から2つ目の引数を引いてその値を返します。
     */
    @Override
    public double subtract(double param1, double param2) throws TException {
        return param1 - param2;
    }
}</pre>
<p class="brush:java"><span class="Apple-style-span" style="font-size: 15px; font-weight: bold;">TinyCalcServlet.java</span></p>
<p>次に、このサービスへの呼び出しを受け付けるサーブレットを用意します。<span style="color: #0000ff;"><strong>最新のThriftにはTServletというクラスが用意されていて</strong></span>、それを継承すると簡単にサーブレットを作る事ができます。</p>
<pre class="brush:java">import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServlet;

/**
 * TinyCalcサービスへの呼び出しを受け付けるサーブレット
 */
public class TinyCalcServlet extends TServlet {
    private static final long serialVersionUID = 1L;

    public TinyCalcServlet() {
        // Binary Protocolを扱うサーブレットとして初期化する
        super(new TinyCalc.Processor(new TinyCalcHandler()), new TBinaryProtocol.Factory());
    }
}</pre>
<p>TServletを継承したら、上記のようにデフォルトコンストラクタを実装し、TServletのコンストラクタを呼び出します。</p>
<p><strong>super()の第一引数</strong>には、サービスの実処理を渡します。つまり先ほど記述したTinyCalcHanderなのですが、直接 TinyCalcHandlerを渡すのではなく、Processorでラップして渡します。ProcessorクラスもThriftによって生成された TinyCalc.Processorクラスを用います。</p>
<p><strong>super()の第二引数</strong>には、プロトコルファクトリを渡します。これによってThriftのクライアントとの通信をどういうプロトコルで行なうかが決まります。プロトコルと言ってもHTTPとかFTPとかではなく、HTTPの上にどういうデータを流すか、という意味です。</p>
<p>Thriftが用意しているプロトコルには以下のようなものがあります。(<a href="http://en.wikipedia.org/wiki/Apache_Thrift">Wikipedia</a>より引用)</p>
<ul>
<li>TBinaryProtocol</li>
<ul>
<li>A straight-forward binary format encoding numeric values as binary. It is faster than the text protocol but more difficult to debug.</li>
<li>数値をバイナリフォーマットで直接的に表現するプロトコル。数値をテキストで表現するプロトコルよりも高速ですが、デバッグが難しくなります。</li>
</ul>
<li>TCompactProtocol</li>
<ul>
<li>Very efficient, dense encoding of data.</li>
<li>とても効率的に、濃密にデータをエンコードするプロトコル。</li>
</ul>
<li>TDebugProtocol</li>
<ul>
<li>Uses a human-readable text format to aid in debugging.</li>
<li>デバッグのしやすさのために、ヒューマンリーダブル(人間が読みやすい)なテキストフォーマットを用いるプロトコル。</li>
</ul>
<li>TDenseProtocol</li>
<ul>
<li>Similar to TCompactProtocol, striping off the meta information from what is transmitted.</li>
<li>TCompactプロトコルにしていますが、転送データからメタ情報を除去したものです。</li>
</ul>
<li>TJSONProtocol</li>
<ul>
<li>Uses JSON for encoding of data.</li>
<li>データのエンコーディングにJSONを用いるプロトコル</li>
</ul>
<li>TSimpleJSONProtocol</li>
<ul>
<li>A write-only protocol using JSON. Suitable for parsing by scripting languages.</li>
<li>スクリプト言語でパースするのに適した、JSONを用いた書き込み専用のプロトコル。</li>
</ul>
</ul>
<p><span style="color: #0000ff;"><strong>今回は最初のTBinaryProtocolを用いました</strong></span>が、別のものを用いても大丈夫です。ただし、クライアント側とプロトコルを合わせるのを忘れずに。</p>
<h3>web.xml</h3>
<p>サーブレットの 準備ができたら、実際に配備してみましょう。今回は /calcというパスに配備してみました。</p>
<p>web.xmlはこんな感じになります。</p>
<pre class="brush:xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"&gt;
  &lt;display-name&gt;thrift-test&lt;/display-name&gt;

  &lt;servlet&gt;
  &lt;servlet-name&gt;TinyCalcServlet&lt;/servlet-name&gt;
  &lt;servlet-class&gt;TinyCalcServlet&lt;/servlet-class&gt;
  &lt;/servlet&gt;

  &lt;servlet-mapping&gt;
  &lt;servlet-name&gt;TinyCalcServlet&lt;/servlet-name&gt;
  &lt;url-pattern&gt;/calc&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;
&lt;/web-app&gt;</pre>
<h2 class="brush:xml">配備して実行する</h2>
<p>念のため、EclipseのProject Explorerのスクリーンショットを載せておきます。</p>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/08/thrift-server.png"><img class="aligncenter size-medium wp-image-506" title="thrift-server" src="http://ohnaka.jp/blog/wp-content/uploads/2011/08/thrift-server-183x300.png" alt="" width="183" height="300" /></a></p>
<p>すべてのファイルが準備できたら、Tomcatなどのサーブレットコンテナに配備して起動してみて下さい。エラーが出なければひとまずOKです。</p>
<p>次のエントリで、実際にクライアントから繋いでみましょう。</p>
]]></content:encoded>
			<wfw:commentRss>http://ohnaka.jp/blog/2011/08/503/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apache Thrift を MacOS X 上で試す</title>
		<link>http://ohnaka.jp/blog/2011/08/493</link>
		<comments>http://ohnaka.jp/blog/2011/08/493#comments</comments>
		<pubDate>Thu, 11 Aug 2011 01:27:16 +0000</pubDate>
		<dc:creator>くにちこ</dc:creator>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[boost]]></category>
		<category><![CDATA[MacOS X]]></category>
		<category><![CDATA[Thrift]]></category>
		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://ohnaka.jp/blog/?p=493</guid>
		<description><![CDATA[「あの超大規模サイトであるFacebookがAPI公開の為に作った」というキャッチーなフレーズにひっかかって、Apache Thriftというものを試してみました。EvernoteのAPIもThriftらしいです。 いっ [...]]]></description>
			<content:encoded><![CDATA[<p>「あの超大規模サイトであるFacebookがAPI公開の為に作った」というキャッチーなフレーズにひっかかって、Apache Thriftというものを試してみました。EvernoteのAPIもThriftらしいです。</p>
<p>いったいどんな事ができる技術なんでしょうね？</p>
<p><strong>「習うより慣れろ」</strong></p>
<p>ということで、とにかく動かして雰囲気を見てみましょう。</p>
<p><span id="more-493"></span></p>
<p>僕もちょっと触ってみただけですが、情報が分散していてわかりにくかったので、自身のメモを兼ねて、動かすまでの手順を書いてみたいと思います。</p>
<h2><img title="もっと読む..." src="http://ohnaka.jp/blog/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif" alt="" />Apache Thriftってなに？</h2>
<p>Thrift自体はいろいろな方がその特長を書かれているのでこちらをどうぞ。</p>
<ul>
<li><a href="http://ja.wikipedia.org/wiki/Thrift_(プロトコル)">Wikipedia「Thrift (プロトコル)</a>」</li>
<li><a href="http://blog.broomie.net/index.cgi?id=36">Thriftが便利すぎる</a></li>
<li><a href="http://www.atmarkit.co.jp/news/200704/03/thrift.html">@IT 「巨大SNSを支える多言語混在RPC開発フレームワーク“<em>Thrift</em>” 」</a></li>
</ul>
<p>簡単に言えば、</p>
<p>「Java, C++, Python, Ruby&#8230;などなど、さまざまな言語で相互にサービスを呼び合う事のできるRPCフレームワーク」</p>
<p>って感じでしょうか？</p>
<h2>Thriftが生まれた背景</h2>
<p>最近のトレンドとして、WebサービスがAPIを公開するといえば「RESTful」がお約束です。情報の取得、作成、更新、削除をHTTPのGET,POST,PUT,DELETEで行なおうという者です。</p>
<p>そのおかげで、とにかくHTTPさえ叩ければいろんなサービスのAPIを繋いであれこれできちゃう世の中になった訳ですが、問題が無い訳ではありません。</p>
<p>いかに「RESTfulなAPI」といってもすぐに呼び出せる訳ではありません。理由は、HTTPの上を流れるデータフォーマットがサービスによってまちまちな為です。そのため、サービスごとに使用を調べ、パースしたり生成したりするコードを書く必要があります。</p>
<p>そういうところをかっちり決めなくていいところが緩くて嬉しいわけですが、「<strong>サービスAは独自のXMLで、サービスBはATOMで、サービスCはJSONで、、、</strong>」となっていると、結構大変です。実際には、それぞれのサービス提供事業者がAPIアクセス用のライブラリを提供してくれているので、それをダウンロードしてきて組み込めばよいようになっているのが普通です。</p>
<p>でも、<span style="color: #0000ff;"><strong>自分が使いたい言語のライブラリを提供してくれているとも限らない</strong></span>のが痛いところ。</p>
<p><strong>「この処理はPythonで書けばあっという間なのに、クライアントライブラリが PerlしかないからPerlで書くか、、、」</strong></p>
<p>といった残念なシチュエーションが生まれている訳です。</p>
<p>そこで出てきたのがThriftです。</p>
<p><strong>「だったらAPIの呼び出しライブラリは自動生成しちゃえば？サーバだって自動生成しちゃうよ。そうすればサービスロジックの作成に専念できるじゃない」</strong></p>
<p>という考え方がThriftの発想です。</p>
<p>ちなみにThriftはHTTPを使いますが、RPCの発想（メソッド呼び出し）なので、いわゆるRESTfulなサービスとは異なる事に注意してください（たぶん）。</p>
<h2>Thriftの開発環境をMacOS Xにインストールする</h2>
<p>Thriftはコードを自動生成してくれるわけですが、その自動生成用のプログラムをビルドしてインストールする必要があります。ここでは、Thrift-0.6.1をMacOS X 10.6.8にインストールする手順を示します。</p>
<h3>Xcodeのインストール</h3>
<p>MacOS XにCコンパイラが入っている必要があります。これはAppleから配布されているXcodeを入れれば入ります。入れていない人はまずこれを入れましょう。</p>
<p>MacOS X Lionの人ならばMac App Storeから Xcodeを無料でインストールできます。</p>
<h3>boostライブラリのインストール</h3>
<p>boostライブラリと言うのはC++で便利にプログラミングする為のライブラリです。これが無いとC++用のコードが生成できないようなので、まずはこれを入れましょう。</p>
<p>http://www.boost.org/ から最新のソースコードをダウンロードしてください。執筆時の最新版は、1.47.0です。ここでは、boost_1_47_0.tar.gzをダウンロードして、適当なフォルダで展開します。</p>
<pre># tar zxvf boost_1_47_0.tar.gz</pre>
<p>展開されたディレクトリに移動します。</p>
<pre># cd boost_1_47_0</pre>
<p>boostのビルドのお約束コマンド、bootstrap.shを実行しましょう。</p>
<pre># ./bootstrap.sh</pre>
<p>すると、カレントディレクトリにb2というコマンドがつくられます。これを実行するとビルドが走ります。</p>
<pre># ./b2</pre>
<p>これで終わりではありません、さらにbjamというコマンドを走らせます。</p>
<pre># ./bjam
Performing configuration checks

    - has_icu builds           : no
warning: Graph library does not contain MPI-based parallel components.
note: to enable them, add "using mpi ;" to your user-config.jam
    - ../config//has_gcc_visibility builds : yes
    - ../config//has_long_double_support builds : yes
warning: skipping optional Message Passing Interface (MPI) library.
note: to enable MPI support, add "using mpi ;" to user-config.jam.
note: to suppress this message, pass "--without-mpi" to bjam.
note: otherwise, you can safely ignore this message.</pre>
<p>こんなメッセージが出て止まってしまいました。指示通り、user-config.jamを修正します。</p>
<pre># vi tools/build/v2/user-config.jam</pre>
<p>最後の行に、以下の設定を追加します。</p>
<pre>using mpi ;</pre>
<p>改めて bjam コマンドでビルドを進めます。</p>
<pre># ./bjam</pre>
<p>うまくビルドできたらインストールします。ルート権限が必要なので、sudoを付けるのを忘れずに。</p>
<pre># sudo ./bjam install</pre>
<h3>Apache Thriftのビルド</h3>
<p>Apache Thriftのサイトから最新版(0.6.1)のソースコードをダウンロードしてきて、展開します。</p>
<pre># tar zxvf thrift-0.6.1.tar.gz</pre>
<p>中に入って、、</p>
<pre># cd thrift-0.6.1</pre>
<p>こんふぃぎゅあー。</p>
<pre># ./configure</pre>
<p style="padding-left: 30px;"><em>2011/9/1 追記。</em><br />
<em>thrift-0.7.0が出ていたのでビルドしようとしたら configureに実行可能フラグが立っておらず、ビルドできませんでした。どうもアーカイブか壊れている気がします。他のファイルも怪しいです。</em><br />
<em>install-shも実行可能になってませんね。以下のコマンドで修正してください。</em><br />
<em>chmod +x configure install-sh</em></p>
<p>めーく。</p>
<pre># make</pre>
<p>あらら、、、</p>
<pre>/usr/libexec/gcc/powerpc-apple-darwin10/4.2.1/as: assembler (/usr/bin/../libexec/gcc/darwin/ppc/as or /usr/bin/../local/libexec/gcc/darwin/ppc/as) for architecture ppc not installed
Installed assemblers are:
/usr/bin/../libexec/gcc/darwin/x86_64/as for architecture x86_64
/usr/bin/../libexec/gcc/darwin/i386/as for architecture i386
src/protocol/fastbinary.c:1203: fatal error: error writing to -: Broken pipe
compilation terminated.
lipo: can't open input file: /var/folders/n0/n07PPobvF1uOeeW-ljfg8U+++TI/-Tmp-//ccSB6lR0.out (No such file or directory)
error: command 'gcc-4.2' failed with exit status 1
make[3]: *** [all-local] Error 1
make[2]: *** [all-recursive] Error 1
make[1]: *** [all-recursive] Error 1
make: *** [all] Error 2
Exit 2</pre>
<p>コンパイルが止まってしまいました。ググってみたところ、<a href="https://issues.apache.org/jira/browse/THRIFT-1143">こんなページ</a>が見つかりました。</p>
<p>どうやらXcode4になって、PowerPC用のアセンブラが無くなってしまったらしく、そのせいで止まっているようです。Intel Macなので、Intel用のコードだけ作ってくれれば良いのですが、CPUのアーキテクチャタイプを正しく検出していないためにPowerPCのコードもビルドしようとしているようです。</p>
<p>先ほどのサイトには Thriftのtrunkに対するパッチも提供されているので、次のバージョンくらいではうまくビルドできるようになっているかもしれませんが、当面は以下の対症療法で回避してください。</p>
<pre># cd lib/py
# sudo ARCHFLAGS="-arch x86_64" python setup.py install</pre>
<p>これがうまく動いたら、Thriftをインストールしましょう。元のディレクトリに戻ってください。</p>
<pre># cd ../..
# make
# sudo make install</pre>
<p>さて、これで /usr/local/bin あたりに thriftというコマンドがインストールされたはずです。thriftコマンドを起動してみましょう。パスが通っているか確認して実行してみてください。tcshを使っている人はrehashしたりするのを忘れずに、、、。</p>
<pre># thrift
Usage: thrift [options] file
Options:
  -version    Print the compiler version
  -o dir      Set the output directory for gen-* packages
               (default: current directory)
  -I dir      Add a directory to the list of directories
                searched for include directives
  -nowarn     Suppress all compiler warnings (BAD!)
  -strict     Strict compiler warnings on
  -v[erbose]  Verbose mode
  -r[ecurse]  Also generate included files
  -debug      Parse debug trace to stdout
  --gen STR   Generate code with a dynamically-registered generator.
   :
   :</pre>
<p>こんな感じでヘルプが表示されたら成功です。</p>
<h2>Thriftを使ってJavaのコードを生成してみる</h2>
<p>僕のメイン言語はJavaなので、ひとまずJavaのコードを生成してみます。木村俊也氏のブログ記事「<a href="http://blog.broomie.net/index.cgi?id=36">Thriftが便利すぎる</a>」のインターフェース定義をお借りして、コードを生成してみました。</p>
<p>適当なフォルダに以下の定義ファイルを作成します。</p>
<h3>TinyCalc.thrift</h3>
<pre>#!/usr/bin/thrift

service TinyCalc
{
        double sum(1: double param1, 2: double param2)
        double subtract(1:double param1, 2:double param2)
}</pre>
<p>簡単な足し算と引き算をするサービスです。</p>
<p>この定義ファイルを使ってJavaのコードを生成します。</p>
<pre># thrift --gen java TinyCalc.thrift</pre>
<p>実行すると、gen-javaというフォルダが作成され、その中にTinyCalc.javaというファイルが１つ出来ています。実はこのソースコード、これ一つでクライアントサイドとサーバサイドで必要な全てのコードが入っています。中身を見てちょっとびっくりしてしまったんですが、<span style="color: #0000ff;"><strong>1つのTinyCalcクラスの中に、7個の静的クラスと2つのインターフェースが盛り込まれています！</strong></span></p>
<p>このクラスを使って実際にRPCの呼び出しをしてみたいと思いますが、ちょっと長くなってきたので、<a title="Apache ThriftのサーバサイドをJava Servletとして動かす" href="http://ohnaka.jp/blog/2011/08/503">次のエントリ</a>で解説します。</p>
]]></content:encoded>
			<wfw:commentRss>http://ohnaka.jp/blog/2011/08/493/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>FlashBuilderの表示フォントを小さくする</title>
		<link>http://ohnaka.jp/blog/2011/08/480</link>
		<comments>http://ohnaka.jp/blog/2011/08/480#comments</comments>
		<pubDate>Wed, 03 Aug 2011 08:01:14 +0000</pubDate>
		<dc:creator>くにちこ</dc:creator>
				<category><![CDATA[Flash]]></category>
		<category><![CDATA[技術]]></category>
		<category><![CDATA[FlashBuilder]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[フォント]]></category>

		<guid isPermaLink="false">http://ohnaka.jp/blog/?p=480</guid>
		<description><![CDATA[僕はMac上でFlashBuilder (今は4.5が最新です)を使っているのですが、「ちょっとフォントが大きいな」と思う事があります。 エディタ内の文字サイズは環境設定から変える事ができますが、そこじゃなくて、タブに表 [...]]]></description>
			<content:encoded><![CDATA[<p>僕はMac上でFlashBuilder (今は4.5が最新です)を使っているのですが、「ちょっとフォントが大きいな」と思う事があります。</p>
<p>エディタ内の文字サイズは環境設定から変える事ができますが、そこじゃなくて、<span style="color: #0000ff;">タブに表示される文字のサイズ</span>や、<span style="color: #0000ff;">パッケージエクスプローラのツリー表示</span>などで使用されているシステム文字です。</p>
<p>僕は日本語版のFlashBuilderを使っているのですが、どうもシステム文字の大きさがアンバランスで、デザイン的に間抜けな感じに見えてしまいます。FlashBuilderのベースとなっているEclipseの方は英語版を使っているのですが、こちらは違和感ありません。以前Eclipseを日本語化した時も似たような違和感を感じた事があったので、日本語フォント特有の問題なのかと思っていました。</p>
<p><span id="more-480"></span>FlashBuilderの初期化ファイルにちょっと手を入れると、引き締まったフォントサイズにする事ができます。</p>
<h3>「Adobe Flash Builder 4.5.app」を開く</h3>
<p>アプリケーションフォルダにFlashBuilderをインストールしているのであれば、まず、「アプリケーション」フォルダの中の、「Adobe Flash Builder 4.5」というフォルダを開きましょう。その中に、いつも起動しているAdobe Flash Builder 4.5.appというアプリケーションがあります。</p>
<p>それを右クリック（もしくはCtrl + クリック）し、「パッケージの内容を表示」というメニューを選択します。</p>
<p><span class="Apple-style-span" style="font-size: 15px; font-weight: bold;">「Adobe  Flash Builder 4.5.ini」を編集する</span></p>
<p>パッケージの内容を開いたら「Contents」というフォルダが出てくるのでそれを開きます。</p>
<p>さらに「MacOS」というフォルダを開くと「Adobe Flash Builder 4.5.ini」というファイルが見えてきますので、それを右クリックして、「テキストエディット」などのアプリケーションで編集します。</p>
<p>よくわからない場合は、「ターミナル」アプリケーションで以下のコマンドを入力しましょう。</p>
<pre>open -a /Applications/TextEdit.app /Applications/Adobe\ Flash\ Builder\ 4.5/Adobe\ Flash\ Builder\ 4.5.app/Contents/MacOS/Adobe\ Flash\ Builder\ 4.5.ini</pre>
<h3>プロパティを追加</h3>
<p>Adobe Flash Builder 4.5.iniが開けたら、最後の行に、以下の記述を追加します。</p>
<pre>-Dorg.eclipse.swt.internal.carbon.smallFonts</pre>
<p>全体では次のような感じになります(バージョンによって多少違うかもしれません)。</p>
<pre class="brush:plain">-nl
ja_JP
-startup
../../../eclipse/plugins/org.eclipse.equinox.launcher_1.1.0.v20100507.jar
--launcher.library
../../../eclipse/plugins/org.eclipse.equinox.launcher.cocoa.macosx_1.1.1.R36x_v20100810
--launcher.defaultAction
openFile
-showsplash
../../../assets/fb_splash_premium.png
-vmargs
-Xms256m
-Xmx512m
-XX:MaxPermSize=256m
-XX:PermSize=64m
-Xdock:name=Flash Builder
-Xdock:icon=../Resources/fb_app.icns
-XstartOnFirstThread
-Declipse.product=com.adobe.flexbuilder.standalone.producte36
-Declipse.application=com.adobe.flexbuilder.standalone.FlashBuilderApplication
-Dorg.eclipse.swt.internal.carbon.smallFonts</pre>
<p class="brush:plain">これでフォントが少し小さくなります。</p>
<h2 class="brush:plain">比較</h2>
<h3>変更前</h3>
<p style="text-align: center;"><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/08/FlashBuilderFont-normal.png" target="_blank"><img class="aligncenter size-medium wp-image-481" title="FlashBuilderFont-normal" src="http://ohnaka.jp/blog/wp-content/uploads/2011/08/FlashBuilderFont-normal-300x174.png" alt="" width="300" height="174" /></a></p>
<p>右上の「Flashデバッグ」パースペクティブのタブが、文字が入りきらずに「Flashデバ…」みたいになってしまってます。</p>
<h3>変更後</h3>
<p style="text-align: left;"><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/08/FlashBuilderFont-small.png" target="_blank"><img class="aligncenter size-medium wp-image-482" title="FlashBuilderFont-small" src="http://ohnaka.jp/blog/wp-content/uploads/2011/08/FlashBuilderFont-small-300x174.png" alt="" width="300" height="174" /></a>変更後はちゃんとタブの中に収まっています。ツリー表示も引き締まっていていい感じですね。</p>
]]></content:encoded>
			<wfw:commentRss>http://ohnaka.jp/blog/2011/08/480/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WicketでページのURLをきれいにする</title>
		<link>http://ohnaka.jp/blog/2011/07/408</link>
		<comments>http://ohnaka.jp/blog/2011/07/408#comments</comments>
		<pubDate>Fri, 08 Jul 2011 03:58:39 +0000</pubDate>
		<dc:creator>くにちこ</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[技術]]></category>
		<category><![CDATA[Wicket]]></category>

		<guid isPermaLink="false">http://ohnaka.jp/blog/?p=408</guid>
		<description><![CDATA[JavaでWebアプリを作る場合、素のJSPとServletで作る事はあまりなく、Strutsなどのフレームワークを使うと思います。 ただ、ちょっとしたデバッグUIを作りたい時など、Strutsだとちょっと大げさな感じに [...]]]></description>
			<content:encoded><![CDATA[<p>JavaでWebアプリを作る場合、素のJSPとServletで作る事はあまりなく、Strutsなどのフレームワークを使うと思います。</p>
<p>ただ、ちょっとしたデバッグUIを作りたい時など、Strutsだとちょっと大げさな感じになってしまいます。そんなわけで「軽量フレームワーク」と言われる<span style="color: #ff0000;"><strong>Apache Wicket</strong></span>をちょこちょこと試しています。</p>
<p><span id="more-408"></span></p>
<p>Wicketの入門は<a href="http://codezine.jp/article/detail/4459">CodeZine</a>とか<a href="http://journal.mycom.co.jp/articles/2006/05/08/wicket/index.html">マイコミジャーナル</a>とかに詳しく載っています。</p>
<p>このあたりのチュートリアルに従って簡単なアプリを作ってみたのですが、一番最初にびっくりしたのが<span style="color: #0000ff;">「URLがきもちわるい！！」という事</span>でした。</p>
<p>たとえば、あるスタティックなページのURLが、</p>
<pre>http://localhost:8080/wicket-test/?wicket:bookmarkablePage=:jp.ohnaka.wicket.SubPage</pre>
<p>こんな感じになってしまうのです。長いし、パッケージ名やクラス名までまるわかりだし、これを見た時に<strong><span style="color: #0000ff;">「やっぱりやめようかな、、、」</span></strong>とまで思ってしまったのですが、<span style="color: #ff0000;">大丈夫、ちゃんと好みのスタイルに変更する事ができました</span>。</p>
<p>以下そのメモです。</p>
<h2>Wicketで簡単なアプリを作る</h2>
<p>まず、IndexページとSubページの２つからなるwicket-testという簡単なアプリを作ります。</p>
<p>なお、Wicketは1.4.17を使っております。Wicketはバージョンが変わると前のバージョンの機能がすぐに使えなくなったりするので、この情報もすぐに古くなってしまうかもしれません、、、。悪しからず。</p>
<h3>web.xml</h3>
<p>まずはweb.xmlから。Strutsなどと同じように、Wicketも Servlet Filterとして動作します。フィルタを /* に対して設定しているため、このWebアプリケーションはパス全体がWicketの配下に入りますが、 /debug/* などに変更すれば、特定のパス以下だけでWicketを動作させる事もできます。</p>
<p>Servlet Filteraのinit-paramにはアプリケーションクラス名を設定してください。今回はjp.ohnaka.wicket.TestApplication クラスにしました。</p>
<pre class="brush:xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"&gt;
  &lt;display-name&gt;wicket-test&lt;/display-name&gt;

  &lt;filter&gt;
   &lt;filter-name&gt;wickletFilter&lt;/filter-name&gt;
   &lt;filter-class&gt;org.apache.wicket.protocol.http.WicketFilter&lt;/filter-class&gt;
   &lt;init-param&gt;
    &lt;param-name&gt;applicationClassName&lt;/param-name&gt;
    &lt;param-value&gt;jp.ohnaka.wicket.TestApplication&lt;/param-value&gt;
   &lt;/init-param&gt;
  &lt;/filter&gt;

  &lt;filter-mapping&gt;
   &lt;filter-name&gt;wickletFilter&lt;/filter-name&gt;
   &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;

&lt;/web-app&gt;</pre>
<h3>jp.ohnaka.wicket.TestApplication クラス</h3>
<p>Wicketで作るWebアプリの大元、メインクラスです。</p>
<p>getHomePage()メソッドでは、コンテクストルートにアクセスした場合に表示するページ(今回はIndexPage)を返すようにしています。</p>
<pre class="brush:java">package jp.ohnaka.wicket;

import org.apache.wicket.Page;
import org.apache.wicket.protocol.http.WebApplication;

public class TestApplication extends WebApplication {

    @Override
    protected void init() {
        super.init();
    }

    @Override
    public Class&lt;? extends Page&gt; getHomePage() {
        return IndexPage.class;
    }

}</pre>
<h3 class="brush:java">jp.ohnaka.wicket.BasePageクラス</h3>
<p>２つのページの基底クラスとなるクラスです。基底クラス無しで直接書いても良いのですが、IndexPage.htmlとSubPage.htmlの共通部分をBasePage.htmlに抜き出したいので、対応するJavaクラスの方も基底クラスを作るようにしています。</p>
<pre class="brush:java">package jp.ohnaka.wicket;

import org.apache.wicket.markup.html.WebPage;

public class BasePage extends WebPage {
    public BasePage() {
    }
}</pre>
<h3>jp.ohnaka.wicket.IndexPageクラス</h3>
<p>IndexPageのロジックです。SubPageへのリンクを埋め込んでいます。</p>
<pre class="brush:applescript">package jp.ohnaka.wicket;

import org.apache.wicket.markup.html.link.BookmarkablePageLink;

public class IndexPage extends BasePage {
    public IndexPage() {
        // SubPageへのリンクを作成する
        add(new BookmarkablePageLink&lt;Void&gt;("linkToSubPage", SubPage.class));
    }
}</pre>
<h3>jp.ohnaka.wicket.SubPageクラス</h3>
<p>SubPageのロジックです。IndexPageに戻れるように、IndexPageへのリンクを埋め込んでいます。</p>
<pre class="brush:java">package jp.ohnaka.wicket;

import org.apache.wicket.markup.html.link.BookmarkablePageLink;

public class SubPage extends BasePage {
    public SubPage() {
        // IndexPageへのリンクを作成する
        add(new BookmarkablePageLink&lt;Void&gt;("linkToIndexPage", IndexPage.class));
    }
}</pre>
<h3>/jp/ohnaka/wicket/BasePage.html ファイル</h3>
<p>IndexPageとSubPageのHTMLの共通部分を抜き出したファイルです。&lt;wicket:child/&gt;の部分がサブクラスのHTMLと差し変わります。</p>
<pre class="brush:xml">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&gt;
&lt;html xmlns:wicket="http://wicket.apache.org/"&gt;
&lt;head&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
    &lt;title&gt;Wicket Test&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;wicket:child/&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<h3>/jp/ohnaka/wicket/IndexPage.html ファイル</h3>
<p>IndexPageのHTMLです。BasePage.htmlの&lt;wicket:child/&gt;タグがこの&lt;wicket:extend&gt;の内容と置き換わります。</p>
<pre class="brush:xml">&lt;wicket:extend&gt;

&lt;a href="#" wicket:id="linkToSubPage"&gt;SubPageへ&lt;/a&gt;

&lt;/wicket:extend&gt;</pre>
<h3>/jp/ohnaka/wicket/SubPage.html ファイル</h3>
<p>SubPageのHTMLです。IndexPageとほとんど同じですね。</p>
<pre class="brush:xml">&lt;wicket:extend&gt;

&lt;a href="#" wicket:id="linkToIndexPage"&gt;IndexPageへ&lt;/a&gt;

&lt;/wicket:extend&gt;</pre>
<h2>デフォルトのページURLのスタイル</h2>
<p>早速配備して動かしてみました。http://localhost:8080/wicket-test/ にアクセスするとIndexPageが表示されます。</p>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/07/IndexPage.png"><img class="aligncenter size-medium wp-image-466" title="IndexPage" src="http://ohnaka.jp/blog/wp-content/uploads/2011/07/IndexPage-300x97.png" alt="" width="300" height="97" /></a>SubPageへのリンクをクリックすると、SubPageに遷移します。</p>
<p><a href="http://ohnaka.jp/blog/wp-content/uploads/2011/07/SubPage.png"><img class="aligncenter size-medium wp-image-467" title="SubPage" src="http://ohnaka.jp/blog/wp-content/uploads/2011/07/SubPage-300x97.png" alt="" width="300" height="97" /></a>ブラウザのアドレス部を見て頂ければわかりますが、冒頭に書いたように、非常に長いURLになっています。SubPageクラスのFQCNがもろに見えている形になっていて、美しくありません。パッケージ名をリファクタリングしたらURLも変わってしまいますし、何より、<span style="color: #0000ff;">実装がエンドユーザに見えてしまうのはあまり良い事ではない</span>です。</p>
<h2>ページのURLを固定する</h2>
<p>SubPageのURLを http://localhost:8080/wicket-test/sub というURLにしてみたいと思います。具体的には、TestApplicationクラスを以下のように修正します。</p>
<pre class="brush:java">package jp.ohnaka.wicket;

import org.apache.wicket.Page;
import org.apache.wicket.protocol.http.WebApplication;

public class TestApplication extends WebApplication {

    @Override
    protected void init() {
        super.init();
        mountBookmarkablePage("/sub", SubPage.class);
        // mountPage("/sub", SubPage.class);  //Wicket 1.5系の場合
    }

    @Override
    public Class&lt;? extends Page&gt; getHomePage() {
        return IndexPage.class;
    }

}</pre>
<p>変わったのは init()メソッドの中です。</p>
<pre>mountBookmarkablePage("/sub", SubPage.class);</pre>
<p>という記述が増えています。</p>
<p>メソッド名に「mount」とありますが、このようにページのURLを変ええる操作はWicketでは「<strong><span style="color: #ff0000;">ページをマウントする</span></strong>」と言うようです。UNIXでもディスクドライブをあるパスに配備する事を「マウント(mount)」と言いますが、それと同じような感じで、SubPageというクラスを <span style="color: #0000ff;">/sub というパスにマウント</span>(配備、設置)するわけです。</p>
<p>この「<span style="color: #0000ff;">マウント</span>」という名前(概念)に行き着くまでに時間がかかってしまい、検索するのに結構苦労しましたがもう大丈夫です。「wicket mount」などで検索すると、他にもいろいろな応用技が見つかります。</p>
<ul>
<li><a href="http://d.hatena.ne.jp/Kishi/20080625/1214410127">WicketのURLをcoolにする</a></li>
<li><a href="http://d.hatena.ne.jp/mdgw/20080204/1202138613">Wicketが生成するアドレスを今風(?)にする</a></li>
</ul>
<p>ただ、Wicketのマウントの仕様はバージョンごとに少しずつ変わっているので、古いバージョンの情報の通りには動かない事があります。その場合は<a href="http://wicket.apache.org/">本家サイト</a>で<span style="color: #0000ff;">mountというキーワードを頼りに</span>ドキュメントを探してみてください。</p>
<h2>【追記】Wicket 1.5系の場合</h2>
<p>おおたさんよりコメントのあった通り、1.5系では mount系のメソッドが大きく変わっているようです。</p>
<p>今回のコードに関しては、以下のようにmountBookmarkablePageメソッドを mountPageメソッドに置き換える事で1.5対応ができました(1.5-RC5.1で確認)。</p>
<pre>mountPage("/sub", SubPage.class);</pre>
]]></content:encoded>
			<wfw:commentRss>http://ohnaka.jp/blog/2011/07/408/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Served from: ohnaka.jp @ 2012-02-23 06:00:36 -->
