プログラマメモ2 - programmer no memo2

[java]ランチャーを作成してアプリケーションを起動する 2006/07/21
2006/11/26

クラスローダ



javaアプリケーションを起動する際に、注意しないといけないのは昔からクラスパスだと思う。



アプリケーションによってはオープンソースのライブラリなど多く使用する場合、起動時に依存するライブラリについて全て暮らすパスを用意しないといけない。



個人的に、JAVA_HOMEにあるlib/extにライブラリを置いとくということはすすめないです。

というのは、《なんとなく動く》ということを引き起こしてしまいがちだからです。



よくあるのがjavaコマンドを使用する際にクラスパスを設定する方法です。

この場合、環境変数を設定して、プラットフォームごとのスクリプト(sh,bat)を用意するの面倒になります。



というわけで、下記のシナリオを考えてみました。



シナリオ:



アプリケーションとアプリケーションをスタートさせるランチャー部分を分ける。

ランチャー部分には環境変数XXX_HOMEなどの設定や、任意のクラスパスを設定したクラスローダを準備させる。

ランチャーは単純にjava -jar launcher.jarで動作させる。







これは軽量HTTPサーバのjettyのバージョンが5のソースコードを調べていたことが着想となります。





最終的には以下のようなjarの構成になります。

launcher.jar

application.jar

dependency.jar



アプリケーションの動作環境は下記のようなディレクトリ構成になります。



HOME/

lib/*.jar

conf/設定ファイル





ここから

開発していてはまった問題です。技術的な説明は調べている途中です。



ランチャー部分のjarと、アプリケーション部分のjarを分離させること。

アプリケーション部分のjarが依存しているjarをみつけることができないケースが発生する。



(1)最初のクラスローダ(ランチャーjar、アプリケーションjar)

(2)オリジナルのクラスローダ(依存しているjar)



問題は(1)のクラスローダで読み込んだクラスからは(2)で読み込んだクラスがみつからないということです。



javaではクラスの呼び出しが発生したときにClassNotFoundが発生します。

ですので、依存しているjarのクラスを実際に使用する段階になってはじめてClassNotFoundが発生します。



クラスローダを重ねていく(委譲していく?)際の注意は、クラスの依存の方向を常に後から読み込んだものは先に読み込んだものに依存するようにする

ということだと現在は自分の中で結論づけています。




ですので、



(1)最初のクラスローダ(ランチャーjar)

(2)オリジナルのクラスローダ(アプリケーションjar、依存しているjar)



というにします。





ではランチャーjarはアプリケーションjarをどのようによびだすかといいますと、クラスローダのloadClassメソッドを使用してクラスをロードして、

リフレクションを使用してアプリケーションを起動します。



(2)オリジナルのクラスローダを設定するときは、



Thread.currentThread().setContextClassLoader(myClassLoader);.





jettyではアプリケーション部分の読み込みは、invokeMainという処理を用意しています。



public static void invokeMain(ClassLoader classloader,String classname,String[] args)

throws IllegalAccessException,InvocationTargetException,NoSuchMethodException,ClassNotFoundException

{

Class invoked_class=null;

invoked_class=classloader.loadClass(classname);

Class[] method_param_types=new Class[1];

method_param_types[0]=args.getClass();

Method main=null;

main=invoked_class.getDeclaredMethod("main",method_param_types);

Object[] method_params=new Object[1];

method_params[0]=args;

main.invoke(null,method_params);

}





クラスローダの振る舞いはじっくり調べてみると必要がありそうですね。





参考リンク:

One-JARでアプリケーションの配布を単純化する

試行錯誤

jettyのサイト

サンのサイト javadocクラスローダの説明っぽい

: