Search code examples
javabytecodeagentbyte-buddy

Method intercepted twice even though it was called once


In the following code snippet I'm calling the method doStuff once on an instance of Subclass. However it is intercepted twice.

Note that doStuff was defined in the parent class SuperClass. If doStuff was defined in SubClass the interception logic would work as expected: only one interception.

Am I using Byte Buddy incorrectly?

package com.test;

import static net.bytebuddy.matcher.ElementMatchers.any;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;

import java.util.concurrent.Callable;

import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType.Builder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;

import org.junit.Test;

public class ReproBugTest {

    @Test
    public void reproBug() {

        new AgentBuilder.Default().type(nameStartsWith("com.test"))
                                    .transform(new AgentBuilder.Transformer() {

                                        @Override
                                        public Builder<?> transform(
                                                Builder<?> builder,
                                                TypeDescription td) {

                                            return builder.method(any())
                                                            .intercept(
                                                                    MethodDelegation.to(MethodInterceptor.class));
                                        }
                                    })
                                    .installOn(
                                            ByteBuddyAgent.installOnOpenJDK());

        SubClass subClass = new SubClass();
        subClass.doStuff();
    }
}

class SuperClass {
    public void doStuff() {
        System.out.println("Doing stuff...");
    }
}

class SubClass extends SuperClass {
}

class MethodInterceptor {

    @RuntimeType
    public static Object intercept(@SuperCall Callable<?> zuper)
            throws Exception {

        // Intercepted twice, bug?
        System.out.println("Intercepted");

        Object returnValue = zuper.call();

        return returnValue;
    }
}

Solution

  • You are intercepting the method call for every type, i.e. for both Subclass and SuperClass. You need to further specify your interceptor for what methods to intercept. In you case, you only want to intercept methods if they are declared by a given type.

    This is easy to implement. Instead of builder.method(any()), you should intercept builder.method(isDeclaredBy(td)). This way, a method is only intercepted if it is declared by the intercepted type.

    Finally, I can see from, your source code that you are using an older version of Byte Buddy. Version 0.7-rc6 runs stable, has additional features and fixes several bugs. (However, some APIs still need to be changed.)