tag:blogger.com,1999:blog-9522812000005921262024-02-20T07:20:01.551+09:00もじゃの日記もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.comBlogger56125tag:blogger.com,1999:blog-952281200000592126.post-48245449349432153962015-10-15T11:10:00.001+09:002015-10-15T11:10:10.470+09:00Dockerfile ビルド時間短縮 tips<p>
ubuntu や centos などの dockerhub イメージを使って Dockerfile を書くとき、大抵の場合 apt-get update や yum update を先頭に書きます。
</p>
<pre class="code">
FROM ubuntu:latest
RUN apt-get update && apt-get -y upgrade && apt-get clean
</pre>
<p>
毎回たくさんのダウンロードが実行される時間を省略するために以下をやっておきます。
</p>
ホストOS 上で docker run
<pre class="code">
$ docker run -it --name t1 ubuntu:latest
</pre>
コンテナで apt-get update など実行して exit
<pre class="code">
# apt-get update && apt-get -y upgrade && apt-get clean
# exit
</pre>
同じ名前でイメージ作成
<pre class="code">
$ docker commit t1 ubuntu:latest ← 同じ名前でイメージ更新
$ docker rm t1 ← 仮コンテナを削除
</pre>
これで Dockerfile の FROM はいじらずにビルド時間を短縮できます。
もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-40972009067883163172015-04-22T14:52:00.002+09:002015-04-22T14:52:15.150+09:00技術ネタは Qiita とここで 2重投稿してみるもじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-30039588754599913562015-04-22T14:32:00.000+09:002015-04-22T14:32:43.932+09:00mysql の procedure(プロシージャ)で add partitionサンプルテーブル log。主キーは id 一つ。
<pre class="code">
create table log (
id bigint not null auto_increment,
message text ,
created_at int not null,
primary key (id)
) engine = InnoDB;
</pre>
このままだとパーティション作成できないので主キーをいじる
<pre class="code">
alter table log
modify id bigint not null,
drop primary key;
alter table log
add primary key (id, created_at),
modify id bigint not null auto_increment;
</pre>
パーティションを 1つ追加しとく。
<pre class="code">
alter table log partition by range (created_at) (
partition pmin values less than (0)
);
</pre>
プロシージャを作って 3年分のパーティション追加。
<pre class="code">
drop procedure if exists add_monthly_partition;
delimiter //
create procedure add_monthly_partition(
in table_name text,
in date_func text,
in years int)
begin
select now() into @d;
select date_add(@d, interval years year) into @last;
while @d < @last do
select concat(
'alter table ', table_name,
' add partition (partition p',
date_format(@d, '%Y%m'),
' values less than (', date_func, '(''',
date_format(@d, '%Y-%m-01'), ''')))'
) into @ddl;
select @ddl;
prepare stmt from @ddl;
execute stmt;
deallocate prepare stmt;
select date_add(@d, interval 1 month) into @d;
end while;
end //
delimiter ;
call add_monthly_partition('log', 'unix_timestamp', 3);
</pre>もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-33107394292221359932014-09-04T16:46:00.000+09:002014-09-04T16:46:09.171+09:00Java クラスパスを再帰的に設定昔は <a href="http://blog.utils.jp/2008/09/blog-post.html">こんな</a> スクリプトを使っていましたが、今はコレ1行。<br />
<br />
CLASSPATH=.`find lib -name "*.jar" -exec echo -n :{} \;`<br />
<br />もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-338175679663558592013-08-08T11:53:00.001+09:002013-08-08T11:53:42.913+09:00UTF-8 BOM ファイルを探すメモメモ
find ./tmpl -name "*.html" -type f | grep -v .svn | xargs file | grep BOM
とか。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com1tag:blogger.com,1999:blog-952281200000592126.post-14526293242710647832011-09-03T12:37:00.005+09:002013-08-08T11:51:50.286+09:00perl のマルチスレッドども。ある案件で perl の マルチスレッドを使うことになりました。
<br />
<br />
<pre class="code">use threads;
my @ts;
for (1..5) {
my $t = threads->create(sub {
# some code
…
# 1つのスレッド処理完了 *1
});
push(@ts, $t);
}
eval { $_->join } for @ts;
# 終了処理 *2
</pre>
<br />
このようなコードを書いたのですが、どれかのスレッドの「# 1つのスレッド処理完了 *1」のタイミング「Scalars leaked: 1」が発生します。
<br />
このエラーが発生するとまだ「# 終了処理 *2」は実行されず、メインプロセスごと落ちてしまいます。
<br />
まだ実行中のスレッドもメインプロセスとともに落ちます。
<br />
<br />
再現率は 100%ではないのですが、スレッドでやる仕事時間が長ければ長いほど「Scalars leaked: 1」は 100%に近い状態で発生していました。
<br />
<br />
スレッドの呼び出し方法が悪いのかと思い 事前に関数定義したり
<br />
<pre class="code">my $runnable = sub {
# some coe
};
my @ts;
for (1..5) {
my $t = threads->create($runnable);
push(@ts, $t);
}
eval { $_->join } for @ts;
</pre>
<br />
<br />
こんなことしたり
<br />
<pre class="code">my @ts;
for (1..5) {
my $t = threads->create(\&runnable);
push(@ts, $t);
}
eval { $_->join } for @ts;
sub runnable {
# some coe
};
</pre>
<br />
<br />
こんなことしたり、色々試しましたが症状は改善しません。
<br />
<pre class="code">my @ts;
for (1..5) {
my $t = threads->create('runnable');
push(@ts, $t);
}
eval { $_->join } for @ts;
sub runnable {
# some coe
};
</pre>
<br />
<br />
perl 5.8.8 を使っていたので perl 5.14.1 にもしてみましたが、「Scalars leaked: 1」の代わりに「Segmentation fault」が発生するようになりました。
<br />
<br />
<br />
最終的に以下の方法でひとまず解決しました。perl 5.10.x あたりから登場する threads::shared を使います。
<br />
<br />
<pre class="code">use threads;
use threads::shared;
my %alive :shared;
my $keepalive :shared;
$keepalive = 1;
for my $i (1..5) {
$alive{$i} = time();
my $t = threads->create(sub {
my $ii = shift;
# some code
$alive{$ii} = time(); # この時間にまだ生きいることを知らせる
# some code
$alive{$ii} = time(); # この時間にまだ生きいることを知らせる
# some code
delete $alive{$ii}; # このスレッドの処理が終わったので %alive を消す
while (1) {
last unless $keepalive; # 他のすべてのスレッドが終了し、メインスレッドの「# 終了処理 *2」が終わるまで待機
sleep(2);
}
}, $i);
$t->detach; # join しない
}
# join の代わりに %alive が全部なくなるまで待つ
while (1) {
my @keys = keys %alive;
last if @keys == 0; # %alive が無くなったら終了
my $now = time();
for my $key (@keys) {
my $a = $alive{$key} or next;
if ($now - $a > 30) {
# %alive 処理ができずに落ちたスレッドがいるかも知れないので、最後の生存通知から 30秒以上たってる場合は以後処理しない。
warn "timeout $key";
delete $alive{$key};
}
}
sleep(5);
}
# 終了処理 *2
# some code
$keepalive = 0; # すべてのスレッドに関数を終了して良いことを知らせる。このあとどこかで Segmentation fault が発生するかも知れないが、構わないものとする。
</pre>
<br />
<br />
たぶん、大丈夫だと思う。美しくはないですが。。
<br />
あやうく Java で書きなおそうかと思いました。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-13179469863828157152011-08-23T14:06:00.001+09:002011-08-23T14:10:24.133+09:00memcached でキーの列挙 (3)前回のエントリ <a href="http://blog.utils.jp/2008/06/memcached-2.html">memcached でキーの列挙 (2)</a>
<br />
<br />こんなん作りました。memcached-tool ぽく。既にあったらすません~。
<br /><a href="http://code.google.com/p/memcached-keys/">http://code.google.com/p/memcached-keys/</a>もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-84384296398086309232011-06-07T18:37:00.002+09:002011-06-07T18:42:42.043+09:00Google App Engine (GAE) の runserver 停止ども!ご無沙汰してます。<br /><br />近頃は受託案件に加え、モバゲー向けに自社製ゲームを出したりしてましたが、6月2日にサービスを終了しました。<br />時間ができたら開発に至った経緯や失敗談など書いていこうと思います。<br /><br /><br /><br />さて、最近は Google App Engine Java で試験的に開発などを行っています。<br />ビルドは Windows にて ant を使っていますが、そこで遭遇した問題を 1つ解決したのでメモ書きです。<br /><br /><br /><br />ant でのビルドと動作確認は runserver タスクを実行しますが、ドキュメント通りに Ctrl+C でプロセスを落としても Jetty の java.exe が残ったままになる問題があります。<br /><br />残ったままでは次の変更が反映されないので、タスクマネージャで java.exe を手動で終了させます。<br />めんどくさいので以下のように runserver を定義することで解決しました。<br /><br /><pre class="code">変更前<br /> <target name="runserver" depends="datanucleusenhance"<br /> description="Starts the development server."><br /> <dev_appserver war="war" /><br /> </target><br /></pre><br /><br /><pre class="code">変更後<br /> <target name="runserver" depends="datanucleusenhance"<br /> description="Starts the development server."><br /> <java classname="com.google.appengine.tools.development.DevAppServerMain"<br /> classpath="${appengine.tools.classpath}"<br /> fork="true" failonerror="true"><br /> <jvmarg value="-javaagent:${appengine.sdk.home}/lib/agent/appengine-agent.jar" /><br /> <arg value="--port=8080"/><br /> <arg value="--address=localhost"/><br /> <arg value="war"/><br /> </java><br /> </target><br /></pre><br /><br />Google App Engine 標準の runserver は dev_appserver マクロを呼び出し、com.google.appengine.tools.KickStart を起動していますが、これをすっ飛ばして<br />com.google.appengine.tools.development.DevAppServerMain を直接起動します。<br /><br />キモは -javaagent ですね。<br /><br />これで少し快適に開発ができるようになるはず。。<br /><br /> もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-29701873507997931602010-05-17T04:10:00.005+09:002010-05-17T04:39:42.776+09:00FastReader (android GoogleReader クライアント) のソースコードを公開しましたFastReader を公開して 1か月半がたちました。<br /><br />現在 9000件のダウンロード、34%のアクティブユーザ数です。<br />まさかこれほどのダウンロード数があるとは思いもよりませんでした。<br /><br />さらに思いもよらぬことに、バグや機能改善要望などを(主に英語で)たくさん頂くことができました。<br />この場を借りてお礼申し上げます。<br /><br />今、もっとも悩ましい問題は「同期が遅く CPU 使用率が高い状態が続く」ことです。<br />開発者としての意地もあるの調査を続けてましたが、どうにも解決策が見いだせず、悶々として過ごしていました。<br /><br />いつまでも解決しないまま放置するのも申し訳ないので、ソースコードを公開して一緒に開発してくださる方を募集してみることにしました。<br /><a href="http://code.google.com/p/fast-reader/">http://code.google.com/p/fast-reader/</a><br /><br />もし参加しても良い、または少しでもお気づきの点がございましたら <a href="mailto:android-support@trycatch.jp">android-support@trycatch.jp</a> までお寄せいただければ幸いです。<br /><br />ちょっと恥ずかしいですが、広告を入れたり、有料バージョンを作ったりしようと目論んでいた時期もありました。<br />このプロジェクトに関しては一切を放棄します。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-23998977822187837682010-04-13T11:14:00.004+09:002010-04-13T11:51:25.923+09:00FastReader を公開してみました4月2日に FastReader という Android用 Google Reader クライアントを公開してみました。<br /><br />最初は LDReader を改良して Google Reader にも対応するマルチなリーダを作ろうと試行錯誤をしていたのですが、カテゴリとタグ、既読管理の方法が大きく違うので断念しました。<br /><br />似たようなアプリに NewsRob、NetaShare という素晴らしいものがあるのですが、せっかく Google Reader と通信するクラスを作ったので LDReader の UI 部分を流用して公開に踏み切りました。<br /><br />今のところの特徴としては、<br />- タブでフィード一覧、タグ一覧を分けて表示<br />- オフラインでつけたスターも後で同期する<br />- 「記事一覧を省略」の設定をすることで、短いステップでフィード消化が可能<br />などです。<br /><br />↓のキャプチャは Android のデベロッパーコンソールのものです。(4月13日午前)<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUF1tIzgmyicA972zZaOrOQTXi9RPrTrLHSpvX3wKRTZA3OCd1N3oWFOsySzkKqQ13rPKicggXAb_A-sQfHT20g5TYgnZZ08G2bQZJ4ZT_vctP-ZNxtGgnF7r__6nfOt1nNXXvyZzeUr0/s1600/marketHome.PNG"><img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 320px; height: 87px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUF1tIzgmyicA972zZaOrOQTXi9RPrTrLHSpvX3wKRTZA3OCd1N3oWFOsySzkKqQ13rPKicggXAb_A-sQfHT20g5TYgnZZ08G2bQZJ4ZT_vctP-ZNxtGgnF7r__6nfOt1nNXXvyZzeUr0/s320/marketHome.PNG" alt="" id="BLOGGER_PHOTO_ID_5459439956245098226" border="0" /></a>公開してみてびっくりしたのが、意外にダウンロードして使ってくれてる人がいらっしゃることです。<br />2日で 1000 ダウンロードを超えました。英語でのバグ報告や機能改善要望も数件いただきました。<br /><br />また恥ずかしながら version 0.0.1 からバグがたくさんあり、一時期は☆が 2.5 まで下がったりもしましたがなんとか 3.5 付近まで回復しました。なんとか 4まではもっていきたいですね。<br /><br />というわけで、機能改善や追加を行っていきます。<br />今後ともよろしくお願いいたします。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com1tag:blogger.com,1999:blog-952281200000592126.post-35952851980615341032010-03-24T09:44:00.002+09:002010-03-24T09:57:16.691+09:00Google Checkout 販売者登録Google Checkout の販売者としての法人口座登録がようやく成功しました。<br /><br />銀行は東京三菱 UFJ 銀行です。英語名は「Bank of Tokyo-Mitsubishi UFJ」。<br />3か月かかりました。<br /><br />何故 3か月もかかったのか、簡単に説明しますと、<br /><br />・失敗の理由が良く分からない。Google に問い合わせても分からない。<br />・銀行側に問い合わせても分からない。何のことか理解してもらえず対応していないの一点張り。<br />・Google Checkout に法人格の括弧「カ)」が登録できなかった。(今はできるようになりました)<br />・会社名は「トライキャッチ」なのですが、口座名義は「トライキヤツチ」と小さいカタカナでないので気づくのに時間がかかった。<br /><br />てな感じです。<br /><br />Google のサポートさんとは 20通ぐらいのやり取りをさせていただきました。<br />最後までご対応いただいてありがとうございます。<br /><br />有料の Android アプリはまだ無いですが、事前準備ができてやる気が沸いてきました。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-91442599188257996822010-02-05T15:44:00.002+09:002010-02-05T15:56:14.714+09:00LDReader 1.0.5 をリリースしました。(LDReader のリリース記事ばっかりですが。。)<br /><br />LDReader 1.0.5 をリリースしました。主な変更点は以下です。<br />1. 記事の削除機能を追加<br />2. バックエンドで記事取り込み中のもっさり感を解消<br />(<a href="http://code.google.com/p/ldreader/source/detail?r=12">ソース差分 r12</a>)<br /><br /><span style="font-weight:bold;">1. 記事の削除機能を追加</span><br />・全記事<br />・未読記事のみ<br />・特定フィードの全記事<br />・特定フィードの未読記事のみ<br />の削除機能を追加しました。<br /><br /><span style="font-weight:bold;">2. バックエンドで記事取り込み中のもっさり感を解消</span><br />一覧系の画面では、ResourceCursorAdapter の autoRequery オプションを true に設定していたのですが、記事が 1件挿入されるたびに SELECT文を発行しており、このせいで画面が固まったようになる問題がありました。autoRequery を false にして独自の更新通知を受け取った場合のみに requery するようにしました。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-59421752414529101372010-01-31T03:07:00.004+09:002010-02-03T14:15:54.401+09:00LDReader 1.0.2 パフォーマンス改善と記事一覧画面追加android の LDReader 1.0.2 をリリースしました。主な変更点はこちら。<br /><br />・記事一覧画面を追加<br />・パフォーマンス改善<br />・バグ修正<br />(<a href="http://code.google.com/p/ldreader/source/detail?r=10">ソース差分</a>)<br /><br /><span style="font-weight:bold;">記事一覧画面について</span><br /><br />今までの画面遷移は<br /> フィード一覧 → 記事詳細<br />だったのですが<br /> フィード一覧 → 記事一覧 → 記事詳細<br />としました。<br /><br />記事一覧を作ってみたら今まで無理して記事詳細に詰め込んでいた機能が、以外とすんなりおさまるようになりました。<br />使用感もこちらの方が自然になったと思います。<br /><br /><span style="font-weight:bold;">パフォーマンス改善</span><br /><br />使っていてデータがたまってくると、記事詳細からフィード一覧へ戻るときどうしても遅いことに気づき調査したところ。。<br /><br />sqlite に対する次の SQL が遅いことが分かりました。<br /><pre class="code">select count(*) from item subscription_id = ? and unread = 1</pre><br /><br />item テーブルのスキーマはこんな感じです。id にアンダーバーがついてるのは android の慣例ですね。<br /><pre class="code">CREATE TABLE item (<br /> _id integer primary key,<br /> subscription_id integer,<br /> uri text,<br /> title text,<br /> body text,<br /> author text,<br /> unread integer,<br /> created_time integer,<br /> modified_time integer<br />);<br />CREATE INDEX idx_item_created_time on item(created_time);<br />CREATE INDEX idx_item_modified_time on item(modified_time);<br /><span style="font-weight:bold;">CREATE INDEX idx_item_subscription_id on item(subscription_id);</span><br />CREATE INDEX idx_item_title on item(title);<br /><span style="font-weight:bold;">CREATE INDEX idx_item_unread on item(unread);</span></pre><br /><br />item テーブルの subscription_id と unread にはそれぞれインディクスを貼ってあるのにどーしてこんなに遅いんだろうと。<br /><br />色々と回り道しつつ、次のように複合インディクスを追加することで解決しました。<br /><pre class="code">CREATE INDEX idx_item_unread_by_sub_id on item(subscription, unread);</pre><br /><br />使ってくださってる方がいらっしゃったら是非アップグレードをお勧めします^^もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com1tag:blogger.com,1999:blog-952281200000592126.post-17305845929184677532009-11-25T18:49:00.004+09:002009-11-25T19:34:41.288+09:00はじめての Wikipedia に携帯 IPアドレス一覧携帯向けのサイトを作っていると、アクセス元の IPアドレスで携帯かそうでないかを判断する仕組みを導入することがあります。<br /><br />いろいろ試行錯誤してこんなページを Wikipedia に作っちゃいました。<br /><a href="http://ja.wikipedia.org/wiki/Mobile:GatewayIP">http://ja.wikipedia.org/wiki/Mobile:GatewayIP</a><br /><br /><br />(以下 Wikipedia にページを作った経緯など)<br /><br />IPアドレスリストは各キャリアがそれぞれのサイトで公開しています。<br /><br /> docomo(NTTドコモ)<br /> http://www.nttdocomo.co.jp/service/imode/make/content/ip/<br /><br /> SoftBank(ソフトバンク)<br /> http://creation.mb.softbank.jp/web/web_ip.html<br /><br /> au(エーユー)<br /> http://www.au.kddi.com/ezfactory/tec/spec/ezsava_ip.html<br /> 「※本情報はEZサーバ以外のホストによる上記表のIPアドレスでのアクセスがないことを保証するものではありません。」とか書いてあるのであまり使っちゃいけないかもしれないですね。。<br /><br />かなり前もって情報は公開されるようなので普段気にしていればいいのですが、つい忘れてしまいがちです。(au は妙にコピペしずらいし)<br /><br />そういう情報をまとめているサイトが無いか、プログラムから利用しやすいウェブサービスは無いかと探しますが、なかなか見つかりません。<br /><br />プログラマなんで、各キャリアのサイトをスクレイピングして CIDR 形式のリストを抽出することが思いつきます。<br /><br />自分で書く前に情報を探すと Net::CIDR::MobileJP という CPAN モジュールを使った素晴らしい情報に出会います。<br /> http://dsas.blog.klab.org/archives/51117561.html<br /><br />早速試してみましたが、最近 SoftBank の URL や構造が変わったようで情報が取れません。<br />そこで Net::CIDR::MobileJP とそのプラグインのソースを見てみると数行の洗練されたコードが見つかります。<br /><br />これなら参考にして作れると思いますが、待てよと。<br />この行為はいろんな人が同じことやってるんだろうなーとか、またキャリアのサイトの構造が変わったらやだなーとか。<br /><br />やっぱり公共性の高いスクレイピングできるまとめページがほしい。<br />というわけで Wikipedia に恐る恐るページを作っちゃいました。<br /><br />決してスクレイピングしやすくは無いですが、ひとまず。。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com1tag:blogger.com,1999:blog-952281200000592126.post-12731912922128264742009-11-19T17:28:00.002+09:002009-11-19T18:07:45.145+09:00LDReader 記事詳細の WebView をちょっと修正したりLDReader 0.0.9 をリリースしました。<br />・記事詳細の WebView でリンクをクリックしたときにダイアログ表示<br />・ProgressDialog の表示後の処理を修正<br /><br /><span style="font-weight:bold;">記事詳細の WebView でリンクをクリックしたときにダイアログ表示</span><br /><br />記事詳細では間違ってリンクをクリックしてしまってブラウザが起動してしまい、非常にストレスを感じていましたが確認用のダイアログを表示することで少し軽減されるようにしました。ソースはこちら。<br /><pre class="code">// NOTE: WebView#setWebViewClient を使用<br />bodyView.setWebViewClient(new WebViewClient() {<br /> @Override<br /> public boolean shouldOverrideUrlLoading(WebView view, final String url) {<br /> // NOTE: 設定でリンクを無効にした場合はそのまま終了<br /> if (ReaderPreferences.isDisableItemLinks(getApplicationContext())) {<br /> return true;<br /> }<br /> // NOTE: ダイアログ表示<br /> new AlertDialog.Builder(ItemActivity.this)<br /> .setTitle(R.string.msg_confirm_browse)<br /> .setMessage(url)<br /> .setPositiveButton("OK", new DialogInterface.OnClickListener() {<br /> public void onClick(DialogInterface dialog, int whichButton) {<br /> // NOTE: OK がおされたら Intent を発行<br /> Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));<br /> intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);<br /> startActivity(intent);<br /> }<br /> })<br /> .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {<br /> public void onClick(DialogInterface dialog, int whichButton) {<br /> }<br /> }).show();<br /> return true;<br /> }<br />});<br /></pre><br />WebViewClient#shouldOverrideUrlLoading は true を返すことで、WebView の標準を動きをオーバーライドすることができます。<br /><br /><span style="font-weight:bold;">ProgressDialog の表示後の処理を修正</span><br /><br />ProgressDialog を表示後に Handler#post を使っていた箇所をすべて Thead を使うようにしました。<br />実はここ、最初は Thread だったんですが Handler は post の他に postDelayed という遅延実行のメソッドも持っており、Thread のような動きをするので、「あ。こっちでいいのか」とつい思い込みをしてました。<br /><br />意図しているのは<br /><pre> プログレスバー表示<br /> ↓<br /> 重い処理<br /> ↓<br /> 終了</pre><br />だったんですが、Handler#post を使うと<br /><pre> 重い処理<br /> ↓<br /> プログレスバー表示(一瞬)<br /> ↓<br /> 終了</pre><br />になっちゃいます。<br /><br />ここに詳しい解説がのってました!<br />http://www.adamrocker.com/blog/261/what-is-the-handler-in-android.html<br /><br /><pre class="code">// NOTE: 修正前<br />final Handler handler = new Handler();<br />final ProgressDialog dialog = new ProgressDialog(this);<br />dialog.show();<br />handler.post(new Runnable() {<br /> public void run() {<br /> // NOTE: ここで遅い処理<br /> // (some code)<br /> // NOTE: 終わったら ProgressDialog を閉じる<br /> handler.post(new Runnable() {<br /> public void run() {<br /> dialog.dismiss();<br /> }<br /> });<br /> }<br />});</pre><br /><pre class="code">// NOTE: 修正後<br />final Handler handler = new Handler();<br />final ProgressDialog dialog = new ProgressDialog(this);<br />dialog.show();<br />new Thread() {<br /> public void run() {<br /> // NOTE: ここで遅い処理<br /> // (some code)<br /> // NOTE: 終わったら ProgressDialog を閉じる<br /> handler.post(new Runnable() {<br /> public void run() {<br /> dialog.dismiss();<br /> }<br /> });<br /> }<br />}.start();</pre>もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-13621342816942071102009-11-16T19:28:00.004+09:002009-11-16T20:23:57.943+09:00LDReader フィード一覧の ListView, ListAdapter にハマるども。こっそり version 0.0.8 までバージョンアップしました。<br /><br /><span style="font-weight:bold;">[0.0.6]</span> ListView とデータベースとの不整合でたまに例外発生する問題を解消したつもりが失敗。<br /><span style="font-weight:bold;">[0.0.7]</span> android.widget.CursorAdapter を使うことで修正、等<br /><span style="font-weight:bold;">[0.0.8]</span> フィード一覧からピン一覧を開くと、背景が真っ暗になる問題を修正。<br /><br /><br /><span style="font-weight:bold;">[0.0.7] での主な変更</span><br /><br />version 0.0.7 以前では、操作していると次のような例外がたまに発生しました。<br /><pre class="code">java.lang.IllegalStateException:<br /> The content of the adapter has changed but ListView did not receive a notification.<br /> Make sure the content of your adapter is not modified from a background thread, but only from the UI thread.</pre><br />この例外はデータベースのレコード数と ListView のレコード数に違いがあるときに発生します。<br />LDReader はバックエンドのサービスでレコードを挿入したりしているので自前で色々していたのですが、付け焼刃ではどうしてもこの例外を抑えることができませんでした。<br /><pre class="code">java.lang.Object<br /> ↳ android.widget.BaseAdapter<br /> ↳ android.widget.CursorAdapter<br /> ↳ android.widget.ResourceCursorAdapter</pre><br />BaseAdapter を継承してゴニョゴニョしていたのですが、探してみると CursorAdapter といクラスがあるではありませんか!<br /><br />ソースをのぞいて見るとデータベースの更新通知を受け取ってクエリを再発行し、ListView との不整合を解消しているようです。さらに ResourceCursorAdapter では、行ごとの View をリソースを使ってカスタマイズしている場合にピッタリなようです。ApiDemos を見ながら四苦八苦して作ったのですが、まだまだ知らないことがたくさんありそうです。<br /><br />ちゅうわけで主な変更はこちら。<a href="http://code.google.com/p/ldreader/source/diff?spec=svn5&r=5&format=side&path=/trunk/src/org/jarx/android/livedoor/reader/SubscriptionActivity.java">SubscriptionActivity (diff r5)</a><br /><br /><br /><span style="font-weight:bold;">[0.0.8] での主な変更</span><br /><br />Cursor#deactivate を自前でやっていたのですが、Activity#managedQuery を使っていれば deactivate と requery は勝手にやってくれるようです。削除しました。<br /><br />ちゅうわけで主な変更はこちら。<a href="http://code.google.com/p/ldreader/source/diff?path=/trunk/src/org/jarx/android/livedoor/reader/SubscriptionActivity.java&format=side&r=6">SubscriptionActivity (diff r6)</a>もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-43826987763056697232009-11-15T02:34:00.007+09:002009-11-15T03:22:14.831+09:00LDReader ピンに対応しました<a href="/2009/11/android-ldreader.html">関連記事</a><br /><br />ども。LDReader をピンに対応させて ver 0.0.5 としました。このブログにコメントいただいちゃったので^ ^<br />以下スナップショットです。<br /><br /><div style="width:100%;"><div style="float:left;"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilS89zOgzNm9PnOFaJ3ggPdUdqogUI5x-3r5KYIJbkpKf5A0_Baq1BsA0_Z7145q-ZMGbRV7J-Va3NEnwwbVe0WhvEMc3aNQ4f73IC-P7-DqKBhBexlWqLRAFfv-DrBu3mrj_LmsX80tk/s1600-h/01.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 215px; height: 320px; float:left;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilS89zOgzNm9PnOFaJ3ggPdUdqogUI5x-3r5KYIJbkpKf5A0_Baq1BsA0_Z7145q-ZMGbRV7J-Va3NEnwwbVe0WhvEMc3aNQ4f73IC-P7-DqKBhBexlWqLRAFfv-DrBu3mrj_LmsX80tk/s320/01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5404014684015186178" /></a></div><div style="float:left; margin-left:10px;"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7sI7GTGSsmXu6fVyBjg7CW0gHJ8_qpi5rmLEjFUg6FNlcAFDKDmlSJGKA4z4OegwL_mhJqqb4KKPWlcb7ydecVJmXXv2niSnYGf8_MFND9IvVn8ejiTL1FYvZ6Jokh4XcOMS7C8dWnmk/s1600-h/02.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 214px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7sI7GTGSsmXu6fVyBjg7CW0gHJ8_qpi5rmLEjFUg6FNlcAFDKDmlSJGKA4z4OegwL_mhJqqb4KKPWlcb7ydecVJmXXv2niSnYGf8_MFND9IvVn8ejiTL1FYvZ6Jokh4XcOMS7C8dWnmk/s320/02.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5404014681589161266" /></a></div></div><br /><div style="clear:left;"></div><br />ちょと見づらいですが、記事タイトル右にピンアイコンがあります。このアイコンをクリックするかメニューから「Pin」を選択するとピンをつけたり外したりできます。<br /><br />ピンは本家同様に一覧でみることができ、ブラウザで閲覧することができます。<br />その他、記事 1件の URL をクリップボードにコピーしたり、全件の URL リストをメールで送れるようにしときました。<br /><br />簡単なバージョンアップですが、今回は少し悩んだ点があります。<br /><br />単純に実装するとすれば、ピン操作は常にサーバに対して追加や削除を実行し、一覧は最新をサーバから持ってくれば良いです。しかし、これだと地下鉄のようなオフライン環境ではピンを追加することができません。<br /><br />そこで次のような pin テーブルを作成して、ユーザの操作をまず action として保存し、それから通信を行うようにしました。追加の場合は 1:ACTION_ADD, 削除の場合は 2:ACTION_REMOVE, サーバから取得した場合は 0:ACTION_NONE とします。<br /><pre class="code">create table if not exists pin (<br /> _id integer primary key,<br /> uri text,<br /> title text,<br /> action integer, -- 0:ACTION_NONE, 1:ACTION_ADD, 2:ACTION_REMOVE<br /> created_time integer<br />)</pre><br />ピンの追加はロジックはこんな感じ。<br /><pre class="code">public boolean pinAdd(String uri, String title)<br /> throws IOException, ReaderException {<br /> if (!isLogined()) {<br /> login();<br /> }<br /> try {<br /> ContentResolver cr = this.context.getContentResolver();<br /> // NOTE: 事前に uri が重複するピンアクションを削除<br /> cr.delete(Pin.CONTENT_URI, Pin._URI + " = ? and " + Pin._ACTION <br /> + " > " + Pin.ACTION_NONE, new String[]{uri});<br /> // NOTE: ピンアクションを追加。<br /> ContentValues values = new ContentValues();<br /> values.put(Pin._URI, uri);<br /> values.put(Pin._TITLE, title);<br /> values.put(Pin._ACTION, Pin.ACTION_ADD);<br /> values.put(Pin._CREATED_TIME, (long) (System.currentTimeMillis() / 1000));<br /> Uri pinUri = cr.insert(Pin.CONTENT_URI, values);<br /> // NOTE: 端末がオフラインの場合はここで終了<br /> if (!isConnected()) {<br /> return true;<br /> }<br /> // NOTE: サーバと http 通信。失敗したら IOException などがスローされる。<br /> boolean success = this.client.pinAdd(uri, title);<br /> if (success) {<br /> // NOTE: 通信が成功したら既存の重複ピンを削除して、<br /> // 1:ACTION_ADD を 2:ACTION_NONE に更新。<br /> cr.delete(Pin.CONTENT_URI, Pin._URI + " = ? and " + Pin._ACTION<br /> + " = " + Pin.ACTION_NONE, new String[]{uri});<br /> values.put(Pin._ACTION, Pin.ACTION_NONE);<br /> cr.update(pinUri, values, null, null);<br /> }<br /> return success;<br /> } catch (ParseException e) {<br /> throw new ReaderException("json parse error", e);<br /> }<br />}<br /></pre><br />このままでは、端末がオフラインのときには 1:ACTION_ADD と 2:ACTION_REMOVE が溜まります。<br />そこで回線がオンラインになったら実行される処理にピンの同期処理を追加します。<br /><pre class="code">public int syncPins() throws IOException, ReaderException {<br /> if (!isLogined()) {<br /> login();<br /> }<br /> ContentResolver cr = this.context.getContentResolver();<br /> // NOTE: 1:ACTION_ADD と 2:ACTION_REMOVE の検索。<br /> String where = Pin._ACTION + " > " + Pin.ACTION_NONE;<br /> String order = Pin._ID + " asc";<br /> Pin.FilterCursor cursor = new Pin.FilterCursor(<br /> cr.query(Pin.CONTENT_URI, null, where, null, null));<br /> try {<br /> while (cursor.moveToNext()) {<br /> // NOTE: 順番にピンアクションを再現<br /> Pin pin = cursor.getPin();<br /> if (pin.getAction() == Pin.ACTION_ADD) {<br /> pinAdd(pin.getUri(), pin.getTitle());<br /> } else {<br /> pinRemove(pin.getUri());<br /> }<br /> Uri uri = ContentUris.withAppendedId(Pin.CONTENT_URI, pin.getId());<br /> cr.delete(uri, null, null);<br /> }<br /> } finally {<br /> cursor.close();<br /> }<br /> // NOTE: サーバからピン一覧を全件取得。<br /> PinsHandler pinsHandler = new PinsHandler();<br /> try {<br /> this.client.handlePinAll(pinsHandler);<br /> } catch (ParseException e) {<br /> throw new ReaderException("json parse error", e);<br /> }<br /> return pinsHandler.counter;<br />}</pre><br />関連するソースはこのへん。<br />@see <a href="http://code.google.com/p/ldreader/source/browse/trunk/src/org/jarx/android/livedoor/reader/Pin.java">org.jarx.android.livedoor.reader.Pin</a><br />@see <a href="http://code.google.com/p/ldreader/source/browse/trunk/src/org/jarx/android/livedoor/reader/PinActivity.java">org.jarx.android.livedoor.reader.PinActivity</a><br />@see <a href="http://code.google.com/p/ldreader/source/browse/trunk/src/org/jarx/android/livedoor/reader/ReaderManager.java">org.jarx.android.livedoor.reader.ReaderManager</a><br /><br />このピンの実装によってずいぶんと使い勝手が向上したように感じます。ありがとうございます。<br />次はウィジェットを実装予定です。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-37906396443723484912009-11-11T21:17:00.006+09:002009-11-11T21:41:36.973+09:00LDReader の画面遷移<a href="/2009/11/android-ldreader.html">前記事</a><br /><br />LDReader の画面遷移は少し変かも知れません。<br /><br />現在の画面遷移は<br /> フィード一覧 → 未読の最新の記事詳細 → (未読がなければ)既読の最新の記事詳細<br />となっています。<br /><br />普通にやるなら<br /> フィード一覧 → 記事一覧 → 記事詳細<br />ですかね。<br /><br />記事一覧画面が無いです。個人的な需要の ”とにかく未読をチェックしたい”で作り始めたのでこうなっちゃってますが、どうでしょう?もし使いづらいとかコメントたくさんもらったら変えたいと思います。<br /><br />フィード一覧画面:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaRi-AIShK4k9Ofdvv1eRXZZozdy9mjeJIxOIuhyphenhyphenOmoEb3YcXK1S_r5uBO3d8Pd6n7DMAaSI4ewyuryiTyTD4M_MldEfmMuUbgK1O46Ot2AZYFmBXuxkzPLMqqRSwrYN4ir_LbxZQSxFw/s1600-h/subs.png"><img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 214px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaRi-AIShK4k9Ofdvv1eRXZZozdy9mjeJIxOIuhyphenhyphenOmoEb3YcXK1S_r5uBO3d8Pd6n7DMAaSI4ewyuryiTyTD4M_MldEfmMuUbgK1O46Ot2AZYFmBXuxkzPLMqqRSwrYN4ir_LbxZQSxFw/s320/subs.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5402823860007667666" /></a><br /><br />記事詳細画面:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbTRz0aQlAKvnauoZvTbVtgOokifhBKeWgregrRm91bxb81Y2ODQwh9oUpqQNy5RJMXBDK1XffibPh_5yf5RnTsW4gIFvRIFryKPAHXHRHuCCMLbM7u_OlIhw8FCanIhldY8hv-RoFd8I/s1600-h/item.png"><img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 214px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbTRz0aQlAKvnauoZvTbVtgOokifhBKeWgregrRm91bxb81Y2ODQwh9oUpqQNy5RJMXBDK1XffibPh_5yf5RnTsW4gIFvRIFryKPAHXHRHuCCMLbM7u_OlIhw8FCanIhldY8hv-RoFd8I/s320/item.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5402825020128162226" /></a>もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-6013922641245368552009-11-11T19:08:00.006+09:002009-11-11T21:25:25.107+09:00android アプリ LDReader を マーケットに公開しましたandroid で動く LDReader という livedoor Reader クライアントを作ってみました。<br /><br />androlib.com: <a href="http://jp.androlib.com/android.application.org-jarx-android-livedoor-reader-zACz.aspx">マーケット紹介ページ</a><br />code.google.com: <a href="http://code.google.com/p/ldreader">Google Code プロジェクトページ</a><br /><br />今のとこ 30件ぐらいダウンロードがあります。(紹介ページの星 4つは、つい自分で押してしまいましたw)<br /><br />この LDReader については何回か書いていきたいなと思います。<br /><br /><span style="font-weight:bold;">LDReader とは:</span><br />定期的にサーバと通信して端末内に情報を保存し、オフラインで RSS の内容を閲覧できるのが特徴です。端末で独自に既読/未読の管理を行います。表示形式はフラットのみです。<br /><br /><span style="font-weight:bold;">作った経緯:</span><br />android を入手して最初にほしいなと思ったアプリケーションで、探しても良さそうなのが見つけられなかったためです。地下鉄での移動が多いのでオフラインでも更新をチェックできることを重要視しました。<br /><br /><span style="font-weight:bold;">Google Code でソースコードを公開した理由:</span><br />android の SDK やその特性は少しクセがあって作るのに結構苦労しました。つまづいた点の紹介をしたかったのと、ここはこれでいいのか?という疑問を持ったままリリースしたのでツッコミがほしかったのが一番の理由です。<br />オープンソース版の FastLadder もあるし、作ったのものをオープンにしてみたかったのもありますね^^<br /><br /><span style="font-weight:bold;">追加で考えている機能:</span><br />・未読数と最新記事の何件かが表示されるウィジェットに対応する。<br />・記事検索ができるように。<br />・キャッシュ記事のクリア。<br />・表示形式を livedoor Reader 本家と同じく、フォルダやレートに対応する。<br />・FastLadder に対応する。(fastladder.org と任意のドメイン)<br />・ピンをつけれるようにする。<br />・レートをつけれるようにする。<br />・android ではあまり見ないフィードを隠す。<br /><br />開発元は株式会社トライキャッチとしました。<br />パッケージのドメインは org.jarx.android を使ってます。オープンソースモノはこのドメインを使っていこうと思ってます。そのうちウェブページも充実させないと。。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com1tag:blogger.com,1999:blog-952281200000592126.post-19857675177073353722009-10-28T18:40:00.002+09:002009-10-28T18:44:52.127+09:00XML の Preference で Intent 呼び出しちょっとハマったのでメモ<br /><pre class="code"> <PreferenceScreen<br /> android:title="[タイトル]"<br /> android:summary="[サマリ]"><br /> <intent android:action="android.intent.action.VIEW"<br /> android:targetPackage="[パッケージ名]"<br /> android:targetClass="[パッケージ名].[クラス名]" /><br /> </PreferenceScreen><br /></pre>もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-92011503422636256412009-08-14T02:15:00.006+09:002009-10-28T18:45:35.111+09:00android ソース入手。(CentOS 5.3 yum git-1.5.4)簡単なことですが、すぐ忘れるのでメモ投稿です。<br /><br />android ソースを入手したくて repo コマンド実行しようとすると、git 1.5.4 以上が必要なんですって。<br /><pre class="code">repo init -u git://android.git.kernel.org/platform/manifest.git<br />fatal: git 1.5.4 or later required<br /></pre><br /><br />私は yum(rpm)派なんですが、rpmforge には 1.5.2 しかなくてどうしようと思ってたら git 本家に yum リポジトリがあったのでそれを使いました。<br />git は 1.5.4 の最新と思われるものを指定してます。<br /><pre class="code">wget http://kernel.org/pub/software/scm/git/RPMS/git.repo -O /etc/yum.repos.d/git.repo<br />yum install git-1.5.4.6-1<br /></pre><br /><br />あとは repo というスクリプトをダウンロードして<br /><pre class="code">mkdir ~/bin<br />wget http://android.git.kernel.org/repo -O ~/bin/repo<br />chmod 755 ~/bin/repo<br /></pre><br /><br />git config で user.email, user.name を設定して、repo init、repo sync でソースコードのダウンロードがはじまります。<br /><pre class="code">git config --global user.email "メアド"<br />git config --global user.name "名前"<br />repo init -u git://android.git.kernel.org/platform/manifest.git<br />repo sync<br /></pre>もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com1tag:blogger.com,1999:blog-952281200000592126.post-30307421611799607792009-07-03T16:46:00.005+09:002009-07-03T17:29:08.330+09:00論理削除とユニークキー制約ども。久々に時間ができたのでぼちぼち日記書いてみようかなと。<br />技術ネタですが。。<br /><br />データベース設計を行うとき、論理削除を採用することが多いです。<br />論理削除用のカラムとしては boolean 型もしくは小さい数値型を使います。<br /><pre class="code">create table item (<br /> id int not null,<br /> code varchar(40) not null,<br /> name varchar(240) not null,<br /> removed boolean not null default false,<br /> constraint item_pk primary key (id) /* ,<br /> constraint item_code_uk unique key (code) 本当はユニークキー制約をつけたいが。。 */<br />);</pre><br />item(商品)テーブルに一意な code(商品コード)という人が認識しやすい番号を任意に設定できるテーブルを設計するとき、ユニークキー制約をつけてしまうと一度論理削除した商品コードが使いまわせない問題が発生します。仕方なくデータベース側のユニークキー制約は外していました。<br /><br />このテーブルを使うアプリケーション側では、挿入、更新前にユニークキーチェックを行いますが、アプリケーションサーバが複数台あったり、バッチ処理が平行して実行される可能性があったりでなかなか厳密な整合性はとりづらいです。<br /><br />最近になってふと、論理削除用のカラムをユニークキー制約に含めてインクリメントすればいいじゃんと思いつきました。そして、主キーが 1つあるタイプならばインクリメントとかしなくてそのまま使えるなーと。<br /><pre class="code">create table item (<br /> id int not null, -- 0 以上の主キー<br /> code varchar(40) not null,<br /> name varchar(240) not null,<br /> removed int not null default 0,<br /> constraint item_pk primary key (id) ,<br /> constraint item_code_uk unique key (code, removed)<br />);</pre><br />論理削除するときは、<br /><pre class="code">update item set removed = id where id = ?</pre><br />通常の検索時には where 句に removed = 0 を指定します。<br /><pre class="code">select * from item where removed = 0</pre><br />まだ実践で使ってないですが多分大丈夫なはず。<br />いやースッキリした。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-24919592311710927752009-04-06T03:19:00.002+09:002009-04-06T03:26:10.776+09:00起業して1年1年が早いです。去年の4月に起業してから過去お付き合いのある会社、知人から仕事をいただいて何とかやってこれました。ありがとうございます。<br /><br />今年は大きな仕事を受注し、社員を増やしたいと思います。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com1tag:blogger.com,1999:blog-952281200000592126.post-50696082229220493602009-01-20T14:03:00.006+09:002009-01-20T18:24:03.167+09:00Java のフレームワークどうしましょあけましておめでとうございます。<br /><br />次の受託開発案件で Java を使うことになりそうなのでフレームワークの比較検討しています。<br /><br />私が今まで使ったことのあるフレームワークは<br />・Struts 1、2系 → EC など<br />・Tapestry 3系 → 業務システム<br />・Click Framework → 自社サービス(openidea.jp)<br />の 3つです。<br /><br />一言にフレームワークといっても色々な役割がありますが、私は次の 4つが主要な機能だと考えます。<br />・URI マッピング<br />・フォームのバリデーション<br />・テンプレート<br />・データベースアクセス<br /><br />表にするとこんな感じでしょうか。かなり昔の記憶で書いているものあって、正確でなかったり、当時スキル不足や調べ切れなかったものもありますが。。<br /><br /><table class="sheet" width="100%"><thead><tr><th><br /></th><th>URI マッピング</th><th>バリデーション</th><th>テンプレート</th><th>データベースアクセス</th></tr></thead><tbody><tr><th>Struts</th><td>サーブレットフィルタ *.do 標準</td><td>独自</td><td>JSP 2.0 標準</td><td>なし</td></tr><tr><th>Tapestry</th><td>サーブレットを /app などにマッピング</td><td>独自</td><td>独自 + ognl 形式</td><td>なし</td></tr><tr><th>Click Framework</th><td>サーブレットフィルタ *.htm 変更不可</td><td>独自</td><td>Velocity 標準</td><td>Cayenne、Spring サポート</td></tr></tbody></table><br /><br />この 3つはどれもぴったりハマることなく、気に入らないところは色々いじって使ってました。それぞれの使った感想を簡単にあげます。<br /><br />Struts:<br />・設定ファイルが面倒。設定ファイルにして便利だったことはほとんどない。<br />・*Form, *Action とたくさんのクラスができて管理が面倒。<br />・学習は比較的容易?情報がたくさんある。<br /><br />Tapestry:<br />・業務システムで採用したのが間違いだったかもしれないが、複雑な画面になるとイベントの制御がかなり難解になる。<br />・カスタムコンポーネントを作り始めると面白い。<br />・ページ制御側の学習コストは非常に高くつく。HTML テンプレートはスッキリ。<br /><br />Click Framework:<br />・拡張子 *.htm が固定で変更できないのが痛い。<br />・学習は容易。<br /><br />ほとんどのフレームワークはテンプレート部分や、データベースアクセスは他のプロダクトに変更することが可能です。実はここの選定が肝だったりします。別途テンプレートやデータベースアクセス部分のことは書きたいなと思います。<br /><br />これらの今まで使ってきたフレームワークの追加調査に加え、Wicket も調査したいです。Spring Framework、S2 など DIコンテナを含むものは今回はパスしようと思います。<br /><br />ここ 2、3年は Perl、PHP 案件がほとんどだったので忘れていましたが、Java 案件は<br />→ 新しいフレームワークの選定<br />→ 開発スタートしてから新しいフレームワークの細かい部分の調査ハマる<br />で遅延しがちなので要注意ですねー。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0tag:blogger.com,1999:blog-952281200000592126.post-78954215186881427262008-12-22T07:22:00.003+09:002008-12-22T07:36:32.426+09:00仕事でつながるサービスどもどもー。起業して半年以上たちますが、未だに一人で受託案件こなしてます。<br />今期は何とか黒字でしたが、そろそろこの一人受託開発スタイルから脱却しないとだめですね。<br /><br />つまり、社員を雇ったり仕事の一部を外注していきたいのですが。。<br /><br />そんなときに見つけたサービス「ランサーズ」さん。最近できた様子です。<br />http://www.lancers.jp/<br /><br />楽天ビジネスっぽい仕組みは、世の中に無ければ作りたいなーと思ってましたが、これ大変分かりやすくて良いです。どんどん発展していきそうな予感です。<br /><br />試しに仕事の依頼をしてみようかなー。もじゃhttp://www.blogger.com/profile/09064340083055620803noreply@blogger.com0