Not only is the Internet dead, it's starting to smell really bad.:2007年07月中旬

2007/07/11(Wed)

現場

省エネの為定時以降空調止まるのに忙しくて残業続きでもうイヤン。

[Cygwin] rename(2)

多分Cygwinの中の人とかWindows詳しい人には常識なんだろけど、rename(2)の挙動がちとアレだ。

int
main(void)
{
	int fd, tmp;
	char path[] = "/tmp/test.XXXXXX";

	fd = open("test.txt", O_RDONLY);
	tmp = mkstemp(path);
	...
	if (rename(path, "test.txt") < 0)
		abort();
	close(fd);
	return 0;
}

open(2)なんかで掴んでるファイルをrenameしようとするとEACCESになるもより。

まあWindowsだしなー、VC6でもfopen(3)してるファイルはrename(2)できないしな。
ちなみにunlink(2)もVC6だと無理なんだが、Cygwinだと可能なのね。

2007/07/12(Thu)

[BTS] Scarab-0.21

ちびっと手が空いたのでQuickSearchで日本語検索できない件の調査開始。

ソース読んだ限りでは Scarabの問題ではなく、 Turbine側の問題っぽ。

これ多分GETで投げたクエリをjava.net.URLDecoderでdecodeする時に
正しいencoding(ISO-2022-JP)ではなく、ISO-8859-1が使われてしまい

[正しい変換]
URLDecoder.decode("%1B%24B%24%22%1B%28B", "ISO-2022-JP")
	→ "あ"

[不正な変換] ←今ココ!
URLDecoder.decode("%1B%24B%24%22%1B%28B", "ISO-8859-1")
	→ "\\u001B$B$\"\\u001B(B"

つー具合にStringの中身が壊れてしまうもより。
後日Turbine側のソース読んで該当個所を探すこと。

最近買ったもの

2007/07/13(Fri)

[BTS] Scarab-0.21

昨日のQuickSearch対策のパッチがScarabのMLにpostされてるのをハッケソ。
http://osdir.com/ml/java.scarab.user/2007-01/msg00014.html
まあこれで動くようになるのは判るけど、ずいぶんなコードですなぁ。
せめてjava.net.URLDecoderは使おう。

このバグはあくまでもTurbine側で対策するのが筋。
まあ100歩譲ってScarab側で対処するのを認めたとしても

--- src/java/org/tigris/scarab/actions/Search.java.orig	2007-07-12 22:59:44.000000000 +0900
+++ src/java/org/tigris/scarab/actions/Search.java	2007-07-12 23:50:52.000000000 +0900
@@ -654,6 +654,11 @@
                 }
                 else
                 {
+                    /* XXX: FIXME */
+                    searchString = new String(
+                        searchString.getBytes("ISO-8859-1"),
+                        params.getCharacterEncoding());
+
                     Module module = user.getCurrentModule();
                     MITList mitList = MITListManager.getSingleModuleAllIssueTypesList(module,user);
                     user.setCurrentMITList(mitList); 

のように

  1. 壊れたStringをbyte arrayに戻す
  2. 正しい文字コードを指定した上でbyte array→Stringに再変換

としたほうが断然スマート。
#まあ、Turbine側を直したらこっちもこの変更をbackoutする必要があるけどね。。。

んでQuickSearchなんだけど、上記のkludgeで無理矢理動くようにしても
LuceneのSpecial Characterである

+ - && || ! ( ) { } [ ] ^ " ~ * ? : \

やANDやNOTといった予約語をちゃんと消毒してなさげ、イケてないなぁ。

2007/07/17(Tue)

連休

低気圧でほとんど死んでた。

[BTS] 続 Scarab-0.21

Turbine fulcrumのソースをもってくるとき、sticky revision指定すると

$ svn co -r 232252 http://svn.apache.org/repos/asf/jakarta/turbine/fulcrum/trunk
svn: REPORT request failed on '/repos/asf/!svn/bc/556879/jakarta/turbine/fulcrum/trunk'
svn: '/repos/asf/!svn/bc/556879/jakarta/turbine/fulcrum/trunk' path not found

とエラーになるのはなぜ?

しょうがないので viewvcでクリック、クリック。

環境変数QUERY_STRINGのURLDecode処理はTurbineが自前でやってるわけではなくて
javax.servlet.ServletRequest#getParameterValues(String)にお任せしているのか。
( org.apache.fulcrum.parser.DefaultParameterParser#setRequest(HttpServletRequest)実装を参照のこと)

んでServletRequest#getParameterValues(String)の実装は org.apache.catalina.connector.HttpRequestBase
にあるので、こんどはそっち参照する。

QUERY_STRINGのバラしとURLDecode処理(Java1.3対策で自前実装)は org.apache.catalina.util.RequestUtil
でやってて、コードの質はアレとしても実行結果は特に問題なさそうに見える。
つーことはHttpRequestBaseがアヤスイ。

627         String encoding = getCharacterEncoding();
628         if (encoding == null)
629             encoding = "ISO-8859-1";

でたISO-8859-1(笑)
CharcterEncodingプロパティの値がどっかで間違ってnullクリアされるのかな。
Scarab側やTurbine側から覗いてみた限りちゃんと値に"ISO-2022-JP"が入ってるんだけども。
というわけでTomcatのバグのような気がしてきた…

# あとfallbackが"ISO-8859-1"ってのは生のShift_JISなんかでGETする古いブラウザ対策なんかね。
# 今となってはUTF-8の方がとも思うけど、まあどうでもいいや。

飽きたので以下次回。

F-14 Tomcatに対抗してMiG-29 Fulcrumというネタなのか?そーなのか?(いまさらすぎ?)

2007/07/18(Wed)

[BTS] まだまだScarab-0.21

昨日書いたHttpServletRequestインタフェースの実装クラスがHttpRequestBaseだというのは大嘘、ゴメソ。
org.apache.coyote.tomcat4.CoyoteRequestFacadeの方だった。

こっちのソース読んだらすぐ見当ついた、QUERY_STRINGの処理は
org.apache.tomcat.util.http.Parametersでやってるんだけど、こいつが犯人だな。

と文字コードに関するプロパティが2つあり、QUERY_STRINGの解析には後者が使われてるんだけど
誰もどっからもsetQueryStringEncoding(String)が呼ばれてないので値がセットされないつーことだ。

うげげげ、4.1.30ではこれ修正されてるわ。

--- jakarta-tomcat-connectors-4.1.29-src/coyote/src/java/org/apache/coyote/tomcat4/CoyoteRequest.java   2003-10-28 00:27:48.000000000 +0900
+++ jakarta-tomcat-connectors-4.1.30-src/coyote/src/java/org/apache/coyote/tomcat4/CoyoteRequest.java   2004-01-25 22:24:00.000000000 +0900
@@ -1917,11 +1950,19 @@
         Parameters parameters = coyoteRequest.getParameters();

         String enc = coyoteRequest.getCharacterEncoding();
+        boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
         if (enc != null) {
             parameters.setEncoding(enc);
+            if (useBodyEncodingForURI) {
+                parameters.setQueryStringEncoding(enc);
+            }
         } else {
             parameters.setEncoding
                 (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
+            if (useBodyEncodingForURI) {
+                parameters.setQueryStringEncoding
+                    (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
+            }
         }

         parameters.handleQueryParameters();

設定ファイルを見ると

    <attribute name="URIEncoding" required="false">
      <p>This specifies the character encoding used to decode the URI bytes,
      after %xx decoding the URL. If not specified, ISO-8859-1 will be used.
      </p>
    </attribute>

    <attribute name="useBodyEncodingForURI" required="false">
      <p>This specifies if the encoding specified in contentType should be used
      for URI query parameters, instead of using the URIEncoding. This
      setting is present for compatibility with Tomcat 4.1.27 and earlier.
      The default value is <code>true</code>.
      </p>
    </attribute>

の2つの属性値が追加されてるね。

QUERY_STRINGの文字コードについては <FORM METHOD="GET" ...>で投げる時は

んで
ブラウザのURL欄に直接入力した場合は、Firefoxだと
about:configのnetwork.standard-url.encode-utf8の値が

ということがほとんどだと思うので(GET使わんからちゃんと調べたことないので嘘書いてるかも)
useBodyEncodingForURI=trueのデフォルト値で問題ないとオモ。
つーわけでScarabを使う時は4.1.30以降を使いましょうってことだ。

tomcat4系の最新版、4.1.36で何もしなくてもQuickSearchで日本語が通るのを確認、すげぇ時間の無駄だった。

[i18n] java.util.ResourceBundle

Scarabのメッセージカタログはどんだけ~(非難の意)?

@悪い例(例えばScarabのような)

[Test.java]
import java.util.ResourceBundle;
public class Test {
    private static ResourceBundle rb = ResourceBundle.getBundle("Test");
    public static void main(String[] argv) {
        System.out.println(rb.getString("msg1"));
    }
}

[Test_en.properties]
msg1=Hello, World.

[Test_ja.properties]
msg1=こんにちは、世界。

@良い例

[Test.java]
import java.util.ResourceBundle;
public class Test {
    private static ResourceBundle rb = ResourceBundle.getBundle("Test");
    public static void main(String[] argv) {
        System.out.println(rb.getString("Hello, World."));
    }
}

[Test_en.properties]
Hello,\ World. = \
Hello, World.

[Test_ja.properties]
Hello,\ World. = \
こんにちは、世界。

後者の方がTest_*.propertiesの翻訳するのも、メッセージからソースの該当個所を検索するものも楽だよね。
これResourceBundleのJavadocすら前者の悪い例で書いてあるからなぁ。

これは「 getttext(3)がcatgets(3)に対していかに優れてるか」って話とよく似ているんだが
そのgettext(3)の生みの親(まあ育児放棄で、育てたのはGNUだがw)Sunがなぜこーゆーことするかね。

それにResourceBundle#getString(String)はカタログに該当するメッセージが無ければ例外を返す仕様。
#Scarabを日本語で使うと例外で死ぬ理由の大部分がこれなんだよね…
#ScarabBundle_ja.propertiesの翻訳が追いついてないのでMissingResourceException頻発するから
#実際の完成度より低く感じられてしまうのよね。
だから上の例だけでは十分ではなくて

[Gettext.java]
import java.util.ResourceBundle;
public class Gettext {
    private ResourceBundle rb;
    public Gettext(String name) {
        this.rb = ResourceBundle.getBundle(name);
    }
    public String getText(String msg) {
        try {
            msg = this.rb.getString(msg);
        } catch (Exception e) {}
        return msg;
    }
}

[Test.java]
public class Test {
    private static Gettext gt = new Gettext("Test");
    public static void main(String[] argv) {
        System.out.println(gt.getText("Hello, World."));
    }
}

みたいにいちいちWrapper classかかんとならんのもメンドイよな。
いっそimport gnu.gettext.* してしまった方がなんぼかマシな希ガス。
#xgettext(1)とかツールが使えるしね…

あとGettext#getText(String)を書かずに済ませたいなら、やっぱりバイトコードを直接弄っちゃうのがナウなヤング?