3

サービス用のプラグインを開発しています。それが機能するためには、サービスが提供しないいくつかのデータが必要です。

プラグインには厳密なロード/アンロード仕様があります。ベアプラグインは次のようになります。

public class Plugin extends JavaPlugin 
{    
    @Override
    public void onEnable() {} //Plugin enters here. Comparable to main(String[] args)

    @Override
    public void onDisable() {} //Plugin exits here, when service shuts down.
}

org.service.aClassというパッケージがあります。その中にはaMethodがあります。aMethodは次のようになります。

public boolean aMethod(boolean bool) {
return bool;
}

過度に単純化されたシナリオですが、機能します。私のプラグインは、aMethodが呼び出されるたびにboolの値を知る必要があります。これは私のプログラムにとって絶対に重要です。その値を取得する他の方法はありません。

aMethodをお勧めしますが、サービスの後にプラグインが読み込まれるため、これは機能しません。私が理解していることから、ロード時間の織り方は、後でロードされるため、ここでも適切ではありません。

それは機能しませんが、これが私が使用していた側面です。

public aspect LogAspect {

    pointcut publicMethodExecuted(): execution(* org.service.aClass.aMethod(..));

    after(): publicMethodExecuted(){
    cat(String.format("Entered method: %s.",
        thisJoinPoint.getSignature()));

    List<Object> arguments = Arrays.asList(thisJoinPoint.getArgs());
    List<Object> argTypes = new ArrayList<Object>();
    for (Object o: arguments) {  
        argTypes.add(o.getClass().toString());

    }

    cat(String.format("With argument types: %s and values: %s.",
            argTypes, arguments));


    cat(String.format("Exited method: %s.", thisJoinPoint.getSignature()));
    }

 public void cat(Object dog) {
    System.out.println("[TEST] " + dog);
    }

}

私は現在、AspectJ:In Actionの本を私の横で開いています。すべてのロード時のウィービングの例で、プログラムは-javaagentフラグで開始する必要があると記載されています。私のプロラムはプラグインなので、これが起こる可能性はありません。

ASMも調べました。ここで、プロファイラー(基本的に私がやりたいこと)を構築するための素晴らしいチュートリアルを見つけまし

それに関する問題は、起動時に-javaagentフラグとpublic static premainを再び使用することです。したがって、onEnableとonDisableしかないため、不適切です。

次に、JavaのAttachAPIについて知りました。クラスがロードされた後、その外観から、エージェントであるプロファイラーをアタッチできるようになります。完璧に見えますが、30分ほど検索したところ、理解できる良い例が見つかりませんでした。

誰か助けてもらえますか?これはツーインワンの質問です:AspectJをこれに使用できますか?もしそうなら、どのように?また、それができない場合、誰かが私をASMプロファイラーでAttach APIを使用するための正しい方向に向けることができますか?

前もって感謝します!

4

1 に答える 1

2

やったよ!

ここで概説したプロファイラー用のランタイム アタッチャーを作成しました。基本的には、プリメインの名前が「agentmain」に変更されています。

アタッチャーと他の便利な機能を備えた Util クラスを作成しました。アタッチャーは、プロファイルできることを示すマニフェストを使用して、エージェントで jar を作成することによって機能します。Util クラスは次のようになります。

    public class Util {

    /**
     * Gets the current JVM PID
     * @return
     * Returns the PID
     * @throws Exception
     */

    public static String getPidFromRuntimeMBean() {
    String jvm = ManagementFactory.getRuntimeMXBean().getName();
    String pid = jvm.substring(0, jvm.indexOf('@'));
    return pid;
    }

    /**
     * Attaches given agent classes to JVM
     * 
     * @param agentClasses
     * A Class<?>[] of classes to be included in agent
     * @param JVMPid
     * The PID of the JVM to attach to
     */

    public static void attachAgentToJVM(Class<?>[] agentClasses, String JVMPid) {

    try {


    File jarFile = File.createTempFile("agent", ".jar");
    jarFile.deleteOnExit();

    Manifest manifest = new Manifest();
    Attributes mainAttributes = manifest.getMainAttributes();
    mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
    mainAttributes.put(new Attributes.Name("Agent-Class"),
        Agent.class.getName());
    mainAttributes.put(new Attributes.Name("Can-Retransform-Classes"),
        "true");
    mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true");

    JarOutputStream jos = new JarOutputStream(new FileOutputStream(
        jarFile), manifest);


    for(Class<?> clazz: agentClasses) {         
        JarEntry agent = new JarEntry(clazz.getName().replace('.',
            '/')
            + ".class");
        jos.putNextEntry(agent);

    jos.write(getBytesFromIS(clazz.getClassLoader()
        .getResourceAsStream(
            clazz.getName().replace('.', '/') + ".class")));
    jos.closeEntry();
    }

    jos.close();
    VirtualMachine vm = VirtualMachine.attach(JVMPid);
    vm.loadAgent(jarFile.getAbsolutePath());
    vm.detach();
    } catch (Exception e) {
        e.printStackTrace();
    }

    }

    /**
     * Gets bytes from InputStream
     * 
     * @param stream
     * The InputStream
     * @return 
     * Returns a byte[] representation of given stream
     */

    public static byte[] getBytesFromIS(InputStream stream) {

    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    try {
        int nRead;
        byte[] data = new byte[16384];

        while ((nRead = stream.read(data, 0, data.length)) != -1) {
        buffer.write(data, 0, nRead);
        }

        buffer.flush();
    } catch (Exception e) {
        System.err.println("Failed to convert IS to byte[]!");
        e.printStackTrace();
    }

    return buffer.toByteArray();

    }

    /**
     * Gets bytes from class
     * 
     * @param clazz    
     * The class
     * @return
     * Returns a byte[] representation of given class
     */

    public static byte[] getBytesFromClass(Class<?> clazz) {            
    return getBytesFromIS(clazz.getClassLoader().getResourceAsStream( clazz.getName().replace('.', '/') + ".class"));   
    }

}

わかりやすくするために JavaDoc コメントを含めました。

使用例は次のとおりです。

Util.attachAgentToJVM(new Class<?>[] { Agent.class, Util.class,
        Profile.class, ProfileClassAdapter.class,
        ProfileMethodAdapter.class }, Util.getPidFromRuntimeMBean());   

アタッチャーは、Agent.class をメイン エージェントにする必要があることに注意してください。これは簡単に変更できます。Class[] の残りの部分は、一時的な agent.jar に含まれるクラスです。

IDE が「UnsatisfiedLinkError」について不平を言う場合、これに必要な attach.(dll|so) が JDK にのみ付属しているためです。%JAVA_PATH%/jre/lib にコピーして貼り付けるだけです。また、すべての com.sun インポートが含まれているため、JDK の tools.jar への参照を追加します。

編集:私は実用的な github の例を持っています。誰もがこれが便利だと思うかもしれません。ここある。

于 2012-09-05T23:15:28.550 に答える