2017-10-19 12 views
3

JavaエージェントとJavassistを使用して、いくつかのJDKクラスにいくつかのログを追加しています。基本的に、システムがいくつかのTLSクラスをロードすると、Javassistはいくつかのバイトコードを追加して、いくつかの接続の問題をデバッグする手助けをします。のは、私はJavassistのを使用してそのクラスを呼び出すようにしようとしたとしましょう、私のエージェントの変換方法ではJavaエージェントでクラスパスにクラスを追加する

package com.something.myagent; 
public class MyAgentPrinter { 
    public static final void sayHello() { 
     System.out.println("Hello!"); 
    } 
} 

:ここ

は、このクラスは、エージェントJARに含まれている与えられた、問題だ

// this is only called for sun.security.ssl.Handshaker 
ClassPool cp = getClassPool(classfileBuffer, className); 
CtClass cc = cp.get(className); 
CtMethod declaredMethod = cc.getDeclaredMethod("calculateKeys"); 
declaredMethod.insertAfter("com.something.myagent.MyAgentPrinter.sayHello();"); 
cc.freeze(); 
return cc.toBytecode(); 

ますそれがうまくいくと思うが、代わりに私はこれを得る:

java.lang.NoClassDefFoundError: com/something/myagent/MyAgentPrinter 
    at sun.security.ssl.Handshaker.printLogLine(Handshaker.java) 
    at sun.security.ssl.Handshaker.calculateKeys(Handshaker.java:1160) 
    at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:292) 

thを追加する方法はありますかクラス[MyAgentPrinter]のアプリケーションのクラスパスに? the java.lang.instrument package documentationで指定され

答えて

5

あなたのエージェントのjarファイルは、すでに、クラスパスに追加されます。

エージェントクラスはシステムクラスローダによってロードされます(ClassLoader.getSystemClassLoaderを参照してください)。これは、通常、アプリケーションmainメソッドを含むクラスをロードするクラスローダーです。 premainメソッドは、アプリケーションmainメソッドと同じセキュリティおよびクラスローダーのルールで実行されます。

この理由から、Javassistは、バイトコードを変換するときにエージェントのクラスを見つけることができます。

ブートストラップローダーまたは拡張ローダーによってロードされる可能性が高いsun.**クラスを変換しようとしているようです。標準のクラスローディング委任は
application loader → extension loader → bootstrap loaderです。したがって、アプリケーションローダーが使用できるクラスは、拡張またはブートストラップローダーによってロードされたクラスでは使用できません。

だから、あなたは、ブートストラップローダにエージェントのクラスを追加する必要があり、すべてのクラスにそれらを利用できるようにする:

public class MyAgent { 
    public static void premain(String agentArgs, Instrumentation inst) throws IOException { 
     JarURLConnection connection = (JarURLConnection) 
      MyAgent.class.getResource("MyAgent.class").openConnection(); 
     inst.appendToBootstrapClassLoaderSearch(connection.getJarFile()); 

     // proceed 
    } 
} 

それはクラスは、あなたがしたいつまり前に、他のアクションの前にこれを行うことが重要ですインストルメントされたコードが利用可能になったことが読み込まれました。これは、エージェントクラス自体、すなわちpremainメソッドを含むクラスは、計装されたコードによってアクセスすることができないことを意味する。 Agentクラスは、意図しない早期ロードを避けるために、MyAgentPrinterへの直接参照を持つべきではありません。

より確実な方法は、the “Manifest Attributes” section of the package documentationを参照して、エージェントの開始前にエントリが追加されるように、Boot-Class-Pathエントリをエージェントのjarのマニフェストに追加することです。しかし、その後、jarファイルの名前を変更してはいけません。

+0

システムクラスローダー(ClassLoader.getSystemClassLoaderを参照)によってエージェントクラスがロードされますが、問題の問題はクラス 'AgentPrinter'が見つからないということです。 'AgentPrinter'が' MyAgent'と同じディレクトリにある場合は、それ以外の場合はそうであるかもしれません。 – rakwaht

+0

@rakwaht:Javassistが失敗しない理由を説明します。失敗します。 – Holger

+0

あなたは正しいと思われます。私はそれが間違っていたので私の答えを削除しました。 PS:私はupvoted :) – rakwaht

関連する問題