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