jak retransformować klasę w czasie wykonywania

Tring modify klasy, które już załadowane w jvm. Rozwiązanie, które znalazłem to:

  • 1. Załącz agenta do jvm określonego przez pid. (np. 8191) (kody: AttachTest)
  • 2. Znajdź klasę, którą chcesz zmodyfikować z tych, które zostały już załadowane w jvm(np. 8191).
  • 3. Dodaj transformator za pomocą instrumentu (kody: AgentMain)
  • 4. Modyfikuj klasę (np. osobę) w metodzie transform (Kody: Demotransformator)
  • 5. Retransformuj class using retransformClasses

Działa dobrze od pierwszego do piątego kroku, ale są problemy w retransformClasses. Ponownie wywołał transform, który zawiera kody do modyfikacji klasy. I modyfikuje inne klasy, których nigdy nie chcę modyfikować. Myślę, że problem może wystąpić podczas addTransformer lub retransformClasses. Ale nadal jestem zdezorientowany. Jak retransformować klasę? Jakieś pomysły? thx

public class AttachTest {
    public static void main(String[] args) throws AttachNotSupportedException,
        IOException, AgentLoadException, AgentInitializationException { 
        String agentPath = "D:\\work\\workspace\\myjar\\loaded.jar";
        String vid = args[0]; 
        VirtualMachine vm = VirtualMachine.attach(vid);
        vm.loadAgent(agentPath);
    }
}

//Agent

public class AgentMain {
    public static void agentmain (String agentArgs, Instrumentation inst)
        throws ClassNotFoundException, UnmodifiableClassException,
        InterruptedException {
    Class<?> [] allLoadedClasses = inst.getAllLoadedClasses();
        String tmpString = null;
        for (int i = 0; i<allLoadedClasses.length; i++) {
        tmpString = allLoadedClasses[i].getName();


        if (0 != tmpString.length()) {
            if (-1 != tmpString.lastIndexOf(".")) {
                tmpString = tmpString.substring(tmpString.lastIndexOf(".")+1,tmpString.length());
            }
            if (tmpString.equals("Person")) {

                inst.addTransformer(new DemoTransformer(), true);
                inst.retransformClasses(allLoadedClasses[i]);

                }
            }
        }
    }
}

|

public class DemoTransformer implements ClassFileTransformer {

    @Override
    public byte[] transform (ClassLoader loader, String className,
        Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
        byte[] classfileBuffer) throws IllegalClassFormatException {

    ModifyMethodTest tm = new ModifyMethodTest(classfileBuffer);

    byte[] byteArray = null;
    try {
        byteArray = tm.modiySleepMethod();

    } catch (Exception e) {

        e.printStackTrace();
    }


    return byteArray;
    }
}

Wyjścia: / ALIGN = "LEFT" / PROGRAM

javax.management.RuntimeMBeanException: java.lang.RuntimeException: Failed to transform [Person]
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.rethrow(DefaultMBeanServerInterceptor.java:856)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.rethrowMaybeMBeanException(DefaultMBeanServerInterceptor.java:869)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:838)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:761)
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1427)
    at javax.management.remote.rmi.RMIConnectionImpl.access$200(RMIConnectionImpl.java:72)
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1265)
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1360)
    at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:788)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:305)
    at sun.rmi.transport.Transport$1.run(Transport.java:159)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:619)
    at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255)
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:142)
    at com.sun.jmx.remote.internal.PRef.invoke(Unknown Source)
    at javax.management.remote.rmi.RMIConnectionImpl_Stub.invoke(Unknown Source)
    at javax.management.remote.rmi.RMIConnector$RemoteMBeanServerConnection.invoke(RMIConnector.java:993)
    at AttachStackOverflow.main(AttachStackOverflow.java:57)
Caused by: java.lang.RuntimeException: Failed to transform [Person]
    at loaded3.TransformerService.transform(TransformerService.java:75)
    at loaded3.TransformerService.transformClass(TransformerService.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.sun.jmx.mbeanserver.StandardMBeanIntrospector.invokeM2(StandardMBeanIntrospector.java:93)
    at com.sun.jmx.mbeanserver.StandardMBeanIntrospector.invokeM2(StandardMBeanIntrospector.java:27)
    at com.sun.jmx.mbeanserver.MBeanIntrospector.invokeM(MBeanIntrospector.java:208)
    at com.sun.jmx.mbeanserver.PerInterface.invoke(PerInterface.java:120)
    at com.sun.jmx.mbeanserver.MBeanSupport.invoke(MBeanSupport.java:262)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:836)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:761)
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1427)
    at javax.management.remote.rmi.RMIConnectionImpl.access$200(RMIConnectionImpl.java:72)
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1265)
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1360)
    at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:788)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:305)
    at sun.rmi.transport.Transport$1.run(Transport.java:159)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
    at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
    at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:124)
    at loaded3.TransformerService.transform(TransformerService.java:72)
    ... 31 more

WYNIKI: PROGRAM DOCELOWY

print Call sayHello()
print Hello World!
Supported Redefine
Supported Retransform
Call transform() in TransformerService
Add transformer
support redefine. return TRUE
support retransforme. return TRUE
IsModifiable class "class Person". return TRUE
Retransform classes
Number of times to Call transform() in DemoTransformer:1
####ASM CODE####
consturct ModifyMethodTest
Call modifySleepMethod
new classreader
new classwriter
construct ModifyClassAdapter
sayHello
consturct Modifymethod
[arg1] = java/io/PrintStream  [arg2] = println  #5
[arg1] = java/io/PrintStream  [arg2] = println  #13
[arg1] = java/util/concurrent/TimeUnit  [arg2] = sleep  #22
[arg1] = java/io/PrintStream  [arg2] = println  #30
sayHello2
consturct Modifymethod
[arg1] = java/io/PrintStream  [arg2] = println  #5
[arg1] = java/io/PrintStream  [arg2] = println  #13
<init>
consturct Modifymethod
[arg1] = java/lang/Object  [arg2] = <init>  #1
main
consturct Modifymethod
[arg1] = Person  [arg2] = <init>  #4
[arg1] = Person  [arg2] = sayHello  #9
[arg1] = Person  [arg2] = sayHello2  #13
[arg1] = java/lang/InterruptedException  [arg2] = printStackTrace  #21
getName
consturct Modifymethod
setName
consturct Modifymethod
Call visitend
Finished to call modifymethodtest
####End of ASM CODE
Remove transformer
Call transform() in TransformerService
Add transformer
support redefine. return TRUE
support retransforme. return TRUE
IsModifiable class "class Person". return TRUE
Retransform classes
Number of times to Call transform() in DemoTransformer:2
####ASM CODE####
consturct ModifyMethodTest
Call modifySleepMethod
new classreader
new classwriter
construct ModifyClassAdapter
sayHello
consturct Modifymethod
[arg1] = java/io/PrintStream  [arg2] = println  #5
[arg1] = java/io/PrintStream  [arg2] = println  #13
[arg1] = java/util/concurrent/TimeUnit  [arg2] = sleep  #22
[arg1] = java/io/PrintStream  [arg2] = println  #30
sayHello2
consturct Modifymethod
[arg1] = java/io/PrintStream  [arg2] = println  #5
[arg1] = java/io/PrintStream  [arg2] = println  #13
<init>
consturct Modifymethod
[arg1] = java/lang/Object  [arg2] = <init>  #1
main
consturct Modifymethod
[arg1] = Person  [arg2] = <init>  #4
[arg1] = Person  [arg2] = sayHello  #9
[arg1] = Person  [arg2] = sayHello2  #13
[arg1] = java/lang/InterruptedException  [arg2] = printStackTrace  #21
getName
consturct Modifymethod
setName
consturct Modifymethod
Call visitend
Finished to call modifymethodtest
####End of ASM CODE
Remove transformer
print in sayHello()
print Call sayHello2()
print Hello World!2
Author: DIMMSum, 2013-09-02

1 answers

Krótka Odpowiedź

  • nie sprawdzaj wszystkich załadowanych klas z oprzyrządowania. Zamiast tego, po prostu sprawdź nazwę klasy przekazaną transformatorowi i jeśli pasuje do twojej klasy docelowej, przekształć ją. W przeciwnym razie po prostu zwróć przekazany classfileBuffer niezmodyfikowany.
  • wykonaj ustawione wywołania poza transformatorem (tzn. w Twoim przypadku wykonaj następujące czynności od agenta), więc zainicjuj transformator nazwą klasy, którą chcesz przekształcić (będzie to wewnętrzny format więc zamiast foo.bar.Snafu, będziesz chciał dopasować się do foo / bar / Snafu. Następnie dodaj transformator, wywołaj retransform, a następnie usuń transformator.
  • aby wywołać retransform, będziesz potrzebował rzeczywistej klasy [pre-transform], którą możesz znaleźć wywołując klasę .forName (w agentmain), lub jeśli koniecznie musisz, możesz go znaleźć w Intrumentacji .getAllLoadedClasses () jako ostatnia Ośrodek wypoczynkowy. Jeśli Klasa docelowa nie została załadowana, będziesz potrzebował classloadera do wywołania klasy .forName (name, boolean, classloader) w takim przypadku możesz przekazać adres URL do docelowej klasy class-path w głównym łańcuchu args agenta.

Długa Odpowiedź

Oto kilka zaleceń:

  1. oddziel operację, którą wywołujesz na 2 oddzielne operacje:
    1. zainstaluj agenta. Wystarczy to zrobić raz.
    2. Przekształć klasę docelową [es]. Możesz chcieć to zrobić N razy.
  2. zaimplementowałbym 1.2 rejestrując prosty JMX MBean podczas instalacji agenta. Ten MBean powinien zapewnić operację podobną do public void transformClass(String className). i powinny być inicjowane z odniesieniem do nabytej przez agenta instancji oprzyrządowania. Klasa MBean, interfejs i wszelkie wymagane klasy 3rd party powinny być zawarte w załadowanym agenta.jar . Należy również zawiera klasę ModifyMethodTest (co zakładam, że już to robi).
  3. w tym samym czasie, gdy instalujesz swój jar agenta, zainstaluj również agenta zarządzania z $ JAVA_HOME/lib / management-agent.jar , który aktywuje agenta zarządzania, abyś mógł wywołać operację przekształcania w MBean, który zamierzasz zarejestrować.
  4. Sparametryzuj klasę Demotransformatora, aby przyjęła wewnętrzną formę nazwy klasy, którą chcesz przekształcić. (tj. jeśli twoja klasa binarna to foo.bar.Snafu , forma wewnętrzna będzie foo / bar / Snafu . Gdy instancja Demotransformatora zaczyna otrzymywać wywołania zwrotne transformacji, Ignoruj wszystkie nazwy klas, które nie pasują do wewnętrznej nazwy klasy formularza, którą podałeś. (tzn. po prostu zwróć classfileBuffer unmodified)
  5. Twoja operacja tranformer MBean transformClass powinna wtedy:
    1. konwertuje przekazaną nazwę klasy na formę wewnętrzną.
    2. Utwórz nowy Demotransformator, przekazujący nazwę klasy formularzy wewnętrznych.
    3. Zarejestruj instancję Demotransformatora używając Instrumentation.addTransformer(theNewDemoTransformer, true) .
    4. wywołanie Instrumentation.retransformClasses(ClassForName(className)) (z nazwą klasy binarnej przekazaną operacji MBean). Kiedy to połączenie wróci, twoja klasa zostanie przekształcona.
    5. Usuń transformator za pomocą Intrumentation.removeTransformer(theNewDemoTransformer).

Poniżej znajduje się nieprzetestowane przybliżenie tego, co mam na myśli:

Transformator MBean

public interface TransformerServiceMBean {
    /**
     * Transforms the target class name
     * @param className The binary name of the target class
     */
    public void transformClass(String className);
}

Transformator Serwis

public class TransformerService implements TransformerServiceMBean {
    /** The JVM's instrumentation instance */
    protected final Instrumentation instrumentation;

    /**
     * Creates a new TransformerService
     * @param instrumentation  The JVM's instrumentation instance 
     */
    public TransformerService(Instrumentation instrumentation) {
        this.instrumentation = instrumentation;
    }

    /**
     * {@inheritDoc}
     * @see com.heliosapm.shorthandexamples.TransformerServiceMBean#transformClass(java.lang.String)
     */
    @Override
    public void transformClass(String className) {
        Class<?> targetClazz = null;
        ClassLoader targetClassLoader = null;
        // first see if we can locate the class through normal means
        try {
            targetClazz = Class.forName(className);
            targetClassLoader = targetClazz.getClassLoader();
            transform(targetClazz, targetClassLoader);
            return;
        } catch (Exception ex) { /* Nope */ }
        // now try the hard/slow way
        for(Class<?> clazz: instrumentation.getAllLoadedClasses()) {
            if(clazz.getName().equals(className)) {
                targetClazz = clazz;
                targetClassLoader = targetClazz.getClassLoader();
                transform(targetClazz, targetClassLoader);
                return;             
            }
        }
        throw new RuntimeException("Failed to locate class [" + className + "]");
    }

    /**
     * Registers a transformer and executes the transform
     * @param clazz The class to transform
     * @param classLoader The classloader the class was loaded from
     */
    protected void transform(Class<?> clazz, ClassLoader classLoader) {
        DemoTransformer dt = new DemoTransformer(clazz.getName(), classLoader);
        instrumentation.addTransformer(dt, true);
        try {
            instrumentation.retransformClasses(clazz);
        } catch (Exception ex) {
            throw new RuntimeException("Failed to transform [" + clazz.getName() + "]", ex);
        } finally {
            instrumentation.removeTransformer(dt);
        }       
    }
}

Transformator Klasy

public class DemoTransformer implements ClassFileTransformer {
    /** The internal form class name of the class to transform */
    protected String className;
    /** The class loader of the class */
    protected ClassLoader classLoader;
    /**
     * Creates a new DemoTransformer
     * @param className The binary class name of the class to transform
     * @param classLoader The class loader of the class
     */
    public DemoTransformer(String className, ClassLoader classLoader) {
        this.className = className.replace('.', '/');
        this.classLoader = classLoader;
    }

    /**
     * {@inheritDoc}
     * @see java.lang.instrument.ClassFileTransformer#transform(java.lang.ClassLoader, java.lang.String, java.lang.Class, java.security.ProtectionDomain, byte[])
     */
    @Override
    public byte[] transform(ClassLoader loader, String className,
            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
            byte[] classfileBuffer) throws IllegalClassFormatException {
        if(className.equals(this.className) && loader.equals(classLoader)) {
            return new ModifyMethodTest(classfileBuffer).modiySleepMethod();
        }
        return classfileBuffer;
    }

}

Agent

public class AgentMain {

    public static void agentmain (String agentArgs, Instrumentation inst) throws Exception {
        TransformerService ts = new TransformerService(inst);
        ObjectName on = new ObjectName("transformer:service=DemoTransformer");
        // Could be a different MBeanServer. If so, pass a JMX Default Domain Name in agentArgs
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        server.registerMBean(ts, on);
        // Set this property so the installer knows we're already here
        System.setProperty("demo.agent.installed", "true");     
    }

}

Instalator Agenta

public class AgentInstaller {
    /**
     * Installs the loader agent on the target JVM identified in <code>args[0]</code>
     * and then transforms all the classes identified in <code>args[1..n]</code>.
     * @param args The target JVM pid in [0] followed by the classnames to transform
     */
    public static void main(String[] args)  {
        String agentPath = "D:\\work\\workspace\\myjar\\loaded.jar";
        String vid = args[0]; 
        VirtualMachine vm = VirtualMachine.attach(vid);
        // Check to see if transformer agent is installed
        if(!vm.getSystemProperties().contains("demo.agent.installed")) {
            vm.loadAgent(agentPath);  
            // that property will be set now, 
            // and the transformer MBean will be installed
        }
        // Check to see if connector is installed
        String connectorAddress = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress", null);
        if(connectorAddress==null) {
            // It's not, so install the management agent
            String javaHome = vm.getSystemProperties().getProperty("java.home");
            File managementAgentJarFile = new File(javaHome + File.separator + "lib" + File.separator + "management-agent.jar");
            vm.loadAgent(managementAgentJarFile.getAbsolutePath());
            connectorAddress = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress", null);
            // Now it's installed
        }
        // Now connect and transform the classnames provided in the remaining args.
        JMXConnector connector = null;
        try {
            // This is the ObjectName of the MBean registered when loaded.jar was installed.
            ObjectName on = new ObjectName("transformer:service=DemoTransformer");
            // Here we're connecting to the target JVM through the management agent
            connector = JMXConnectorFactory.connect(new JMXServiceURL(connectorAddress));
            MBeanServerConnection server = connector.getMBeanServerConnection();
            for(int i = 1; i < args.length; i++) {
                String className = args[i];
                // Call transformClass on the transformer MBean
                server.invoke(on, "transformClass", new Object[]{className}, new String[]{String.class.getName()});
            }
        } catch (Exception ex) {
            ex.printStackTrace(System.err);
        } finally {
            if(connector!=null) try { connector.close(); } catch (Exception e) {}
        }
        // Done. (Hopefully)
    }
}

================= aktualizacja =================

Hej Nick; tak, to jedno z ograniczeń obecnych (np. Java 5-8) transformatorów klasy. Cytuję z : :

"retransformacja może zmieniać ciała metody, stałą pulę i atrybuty. Retransformacja nie może dodawać, usuwać ani zmieniać nazw pól lub metod, zmienić sygnatury metod lub zmienić dziedziczenie. Ograniczenia te mogą zostać zniesione w przyszłych wersjach. Plik klasowy bajty są sprawdzane, weryfikowane i instalowane dopiero po transformacje zostały zastosowane, jeśli wynikowe bajty są błędne ta metoda wyrzuci wyjątek."

Na marginesie, to samo ograniczenie jest udokumentowane dosłownie dla przedefiniowania klas też.

Jako takie, masz 2 opcje:

  1. Nie dodawaj nowych metod. Jest to często bardzo ograniczające i dyskwalifikuje użycie bardzo popularnych wzorców AOP w kodzie bajtowym, takich jak metoda pakowanie. W zależności od używanej biblioteki manipulacji kodem bajtowym możesz wprowadzić wszystkie funkcje, które chcesz istniejących metod. Niektóre biblioteki ułatwiają to niż inne. Lub, powinienem powiedzieć, niektóre biblioteki ułatwią to niż i inni

  2. Przekształć klasę, zanim zostanie załadowana. To używa tego samego ogólnego wzorca kodu, o którym już rozmawialiśmy, z wyjątkiem tego, że nie uruchamiasz transformacja poprzez wywołanie klasy retransformacyjnej. Zamiast tego rejestrujesz ClassFileTransformer, aby wykonać transformację przed Klasa zostanie załadowana a twoja klasa docelowa zostanie zmodyfikowana, gdy zostanie załadowana pierwsza klasa. W tym przypadku możesz dowolnie modyfikować klasę jak, pod warunkiem, że koniec produkt nadal można zwalidować. Pokonanie aplikacji do stempla (tzn. zdobycie swojej klasy zarejestrowane przed załadowaniem aplikacji klasy) będzie najprawdopodobniej wymagać polecenia w rodzaju javaagent , chociaż jeśli masz ścisłą kontrolę z cyklu życia aplikacji można to zrobić w bardziej tradycyjnym kodzie warstwy aplikacji. Jak mówiłem, musisz tylko zrobić na pewno masz transformator zarejestrowany przed klasą docelową jest załadowany.

Inną odmianą #2, której możesz użyć, jest symulacja zupełnie nowej klasy za pomocą nowego classloadera. Jeśli utworzysz nowy izolowany classloader, który nie deleguje do istniejącej klasy [załadowanej] , ale ma dostęp do kodu bajtowego klasy [rozładowanej] docelowej, zasadniczo odtwarzasz wymagania #2 powyżej, ponieważ JVM uważa to za zupełnie nową klasę.

================ Aktualizacja ================

W twoich ostatnich komentarzach, czuję, jakbym trochę zgubił ślad gdzie jesteś. W każdym razie Oracle JDK 1.6 zdecydowanie obsługuje retransform. Nie jestem zbyt zaznajomiony z ASM, ale ostatni błąd, który napisałeś wskazuje, że transformacja ASM w jakiś sposób zmodyfikowała schemat klasy, który nie jest dozwolony, więc retransform nie powiódł się.

Pomyślałem, że działający przykład zwiększy jasność. Te same klasy co wyżej (plus jedna klasa testowa zwana osobą) to tutaj . Jest kilka modyfikacji/dodatków:

  • operacja transform w TransformerService ma teraz 3 parametry:
    1. nazwa klasy binarnej
    2. nazwa metody dla instrumentu
    3. Wyrażenie [regularne] pasujące do sygnatury metody. (jeśli null lub empty, pasuje do wszystkich podpisów)
    4. rzeczywista modyfikacja kodu bajtowego odbywa się za pomocą Javassist w klasie ModifyMethodTest . Wszystkie instrumentation does is add a System.Wynocha.println to wygląda tak: -->Invoked method [com.heliosapm.shorthandexamples.Person.sayHello((I)V)]
  • AgentInstaller (który ma główną wersję demo) samo-instaluje agenta i usługę transform. (Łatwiejsze dla celów Dev/Demo, ale nadal będzie działać z innymi JVM)
  • Po zainstalowaniu agenta główny wątek tworzy instancję Person i po prostu pętle, wywołując dwie metody SayHello tej osoby.

Przed transformacją wyjście wygląda następująco.

Temp File:c:\temp\com.heliosapm.shorthandexamples.AgentMain8724970986698386534.jar
Installing AgentMain...
AgentMain Installed
Agent Loaded
Instrumentation Deployed:true
Hello [0]
Hello [0]
Hello [1]
Hello [-1]
Hello [2]
Hello [-2]

Osoba ma 2 SayHello metody, jedna bierze int, druga bierze String. (String one wypisuje tylko ujemny indeks pętli).

Po uruchomieniu AgentInstaller, agent jest zainstalowany i osoba jest wywoływana w pętli, podłączam się do JVM za pomocą JConsole:

Znalezienie AgentInstaller JVM

Przechodzę do TransformerService MBean i powołuję się na transformClass operacja. Podaję w pełni kwalifikowaną nazwę klasy [binary], nazwę metody do instrumentu oraz wyrażenie regex (I)v, które pasuje tylko sayHello metoda, która przyjmuje int jako argument. (Lub Mogę dostarczyć .*, lub nic, aby dopasować wszystkie przeciążenia). Wykonuję operację.

Wywołanie operacji

Teraz, kiedy wrócę do uruchomionego JVM i zbadam wyjście:

Examining class [com/heliosapm/shorthandexamples/Person]
Instrumenting class [com/heliosapm/shorthandexamples/Person]
[ModifyMethodTest] Adding [System.out.println("\n\t-->Invoked method [com.heliosapm.shorthandexamples.Person.sayHello((I)V)]");]
[ModifyMethodTest] Intrumented [1] methods

    -->Invoked method [com.heliosapm.shorthandexamples.Person.sayHello((I)V)]
Hello [108]
Hello [-108]

    -->Invoked method [com.heliosapm.shorthandexamples.Person.sayHello((I)V)]
Hello [109]
Hello [-109]
Zrobione. Metoda / align = "left" /

Należy pamiętać, że retransformacja jest dozwolona, ponieważ modyfikacja kodu bajtowego Javassist nie wprowadziła żadnych zmian poza wstrzyknięciem kodu do istniejącej metody.

Ma sens ?

 50
Author: Nicholas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-09 13:03:13