package net.bytebuddy.dynamic.loading; import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.test.utility.ClassFileExtraction; import net.bytebuddy.test.utility.IntegrationRule; import net.bytebuddy.test.utility.MockitoRule; import org.hamcrest.CoreMatchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.io.InputStream; import java.lang.instrument.ClassFileTransformer; import java.net.URL; import java.security.ProtectionDomain; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import static junit.framework.TestCase.assertEquals; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @RunWith(Parameterized.class) public class ByteArrayClassLoaderTest { private static final ProtectionDomain DEFAULT_PROTECTION_DOMAIN = null; private static final String FOO = "foo", BAR = "bar", QUX = "qux", CLASS_FILE = ".class"; private final ByteArrayClassLoader.PersistenceHandler persistenceHandler; private final boolean expectedResourceLookup; @Rule public TestRule mockitoRule = new MockitoRule(this); @Rule public MethodRule integrationRule = new IntegrationRule(); private InjectionClassLoader classLoader; private URL sealBase; @Mock private PackageDefinitionStrategy packageDefinitionStrategy; @Mock private ClassFileTransformer classFileTransformer; public ByteArrayClassLoaderTest(ByteArrayClassLoader.PersistenceHandler persistenceHandler, boolean expectedResourceLookup) { this.persistenceHandler = persistenceHandler; this.expectedResourceLookup = expectedResourceLookup; } @Parameterized.Parameters public static Collection<Object[]> data() { return Arrays.asList(new Object[][]{ {ByteArrayClassLoader.PersistenceHandler.LATENT, false}, {ByteArrayClassLoader.PersistenceHandler.MANIFEST, true} }); } @Before public void setUp() throws Exception { classLoader = new ByteArrayClassLoader(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassFileExtraction.of(Foo.class), DEFAULT_PROTECTION_DOMAIN, persistenceHandler, packageDefinitionStrategy, classFileTransformer); sealBase = new URL("file://foo"); when(packageDefinitionStrategy.define(classLoader, Foo.class.getPackage().getName(), Foo.class.getName())) .thenReturn(new PackageDefinitionStrategy.Definition.Simple(FOO, BAR, QUX, QUX, FOO, BAR, sealBase)); when(packageDefinitionStrategy.define(classLoader, Bar.class.getPackage().getName(), Bar.class.getName())) .thenReturn(PackageDefinitionStrategy.Definition.Trivial.INSTANCE); when(classFileTransformer.transform(eq(classLoader), anyString(), Mockito.any(Class.class), Mockito.any(ProtectionDomain.class), Mockito.any(byte[].class))).thenAnswer(new Answer<byte[]>() { @Override public byte[] answer(InvocationOnMock invocation) throws Throwable { return (byte[]) invocation.getArguments()[4]; } }); } @Test public void testLoading() throws Exception { Class<?> type = classLoader.loadClass(Foo.class.getName()); assertThat(type.getClassLoader(), is((ClassLoader) classLoader)); assertEquals(classLoader.loadClass(Foo.class.getName()), type); assertThat(type, not(CoreMatchers.<Class<?>>is(Foo.class))); } @Test @IntegrationRule.Enforce public void testPackageDefinition() throws Exception { Class<?> type = classLoader.loadClass(Foo.class.getName()); assertThat(type.getPackage(), notNullValue(Package.class)); assertThat(type.getPackage(), not(Foo.class.getPackage())); assertThat(type.getPackage().getName(), is(Foo.class.getPackage().getName())); assertThat(type.getPackage().getSpecificationTitle(), is(FOO)); assertThat(type.getPackage().getSpecificationVersion(), is(BAR)); assertThat(type.getPackage().getSpecificationVendor(), is(QUX)); assertThat(type.getPackage().getImplementationTitle(), is(QUX)); assertThat(type.getPackage().getImplementationVersion(), is(FOO)); assertThat(type.getPackage().getImplementationVendor(), is(BAR)); assertThat(type.getPackage().isSealed(), is(true)); assertThat(type.getPackage().isSealed(sealBase), is(true)); } @Test public void testResourceStreamLookupBeforeLoading() throws Exception { InputStream inputStream = classLoader.getResourceAsStream(Foo.class.getName().replace('.', '/') + CLASS_FILE); try { assertThat(inputStream, expectedResourceLookup ? notNullValue(InputStream.class) : nullValue(InputStream.class)); } finally { if (inputStream != null) { inputStream.close(); } } } @Test public void testResourceStreamLookupAfterLoading() throws Exception { assertThat(classLoader.loadClass(Foo.class.getName()).getClassLoader(), is((ClassLoader) classLoader)); InputStream inputStream = classLoader.getResourceAsStream(Foo.class.getName().replace('.', '/') + CLASS_FILE); try { assertThat(inputStream, expectedResourceLookup ? notNullValue(InputStream.class) : nullValue(InputStream.class)); } finally { if (inputStream != null) { inputStream.close(); } } } @Test public void testResourceLookupBeforeLoading() throws Exception { assertThat(classLoader.getResource(Foo.class.getName().replace('.', '/') + CLASS_FILE), expectedResourceLookup ? notNullValue(URL.class) : nullValue(URL.class)); } @Test public void testResourceLookupAfterLoading() throws Exception { assertThat(classLoader.loadClass(Foo.class.getName()).getClassLoader(), is((ClassLoader) classLoader)); assertThat(classLoader.getResource(Foo.class.getName().replace('.', '/') + CLASS_FILE), expectedResourceLookup ? notNullValue(URL.class) : nullValue(URL.class)); } @Test public void testResourcesLookupBeforeLoading() throws Exception { Enumeration<URL> enumeration = classLoader.getResources(Foo.class.getName().replace('.', '/') + CLASS_FILE); assertThat(enumeration.hasMoreElements(), is(expectedResourceLookup)); if (expectedResourceLookup) { assertThat(enumeration.nextElement(), notNullValue(URL.class)); assertThat(enumeration.hasMoreElements(), is(false)); } } @Test public void testResourcesLookupAfterLoading() throws Exception { assertThat(classLoader.loadClass(Foo.class.getName()).getClassLoader(), is((ClassLoader) classLoader)); Enumeration<URL> enumeration = classLoader.getResources(Foo.class.getName().replace('.', '/') + CLASS_FILE); assertThat(enumeration.hasMoreElements(), is(expectedResourceLookup)); if (expectedResourceLookup) { assertThat(enumeration.nextElement(), notNullValue(URL.class)); assertThat(enumeration.hasMoreElements(), is(false)); } } @Test public void testResourceLookupWithPrefixBeforeLoading() throws Exception { assertThat(classLoader.getResource("/" + Foo.class.getName().replace('.', '/') + CLASS_FILE), expectedResourceLookup ? notNullValue(URL.class) : nullValue(URL.class)); } @Test public void testResourceLookupWithPrefixAfterLoading() throws Exception { assertThat(classLoader.loadClass(Foo.class.getName()).getClassLoader(), is((ClassLoader) classLoader)); assertThat(classLoader.getResource("/" + Foo.class.getName().replace('.', '/') + CLASS_FILE), expectedResourceLookup ? notNullValue(URL.class) : nullValue(URL.class)); } @Test public void testResourcesLookupWithPrefixBeforeLoading() throws Exception { Enumeration<URL> enumeration = classLoader.getResources("/" + Foo.class.getName().replace('.', '/') + CLASS_FILE); assertThat(enumeration.hasMoreElements(), is(expectedResourceLookup)); if (expectedResourceLookup) { assertThat(enumeration.nextElement(), notNullValue(URL.class)); assertThat(enumeration.hasMoreElements(), is(false)); } } @Test public void testResourcesLookupWithPrefixAfterLoading() throws Exception { assertThat(classLoader.loadClass(Foo.class.getName()).getClassLoader(), is((ClassLoader) classLoader)); Enumeration<URL> enumeration = classLoader.getResources("/" + Foo.class.getName().replace('.', '/') + CLASS_FILE); assertThat(enumeration.hasMoreElements(), is(expectedResourceLookup)); if (expectedResourceLookup) { assertThat(enumeration.nextElement(), notNullValue(URL.class)); assertThat(enumeration.hasMoreElements(), is(false)); } } @Test(expected = ClassNotFoundException.class) public void testNotFoundException() throws Exception { // Note: Will throw a class format error instead targeting not found exception targeting loader attempts. classLoader.loadClass(BAR); } @Test public void testPackage() throws Exception { assertThat(classLoader.loadClass(Foo.class.getName()).getPackage().getName(), is(Foo.class.getPackage().getName())); assertThat(classLoader.loadClass(Foo.class.getName()).getPackage(), not(Foo.class.getPackage())); } @Test public void testInjection() throws Exception { assertThat(classLoader.defineClass(Bar.class.getName(), ClassFileLocator.ForClassLoader.read(Bar.class).resolve()).getName(), is(Bar.class.getName())); } @Test public void testDuplicateInjection() throws Exception { Class<?> type = classLoader.defineClass(Bar.class.getName(), ClassFileLocator.ForClassLoader.read(Bar.class).resolve()); assertThat(classLoader.defineClass(Bar.class.getName(), ClassFileLocator.ForClassLoader.read(Bar.class).resolve()), is((Object) type)); } @Test public void testPredefinedInjection() throws Exception { Class<?> type = classLoader.defineClass(Foo.class.getName(), ClassFileLocator.ForClassLoader.read(Foo.class).resolve()); assertThat(type, is((Object) classLoader.loadClass(Foo.class.getName()))); } private static class Foo { /* Note: Foo is know to the system class loader but not to the bootstrap class loader */ } private static class Bar { /* Note: Bar is know to the system class loader but not to the bootstrap class loader */ } }