1 /*
   2  * Copyright 2003-2008 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.jdi;
  26 
  27 import java.io.*;
  28 import java.net.*;
  29 
  30 /*
  31  * This class loader is used for two different reasons:
  32  *
  33  * 1) To support multiple simultaneous debuggees.
  34  *
  35  * SA's architecture does not allow us to use multiple simultaneous
  36  * debuggees. This is because of lots of static fields caching
  37  * vmStruct fields and singleton assumption in classes such as
  38  * 'sun.jvm.hotspot.runtime.VM'. Hence, we use instances of this
  39  * class loader to create a separate namespace for each debuggee VM.
  40  *
  41  * 2) To support cross VM version debugging.
  42  *
  43  * SA has very close dependency on VM data structures. Due to this, a
  44  * version of SA can only support debuggees running a same dot-dot release and
  45  * update releases only. For eg. this version of SA supports only 1.4.2 and
  46  * 1.4.2_xx releases only. But, users may want to debug debuggees running
  47  * a different version of VM. To support this, we use an instance of this
  48  * class loader to load classes from corresponding sa-jdi.jar.
  49  *
  50  * Note that JDI classes would still be loaded from the debugger's tools.jar
  51  * and not from debuggee's tools.jar. This means that if JDI interface evolved
  52  * b/w debuggee and debugger VM versions, user may still get problems. This is
  53  * the case when debugger runs on 1.5.0 and debuggee runs on 1.4.2. Because JDI
  54  * evolved b/w these versions (generics, enum, varargs etc.), 1.4.2 sa-jdi.jar
  55  * won't implement 1.5.0 JDI properly and user would get verifier errors. This
  56  * class loader solution is suited for different dot-dot release where JDI will
  57  * not evolve but VM data structures might change and SA implementation might
  58  * have to change. For example, a debuggee running 1.5.1 VM can be debugged
  59  * with debugger running on 1.5.0 VM. Here, JDI is same but VM data structures
  60  * could still change.
  61  */
  62 
  63 class SAJDIClassLoader extends URLClassLoader {
  64     private static final boolean DEBUG;
  65     static {
  66         DEBUG = System.getProperty("sun.jvm.hotspot.jdi.SAJDIClassLoader.DEBUG") != null;
  67     }
  68 
  69     private ClassLoader parent;
  70     private boolean classPathSet;
  71 
  72     SAJDIClassLoader(ClassLoader parent) {
  73         super(new URL[0], parent);
  74         this.parent = parent;
  75     }
  76 
  77     SAJDIClassLoader(ClassLoader parent, String classPath) {
  78         this(parent);
  79         this.classPathSet = true;
  80         try {
  81             addURL(new File(classPath).toURI().toURL());
  82         } catch(MalformedURLException mue) {
  83             throw new RuntimeException(mue);
  84         }
  85     }
  86 
  87     public synchronized Class loadClass(String name)
  88         throws ClassNotFoundException {
  89         // First, check if the class has already been loaded
  90         Class c = findLoadedClass(name);
  91         if (c == null) {
  92             /* If we are loading any class in 'sun.jvm.hotspot.'  or any of the
  93              * sub-packages (except for 'debugger' sub-pkg. please refer below),
  94              * we load it by 'this' loader. Or else, we forward the request to
  95              * 'parent' loader, system loader etc. (rest of the code follows
  96              * the patten in java.lang.ClassLoader.loadClass).
  97              *
  98              * 'sun.jvm.hotspot.debugger.' and sub-package classes are
  99              * also loaded by parent loader. This is done for two reasons:
 100              *
 101              * 1. to avoid code bloat by too many classes.
 102              * 2. to avoid loading same native library multiple times
 103              *    from multiple class loaders (which results in getting a
 104              *    UnsatisifiedLinkageError from System.loadLibrary).
 105              */
 106 
 107             if (name.startsWith("sun.jvm.hotspot.") &&
 108                 !name.startsWith("sun.jvm.hotspot.debugger.")) {
 109                 return findClass(name);
 110             }
 111             if (parent != null) {
 112                 c = parent.loadClass(name);
 113             } else {
 114                 c = findSystemClass(name);
 115             }
 116         }
 117         return c;
 118     }
 119 
 120     protected Class findClass(String name) throws ClassNotFoundException {
 121         if (DEBUG) {
 122             System.out.println("SA/JDI loader: about to load " + name);
 123         }
 124         if (classPathSet) {
 125             return super.findClass(name);
 126         } else {
 127             byte[] b = null;
 128             try {
 129                 InputStream in = getResourceAsStream(name.replace('.', '/') + ".class");
 130                 // Read until end of stream is reached
 131                 b = new byte[1024];
 132                 int total = 0;
 133                 int len = 0;
 134                 while ((len = in.read(b, total, b.length - total)) != -1) {
 135                     total += len;
 136                     if (total >= b.length) {
 137                         byte[] tmp = new byte[total * 2];
 138                         System.arraycopy(b, 0, tmp, 0, total);
 139                         b = tmp;
 140                     }
 141                 }
 142                 // Trim array to correct size, if necessary
 143                 if (total != b.length) {
 144                     byte[] tmp = new byte[total];
 145                     System.arraycopy(b, 0, tmp, 0, total);
 146                     b = tmp;
 147                 }
 148             } catch (Exception exp) {
 149                 throw (ClassNotFoundException) new ClassNotFoundException().initCause(exp);
 150             }
 151             return defineClass(name, b, 0, b.length);
 152         }
 153     }
 154 }