1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 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 import java.security.ProtectionDomain; 22 23 /** 24 * The class loader for Javassist. 25 * 26 * <p>This is a sample class loader using <code>ClassPool</code>. 27 * Unlike a regular class loader, this class loader obtains bytecode 28 * from a <code>ClassPool</code>. 29 * 30 * <p>Note that Javassist can be used without this class loader; programmers 31 * can define their own versions of class loader. They can run 32 * a program even without any user-defined class loader if that program 33 * is statically translated with Javassist. 34 * This class loader is just provided as a utility class. 35 * 36 * <p>Suppose that an instance of <code>MyTranslator</code> implementing 37 * the interface <code>Translator</code> is responsible for modifying 38 * class files. 39 * The startup program of an application using <code>MyTranslator</code> 40 * should be something like this: 41 * 42 * <ul><pre> 43 * import javassist.*; 44 * 45 * public class Main { 46 * public static void main(String[] args) throws Throwable { 47 * MyTranslator myTrans = new MyTranslator(); 48 * ClassPool cp = ClassPool.getDefault(); 49 * Loader cl = new Loader(cp); 50 * cl.addTranslator(cp, myTrans); 51 * cl.run("MyApp", args); 52 * } 53 * } 54 * </pre></ul> 55 * 56 * <p>Class <code>MyApp</code> is the main program of the application. 57 * 58 * <p>This program should be executed as follows: 59 * 60 * <ul><pre> 61 * % java Main <i>arg1</i> <i>arg2</i>... 62 * </pre></ul> 63 * 64 * <p>It modifies the class <code>MyApp</code> with a <code>MyTranslator</code> 65 * object before the JVM loads it. 66 * Then it calls <code>main()</code> in <code>MyApp</code> with arguments 67 * <i>arg1</i>, <i>arg2</i>, ... 68 * 69 * <p>This program execution is equivalent to: 70 * 71 * <ul><pre> 72 * % java MyApp <i>arg1</i> <i>arg2</i>... 73 * </pre></ul> 74 * 75 * <p>except that classes are translated by <code>MyTranslator</code> 76 * at load time. 77 * 78 * <p>If only a particular class must be modified when it is loaded, 79 * the startup program can be simpler; <code>MyTranslator</code> is 80 * unnecessary. For example, if only a class <code>test.Rectangle</code> 81 * is modified, the <code>main()</code> method above will be the following: 82 * 83 * <ul><pre> 84 * ClassPool cp = ClassPool.getDefault(); 85 * Loader cl = new Loader(cp); 86 * CtClass ct = cp.get("test.Rectangle"); 87 * ct.setSuperclass(cp.get("test.Point")); 88 * cl.run("MyApp", args);</pre></ul> 89 * 90 * <p>This program changes the super class of the <code>test.Rectangle</code> 91 * class. 92 * 93 * <p><b>Note 1:</b> 94 * 95 * <p>This class loader does not allow the users to intercept the loading 96 * of <code>java.*</code> and <code>javax.*</code> classes (and 97 * <code>sun.*</code>, <code>org.xml.*</code>, ...) unless 98 * <code>Loader.doDelegation</code> is <code>false</code>. This is because 99 * the JVM prohibits a user class loader from loading a system class. 100 * Also see Note 2. 101 * If this behavior is not appropriate, a subclass of <code>Loader</code> 102 * must be defined and <code>loadClassByDelegation()</code> must be overridden. 103 * 104 * <p><b>Note 2:</b> 105 * 106 * <p>If classes are loaded with different class loaders, they belong to 107 * separate name spaces. If class <code>C</code> is loaded by a class 108 * loader <code>CL</code>, all classes that the class <code>C</code> 109 * refers to are also loaded by <code>CL</code>. However, if <code>CL</code> 110 * delegates the loading of the class <code>C</code> to <code>CL'</code>, 111 * then those classes that the class <code>C</code> refers to 112 * are loaded by a parent class loader <code>CL'</code> 113 * instead of <code>CL</code>. 114 * 115 * <p>If an object of class <code>C</code> is assigned 116 * to a variable of class <code>C</code> belonging to a different name 117 * space, then a <code>ClassCastException</code> is thrown. 118 * 119 * <p>Because of the fact above, this loader delegates only the loading of 120 * <code>javassist.Loader</code> 121 * and classes included in package <code>java.*</code> and 122 * <code>javax.*</code> to the parent class 123 * loader. Other classes are directly loaded by this loader. 124 * 125 * <p>For example, suppose that <code>java.lang.String</code> would be loaded 126 * by this loader while <code>java.io.File</code> is loaded by the parent 127 * class loader. If the constructor of <code>java.io.File</code> is called 128 * with an instance of <code>java.lang.String</code>, then it may throw 129 * an exception since it accepts an instance of only the 130 * <code>java.lang.String</code> loaded by the parent class loader. 131 * 132 * @see javassist.ClassPool 133 * @see javassist.Translator 134 */ 135 public class Loader extends ClassLoader { 136 private Hashtable notDefinedHere; // must be atomic. 137 private Vector notDefinedPackages; // must be atomic. 138 private ClassPool source; 139 private Translator translator; 140 private ProtectionDomain domain; 141 142 /** 143 * Specifies the algorithm of class loading. 144 * 145 * <p>This class loader uses the parent class loader for 146 * <code>java.*</code> and <code>javax.*</code> classes. 147 * If this variable <code>doDelegation</code> 148 * is <code>false</code>, this class loader does not delegate those 149 * classes to the parent class loader. 150 * 151 * <p>The default value is <code>true</code>. 152 */ 153 public boolean doDelegation = true; 154 155 /** 156 * Creates a new class loader. 157 */ Loader()158 public Loader() { 159 this(null); 160 } 161 162 /** 163 * Creates a new class loader. 164 * 165 * @param cp the source of class files. 166 */ Loader(ClassPool cp)167 public Loader(ClassPool cp) { 168 init(cp); 169 } 170 171 /** 172 * Creates a new class loader 173 * using the specified parent class loader for delegation. 174 * 175 * @param parent the parent class loader. 176 * @param cp the source of class files. 177 */ Loader(ClassLoader parent, ClassPool cp)178 public Loader(ClassLoader parent, ClassPool cp) { 179 super(parent); 180 init(cp); 181 } 182 init(ClassPool cp)183 private void init(ClassPool cp) { 184 notDefinedHere = new Hashtable(); 185 notDefinedPackages = new Vector(); 186 source = cp; 187 translator = null; 188 domain = null; 189 delegateLoadingOf("javassist.Loader"); 190 } 191 192 /** 193 * Records a class so that the loading of that class is delegated 194 * to the parent class loader. 195 * 196 * <p>If the given class name ends with <code>.</code> (dot), then 197 * that name is interpreted as a package name. All the classes 198 * in that package and the sub packages are delegated. 199 */ delegateLoadingOf(String classname)200 public void delegateLoadingOf(String classname) { 201 if (classname.endsWith(".")) 202 notDefinedPackages.addElement(classname); 203 else 204 notDefinedHere.put(classname, this); 205 } 206 207 /** 208 * Sets the protection domain for the classes handled by this class 209 * loader. Without registering an appropriate protection domain, 210 * the program loaded by this loader will not work with a security 211 * manager or a signed jar file. 212 */ setDomain(ProtectionDomain d)213 public void setDomain(ProtectionDomain d) { 214 domain = d; 215 } 216 217 /** 218 * Sets the soruce <code>ClassPool</code>. 219 */ setClassPool(ClassPool cp)220 public void setClassPool(ClassPool cp) { 221 source = cp; 222 } 223 224 /** 225 * Adds a translator, which is called whenever a class is loaded. 226 * 227 * @param cp the <code>ClassPool</code> object for obtaining 228 * a class file. 229 * @param t a translator. 230 * @throws NotFoundException if <code>t.start()</code> throws an exception. 231 * @throws CannotCompileException if <code>t.start()</code> throws an exception. 232 */ addTranslator(ClassPool cp, Translator t)233 public void addTranslator(ClassPool cp, Translator t) 234 throws NotFoundException, CannotCompileException { 235 source = cp; 236 translator = t; 237 t.start(cp); 238 } 239 240 /** 241 * Loads a class with an instance of <code>Loader</code> 242 * and calls <code>main()</code> of that class. 243 * 244 * <p>This method calls <code>run()</code>. 245 * 246 * @param args command line parameters. 247 * <ul> 248 * <code>args[0]</code> is the class name to be loaded. 249 * <br><code>args[1..n]</code> are parameters passed 250 * to the target <code>main()</code>. 251 * </ul> 252 * 253 * @see javassist.Loader#run(String[]) 254 */ main(String[] args)255 public static void main(String[] args) throws Throwable { 256 Loader cl = new Loader(); 257 cl.run(args); 258 } 259 260 /** 261 * Loads a class and calls <code>main()</code> in that class. 262 * 263 * @param args command line parameters. 264 * <ul> 265 * <code>args[0]</code> is the class name to be loaded. 266 * <br><code>args[1..n]</code> are parameters passed 267 * to the target <code>main()</code>. 268 * </ul> 269 */ run(String[] args)270 public void run(String[] args) throws Throwable { 271 int n = args.length - 1; 272 if (n >= 0) { 273 String[] args2 = new String[n]; 274 for (int i = 0; i < n; ++i) 275 args2[i] = args[i + 1]; 276 277 run(args[0], args2); 278 } 279 } 280 281 /** 282 * Loads a class and calls <code>main()</code> in that class. 283 * 284 * @param classname the loaded class. 285 * @param args parameters passed to <code>main()</code>. 286 */ run(String classname, String[] args)287 public void run(String classname, String[] args) throws Throwable { 288 Class c = loadClass(classname); 289 try { 290 c.getDeclaredMethod("main", new Class[] { String[].class }).invoke( 291 null, 292 new Object[] { args }); 293 } 294 catch (java.lang.reflect.InvocationTargetException e) { 295 throw e.getTargetException(); 296 } 297 } 298 299 /** 300 * Requests the class loader to load a class. 301 */ loadClass(String name, boolean resolve)302 protected Class loadClass(String name, boolean resolve) 303 throws ClassFormatError, ClassNotFoundException { 304 name = name.intern(); 305 synchronized (name) { 306 Class c = findLoadedClass(name); 307 if (c == null) 308 c = loadClassByDelegation(name); 309 310 if (c == null) 311 c = findClass(name); 312 313 if (c == null) 314 c = delegateToParent(name); 315 316 if (resolve) 317 resolveClass(c); 318 319 return c; 320 } 321 } 322 323 /** 324 * Finds the specified class using <code>ClassPath</code>. 325 * If the source throws an exception, this returns null. 326 * 327 * <p>This method can be overridden by a subclass of 328 * <code>Loader</code>. Note that the overridden method must not throw 329 * an exception when it just fails to find a class file. 330 * 331 * @return null if the specified class could not be found. 332 * @throws ClassNotFoundException if an exception is thrown while 333 * obtaining a class file. 334 */ findClass(String name)335 protected Class findClass(String name) throws ClassNotFoundException { 336 byte[] classfile; 337 try { 338 if (source != null) { 339 if (translator != null) 340 translator.onLoad(source, name); 341 342 try { 343 classfile = source.get(name).toBytecode(); 344 } 345 catch (NotFoundException e) { 346 return null; 347 } 348 } 349 else { 350 String jarname = "/" + name.replace('.', '/') + ".class"; 351 InputStream in = this.getClass().getResourceAsStream(jarname); 352 if (in == null) 353 return null; 354 355 classfile = ClassPoolTail.readStream(in); 356 } 357 } 358 catch (Exception e) { 359 throw new ClassNotFoundException( 360 "caught an exception while obtaining a class file for " 361 + name, e); 362 } 363 364 int i = name.lastIndexOf('.'); 365 if (i != -1) { 366 String pname = name.substring(0, i); 367 if (getPackage(pname) == null) 368 try { 369 definePackage( 370 pname, null, null, null, null, null, null, null); 371 } 372 catch (IllegalArgumentException e) { 373 // ignore. maybe the package object for the same 374 // name has been created just right away. 375 } 376 } 377 378 if (domain == null) 379 return defineClass(name, classfile, 0, classfile.length); 380 else 381 return defineClass(name, classfile, 0, classfile.length, domain); 382 } 383 loadClassByDelegation(String name)384 protected Class loadClassByDelegation(String name) 385 throws ClassNotFoundException 386 { 387 /* The swing components must be loaded by a system 388 * class loader. 389 * javax.swing.UIManager loads a (concrete) subclass 390 * of LookAndFeel by a system class loader and cast 391 * an instance of the class to LookAndFeel for 392 * (maybe) a security reason. To avoid failure of 393 * type conversion, LookAndFeel must not be loaded 394 * by this class loader. 395 */ 396 397 Class c = null; 398 if (doDelegation) 399 if (name.startsWith("java.") 400 || name.startsWith("javax.") 401 || name.startsWith("sun.") 402 || name.startsWith("com.sun.") 403 || name.startsWith("org.w3c.") 404 || name.startsWith("org.xml.") 405 || notDelegated(name)) 406 c = delegateToParent(name); 407 408 return c; 409 } 410 notDelegated(String name)411 private boolean notDelegated(String name) { 412 if (notDefinedHere.get(name) != null) 413 return true; 414 415 int n = notDefinedPackages.size(); 416 for (int i = 0; i < n; ++i) 417 if (name.startsWith((String)notDefinedPackages.elementAt(i))) 418 return true; 419 420 return false; 421 } 422 delegateToParent(String classname)423 protected Class delegateToParent(String classname) 424 throws ClassNotFoundException 425 { 426 ClassLoader cl = getParent(); 427 if (cl != null) 428 return cl.loadClass(classname); 429 else 430 return findSystemClass(classname); 431 } 432 getPackage(String name)433 protected Package getPackage(String name) { 434 return super.getPackage(name); 435 } 436 /* 437 // Package p = super.getPackage(name); 438 Package p = null; 439 if (p == null) 440 return definePackage(name, null, null, null, 441 null, null, null, null); 442 else 443 return p; 444 } 445 */ 446 } 447