同一マシンで RMI による取得を試みてみたところ、手元の MacBook でクライアントの実行に平均で 330 ms 秒かかってしまった。リモート側のデータの更新頻度がそれなりに高くリモートとのやりとりが増えるとなると、この処理時間では影響が出るので別の手段を考えよう。 RMI が悪いのでなく求めているものと残念ながら一致していないだけ。
初回アクセス時の初期化処理に時間がかかるだけで、 2 度目移行は 1ms と高速でした。適当なこと書いてすみませんでした。
public interface Echo extends Remote {
String getMessage(String message) throws RemoteException;
}
public class HelloWorldEcho implements Echo {
public String getMessage(String message) throws RemoteException {
return "Hello World! " + message;
}
public static void main(String[] args) throws Exception {
Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
Echo echo = new HelloWorldEcho();
Remote remote= UnicastRemoteObject.exportObject(echo, 0);
registry.rebind("echo", remote);
Thread.sleep(60000); // 1 分だけ起動
}
}
public class EchoClient {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
Echo echo = (Echo)Naming.lookup("echo");
System.out.println(echo.getMessage("hoge"));
System.out.println(System.currentTimeMillis() - start);
start = System.currentTimeMillis();
System.out.println(echo.getMessage("hoge"));
System.out.println(System.currentTimeMillis() - start);
}
}
サンプルなので throws Exception しています。
プログラムを書き始めたころを思い出した。何から始めていいのか分からない。分からないことが分からない。
今最低限のプログラムを書けるようになっていったことを思い出せば、興味のある料理(分野)をとにかく作っていくことが大事なんだろうと思う。不味い(下手なコード)こともあるけれど、数をとにかくこなしていき一品(プログラム)を確実につくっていく。そうして作った一品の中で良かったと思える行程(モジュール)は別の料理(プログラム)に生かすこともできるようになる。他人のアドバイスも参考にするけど体験することが大事。
と、料理に飽きないように自分に言い聞かせるのでした。まずは酒のつまみをさらっと作れることを目標にしていこう。
ベンチの取り方に誤りがあったので修正。
前回は RMI でアクセスし思ったようなパフォーマンスを得られなかったので、どうしようかなあと考え memcached があるじゃないかということで計測してみた。
spymemcached ライブラリを使いコードは以下の簡単なもので試した。
public class MemcachedApplication {
public static void main(String[] args) throws IOException {
MemcachedClient c = new MemcachedClient(new InetSocketAddress("localhost", 11211));
long start = System.currentTimeMillis();
c.set("someKey", 3600, (Object) new Hoge());
System.out.println(System.currentTimeMillis() - start);
start = System.currentTimeMillis();
Hoge myObject = (Hoge) c.get("someKey");
System.out.println(System.currentTimeMillis() - start);
System.out.println("age:" + myObject.age);
start = System.currentTimeMillis();
c.set("someKey", 3600, (Object) new Hoge());
System.out.println(System.currentTimeMillis() - start);
c.shutdown();
}
public static class Hoge implements Serializable {
public int age = 100;
}
}
199 15 age:100 0
初回接続に時間がかかるだけで、 get/set とも 15 ms ぐらいですむので実用できそう。シンプルなプロトコルでやりとりする memcached が使われる理由が分かった。どうでもいいけどブロック (delegate) 使って時間計測したい。
どこの Maven リポジトリにも管理されていないライブラリをインハウスリポジトリ(社内リポジトリ)で管理させる方法があまりなくて少し苦労しました。 scp でデプロイしていますが webdav の場合でも置き換えればいいはずです。
デプロイ先の認証情報を記述しておきます。 mvn コマンドを叩くときに使います。面倒なのでよくあるユーザー名=パスワードにしていますが、できる限り公開鍵認証を使いましょう。
<settings>
<servers>
<server>
<id>com.example</id>
<username>ussy</username>
<password>ussy</password>
</server>
</servers>
</settings>
mvn deploy:deploy-file -Dfile=/Users/ussy/Downloads/memcached-2.2.jar -DgroupId=net.spy -DartifactId=memcached \ -Dversion=2.2 -Dpackaging=jar -DgeneratePom=true \ -Durl=scp://192.168.10.10/home/maven2 -DrepositoryId=com.example
settings.xml に記述している id と repositoryId および接続先である url を結びつけることが大事でした。これでインハウスリポジトリにデプロイできました。
jar ライブラリだけでなく source.jar を提供してくれている場合は source も管理させます。ソースがついていないと Eclipse で補完を効かせる場合にも、まともな変数名になりません。
mvn deploy:deploy-file -Dfile=/Users/ussy/Downloads/memcached-2.2-sources.jar -DgroupId=net.spy \ -DartifactId=memcached -Dversion=2.2 -Dpackaging=java-source \ -Durl=scp://192.168.10.10/home/maven2 -DrepositoryId=com.example
packageing に java-source を指定すること、 -DgeneratePom=true を取り除くことが大事でした。自分の環境だと -DgeneratePom=false にすると NullPointerException が発生し、うまくいきませんでした。
[追記 20100526]
maven 2.2.1 であれば -DgeneratePom=false を指定する必要があります。
[/追記 20100526]
http://maven.apache.org/guides/mini/guide-3rd-party-jars-remote.html
javadoc のデプロイは試していません。
小規模なリリース前に壊れて焦った。直接の原因はエクスプローラーが本当に応答しなくなり強制終了させた自分に原因があるんだと思うけど部屋が暑くてマシンにも負荷がかかってたのも事実、という言い訳。
コードはソース管理に放り込んでいるし、大事なデータは SyncToy で毎時ファイルサーバーに同期を取るようにしていたので無事。一番辛かったのが Firefox の設定やらグリモンが飛んだこと。ブックマークに必要なものは保存しているけどセットアップが面倒すぎる。 uc.js で毎回読み込ませるのでなく pref.js に user_pref の設定をマージできるものないかなあ。
それからハードディスクの入れ替えついでにメモリも 1GB から 3GB に増設させてもらった。 Java の開発だと IDE と Firefox で使用メモリが簡単に 700 MB にいってしまうためすぐに重くなっていた。メモリを増やしたおかげで XP の起動が 10 秒ちょっとになった。別に 2GB でもよかったけど、これで仮想マシンも気軽に使えるようになると思う。 4,5000 円で DDR2 の 2GB が揃えられて開発効率が上がるんだから会社はもっと投資すべき。
引っ越しが終わって 2 週間弱たって生活スタイルも変わったので軽く書いておく。一般人がどこを対象にしているのかは知りません。
引っ越しのきっかけは以前も書いたとおり料理をつくりたかったから。貯蓄より作りたいっていうほうが気持ち的に大きいと思う。
前に居たときのほうが会社に近かったけど、一人暮らしなんだし同じような場所に住んでもつまらないので場所をガラリと変えることにした。駅周辺は流行っているけど住んでいる周り徒歩 10 分まで特に何もない。でも家賃はそこそこ高い。一見デメリットだけど遊び場がなく駅からもそこそこ遠いため周りの部屋に人が集まりづらい。それから近くにコンビニ、オリジン弁当、モスバーガーなんかがあると買ったりホイホイ入ってしまったりするだろうからその予防策。買いだめという特殊能力を身につけるのだ。
思った通り静かな環境を手に入れられた。低確率で五月蠅い住民に出会うこともあるんだろうけど。
それから仕事の面では今年になって就業規則が変わって朝 9 時出社にさせられた。首都圏で最も電車が混む時間。若干堅い系統の仕事なのでこれに関しては仕方ないと思ったけどフレックスまで消すことなかろう。寝坊に使わないっての。
そんなめちゃくちゃ混む時間に乗りたくないので、朝 8 時ぐらいには出社するようにした。肩が触れ合うか触れ合わないかぐらい。早く行っても社内に人はいないので静かな時間も得られた。この時間を使ってフィードを処理したり、試していることへのコーディング時間を得られるようになった。本当はもっと早く出たいけど、そんな愛社精神を持っているわけでもないので早朝勤務体系が認められたら出る。
こんなライフサイクルになったため日をまたぐ前ぐらいには眠り始めて、 7 時前には出かけられるようになった。 2 年前まで休日も昼過ぎまで寝ていたのがデフォだったことを考えるとよく変われたものだと思う。
気軽な分析結果をサクっと見られるのはいいなあ。詳細をクリックした後に Google Chart にリクエスト投げてあれだけのレスポンスってのもいい。
下の URL でブックマークしてスマートキーワードに登録
http://eow.alc.co.jp/%s/UTF-8/
今まで英単語変換は excite, goo を利用していて、英語から日本語へは ej。日本語から英語は je をスマートキーワードに与えていた。このやり方だとユーザーが変換方向を考えて入力(ボタンクリック含む)しないといけなかったけど、入力文字が英語か日本語かサーバーが理解してくれるおかげでこちらは何も考えなくて良くなった。
yyyyMM または yyyyMMdd という文字列形式からでも java.util.Date 型に変換するコードを書きました。年月日のフィールドが結合している場合を想定しています。
元は Commons Lang の DateUtils#parseDate(String str, String[] parsePatterns) です。 SimpleDateFormat クラスはマルチスレッドに対応していないのですが Commons Lang ではメソッド内でインスタンスを生成し、 applyPattern でフォーマットを切り替えることで回避していました。
public static Date parseDate(String str, String[] parsePatterns) throws ParseException {
if (str == null || parsePatterns == null) {
throw new IllegalArgumentException("Date and Patterns must not be null");
}
SimpleDateFormat parser = null;
ParsePosition pos = new ParsePosition(0);
for (int i = 0; i < parsePatterns.length; i++) {
String parsePattern = parsePatterns[i];
if (i == 0) {
parser = new SimpleDateFormat(parsePattern);
parser.setLenient(false);
} else {
parser.applyPattern(parsePattern);
}
pos.setIndex(0);
Date date = parser.parse(str, pos);
if (date != null && parsePattern.length() == str.length() && pos.getIndex() == str.length()) {
return date;
}
}
throw new ParseException("Unable to parse the date: " + str, -1);
}
parseDate("20091", new String[] { "yyyy", "yyyyMM", "yyyyMMdd" });
まず元々のコードに setLenient は入っていません。 setLenient を false にするようにしたのですが、 20091 という文字列を渡すと 2009/1/1 が返ってきます。ここから 200911 を渡すと 2009/11 になり、ユーザーは 1/1 を期待してしまうかもしれません。
これを引数の順番が問題と考えれば、引数の順序を入れ替えればよいのかもしれませんが、個人的にあいまいなルールは混乱を招くと思っているので parsePattern.length() == str.length() の文字列長比較を入れてあげることで厳密にチェックを行うようにし、期待する結果を得ることができるようになりました。
このユーティリティメソッドの前には入力チェックが入ると思うので問題にならないかもしれませんが、コストもかからないのでやる価値はあるかなと思います。
しかしコード長いなあ。
テーブル構造が仕組み上あまり ORM と相性のよろしくないデータベースにアクセスしようと考える。単純にオブジェクトとデータベースをマッピングできない。サンプルも副問い合わせが絡む少し複雑な SQL。 Java のコードに SQL を書き込むことは何としても避けたいので、ファイルに書き出すことができオブジェクトにバインドを行うフレームワークを探す。
有名どころだとこれぐらい?ほかにもあるのかも。 iBatis は XML ファイルに SQL を書いて戻り値となる Entity のクラス定義も書いた記憶がある。久しぶりにサイトに覗きにいったところいい意味で変わってないなあと思った。
S2Dao と S2JDBC を比較すると今は S2JDBC を推奨している。当時はインターフェースを書くだけでテーブル操作ができた S2Dao に驚いたなあ。
S2JDBC を実際に触ってみるとタイプセーフで行けるのは簡単なものならまだいいものの、複雑なものでは何をやっているのかよく分からない。 出力している SQL を見れば問題ないことは分かるけど不安な気持ちが残るかな。このあたりは使い分ければいいと思うけど問題はどこから SQL にするのかってあたりなんだろうか。今回は先に書いたとおりテーブルの関連性が綺麗にオブジェクトに結びつけられないレガシー設計ということで SQL ですべて書いていくこととするためこの問題はクリア。
S2Dao のようにインターフェースだけですむわけではないけどに Container に組み込まれている、設定情報が最低限であること、 interceptor を使わず動きがわかりやすいことから S2JDBC を使おうと思います。
ところで Spring JDBC は SQL のファイル読み込みは組み込まれていないのでしょうか。難しい処理でないから独自に組み込めってことなのか、あれば便利だと思うんだけどなあ。
それから久しぶりに生 JDBC 書いたところあまりの面倒さに、これはないわーとなりました。
さらっとページを JSP で用意して、 API を使っているだけで特にハマっていることはありません。難しいことをしているわけでないので当たり前なんでしょうか。というぐらい分かりやすいフレームワークです。もちろん最低限の Seasar の知識があったこともあると思います。
SAStruts は実際に使ったことがないのですが、 Action の戻り値は Cubby のほうが拡張しやすい印象です。 SAStruts は Ajax 時には Action で null を返し、その前にレスポンスに直接書き込むようです。一方 Cubby は ActionResult を返す必要があります。例えば標準で用意してくれている SendError クラスはステータスコードとメッセージを返すだけですが、認証エラー時にヘッダーに情報を詰め込みたい場合がありました。そこで以下のようなクラスをつくりました。 ActionResult を実装すればいいんだなというのが目に見て分かるのはプログラマーに優しいなあと個人的に感じます。その実装が軽く済むというのも大きいです。
public class SendHeaderError implements ActionResult {
private int statusCode;
private String message;
private Map<String, String> headers = new HashMap<String, String>();
/**
* インスタンスを生成します。
*
* @param statusCode
* ステータスコード
*/
public SendHeaderError(int statusCode) {
this(statusCode, null);
}
/**
* インスタンスを生成します。
*
* @param statusCode
* ステータスコード
* @param message
* メッセージ
* @see HttpServletResponse#sendError(int, String)
*/
public SendHeaderError(int statusCode, String message) {
this.statusCode = statusCode;
this.message = message;
}
/**
* ヘッダーを追加します。
*
* @param name
* キー
* @param value
* 値
* @return 自身のインスタンス
*/
public SendHeaderError addHeader(String name, String value) {
this.headers.put(name, value);
return this;
}
/**
* {@inheritDoc}
*/
public void execute(Action action, Class<? extends Action> actionClass, Method method, HttpServletRequest request,
HttpServletResponse response) throws Exception {
for (Map.Entry<String, String> e : this.headers.entrySet()) {
response.setHeader(e.getKey(), e.getValue());
}
if (this.message == null) {
response.sendError(this.statusCode);
} else {
response.sendError(this.statusCode, this.message);
}
}
}
return new SendHeaderError(HttpServletResponse.SC_UNAUTHORIZED).addHeader(
"WWW-Authenticate",
"BASIC realm=\"Authentication User\"");
実際に response に書き込んで行っていることには変わりませんので、このあたりは個々のスタイルに合うかどうかだと思います。
拡張公開しました。未読数を取得する拡張で Vimperator にあるよというものですが、よかったら使ってみてください。やってることは特に面白いことはないですが、 Firefox は拡張を自分で気軽につくれるのが素晴らしいですね。

巷でラムダッシュが大人気です。いや雷悶とユーキさんのブログに、これはいいと書かれていただけです。それでも自分の中で二人の評価の基準は高いと思っているのでこれは信頼性があるなということで購入しました。
ラムダッシュの中にもいくつかシリーズが分かれていて、自分は充電器と洗浄機がセットになっている ES-LA50-S にしました。これまでカミソリでヒゲを剃っていたのですが、実際に使用してみるとこれは楽。口周りがツルツルです。もっと早く手に入れておけばよかったなあ。
# ユーキさん [作りたいとか、じゃなくて 安く売ってる野菜(もやし、キャベツなど)をいかに 使っていくか?だと思うのです さておき..]
# Ussy [>作りたい やっぱり根がエンジニアなんでしょうねえ。もちろん安く済ませて節約したいのもあります。 >レシピ 香ばし..]