トップ 差分 一覧 Farm ソース 検索 ヘルプ PDF RSS ログイン

Diary/2007-4-19

Jarのマニフェストをいじる

マニフェスト中の、特定のキーの値が欲しいとき

Manifest manifest = jf.getManifest();
Attributes ma = manifest.getMainAttributes();
String classpath = (String)(ma.getValue("Class-Path"));

COINSを実行可能JARから利用する

実行可能なJarファイルでは、manifestのClass-Pathで必要なライブラリへの
パスを通すことができる。
したがって、COINSを自分でつくったアプリケーションから使う場合、
COINSをビルドしてjarにしたファイル
(COINSで配布されているjarではなくてclassesの下だけをjarにしたもの)
に対して、manifestでclass-pathを指定すればよいはずである。
が、Unix系(Linux、FreeBSD、MacOSX)ではうまく動かなかった。
# なぜかWindowsXPではうまく動いた。
これは、manifestの中のClass-PathがJavaのプロパティであるjava.class.pathに
含まれないために、COINSの中でクラスを動的にロードしようとしても
当該クラスをみつけることができないことに起因していた。
というわけで、これを回避するために、ちょっとハックしてみた。
coins-1.4.2.2-ja-searchClass_resursive.patch(705)
COINSのディレクトリの中で、

patch -p0 < coins-1.4.2.2-ja-searchClass_resursive.patch

とすれば適用できる。

COINSを実行可能JARから利用するための作業工程


たとえば、

wallet.jar
lib/coins.jar

というようなディレクトリ構成にしていて
wallet.jarは実行可能jarファイルで、manifestファイルで次のように

Manifest-Version: 1.0
Main-Class: net.wasamon.wallet.WalletGUI
Class-Path: lib/coins.jar

Class-Pathにlib/coins.jarをちゃんと指定していても

java -jar wallet.jar

としてLinuxやFreeBD、MacOSXで起動しようとすると
次のような例外が発生して実行できない。
ちなみにWindowsでダブルクリックするとなぜか実行できる。

Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: coins.driver.SuffixFactory
at coins.driver.CompilerDriver.initializeSuffixFactory(CompilerDriver.java:81)
at coins.driver.CompilerDriver.<init>(CompilerDriver.java:184)
at net.wasamon.wallet.WalletDriver.doCompile(WalletDriver.java:156)
at net.wasamon.wallet.WalletGUI.actionPerformed(WalletGUI.java:204)

ちなみに、

java -cp wallet.jar:lib/coins.jar net.wasamon.wallet.WalletGUI

とした場合には、ちゃんと実行できる。
もちろん、coins.driver.SuffixFactoryはcoins.jarに含まれている。
で、みてみると、coins.driver.CompilerDriver.initializeSuffixFactoryで
getAttachedFileInputStream(Class String)という関数を呼び出していて、
このメソッドが原因となる例外を発生している。
このメソッドでは、
System.getProperty("java.class.path")で得られる
パスから、該当するクラスを検索するために
ディレクトリあるいは、jarファイルであっても求めるクラスを探す
searchClassメソッドを呼び出し、この戻り値が全て偽の場合に、例外が投げられる。
ここで問題となるのは、

java -jar wallet.jar

として起動した場合には、System.getProperty("java.class.path")で得られるのが
wallet.jarだけであるということ。
たしかに、これじゃあ、coins.jarの方は検索していないわけだから見つかるはずもない。
なのでmanifestで指定したClass-Pathファイルについても検索してもらう必要がある。
たとえば、こんな感じ。

Manifest manifest = jf.getManifest();
Attributes ma = manifest.getMainAttributes();
String classpath = (String)(ma.getValue("Class-Path"));

Attrubutesには、get(Object)というメソッドもあるが、getValue(String)でないとダメ。
で後は、ここで得られたclasspathを空白で分割して、
それぞれについて再帰的にsearchClassを検索すればいい。
幅優先で探索するべきなのかもしれないけど、まあ、とりあえず深さ優先で。
ちなみに、オリジナルのsearchClassは、真偽値を返すメソッドだが、
これでは、新しくmanifest中のClass-Pathから得たパスを返すことができないので、
真の代わりにパスを文字列で返し、偽の場合はnullを返すようにした。

[Java]
[COINS]