@@ -57,11 +57,15 @@ package org.aspectj.apache.bcel.util; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.lang.ref.Reference; | |||
import java.lang.ref.ReferenceQueue; | |||
import java.lang.ref.SoftReference; | |||
import java.net.URL; | |||
import java.net.URLClassLoader; | |||
import java.util.AbstractMap; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.WeakHashMap; | |||
import org.aspectj.apache.bcel.classfile.ClassParser; | |||
@@ -76,32 +80,35 @@ import org.aspectj.apache.bcel.classfile.JavaClass; | |||
* | |||
* @see org.aspectj.apache.bcel.Repository | |||
* | |||
* @version $Id: ClassLoaderRepository.java,v 1.7 2006/08/18 14:51:00 acolyer Exp $ | |||
* @version $Id: ClassLoaderRepository.java,v 1.8 2006/08/21 15:23:58 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* @author David Dixon-Peugh | |||
*/ | |||
public class ClassLoaderRepository implements Repository { | |||
private static java.lang.ClassLoader bootClassLoader = null; | |||
private java.lang.ClassLoader loader; | |||
private WeakHashMap /*<String classname,JavaClass>*/loadedClassesLocalCache = new WeakHashMap(); | |||
private static Map /*<URL,JavaClass>*/loadedUrlsSharedCache = new HashMap(); | |||
public static boolean useSharedCache = true; | |||
private static long timeManipulatingURLs = 0L; | |||
private static long timeSpentLoading = 0L; | |||
private static int classesLoadedCount = 0; | |||
// Choice of cache... | |||
private WeakHashMap /*<URL,SoftRef(JavaClass)>*/localCache = new WeakHashMap(); | |||
private static SoftHashMap /*<URL,JavaClass>*/sharedCache = new SoftHashMap(Collections.synchronizedMap(new HashMap())); | |||
// 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"); | |||
private static int cacheHitsShared = 0; | |||
private static int missSharedEvicted = 0; // Misses in shared cache access due to reference GC | |||
private static int misses = 0; | |||
private long timeManipulatingURLs = 0L; | |||
private long timeSpentLoading = 0L; | |||
private int classesLoadedCount = 0; | |||
private int misses = 0; | |||
private int cacheHitsLocal = 0; | |||
private int missLocalEvicted = 0; // Misses in local cache access due to reference GC | |||
static { | |||
useSharedCache = System.getProperty("org.aspectj.apache.bcel.useSharedCache","true").equalsIgnoreCase("true"); | |||
} | |||
public ClassLoaderRepository( java.lang.ClassLoader loader ) { | |||
this.loader = (loader != null) ? loader : getBootClassLoader(); | |||
this.loader = (loader != null) ? loader : getBootClassLoader(); | |||
} | |||
private static synchronized java.lang.ClassLoader getBootClassLoader() { | |||
@@ -110,43 +117,113 @@ public class ClassLoaderRepository implements Repository { | |||
} | |||
return bootClassLoader; | |||
} | |||
// Can track back to its key | |||
public static class SoftHashMap extends AbstractMap { | |||
private Map map; | |||
boolean recordMiss = true; // only interested in recording miss stats sometimes | |||
private ReferenceQueue rq = new ReferenceQueue(); | |||
public SoftHashMap(Map map) { this.map = map; } | |||
public SoftHashMap() { this(new HashMap()); } | |||
public SoftHashMap(Map map, boolean b) { this(map); this.recordMiss=b;} | |||
class SpecialValue extends SoftReference { | |||
private final Object key; | |||
SpecialValue(Object k,Object v) { | |||
super(v,rq); | |||
this.key = k; | |||
} | |||
} | |||
private void processQueue() { | |||
SpecialValue sv = null; | |||
while ((sv = (SpecialValue)rq.poll())!=null) { | |||
map.remove(sv.key); | |||
} | |||
} | |||
public Object get(Object key) { | |||
SpecialValue value = (SpecialValue)map.get(key); | |||
if (value==null) return null; | |||
if (value.get()==null) { | |||
// it got GC'd | |||
map.remove(value.key); | |||
if (recordMiss) missSharedEvicted++; | |||
return null; | |||
} else { | |||
return value.get(); | |||
} | |||
} | |||
public Object put(Object k, Object v) { | |||
processQueue(); | |||
return map.put(k, new SpecialValue(k,v)); | |||
} | |||
public Set entrySet() { | |||
return map.entrySet(); | |||
} | |||
public void clear() { | |||
processQueue(); | |||
map.clear(); | |||
} | |||
public int size() { | |||
processQueue(); | |||
return map.size(); | |||
} | |||
public Object remove(Object k) { | |||
processQueue(); | |||
SpecialValue value = (SpecialValue)map.remove(k); | |||
if (value==null) return null; | |||
if (value.get()!=null) { | |||
return value.get(); | |||
} | |||
return null; | |||
} | |||
} | |||
/** | |||
* Store a new JavaClass into this repository as a soft reference and return the reference | |||
*/ | |||
private Reference storeClassAsReference( JavaClass clazz ) { | |||
Reference ref = new SoftReference(clazz); | |||
loadedClassesLocalCache.put( clazz.getClassName(), ref); | |||
clazz.setRepository( this ); | |||
return ref; | |||
private void storeClassAsReference(URL url, JavaClass clazz ) { | |||
if (useSharedCache) { | |||
clazz.setRepository(null); // can't risk setting repository, we'll get in a pickle! | |||
sharedCache.put(url, clazz); | |||
} else { | |||
clazz.setRepository(this); | |||
localCache.put(url, new SoftReference(clazz)); | |||
} | |||
} | |||
/** | |||
* Store a reference in the shared cache | |||
*/ | |||
private void storeReferenceShared(URL url, Reference ref) { | |||
if (useSharedCache) loadedUrlsSharedCache.put(url, ref); | |||
} | |||
/** | |||
* Store a new JavaClass into this Repository. | |||
*/ | |||
public void storeClass( JavaClass clazz ) { | |||
storeClassAsReference(clazz); | |||
storeClassAsReference(toURL(clazz.getClassName()),clazz); | |||
} | |||
/** | |||
* Remove class from repository | |||
*/ | |||
public void removeClass(JavaClass clazz) { | |||
loadedClassesLocalCache.remove(clazz.getClassName()); | |||
if (useSharedCache) sharedCache.remove(toURL(clazz.getClassName())); | |||
else localCache.remove(toURL(clazz.getClassName())); | |||
} | |||
/** | |||
* Find an already defined JavaClass in the local cache. | |||
*/ | |||
public JavaClass findClass( String className ) { | |||
Object o = loadedClassesLocalCache.get( className ); | |||
if (useSharedCache) return findClassShared(toURL(className)); | |||
else return findClassLocal(toURL(className)); | |||
} | |||
private JavaClass findClassLocal( URL url ) { | |||
Object o = localCache.get(url); | |||
if (o != null) { | |||
o = ((Reference)o).get(); | |||
if (o != null) { | |||
@@ -162,55 +239,56 @@ public class ClassLoaderRepository implements Repository { | |||
* Find an already defined JavaClass in the shared cache. | |||
*/ | |||
private JavaClass findClassShared(URL url) { | |||
if (!useSharedCache) return null; | |||
Object o = (Reference)loadedUrlsSharedCache.get(url); | |||
if (o != null) { | |||
o = ((Reference)o).get(); | |||
if (o != null) { | |||
return (JavaClass)o; | |||
} else { | |||
missSharedEvicted++; | |||
} | |||
} | |||
return null; | |||
return (JavaClass)sharedCache.get(url); | |||
} | |||
private URL toURL(String className) { | |||
URL url = (URL)nameMap.get(className); | |||
if (url==null) { | |||
String classFile = className.replace('.', '/'); | |||
url = loader.getResource( classFile + ".class" ); | |||
nameMap.put(className, url); | |||
} | |||
return url; | |||
} | |||
/** | |||
* Lookup a JavaClass object from the Class Name provided. | |||
*/ | |||
public JavaClass loadClass( String className ) throws ClassNotFoundException { | |||
String classFile = className.replace('.', '/'); | |||
// Check the local cache | |||
JavaClass clazz = findClass(className); | |||
if (clazz != null) { cacheHitsLocal++; return clazz; } | |||
// translate to a URL | |||
long time = System.currentTimeMillis(); | |||
java.net.URL url = toURL(className); | |||
timeManipulatingURLs += (System.currentTimeMillis() - time); | |||
if (url==null) throw new ClassNotFoundException(className + " not found."); | |||
JavaClass clazz = null; | |||
try { | |||
// Work out the URL | |||
long time = System.currentTimeMillis(); | |||
java.net.URL url = (useSharedCache?loader.getResource( classFile + ".class" ):null); | |||
if (useSharedCache && url==null) throw new ClassNotFoundException(className + " not found."); | |||
InputStream is = (useSharedCache?url.openStream():loader.getResourceAsStream( classFile + ".class" )); | |||
timeManipulatingURLs += (System.currentTimeMillis() - time); | |||
// Check the shared cache | |||
// Look in the appropriate cache | |||
if (useSharedCache) { | |||
clazz = findClassShared(url); | |||
if (clazz != null) { cacheHitsShared++; timeSpentLoading+=(System.currentTimeMillis() - time); return clazz; } | |||
if (clazz != null) { cacheHitsShared++; return clazz; } | |||
} else { | |||
clazz = findClassLocal(url); | |||
if (clazz != null) { cacheHitsLocal++; return clazz; } | |||
} | |||
// Didn't find it in either cache | |||
misses++; | |||
// Didn't find it in either cache | |||
misses++; | |||
if (is == null) { // TODO move this up? | |||
throw new ClassNotFoundException(className + " not found."); | |||
} | |||
ClassParser parser = new ClassParser( is, className ); | |||
try { | |||
// Load it | |||
String classFile = className.replace('.', '/'); | |||
InputStream is = (useSharedCache?url.openStream():loader.getResourceAsStream( classFile + ".class" )); | |||
if (is == null) { | |||
throw new ClassNotFoundException(className + " not found."); | |||
} | |||
ClassParser parser = new ClassParser( is, className ); | |||
clazz = parser.parse(); | |||
// Store it in both caches | |||
Reference ref = storeClassAsReference( clazz ); | |||
storeReferenceShared(url,ref); | |||
// Cache it | |||
storeClassAsReference(url, clazz ); | |||
timeSpentLoading += (System.currentTimeMillis() - time); | |||
classesLoadedCount++; | |||
@@ -221,27 +299,42 @@ public class ClassLoaderRepository implements Repository { | |||
} | |||
/** | |||
/** | |||
* Produce a report on cache usage. | |||
*/ | |||
public String reportAllStatistics() { | |||
public String report() { | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append("BCEL repository report."); | |||
if (!useSharedCache) sb.append(" (Shared cache deactivated)"); | |||
if (useSharedCache) sb.append(" (shared cache)"); | |||
else sb.append(" (local cache)"); | |||
sb.append(" Total time spent loading: "+timeSpentLoading+"ms."); | |||
sb.append(" Time manipulating URLs: "+timeManipulatingURLs+"ms."); | |||
sb.append(" Time spent manipulating URLs: "+timeManipulatingURLs+"ms."); | |||
sb.append(" Classes loaded: "+classesLoadedCount+"."); | |||
if (useSharedCache) sb.append(" URL cache (hits/missDueToEviction): ("+cacheHitsShared+"/"+missSharedEvicted+")."); | |||
sb.append(" Local cache (hits/missDueToEviction): ("+cacheHitsLocal+"/"+missLocalEvicted+")."); | |||
if (useSharedCache) { | |||
sb.append(" Shared cache size: "+sharedCache.size()); | |||
sb.append(" Shared cache (hits/missDueToEviction): ("+cacheHitsShared+"/"+missSharedEvicted+")."); | |||
} else { | |||
sb.append(" Local cache size: "+localCache.size()); | |||
sb.append(" Local cache (hits/missDueToEviction): ("+cacheHitsLocal+"/"+missLocalEvicted+")."); | |||
} | |||
return sb.toString(); | |||
} | |||
public int reportLocalCacheHits() { | |||
return cacheHitsLocal; | |||
} | |||
public static int reportSharedCacheHits() { | |||
return cacheHitsShared; | |||
/** | |||
* Returns an array of the stats, for testing, the order is fixed: | |||
* 0=time spent loading (static) | |||
* 1=time spent manipulating URLs (static) | |||
* 2=classes loaded (static) | |||
* 3=cache hits shared (static) | |||
* 4=misses in shared due to eviction (static) | |||
* 5=cache hits local | |||
* 6=misses in local due to eviction | |||
* 7=shared cache size | |||
*/ | |||
public long[] reportStats() { | |||
return new long[]{timeSpentLoading,timeManipulatingURLs,classesLoadedCount, | |||
cacheHitsShared,missSharedEvicted,cacheHitsLocal,missLocalEvicted, | |||
sharedCache.size()}; | |||
} | |||
/** | |||
@@ -257,7 +350,6 @@ public class ClassLoaderRepository implements Repository { | |||
missLocalEvicted = 0; | |||
misses = 0; | |||
clear(); | |||
clearShared(); | |||
} | |||
@@ -267,12 +359,10 @@ public class ClassLoaderRepository implements Repository { | |||
/** Clear all entries from the local cache */ | |||
public void clear() { | |||
loadedClassesLocalCache.clear(); | |||
} | |||
/** Clear all entries from the shared cache */ | |||
public static void clearShared() { | |||
loadedUrlsSharedCache.clear(); | |||
if (useSharedCache) sharedCache.clear(); | |||
else localCache.clear(); | |||
} | |||
} | |||
@@ -26,20 +26,33 @@ public class ClassloaderRepositoryTest extends TestCase { | |||
// Retrieve string 5 times from same repository, 4 hits should be from local cache | |||
public void testLocalCacheWorks() throws ClassNotFoundException { | |||
ClassLoaderRepository.useSharedCache=false; | |||
JavaClass jc = rep1.loadClass("java.lang.String"); | |||
jc = rep1.loadClass("java.lang.String"); | |||
jc = rep1.loadClass("java.lang.String"); | |||
jc = rep1.loadClass("java.lang.String"); | |||
jc = rep1.loadClass("java.lang.String"); | |||
assertTrue("Should have used local cache 4 times: "+rep1.reportLocalCacheHits(),rep1.reportLocalCacheHits()==4); | |||
assertTrue("Should have used local cache 4 times: "+reportLocalCacheHits(rep1),reportLocalCacheHits(rep1)==4); | |||
} | |||
// Retrieve string 5 times from same repository, 4 hits should be from local cache | |||
public void testSharedCacheWorksOnOne() throws ClassNotFoundException { | |||
ClassLoaderRepository.useSharedCache=true; | |||
JavaClass jc = rep1.loadClass("java.lang.String"); | |||
jc = rep1.loadClass("java.lang.String"); | |||
jc = rep1.loadClass("java.lang.String"); | |||
jc = rep1.loadClass("java.lang.String"); | |||
jc = rep1.loadClass("java.lang.String"); | |||
assertTrue("Should have used local cache 4 times: "+reportSharedCacheHits(rep1),reportSharedCacheHits(rep1)==4); | |||
} | |||
// Retrieve String through one repository then load again through another, should be shared cache hit | |||
public void testSharedCacheWorks() throws ClassNotFoundException { | |||
ClassLoaderRepository.useSharedCache=true; | |||
JavaClass jc = rep1.loadClass("java.lang.String"); | |||
jc = rep2.loadClass("java.lang.String"); | |||
assertTrue("Should have retrieved String from shared cache: "+ClassLoaderRepository.reportSharedCacheHits(), | |||
ClassLoaderRepository.reportSharedCacheHits()==1); | |||
assertTrue("Should have retrieved String from shared cache: "+reportSharedCacheHits(rep1), | |||
reportSharedCacheHits(rep1)==1); | |||
} | |||
// Shared cache OFF, shouldn't get a shared cache hit | |||
@@ -49,8 +62,8 @@ public class ClassloaderRepositoryTest extends TestCase { | |||
JavaClass jc = rep1.loadClass("java.lang.String"); | |||
jc = rep2.loadClass("java.lang.String"); | |||
assertTrue("Should not have retrieved String from shared cache: "+ | |||
ClassLoaderRepository.reportSharedCacheHits(), | |||
ClassLoaderRepository.reportSharedCacheHits()==0); | |||
reportSharedCacheHits(rep1), | |||
reportSharedCacheHits(rep1)==0); | |||
} finally { | |||
ClassLoaderRepository.useSharedCache=true; | |||
} | |||
@@ -58,10 +71,19 @@ public class ClassloaderRepositoryTest extends TestCase { | |||
public void tearDown() throws Exception { | |||
super.tearDown(); | |||
System.err.println("Rep1: "+rep1.reportAllStatistics()); | |||
System.err.println("Rep2: "+rep2.reportAllStatistics()); | |||
System.err.println("Rep1: "+rep1.reportStats()); | |||
System.err.println("Rep2: "+rep2.reportStats()); | |||
rep1.reset(); | |||
rep2.reset(); | |||
} | |||
private long reportLocalCacheHits(ClassLoaderRepository rep) { | |||
return rep.reportStats()[5]; | |||
} | |||
private long reportSharedCacheHits(ClassLoaderRepository rep) { | |||
return rep.reportStats()[3]; | |||
} | |||
} | |||
@@ -187,7 +187,12 @@ public class BcelWeaver implements IWeaver { | |||
WeaverStateInfo wsi = type.getWeaverState(); | |||
if (wsi != null && wsi.isReweavable()) { | |||
BcelObjectType classType = getClassType(type.getName()); | |||
classType.setJavaClass(Utility.makeJavaClass(classType.getJavaClass().getFileName(), wsi.getUnwovenClassFileData(classType.getJavaClass().getBytes()))); | |||
JavaClass wovenJavaClass = classType.getJavaClass(); | |||
JavaClass unwovenJavaClass = Utility.makeJavaClass(wovenJavaClass.getFileName(), | |||
wsi.getUnwovenClassFileData(wovenJavaClass.getBytes())); | |||
world.storeClass(unwovenJavaClass); | |||
classType.setJavaClass(unwovenJavaClass); | |||
// classType.setJavaClass(Utility.makeJavaClass(classType.getJavaClass().getFileName(), wsi.getUnwovenClassFileData(classType.getJavaClass().getBytes()))); | |||
} | |||
//TODO AV - happens to reach that a lot of time: for each type flagged reweavable X for each aspect in the weaverstate |
@@ -81,7 +81,7 @@ import org.aspectj.weaver.patterns.SimpleScope; | |||
public class BcelWorld extends World implements Repository { | |||
private ClassPathManager classPath; | |||
private Repository delegate; | |||
protected Repository delegate; | |||
//private ClassPathManager aspectPath = null; | |||
@@ -644,7 +644,7 @@ public class BcelWorld extends World implements Repository { | |||
} | |||
public void storeClass(JavaClass clazz) { | |||
throw new RuntimeException("Not implemented"); | |||
// doesn't need to do anything | |||
} | |||
public void removeClass(JavaClass clazz) { |
@@ -19,6 +19,7 @@ import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.aspectj.apache.bcel.classfile.JavaClass; | |||
import org.aspectj.bridge.IMessageHandler; | |||
import org.aspectj.util.LangUtil; | |||
import org.aspectj.weaver.ICrossReferenceHandler; | |||
@@ -48,7 +49,6 @@ import org.aspectj.weaver.reflect.ReflectionWorld; | |||
*/ | |||
public class LTWWorld extends BcelWorld implements IReflectionWorld { | |||
private AnnotationFinder annotationFinder; | |||
private ClassLoader loader; // weavingContext? | |||
private IWeavingContext weavingContext; | |||
@@ -244,5 +244,10 @@ public class LTWWorld extends BcelWorld implements IReflectionWorld { | |||
((ReferenceType)ret).setDelegate(rtd); | |||
return ret; | |||
} | |||
public void storeClass(JavaClass clazz) { | |||
delegate.storeClass(clazz); | |||
} | |||
} |
@@ -14,10 +14,12 @@ | |||
package org.aspectj.weaver.bcel; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.lang.ref.Reference; | |||
import java.lang.reflect.Field; | |||
import java.net.URL; | |||
import java.net.URLClassLoader; | |||
import java.util.Enumeration; | |||
import java.util.Map; | |||
import java.util.zip.ZipEntry; | |||
import java.util.zip.ZipFile; | |||
@@ -27,43 +29,185 @@ import org.aspectj.apache.bcel.util.ClassLoaderRepository; | |||
/** NOT YET INCLUDED IN A FULL TEST RUN - WORK IN PROGRESS CHECKING CLASSLOADERREPOSITORY OPTIMIZATIONS */ | |||
public class ClassLoaderRepositoryTests extends TestCase { | |||
public void testRepositorySharing() throws Exception { | |||
ClassLoaderRepository.useSharedCache=false; | |||
File f = new File("../lib/aspectj/lib/aspectjtools.jar"); | |||
private File f; | |||
private ZipFile zf; | |||
private Enumeration entries; | |||
private Map map; | |||
public void setUp() throws Exception { | |||
f = new File("../lib/aspectj/lib/aspectjtools.jar"); | |||
assertTrue("Couldn't find aspectjtools to test. Tried: "+f.getAbsolutePath(),f.exists()); | |||
zf = new ZipFile(f); | |||
entries = zf.entries(); | |||
// ClassLoaderRepository.sharedCacheCompactFrequency = 16384; | |||
map = getSharedMap(); | |||
} | |||
public void tearDown() { | |||
new ClassLoaderRepository(null).reset(); | |||
} | |||
private ClassLoaderRepository setupRepository() throws Exception { | |||
ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |||
ClassLoader cl1 = new URLClassLoader(new URL[]{f.toURL()},cl); | |||
ClassLoader cl2 = new URLClassLoader(new URL[]{f.toURL()},cl); | |||
ClassLoaderRepository rep1 = new ClassLoaderRepository(cl1); | |||
ClassLoaderRepository rep2 = new ClassLoaderRepository(cl2); | |||
try { | |||
assertTrue("Couldnt find aspectjtools to test. Tried: "+f.getAbsolutePath(),f.exists()); | |||
ZipFile zf = new ZipFile(f); | |||
int i = 0; | |||
Enumeration entries = zf.entries(); | |||
while (entries.hasMoreElements()) { | |||
ZipEntry zfe = (ZipEntry)entries.nextElement(); | |||
String classfileName = zfe.getName(); | |||
if (classfileName.endsWith(".class")) { | |||
String clazzname = classfileName.substring(0,classfileName.length()-6).replace('/','.'); | |||
// twice by each | |||
rep1.loadClass(clazzname); | |||
rep1.loadClass(clazzname); | |||
rep2.loadClass(clazzname); | |||
rep2.loadClass(clazzname); | |||
i++; | |||
} | |||
ClassLoader res = new URLClassLoader(new URL[]{f.toURL()},cl); | |||
ClassLoaderRepository rep = new ClassLoaderRepository(res); | |||
return rep; | |||
} | |||
private void compareTwoRepositories() throws Exception { | |||
ClassLoaderRepository rep1 = setupRepository(); | |||
ClassLoaderRepository rep2 = setupRepository(); | |||
int i = 0; | |||
while (entries.hasMoreElements()) { | |||
ZipEntry zfe = (ZipEntry)entries.nextElement(); | |||
String classfileName = zfe.getName(); | |||
if (classfileName.endsWith(".class")) { | |||
String clazzname = classfileName.substring(0,classfileName.length()-6).replace('/','.'); | |||
// twice by each | |||
rep1.loadClass(clazzname); | |||
rep1.loadClass(clazzname); | |||
rep2.loadClass(clazzname); | |||
rep2.loadClass(clazzname); | |||
i++; | |||
} | |||
} | |||
System.err.println("Successfully compared "+i+" entries!!"); | |||
System.err.println(rep1.report()); | |||
System.err.println(rep2.report()); | |||
} | |||
private void loadOnce() throws Exception { | |||
ClassLoaderRepository rep = setupRepository(); | |||
while (entries.hasMoreElements()) { | |||
ZipEntry zfe = (ZipEntry) entries.nextElement(); | |||
String classfileName = zfe.getName(); | |||
if (classfileName.endsWith(".class")) { | |||
String clazzname = classfileName.substring(0, | |||
classfileName.length() - 6).replace('/', '.'); | |||
rep.loadClass(clazzname); | |||
} | |||
} | |||
} | |||
public void testMultiThreaded() throws Throwable { | |||
ClassLoaderRepository.useSharedCache=true; | |||
// ClassLoaderRepository.sharedCacheCompactFrequency = 200; | |||
//loadOnce(); | |||
TestThread threads[] = new TestThread[6]; | |||
for (int i=0; i<threads.length; i++) { | |||
threads[i] = new TestThread((i%3)*1000); | |||
threads[i].start(); | |||
} | |||
for (int i=0; i<threads.length; i++) { | |||
threads[i].join(); | |||
if (threads[i].getFailure() != null) { | |||
throw threads[i].getFailure(); | |||
} | |||
} | |||
} | |||
private class TestThread extends Thread { | |||
public Throwable failure = null; | |||
Enumeration entries; | |||
// ensure the threads are loading DIFFERENT shared classes at the same time... | |||
public TestThread(int skip) { | |||
entries = zf.entries(); | |||
for (int i=0; i<skip && entries.hasMoreElements(); i++) { | |||
entries.nextElement(); | |||
} | |||
System.err.println("Successfully compared "+i+" entries!!"); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
fail(e.getMessage()); | |||
} | |||
public void run() { | |||
try { | |||
ClassLoaderRepository rep = setupRepository(); | |||
int i = 0; | |||
while (entries.hasMoreElements()) { | |||
ZipEntry zfe = (ZipEntry)entries.nextElement(); | |||
String classfileName = zfe.getName(); | |||
if (classfileName.endsWith(".class")) { | |||
String clazzname = classfileName.substring(0,classfileName.length()-6).replace('/','.'); | |||
rep.loadClass(clazzname); | |||
rep.loadClass(clazzname); | |||
i++; | |||
} | |||
} | |||
System.err.println("Thread finished: "+rep.report()); | |||
} catch (Throwable t) { | |||
failure = t; | |||
} | |||
} | |||
public Throwable getFailure() { | |||
return failure; | |||
} | |||
System.err.println(rep1.reportAllStatistics()); | |||
System.err.println(rep2.reportAllStatistics()); | |||
} | |||
public void testNotSharedRepository() throws Exception { | |||
ClassLoaderRepository.useSharedCache=false; | |||
compareTwoRepositories(); | |||
} | |||
public void testSharedUrlRepository() throws Exception { | |||
ClassLoaderRepository.useSharedCache=true; | |||
compareTwoRepositories(); | |||
// ClassLoaderRepository.compactSharedCache(); | |||
} | |||
public void testPurgeUrlRepository() throws Exception { | |||
ClassLoaderRepository.useSharedCache = true; | |||
ClassLoaderRepository rep = setupRepository(); | |||
Reference ref = null; | |||
while (ref==null && entries.hasMoreElements()) { | |||
ZipEntry zfe = (ZipEntry)entries.nextElement(); | |||
String classfileName = zfe.getName(); | |||
if (classfileName.endsWith(".class")) { | |||
String clazzname = classfileName.substring(0,classfileName.length()-6).replace('/','.'); | |||
rep.loadClass(clazzname); | |||
assertEquals("expected one entry in shared URL cache "+map.size(), 1, map.size()); | |||
ref = (Reference)map.values().iterator().next(); | |||
ref.clear(); | |||
ref.enqueue(); | |||
map.size();//force purge | |||
} | |||
} | |||
assertEquals("expected empty shared URL cache "+map.size(), 0, map.size()); | |||
} | |||
public void testAutoPurgeUrlRepository() throws Exception { | |||
ClassLoaderRepository.useSharedCache = true; | |||
assertEquals("expected empty shared URL cache "+map.size(), 0, map.size()); | |||
ClassLoaderRepository rep = setupRepository(); | |||
Reference ref = null; | |||
int i = 0; | |||
while (i<3 && entries.hasMoreElements()) { | |||
ZipEntry zfe = (ZipEntry)entries.nextElement(); | |||
String classfileName = zfe.getName(); | |||
if (classfileName.endsWith(".class")) { | |||
String clazzname = classfileName.substring(0,classfileName.length()-6).replace('/','.'); | |||
rep.loadClass(clazzname); | |||
ref = (Reference)map.values().iterator().next(); | |||
ref.clear(); | |||
ref.enqueue(); | |||
i++; | |||
} | |||
} | |||
assertTrue("expected smaller shared URL cache "+map.size(), map.size()<3); | |||
} | |||
private Field getSharedMapField() throws Exception { | |||
Field field = ClassLoaderRepository.class.getDeclaredField("sharedCache"); | |||
field.setAccessible(true); | |||
return field; | |||
} | |||
private Map getSharedMap() throws Exception { | |||
return (Map)getSharedMapField() .get(null); | |||
} | |||
} | |||