package net.bytebuddy.agent.builder;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ByteArrayClassLoader;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.test.utility.*;
import net.bytebuddy.utility.JavaModule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.none;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
public class AgentBuilderDefaultApplicationResubmissionTest {
private static final String FOO = "foo";
private static final long TIMEOUT = 1L;
@Rule
public MethodRule agentAttachmentRule = new AgentAttachmentRule();
@Rule
public MethodRule integrationRule = new IntegrationRule();
private ClassLoader classLoader;
@Before
public void setUp() throws Exception {
classLoader = new ByteArrayClassLoader(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassFileExtraction.of(Foo.class));
}
@Test
@AgentAttachmentRule.Enforce(retransformsClasses = true)
@IntegrationRule.Enforce
public void testResubmission() throws Exception {
// A redefinition reflects on loaded types which are eagerly validated types (Java 7- for redefinition).
// This causes type equality for outer/inner classes to fail which is why an external class is used.
final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
try {
assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class));
ClassFileTransformer classFileTransformer = new AgentBuilder.Default(new ByteBuddy().with(TypeValidation.DISABLED))
.ignore(none())
.disableClassFormatChanges()
.with(AgentBuilder.LocationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.withResubmission(new AgentBuilder.RedefinitionStrategy.ResubmissionScheduler() {
@Override
public boolean isAlive() {
return true;
}
@Override
public Cancelable schedule(final Runnable job) {
return new Cancelable.ForFuture(scheduledExecutorService.scheduleWithFixedDelay(job, TIMEOUT, TIMEOUT, TimeUnit.SECONDS));
}
})
.type(ElementMatchers.is(Foo.class), ElementMatchers.is(classLoader)).transform(new FooTransformer())
.installOnByteBuddyAgent();
try {
Class<?> type = classLoader.loadClass(Foo.class.getName());
Thread.sleep(TimeUnit.SECONDS.toMillis(TIMEOUT * 3));
assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) FOO));
} finally {
ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer);
}
} finally {
scheduledExecutorService.shutdown();
}
}
@Test
public void testResubmissionCancelationNonOperational() throws Exception {
AgentBuilder.RedefinitionStrategy.ResubmissionScheduler.Cancelable.NoOp.INSTANCE.cancel();
}
@Test
public void testResubmissionCancelationForFuture() throws Exception {
Future<?> future = mock(Future.class);
new AgentBuilder.RedefinitionStrategy.ResubmissionScheduler.Cancelable.ForFuture(future).cancel();
verify(future).cancel(true);
verifyNoMoreInteractions(future);
}
@Test
public void testObjectProperties() throws Exception {
ObjectPropertyAssertion.of(AgentBuilder.RedefinitionStrategy.ResubmissionScheduler.Cancelable.NoOp.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.RedefinitionStrategy.ResubmissionScheduler.Cancelable.ForFuture.class).apply();
}
public static class Foo {
public String foo() {
return null;
}
}
private static class FooTransformer implements AgentBuilder.Transformer {
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
return builder.method(named(FOO)).intercept(FixedValue.value(FOO));
}
}
}