Search code examples
javanoclassdeffounderrorbyte-buddyjavaagents

calling lambda from an intercept method with bytebuddy raises java.lang.NoClassDefFoundError:


I am trying to do some instrumentation using ByteBuddy and a java agent. In one of the steps, I would like to capture the stacktrace and filter it with the calls that I care about. Let's imagine a premain function looks like this:

public class SeleniumReporter {
    public static void premain(final String agentArgs, final Instrumentation inst) {

        new AgentBuilder.Default()
                .with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager())
                .type(named("org.openqa.selenium.remote.RemoteWebDriver"))
                .transform((builder, type, classLoader, module) -> builder
                        .method(nameStartsWith("findElement")
                                .and(takesArguments(By.class).or(takesArguments(String.class, String.class)))
                                .and(isPublic())
                        )
                        .intercept(Advice.to(FindElementInterceptor.class))
                )
                .installOn(inst);
    }
}

and an interceptor takes this form:

public class FindElementInterceptor {
    @Advice.OnMethodExit
    public static void log(@Advice.This RemoteWebDriver driver, @Advice.Origin String method, @Advice.AllArguments Object[] args) {
        /*
        ... Some extra code
        */
        
        final String stackTrace = Arrays.stream(Thread.currentThread().getStackTrace())
                .map(t -> String.format("%s:%s", t.getClassName(), t.getMethodName()))
                .filter(s -> !s.startsWith("org.codehaus.plexus."))
                .filter(s -> !s.startsWith("org.apache.maven."))
                .collect(Collectors.joining(";"));

        System.out.println(stackTrace);
    }
}

Running that code will throw a java.lang.NoClassDefFoundError because the lambda expression are not loaded. Hence, my question is: What can I do to ensure there are loaded? I was thinking of creating a class and load it through ByteBuddy with all the utils. Is there a more elegant way?


Solution

  • Byte Buddy cannot copy the lambda expressions from the advice method to the target method. Lambdas are technically speaking only private methods in the class that defines it which will not be available to the target class.

    Use regular iteration instead to avoid this problem. Alternatively, you need to implement the expressions in individual classes and inject these classes into the target class loader using a ClassInjector.