2008年12月22日月曜日

仕事でつながるサービス

どもどもー。起業して半年以上たちますが、未だに一人で受託案件こなしてます。
今期は何とか黒字でしたが、そろそろこの一人受託開発スタイルから脱却しないとだめですね。

つまり、社員を雇ったり仕事の一部を外注していきたいのですが。。

そんなときに見つけたサービス「ランサーズ」さん。最近できた様子です。
http://www.lancers.jp/

楽天ビジネスっぽい仕組みは、世の中に無ければ作りたいなーと思ってましたが、これ大変分かりやすくて良いです。どんどん発展していきそうな予感です。

試しに仕事の依頼をしてみようかなー。

2008年11月12日水曜日

mod_ktai バージョンアップ

mod_ktai がバージョンアップしたようです。

前回は mod_ktai おしいなぁという感想だったんですが、今度のバージョンは VirtualHost に対応しています。UTF-8 の PCサイトと Shift_JIS の携帯サイトが共存する環境でも問題なく使えるようになりました。

よかったよかった^ ^

これによって表示は mod_ktai に任せることができるので、あとは入力される 3キャリアの絵文字をノーマライズすればいいですね。

2008年10月24日金曜日

Shift_JIS で encodeURIComponent

Ajax が流行り始めてからほとんど UTF-8 でウェブアプリケーションを作っていたので気にならなかったのですが、Shift_JIS で作るとなるとちょっと面倒くさいです。

例えば script.aculo.us の Ajax.InPlaceEditor のコールバックとかで普通に書くと encodeURIComponent が UTF-8 エンコードを施すので、サーバ側で Shift_JIS として受け取れません。
new Ajax.InPlaceEditor(e, "/example/modify_text", {
callback: function(f, v) {
return "text=" + encodeURIComponent(v);
},
ajaxOption: {method: "post"}
}


そこで思いついたのが encodeURIComponent を 2回行う方法。これでサーバ側で個別にデコードしてやれば良いです。JavaScript を使って日本語パラメータを送信する必要があるところだけです。
new Ajax.InPlaceEditor(e, "/example/modify_text", {
callback: function(f, v) {
return "double_encoded_text=" + encodeURIComponent(encodeURIComponent(v));
},
ajaxOption: {method: "post"}
}


他に方法あるのかなぁ。。

2008年10月17日金曜日

mod_ktai を試しました

避けてきた携帯3キャリアの絵文字対応をすることになりました。

色々さがして mod_ktai を試しましたが、3つの不満点が・・・

1) 入力のフィルタはしてくれない
2) UTF-8 での閉じるダブルコーテーションが外れるバグ
3) ダブルコーテーションバグを回避しようとして、UTF-8 で提供している他のサービスと分けて、VirtualHost 内に AddOutputFilterByType を記述してみたら、docomo 用のレスポンスヘッダ application/xhtml+xml に反応しないんです。
そこで次のようにタイプを追加したら、docomo 端末でアクセスしても何故か PC 用の絵文字画像に変換されてしまう><
LoadModule ktai_emoji_module modules/mod_ktai_emoji.so
KtaiEmojiConvertMode auto
KtaiEmojiConvertNativeEmojiDocomo 0
KtaiEmojiEnableAddGuidOn 0
KtaiEmojiHasIconFile 1
KtaiEmojiIconDir /img/emoji

<VirtualHost *:80>
ServerName example.com
AddOutputFilterByType KTAI_EMOJI_OUTPUT_FILTER text/html application/xhtml+xml
</VirtualHost>


おしい。

2008年9月15日月曜日

簡単クラスパス設定 シェルスクリプト

Java のクラスパス、設定するの正直面倒くさいです。そこで少しだけ便利なスクリプトを使っています。使い方はこんな感じ。
Usage: . classpath.sh (option) [dir]
(option)
-a add CLASSPATH
-r recursive

指定したディレクトリを走査して jar ファイルだけを環境変数 CLASSPATH に追加します。以下ソースです。
#!/bin/sh

if [ -z "$1" ]; then
echo "Usage: . classpath.sh (option) [dir]"
echo "(option)"
echo " -a add CLASSPATH"
echo " -r recursive"
exit 0
fi

_recursive=0
_add=0
dir=

parse_arguments() {
for arg do
case "$arg" in
-r) _recursive=1 ;;
-a) _add=1 ;;
*) dir=$arg ;;
esac
done
}

classpath() {
for i in "$1"/*; do
if [ -d "$i" -a $_recursive -eq 1 ]; then
classpath "$i"
elif [ "${i##*.}" = "jar" ]; then
_classpath=$_classpath:$i
fi
done
}

if [ $_add -eq 1 ]; then
_classpath=$CLASSPATH
if [ -z "$_classpath" ] ; then
_classpath=.
fi
fi

parse_arguments $*
classpath "$dir"
export CLASSPATH=$_classpath
unset _classpath

2008年8月29日金曜日

上流工程と下流工程の別の呼び方

いわゆるウォーターフォール開発の工程で、企画から要件定義や設計は「上流工程」、プログラミングからテストや運用は「下流工程」にあたります。私はこの言葉あまり好きではありません。

水が流れる → 川 をイメージして出来たのでしょうが、どうも下流はイメージが良くないです。
日本だけですかね?これって。

全工程を半分に割ってそれぞれを呼べばよいのですが、
  • 前半工程、後半工程
  • 往路工程、復路工程
むむむ。どれもしっくりこないですねぇ。。

2008年8月8日金曜日

プログラミングコードの貼り付け(3)

前記事:プログラミングコードの貼り付け(2)

現在はこれに落ち着いてます。
pre.code {
background: #eee;
border: 1px solid #ddd;
width: 95%;
padding: 5px;
font-size: 85%;
white-space: -moz-pre-wrap; /* Mozilla */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
white-space: pre-wrap; /* CSS3 */
word-wrap: break-word; /* IE 5.5+ */
overflow: auto;
}

2008年8月7日木曜日

ant-velocity

どもー。

静的コンテンツだけのページを作るとき、どーしてもヘッダフッタをコピペで何度も書きたくないので調べたところ、Apache Jakarta プロジェクトから次の 2つのプロジェクトを見つけました。

・Apache Velocity Anakia
・Apache Velocity Texen

どちらも、Velocity というテンプレートエンジンを使って Ant のカスタムタスクを提供しています。それぞれすばらしいツールなんですが、ちょっと気に入りません。

Apache Velocity Anakia ... XML をソースとして、vsl を記述する必要がある。
Apache Velocity Texen ... コントロールファイルを記述する必要がある。

Ant と Velocity 使うならもっとシンプルにできそうなのに。。そう考えて作っちゃいました!

ant-velocity-1.0.0

もっともシンプルな Ant ビルドファイルはこんな感じ。カスタムタスクを定義して、todir、fileset を指定するだけ。tmpl ディレクトリ以下の html ファイルを Velocity テンプレートとして評価し、htdocs に出力します。
<taskdef name="velocity" classname="org.jarx.ant.VelocityTask"
classpathref="classpath" />
<velocity todir="htdocs" encoding="UTF-8">
<fileset dir="tmpl" includes="**/*.html">
</velocity>

変数を指定したい場合はこう。
<velocity todir="htdocs" encoding="UTF-8">
<fileset dir="tmpl" includes="**/*.html">
<parameter name="baseurl" value="http://jarx.org/" />
</velocity>

変数をプロパティファイルに記述したい場合はこう。
<velocity todir="htdocs" encoding="UTF-8"
propertyFile="resource.properties">
<fileset dir="tmpl" includes="**/*.html">
</velocity>

ボーダーテンプレートを使いたい場合はこう。
<velocity todir="htdocs" encoding="UTF-8"
borderTmplFile="tmpl/border-template.html">
<fileset dir="tmpl" includes="**/*.html">
</velocity>

ボーダーテンプレートはこんな感じ。#parse(${path}) の部分に fileset で指定したテンプレートの内容が入ります。
<html>
<head><title>border template example</title></head>
<body>#parse(${path})</body>
</html>

2008年8月4日月曜日

こういうの作りたかった - キャリー[独立]

まだ会社に勤めてたころ、空いた時間を有効活用してプログラムのバイトしたいなーなんて思ってた時期がありました。イメージはメソッド1つ単位からバイトできるサービスです。

この キャリー[独立] というサービスは、主にフリーのエンジニアのためのもっと大きな単位(人月)の案件を紹介しています。これでもいいかなーなんて思います。
http://f-engineer.jp/search/

困ったら使おうw

こういうの作りたかった - コード*(なにがし)

プログラムに行き詰ったら誰かに相談したい。そう思うことは良くあります。

私がずっと作りたかったものにすごく近いイメージのサービスを発見。
http://code.nanigac.com/

ちょっと前からウォッチしてますが、よさそう。今度投稿してみよう。

そろそろ案件管理が大変に。。

そろそろ案件管理が大変になってきた。

会社設立してから今までは Excel で管理してたけど、来月から仕入れも発生するし。。何かいい ASP とかないかなー。

こういうとき、プログラマあがりの私は「つくろうかな」という思考になっちゃうので自重しなければ。。

2008年8月1日金曜日

svn:ignore にハマった

svn:ignore が有効になるのは、「SVN 管理下にないディレクトリ・ファイル」に対してなんですね。ちょっと勘違いしてました。

2008年7月3日木曜日

jar ファイルの場所を見つける方法

自作の jar ファイルに含まれたクラスを使うとき、その jar のパスやディレクトリを知りたいと思ったことありませんか?

かなり昔に作った自作ライブラリの一部ですが、最近、思い出したように使ってみて便利だったので紹介します。
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.ProtectionDomain;
import java.security.CodeSource;

public class ClassUtils {

public static URL findClassLocation(Class c)
throws SecurityException {
Package p = c.getPackage();
String className;
if (p != null) {
className = c.getName().substring(p.getName().length() + 1);
} else {
className = c.getName();
}
URL location = c.getResource(className + ".class");
if (location == null) {
// NOTE: 昔 Tomcat の WEB-INF/lib とかの jar を探すときはこの
// 処理に入ったのだが。。。今は不要かも。
ProtectionDomain domain = c.getProtectionDomain();
CodeSource source = domain.getCodeSource();
if (source != null) {
location = source.getLocation();
}
}
return location;
}

private static File findBaseDirectory(URL classLocation)
throws IOException {
if (classLocation == null) {
return null;
}
String file = classLocation.getFile();
if (classLocation.getProtocol().equals("jar")) {
int i = file.lastIndexOf('!');
if (i != -1) {
file = file.substring(0, i);
}
return findBaseDirectory(new URL(file));
}
File dir = new File(URLDecoder.decode(file,
System.getProperty("file.encoding")));
if (!dir.isDirectory()) {
dir = dir.getParentFile();
}
return dir;
}

public static File findBaseDirectory(Class c)
throws SecurityException, IOException {
return findBaseDirectory(findClassLocation(c));
}

public static void main(String[] args) throws Exception {
System.out.println(findBaseDirectory(java.lang.String.class));
}
}


これがあれば xxx.home などのシステムプロパティ渡さずに済むことが多々あると思います。
sen などもこれに対応してくれればなぁ。。。

2008年6月30日月曜日

プログラミングコードの貼り付け(2)

前記事:プログラミングコードの貼り付け
またまた Blogger の仕様が元に戻りました?今日確認したら pre の中に br が入らなくなったようなので、以下の CSS を取り除きました。
pre.code br {
display: none;
}

どうせやるなら以下のような書き方のほうが Blogger 云々に右往左往しなくて良かったかも。
pre.code br+br {
display: run-in;
}

2008年6月27日金曜日

Java めんどくさっ

先日 OpenIdea.jp というサービスを初めた記事を書きました。Java で出来ています。でも作ってる途中、感じました。。。

Java めんどくさっ

ちゃんと書くにはいい言語で、総合的には最も好きなんですが、何せ面倒くさい。

そんなとき以下の記事見つけました。
COBOL のように死んだ言語 ~ Java は置き換えられる時期に来ているのか

ふむ。なるほど。。。とりあえず Groovy はじめてみよう。

プログラミングコードの貼り付け

今日から Blogger の仕様変わりました?何故か pre で囲んでいたプログラミングコードが余計に改行されるようになったんで次の対策をとりました。

元々以下のような CSS を [レイアウト] - [HTML の編集] から仕込んで置いたのですが、
pre.code {
background: #eee;
border: 1px solid #ddd;
width: 95%;
padding: 5px;
font-size: 85%;
word-wrap: break-word; /* fix for long text breaking sidebar float in IE */
overflow: auto; /* fix for long non-text content breaking IE sidebar float */
}

この CSS を追加することで pre.code 中の br を無効にできました。良かった良かった。
pre.code br {
display: none;
}

参考にさせていただいたのは次の 2サイトです。
<br>の連打をuser CSSで無効にする。 - ま゚゚
pre 要素のスタイル定義とマークアップ

2008年6月30日 続きの記事へ

2008年6月26日木曜日

サーブレットコンテナ(Tomcat)とリバースプロクシを組み合わせた時の問題解決

Tomcat とリバースプロクシを組み合わせた時に微妙な問題に直面しました。その問題の解決方法をメモしておきます。

状況としては、Tomcat は http://localhost:8080/app/ のようにコンテキストパス /app で動作しています。
/app で動作しているアプリケーションに http://app.example.com/ という URL でリバースプロクシの設定を行います。
Tomcat は他のアプリケーションも動作しているので、ルートコンテキストは用いることができません。
http://app.example.com/ (Apache)

(リバースプロクシ)

http://localhost:8080/app/ (Tomcat)

まずはこんな感じで設定しました。問題は 2つあります。
ProxyPath / http://localhost:8080/app
ProxyPassReverse / http://localhost:8080/

1つ目は Cookie 問題です。Tomcat の発行する Set-Cookie の Path 属性が /app となり、ブラウザが / へのアクセスのときにはリクエストヘッダに Cookie をつけて送信しません。
Set-Cookie: JSESSIONID=XXXXXXXXXXXXX; Path=/app

ここでは、ProxyPassReverseCookiePath ディレクティブを用いて、Set-Cookie の Path属性 /app を / に置換します。
ProxyPath / http://localhost:8080/app/
ProxyPassReverse / http://localhost:8080/
ProxyPassReverseCookiePath /app /

これでブラウザへは次のヘッダが返されます。
Set-Cookie; JSESSIONID=XXXXXXXXXXXXX; Path=/

2つ目はコンテキストパス問題です。Tomcat をルートコンテキストで動かしたり、ルートコンテキストは他で使っていても、ポートを変えてもう 1つ Tomcat を起動すれば問題はすぐ解決するのですが、ここは敢えて別の方法をとってみます。

やりたいことは、次の 2つの URL どちらからアクセスしても同じように動くことです。
http://app.example.com/
http://localhost:8080/app/

今回は以下のような方法を使ってみました。

まず、リバースプロクシの設定に加え X-Context-Path という独自のヘッダを付与します。
RequestHeader set X-Context-Path ""
ProxyPath / http://localhost:8080/app/
ProxyPassReverse / http://localhost:8080/
ProxyPassReverseCookiePath /app /

これで Tomcat は、リバースプロクシからきたリクエストを X-Context-Path というヘッダで判断することができます。
次にサーブレットフィルタを作成し、リクエストオブジェクトをラップして、getContextPath メソッドをオーバーライドします。
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
doFilter((HttpServletRequest) req, (HttpServletResponse) res, chain);
}
public void doFilter(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
// NOTE: X-Context-Path を受け取って、null で無ければその値を
// コンテキストパスとして使用する。
// ※実際は XSS などを埋め込まれないように文字列チェックする必要
// あります。
final String contextPath = req.getHeader("X-Context-Path");
if (contextPath != null) {
req = new HttpServletRequestWrapper(req) {
public String getContextPath() {
return contextPath;
}
};
}
chain.doFilter(req, res);
}

この他に getServerPort や getRequestURI など変更すべき箇所がたくさんありますが省略。。こうしておけば、http://localhost:8080/app/ にアクセスしたときも http://app.example.com/ にアクセスしたときも期待通りに動作します。

会社案内資料

今日、知り合いと一緒に他社をまわったときに気づきました。
わが社には会社案内資料がない。作らねば。。

2008年6月25日水曜日

OpenIdea.jp

OpenIdea.jp というサービスを作ってみました。

作った理由:
  • 日ごろ思いつくアイデアを公開したかった。(普段はメールの下書きでためていました。)
  • 何でも良いからサービスを 1つ公開したかった。
苦手な HTML コーディングや、ロゴなども自分でやってます。
Java、ClickFramework、DbUtils、MySQL などで動いてます。まだ検索やランキングも動いてなかったり。。

今のとこ、何ら新しいことはないのですが、実験場として色々追加していく予定です。

今後の予定:
  • アイデアグラフ(最初はツリー表示でいいかな)
  • OpenID 2.0 対応
  • メール投稿
まだプレスリリースとかしてません。もうちょっと触ったらやろうと思います。

2008年6月4日水曜日

memcached でキーの列挙(2)

前回(memcached でキーの列挙)の続きです。サンプルの Perl コードです。ちょこっといじればイベント駆動のモジュールが作れそう。。

my $size = 1000;
my $cache = Cache::Memcached->new({
servers => ['localhost:11211'],
});
my $slabs = $cache->stats('slabs');
my @stat_ids = ();
for my $host (keys %{$slabs->{hosts}}) {
my $slabs_text = $slabs->{hosts}->{$host}->{slabs};
my @lines = split("\n", $slabs_text);
for my $line (@lines) {
($line =~ /STAT ([0-9]+):chunk_size [0-9]+/) or next;
push(@stat_ids, $1);
}
}
for my $stat_id (@stat_ids) {
my $cmd = "cachedump $stat_id $size";
print "[$cmd]\n";
my $cachedump = $cache->stats($cmd);
for my $host (keys %{$cachedump->{hosts}}) {
my $cachedump_text = $cachedump->{hosts}->{$host}->{$cmd};
my @lines = split("\n", $cachedump_text);
for my $line (@lines) {
($line =~ /^ITEM (.+) \[(.+) b; (.+) s\]/) or next;
my ($key, $b, $s) = ($1, $2, $3);
print "\t$key [$b bytes]\n";
}
}
}

2008年6月3日火曜日

memcached でキーの列挙

memcached で、キーの列挙ができないか調査していたところ、PHPで実現している方のブログ記事を見つけました。( http://blog.cles.jp/item/2141 )

これ Perl の Cache::Memcached でできるだろうと思って挑戦したのですが、何故か stats cachedump が動作しないんです。

そこで telnet を使って直接コマンドをたたいてみることにしました。まずは接続。。
> telnet localhost 11211

次に stats slabs コマンドを実行してみます。
stats slabs
STAT 1:chunk_size 80
STAT 1:chunks_per_page 13107
STAT 1:total_pages 1
STAT 1:total_chunks 13107
STAT 1:used_chunks 13107
STAT 1:free_chunks 0
STAT 1:free_chunks_end 13065
STAT 2:chunk_size 100
STAT 2:chunks_per_page 10485
STAT 2:total_pages 1
STAT 2:total_chunks 10485
STAT 2:used_chunks 10485
STAT 2:free_chunks 0
STAT 2:free_chunks_end 10447
.....
STAT active_slabs 16
STAT total_malloced 16763844
END

この出力結果のうちの STAT {数字} の部分を使って stats cachedump コマンドを実行します。STAT {数字} は連番ではないみたいなので注意が必要です。
stats cachedump {数字} {ダンプするキーの最大数}
ITEM {キー} [{統計情報}]
.....
END

おお。キーの列挙できるじゃん。ちゅうことで Cache::Memcached のソースみたら stats メソッドは引数を最初の 1個しか使ってないのか。
# 中身も見ずにこういう呼び出しをしてしまってた。
$cache->stats('cachedump', 1, 100);

# こうすりゃ良かったんすね。
$cache->stats('cachedump 1 100');

memcached でキーの列挙(2)へ続く

2008年4月17日木曜日

会社案内サイト

知り合いの "タローさん" のお力添えで会社案内サイトができました。ありがとうございます m(_ _)m

http://trycatch.jp/

遊び心のある Flash ですw 肝心の内容がまだまだ薄っぺらいですが、ニュースや事例で埋めていきたいと思います。

2008年4月10日木曜日

意外と面倒くさい autolink

どうも。こんにちは。今回は Wiki などに使われるテキスト中の URL を自動的にリンクする方法について書いてみたいと思います。

Java で正規表現を使って実装するとこんな感じでしょうか。
import java.util.regex.*;
private static Pattern linkPattern = Pattern.compile(
"(https?)(://[\\p{Alnum}\\+\\$\\;\\?\\.%,!#~*/:@&=_-]+)");
public static String link(String s, int length, String suffix) {
StringBuffer buff = new StringBuffer((int) (s.length() * 1.2f));
Matcher m = linkPattern.matcher(s);
while (m.find()) {
String g = m.group();
if (length > 0 && g.length() > length) {
g = g.substring(0, length);
if (suffix != null) {
g += suffix;
}
}
g = m.quoteReplacement(g);
m.appendReplacement(buff, "<a href=\"$1$2\">" + g + "</a>");
}
m.appendTail(buff);
return new String(buff);
}

ところが、ユーザの投稿した文書をこれでフィルタして HTML として出力するときには CSS(クロスサイトスクリプティング)対策としてサニタイジングを行う必要があり、少し注意しなければなりません。例えば、次のような文書をユーザが投稿したとします。
URL サンプルでは <http://example.com/> を使いましょう。

まず、CSS 対策としてサニタイジングします。
URL サンプルでは &lt;http://example.com/&gt; を使いましょう。

これを自動リンクフィルタすると次のようになります。
URL サンプルでは&lt;<a href="http://example.com/&gt;">http://example.com/&gt;</a>を使いましょう。

本来は http://example.com/ をリンクとして出力したいのですが、http://example.com/&gt; がリンクになってしまいます。

これを回避するためにサニタイジングと自動リンクを同時に行います。
private static Pattern linkHtmlPattern = Pattern.compile(
"((https?)(://[\\p{Alnum}\\+\\$\\;\\?\\.%,!#~*/:@&=_-]+))|([&<>\"\'])");
public static String linkHtml(String s, int length, String suffix) {
StringBuffer buff = new StringBuffer((int) (s.length() * 1.2f));
Matcher m = linkHtmlPattern.matcher(s);
while (m.find()) {
String g = m.group();
if (g.length() == 1) {
if (g.equals("&")) {
g = "&amp;";
} else if (g.equals("<")) {
g = "&lt;";
} else if (g.equals(">")) {
g = "&gt;";
} else if (g.equals("\"")) {
g = "&quot;";
} else if (g.equals("\'")) {
g = "&#x27;";
}
m.appendReplacement(buff, g);
continue;
}
if (length > 0 && g.length() > length) {
g = g.substring(0, length);
if (suffix != null) {
g += suffix;
}
}
g = m.quoteReplacement(g);
m.appendReplacement(buff, "<a href=\"$1\">" + g + "</a>");
}
m.appendTail(buff);
return new String(buff);
}
結構面倒くさいです。。色々考えたのですが、今のところこれが一番かなと思います。

Perl の場合は URI::Find という便利なクラスがあります。使い方はこんな感じです。
use URI::Find;
my $finder = URI::Find->new(
sub {
my($uri, $orig_uri) = @_;
return qq {<a href="$uri">$orig_uri</a>};
});
$finder->find(\$text);

では、サニタイジングと併せて使う場合はどうやったらいいのでしょう?すごく悩んでこんな実装にしたことがあります。
$text =~ s/&gt;/ __GT__ /g;
$text =~ s/&lt;/ __LT__ /g;
$text =~ s/&amp;/ __AMP__ /g;
$text =~ s/&quot;/ __QUOT__ /g;
$text =~ s/&#x27;/ __APOS__ /g;
my $finder = URI::Find->new(
sub {
my($uri, $orig_uri) = @_;
return qq {<a href="$uri">$orig_uri</a>};
});
$finder->find(\$text);
$text =~ s/ __GT__ /&gt;/g;
$text =~ s/ __LT__ /&lt;/g;
$text =~ s/ __AMP__ /&amp;/g;
$text =~ s/ __QUOT__ /&quot;/g;
$text =~ s/ __APOS__ /&#x27;/g;

うむむ。もっといい方法ないですかね。。

2008年4月3日木曜日

DbUtils でアンダバー区切りをハンガリアン記法にマッピング

今回は Jakarta Commons の DbUtils について少し書こうと思います。

DbUtils は次のコードサンプルのように SQLクエリの結果を JavaBeans として得ることができるシンプルで便利な機能を持っています。
QueryRunner run = new QueryRunner(dataSource);
ResultSetHandler h = new BeanHandler(Person.class);
Person p = (Person) run.query(
"SELECT * FROM Person WHERE name=?", "John Doe", h);

例えば、Person テーブルの定義が id と name カラムを持つ場合、
create table person (
id integer not null primary key,
name varchar(20) not null
);

Person クラスはこんな感じです。
public class Person implements java.io.Serializable {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

最近 DbUtils を使い始めたのですが、1つ気に入らない問題が出てきました。Java 側の命名規則はハンガリアン記法、データベース側の命名規則はアンダーバー区切りで、次のようなテーブルに対応する JavaBeans は、
create table person (
id integer not null primary key,
first_name varchar(20) not null,
last_name varchar(20) not null
);

first_name カラムは first_name という JavaBeans プロパティにマッピングされるので、専用の setFirst_name などという専用のセッターを用意すればなんとかなりますがあまり美しくありません。
public class Person implements java.io.Serializable {
private int id;
private String firstName;
private String lastName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName= firstName;
}
public String getLastName() {
return lastName;
}
public void setFirstName(String lastName) {
this.lastName= lastName;
}
// NOTE: for DbUtils
public void setFirst_name(String firstName) {
setFirstName(firstName);
}
public void setLast_name(String lastName) {
setLastName(lastName);
}
}

上記のサンプルが書かれているサイトの下の方にも記述がありますが、
http://commons.apache.org/dbutils/examples.html
org.apache.commons.dbutils.BeanProcessor の mapColumnsToProperties メソッドをカスタマイズすることで first_name から firstName へのマッピングを行うことができます。
import java.beans.PropertyDescriptor;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Arrays;
import org.apache.commons.dbutils.BeanProcessor;
public class MyBeanProcessor extends BeanProcessor {
@Override
protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
PropertyDescriptor[] props) throws SQLException {
int cols = rsmd.getColumnCount();
int columnToProperty[] = new int[cols + 1];
Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);
for (int col = 1; col <= cols; col++) {
String columnName = rsmd.getColumnName(col);
for (int i = 0; i < props.length; i++) {
// NOTE: 元のコードを
// if (columnName.equalsIgnoreCase(props[i].getName())) {
// NOTE: 次のように変更
if (equalsColumnProperty(columnName, props[i].getName())) {
columnToProperty[col] = i;
break;
}
}
}
return columnToProperty;
}
private boolean equalsColumnProperty(String colName, String propName) {
// NOTE: 頻繁に呼び出されるので実際にはキャッシュを使うなどして
// 高速化したほうがよいです。
return colName.replaceAll("_", "").equalsIgnoreCase(propName);
}
}

カスタマイズした MyBeanProcessor の使い方はこんな感じです。実際はRowProcessor のインスタンスはシステムに 1つでいいかと思います。
RowProcessor rp = new BasicRowProcessor(new MyBeanProcessor());
QueryRunner run = new QueryRunner(dataSource);
ResultSetHandler h = new BeanHandler(Person.class, rp);
Person p = (Person) run.query(
"SELECT * FROM Person WHERE first_name=?", "John Doe", h);

2008年4月2日水曜日

会社設立!

4月1日づけで登記申請を行いました。この日付が会社設立になるようです。専門の方の助けを借りるとあっという間です。
会社作ると言い出してからはや5年ぐらい。 長かったー。パチパチ。
早速、何かのモノの本で読んだ最初に決めるべき経営理念とやらを書いてみます。

経営理念
  • 自分、まわりの人、ひいてはすべての人を幸せに。
  • 常に創意工夫を。
  • サービスをもって文化、社会の発展に貢献を。
ふむ。まぁまぁですかね。。今考えたんで。

もうちょっと具体的な私個人の「~したい」をまとめると
  • 世間に認知され使われるサービスを作りたい。
  • たくさんの人を雇いたい。もしくは仕事の基盤を作り出したい。
  • 自社ビルを持ちたい。
こんな感じでしょうか。「たくさんの人を雇いたい」については、たまに実家に帰ると景気が悪いような話を聞いたり、職のない若者がいると聞いていたのでなんとかしないといけないなーと常々思っていることです。「自社ビルをもちたい」は幼稚園のころ母親に約束をしていたようです。今はまったくもって道筋は立っていませんが、いつかは。。という目標にして忘れないようにしたいです。

さて、次は事業計画やら会社のホームページ作らなければなりません。(今から作るんです。遅すぎですかねw)

2008年3月31日月曜日

prototype.js を使ったスターレイティング(JavaScript 星評価)

YouTube や Amazon、livedoor Reader でコンテンツの評価に使われているスターレイティングを prototype.js を使って割と汎用的に実装してみました。

http://www.masugadesign.com/the-lab/scripts/unobtrusive-ajax-star-rating-bar/
などには高機能なライブラリがあるようですが、とっつきにくいので自作しました。順を追って説明していきます。

準備と前提条件:
  1. prototype.js(今回は ver1.6)を使用します。 http://prototypejs.org/ からソースをダウンロードしておきましょう。
  2. 5段階の星を表現するために、未選択のものを含めて 6つの画像が必要です。私は http://iconlet.com/ から素材を入手してペイントで加工しました。(サンプル:、使った素材のライセンスはコチラ http://creativecommons.org/licenses/by-sa/3.0/)


上記 2つの準備が終わったら、starrating.js を作成します。計算部分は livedoor Reader を参考にさせていただきました。
StarRating = Class.create({
initialize: function(id, images, options) {
var img = $(id);
Event.observe(img, "mousemove", function(e) {
if (!img.getAttribute("orgSrc")) {
img.setAttribute("orgSrc", img.src);
}
var width = img.offsetWidth;
var cell = width / images.length;
var offsetX = !isNaN(e.offsetX) ? e.offsetX: e.layerX - img.offsetLeft;
// Position.cumulativeOffset(img)[0]
if (offsetX == 0) offsetX++;
if (offsetX > width) offsetX = width;
var rate = Math.ceil(offsetX / cell);
if (rate < 1) {
rate = 1;
}
if (options.basePath) {
img.src = options.basePath + images[rate - 1];
} else {
img.src = images[rate - 1];
}
img.setAttribute("rate", rate);
});
Event.observe(img, "mouseout", function(e) {
var src = img.getAttribute("orgSrc");
if (src) {
img.src = src;
}
});
if (options.onClick) {
Event.observe(img, "click", function(e) {
img.removeAttribute("orgSrc");
options.onClick.call(this, img, img.getAttribute("rate"));
});
}
}
});

prototype.js, starrating.js、星画像を次のようにウェブサーバに配置します。
  • /js/prototype.js
  • /js/starrating.js
  • /img/rate0.png
  • /img/rate1.png
  • /img/rate2.png
  • /img/rate3.png
  • /img/rate4.png
  • /img/rate5.png

そして最後にスターレイティングを呼び出すコードを書きます。
<script src="/js/prototype.js" type="text/javascript"></script>
<script src="/js/starrating.js" type="text/javascript"></script>
<img src="/img/rate0.png" width="80" height="16" id="starRating" />
<script language="javascript" type="text/javascript">
new StarRating(
"starRating",
["rate1.png", "rate2.png", "rate3.png", "rate4.png", "rate5.png"], {
basePath: "/img/",
onClick: function (img, rating) {
alert(rating);
}
}
);
</script>

まず、img 要素で rateX.png を定義します。このとき、id には任意の名前を指定してください。サンプルでは "starRating" を使っています。次に、script タグの中で StarRating を 生成します。第一引数は img 要素の id、第二引数は、マウスオーバーで切り替える画像の配列を指定します。第三引数のオプションでは basePath と onClick コールバック関数を指定することが可能です。onClick コールバック関数の引数は img 要素と算出されたレイティングです。

サンプルは alert しているだけですが、実際には Ajax.Request 等を用いてサーバ側にユーザがどの星をクリックしたかを送信することになると思います。

ロゴ決まりました

知り合いの "jiju" さんにロゴ作っていただきました。ありがとうございますm(_ _)m
try-catch がプログラムの用語であること、「やさしげでクール」、そんな印象でデザインしてくださったそうです。

ロゴに負けないいい会社にしたいですねー。

2008年3月18日火曜日

レンタルオフィス

六本木にレンタルオフィス 1席を借りました。とても気に入りました。

独立して 3年立ちますが、すべて業務委託で常駐だったのでこの自由さには驚愕です。
今は昼前に起きてゆっくりご飯を食べてから出勤してます。

2008年3月14日金曜日

Java でタグクラウドの実装

Java でタグクラウドの実装をする必要があったので、ググってみましたがなかなかピンとくる情報に出会えなかったため、ネタにしてみます。

幸いにも計算部分は Perl の実装を参考にさせていただくことができたので、Java 風のアレンジを加えてみました。

まずタグのインタフェースを定義します。Comparable を継承しているのはソートに使用するためです。
public interface Tag extends Comparable {
/** タグそのものを返します */
String getValue();
/** タグの使用回数など指標になる数値を返します */
int getScore();
/** タグクラウドの文字の大きさを返します */
int getCloudLevel();
/** タグクラウドの文字の大きさを設定します */
void setCloudLevel(int cloudLevel);
}

次にインタフェース Tag の実装クラスです。
import java.io.Serializable;
public class ExampleTag implements Tag, Serializable {
private String value;
private int score;
private int cloudLevel;
public ExampleTag(String value, int score) {
this.value = value;
this.score = score;
}
public String getValue() {
return value;
}
public int getScore() {
return score;
}
public int getCloudLevel() {
return cloudLevel;
}
public void setCloudLevel(int cloudLevel) {
this.cloudLevel = cloudLevel;
}
public int compareTo(Object o) {
Tag tag = (Tag) o;
return (getScore() - tag.getScore());
}
public String toString() {
return "ExampleTag[value=" + value + ",score=" + score
+ ",cloudLevel=" + cloudLevel + "]";
}
}

最後にタグクラウドの実装です。main にテスト用のコードを書いておきます。TagCloud クラスを作成して Tag オブジェクトを addTag していって最後に getTags で取り出して使います。順序を維持するために java.util.LinkedHashSet を使っています。
import java.util.Iterator;
import java.util.Set;
public class TagCloud {
/** フォントの大きさの段階を指定 */
private final int maxLevel;
/** タグを順序どおりに格納する Set */
private final Set<Tag> tags;
/** 計算済みフラグ */
private boolean clouded;
/** 最小 */
private int minScore = Integer.MAX_VALUE;
/** 最大 */
private int maxScore = Integer.MIN_VALUE;
/** コンストラクタ */
public TagCloud(int maxLevel) {
this(maxLevel, 16);
}
/** コンストラクタ */
public TagCloud(int maxLevel, int capacity) {
if (maxLevel < 1) {
throw new IllegalArgumentException("invalid maxLevel " + maxLevel);
}
this.maxLevel = maxLevel;
this.tags = new java.util.LinkedHashSet<Tag>(capacity);
}
/** タグを追加します */
public void addTag(Tag tag) {
if (tag != null) {
tags.add(tag);
int score = tag.getScore();
this.minScore = Math.min(minScore, score);
this.maxScore = Math.max(maxScore, score);
this.clouded = false;
}
}
/** タグの Set を返します */
public Set getTags() {
cloud();
return tags;
}
/** タグクラウドのための計算をします */
private void cloud() {
final int size = tags.size();
if (clouded || size == 0) {
return;
}
if (size == 1) {
Tag tag = tags.iterator().next();
tag.setCloudLevel(1);
} else {
double min = (minScore == 0) ? 0.0: Math.log(minScore);
double max = (maxScore == 0) ? 0.0: Math.log(maxScore);
double f;
if (min == max) {
min -= maxLevel;
f = 1.0;
} else {
f = maxLevel / (max - min);
}
if (size < maxLevel) {
f *= ((double) size / maxLevel);
}
for (Tag tag: tags) {
int score = tag.getScore();
if (score != 0) {
tag.setCloudLevel((int) ((Math.log(score) - min) * f));
}
}
}
this.clouded = true;
}
// NOTE: for test
public static void main(String[] args) {
TagCloud tagCloud = new TagCloud(4);
tagCloud.addTag(new ExampleTag("abcd", 3));
tagCloud.addTag(new ExampleTag("efg", 1));
tagCloud.addTag(new ExampleTag("hijk", 2));
tagCloud.addTag(new ExampleTag("lmn", 10));
tagCloud.addTag(new ExampleTag("opqrstu", 7));
tagCloud.addTag(new ExampleTag("vwxyz", 5));
System.out.println(tagCloud.getTags());
}
}

上記 3つのソースを同じディレクトリにおいて javac *.java でコンパイル、java TagCloud でテスト実行すると以下のような結果が出力されます。
[ExampleTag[value=abcd,score=3,cloudLevel=1],
ExampleTag[value=efg,score=1,cloudLevel=0],
ExampleTag[value=hijk,score=2,cloudLevel=1],
ExampleTag[value=lmn,score=10,cloudLevel=3],
ExampleTag[value=opqrstu,score=7,cloudLevel=3],
ExampleTag[value=vwxyz,score=5,cloudLevel=2]]

このクラスを利用して JSP などで HTML 出力すれば
<style type="text/css">
.tagcloud {width:180px;}
.tagcloud span {padding:5px; font-weight:bold; color:steelblue;}
.tagcloud .tag0 {font-size:10px;}
.tagcloud .tag1 {font-size:16px;}
.tagcloud .tag2 {font-size:22px;}
.tagcloud .tag3 {font-size:28px;}
</style>
<div class="tagcloud">
<span class="tag1">abcd</span>
<span class="tag0">efg</span>
<span class="tag1">hijk</span>
<span class="tag3">lmn</span>
<span class="tag3">opqrstu</span>
<span class="tag2">vwxyz</span>
</div>


こんな感じのタグクラウドになります。

2008年3月5日水曜日

登記

さて、会社名を決めたところで次は登記をせねばなりません。

今年は確定申告を専門の会社にお願いしたので、同じところに登記お任せすることにしました。まず決める必要があると教えていただいたのが次の 5つでした。
  • 会社名
  • 事業目的
  • 資本金
  • 取締役の数
  • 本店の住所
すごく分かりやすく親切な方に出会いまして、順序を追って主要なことから説明を頂いているようです。

会社名は決めたので、次に事業目的を改めて考えてみました。口語ではなんとなく説明できるのですが、それらしい言葉が浮かんでこないのでウェブで調べて最終的に次の 4つに決めました。

事業目的:
1) インターネットウェブサイトのコンサルティング、企画、 設計、開発、運営及び販売
2) インターネット等のオンラインを利用した市場調査、宣伝 及び広告等の受託
3) コンピュータシステムのコンサルティング、企画、設計、 開発、運営及び販売
4) 株式の保有、売買並びにその他の投資事業

どこかで「定款を変更するのは大変だからなるべくたくさん書いたほうが良い」と聞いていたのですが、まずは現実的な範囲で思いつく事業目的にすれば良いようです。

資本金は 100万円としました。取締役は私一人です。本店の住所はレンタルオフィスを借りることにしました。月 5万円程度の予算で考えています。

2008年3月1日土曜日

会社名決定

「株式会社トライキャッチ」です。プログラムで使われる try-catch ブロックが由来です。
trycatch.jp ドメインも取得しました。

いろいろ悩んだ候補はコチラ。
  • ハードワーカー(13画がいいらしいといと聞いていたのと、自分らしいということで。求人が厳しいとのアドバイス受けて却下w)
  • シンプルイズベスト(Simple is the best と the が抜けているとの指摘受け却下w)
  • プレイウェブ(ウェブを楽しもうと。最終的にピンとこないので却下)
ぼちぼち定款とか資本金を決めて手続きはじめます。3月末を設立の目標とします。

2008年2月20日水曜日

ブログはじめました。

プログラムのことや起業などの書こうかと。よろしくです。