diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/ClassFileLocator.java b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/ClassFileLocator.java index fb198970e46..a43b8aacc9f 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/ClassFileLocator.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/ClassFileLocator.java @@ -5,7 +5,6 @@ import java.io.*; import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; import java.lang.reflect.Field; import java.security.AccessControlContext; @@ -1035,9 +1034,9 @@ public byte[] transform(ClassLoader classLoader, String internalName, Class redefinedType, ProtectionDomain protectionDomain, - byte[] classFile) throws IllegalClassFormatException { + byte[] binaryRepresentation) { if (internalName != null && isChild(classLoader) && typeName.equals(internalName.replace('/', '.'))) { - this.binaryRepresentation = classFile; + this.binaryRepresentation = binaryRepresentation; } return DO_NOT_TRANSFORM; } diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassReloadingStrategy.java b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassReloadingStrategy.java index 1f52a3f173b..5b3ecae0e99 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassReloadingStrategy.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassReloadingStrategy.java @@ -6,7 +6,10 @@ import java.io.File; import java.io.IOException; -import java.lang.instrument.*; +import java.lang.instrument.ClassDefinition; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.UnmodifiableClassException; import java.security.ProtectionDomain; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -96,17 +99,17 @@ protected ClassReloadingStrategy(Instrumentation instrumentation, * Creates a class reloading strategy for the given instrumentation. The given instrumentation must either * support {@link java.lang.instrument.Instrumentation#isRedefineClassesSupported()} or * {@link java.lang.instrument.Instrumentation#isRetransformClassesSupported()}. If both modes are supported, - * classes will be transformed using a class redefinition. + * classes will be transformed using a class retransformation. * * @param instrumentation The instrumentation to be used by this reloading strategy. * @return A suitable class reloading strategy. */ public static ClassReloadingStrategy of(Instrumentation instrumentation) { Engine engine; - if (instrumentation.isRedefineClassesSupported()) { - engine = Engine.REDEFINITION; - } else if (instrumentation.isRetransformClassesSupported()) { + if (instrumentation.isRetransformClassesSupported()) { engine = Engine.RETRANSFORMATION; + } else if (instrumentation.isRedefineClassesSupported()) { + engine = Engine.REDEFINITION; } else { throw new IllegalArgumentException("Instrumentation does not support manipulation of loaded classes: " + instrumentation); } @@ -421,7 +424,7 @@ public byte[] transform(ClassLoader classLoader, String internalTypeName, Class classBeingRedefined, ProtectionDomain protectionDomain, - byte[] classfileBuffer) throws IllegalClassFormatException { + byte[] classfileBuffer) { if (internalTypeName == null) { return NO_REDEFINITION; } diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/loading/ClassReloadingStrategyTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/loading/ClassReloadingStrategyTest.java index 9aaa9a8fc12..a121102a02a 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/loading/ClassReloadingStrategyTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/loading/ClassReloadingStrategyTest.java @@ -6,10 +6,10 @@ import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.test.utility.AgentAttachmentRule; +import net.bytebuddy.test.utility.ClassFileExtraction; import net.bytebuddy.test.utility.JavaVersionRule; import net.bytebuddy.test.utility.ObjectPropertyAssertion; import net.bytebuddy.utility.RandomString; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; @@ -17,7 +17,9 @@ import java.lang.instrument.ClassDefinition; import java.lang.instrument.Instrumentation; +import java.security.AccessController; import java.util.Collections; +import java.util.concurrent.Callable; import static junit.framework.TestCase.assertEquals; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -29,6 +31,8 @@ public class ClassReloadingStrategyTest { private static final String FOO = "foo", BAR = "bar"; + private static final String LAMBDA_SAMPLE_FACTORY = "net.bytebuddy.test.precompiled.LambdaSampleFactory"; + @Rule public MethodRule agentAttachmentRule = new AgentAttachmentRule(); @@ -185,18 +189,41 @@ public void testEngineSelfReport() throws Exception { @Test @JavaVersionRule.Enforce(8) - @AgentAttachmentRule.Enforce(retransformsClasses = true, redefinesClasses = true) - @Ignore("Fails on the integration server, should be isolated as test is currently not repeatable") + @AgentAttachmentRule.Enforce(retransformsClasses = true) public void testAnonymousType() throws Exception { - ((Runnable) Class.forName("net.bytebuddy.test.precompiled.AnonymousClassLoader").newInstance()).run(); + ClassLoader classLoader = new ByteArrayClassLoader(null, + ClassFileExtraction.of(Class.forName(LAMBDA_SAMPLE_FACTORY)), + null, + AccessController.getContext(), + ByteArrayClassLoader.PersistenceHandler.MANIFEST, + PackageDefinitionStrategy.NoOp.INSTANCE); + Instrumentation instrumentation = ByteBuddyAgent.install(); + Class factory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); + @SuppressWarnings("unchecked") + Callable instance = (Callable) factory.getDeclaredMethod("nonCapturing").invoke(factory.newInstance()); + ClassReloadingStrategy classReloadingStrategy = ClassReloadingStrategy.of(instrumentation).preregistered(instance.getClass()); + ClassFileLocator classFileLocator = ClassFileLocator.AgentBased.of(instrumentation, instance.getClass()); + try { + assertThat(instance.call(), is(FOO)); + new ByteBuddy() + .redefine(instance.getClass(), classFileLocator) + .method(named("call")) + .intercept(FixedValue.value(BAR)) + .make() + .load(instance.getClass().getClassLoader(), classReloadingStrategy); + assertThat(instance.call(), is(BAR)); + } finally { + classReloadingStrategy.reset(classFileLocator, instance.getClass()); + assertThat(instance.call(), is(FOO)); + } } @Test public void testResetEmptyNoEffectImplicitLocator() throws Exception { Instrumentation instrumentation = mock(Instrumentation.class); - when(instrumentation.isRedefineClassesSupported()).thenReturn(true); + when(instrumentation.isRetransformClassesSupported()).thenReturn(true); ClassReloadingStrategy.of(instrumentation).reset(); - verify(instrumentation, times(2)).isRedefineClassesSupported(); + verify(instrumentation, times(2)).isRetransformClassesSupported(); verifyNoMoreInteractions(instrumentation); } @@ -204,9 +231,9 @@ public void testResetEmptyNoEffectImplicitLocator() throws Exception { public void testResetEmptyNoEffect() throws Exception { Instrumentation instrumentation = mock(Instrumentation.class); ClassFileLocator classFileLocator = mock(ClassFileLocator.class); - when(instrumentation.isRedefineClassesSupported()).thenReturn(true); + when(instrumentation.isRetransformClassesSupported()).thenReturn(true); ClassReloadingStrategy.of(instrumentation).reset(classFileLocator); - verify(instrumentation, times(2)).isRedefineClassesSupported(); + verify(instrumentation, times(2)).isRetransformClassesSupported(); verifyNoMoreInteractions(instrumentation); verifyZeroInteractions(classFileLocator); } diff --git a/byte-buddy-dep/src/test/resources/net/bytebuddy/test/precompiled/AnonymousClassLoader.class b/byte-buddy-dep/src/test/resources/net/bytebuddy/test/precompiled/AnonymousClassLoader.class deleted file mode 100644 index 9eefa809fce..00000000000 Binary files a/byte-buddy-dep/src/test/resources/net/bytebuddy/test/precompiled/AnonymousClassLoader.class and /dev/null differ diff --git a/byte-buddy-dep/src/test/resources/net/bytebuddy/test/precompiled/AnonymousClassLoader.java b/byte-buddy-dep/src/test/resources/net/bytebuddy/test/precompiled/AnonymousClassLoader.java deleted file mode 100644 index 910a31c0abb..00000000000 --- a/byte-buddy-dep/src/test/resources/net/bytebuddy/test/precompiled/AnonymousClassLoader.java +++ /dev/null @@ -1,44 +0,0 @@ -package net.bytebuddy.test.precompiled; - -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.agent.ByteBuddyAgent; -import net.bytebuddy.dynamic.ClassFileLocator; -import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; -import net.bytebuddy.implementation.FixedValue; - -import java.lang.instrument.Instrumentation; -import java.util.concurrent.Callable; - -import static net.bytebuddy.matcher.ElementMatchers.named; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -public class AnonymousClassLoader implements Runnable { - - private static final String FOO = "foo", BAR = "bar"; - - @Override - public void run() { - Instrumentation instrumentation = ByteBuddyAgent.install(); - Callable lambda = () -> FOO; - ClassReloadingStrategy classReloadingStrategy = ClassReloadingStrategy.of(instrumentation).preregistered(lambda.getClass()); - ClassFileLocator classFileLocator = ClassFileLocator.AgentBased.of(instrumentation, lambda.getClass()); - try { - - try { - assertThat(lambda.call(), is(FOO)); - new ByteBuddy() - .redefine(lambda.getClass(), classFileLocator) - .method(named("call")) - .intercept(FixedValue.value(BAR)) - .make() - .load(lambda.getClass().getClassLoader(), classReloadingStrategy); - assertThat(lambda.call(), is(BAR)); - } finally { - classReloadingStrategy.reset(classFileLocator, lambda.getClass()); - } - } catch (Exception exception) { - throw new AssertionError(exception); - } - } -}