- PR -

自作ClassLoader

1
投稿者投稿内容
づか
会議室デビュー日: 2007/06/26
投稿数: 15
お住まい・勤務地: Tokyo, Japan
投稿日時: 2007-06-30 11:28
こんにちは。今度は、自作ClassLoaderではまっています。

自分で作っているアプリケーションサーバーに、ホットデプロイ形式でJarを読み込めるフォルダーがあって、そこからホットデプロイで読み込む為のクラスローダを作っていてはまっています。
次のことで、詳しい方いらっしゃったら教えてください。

・ClassLoaderがクラスをLoadするときのメカニズム
・InnerClassをどのように扱っているか
・Classloaderとしては「.property」ファイルをどのように処理しているか、処理する必要があるのか
・どのようなときにjava.lang.LinkageError: duplicate class definition:が出てしまうか

が分かりません。
自分でも調べているのですが、もし、詳しい方がいらっしゃったら御教示ください。
よろしくおねがいします。

_________________
Alinous-Core SQL-HTML Language commiter
Tomohiro Iizuka
http://jp.alinous.org
https://sourceforge.jp/projects/alinous-core/
づか
会議室デビュー日: 2007/06/26
投稿数: 15
お住まい・勤務地: Tokyo, Japan
投稿日時: 2007-06-30 11:48
自己レスですみません。

今、ClassLoaderをいろいろと調べています。
今までは、全部、Jarに存在するクラスが名前解決可能だったのですが、今回はそうではありません。使われていないクラスがあって、そのクラスが他のメンバーへの参照を持っていたりして、そこで、メソッドのメンバーにJARに含まれていないClassが使われていてもOKです。

なので、Reflectionでクラスを総スキャンする必要があるときには、そこで起きるNoClassDefExceptionを一部無視する必要があったりします。
また、クラスを読み込むときに、findClaass()が使われるケースとloadClass()が使われる2パターンがあるようです。

java.lang.LinkageError: duplicate class definition:はそのへんのメカニズムとどうやら、関係しているようです。
もうちょっと、また、調べてみます。

あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2007-06-30 12:11
引用:

づかさんの書き込み (2007-06-30 11:28) より:
こんにちは。今度は、自作ClassLoaderではまっています。
・ClassLoaderがクラスをLoadするときのメカニズム


ClassLoaderとURLClassLoaderのソースを眺めてみましょう。
loadClass() ⇒ findClass() ⇒ defineClass()

よくある方法では、URLClassLoaderを拡張して、
・ロード時の挙動を変えたい(子⇒親の優先度ポリシーなど)のならloadClass()
・クラスファイルの解決(読み込み)方法を拡張したいならfindClass()
これらをそれぞれ必要に応じてオーバーライドします。

単にクラスローダの構築後にjarをクラスパスに追加したいだけならば、
URLClassLoaderを拡張してaddURL(URL)をpublicにするだけでも可能です。

引用:

・InnerClassをどのように扱っているか


「外側クラス名$内側クラス名」という普通の名前になるので特に考慮は不要です。

引用:

・Classloaderとしては「.property」ファイルをどのように処理しているか、処理する必要があるのか


findResource()とfindResources()のオーバーライドが必要です。
優先度ポリシーを変える場合はgetResource()系もオーバーライドしましょう。

引用:

・どのようなときにjava.lang.LinkageError: duplicate class definition:が出てしまうか


LinkageErrorはクラスローダというよりはロードしたクラス側の問題かと。

「duplicate class definition」は出たことないので知りませんが、
同じクラスを同じクラスローダで複数回defineClass()した場合では?
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2007-06-30 12:19
Java仮想マシン仕様がこのあたり詳しいですよ。
一度読んでみることをお勧めします。

ClassLoaderを自作する場合、loadClass()をオーバーライドすることになります。
loadClass()は戻り値がClassなわけですが、どうやってClassオブジェクトを生成するかというとdefineClass()を利用して生成します。
つまり、jarなどのアーカイブされた状態からClassをあらわすバイナリを取得し、そのバイナリをdefineClass()に渡してClassを得るという流れです。

loadClass()のソースコードは以下のようになっています。(JDK1.6 java.lang.ClassLoaderより)
コード:
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
	// First, check if the class has already been loaded
	Class c = findLoadedClass(name);
	if (c == null) {
		try {
			if (parent != null) {
				c = parent.loadClass(name, false);
			} else {
				c = findBootstrapClass0(name);
			}
		} catch (ClassNotFoundException e) {
			// If still not found, then invoke findClass in order
			// to find the class.
			c = findClass(name);
		}
	}
	if (resolve) {
		resolveClass(c);
	}
	return c;
}


・findLoadedClass()の実装により読み込み済みClassの場合はキャッシュを使う
・親があれば親のloadClass()で読み込みを試みる
・親がなければfindClass()で探す

"duplicate class definition"のエラーメッセージからするとfindLoadedClass()の実装に問題があったりするのでしょうか?
多重に読み込むとリンクの際にエラーとなるのではないでしょうか。

「.property」ファイルなどは、つまるところクラスパス上に配置して
getResourceAsStream()で読み込むことが多いので、その点を実装してあれば大丈夫では?
getResourceAsStream()の実装を作り、その実装を利用してclassのバイナリを読み込んでdefineClass()するのが通常でしょうね。

InnerClassは独自の命名規約で変換されるはず。ちょっと資料が見つからないのですが
完全修飾名は外部クラス名$内部クラス名になります。
無名クラスの場合は外部クラス名$0といったような数字になります。
このあたりは探せばちゃんとした仕様がみつかるはず。
づか
会議室デビュー日: 2007/06/26
投稿数: 15
お住まい・勤務地: Tokyo, Japan
投稿日時: 2007-06-30 12:28
あしゅさん、nagiseさん

ありがとうございます。
おかげさまで解決しそうです。

リソース系のシステムプロパティもClassLoaderで扱っているとは、今回、正直、初めて知りました。

どうも、自分自身がこのClassLoaderで、Jarに入っているクラスのフルスキャンをかけていた関係で、その中でJarに含まれていないクラスがMethodの引数にあって例外が発生していて、その例外が別の例外にキャッチされてcauseになって、他の例外に変換されていた関係ではまっていたようです。

java.lang.LinkageError: duplicate class definitionは、やはり、2回同じクラスをdefineClass()したときに出ていました。ホットデプロイを実現するには、ClassLoaderを読み込むJar用に小分けして、Jarファイルに変更があったら子ClassLoaderごと捨てるのが良さげです。
(ClassLoaderにはUnloadするメソッドがなかったので)

これで、きちんとしたClassLoaderが出来そうです。
本当にありがとうございます。

1

スキルアップ/キャリアアップ(JOB@IT)