- PR -

Jar ファイルからのクラス読み込み処理

投稿者投稿内容
ヒラ
常連さん
会議室デビュー日: 2006/11/30
投稿数: 20
投稿日時: 2006-12-13 01:35
厚かましいのを承知で、これだけ教えていただけませんか??
コード:
public class Adapt extends URLClassLoader {
	private Adapt(URL[] urls, ClassLoader parent){
		super(urls, parent);
	}
	
	
	// override of ClassLoader method
	protected Class findClass(String name) throws ClassNotFoundException{
		String resname = name.replace('.', '/') + ".class";
		InputStream is = getResourceAsStream(resname);
		
		System.out.println("resname = " + resname);
		
		if(is == null){
			System.out.println("Unable to load class " + name + " for annotation checking");
			return super.findClass(name);
		}else{
			System.out.println("Processing class " + name + " ....");
			try{
				// read the entire content into byte array
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				byte[] buff = new byte[1024];
				int length;
				
				while((length = is.read(buff)) >= 0){
					bos.write(buff, 0, length);
				}
				byte[] bytes = bos.toByteArray();
				
				try{
				ClassReader cr = new ClassReader(bytes);
				ClassWriter cw = new ClassWriter(false);
				NotifierClassVisitor ncv = new NotifierClassVisitor(cw);
				cr.accept(ncv, false);
				bytes = cw.toByteArray();
				}catch(Exception e){
					throw new ClassNotFoundException(name, e);
				}
				
				// stores the adapted class on disk
				try{
					FileOutputStream fos = new FileOutputStream(resname + ".adapted");
					fos.write(bytes);
					fos.close();
					System.out.println("*** " + resname + ".adapted" + " created.");
					System.out.println();
				}catch(Exception e){
				}
				
				// return the (possibly modified) class
				return defineClass(name, bytes, 0, bytes.length);
		
			}catch(IOException e){
				throw new ClassNotFoundException("Error reading class " + name);
			}
		}
	}
	
	public static void main(String[] args) throws Exception{
		File file = new File(args[0]);
		JarFile jarfile = new JarFile(file);
		
		URL[] urls = new URL[1];
		urls[0] = file.toURL();
		
		System.out.println("urls[0] = " + urls[0]);
		
		
		for(Enumeration en = jarfile.entries(); en.hasMoreElements();){
			JarEntry entry = (JarEntry)en.nextElement();
			
			if(entry.getName().endsWith(".class")){
				String className = entry.getName().replace("/", "\\\\.").replaceAll("\\\\.class", "");
				System.out.println("className = " + className);
		
		if(args.length >= 1){
			try{
				// get paths to be used for loading
				ClassLoader base = ClassLoader.getSystemClassLoader();
				
				if(base instanceof URLClassLoader){
					urls = ((URLClassLoader)base).getURLs();
				}else{
					urls = new URL[]{new File(".").toURI().toURL()};
				}
				
				// load the target class using custom class loader
				Adapt loader = new Adapt(urls, base.getParent());
				System.out.println("args[0]: " + args[0]);
				System.out.println("urls[0]: " + urls[0]);
				System.out.println("urls: " + urls);
				Class clas = loader.loadClass(className);
				
				// invoke the "main" method of the application class
				Class[] ptypes = new Class[] {args.getClass()};
				Method main = clas.getDeclaredMethod("main", ptypes);
				String[] pargs = new String[args.length-1];
				System.arraycopy(args, 1, pargs, 0, pargs.length);
				Thread.currentThread().setContextClassLoader(loader);
				main.invoke(null, new Object[]{pargs});
			}catch(Exception e){
				e.printStackTrace();
			}
		}else{
			System.out.println("Usage: asm.Adapt report-class main-class args...");
		}
			}
		}
	}
}


これで実行すると、一つのクラスについては、.adaptedファイルを吐き出すのですが、その後エラーが出て終了します。
defineClassの部分なのですが、どうしてもわかりません。
けっこう切羽詰っているので最後のチャンスだと思ってお聞きします。
お分かりの方いらっしゃいましたら、どうか教えていただければと思います。
よろしくお願いします。
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2006-12-13 01:38
とりあえず、どういたエラーが発生するかだけでも貼り付けてはいかがでしょうか?
ヒラ
常連さん
会議室デビュー日: 2006/11/30
投稿数: 20
投稿日時: 2006-12-13 04:39
そうでした。
すみません。
エラーは下記のようなものが出ました。
コード:
Exception in thread "main" java.lang.NoClassDefFoundError: A (wrong name: source/A)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(Unknown Source)
	at java.lang.ClassLoader.defineClass(Unknown Source)
	at asm.Adapt.findClass(Adapt.java:95)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at asm.Adapt.main(Adapt.java:136)


Adapt.java:95は、return defineClass(name, bytes, 0, bytes.length);の部分で、
Adapt.java:136は、Class clas = loader.loadClass(className);の部分です。
クラスパスを任意ファイル(jar)に指定して動かしてみました。

よろしくお願いします。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2006-12-13 10:00
defineClassで定義しようとしたクラスの依存クラスが解決できないからでは?
ちょっと原因がはっきりわかりませんが。Aの定義ってどうなっていますか?
ヒラ
常連さん
会議室デビュー日: 2006/11/30
投稿数: 20
投稿日時: 2006-12-13 11:05
>かつのりさん
Aの定義というのは、Aのクラスファイルの中身ということですか?
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2006-12-13 11:19
引用:

ヒラさんの書き込み (2006-12-13 01:35) より:
コード:
// get paths to be used for loading
ClassLoader base = ClassLoader.getSystemClassLoader();

if(base instanceof URLClassLoader){
	urls = ((URLClassLoader)base).getURLs();
}else{
	urls = new URL[]{new File(".").toURI().toURL()};
}

// load the target class using custom class loader
Adapt loader = new Adapt(urls, base.getParent());





親クラスローダがbase.getParent()だからではないですか?
システムクラスローダのさらに親からの分岐になってしまいます。

あと、main()から起動されるので問題ないと思いますが、
親クラスローダにシステムクラスローダを使うのは良くないです。

Adapt.class.getClassLoader()
Thread.currentThread().getContextClassLoader()

Servletコンテナ等で動かす場合に親子関係の問題が起こるので、
このどちらかを親クラスローダとする方がよいと思います。

#Java EEなコンテナだとSecurityManagerの問題も起こりえますけど。
ヒラ
常連さん
会議室デビュー日: 2006/11/30
投稿数: 20
投稿日時: 2006-12-13 11:56
>あしゅさん
さっそく実行してみました。
が、以前同様のエラーメッセージが出続けています。

念のため確認なのですが、この際の親クラスローダを変更するというのは、
ClassLoader base = ClassLoader.getSystemClassLoader();
を、
ClassLoader base = Adapt.class.getClassLoader();
のように変更することですよね?
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2006-12-13 12:07
引用:

ヒラさんの書き込み (2006-12-13 11:56) より:
念のため確認なのですが、この際の親クラスローダを変更するというのは、
ClassLoader base = ClassLoader.getSystemClassLoader();
を、
ClassLoader base = Adapt.class.getClassLoader();
のように変更することですよね?



親とするクラスローダ自体はそうです。

コード:
// load the target class using custom class loader
Adapt loader = new Adapt(urls, base.getParent());


このbase.getParent()が怪しいです。baseでいいのでは?

それでも解決しない場合は、
JARに含まれるクラスを親クラスローダで解決可能であるか確認し、
解決できる場合はクラスパスから除外する必要があると思います。

もしくはJava標準の「親から子」から「子から親」の委譲ポリシーに
変更することでも対応できますが、さらにはまる事になると思います。

まずは、バイトコードの改変の前に単純にロードできるか確認すべきでは?

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