ContextClassLoader?

とあるJavaのプログラムが、スタンドアロンでは動くけど、
Antからだと動かない。
色々調べていくと、ContextClassLoaderあたりがあやしい。
でも、ContextClassLoaderって何??(汗


JavaプログラムをAntから実行するときは、Ant実行時のパスに通すのではなく、
taskdefの時にパスを設定してる。また、Javaプログラムが使っている
とあるライブラリは、リソースを取得する時にカレントスレッドの
getContextClassLoader().getResource()から取得してた。


Antから実行した場合、ContextClassLoaderはtaskdefで定義したときの
クラスローダではなく、(その親の?)Ant起動時かなんかのClassLoaderが
入ってきちゃうので、リソースが見つからないみたい。
# そもそもAntがどうやって動いているのか知らないので、なんとなくな予想だけど。


なので、Class#getClassLoaderからならリソースが見つけられるし、
Ant実行時にカスタムタスクやリソース関連のパスを通しても大丈夫だった。
でも依存ライブラリがすげーたくさんあるから、起動時のパスには通したくない・・・。





というわけで、一応これらのことを調べてみる。
まずはリソースとして、jp/ykhr/kokko/aiueo.txtをjar化しておく。
aiueo.txtの内容は、こんな感じ。


あいうえお
かきくけこ

orz


そして、次のようのなプログラムを実行する。


public class Test extends Task {

private static final String TARGET = "jp/ykhr/kokko/aiueo.txt";

public static void main(String[] args) {
Test test = new Test();
test.execute();
}

public void execute() throws BuildException {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
URL url = loader.getResource(TARGET);

if (url == null) {
System.out.println(TARGET + " is not available.");
return;
}

BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(url.openStream()));
String line = null;
while )((line = reader.readLine())( != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ignore) {
}
}
}
}
}

単に、jarファイル内のaiueo.txtを取得して、内容を表示するだけ。
Taskとしてもスタンドアロンでも動作するようにしてある。


まずは、普通にmainから動かしたとき。


あいうえお
かきくけこ

orz

元気に動いてる。


次に、下のようなAntスクリプトを書いて、実行する。
resource.jarがaiueo.txtのもので、test.jarが上のプログラムが入っている。


<?xml version="1.0" encoding="Shift_JIS"?>













これの実行結果。


default:
[ykhr] jp/ykhr/kokko/aiueo.txt is not available.
BUILD SUCCESSFUL
というわけで、aiueo.txtが取得できない。


試しに、Ant実行時にresource.jarにパスを通して実行すると・・・。


default:
[ykhr] あいうえお
[ykhr] かきくけこ
[ykhr] orz
BUILD SUCCESSFUL
というわけで。




実験終わり。
とあるライブラリが、なぜClass#getClassLoaderからリソースを取得しないのか
わからないし、Antが悪いとも思えないし、どーすりゃいーかわからん。


とりあえず、Javaプログラムの最初と最後で以下のようにしてみたけど、
こんなんアリ!?


Thread current = Thread.currentThread();
ClassLoader saved = current.getContextClassLoader();
current.setContextClassLoader(getClass().getClassLoader());

try {
// いろいろ実行
} finally {
current.setContextClassLoader(saved);
}







ってここまで書いたけど、どうやらこれっぽい?
http://issues.apache.org/bugzilla/show_bug.cgi?id=36717
とあるライブラリってJavassistのことではないんだけど、
確かにJavassistの部分でもエラーになってたし。
# 実はJavassistがどこからパスの情報を取得しているのか、分からなかっただけ〜