Skip to content

Commit 92cbac2

Browse files
charlesmungerCharles MungerTimvdLippe
authored
Undo parent for MultipleParentsClassLoader (#2312)
66998ea inadvertently undid a change from 9bc9be6, which breaks behavior under some classloaders that special case mockito classes. Use of an explicit parent with `MultipleParentsClassLoader` appears to be discouraged in the documentation also. Co-authored-by: Charles Munger <[email protected]> Co-authored-by: Tim van der Lippe <[email protected]>
1 parent 67c5632 commit 92cbac2

File tree

2 files changed

+107
-3
lines changed

2 files changed

+107
-3
lines changed

src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public <T> Class<? extends T> mockClass(MockFeatures<T> features) {
136136
if (shouldIncludeContextLoader) {
137137
loaderBuilder = loaderBuilder.appendMostSpecific(contextLoader);
138138
}
139-
ClassLoader classLoader = loaderBuilder.build(MockMethodInterceptor.class.getClassLoader());
139+
ClassLoader classLoader = loaderBuilder.build();
140140

141141
// If Mockito does not need to create a new class loader and if a mock is not based on a JDK
142142
// type, we attempt

src/test/java/org/mockitousage/bugs/creation/PackagePrivateWithContextClassLoaderTest.java

+106-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
import static org.mockito.Mockito.when;
1010
import static org.mockito.Mockito.withSettings;
1111

12+
import java.io.ByteArrayOutputStream;
13+
import java.io.File;
14+
import java.io.IOException;
15+
import java.io.InputStream;
16+
import java.lang.reflect.Method;
1217
import org.junit.After;
1318
import org.junit.Before;
1419
import org.junit.Test;
@@ -18,11 +23,12 @@ public class PackagePrivateWithContextClassLoaderTest {
1823

1924
private ClassLoader oldContextClassloader;
2025

21-
public static class PublicClass {
22-
26+
public abstract static class PublicClass {
2327
int packagePrivateMethod() {
2428
return 0;
2529
}
30+
31+
abstract void packagePrivateAbstractMethod();
2632
}
2733

2834
public interface PublicInterface {}
@@ -67,4 +73,102 @@ public void should_be_able_to_mock_package_private_extra_interface() throws Exce
6773
PublicInterface.class,
6874
withSettings().extraInterfaces(PackagePrivateInterface.class));
6975
}
76+
77+
/**
78+
* In this test we have a class that delegates loading of mockito/JDK classes to its parent,
79+
* but defines in its own for others. If mockito selects the defining classloader of the mock
80+
* to the classloader of mockito, calling the abstract package-private method will fail - the
81+
* defining classloader of the mocked type's package is different from the generated mock class
82+
* package. Because the nonDelegatingLoader is a child of mockito's loader, it's more specific
83+
* and should be preferred.
84+
*/
85+
@Test
86+
public void classloader_with_parent_but_does_not_delegate() throws Exception {
87+
ClassLoader nonDelegatingLoader = new NotAlwaysDelegatingClassLoader();
88+
Thread.currentThread().setContextClassLoader(nonDelegatingLoader);
89+
Class<?> loaded =
90+
Class.forName(LoadedByCustomLoader.class.getName(), false, nonDelegatingLoader);
91+
Method attemptMock = loaded.getDeclaredMethod("attemptMock");
92+
attemptMock.invoke(null);
93+
}
94+
95+
public static class LoadedByCustomLoader {
96+
public static void attemptMock() {
97+
PublicClass mock = mock(PublicClass.class);
98+
mock.packagePrivateAbstractMethod();
99+
}
100+
}
101+
102+
/**
103+
* This classloader has a parent, but doesn't always delegate to it.
104+
*/
105+
public static final class NotAlwaysDelegatingClassLoader extends ClassLoader {
106+
107+
/**
108+
* Initial size of buffer used to read class data.
109+
*/
110+
/* Note: should be enough for most classes, and is not a hard limit. */
111+
private static final int BUF_SIZE = 4096;
112+
113+
public NotAlwaysDelegatingClassLoader() {
114+
super(NotAlwaysDelegatingClassLoader.class.getClassLoader());
115+
}
116+
117+
@Override
118+
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
119+
// First, check if the class has already been loaded. If not, load it
120+
// ourselves or delegate to the parent.
121+
Class<?> result = findLoadedClass(name);
122+
if (result == null) {
123+
// All classes defined in this testsuite should be loaded by this classloader,
124+
// but any other class (e.g. those coming from java.* or org.mockito.* packages)
125+
// will be loaded by the parent.
126+
if (name.startsWith("org.mockitousage.")) {
127+
result = findClass(name);
128+
} else {
129+
return super.loadClass(name, resolve);
130+
}
131+
}
132+
if (resolve) {
133+
resolveClass(result);
134+
}
135+
return result;
136+
}
137+
138+
@Override
139+
public Class<?> findClass(String className) throws ClassNotFoundException {
140+
try {
141+
// Create a package for this class, unless it's in the default package.
142+
int dotpos = className.lastIndexOf('.');
143+
if (dotpos != -1) {
144+
String pkgname = className.substring(0, dotpos);
145+
if (getPackage(pkgname) == null) {
146+
definePackage(pkgname, null, null, null, null, null, null, null);
147+
}
148+
}
149+
String resourceName = className.replace('.', File.separatorChar) + ".class";
150+
InputStream input = getSystemResourceAsStream(resourceName);
151+
if (input == null) {
152+
throw new ClassNotFoundException("Couldn't find resource " + resourceName);
153+
}
154+
byte[] classData = loadClassData(input);
155+
return defineClass(className, classData, 0, classData.length, null);
156+
} catch (IOException e) {
157+
throw new ClassNotFoundException("Cannot load " + className, e);
158+
}
159+
}
160+
161+
/**
162+
* Load class data from a given input stream.
163+
*/
164+
private byte[] loadClassData(InputStream input) throws IOException {
165+
ByteArrayOutputStream output = new ByteArrayOutputStream(BUF_SIZE);
166+
byte[] buffer = new byte[BUF_SIZE];
167+
int readCount;
168+
while ((readCount = input.read(buffer, 0, BUF_SIZE)) >= 0) {
169+
output.write(buffer, 0, readCount);
170+
}
171+
return output.toByteArray();
172+
}
173+
}
70174
}

0 commit comments

Comments
 (0)