/Users/lyon/j4p/src/javassist/Loader.java

1    /* 
2     * Javassist, a Java-bytecode translator toolkit. 
3     * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. 
4     * 
5     * The contents of this file are subject to the Mozilla Public License Version 
6     * 1.1 (the "License"); you may not use this file except in compliance with 
7     * the License.  Alternatively, the contents of this file may be used under 
8     * the terms of the GNU Lesser General Public License Version 2.1 or later. 
9     * 
10    * Software distributed under the License is distributed on an "AS IS" basis, 
11    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
12    * for the specific language governing rights and limitations under the 
13    * License. 
14    */ 
15    
16   package javassist; 
17    
18   import java.io.*; 
19   import java.util.Hashtable; 
20   import java.util.Vector; 
21    
22   /** 
23    * The class loader for Javassist (JDK 1.2 or later only). 
24    * 
25    * <p>This is a sample class loader using <code>ClassPool</code>. 
26    * Unlike a regular class loader, this class loader obtains bytecode 
27    * from a <code>ClassPool</code>. 
28    * 
29    * <p>Note that Javassist can be used without this class loader; programmers 
30    * can define their own versions of class loader.  They can run 
31    * a program even without any user-defined class loader if that program 
32    * is statically translated with Javassist. 
33    * This class loader is just provided as a utility class. 
34    * 
35    * <p>Suppose that an instance of <code>MyTranslator</code> implementing 
36    * the interface <code>Translator</code> is responsible for modifying 
37    * class files. 
38    * The startup program of an application using <code>MyTranslator</code> 
39    * should be something like this: 
40    * 
41    * <ul><pre> 
42    * import javassist.*; 
43    * 
44    * public class Main { 
45    *   public static void main(String[] args) throws Throwable { 
46    *     MyTranslator myTrans = new MyTranslator(); 
47    *     ClassPool cp = ClassPool.getDefault(myTrans); 
48    *     Loader cl = new Loader(cp); 
49    *     cl.run("MyApp", args); 
50    *   } 
51    * } 
52    * </pre></ul> 
53    * 
54    * <p>Class <code>MyApp</code> is the main program of the application. 
55    * 
56    * <p>This program should be executed as follows: 
57    * 
58    * <ul><pre> 
59    * % java Main <i>arg1</i> <i>arg2</i>... 
60    * </pre></ul> 
61    * 
62    * <p>It modifies the class <code>MyApp</code> with a <code>MyTranslator</code> 
63    * object before loading it into the JVM. 
64    * Then it calls <code>main()</code> in <code>MyApp</code> with arguments 
65    * <i>arg1</i>, <i>arg2</i>, ... 
66    * 
67    * <p>This program execution is equivalent to: 
68    * 
69    * <ul><pre> 
70    * % java MyApp <i>arg1</i> <i>arg2</i>... 
71    * </pre></ul> 
72    * 
73    * <p>except that classes are translated by <code>MyTranslator</code> 
74    * at load time. 
75    * 
76    * <p>If only a particular class is modified when it is loaded, 
77    * a program like the following can be used: 
78    * 
79    * <ul><pre>ClassPool cp = ClassPool.getDefault(); 
80    * Loader cl = new Loader(cp); 
81    * 
82    * CtClass ct = cp.get("test.Rectangle"); 
83    * ct.setSuperclass(cp.get("test.Point")); 
84    * 
85    * Class c = cl.loadClass("test.Rectangle"); 
86    * Object rect = c.newInstance();</pre></ul> 
87    * 
88    * <p>This program modifies the super class of the <code>Rectangle</code> 
89    * class and loads it into the JVM with a class loader <code>cl</code>. 
90    * 
91    * <p><b>Note 1:</b> 
92    * 
93    * <p>This class loader does not allow the users to intercept the loading 
94    * of <code>java.*</code> and <code>javax.*</code> classes unless 
95    * <code>Loader.doDelegation</code> is <code>false</code>.  Also see 
96    * Note 2. 
97    * 
98    * <p><b>Note 2:</b> 
99    * 
100   * <p>If classes are loaded with different class loaders, they belong to 
101   * separate name spaces.  If class <code>C</code> is loaded by a class 
102   * loader <code>CL</code>, all classes that the class <code>C</code> 
103   * refers to are also loaded by <code>CL</code>.  However, if <code>CL</code> 
104   * delegates the loading of the class <code>C</code> to <code>CL'</code>, 
105   * then those classes that the class <code>C</code> refers to 
106   * are loaded by a parent class loader <code>CL'</code> 
107   * instead of <code>CL</code>. 
108   * 
109   * <p>If an object of class <code>C</code> is assigned 
110   * to a variable of class <code>C</code> belonging to a different name 
111   * space, then a <code>ClassCastException</code> is thrown. 
112   * 
113   * <p>Because of the fact above, this loader delegates only the loading of 
114   * <code>javassist.Loader</code> 
115   * and classes included in package <code>java.*</code> and 
116   * <code>javax.*</code> to the parent class 
117   * loader.  Other classes are directly loaded by this loader. 
118   * 
119   * <p>For example, suppose that <code>java.lang.String</code> would be loaded 
120   * by this loader while <code>java.io.File</code> is loaded by the parent 
121   * class loader.  If the constructor of <code>java.io.File</code> is called 
122   * with an instance of <code>java.lang.String</code>, then it may throw 
123   * an exception since it accepts an instance of only the 
124   * <code>java.lang.String</code> loaded by the parent class loader. 
125   * 
126   * @see javassist.ClassPool 
127   * @see javassist.Translator 
128   */ 
129  public class Loader extends ClassLoader { 
130      private Hashtable notDefinedHere;   // must be atomic. 
131      private Vector notDefinedPackages;  // must be atomic. 
132      private ClassPool source; 
133   
134      /** 
135       * Specifies the algorithm of class loading. 
136       * 
137       * <p>This class loader uses the parent class loader for 
138       * <code>java.*</code> and <code>javax.*</code> classes. 
139       * If this variable <code>doDelegation</code> 
140       * is <code>false</code>, this class loader does not delegate those 
141       * classes to the parent class loader. 
142       * 
143       * <p>The default value is <code>true</code>. 
144       */ 
145      public boolean doDelegation = true; 
146   
147      /** 
148       * Creates a new class loader. 
149       */ 
150      public Loader() { 
151          this(null); 
152      } 
153   
154      /** 
155       * Creates a new class loader. 
156       * 
157       * @param cp        the source of class files. 
158       */ 
159      public Loader(ClassPool cp) { 
160          init(cp); 
161      } 
162   
163      /** 
164       * Creates a new class loader 
165       * using the specified parent class loader for delegation. 
166       * 
167       * @param parent    the parent class loader. 
168       * @param cp        the source of class files. 
169       */ 
170      public Loader(ClassLoader parent, ClassPool cp) { 
171          super(parent); 
172          init(cp); 
173      } 
174   
175      private void init(ClassPool cp) { 
176          notDefinedHere = new Hashtable(); 
177          notDefinedPackages = new Vector(); 
178          source = cp; 
179          delegateLoadingOf("javassist.Loader"); 
180      } 
181   
182      /** 
183       * Records a class so that the loading of that class is delegated 
184       * to the parent class loader. 
185       * 
186       * <p>If the given class name ends with <code>.</code> (dot), then 
187       * that name is interpreted as a package name.  All the classes 
188       * in that package and the sub packages are delegated. 
189       */ 
190      public void delegateLoadingOf(String classname) { 
191          if (classname.endsWith(".")) 
192              notDefinedPackages.addElement(classname); 
193          else 
194              notDefinedHere.put(classname, this); 
195      } 
196   
197      /** 
198       * Sets the soruce <code>ClassPool</code>. 
199       */ 
200      public void setClassPool(ClassPool cp) { 
201          source = cp; 
202      } 
203   
204      /** 
205       * Loads a class with an instance of <code>Loader</code> 
206       * and calls <code>main()</code> of that class. 
207       * 
208       * <p>This method calls <code>run()</code>. 
209       * 
210       * @param args[0]           class name to be loaded. 
211       * @param args[1-n]         parameters passed to <code>main()</code>. 
212       * 
213       * @see javassist.Loader#run(String[]) 
214       */ 
215      public static void main(String[] args) throws Throwable { 
216          Loader cl = new Loader(); 
217          cl.run(args); 
218      } 
219   
220      /** 
221       * Loads a class and calls <code>main()</code> in that class. 
222       * 
223       * @param args[0]           the name of the loaded class. 
224       * @param args[1-n]         parameters passed to <code>main()</code>. 
225       */ 
226      public void run(String[] args) throws Throwable { 
227          int n = args.length - 1; 
228          if (n >= 0) { 
229              String[] args2 = new String[n]; 
230              for (int i = 0; i < n; ++i) 
231                  args2[i] = args[i + 1]; 
232   
233              run(args[0], args2); 
234          } 
235      } 
236   
237      /** 
238       * Loads a class and calls <code>main()</code> in that class. 
239       * 
240       * @param classname         the loaded class. 
241       * @param args              parameters passed to <code>main()</code>. 
242       */ 
243      public void run(String classname, String[] args) throws Throwable { 
244          Class c = loadClass(classname); 
245          try { 
246              c.getDeclaredMethod("main", new Class[]{String[].class}) 
247                      .invoke(null, new Object[]{args}); 
248          } catch (java.lang.reflect.InvocationTargetException e) { 
249              throw e.getTargetException(); 
250          } 
251      } 
252   
253      /** 
254       * Requests the class loader to load a class. 
255       */ 
256      protected Class loadClass(String name, boolean resolve) 
257              throws ClassFormatError, ClassNotFoundException { 
258          Class c = findLoadedClass(name); 
259          if (c == null) 
260              c = loadClassByDelegation(name); 
261   
262          if (c == null) 
263              c = findClass(name); 
264   
265          if (c == null) 
266              c = delegateToParent(name); 
267   
268          if (resolve) 
269              resolveClass(c); 
270   
271          return c; 
272      } 
273   
274      /** 
275       * Finds the specified class using <code>ClassPath</code>. 
276       * If the source throws an exception, this returns null. 
277       * 
278       * <p>This method can be overridden by a subclass of 
279       * <code>Loader</code>. 
280       */ 
281      protected Class findClass(String name) { 
282          byte[] classfile; 
283          try { 
284              if (source != null) 
285                  classfile = source.write(name); 
286              else { 
287                  String jarname = "/" + name.replace('.', '/') + ".class"; 
288                  InputStream in = this.getClass().getResourceAsStream(jarname); 
289   
290                  classfile = ClassPoolTail.readStream(in); 
291              } 
292          } catch (Exception e) { 
293              return null; 
294          } 
295   
296          int i = name.lastIndexOf('.'); 
297          if (i != -1) { 
298              String pname = name.substring(0, i); 
299              if (getPackage(pname) == null) 
300                  try { 
301                      definePackage(pname, null, null, null, 
302                              null, null, null, null); 
303                  } catch (IllegalArgumentException e) { 
304  // ignore.  maybe the package object for the same 
305  // name has been created just right away. 
306                  } 
307          } 
308   
309          return defineClass(name, classfile, 0, classfile.length); 
310      } 
311   
312      private Class loadClassByDelegation(String name) 
313              throws ClassNotFoundException { 
314          /* The swing components must be loaded by a system 
315           * class loader. 
316           * javax.swing.UIManager loads a (concrete) subclass 
317           * of LookAndFeel by a system class loader and cast 
318           * an instance of the class to LookAndFeel for 
319           * (maybe) a security reason.  To avoid failure of 
320           * type conversion, LookAndFeel must not be loaded 
321           * by this class loader. 
322           */ 
323   
324          Class c = null; 
325          if (doDelegation) 
326              if (name.startsWith("java.") || name.startsWith("javax.") 
327                      || name.startsWith("sun.") || name.startsWith("com.sun.") 
328                      || notDelegated(name)) 
329                  c = delegateToParent(name); 
330   
331          return c; 
332      } 
333   
334      private boolean notDelegated(String name) { 
335          if (notDefinedHere.get(name) != null) 
336              return true; 
337   
338          int n = notDefinedPackages.size(); 
339          for (int i = 0; i < n; ++i) 
340              if (name.startsWith((String) notDefinedPackages.elementAt(i))) 
341                  return true; 
342   
343          return false; 
344      } 
345   
346      private Class delegateToParent(String classname) 
347              throws ClassNotFoundException { 
348          ClassLoader cl = getParent(); 
349          if (cl != null) 
350              return cl.loadClass(classname); 
351          else 
352              return findSystemClass(classname); 
353      } 
354   
355      protected Package getPackage(String name) { 
356          return super.getPackage(name); 
357      } 
358      /* 
359      // Package p = super.getPackage(name); 
360      Package p = null; 
361      if (p == null) 
362          return definePackage(name, null, null, null, 
363                               null, null, null, null); 
364      else 
365          return p; 
366  } 
367  */ 
368  } 
369