com.fitbur.mockito.internal.creation.bytebuddy.CachingMockBytecodeGenerator Maven / Gradle / Ivy
package com.fitbur.mockito.internal.creation.bytebuddy;
import static com.fitbur.mockito.internal.util.StringJoiner.join;
import java.lang.ref.WeakReference;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.fitbur.mockito.exceptions.base.MockitoException;
class CachingMockBytecodeGenerator {
private final Lock avoidingClassLeakCacheLock = new ReentrantLock();
public final WeakHashMap avoidingClassLeakageCache =
new WeakHashMap();
private final MockBytecodeGenerator mockBytecodeGenerator = new MockBytecodeGenerator();
@SuppressWarnings("unchecked")
public Class get(MockFeatures params) {
// TODO improves locking behavior with ReentrantReadWriteLock ?
avoidingClassLeakCacheLock.lock();
try {
Class> generatedMockClass = mockCachePerClassLoaderOf(params.mockedType).getOrGenerateMockClass(
params
);
return (Class) generatedMockClass;
} finally {
avoidingClassLeakCacheLock.unlock();
}
}
private CachedBytecodeGenerator mockCachePerClassLoaderOf(Class mockedType) {
if (!avoidingClassLeakageCache.containsKey(mockedType.getClassLoader())) {
avoidingClassLeakageCache.put(
mockedType.getClassLoader(),
new CachedBytecodeGenerator(mockBytecodeGenerator)
);
}
return avoidingClassLeakageCache.get(mockedType.getClassLoader());
}
private static class CachedBytecodeGenerator {
private ConcurrentHashMap>> generatedClassCache =
new ConcurrentHashMap>>();
private final MockBytecodeGenerator generator;
private CachedBytecodeGenerator(MockBytecodeGenerator generator) {
this.generator = generator;
}
public Class> getOrGenerateMockClass(MockFeatures features) {
MockKey> mockKey = MockKey.of(features.mockedType, features.interfaces);
Class> generatedMockClass = null;
WeakReference> classWeakReference = generatedClassCache.get(mockKey);
if(classWeakReference != null) {
generatedMockClass = classWeakReference.get();
}
if(generatedMockClass == null) {
generatedMockClass = generate(features);
}
generatedClassCache.put(mockKey, new WeakReference>(generatedMockClass));
return generatedMockClass;
}
private Class extends T> generate(MockFeatures mockFeatures) {
try {
return generator.generateMockClass(mockFeatures);
} catch (Exception bytecodeGenerationFailed) {
throw prettifyFailure(mockFeatures, bytecodeGenerationFailed);
}
}
private RuntimeException prettifyFailure(MockFeatures> mockFeatures, Exception generationFailed) {
if (Modifier.isPrivate(mockFeatures.mockedType.getModifiers())) {
throw new MockitoException(join(
"Mockito cannot mock this class: " + mockFeatures.mockedType + ".",
"Most likely it is a private class that is not visible by Mockito",
""
), generationFailed);
}
throw new MockitoException(join(
"Mockito cannot mock this class: " + mockFeatures.mockedType,
"",
"Mockito can only mock visible & non-final classes.",
"If you're not sure why you're getting this error, please report to the mailing list.",
"",
"Underlying exception : " + generationFailed),
generationFailed
);
}
// should be stored as a weak reference
private static class MockKey {
private final String mockedType;
private final Set types = new HashSet();
private MockKey(Class mockedType, Set> interfaces) {
this.mockedType = mockedType.getName();
for (Class> anInterface : interfaces) {
types.add(anInterface.getName());
}
types.add(this.mockedType);
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
MockKey mockKey = (MockKey>) other;
if (!mockedType.equals(mockKey.mockedType)) return false;
if (!types.equals(mockKey.types)) return false;
return true;
}
@Override
public int hashCode() {
int result = mockedType.hashCode();
result = 31 * result + types.hashCode();
return result;
}
public static MockKey of(Class mockedType, Set> interfaces) {
return new MockKey(mockedType, interfaces);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy