Clover coverage report - clover
Coverage timestamp: Sat Oct 8 2005 22:54:17 EDT
file stats: LOC: 305   Methods: 20
NCLOC: 173   Classes: 4
 
 Source file Conditionals Statements Methods TOTAL
AppClassLoader.java 87.5% 91% 90% 90.3%
coverage coverage
 1   
 package abbot.script;
 2   
 
 3   
 import java.awt.*;
 4   
 import java.util.EmptyStackException;
 5   
 import java.lang.reflect.Field;
 6   
 
 7   
 import javax.swing.SwingUtilities;
 8   
 
 9   
 import abbot.Log;
 10   
 import abbot.Platform;
 11   
 import abbot.i18n.Strings;
 12   
 import abbot.util.*;
 13   
 
 14   
 /**
 15   
  * A custom class loader which installs itself as if it were the application
 16   
  * class loader.  A classpath of null is equivalent to the system property
 17   
  * java.class.path.<p> 
 18   
  * The class loader may optionally load a class <i>before</i> the parent class
 19   
  * loader gets a chance to look for the class (instead of the default
 20   
  * behavior, which always delegates to the parent class loader first).  This
 21   
  * behavior enables the class to be reloaded simply by using a new instance of
 22   
  * this class loader with each launch of the app.<p>  
 23   
  * This class mimics the behavior of sun.misc.Launcher$AppClassLoader as much
 24   
  * as possible.<p>
 25   
  * Bootstrap classes are always delegated to the bootstrap loader, with the
 26   
  * exception of the sun.applet package, which should never be delegated, since
 27   
  * it does not work properly unless it is reloaded.<p>  
 28   
  * The parent of this class loader will be the normal, default AppClassLoader
 29   
  * (specifically, the class loader which loaded this class will be used).
 30   
  */
 31   
 public class AppClassLoader extends NonDelegatingClassLoader {
 32   
 
 33   
     /** A new event queue installed for the lifetime of this class loader. */
 34   
     private AppEventQueue eventQueue;
 35   
     /** This class loader is used to load bootstrap classes that must be
 36   
      * preloaded.
 37   
      */
 38   
     private BootstrapClassLoader bootstrapLoader;
 39   
     /** For use in checking if a class is in the framework class path. */
 40   
     private NonDelegatingClassLoader extensionsLoader;
 41   
     /** Whether the framework itself is being tested. */
 42   
     private boolean frameworkIsUnderTest = false;
 43   
 
 44   
     /** Old class loader context for the thread where this loader was
 45   
      * installed.
 46   
      */
 47   
     private ClassLoader oldClassLoader = null;
 48   
     private Thread installedThread = null;
 49   
     private String oldClassPath = null;
 50   
     private class InstallationLock {}
 51   
     private InstallationLock lock = new InstallationLock();
 52   
      
 53   
     /** Constructs an AppClassLoader using the current classpath (as found in
 54   
         java.class.path).
 55   
     */
 56  3
     public AppClassLoader() {
 57  3
         this(null);
 58   
     }
 59   
 
 60   
     /**
 61   
      * Constructs a AppClassLoader with a custom classpath, indicating
 62   
      * whether the class loader should delegate to its parent class loader
 63   
      * prior to searching for a given resource.<p>
 64   
      * The class path argument may use either a colon or semicolon to separate
 65   
      * its elements.<p>
 66   
      */
 67  82
     public AppClassLoader(String classPath) {
 68  82
         super(classPath, AppClassLoader.class.getClassLoader());
 69  82
         bootstrapLoader = new BootstrapClassLoader();
 70   
         // Use this one to look up extensions; we want to reload them, but may
 71   
         // need to look in the framework class path for them.  
 72   
         // Make sure it *only* loads extensions, though, and defers all other
 73   
         // lookups to its parent.
 74  82
         extensionsLoader =
 75   
             new NonDelegatingClassLoader(System.getProperty("java.class.path"),
 76   
                                          AppClassLoader.class.
 77   
                                          getClassLoader()) {
 78  3
                 protected boolean shouldDelegate(String name) {
 79  3
                     return !isExtension(name);
 80   
                 }
 81   
             };
 82   
         // Don't want to open a whole new can of class loading worms!
 83   
         /*
 84   
         // If we're testing the framework itself, then absolutely DO NOT
 85   
         // delegate those classes. 
 86   
         if (getClassPath().indexOf("abbot.jar") != -1) {
 87   
             frameworkIsUnderTest = true;
 88   
         }
 89   
         */
 90   
     }
 91   
 
 92  1
     public boolean isEventDispatchThread() {
 93  1
         return (eventQueue != null
 94   
             && Thread.currentThread() == eventQueue.thread)
 95   
             || (eventQueue == null
 96   
                 && SwingUtilities.isEventDispatchThread());
 97   
     }
 98   
 
 99   
     /** Should the parent class loader try to load this class first? */
 100   
     // FIXME we should only need the delegate flag if stuff in the classpath
 101   
     // is also found on the system classpath, e.g. the framework itself
 102   
     // Maybe just set it internally in case the classpaths overlap?
 103  158
     protected boolean shouldDelegate(String name) {
 104  158
         return bootstrapLoader.shouldDelegate(name)
 105   
             && !isExtension(name)
 106   
             && !(frameworkIsUnderTest && isFrameworkClass(name));
 107   
     }
 108   
 
 109  0
     private boolean isFrameworkClass(String name) {
 110  0
         return name.startsWith("abbot.")
 111   
             || name.startsWith("junit.extensions.abbot.");
 112   
     }
 113   
 
 114  230
     private boolean isExtension(String name) {
 115  230
         return name.startsWith("abbot.tester.extensions.")
 116   
             || name.startsWith("abbot.script.parsers.extensions.");
 117   
     }
 118   
 
 119   
     /**
 120   
      * Finds and loads the class with the specified name from the search
 121   
      * path.  If the class is a bootstrap class and must be preloaded, use our
 122   
      * own bootstrap loader.  If it is an extension class, use our own
 123   
      * extensions loader.
 124   
      *
 125   
      * @param name the name of the class
 126   
      * @return the resulting class
 127   
      * @exception ClassNotFoundException if the class could not be found
 128   
      */
 129  118
     public Class findClass(String name) throws ClassNotFoundException {
 130  118
         if (isBootstrapClassRequiringReload(name)) {
 131  17
             try {
 132  17
                 return bootstrapLoader.findClass(name);
 133   
             }
 134   
             catch(ClassNotFoundException cnf) {
 135  0
                 Log.warn(cnf);
 136   
             }
 137   
         }
 138   
 
 139   
         // Look for extensions first in the framework class path (with a
 140   
         // special loader), then in the app class path.
 141   
         // Extensions *must* have the same class loader as the corresponding
 142   
         // custom components
 143  101
         try {
 144  101
             return super.findClass(name);
 145   
         }
 146   
         catch(ClassNotFoundException cnf) {
 147  97
             if (isExtension(name)) {
 148  1
                 return extensionsLoader.findClass(name);
 149   
             }
 150  96
             throw cnf;
 151   
         }
 152   
     }
 153   
 
 154   
     /** Ensure that everything else subsequently loaded on the same thread or
 155   
      * any subsequently spawned threads uses the given class loader.  Also
 156   
      * ensure that classes loaded by the event dispatch thread and threads it
 157   
      * spawns use the given class loader.
 158   
      */
 159  16
     public void install() {
 160   
 
 161  16
         if (SwingUtilities.isEventDispatchThread()) {
 162  0
             throw new IllegalStateException(Strings.get("appcl.invalid_state"));
 163   
         }
 164   
 
 165  16
         if (installedThread != null) {
 166  0
             String msg = Strings.get("appcl.already_installed",
 167   
                                      new Object[] { installedThread });
 168  0
             throw new IllegalStateException(msg);
 169   
         }
 170   
         
 171   
         // Change the effective classpath, but make sure it's available if
 172   
         // someone needs to access it.
 173  16
         oldClassPath = System.getProperty("java.class.path");
 174  16
         System.setProperty("abbot.class.path", oldClassPath);
 175  16
         System.setProperty("java.class.path", getClassPath());
 176  16
         Log.debug("java.class.path set to "
 177   
                   + System.getProperty("java.class.path"));
 178   
         
 179   
         // Install our own handler for catching exceptions on the event
 180   
         // dispatch thread.
 181  16
         try {
 182  16
             new EventExceptionHandler().install();
 183   
         }
 184   
         catch(Exception e) {
 185   
             // ignore any exceptions, since they're not fatal
 186   
         }
 187   
 
 188  16
         eventQueue = new AppEventQueue();
 189  16
         eventQueue.install();
 190   
         
 191  16
         Thread current = Thread.currentThread();
 192  16
         installedThread = current;
 193  16
         oldClassLoader = installedThread.getContextClassLoader();
 194  16
         installedThread.setContextClassLoader(this);
 195   
     }
 196   
     
 197   
     /** Reverse the effects of install.   Has no effect if the class loader
 198   
      * has not been installed on any thread.
 199   
      */
 200  17
     public void uninstall() {
 201   
         // Ensure that no two threads attempt to uninstall 
 202  17
         synchronized(lock) {
 203  17
             if (eventQueue != null) {
 204  15
                 eventQueue.uninstall();
 205  15
                 eventQueue = null;
 206   
             }
 207  17
             if (installedThread != null) {
 208  15
                 installedThread.setContextClassLoader(oldClassLoader);
 209  15
                 oldClassLoader = null;
 210  15
                 installedThread = null;
 211  15
                 System.setProperty("java.class.path", oldClassPath);
 212  15
                 oldClassPath = null;
 213   
             }
 214   
         }
 215   
     }
 216   
 
 217   
     private class AppEventQueue extends EventQueue {
 218   
 
 219   
         private Thread thread;
 220   
 
 221   
         /** Ensure the class loader for the event dispatch thread is the right
 222   
             one.
 223   
         */
 224  16
         public void install() {
 225  16
             Runnable installer = new Runnable() {
 226  16
                 public void run() {
 227  16
                     Toolkit.getDefaultToolkit().
 228   
                         getSystemEventQueue().push(AppEventQueue.this);
 229   
                 }
 230   
             };
 231   
             // Avoid deadlock with the event queue, in case it has the tree
 232   
             // lock (pickens). 
 233  16
             AWT.invokeAndWait(installer);
 234  16
             Runnable threadTagger = new Runnable() {
 235  16
                 public void run() {
 236  16
                     thread = Thread.currentThread();
 237  16
                     thread.setContextClassLoader(AppClassLoader.this);
 238  16
                     thread.setName(thread.getName() + " (AppClassLoader)");
 239   
                 }
 240   
             };
 241  16
             AWT.invokeAndWait(threadTagger);
 242   
         }
 243   
 
 244   
         /** Pop this and any subsequently pushed event queues. */
 245  15
         public void uninstall() {
 246  15
             Log.debug("Uninstalling AppEventQueue");
 247  15
             try {
 248  15
                 pop();
 249  15
                 thread = null;
 250   
             }
 251   
             catch(EmptyStackException ese) {
 252   
             }
 253  15
             Log.debug("AppEventQueue uninstalled");
 254   
         }
 255   
 
 256  0
         public String toString() {
 257  0
             return "Abbot Event Queue: " + thread;
 258   
         }
 259   
     }
 260   
     /** List of bootstrap classes we most definitely want to be loaded by this
 261   
      * class loader, rather than any parent, or the bootstrap loader.
 262   
      */
 263   
     private String[] mustReloadPrefixes = {
 264   
         "sun.applet.",  // need the whole package, not just AppletViewer/Main
 265   
     };
 266   
     /** Does the given class absolutely need to be preloaded? */
 267  1934
     private boolean isBootstrapClassRequiringReload(String name) {
 268  1934
         for (int i=0;i < mustReloadPrefixes.length;i++) {
 269  1934
             if (name.startsWith(mustReloadPrefixes[i]))
 270  389
                 return true;
 271   
         }
 272  1545
         return false;
 273   
     }
 274   
 
 275   
     /** Returns the path to the primary JRE classes, not including any
 276   
      * extensions.  This is primarily needed for loading
 277   
      * sun.applet.AppletViewer/Main, since most other classes in the bootstrap
 278   
      * path should <i>only</i> be loaded by the bootstrap loader.
 279   
      */
 280  82
     private static String getBootstrapPath() {
 281  82
         return System.getProperty("sun.boot.class.path");
 282   
     }
 283   
 
 284   
     /** Provide access to bootstrap classes that we need to be able to
 285   
      * reload.
 286   
      */ 
 287   
     private class BootstrapClassLoader extends NonDelegatingClassLoader {
 288  82
         public BootstrapClassLoader() {
 289  82
             super(getBootstrapPath(), null);
 290   
         }
 291  1816
         protected boolean shouldDelegate(String name) {
 292   
             // Exclude all bootstrap classes, except for those we know we
 293   
             // *must* be reloaded on each run in order to have function
 294   
             // properly (e.g. applet)  
 295  1816
             return !isBootstrapClassRequiringReload(name)
 296   
                 && !"abbot.script.AppletSecurityManager".equals(name);
 297   
         }
 298   
     }
 299   
 
 300  104
     public String toString() {
 301  104
         return super.toString() + " (java.class.path="
 302   
             + System.getProperty("java.class.path") + ")";
 303   
     }
 304   
 }
 305