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
そして、次のようのなプログラムを実行する。
単に、jarファイル内のaiueo.txtを取得して、内容を表示するだけ。
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) {
}
}
}
}
}
Taskとしてもスタンドアロンでも動作するようにしてある。
まずは、普通にmainから動かしたとき。
元気に動いてる。
あいうえお
かきくけこorz
次に、下のようなAntスクリプトを書いて、実行する。
resource.jarがaiueo.txtのもので、test.jarが上のプログラムが入っている。
<?xml version="1.0" encoding="Shift_JIS"?>
これの実行結果。
というわけで、aiueo.txtが取得できない。
default:
[ykhr] jp/ykhr/kokko/aiueo.txt is not available.
BUILD SUCCESSFUL
試しに、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がどこからパスの情報を取得しているのか、分からなかっただけ〜