package net.bytebuddy.agent.builder;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.build.EntryPoint;
import net.bytebuddy.build.Plugin;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.TypeResolutionStrategy;
import net.bytebuddy.dynamic.loading.ClassInjector;
import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.LoadedTypeInitializer;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import net.bytebuddy.test.utility.JavaVersionRule;
import net.bytebuddy.test.utility.MockitoRule;
import net.bytebuddy.test.utility.ObjectPropertyAssertion;
import net.bytebuddy.utility.JavaModule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
import org.junit.rules.TestRule;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.File;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Constructor;
import java.security.ProtectionDomain;
import java.util.*;
import static net.bytebuddy.matcher.ElementMatchers.none;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.*;
public class AgentBuilderDefaultTest {
private static final String FOO = "foo";
private static final byte[] QUX = new byte[]{1, 2, 3}, BAZ = new byte[]{4, 5, 6};
private static final Class<?> REDEFINED = Foo.class, AUXILIARY = Bar.class, OTHER = Qux.class;
@Rule
public TestRule mockitoRule = new MockitoRule(this);
@Rule
public MethodRule javaVersionRule = new JavaVersionRule();
@Mock
private Instrumentation instrumentation;
@Mock
private ByteBuddy byteBuddy;
@Mock
private DynamicType.Builder<?> builder;
@Mock
private DynamicType.Unloaded<?> dynamicType;
@Mock
private LoadedTypeInitializer loadedTypeInitializer;
@Mock
private AgentBuilder.RawMatcher typeMatcher;
@Mock
private AgentBuilder.Transformer transformer;
@Mock
private AgentBuilder.PoolStrategy poolStrategy;
@Mock
private AgentBuilder.TypeStrategy typeStrategy;
@Mock
private AgentBuilder.LocationStrategy locationStrategy;
@Mock
private AgentBuilder.InitializationStrategy initializationStrategy;
@Mock
private AgentBuilder.InitializationStrategy.Dispatcher dispatcher;
@Mock
private TypePool typePool;
@Mock
private TypePool.Resolution resolution;
@Mock
private AgentBuilder.Listener listener;
@Mock
private AgentBuilder.InstallationListener installationListener;
@Before
@SuppressWarnings("unchecked")
public void setUp() throws Exception {
when(builder.make(TypeResolutionStrategy.Disabled.INSTANCE, typePool)).thenReturn((DynamicType.Unloaded) dynamicType);
when(dynamicType.getTypeDescription()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeStrategy.builder(any(TypeDescription.class),
eq(byteBuddy),
any(ClassFileLocator.class),
any(MethodNameTransformer.class))).thenReturn((DynamicType.Builder) builder);
Map<TypeDescription, LoadedTypeInitializer> loadedTypeInitializers = new HashMap<TypeDescription, LoadedTypeInitializer>();
loadedTypeInitializers.put(new TypeDescription.ForLoadedType(REDEFINED), loadedTypeInitializer);
when(dynamicType.getLoadedTypeInitializers()).thenReturn(loadedTypeInitializers);
when(dynamicType.getBytes()).thenReturn(BAZ);
when(transformer.transform(builder, new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED)))
.thenReturn((DynamicType.Builder) builder);
when(poolStrategy.typePool(any(ClassFileLocator.class), any(ClassLoader.class))).thenReturn(typePool);
when(typePool.describe(REDEFINED.getName())).thenReturn(resolution);
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED});
when(initializationStrategy.dispatcher()).thenReturn(dispatcher);
when(dispatcher.apply(builder)).thenReturn((DynamicType.Builder) builder);
}
@Test
public void testSuccessfulWithoutExistingClass() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain()))
.thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
assertThat(transform(classFileTransformer, JavaModule.ofType(REDEFINED), REDEFINED.getClassLoader(), REDEFINED.getName(), null, REDEFINED.getProtectionDomain(), QUX), is(BAZ));
verify(listener).onDiscovery(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false);
verify(listener).onTransformation(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false, dynamicType);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verifyNoMoreInteractions(instrumentation);
verify(initializationStrategy).dispatcher();
verifyNoMoreInteractions(initializationStrategy);
verify(dispatcher).apply(builder);
verify(dispatcher).register(dynamicType,
REDEFINED.getClassLoader(),
new AgentBuilder.Default.Transformation.Simple.Resolution.BootstrapClassLoaderCapableInjectorFactory(
AgentBuilder.Default.BootstrapInjectionStrategy.Disabled.INSTANCE,
REDEFINED.getClassLoader(),
REDEFINED.getProtectionDomain()));
verifyNoMoreInteractions(dispatcher);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verify(transformer).transform(builder, new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED));
verifyNoMoreInteractions(transformer);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSuccessfulWithoutExistingClassConjunction() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain()))
.thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(ElementMatchers.any()).and(typeMatcher).transform(transformer)
.installOn(instrumentation);
assertThat(transform(classFileTransformer, JavaModule.ofType(REDEFINED), REDEFINED.getClassLoader(), REDEFINED.getName(), null, REDEFINED.getProtectionDomain(), QUX), is(BAZ));
verify(listener).onDiscovery(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false);
verify(listener).onTransformation(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false, dynamicType);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verifyNoMoreInteractions(instrumentation);
verify(initializationStrategy).dispatcher();
verifyNoMoreInteractions(initializationStrategy);
verify(dispatcher).apply(builder);
verify(dispatcher).register(dynamicType,
REDEFINED.getClassLoader(),
new AgentBuilder.Default.Transformation.Simple.Resolution.BootstrapClassLoaderCapableInjectorFactory(
AgentBuilder.Default.BootstrapInjectionStrategy.Disabled.INSTANCE,
REDEFINED.getClassLoader(),
REDEFINED.getProtectionDomain()));
verifyNoMoreInteractions(dispatcher);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSuccessfulWithoutExistingClassDisjunction() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain()))
.thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(none()).or(typeMatcher).transform(transformer)
.installOn(instrumentation);
assertThat(transform(classFileTransformer, JavaModule.ofType(REDEFINED), REDEFINED.getClassLoader(), REDEFINED.getName(), null, REDEFINED.getProtectionDomain(), QUX), is(BAZ));
verify(listener).onDiscovery(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false);
verify(listener).onTransformation(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false, dynamicType);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verifyNoMoreInteractions(instrumentation);
verify(initializationStrategy).dispatcher();
verifyNoMoreInteractions(initializationStrategy);
verify(dispatcher).apply(builder);
verify(dispatcher).register(dynamicType,
REDEFINED.getClassLoader(),
new AgentBuilder.Default.Transformation.Simple.Resolution.BootstrapClassLoaderCapableInjectorFactory(
AgentBuilder.Default.BootstrapInjectionStrategy.Disabled.INSTANCE,
REDEFINED.getClassLoader(),
REDEFINED.getProtectionDomain()));
verifyNoMoreInteractions(dispatcher);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSuccessfulWithExistingClass() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
assertThat(transform(classFileTransformer, JavaModule.ofType(REDEFINED), REDEFINED.getClassLoader(), REDEFINED.getName(), REDEFINED, REDEFINED.getProtectionDomain(), QUX), is(BAZ));
verify(listener).onDiscovery(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onTransformation(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true, dynamicType);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verifyNoMoreInteractions(instrumentation);
verify(initializationStrategy).dispatcher();
verifyNoMoreInteractions(initializationStrategy);
verify(dispatcher).apply(builder);
verify(dispatcher).register(dynamicType,
REDEFINED.getClassLoader(),
new AgentBuilder.Default.Transformation.Simple.Resolution.BootstrapClassLoaderCapableInjectorFactory(
AgentBuilder.Default.BootstrapInjectionStrategy.Disabled.INSTANCE,
REDEFINED.getClassLoader(),
REDEFINED.getProtectionDomain()));
verifyNoMoreInteractions(dispatcher);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSuccessfulWithExistingClassFallback() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(new RuntimeException());
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain()))
.thenReturn(true);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.with(AgentBuilder.FallbackStrategy.Simple.ENABLED)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
assertThat(transform(classFileTransformer, JavaModule.ofType(REDEFINED), REDEFINED.getClassLoader(), REDEFINED.getName(), REDEFINED, REDEFINED.getProtectionDomain(), QUX), is(BAZ));
verify(listener).onDiscovery(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onTransformation(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true, dynamicType);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verifyNoMoreInteractions(instrumentation);
verify(initializationStrategy).dispatcher();
verifyNoMoreInteractions(initializationStrategy);
verify(dispatcher).apply(builder);
verify(dispatcher).register(dynamicType,
REDEFINED.getClassLoader(),
new AgentBuilder.Default.Transformation.Simple.Resolution.BootstrapClassLoaderCapableInjectorFactory(
AgentBuilder.Default.BootstrapInjectionStrategy.Disabled.INSTANCE,
REDEFINED.getClassLoader(),
REDEFINED.getProtectionDomain()));
verifyNoMoreInteractions(dispatcher);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testResetDisabled() throws Exception {
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.DISABLED);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).removeTransformer(classFileTransformer);
verifyNoMoreInteractions(instrumentation);
}
@Test
public void testResetObsolete() throws Exception {
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(false);
assertThat(classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.DISABLED), is(false));
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).removeTransformer(classFileTransformer);
verifyNoMoreInteractions(instrumentation);
}
@Test(expected = IllegalStateException.class)
public void testResetRedefinitionUnsupported() throws Exception {
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.REDEFINITION);
}
@Test(expected = IllegalStateException.class)
public void testResetRetransformationUnsupported() throws Exception {
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.RETRANSFORMATION);
}
@Test
public void testResetRedefinitionWithError() throws Exception {
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED});
Throwable throwable = new RuntimeException();
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(throwable);
assertThat(classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.REDEFINITION), is(true));
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRedefineClassesSupported();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).removeTransformer(classFileTransformer);
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
}
@Test
public void testResetRetransformationWithError() throws Exception {
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED});
Throwable throwable = new RuntimeException();
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(throwable);
assertThat(classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.RETRANSFORMATION), is(true));
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).isRetransformClassesSupported();
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).removeTransformer(classFileTransformer);
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
}
@Test
public void testResetRedefinitionWithErrorFromFallback() throws Exception {
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(AgentBuilder.FallbackStrategy.Simple.ENABLED)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED});
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
Throwable throwable = new RuntimeException(), suppressed = new RuntimeException();
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(suppressed);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain()))
.thenThrow(throwable);
assertThat(classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.REDEFINITION), is(true));
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRedefineClassesSupported();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).removeTransformer(classFileTransformer);
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
}
@Test
public void testResetRetransformationWithErrorFromFallback() throws Exception {
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(AgentBuilder.FallbackStrategy.Simple.ENABLED)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED});
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
Throwable throwable = new RuntimeException(), suppressed = new RuntimeException();
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(suppressed);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain()))
.thenThrow(throwable);
assertThat(classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.RETRANSFORMATION), is(true));
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRetransformClassesSupported();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).removeTransformer(classFileTransformer);
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
}
@Test
public void testSuccessfulWithRetransformationMatched() throws Exception {
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).retransformClasses(REDEFINED);
verify(instrumentation).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSuccessfulWithRetransformationMatchedFallback() throws Exception {
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(new RuntimeException());
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain()))
.thenReturn(true);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(AgentBuilder.FallbackStrategy.Simple.ENABLED)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).retransformClasses(REDEFINED);
verify(instrumentation).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSuccessfulWithRetransformationMatchedAndReset() throws Exception {
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
assertThat(classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.RETRANSFORMATION), is(true));
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).removeTransformer(classFileTransformer);
verify(instrumentation, times(2)).getAllLoadedClasses();
verify(instrumentation, times(2)).isModifiableClass(REDEFINED);
verify(instrumentation, times(2)).retransformClasses(REDEFINED);
verify(instrumentation, times(2)).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher, times(2)).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verify(installationListener).onReset(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSuccessfulWithRetransformationMatchedFallbackAndReset() throws Exception {
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(new RuntimeException());
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain()))
.thenReturn(true);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(AgentBuilder.FallbackStrategy.Simple.ENABLED)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
assertThat(classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.RETRANSFORMATION), is(true));
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).removeTransformer(classFileTransformer);
verify(instrumentation, times(2)).getAllLoadedClasses();
verify(instrumentation, times(2)).isModifiableClass(REDEFINED);
verify(instrumentation, times(2)).retransformClasses(REDEFINED);
verify(instrumentation, times(2)).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher, times(2)).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher, times(2)).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verify(installationListener).onReset(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRetransformationWithNonRedefinable() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(false);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verifyZeroInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRetransformationWithNonMatched() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(false);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRetransformationWithNonMatchedListenerException() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(false);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
doThrow(new RuntimeException()).when(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRetransformationWithNonMatchedListenerCompleteException() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(false);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
doThrow(new RuntimeException()).when(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
@SuppressWarnings("unchecked")
public void testSuccessfulWithRetransformationMatchedChunked() throws Exception {
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED, OTHER});
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain())).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isModifiableClass(OTHER)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator = mock(AgentBuilder.RedefinitionStrategy.BatchAllocator.class);
when(redefinitionBatchAllocator.batch(Arrays.asList(REDEFINED, OTHER)))
.thenReturn((Iterable) Arrays.asList(Collections.singletonList(REDEFINED), Collections.singletonList(OTHER)));
AgentBuilder.RedefinitionStrategy.Listener redefinitionListener = mock(AgentBuilder.RedefinitionStrategy.Listener.class);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(redefinitionBatchAllocator)
.with(redefinitionListener)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).isModifiableClass(OTHER);
verify(instrumentation).retransformClasses(REDEFINED);
verify(instrumentation).retransformClasses(OTHER);
verify(instrumentation).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
verify(redefinitionBatchAllocator).batch(Arrays.asList(REDEFINED, OTHER));
verifyNoMoreInteractions(redefinitionBatchAllocator);
verify(redefinitionListener).onBatch(0, Collections.<Class<?>>singletonList(REDEFINED), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onBatch(1, Collections.<Class<?>>singletonList(OTHER), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onComplete(2, Arrays.asList(REDEFINED, OTHER), Collections.<List<Class<?>>, Throwable>emptyMap());
verifyNoMoreInteractions(redefinitionListener);
}
@Test
@SuppressWarnings("unchecked")
public void testRetransformationChunkedOneFails() throws Exception {
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED, OTHER});
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain())).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isModifiableClass(OTHER)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
Throwable throwable = new UnmodifiableClassException();
doThrow(throwable).when(instrumentation).retransformClasses(OTHER);
AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator = mock(AgentBuilder.RedefinitionStrategy.BatchAllocator.class);
when(redefinitionBatchAllocator.batch(Arrays.asList(REDEFINED, OTHER)))
.thenReturn((Iterable) Arrays.asList(Collections.singletonList(REDEFINED), Collections.singletonList(OTHER)));
AgentBuilder.RedefinitionStrategy.Listener redefinitionListener = mock(AgentBuilder.RedefinitionStrategy.Listener.class);
when(redefinitionListener.onError(1, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER))).thenReturn((Iterable) Collections.emptyList());
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(redefinitionBatchAllocator)
.with(redefinitionListener)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).isModifiableClass(OTHER);
verify(instrumentation).retransformClasses(REDEFINED);
verify(instrumentation).retransformClasses(OTHER);
verify(instrumentation).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
verify(redefinitionBatchAllocator).batch(Arrays.asList(REDEFINED, OTHER));
verifyNoMoreInteractions(redefinitionBatchAllocator);
verify(redefinitionListener).onBatch(0, Collections.<Class<?>>singletonList(REDEFINED), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onBatch(1, Collections.<Class<?>>singletonList(OTHER), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onError(1, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onComplete(2, Arrays.asList(REDEFINED, OTHER), Collections.singletonMap(Collections.<Class<?>>singletonList(OTHER), throwable));
verifyNoMoreInteractions(redefinitionListener);
}
@Test
@SuppressWarnings("unchecked")
public void testRetransformationChunkedOneFailsResubmit() throws Exception {
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED, OTHER});
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain())).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isModifiableClass(OTHER)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
Throwable throwable = new UnmodifiableClassException();
doThrow(throwable).when(instrumentation).retransformClasses(OTHER);
AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator = mock(AgentBuilder.RedefinitionStrategy.BatchAllocator.class);
when(redefinitionBatchAllocator.batch(Arrays.asList(REDEFINED, OTHER)))
.thenReturn((Iterable) Arrays.asList(Collections.singletonList(REDEFINED), Collections.singletonList(OTHER)));
AgentBuilder.RedefinitionStrategy.Listener redefinitionListener = mock(AgentBuilder.RedefinitionStrategy.Listener.class);
when(redefinitionListener.onError(1, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER)))
.thenReturn((Iterable) Collections.singleton(Collections.singletonList(OTHER)));
when(redefinitionListener.onError(2, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER)))
.thenReturn((Iterable) Collections.emptyList());
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(redefinitionBatchAllocator)
.with(redefinitionListener)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).isModifiableClass(OTHER);
verify(instrumentation).retransformClasses(REDEFINED);
verify(instrumentation, times(2)).retransformClasses(OTHER);
verify(instrumentation).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
verify(redefinitionBatchAllocator).batch(Arrays.asList(REDEFINED, OTHER));
verifyNoMoreInteractions(redefinitionBatchAllocator);
verify(redefinitionListener).onBatch(0, Collections.<Class<?>>singletonList(REDEFINED), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onBatch(1, Collections.<Class<?>>singletonList(OTHER), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onBatch(2, Collections.<Class<?>>singletonList(OTHER), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onError(1, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onError(2, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onComplete(3, Arrays.asList(REDEFINED, OTHER), Collections.singletonMap(Collections.<Class<?>>singletonList(OTHER), throwable));
verifyNoMoreInteractions(redefinitionListener);
}
@Test
@SuppressWarnings("unchecked")
public void testRetransformationChunkedOneFailsEscalated() throws Exception {
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED, OTHER});
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain())).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isModifiableClass(OTHER)).thenReturn(true);
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
Throwable throwable = new RuntimeException();
doThrow(throwable).when(instrumentation).retransformClasses(REDEFINED, OTHER);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.RedefinitionStrategy.BatchAllocator.ForTotal.INSTANCE)
.with(AgentBuilder.RedefinitionStrategy.Listener.ErrorEscalating.FAIL_FAST)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).isModifiableClass(OTHER);
verify(instrumentation).retransformClasses(REDEFINED, OTHER);
verify(instrumentation).isRetransformClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onError(eq(instrumentation), eq(classFileTransformer), argThat(new CauseMatcher(throwable)));
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test(expected = IllegalStateException.class)
public void testRetransformationNotSupported() throws Exception {
new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(listener)
.disableNativeMethodPrefix()
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
}
@Test
public void testSuccessfulWithRedefinitionMatched() throws Exception {
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).redefineClasses(any(ClassDefinition.class));
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(dispatcher);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSuccessfulWithRedefinitionMatchedFallback() throws Exception {
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(new RuntimeException());
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain()))
.thenReturn(true);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(AgentBuilder.FallbackStrategy.Simple.ENABLED)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).redefineClasses(any(ClassDefinition.class));
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSuccessfulWithRedefinitionMatchedAndReset() throws Exception {
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
assertThat(classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.REDEFINITION), is(true));
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).removeTransformer(classFileTransformer);
verify(instrumentation, times(2)).getAllLoadedClasses();
verify(instrumentation, times(2)).isModifiableClass(REDEFINED);
verify(instrumentation, times(2)).redefineClasses(any(ClassDefinition.class));
verify(instrumentation, times(2)).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher, times(2)).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(dispatcher);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verify(installationListener).onReset(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSuccessfulWithRedefinitionMatchedFallbackAndReset() throws Exception {
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(new RuntimeException());
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain()))
.thenReturn(true);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(AgentBuilder.FallbackStrategy.Simple.ENABLED)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
when(instrumentation.removeTransformer(classFileTransformer)).thenReturn(true);
assertThat(classFileTransformer.reset(instrumentation, AgentBuilder.RedefinitionStrategy.REDEFINITION), is(true));
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).removeTransformer(classFileTransformer);
verify(instrumentation, times(2)).getAllLoadedClasses();
verify(instrumentation, times(2)).isModifiableClass(REDEFINED);
verify(instrumentation, times(2)).redefineClasses(any(ClassDefinition.class));
verify(instrumentation, times(2)).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher, times(2)).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher, times(2)).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), null, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verify(installationListener).onReset(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRedefinitionWithNonRedefinable() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(false);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verifyZeroInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRedefinitionWithNonMatched() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(false);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRedefinitionWithIgnoredType() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
@SuppressWarnings("unchecked")
ElementMatcher<? super TypeDescription> ignoredTypes = mock(ElementMatcher.class);
when(ignoredTypes.matches(new TypeDescription.ForLoadedType(REDEFINED))).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(ignoredTypes)
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verifyZeroInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(ignoredTypes).matches(new TypeDescription.ForLoadedType(REDEFINED));
verifyNoMoreInteractions(ignoredTypes);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRedefinitionWithIgnoredClassLoader() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
@SuppressWarnings("unchecked")
ElementMatcher<? super TypeDescription> ignoredTypes = mock(ElementMatcher.class);
when(ignoredTypes.matches(new TypeDescription.ForLoadedType(REDEFINED))).thenReturn(true);
@SuppressWarnings("unchecked")
ElementMatcher<? super ClassLoader> ignoredClassLoaders = mock(ElementMatcher.class);
when(ignoredClassLoaders.matches(REDEFINED.getClassLoader())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(ignoredTypes, ignoredClassLoaders)
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verifyZeroInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(ignoredClassLoaders).matches(REDEFINED.getClassLoader());
verifyNoMoreInteractions(ignoredClassLoaders);
verify(ignoredTypes).matches(new TypeDescription.ForLoadedType(REDEFINED));
verifyNoMoreInteractions(ignoredTypes);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRedefinitionWithIgnoredTypeChainedConjunction() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
@SuppressWarnings("unchecked")
ElementMatcher<? super TypeDescription> ignoredTypes = mock(ElementMatcher.class);
when(ignoredTypes.matches(new TypeDescription.ForLoadedType(REDEFINED))).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(ElementMatchers.any()).and(ignoredTypes)
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verifyZeroInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(ignoredTypes).matches(new TypeDescription.ForLoadedType(REDEFINED));
verifyNoMoreInteractions(ignoredTypes);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRedefinitionWithIgnoredTypeChainedDisjunction() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
@SuppressWarnings("unchecked")
ElementMatcher<? super TypeDescription> ignoredTypes = mock(ElementMatcher.class);
when(ignoredTypes.matches(new TypeDescription.ForLoadedType(REDEFINED))).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none()).or(ignoredTypes)
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verifyZeroInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(ignoredTypes).matches(new TypeDescription.ForLoadedType(REDEFINED));
verifyNoMoreInteractions(ignoredTypes);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRedefinitionWithNonMatchedListenerException() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(false);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
doThrow(new RuntimeException()).when(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testSkipRedefinitionWithNonMatchedListenerFinishedException() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(false);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
doThrow(new RuntimeException()).when(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
@SuppressWarnings("unchecked")
public void testSuccessfulWithRedefinitionMatchedChunked() throws Exception {
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED, OTHER});
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain())).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isModifiableClass(OTHER)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator = mock(AgentBuilder.RedefinitionStrategy.BatchAllocator.class);
when(redefinitionBatchAllocator.batch(Arrays.asList(REDEFINED, OTHER)))
.thenReturn((Iterable) Arrays.asList(Collections.singletonList(REDEFINED), Collections.singletonList(OTHER)));
AgentBuilder.RedefinitionStrategy.Listener redefinitionListener = mock(AgentBuilder.RedefinitionStrategy.Listener.class);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(redefinitionBatchAllocator)
.with(redefinitionListener)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).isModifiableClass(OTHER);
verify(instrumentation, times(2)).redefineClasses(any(ClassDefinition.class));
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
verify(redefinitionBatchAllocator).batch(Arrays.asList(REDEFINED, OTHER));
verifyNoMoreInteractions(redefinitionBatchAllocator);
verify(redefinitionListener).onBatch(0, Collections.<Class<?>>singletonList(REDEFINED), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onBatch(1, Collections.<Class<?>>singletonList(OTHER), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onComplete(2, Arrays.asList(REDEFINED, OTHER), Collections.<List<Class<?>>, Throwable>emptyMap());
verifyNoMoreInteractions(redefinitionListener);
}
@Test
@SuppressWarnings("unchecked")
public void testRedefinitionChunkedOneFails() throws Exception {
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED, OTHER});
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain())).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isModifiableClass(OTHER)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
Throwable throwable = new ClassNotFoundException();
doThrow(throwable).when(instrumentation).redefineClasses(argThat(new ClassRedefinitionMatcher(OTHER)));
AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator = mock(AgentBuilder.RedefinitionStrategy.BatchAllocator.class);
when(redefinitionBatchAllocator.batch(Arrays.asList(REDEFINED, OTHER)))
.thenReturn((Iterable) Arrays.asList(Collections.singletonList(REDEFINED), Collections.singletonList(OTHER)));
AgentBuilder.RedefinitionStrategy.Listener redefinitionListener = mock(AgentBuilder.RedefinitionStrategy.Listener.class);
when(redefinitionListener.onError(1, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER))).thenReturn((Iterable) Collections.emptyList());
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(redefinitionBatchAllocator)
.with(redefinitionListener)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).isModifiableClass(OTHER);
verify(instrumentation).redefineClasses(argThat(new ClassRedefinitionMatcher(REDEFINED)));
verify(instrumentation).redefineClasses(argThat(new ClassRedefinitionMatcher(OTHER)));
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
verify(redefinitionBatchAllocator).batch(Arrays.asList(REDEFINED, OTHER));
verifyNoMoreInteractions(redefinitionBatchAllocator);
verify(redefinitionListener).onBatch(0, Collections.<Class<?>>singletonList(REDEFINED), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onBatch(1, Collections.<Class<?>>singletonList(OTHER), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onError(1, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onComplete(2, Arrays.asList(REDEFINED, OTHER), Collections.singletonMap(Collections.<Class<?>>singletonList(OTHER), throwable));
verifyNoMoreInteractions(redefinitionListener);
}
@Test
@SuppressWarnings("unchecked")
public void testRedefinitionChunkedOneFailsResubmit() throws Exception {
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED, OTHER});
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain())).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isModifiableClass(OTHER)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
Throwable throwable = new ClassNotFoundException();
doThrow(throwable).when(instrumentation).redefineClasses(argThat(new ClassRedefinitionMatcher(OTHER)));
AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator = mock(AgentBuilder.RedefinitionStrategy.BatchAllocator.class);
when(redefinitionBatchAllocator.batch(Arrays.asList(REDEFINED, OTHER)))
.thenReturn((Iterable) Arrays.asList(Collections.singletonList(REDEFINED), Collections.singletonList(OTHER)));
AgentBuilder.RedefinitionStrategy.Listener redefinitionListener = mock(AgentBuilder.RedefinitionStrategy.Listener.class);
when(redefinitionListener.onError(1, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER)))
.thenReturn((Iterable) Collections.singleton(Collections.singletonList(OTHER)));
when(redefinitionListener.onError(2, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER)))
.thenReturn((Iterable) Collections.emptyList());
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(redefinitionBatchAllocator)
.with(redefinitionListener)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).isModifiableClass(OTHER);
verify(instrumentation).redefineClasses(argThat(new ClassRedefinitionMatcher(REDEFINED)));
verify(instrumentation, times(2)).redefineClasses(argThat(new ClassRedefinitionMatcher(OTHER)));
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
verifyZeroInteractions(installationListener);
verify(redefinitionBatchAllocator).batch(Arrays.asList(REDEFINED, OTHER));
verifyNoMoreInteractions(redefinitionBatchAllocator);
verify(redefinitionListener).onBatch(0, Collections.<Class<?>>singletonList(REDEFINED), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onBatch(1, Collections.<Class<?>>singletonList(OTHER), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onBatch(2, Collections.<Class<?>>singletonList(OTHER), Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onError(1, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onError(2, Collections.<Class<?>>singletonList(OTHER), throwable, Arrays.asList(REDEFINED, OTHER));
verify(redefinitionListener).onComplete(3, Arrays.asList(REDEFINED, OTHER), Collections.singletonMap(Collections.<Class<?>>singletonList(OTHER), throwable));
verifyNoMoreInteractions(redefinitionListener);
}
@Test
@SuppressWarnings("unchecked")
public void testRedefinitionChunkedOneFailsEscalated() throws Exception {
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED, OTHER});
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain())).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain())).thenReturn(true);
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(instrumentation.isModifiableClass(OTHER)).thenReturn(true);
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
Throwable throwable = new RuntimeException();
doThrow(throwable).when(instrumentation).redefineClasses(any(ClassDefinition.class), any(ClassDefinition.class));
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(AgentBuilder.RedefinitionStrategy.BatchAllocator.ForTotal.INSTANCE)
.with(AgentBuilder.RedefinitionStrategy.Listener.ErrorEscalating.FAIL_FAST)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verifyZeroInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(instrumentation).getAllLoadedClasses();
verify(instrumentation).isModifiableClass(REDEFINED);
verify(instrumentation).isModifiableClass(OTHER);
verify(instrumentation).redefineClasses(any(ClassDefinition.class), any(ClassDefinition.class));
verify(instrumentation).isRedefineClassesSupported();
verifyNoMoreInteractions(instrumentation);
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verify(typeMatcher).matches(new TypeDescription.ForLoadedType(OTHER), OTHER.getClassLoader(), JavaModule.ofType(OTHER), OTHER, OTHER.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verify(installationListener).onError(eq(instrumentation), eq(classFileTransformer), argThat(new CauseMatcher(throwable)));
verifyNoMoreInteractions(installationListener);
}
@Test(expected = IllegalStateException.class)
public void testRedefinitionNotSupported() throws Exception {
new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(listener)
.disableNativeMethodPrefix()
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
}
@Test
public void testTransformationWithError() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
RuntimeException exception = new RuntimeException();
when(resolution.resolve()).thenThrow(exception);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
assertThat(transform(classFileTransformer, JavaModule.ofType(REDEFINED), REDEFINED.getClassLoader(), REDEFINED.getName(), null, REDEFINED.getProtectionDomain(), QUX),
nullValue(byte[].class));
verify(listener).onDiscovery(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false);
verify(listener).onError(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false, exception);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), false);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verifyNoMoreInteractions(instrumentation);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testIgnored() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(false);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
assertThat(transform(classFileTransformer, JavaModule.ofType(REDEFINED), REDEFINED.getClassLoader(), REDEFINED.getName(), REDEFINED, REDEFINED.getProtectionDomain(), QUX),
nullValue(byte[].class));
verify(listener).onDiscovery(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verifyNoMoreInteractions(instrumentation);
verifyZeroInteractions(initializationStrategy);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test(expected = IllegalArgumentException.class)
public void testEmptyPrefixThrowsException() throws Exception {
new AgentBuilder.Default(byteBuddy).enableNativeMethodPrefix("");
}
@Test
public void testAuxiliaryTypeInitialization() throws Exception {
when(dynamicType.getAuxiliaryTypes()).thenReturn(Collections.<TypeDescription, byte[]>singletonMap(new TypeDescription.ForLoadedType(AUXILIARY), QUX));
Map<TypeDescription, LoadedTypeInitializer> loadedTypeInitializers = new HashMap<TypeDescription, LoadedTypeInitializer>();
loadedTypeInitializers.put(new TypeDescription.ForLoadedType(REDEFINED), loadedTypeInitializer);
LoadedTypeInitializer auxiliaryInitializer = mock(LoadedTypeInitializer.class);
loadedTypeInitializers.put(new TypeDescription.ForLoadedType(AUXILIARY), auxiliaryInitializer);
when(dynamicType.getLoadedTypeInitializers()).thenReturn(loadedTypeInitializers);
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
assertThat(transform(classFileTransformer, JavaModule.ofType(REDEFINED), REDEFINED.getClassLoader(), REDEFINED.getName(), REDEFINED, REDEFINED.getProtectionDomain(), QUX), is(BAZ));
verify(listener).onDiscovery(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onTransformation(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true, dynamicType);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verifyNoMoreInteractions(instrumentation);
verify(initializationStrategy).dispatcher();
verifyNoMoreInteractions(initializationStrategy);
verify(dispatcher).apply(builder);
verify(dispatcher).register(dynamicType,
REDEFINED.getClassLoader(),
new AgentBuilder.Default.Transformation.Simple.Resolution.BootstrapClassLoaderCapableInjectorFactory(
AgentBuilder.Default.BootstrapInjectionStrategy.Disabled.INSTANCE,
REDEFINED.getClassLoader(),
REDEFINED.getProtectionDomain()));
verifyNoMoreInteractions(dispatcher);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testRedefinitionConsiderationException() throws Exception {
RuntimeException exception = new RuntimeException();
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED});
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(exception);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(listener).onError(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true, exception);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testRetransformationConsiderationException() throws Exception {
RuntimeException exception = new RuntimeException();
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED});
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(exception);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(listener).onError(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true, exception);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testRedefinitionConsiderationExceptionListenerException() throws Exception {
RuntimeException exception = new RuntimeException();
when(instrumentation.isRedefineClassesSupported()).thenReturn(true);
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED});
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(exception);
doThrow(new RuntimeException()).when(listener).onError(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true, exception);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(instrumentation).addTransformer(classFileTransformer, false);
verify(listener).onError(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true, exception);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testRetransformationConsiderationExceptionListenerException() throws Exception {
RuntimeException exception = new RuntimeException();
when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
when(instrumentation.getAllLoadedClasses()).thenReturn(new Class<?>[]{REDEFINED});
when(instrumentation.isModifiableClass(REDEFINED)).thenReturn(true);
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenThrow(exception);
doThrow(new RuntimeException()).when(listener).onError(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true, exception);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
verify(instrumentation).addTransformer(classFileTransformer, true);
verify(listener).onError(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true, exception);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testDecoratedTransformation() throws Exception {
when(dynamicType.getBytes()).thenReturn(BAZ);
when(resolution.resolve()).thenReturn(new TypeDescription.ForLoadedType(REDEFINED));
when(typeMatcher.matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain()))
.thenReturn(true);
ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(none())
.type(typeMatcher).transform(transformer)
.type(typeMatcher).transform(transformer).asDecorator()
.installOn(instrumentation);
assertThat(transform(classFileTransformer, JavaModule.ofType(REDEFINED), REDEFINED.getClassLoader(), REDEFINED.getName(), REDEFINED, REDEFINED.getProtectionDomain(), QUX), is(BAZ));
verify(listener).onDiscovery(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onTransformation(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true, dynamicType);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(instrumentation).addTransformer(classFileTransformer, false);
verifyNoMoreInteractions(instrumentation);
verify(initializationStrategy).dispatcher();
verifyNoMoreInteractions(initializationStrategy);
verify(dispatcher).apply(builder);
verify(dispatcher).register(dynamicType,
REDEFINED.getClassLoader(),
new AgentBuilder.Default.Transformation.Simple.Resolution.BootstrapClassLoaderCapableInjectorFactory(
AgentBuilder.Default.BootstrapInjectionStrategy.Disabled.INSTANCE,
REDEFINED.getClassLoader(),
REDEFINED.getProtectionDomain()));
verifyNoMoreInteractions(dispatcher);
verify(typeMatcher, times(2)).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(typeMatcher);
verify(transformer, times(2)).transform(builder, new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED));
verifyNoMoreInteractions(transformer);
verify(installationListener).onBeforeInstall(instrumentation, classFileTransformer);
verify(installationListener).onInstall(instrumentation, classFileTransformer);
verifyNoMoreInteractions(installationListener);
}
@Test
public void testBootstrapClassLoaderCapableInjectorFactoryReflection() throws Exception {
AgentBuilder.Default.BootstrapInjectionStrategy bootstrapInjectionStrategy = mock(AgentBuilder.Default.BootstrapInjectionStrategy.class);
ClassLoader classLoader = mock(ClassLoader.class);
ProtectionDomain protectionDomain = mock(ProtectionDomain.class);
assertThat(new AgentBuilder.Default.Transformation.Simple.Resolution.BootstrapClassLoaderCapableInjectorFactory(bootstrapInjectionStrategy,
classLoader,
protectionDomain).resolve(), is((ClassInjector) new ClassInjector.UsingReflection(classLoader, protectionDomain)));
verifyZeroInteractions(bootstrapInjectionStrategy);
}
@Test
public void testBootstrapClassLoaderCapableInjectorFactoryInstrumentation() throws Exception {
AgentBuilder.Default.BootstrapInjectionStrategy bootstrapInjectionStrategy = mock(AgentBuilder.Default.BootstrapInjectionStrategy.class);
ProtectionDomain protectionDomain = mock(ProtectionDomain.class);
ClassInjector classInjector = mock(ClassInjector.class);
when(bootstrapInjectionStrategy.make(protectionDomain)).thenReturn(classInjector);
assertThat(new AgentBuilder.Default.Transformation.Simple.Resolution.BootstrapClassLoaderCapableInjectorFactory(bootstrapInjectionStrategy,
null,
protectionDomain).resolve(), is(classInjector));
verify(bootstrapInjectionStrategy).make(protectionDomain);
verifyNoMoreInteractions(bootstrapInjectionStrategy);
}
@Test
public void testEnabledBootstrapInjection() throws Exception {
assertThat(new AgentBuilder.Default.BootstrapInjectionStrategy.Enabled(mock(File.class), mock(Instrumentation.class))
.make(mock(ProtectionDomain.class)), instanceOf(ClassInjector.UsingInstrumentation.class));
}
@Test(expected = IllegalStateException.class)
public void testDisabledBootstrapInjection() throws Exception {
AgentBuilder.Default.BootstrapInjectionStrategy.Disabled.INSTANCE.make(mock(ProtectionDomain.class));
}
@Test
@SuppressWarnings("unchecked")
public void testExecutingTransformerReturnsNullValue() throws Exception {
assertThat(new AgentBuilder.Default.ExecutingTransformer(byteBuddy,
listener,
poolStrategy,
typeStrategy,
locationStrategy,
mock(AgentBuilder.Default.NativeMethodStrategy.class),
initializationStrategy,
mock(AgentBuilder.Default.BootstrapInjectionStrategy.class),
AgentBuilder.LambdaInstrumentationStrategy.DISABLED,
AgentBuilder.DescriptionStrategy.Default.HYBRID,
mock(AgentBuilder.FallbackStrategy.class),
mock(AgentBuilder.InstallationListener.class),
mock(AgentBuilder.RawMatcher.class),
mock(AgentBuilder.Default.Transformation.class),
new AgentBuilder.CircularityLock.Default())
.transform(mock(ClassLoader.class),
FOO,
Object.class,
mock(ProtectionDomain.class),
new byte[0]), nullValue(byte[].class));
}
@Test(expected = IllegalStateException.class)
public void testExecutingTransformerReturnsRequiresLock() throws Exception {
new AgentBuilder.Default()
.with(mock(AgentBuilder.CircularityLock.class))
.installOn(mock(Instrumentation.class));
}
@Test
@SuppressWarnings("unchecked")
public void testExecutingTransformerDoesNotRecurse() throws Exception {
final AgentBuilder.Default.ExecutingTransformer executingTransformer = new AgentBuilder.Default.ExecutingTransformer(byteBuddy,
listener,
poolStrategy,
typeStrategy,
locationStrategy,
mock(AgentBuilder.Default.NativeMethodStrategy.class),
initializationStrategy,
mock(AgentBuilder.Default.BootstrapInjectionStrategy.class),
AgentBuilder.LambdaInstrumentationStrategy.DISABLED,
AgentBuilder.DescriptionStrategy.Default.HYBRID,
mock(AgentBuilder.FallbackStrategy.class),
mock(AgentBuilder.InstallationListener.class),
mock(AgentBuilder.RawMatcher.class),
mock(AgentBuilder.Default.Transformation.class),
new AgentBuilder.Default.CircularityLock.Default());
final ClassLoader classLoader = mock(ClassLoader.class);
final ProtectionDomain protectionDomain = mock(ProtectionDomain.class);
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
assertThat(executingTransformer.transform(classLoader,
FOO,
Object.class,
protectionDomain,
new byte[0]), nullValue(byte[].class));
return null;
}
}).when(listener).onComplete(FOO, classLoader, JavaModule.UNSUPPORTED, true);
assertThat(executingTransformer.transform(classLoader,
FOO,
Object.class,
protectionDomain,
new byte[0]), nullValue(byte[].class));
verify(listener).onComplete(FOO, classLoader, JavaModule.UNSUPPORTED, true);
}
@Test
@JavaVersionRule.Enforce(9)
public void testExecutingTransformerDoesNotRecurseWithModules() throws Exception {
final AgentBuilder.Default.ExecutingTransformer executingTransformer = new AgentBuilder.Default.ExecutingTransformer(byteBuddy,
listener,
poolStrategy,
typeStrategy,
locationStrategy,
mock(AgentBuilder.Default.NativeMethodStrategy.class),
initializationStrategy,
mock(AgentBuilder.Default.BootstrapInjectionStrategy.class),
AgentBuilder.LambdaInstrumentationStrategy.DISABLED,
AgentBuilder.DescriptionStrategy.Default.HYBRID,
mock(AgentBuilder.FallbackStrategy.class),
mock(AgentBuilder.InstallationListener.class),
mock(AgentBuilder.RawMatcher.class),
mock(AgentBuilder.Default.Transformation.class),
new AgentBuilder.CircularityLock.Default());
final ClassLoader classLoader = mock(ClassLoader.class);
final ProtectionDomain protectionDomain = mock(ProtectionDomain.class);
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
assertThat(executingTransformer.transform(JavaModule.ofType(Object.class).unwrap(),
classLoader,
FOO,
Object.class,
protectionDomain,
new byte[0]), nullValue(byte[].class));
return null;
}
}).when(listener).onComplete(FOO, classLoader, JavaModule.ofType(Object.class), true);
assertThat(executingTransformer.transform(JavaModule.of(Object.class).unwrap(),
classLoader,
FOO,
Object.class,
protectionDomain,
new byte[0]), nullValue(byte[].class));
verify(listener).onComplete(FOO, classLoader, JavaModule.ofType(Object.class), true);
}
@Test
public void testIgnoredTypeMatcherOnlyAppliedOnceWithMultipleTransformations() throws Exception {
AgentBuilder.RawMatcher ignored = mock(AgentBuilder.RawMatcher.class);
ClassFileTransformer classFileTransformer = new AgentBuilder.Default(byteBuddy)
.with(initializationStrategy)
.with(poolStrategy)
.with(typeStrategy)
.with(installationListener)
.with(listener)
.disableNativeMethodPrefix()
.ignore(ignored)
.type(typeMatcher).transform(transformer)
.type(typeMatcher).transform(transformer)
.installOn(instrumentation);
assertThat(transform(classFileTransformer, JavaModule.ofType(REDEFINED), REDEFINED.getClassLoader(), REDEFINED.getName(), REDEFINED, REDEFINED.getProtectionDomain(), QUX), nullValue(byte[].class));
verify(listener).onDiscovery(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onIgnored(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verify(listener).onComplete(REDEFINED.getName(), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), true);
verifyNoMoreInteractions(listener);
verify(ignored).matches(new TypeDescription.ForLoadedType(REDEFINED), REDEFINED.getClassLoader(), JavaModule.ofType(REDEFINED), REDEFINED, REDEFINED.getProtectionDomain());
verifyNoMoreInteractions(ignored);
}
@Test
public void testDisableClassFormatChanges() throws Exception {
assertThat(new AgentBuilder.Default().disableClassFormatChanges(), is(new AgentBuilder.Default(new ByteBuddy()
.with(Implementation.Context.Disabled.Factory.INSTANCE))
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REDEFINE_FROZEN)));
}
@Test
public void testBuildPlugin() throws Exception {
Plugin plugin = mock(Plugin.class);
assertThat(AgentBuilder.Default.of(plugin), is((AgentBuilder) new AgentBuilder.Default()
.with(new AgentBuilder.TypeStrategy.ForBuildEntryPoint(EntryPoint.Default.REBASE))
.type(plugin)
.transform(new AgentBuilder.Transformer.ForBuildPlugin(plugin))));
}
@Test
public void testBuildPluginWithEntryPoint() throws Exception {
Plugin plugin = mock(Plugin.class);
EntryPoint entryPoint = mock(EntryPoint.class);
ByteBuddy byteBuddy = mock(ByteBuddy.class);
when(entryPoint.getByteBuddy()).thenReturn(byteBuddy);
assertThat(AgentBuilder.Default.of(entryPoint, plugin), is((AgentBuilder) new AgentBuilder.Default(byteBuddy)
.with(new AgentBuilder.TypeStrategy.ForBuildEntryPoint(entryPoint))
.type(plugin)
.transform(new AgentBuilder.Transformer.ForBuildPlugin(plugin))));
}
@Test(expected = IllegalStateException.class)
public void testRetransformationDisabledNotEnabledAllocator() throws Exception {
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.DISABLED)
.with(mock(AgentBuilder.RedefinitionStrategy.BatchAllocator.class));
}
@Test(expected = IllegalStateException.class)
public void testRetransformationDisabledNotEnabledListener() throws Exception {
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.DISABLED)
.with(mock(AgentBuilder.RedefinitionStrategy.Listener.class));
}
@Test(expected = IllegalStateException.class)
public void testRetransformationDisabledNotEnabledResubmission() throws Exception {
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.DISABLED)
.withResubmission(mock(AgentBuilder.RedefinitionStrategy.ResubmissionScheduler.class));
}
@Test
public void testObjectProperties() throws Exception {
ObjectPropertyAssertion.of(AgentBuilder.Default.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.Ignoring.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.Transforming.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.Transformation.Simple.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.Transformation.Simple.Resolution.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.Transformation.Ignored.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.Transformation.Compound.class).create(new ObjectPropertyAssertion.Creator<List<?>>() {
@Override
public List<?> create() {
return Collections.singletonList(mock(AgentBuilder.Default.Transformation.class));
}
}).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.Transformation.Resolution.Unresolved.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.Transformation.Resolution.Sort.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.BootstrapInjectionStrategy.Enabled.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.BootstrapInjectionStrategy.Unsafe.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.BootstrapInjectionStrategy.Disabled.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.ExecutingTransformer.Factory.CreationAction.class);
final Iterator<Class<?>> java9Dispatcher = Arrays.<Class<?>>asList(Object.class, String.class, Integer.class, Double.class, Float.class).iterator();
ObjectPropertyAssertion.of(AgentBuilder.Default.ExecutingTransformer.Java9CapableVmDispatcher.class).create(new ObjectPropertyAssertion.Creator<Class<?>>() {
@Override
public Class<?> create() {
return java9Dispatcher.next();
}
}).apply();
final Iterator<Class<?>> legacyDispatcher = Arrays.<Class<?>>asList(Object.class, String.class, Integer.class, Double.class, Float.class).iterator();
ObjectPropertyAssertion.of(AgentBuilder.Default.ExecutingTransformer.LegacyVmDispatcher.class).create(new ObjectPropertyAssertion.Creator<Class<?>>() {
@Override
public Class<?> create() {
return legacyDispatcher.next();
}
}).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.Transformation.Simple.Resolution.BootstrapClassLoaderCapableInjectorFactory.class).apply();
final Iterator<Constructor<?>> iterator = Arrays.<Constructor<?>>asList(String.class.getDeclaredConstructors()).iterator();
ObjectPropertyAssertion.of(AgentBuilder.Default.ExecutingTransformer.Factory.ForJava9CapableVm.class).create(new ObjectPropertyAssertion.Creator<Constructor<?>>() {
@Override
public Constructor<?> create() {
return iterator.next();
}
}).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.ExecutingTransformer.Factory.ForLegacyVm.class).apply();
}
public static class Foo {
/* empty */
}
public static class Bar {
/* empty */
}
public static class Qux {
/* empty */
}
private static byte[] transform(ClassFileTransformer classFileTransformer,
JavaModule javaModule,
ClassLoader classLoader,
String typeName,
Class<?> type,
ProtectionDomain protectionDomain,
byte[] binaryRepresentation) throws Exception {
try {
return (byte[]) ClassFileTransformer.class.getDeclaredMethod("transform", Class.forName("java.lang.Module"), ClassLoader.class, String.class, Class.class, ProtectionDomain.class, byte[].class)
.invoke(classFileTransformer, javaModule.unwrap(), classLoader, typeName, type, protectionDomain, binaryRepresentation);
} catch (Exception ignored) {
return classFileTransformer.transform(classLoader, typeName, type, protectionDomain, binaryRepresentation);
}
}
private static class ClassRedefinitionMatcher implements ArgumentMatcher<ClassDefinition> {
private final Class<?> type;
private ClassRedefinitionMatcher(Class<?> type) {
this.type = type;
}
@Override
public boolean matches(ClassDefinition classDefinition) {
return classDefinition.getDefinitionClass() == type;
}
}
private static class CauseMatcher implements ArgumentMatcher<Throwable> {
private final Throwable throwable;
private CauseMatcher(Throwable throwable) {
this.throwable = throwable;
}
@Override
public boolean matches(Throwable item) {
return throwable.equals((item).getCause());
}
}
}