JDB を使う

– 昨日の一件のおかげで初めて Java のデバッガを使うことに。
– デバッグ対象の JVM のオプションに

-Xdebug -Xrunjdwp:transport=dt_socket,address=50000,server=y,suspend=n

を追加して実行(Resin の場合は httpd.sh の args=”…” を -J-Xdebug -J-Xrunjdwp:…のように -J を付与)しておけば次のようなデバッグができる。

$ jdb -attach 50000

threads

グループ tcp-server:
  (java.lang.Thread)0x3b0 tcp-accept-8080 実行中
  (java.lang.Thread)0x3b1 tcp-accept-8080 状況待機中
  (java.lang.Thread)0x3b2 tcpConnection-8080-0 状況待機中

thread 0x3b2

tcpConnection-8080-0[1] suspend
すべてのスレッドが中断されました。
tcpConnection-8080-0[1] where
  [1] java.lang.Object.wait (ネイティブ メソッド)
  [2] com.caucho.server.TcpServer.accept (TcpServer.java:648)
  [3] com.caucho.server.TcpConnection.accept (TcpConnection.java:211)
  [4] com.caucho.server.TcpConnection.run (TcpConnection.java:132)
  [5] java.lang.Thread.run (Thread.java:534)
tcpConnection-8080-0[1] resume
すべてのスレッドが再開されました。

quit

という感じでかなり便利。
http://java.sun.com/j2se/1.4/ja/docs/ja/tooldocs/win32/jdb.html#description
– Sun JDK ドキュメント: jdb – Java デバッガ

JDBC のログとりメモ

– close() するのは PreparedStatement#close() でなく ResultSet#close() でもなく、絶対に Connection でなければならない。
– ログ情報には PreparedStatement#toString() がよさげ(少なくとも com.mysql.jdbc では)。
理由は、ログのための利便性から。SQLException がどのクエリでエラーになったかを残してくれないので、 PreparedStatement インスタンスがクエリ周りの情報をきっちり保持してくれていれば、toString() でログにどのクエリ文を実行したかが残せる。しかし、PreparedStatement#close() は DB 接続を閉じる仕様とは限らないため、Connection#close() を呼ぶ必要がある。PreparedStatement#close() だけにしたらプールがあふれ、思わぬ場所で DB 接続が開くのをずっと待ち続けることになってしまった。
– 例:

boolean isExist(int id){
    PreparedStatement ps = null;
    try{
        final Connection con = getConnection();
        try{
            ps = con.prepareStatement(“SELECT id FROM list WHERE id = ? LIMIT 1”);
            ps.setInt(1, id);
            return ps.executeQuery().next();
        }finally{
            con.close();
        }
    }catch(SQLException e){
        log(“Query error [“+ps+”]”, e); // void log(String, Throwable); とする
        return false;
    }
}

Bug Pattern というか今日のミス

– 事前に気づいたけど相当焦ったもの。

SELECT * FROM foo WHERE …;

な、ただレコードの有無を調べる(要素は取り出さない)クエリのパフォーマンスを意識して SQL クエリのみ書き換えたときに、うっかり次のようにしまった。

SELECT COUNT(*) FROM foo WHERE …;

常に 1 レコード返るようになり、レコード数 0 のときも毎回処理が行われることに… リリース前に自分で気づいたけれど反省。

委譲とコンポジション(Effective Java の間違い)

Effective Java には、自身を参照にして渡さないと委譲とは呼べない、とあるが、これは間違い。Effective Java にも挙げられている [GoF P32] には次のようにある。

委譲では、1 つの要求を 2 つのオブジェクトが扱う。要求を受け取ったオブジェクトは委譲者へオペレーションを委譲する。これは、サブクラスが親クラスに要求を渡すことと同様である。

Effective Java にある自身の参照渡しはどこからでてきたのかというと、多分次の文の誤解から。

しかし継承の場合は、継承されたオペレーションは、C++ ならば this メンバ関数、Smalltalk ならば self を用いて、要求を受け取ったオブジェクトを参照できる。委譲でこれと同じ効果を実現するには、要求を受け取ったオブジェクトが自身を委譲者に渡す。そうすれば、委譲したオペレーションが受け手のオブジェクトを参照できるようになる。

その他参考:
<http://www.ogis-ri.co.jp/otc/otc2/oosquare-ml/Archive/200005.month/767.html>
– オージス総研 ML

MySQL で ENUM を使うべきではない

– ENUM 型のカラムには、リストにない任意の文字列をエラーなしで指定できてしまうため、不具合の温床になりうる。例えば次のテーブルを考える。

CREATE TABLE `foo` ( `number` ENUM(‘zero’,’one’,’two’) NOT NULL DEFAULT ‘zero’);

これに zero, one, two 以外の値が入ることは望ましくないが、リストにない文字列をいれることで空文字が入ってしまう。

mysql> INSERT INTO `foo` VALUES (‘six’);
Query OK, 1 row affected, 1 warning (0.03 sec)
 
mysql> select * from foo;
 +——–+
 | number
 +——–+
 |
 +——–+
1 row in set (0.02 sec)

これは typo でも発生してしまうため、TINYINT などを使って、マジックナンバーは各言語の const 変数でラップしたほうがエラーを捕捉できていいかなと。

Servlet の基本

– 基本は通常のクラスと同じだが、条件として javax.servlet.http.HttpServletを親クラスとする必要がある。
– protected void do*(HttpServletRequest, HttpServletResponse)
doGet, doPost, doDelete, doHead, doOptions, doPut, doTrace があり、それぞれ対応するリクエストのときに呼び出される。
– protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException
別のオーバーロードされた service メソッドから処理を受け、配分するのが役目なので protected。
parent.service(req,res) を呼び出した時点で do*(req,res) の処理が行われるので、その前後の共通動作を定義するのに便利。
– 日本語(マルチバイト文字)を引数にとる
— getParametervalues,getParameterを使い、化けた状態の文字を取得したあと

new String(string.getBytes(“ISO_8859_1”), “Shift_JIS”);

のようにする。(HTTPプロトコルの文字コードが ISO-8859-1 のため)
— HttpServletRequestクラスのsetCharacterEncodingで、パラメータを取得する前にエンコードを指定しておく。
— HttpServletRequest#setCharacterEncoding(String enc) の挙動の違いの問題に注意!
Tomcat 4.1.29 以降の 4.1.*/5.0.16以降の 5.0.*、では、Getメソッドではエンコードされなくなった。GETの引数はURIに含まれるので、URIはエンコードされるべきではないという見解による仕様変更とのこと。
– スレッドセーフということ。
サーバサイドでは1つのインスタンスを使いまわしてマルチスレッドで動作するので、スレッド同士の不要な干渉をなくさなければならない。
“staticではない”フィールド(インスタンス変数)もスレッド間では共有されている事に注意!
–> ページ閲覧者固有の変数を持ちたい場合、メソッド内で定義した変数を使わなければならない。
データアクセスなど同期を取らないといけない場合は、(デッドロックにくれぐれも注意しつつ)適宜 synchronized にする。

メールのContent-Header, Content-Body の仕様(RFCのピックアップまとめ)

世界中で標準となっているeメール仕様(SMTPプロトコル仕様)はRFC822などのRFCドキュメントです。
原本を読めばすべて書かれているのですが、全部読むのは大変なのでピックアップしてまとめてみました。

続きを読む メールのContent-Header, Content-Body の仕様(RFCのピックアップまとめ)

Outlook Express で Multipart/Related したときの望ましくない挙動

– ドキュメント整理(2004-06)
– Multipart/Related でも、直接の呼び出しが実行中のメールに存在しない場合には添付ファイル扱いになる(A HREF=”CID:〜”ではだめ)。
– HTML同士の Related で<A>や<Iframe>を使おうとしても無理。やはり添付ファイル扱いに。

PHP でメールするときの注意等

– 過去に書いたドキュメントを整理するため、整形してコピペ。内容は多分 2004-06 くらいのもの。
– mail関数と、そのラッパーであるmb_send_mail関数があるが、後者はオプションのヘッダ指定に無関係に、自動的に

MIME-Version: 1.0
Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit

という3つのヘッダを挿入する(Content-Typeを明示的に指定していてもなお)。
– mail関数のデフォルトのヘッダーは
mail(‘foo@yyy.zz’,’title’,’message’);
として送信した場合、

Return-Path: <anonymous@local.host>
Delivered-To: foo@yyy.zz
Received: (qmail xxxxx invoked from network); 2 Apr 2004 15:13:40 +0900
Received: from unknown (HELO local.host) (xxx.xxx.xxx.xxx)
  by pcxxx.xxx.xxx.xxx.in-addr.foo with SMTP; 2 Apr 2004 15:13:40 +0900
Received: (qmail xxxxx invoked by uid 80); 2 Apr 2004 15:13:40 +0900
Date: 2 Apr 2004 15:13:40 +0900
Message-ID: <20040402061340.11671.qmail@local.host>
From: anonymous@local.host
To: foo@yyy.zz
Subject: title
 
message
 

のようになる。(Received:はもちろんmail関数がつけたものではない)
– MIME-Versionフィールドを指定する場合、第四引数でヘッダ指定する場合に改行は\r\nや直接の改行(SJISの場合CRLFになる)ではなく\nでなければならない。(MIME-Version:1.0を宣言した行以降は\rも\nも1つの行として認識されるため。
– PHPでHTMLメールを送るごく簡単な例)

sAddHeader = “MIME-Version: 1.0\n”
.”Content-Type: multipart/alternative;\n”
.”boundary=\”—-=_NextPart_000_0024_01C418A2.AA46C5F0\”\n”;
$sMessage = “This is a multi-part message in MIME format.”
.”\n\n——=_NextPart_000_0024_01C418A2.AA46C5F0\n”
.”Content-Type: text/plain; charset=ISO-2022-JP\n”
.”Content-Transfer-Encoding: 7bit\n\n”
.”プレーンテキストメールです\n”
.”\n\n——=_NextPart_000_0024_01C418A2.AA46C5F0\n”
.”Content-Type: text/html; charset=ISO-2022-JP\n”
.”Content-Transfer-Encoding: 7bit\n\n”
.”<font color=’red’>HTMLメールです。あ</font>いう<b>えお</b>\n”
.”\n\n——=_NextPart_000_0024_01C418A2.AA46C5F0–\n”;
//送信先メールアドレス等。
$sMailAddress = ‘foo@xxx.zz’;
$sTitle = ‘タイトル’;
if(mail($sMailAddress,$sTitle,$sMessage,$sAddHeader))
echo “sent successfully!\n”;
else
echo “sent failure”;

プラグインや国際化で動作がおかしいときに状態をクリーンにする

eclipse.exe -clean

みたく -clean オプションを付けて Eclipse IDE を起動すると、クリーンアップされて状態が直ったりする。何かあったときに一度だけやればいい。
3.0.x の日本語化周りで、一部しか日本語化されなかった場合などに有効。