package net.bytebuddy.test.utility;
import net.bytebuddy.agent.ByteBuddyAgent;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.instrument.Instrumentation;
import java.security.AccessController;
import java.util.logging.Logger;
/**
* This rules assures that the running JVM is a JDK JVM with an available
* <a href="https://blogs.oracle.com/CoreJavaTechTips/entry/the_attach_api">attach API</a>.
*/
public class AgentAttachmentRule implements MethodRule {
private final boolean available;
public AgentAttachmentRule() {
available = ByteBuddyAgent.AttachmentProvider.DEFAULT.attempt().isAvailable();
}
@Override
public Statement apply(Statement base, FrameworkMethod method, Object target) {
Enforce enforce = method.getAnnotation(Enforce.class);
if (enforce != null) {
if (!available) {
return new NoOpStatement("The executing JVM does not support runtime attachment");
}
Instrumentation instrumentation = ByteBuddyAgent.install(ByteBuddyAgent.AttachmentProvider.DEFAULT);
if (enforce.redefinesClasses() && !instrumentation.isRedefineClassesSupported()) {
return new NoOpStatement("The executing JVM does not support class redefinition");
} else if (enforce.retransformsClasses() && !instrumentation.isRetransformClassesSupported()) {
return new NoOpStatement("The executing JVM does not support class retransformation");
} else if (enforce.nativeMethodPrefix() && !instrumentation.isNativeMethodPrefixSupported()) {
return new NoOpStatement("The executing JVM does not support class native method prefixes");
}
}
return base;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Enforce {
boolean redefinesClasses() default false;
boolean retransformsClasses() default false;
boolean nativeMethodPrefix() default false;
}
private static class NoOpStatement extends Statement {
private final String reason;
public NoOpStatement(String reason) {
this.reason = reason;
}
@Override
public void evaluate() throws Throwable {
Logger.getLogger("net.bytebuddy").warning("Ignoring test case: " + reason);
}
}
}