1 package com.xtremelabs.robolectric.bytecode; 2 3 import javassist.CannotCompileException; 4 import javassist.ClassPool; 5 import javassist.LoaderClassPath; 6 import javassist.NotFoundException; 7 8 import java.io.File; 9 import java.lang.System; 10 import java.util.List; 11 12 public class RobolectricClassLoader extends javassist.Loader { 13 private ClassCache classCache; 14 private AndroidTranslator androidTranslator; 15 RobolectricClassLoader(ClassHandler classHandler)16 public RobolectricClassLoader(ClassHandler classHandler) { 17 this(classHandler, null); 18 } 19 RobolectricClassLoader(ClassHandler classHandler, List<String> customClassNames)20 public RobolectricClassLoader(ClassHandler classHandler, List<String> customClassNames) { 21 super(RobolectricClassLoader.class.getClassLoader(), null); 22 23 delegateLoadingOf(AndroidTranslator.class.getName()); 24 delegateLoadingOf(ClassHandler.class.getName()); 25 26 final String classCachePath = System.getProperty("cached.robolectric.classes.path"); 27 final File classCacheDirectory; 28 if (null == classCachePath || "".equals(classCachePath.trim())) { 29 classCacheDirectory = new File("./tmp"); 30 } else { 31 classCacheDirectory = new File(classCachePath); 32 } 33 34 classCache = new ClassCache(new File(classCacheDirectory, "cached-robolectric-classes.jar").getAbsolutePath(), AndroidTranslator.CACHE_VERSION); 35 try { 36 ClassPool classPool = new ClassPool(); 37 classPool.appendClassPath(new LoaderClassPath(RobolectricClassLoader.class.getClassLoader())); 38 39 androidTranslator = new AndroidTranslator(classHandler, classCache, customClassNames); 40 addTranslator(classPool, androidTranslator); 41 } catch (NotFoundException e) { 42 throw new RuntimeException(e); 43 } catch (CannotCompileException e) { 44 throw new RuntimeException(e); 45 } 46 } 47 addCustomShadowClass(String classOrPackageToBeInstrumented)48 public void addCustomShadowClass(String classOrPackageToBeInstrumented) { 49 androidTranslator.addCustomShadowClass(classOrPackageToBeInstrumented); 50 } 51 52 @Override loadClass(String name)53 public Class loadClass(String name) throws ClassNotFoundException { 54 boolean shouldComeFromThisClassLoader = !(name.startsWith("org.junit") || name.startsWith("org.hamcrest") 55 || name.startsWith("org.specs2") || name.startsWith("scala.")); //org.specs2 and scala. allows for android projects with mixed scala\java tests to be run with Maven Surefire (see the RoboSpecs project on github) 56 57 Class<?> theClass; 58 if (shouldComeFromThisClassLoader) { 59 theClass = super.loadClass(name); 60 } else { 61 theClass = getParent().loadClass(name); 62 } 63 64 return theClass; 65 } 66 bootstrap(Class testClass)67 public Class<?> bootstrap(Class testClass) { 68 String testClassName = testClass.getName(); 69 70 try { 71 return loadClass(testClassName); 72 } catch (ClassNotFoundException e) { 73 throw new RuntimeException(e); 74 } 75 } 76 77 @Override findClass(String name)78 protected Class findClass(String name) throws ClassNotFoundException { 79 byte[] classBytes = classCache.getClassBytesFor(name); 80 if (classBytes != null) { 81 return defineClass(name, classBytes, 0, classBytes.length); 82 } 83 return super.findClass(name); 84 } 85 } 86