意図:
私はjava.lang.instrumentパッケージを使用して、Java プログラム用のインストルメンテーションを作成しています。各メソッドの最初と最後にメソッド呼び出しを追加するために、このシステムを介してバイトコード操作を使用するという考え方です。一般的に言えば、変更された Java メソッドは次のようになります。
public void whateverMethod(){
MyFancyProfiler.methodEntered("whateverMethod");
//the rest of the method as usual...
MyFancyProfiler.methodExited("whateverMethod");
}
MyFancyProfiler
premain
は、メソッド ( の一部) の実行中に初期化される、比較的複雑なシステムへのエントリ ポイントjava.lang.instrument
です。
編集-この質問の解決策でMyFancyProfiler
説明されているようなメカニズムを通じて、システムの残りの部分への参照を取得する静的 API が含まれています。参照は として取得され、リフレクションを介して適切な呼び出しが行われるため、現在の ClassLoader が基礎となるクラスを認識していなくても機能します。Object
困難
単純な Java プログラムの場合、このアプローチはうまく機能します。「実際の」アプリ (ウィンドウ化されたアプリケーション、特にRCP/OSGiアプリケーションなど) の場合、ClassLoaders
. ClassLoaders
クラスを見つける方法がわからない人もいるMyFancyProfiler
ので、 で静的メソッドを呼び出そうとすると例外がスローされMyFancyProfiler
ます。
これに対する私の解決策(および私の実際の問題が発生している場所)は、現在、反射的に呼び出しMyFancyProfiler
て遭遇したそれぞれに「注入」することです。その要点は次のとおりです。ClassLoader
defineClass
public byte[] transform(ClassLoader loader, String className, /* etc... */) {
if(/* this is the first time I've seen loader */){
//try to look up `MyFancyProfiler` in `loader`.
if(/* loader can't find my class */){
// get the bytes for the MyFancyProfiler class
// reflective call to
// loader.defineClass(
// "com.foo.bar.MyFancyProfiler", classBytes, 0, classBytes.length);
}
}
// actually do class transformation via ASM bytecode manipulation
}
詳細情報を編集- このインジェクションの理由は、ロードされた ClassLoader に関係なく、すべてのクラスがMyFancyProfiler.methodEntered
直接呼び出すことができるようにするためです。その呼び出しを行ったら、MyFancyProfiler
リフレクションを使用してシステムの残りの部分とやり取りする必要があります。そうしないと、直接参照しようとすると InvocationTargetException または NoClassDef 例外が発生します。私は現在、「直接的な」依存関係MyFancyProfiler
がJREシステムクラスだけであるように動作しているので、問題ないようです。
問題
これでも機能します!ほとんどの時間!しかし、Eclipse をトレースしようとしている (コマンド ラインから IDE を起動している) ときに遭遇する少なくとも 2 つの個別の ClassLoader については、メソッドNullPointerException
内から次のようなメッセージが表示されます。ClassLoader.defineClass
java.lang.NullPointerException
at java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:500)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
// at my code that calls the reflection
ClassLoader.javaの 500 行目は、コンストラクター時に初期化される Set のように見える へdomains.add(pd)
の呼び出しであり、(私が知る限り)「デフォルト」である必要があります。したがって、その行が . を引き起こす明白な方法はわかりません。現在、私は困惑しており、誰かがこれについて何らかの洞察を提供できることを望んでいます。domains
pd
ProtectionDomain
ProtectionDomain
NullPointerException
defineClass
がこのように失敗する原因は何ですか? また、明らかな解決策がない場合は、全体的な問題に対する潜在的な代替アプローチを提供できますか?