package net.bytebuddy.dynamic.loading; import net.bytebuddy.test.utility.IntegrationRule; import net.bytebuddy.test.utility.MockitoRule; import net.bytebuddy.test.utility.ObjectPropertyAssertion; 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.mockito.Mock; import java.lang.reflect.Method; import java.net.URL; import java.util.Enumeration; import java.util.NoSuchElementException; import static net.bytebuddy.matcher.ElementMatchers.isBootstrapClassLoader; import static net.bytebuddy.matcher.ElementMatchers.not; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class MultipleParentClassLoaderTest { private static final String FOO = "foo", BAR = "bar", QUX = "qux", BAZ = "baz", SCHEME = "http://"; @Rule public TestRule mockitoRule = new MockitoRule(this); @Rule public MethodRule integrationRule = new IntegrationRule(); @Mock private ClassLoader first, second; private URL fooUrl, barFirstUrl, barSecondUrl, quxUrl; @Before @SuppressWarnings("unchecked") public void setUp() throws Exception { when(first.loadClass(FOO)).thenReturn((Class) Foo.class); when(first.loadClass(BAR)).thenReturn((Class) BarFirst.class); when(first.loadClass(QUX)).thenThrow(new ClassNotFoundException()); when(first.loadClass(BAZ)).thenThrow(new ClassNotFoundException()); when(second.loadClass(BAR)).thenReturn((Class) BarSecond.class); when(second.loadClass(QUX)).thenReturn((Class) Qux.class); when(second.loadClass(BAZ)).thenThrow(new ClassNotFoundException()); fooUrl = new URL(SCHEME + FOO); barFirstUrl = new URL(SCHEME + BAR); barSecondUrl = new URL(SCHEME + BAZ); quxUrl = new URL(SCHEME + QUX); when(first.getResource(FOO)).thenReturn(fooUrl); when(first.getResource(BAR)).thenReturn(barFirstUrl); when(second.getResource(BAR)).thenReturn(barSecondUrl); when(second.getResource(QUX)).thenReturn(quxUrl); when(first.getResources(FOO)).thenReturn(new SingleElementEnumeration(fooUrl)); when(first.getResources(BAR)).thenReturn(new SingleElementEnumeration(barFirstUrl)); when(second.getResources(BAR)).thenReturn(new SingleElementEnumeration(barSecondUrl)); when(second.getResources(QUX)).thenReturn(new SingleElementEnumeration(quxUrl)); } @Test public void testSingleParentReturnsOriginal() throws Exception { assertThat(new MultipleParentClassLoader.Builder() .append(getClass().getClassLoader(), getClass().getClassLoader()) .build(), is(ClassLoader.getSystemClassLoader())); } @Test public void testSingleParentReturnsOriginalChained() throws Exception { assertThat(new MultipleParentClassLoader.Builder() .append(ClassLoader.getSystemClassLoader()) .append(ClassLoader.getSystemClassLoader()) .build(), is(ClassLoader.getSystemClassLoader())); } @Test public void testClassLoaderFilter() throws Exception { assertThat(new MultipleParentClassLoader.Builder() .append(getClass().getClassLoader(), null) .filter(not(isBootstrapClassLoader())) .build(), is(getClass().getClassLoader())); } @Test public void testMultipleParentClassLoading() throws Exception { ClassLoader classLoader = new MultipleParentClassLoader.Builder().append(first, second, null).build(); assertThat(classLoader.loadClass(FOO), CoreMatchers.<Class<?>>is(Foo.class)); assertThat(classLoader.loadClass(BAR), CoreMatchers.<Class<?>>is(BarFirst.class)); assertThat(classLoader.loadClass(QUX), CoreMatchers.<Class<?>>is(Qux.class)); verify(first).loadClass(FOO); verify(first).loadClass(BAR); verify(first).loadClass(QUX); verifyNoMoreInteractions(first); verify(second).loadClass(QUX); verifyNoMoreInteractions(second); } @Test(expected = ClassNotFoundException.class) public void testMultipleParentClassLoadingNotFound() throws Exception { new MultipleParentClassLoader.Builder().append(first, second, null).build().loadClass(BAZ); } @Test @IntegrationRule.Enforce public void testMultipleParentURL() throws Exception { ClassLoader classLoader = new MultipleParentClassLoader.Builder().append(first, second, null).build(); assertThat(classLoader.getResource(FOO), is(fooUrl)); assertThat(classLoader.getResource(BAR), is(barFirstUrl)); assertThat(classLoader.getResource(QUX), is(quxUrl)); verify(first).getResource(FOO); verify(first).getResource(BAR); verify(first).getResource(QUX); verifyNoMoreInteractions(first); verify(second).getResource(QUX); verifyNoMoreInteractions(second); } @Test public void testMultipleParentURLNotFound() throws Exception { assertThat(new MultipleParentClassLoader.Builder().append(first, second, null).build().getResource(BAZ), nullValue(URL.class)); } @Test @IntegrationRule.Enforce public void testMultipleParentEnumerationURL() throws Exception { ClassLoader classLoader = new MultipleParentClassLoader.Builder().append(first, second, null).build(); Enumeration<URL> foo = classLoader.getResources(FOO); assertThat(foo.hasMoreElements(), is(true)); assertThat(foo.nextElement(), is(fooUrl)); assertThat(foo.hasMoreElements(), is(false)); Enumeration<URL> bar = classLoader.getResources(BAR); assertThat(bar.hasMoreElements(), is(true)); assertThat(bar.nextElement(), is(barFirstUrl)); assertThat(bar.hasMoreElements(), is(true)); assertThat(bar.nextElement(), is(barSecondUrl)); assertThat(bar.hasMoreElements(), is(false)); Enumeration<URL> qux = classLoader.getResources(QUX); assertThat(qux.hasMoreElements(), is(true)); assertThat(qux.nextElement(), is(quxUrl)); assertThat(qux.hasMoreElements(), is(false)); } @Test(expected = NoSuchElementException.class) public void testMultipleParentEnumerationNotFound() throws Exception { ClassLoader classLoader = new MultipleParentClassLoader.Builder().append(first, second, null).build(); Enumeration<URL> enumeration = classLoader.getResources(BAZ); assertThat(enumeration.hasMoreElements(), is(false)); enumeration.nextElement(); } @Test public void testMultipleParentClassLoaderExplicitParentOnly() throws Exception { assertThat(new MultipleParentClassLoader.Builder().build(first), is(first)); } @Test public void testMultipleParentClassLoaderExplicitParentPreIncluded() throws Exception { assertThat(new MultipleParentClassLoader.Builder().append(first).build(first), is(first)); } @Test public void testMultipleParentClassLoaderExplicitParentPreIncludedWithOther() throws Exception { ClassLoader classLoader = new MultipleParentClassLoader.Builder().append(first, second).build(first);; assertThat(classLoader, CoreMatchers.not(first)); assertThat(classLoader, CoreMatchers.not(second)); assertThat(classLoader.getParent(), is(first)); } @Test public void testMultipleParentClassLoaderExplicitParentNotPreIncludedWithOther() throws Exception { ClassLoader classLoader = new MultipleParentClassLoader.Builder().append(second).build(first);; assertThat(classLoader, CoreMatchers.not(second)); assertThat(classLoader.getParent(), is(first)); } @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(MultipleParentClassLoader.Builder.class).apply(); } public static class Foo { /* empty */ } public static class BarFirst { /* empty */ } public static class BarSecond { /* empty */ } public static class Qux { /* empty */ } private static class SingleElementEnumeration implements Enumeration<URL> { private URL element; public SingleElementEnumeration(URL element) { this.element = element; } @Override public boolean hasMoreElements() { return element != null; } @Override public URL nextElement() { if (!hasMoreElements()) { throw new AssertionError(); } try { return element; } finally { element = null; } } } }