// For fast translation of the classname *intentionally not static*
private SoftHashMap /* <String,URL> */nameMap = new SoftHashMap(new HashMap(), false);
- public static boolean useSharedCache = System.getProperty("org.aspectj.apache.bcel.useSharedCache", "true").equalsIgnoreCase("true");
+ public static boolean useSharedCache =
+ System.getProperty("org.aspectj.apache.bcel.useSharedCache", "true").equalsIgnoreCase("true");
- //Cache not found classes as well to prevent unnecessary file I/O operations
- public static final boolean useUnavailableClassesCache =
+ // Cache not found classes as well to prevent unnecessary file I/O operations
+ public static boolean useUnavailableClassesCache =
System.getProperty("org.aspectj.apache.bcel.useUnavailableClassesCache", "false").equalsIgnoreCase("true");
- //Ignore cache clear requests to not build up the cache over and over again
- public static final boolean ignoreCacheClearRequests =
+ // Ignore cache clear requests to not build up the cache over and over again
+ public static boolean ignoreCacheClearRequests =
System.getProperty("org.aspectj.apache.bcel.ignoreCacheClearRequests", "false").equalsIgnoreCase("true");
- //Second cache for the unavailable classes
+ // Second cache for the unavailable classes
private static Set<String> unavailableClasses = new HashSet<String>();
private static int cacheHitsShared = 0;
private int classesLoadedCount = 0;
private int misses = 0;
private int cacheHitsLocal = 0;
+ private int unavailableClassesCacheHits = 0;
private int missLocalEvicted = 0; // Misses in local cache access due to reference GC
public ClassLoaderRepository(java.lang.ClassLoader loader) {
}
/**
- * Lookup a JavaClass object from the Class Name provided.
+ * Lookup a JavaClass object from the classname provided.
*/
public JavaClass loadClass(String className) throws ClassNotFoundException {
-
- //Quick evaluation of unavailable classes to prevent unnecessary file I/O
- if (useUnavailableClassesCache && unavailableClasses.contains(className))
+ // Quick evaluation of unavailable classes to prevent unnecessary file I/O
+ if (useUnavailableClassesCache && unavailableClasses.contains(className)) {
+ unavailableClassesCacheHits++;
throw new ClassNotFoundException(className + " not found.");
+ }
- // translate to a URL
long time = System.currentTimeMillis();
java.net.URL url = toURL(className);
timeManipulatingURLs += (System.currentTimeMillis() - time);
if (url == null) {
- if (useUnavailableClassesCache)
+ if (useUnavailableClassesCache) {
unavailableClasses.add(className);
+ }
throw new ClassNotFoundException(className + " not found - unable to determine URL");
}
JavaClass clazz = null;
-
// Look in the appropriate cache
if (useSharedCache) {
clazz = findClassShared(url);
*/
public long[] reportStats() {
return new long[] { timeSpentLoading, timeManipulatingURLs, classesLoadedCount, cacheHitsShared, missSharedEvicted,
- cacheHitsLocal, missLocalEvicted, sharedCache.size() };
+ cacheHitsLocal, missLocalEvicted, sharedCache.size(), unavailableClassesCacheHits };
}
/**
cacheHitsShared = 0;
missSharedEvicted = 0;
missLocalEvicted = 0;
+ unavailableClassesCacheHits = 0;
misses = 0;
clear();
}
/** Clear all entries from the local cache */
public void clear() {
if (!ignoreCacheClearRequests) {
- if (useSharedCache)
+ if (useSharedCache) {
sharedCache.clear();
- else
+ } else {
localCache.clear();
+ }
+ unavailableClasses.clear();
}
}
/*
* Tests create a simple classloader repository configuration and check sharing of information.
+ *
+ * @author Andy Clement
*/
public class ClassloaderRepositoryTest extends TestCase {
}
}
+ // ClassLoaderRepository.ignoreCacheClearRequests
+ public void testIgnoreCacheClearRequests() throws Exception {
+ ClassLoaderRepository.useSharedCache = false;
+ try {
+ // the 'normal' flow with ignore in default of false
+ ClassLoaderRepository.ignoreCacheClearRequests = false;
+ try {
+ ClassLoaderRepository repository = setupRepository();
+ repository.loadClass("java.lang.String");
+ long localCacheHits = repository.reportStats()[5];
+ assertEquals(0, localCacheHits);
+ repository.clear();
+ repository.loadClass("java.lang.String");
+ localCacheHits = repository.reportStats()[5];
+ assertEquals(0, localCacheHits); // cache was cleared, so no hit
+ } finally {
+ ClassLoaderRepository.ignoreCacheClearRequests = false;
+ }
+ // with ignore cache clear turned on
+ ClassLoaderRepository.ignoreCacheClearRequests = true;
+ try {
+ ClassLoaderRepository repository = setupRepository();
+ repository.loadClass("java.lang.String");
+ long localCacheHits = repository.reportStats()[5];
+ assertEquals(0, localCacheHits);
+ repository.clear();
+ repository.loadClass("java.lang.String");
+ localCacheHits = repository.reportStats()[5];
+ assertEquals(1, localCacheHits);
+ } finally {
+ ClassLoaderRepository.ignoreCacheClearRequests = false;
+ }
+ } finally {
+ ClassLoaderRepository.useSharedCache = true;
+ }
+ }
+
+ // ClassLoaderRepository.useUnavailableClassesCache
+ public void testUnavailableClassesCache() throws Exception {
+ ClassLoaderRepository.useUnavailableClassesCache = false;
+ try {
+ ClassLoaderRepository repository = setupRepository();
+ attemptLoadThatWillFail(repository);
+ for (int i = 0; i < 1000; i++) {
+ attemptLoadThatWillFail(repository);
+ }
+ assertEquals(0, repository.reportStats()[8]);
+ } finally {
+ ClassLoaderRepository.useUnavailableClassesCache = false; // back to default
+ }
+
+ ClassLoaderRepository.useUnavailableClassesCache = true;
+ try {
+ ClassLoaderRepository repository = setupRepository();
+ assertNotNull(repository.loadClass("java.lang.String"));
+ attemptLoadThatWillFail(repository);
+ for (int i = 0; i < 1000; i++) {
+ attemptLoadThatWillFail(repository);
+ }
+ assertEquals(1000,repository.reportStats()[8]);
+ } finally {
+ ClassLoaderRepository.useUnavailableClassesCache = false;
+ }
+ // If checking the report stats for time spent manipulating URLs it will be massively reduced
+ }
+
+ private ClassLoaderRepository setupRepository() throws Exception {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ ClassLoader res = new URLClassLoader(new URL[] {}, cl);
+ ClassLoaderRepository rep = new ClassLoaderRepository(res);
+ return rep;
+ }
+
+ private void attemptLoadThatWillFail(ClassLoaderRepository repository) {
+ try {
+ repository.loadClass("this.is.made.up");
+ throw new IllegalStateException("Should not have found 'this.is.made.up'");
+ } catch (ClassNotFoundException cnfe) {
+ // ... expected ...
+ }
+ }
+
public void tearDown() throws Exception {
super.tearDown();
System.err.println("Rep1: "+rep1.reportStats());
private World world;
private static boolean useCachingClassLoaderRepository;
- //Use single instance of Repository and ClassLoader
- public static final boolean useSingleInstances =
+ // Use single instance of Repository and ClassLoader
+ public static boolean useSingleInstances =
System.getProperty("org.aspectj.apache.bcel.useSingleRepositoryInstance", "false").equalsIgnoreCase("true");
static {
}
public void setClassLoader(ClassLoader aLoader) {
- //Set class loader ref
- if (useSingleInstances && staticClassLoaderRef == null)
+ // Set class loader ref
+ if (useSingleInstances && staticClassLoaderRef == null) {
staticClassLoaderRef = new BcelWeakClassLoaderReference(aLoader);
- else
+ } else {
this.classLoaderRef = new BcelWeakClassLoaderReference(aLoader);
+ }
- //Set repository
+ // Set repository
if (useCachingClassLoaderRepository) {
- if (useSingleInstances && staticBcelRepository == null)
+ if (useSingleInstances && staticBcelRepository == null) {
staticBcelRepository = new ClassLoaderRepository(getClassLoader());
- else
+ } else {
this.bcelRepository = new ClassLoaderRepository(classLoaderRef);
- }
- else {
- if (useSingleInstances && staticBcelRepository == null)
+ }
+ } else {
+ if (useSingleInstances && staticBcelRepository == null) {
staticBcelRepository = new NonCachingClassLoaderRepository(getClassLoader());
- else
+ } else {
this.bcelRepository = new NonCachingClassLoaderRepository(classLoaderRef);
+ }
}
}