Byte Buddy Advice.OnMethodExit: constructor retransformation

廉价感情. 提交于 2021-02-11 15:28:58

问题


I'm trying to create Java Agent that will intercept FileInputStream/FileOutputStream constructors:

import java.io.*;
import java.lang.instrument.Instrumentation;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.AgentBuilder.InitializationStrategy;
import net.bytebuddy.agent.builder.AgentBuilder.Listener;
import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy;
import net.bytebuddy.agent.builder.AgentBuilder.TypeStrategy;
import net.bytebuddy.asm.Advice;
import static net.bytebuddy.dynamic.ClassFileLocator.CLASS_FILE_EXTENSION;
import static net.bytebuddy.matcher.ElementMatchers.*;
import net.bytebuddy.matcher.StringMatcher;

public class Agent {

    private static final List<Class<?>> BOOTSTRAP_CLASSES = Arrays.asList(
       Interceptor.class
    );

    private Agent() {
    }

    public static void premain(String arg, Instrumentation instrumentation) {
        injectBootstrapClasses(instrumentation);
        new AgentBuilder.Default()
            .with(RedefinitionStrategy.RETRANSFORMATION)
            .with(InitializationStrategy.NoOp.INSTANCE)
            .with(TypeStrategy.Default.REDEFINE)
            .ignore(new AgentBuilder.RawMatcher.ForElementMatchers(nameStartsWith("net.bytebuddy.").or(isSynthetic()), any(), any()))
            .with(new Listener.Filtering(
                new StringMatcher("java.io.FileInputStream", StringMatcher.Mode.EQUALS_FULLY)
                    .or(new StringMatcher("java.io.FileOutputStream", StringMatcher.Mode.EQUALS_FULLY)),
                Listener.StreamWriting.toSystemOut()))
            .type(named("java.io.FileInputStream").or(named("java.io.FileOutputStream")))
            .transform((builder, type, classLoader, module) ->
                builder
                    .constructor(any())
                    .intercept(Advice.to(Interceptor.class))
            )
            .installOn(instrumentation);
    }

    private static void injectBootstrapClasses(Instrumentation instrumentation) {
        try {
            File jarFile = File.createTempFile(Agent.class.getSimpleName(), ".jar");
            jarFile.deleteOnExit();
            try (JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile)))) {
                for (Class<?> bootstrapClass : BOOTSTRAP_CLASSES) {
                    String klassPath = classFileFullname(bootstrapClass);
                    jarOutputStream.putNextEntry(new JarEntry(klassPath));
                    jarOutputStream.write(readFully(bootstrapClass.getClassLoader().getResourceAsStream(klassPath)));
                }
            }
            instrumentation.appendToBootstrapClassLoaderSearch(new JarFile(jarFile));
        } catch (IOException exception) {
            throw new IllegalStateException("Cannot write jar file to disk", exception);
        }
    }

    private static String classFileFullname(Class<?> bootstrapClass) {
        return bootstrapClass.getName().replace('.', '/') + CLASS_FILE_EXTENSION;
    }

    private static byte[] readFully(InputStream input) throws IOException {
        byte[] buffer = new byte[8192];
        try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
            int bytesRead;
            while ((bytesRead = input.read(buffer)) != -1) {
                output.write(buffer, 0, bytesRead);
            }
            return output.toByteArray();
        }
    }

    public static class Interceptor{
        @Advice.OnMethodExit
        public static void intercept() {
            System.out.println("Exit constructor");
        }
    }

}

The above code prints:

java.lang.IllegalStateException: Cannot call super (or default) method for public java.io.FileOutputStream(java.lang.String) throws java.io.FileNotFoundException
    at net.bytebuddy.implementation.SuperMethodCall$Appender.apply(SuperMethodCall.java:97)
    at net.bytebuddy.asm.Advice$Appender$EmulatingMethodVisitor.resolve(Advice.java:7812)
    at net.bytebuddy.asm.Advice$Appender.apply(Advice.java:7765)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:620)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:609)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$RedefinitionClassVisitor$CodePreservingMethodVisitor.visitCode(TypeWriter.java:3969)
    at net.bytebuddy.jar.asm.ClassReader.b(Unknown Source)
    at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
    at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:2941)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1633)
    at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:171)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2676)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:8902)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:9303)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:9266)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1300(AgentBuilder.java:9044)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:9622)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:9572)
    at java.security.AccessController.doPrivileged(Native Method)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:9191)
    at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
    at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
    at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)
    at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector$ForRetransformation.doApply(AgentBuilder.java:6213)
    at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector.apply(AgentBuilder.java:6071)
    at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy.apply(AgentBuilder.java:4252)
    at net.bytebuddy.agent.builder.AgentBuilder$Default.installOn(AgentBuilder.java:8258)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$Delegator.installOn(AgentBuilder.java:9957)
    at com.github.soldierkam.agent.Agent.premain(Agent.java:46)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
    at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)

How to solve this problem? It looks like ByteBuddy tries to call OutputStream(String) constructor that does not exists. I'm using Byte Buddy 1.7.1 and JVM 1.8.0_131


回答1:


You need to use the Advice component as a visitor to enhance the existing constructor; currently, you are implementing a new method.

This should work:

builder.visit(Advice.to(Interceptor.class).on(isConstructor()));


来源:https://stackoverflow.com/questions/44747219/byte-buddy-advice-onmethodexit-constructor-retransformation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!