• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2008 The Android Open Source Project
2 
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.IOException;
6 import java.io.RandomAccessFile;
7 import java.lang.reflect.Constructor;
8 import java.lang.reflect.Method;
9 import java.lang.reflect.InvocationTargetException;
10 
11 /**
12  * A class loader with atypical behavior: we try to load a private
13  * class implementation before asking the system or boot loader.  This
14  * is used to create multiple classes with identical names in a single VM.
15  *
16  * If DexFile is available, we use that; if not, we assume we're not in
17  * Dalvik and instantiate the class with defineClass().
18  *
19  * The location of the DEX files and class data is dependent upon the
20  * test framework.
21  */
22 public class FancyLoader extends ClassLoader {
23     /* this is where the "alternate" .class files live */
24     static final String CLASS_PATH = "classes-ex/";
25 
26     /* this is the "alternate" DEX/Jar file */
27     static final String DEX_FILE = "test-ex.jar";
28 
29     /* on Dalvik, this is a DexFile; otherwise, it's null */
30     private Class mDexClass;
31 
32     /**
33      * Construct FancyLoader, grabbing a reference to the DexFile class
34      * if we're running under Dalvik.
35      */
FancyLoader(ClassLoader parent)36     public FancyLoader(ClassLoader parent) {
37         super(parent);
38 
39         try {
40             mDexClass = parent.loadClass("dalvik/system/DexFile");
41         } catch (ClassNotFoundException cnfe) {
42             // ignore -- not running Dalvik
43         }
44     }
45 
46     /**
47      * Finds the class with the specified binary name.
48      *
49      * We search for a file in CLASS_PATH or pull an entry from DEX_FILE.
50      * If we don't find a match, we throw an exception.
51      */
findClass(String name)52     protected Class<?> findClass(String name) throws ClassNotFoundException
53     {
54         if (mDexClass != null) {
55             return findClassDalvik(name);
56         } else {
57             return findClassNonDalvik(name);
58         }
59     }
60 
61     /**
62      * Finds the class with the specified binary name, from a DEX file.
63      */
findClassDalvik(String name)64     private Class<?> findClassDalvik(String name)
65         throws ClassNotFoundException {
66 
67         Constructor ctor;
68         Object dexFile;
69 
70         /*
71          * Construct a DexFile object through reflection.
72          */
73         try {
74             ctor = mDexClass.getConstructor(new Class[] { String.class });
75         } catch (NoSuchMethodException nsme) {
76             throw new ClassNotFoundException("getConstructor failed", nsme);
77         }
78 
79         try {
80             dexFile = ctor.newInstance(DEX_FILE);
81         } catch (InstantiationException ie) {
82             throw new ClassNotFoundException("newInstance failed", ie);
83         } catch (IllegalAccessException iae) {
84             throw new ClassNotFoundException("newInstance failed", iae);
85         } catch (InvocationTargetException ite) {
86             throw new ClassNotFoundException("newInstance failed", ite);
87         }
88 
89         /*
90          * Call DexFile.loadClass(String, ClassLoader).
91          */
92         Method meth;
93 
94         try {
95             meth = mDexClass.getMethod("loadClass",
96                     new Class[] { String.class, ClassLoader.class });
97         } catch (NoSuchMethodException nsme) {
98             throw new ClassNotFoundException("getMethod failed", nsme);
99         }
100 
101         try {
102             meth.invoke(dexFile, name, this);
103         } catch (IllegalAccessException iae) {
104             throw new ClassNotFoundException("loadClass failed", iae);
105         } catch (InvocationTargetException ite) {
106             throw new ClassNotFoundException("loadClass failed",
107                 ite.getCause());
108         }
109 
110         return null;
111     }
112 
113     /**
114      * Finds the class with the specified binary name, from .class files.
115      */
findClassNonDalvik(String name)116     private Class<?> findClassNonDalvik(String name)
117         throws ClassNotFoundException {
118 
119         String pathName = CLASS_PATH + name + ".class";
120         //System.out.println("--- Fancy: looking for " + pathName);
121 
122         File path = new File(pathName);
123         RandomAccessFile raf;
124 
125         try {
126             raf = new RandomAccessFile(path, "r");
127         } catch (FileNotFoundException fnfe) {
128             throw new ClassNotFoundException("Not found: " + pathName);
129         }
130 
131         /* read the entire file in */
132         byte[] fileData;
133         try {
134             fileData = new byte[(int) raf.length()];
135             raf.readFully(fileData);
136         } catch (IOException ioe) {
137             throw new ClassNotFoundException("Read error: " + pathName);
138         } finally {
139             try {
140                 raf.close();
141             } catch (IOException ioe) {
142                 // drop
143             }
144         }
145 
146         /* create the class */
147         //System.out.println("--- Fancy: defining " + name);
148         try {
149             return defineClass(name, fileData, 0, fileData.length);
150         } catch (Throwable th) {
151             throw new ClassNotFoundException("defineClass failed", th);
152         }
153     }
154 
155     /**
156      * Load a class.
157      *
158      * Normally a class loader wouldn't override this, but we want our
159      * version of the class to take precedence over an already-loaded
160      * version.
161      *
162      * We still want the system classes (e.g. java.lang.Object) from the
163      * bootstrap class loader.
164      */
loadClass(String name, boolean resolve)165     protected Class<?> loadClass(String name, boolean resolve)
166         throws ClassNotFoundException
167     {
168         Class res;
169 
170         /*
171          * 1. Invoke findLoadedClass(String) to check if the class has
172          * already been loaded.
173          *
174          * This doesn't change.
175          */
176         res = findLoadedClass(name);
177         if (res != null) {
178             System.out.println("FancyLoader.loadClass: "
179                 + name + " already loaded");
180             if (resolve)
181                 resolveClass(res);
182             return res;
183         }
184 
185         /*
186          * 3. Invoke the findClass(String) method to find the class.
187          */
188         try {
189             res = findClass(name);
190             if (resolve)
191                 resolveClass(res);
192         }
193         catch (ClassNotFoundException e) {
194             // we couldn't find it, so eat the exception and keep going
195         }
196 
197         /*
198          * 2. Invoke the loadClass method on the parent class loader.  If
199          * the parent loader is null the class loader built-in to the
200          * virtual machine is used, instead.
201          *
202          * (Since we're not in java.lang, we can't actually invoke the
203          * parent's loadClass() method, but we passed our parent to the
204          * super-class which can take care of it for us.)
205          */
206         res = super.loadClass(name, resolve);   // returns class or throws
207         return res;
208     }
209 }
210 
211