raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.23k stars 804 forks source link

Error when trying to modify the response of a method assigned to a public static final config variable #1605

Closed jwalor closed 1 week ago

jwalor commented 6 months ago

jdk corretto-17.0.7 , and bytebuddy version is 1.14.9

I get :

None of [public static java.lang.Object com.bcp.fraud.streams.Main$MyInterceptor.intercept(com.bcp.fraud.streams.Main$MyCallable,java.lang.Object[]) throws java.lang.Exception] allows for delegation from public static org.apache.kafka.common.config.ConfigDef io.confluent.kafka.schemaregistry.rest.SchemaRegistryConfig.baseSchemaRegistryConfigDef()

this is my code:

new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(new FlowListener())
.ignore(nameStartsWith("net.bytebuddy."))
.type(ElementMatchers.named("io.confluent.kafka.schemaregistry.rest.SchemaRegistryConfig"))
.transform((builder, typeDescription, classLoader, module, protectionDomain) -> { //return builder; return builder
.method(named("baseSchemaRegistryConfigDef")) .intercept(MethodDelegation.to(MyInterceptor.class));
}) .installOn(inst);

What I want is to modify a routine of that method in Java, what should I use?

I use net.bytebuddy.agent.ByteBuddyAgent.install method to instrumentation at runtime.

raphw commented 6 months ago

What's your interceptor class, including annotations and imports?

jwalor commented 6 months ago

Hi , thanks for replying, Is it technically possible to modify this method? image

--> Code: `import io.confluent.kafka.schemaregistry.rest.SchemaRegistryConfig; import io.confluent.rest.RestConfigException; import javassist.NotFoundException; import net.bytebuddy.agent.ByteBuddyAgent; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.MemberSubstitution; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.utility.JavaModule; import org.apache.kafka.common.config.ConfigDef;

import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; import java.util.Properties; import static net.bytebuddy.matcher.ElementMatchers.*;

public class MainX2 {

public static void main(String[] args) throws RestConfigException, NotFoundException {
    SchemaRegistryConfig test = new SchemaRegistryConfig(new Properties());
    Instrumentation instrumentation = ByteBuddyAgent.install();
    new AgentBuilder.Default()
            .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
            .with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
            .with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
            .with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
            .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
            .type(ElementMatchers.named("io.confluent.kafka.schemaregistry.rest.SchemaRegistryConfig"))
            .transform(new AgentBuilder.Transformer() {
                @Override
                public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
                                                        ClassLoader classLoader, JavaModule module, ProtectionDomain protectionDomain) {
                    try {
                        return builder
                                .visit(MemberSubstitution.strict()
                                        .method(named("baseSchemaRegistryConfigDef"))
                                        .replaceWith(MainX2.class.getMethod("baseSchemaRegistryConfigDef"))
                                        .on(any()));
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }

                }
            }).installOn(instrumentation);

    SchemaRegistryConfig teSchemaRegistryConfig = new SchemaRegistryConfig(new Properties());

    System.out.println(teSchemaRegistryConfig.baseSchemaRegistryConfigDef().configKeys().size());

}

public static ConfigDef baseSchemaRegistryConfigDef() {
    return new ConfigDef();
}

` I would like to know if it is possible to replace the body of that method with a modified one. I have tried some examples but no success.

raphw commented 6 months ago

It is, but the static initializer will not be rerun on a retransformation. In this case, you would need to hook before the class is loaded for the first time.

jwalor commented 6 months ago

I agree Rafael, with AgentBuilder could I achieve it? If you have any reference it would help me a lot for what I am looking for

raphw commented 6 months ago

You mean rerun the static initializer? That is not possible, unfortunately, the JVM does not support it.

jwalor commented 6 months ago

If I understood what you indicated... ("you would need to hook before the class is loaded for the first time.") could you give me some reference on how I could resolve this case? thank you so much :)

jwalor commented 6 months ago

@raphw
Could you just tell me which api to use?

raphw commented 6 months ago

You are doing it right, but there's nothing that can fix your problem if the initializer already ran.

jwalor commented 6 months ago

Thank you very much for the support! , I found everything about bytebuddy interesting.