1   package gate.util;
2   
3   /**
4    * A ClassLoader that supports class reloading.
5    * It maintains a list of URLs which are searched for classes not found in the
6    * system classloader.
7    * URLs can be loaded and unloaded. Loading the same URL twice will cause the
8    * jar file or directory pointed by the URL to be reloaded.
9    */
10  
11  import java.net.URL;
12  import java.net.URLClassLoader;
13  import java.util.*;
14  
15  public class ReloadingClassLoader extends ClassLoader {
16  
17    /**
18     * Constructs a ReloadingClassLoader using a custom class loader as parent.
19     *
20     * @param parent the parent class loader. The parent class loader should give
21     * access to the system classes at the least (in order to load a new class
22     * access to {@link java.lang.Object} is required).
23     */
24    public ReloadingClassLoader(ClassLoader parent){
25      this.parent = parent;
26      loaders = new HashMap();
27    }
28  
29  
30    /**
31     * Constructs a ReloadingClassLoader using the System Class Loader as a
32     * parent.
33     */
34    public ReloadingClassLoader() {
35      this(ClassLoader.getSystemClassLoader());
36    }
37  
38    /**
39     * Registers an URL as a location where class files can be found.
40     * If the URL was already registered the the classes found at the location
41     * will be reloaded.
42     * @param url the URL pointing to a jar file or to a directory containing
43     * class files.
44     */
45    public void load(URL url){
46      LocationClassLoader loader = new LocationClassLoader(url);
47      loaders.put(url, loader);
48    }
49  
50    /**
51     * Removes a registered URL.
52     * @param url the URl to be unloaded.
53     */
54    public void unload(URL url){
55      loaders.remove(url);
56    }
57  
58    /**
59     * Loads the class with the specified name.  It searches for classes in the
60     * following order:
61     * <ol>
62     *   <li>the parent classloader</li>
63     *   <li>all the locations registered with this class loader</li>
64     * </ol>
65     *
66     * @param  name The name of the class
67     * @param  resolve If <tt>true</tt> then resolve the class
68     * @return  The resulting <tt>Class</tt> object
69     * @throws  ClassNotFoundException If the class could not be found
70     */
71    protected synchronized Class loadClass(String name, boolean resolve)
72        throws ClassNotFoundException{
73      Class c = null;
74      //check with the parent (most classes are fixed)
75      if(parent != null){
76        try {
77          c = parent.loadClass(name);
78        }catch (ClassNotFoundException cnfe) {}
79      }
80  
81      if(c == null){
82        //Check all the loaders for the class
83        Iterator loaderIter = loaders.values().iterator();
84        while (c == null && loaderIter.hasNext()) {
85          LocationClassLoader aLoader = (LocationClassLoader) loaderIter.next();
86          try {
87            c = aLoader.loadClass(name, false);
88          } catch (ClassNotFoundException e) {}
89        }
90      }
91      if(c == null) throw new ClassNotFoundException(name);
92      if (resolve) resolveClass(c);
93      return c;
94    }
95  
96    /**
97     * A ClassLoader that loads classes from a location specified by an URL.
98     */
99    protected class LocationClassLoader extends URLClassLoader {
100 
101     /**
102      * Constructs a LocationClassLoader for a specified URL.
103      * Uses the same parent classloader as the enclosing ReloadingClassLoader.
104      * @param location the URL to be searched for class files.
105      */
106     public LocationClassLoader(URL location) {
107       super(new URL[]{location}, null);
108       this.location = location;
109       classCache = new HashMap();
110     }
111 
112     /**
113      * Loads the class with the specified name. It will search first the parent
114      * class loader, then an internal cache for classes already loaded and then
115      * the registered URL.
116      *
117      * @param  name The name of the class
118      * @param  resolve If <tt>true</tt> then resolve the class
119      * @return  The resulting <tt>Class</tt> object
120      *
121      * @throws  ClassNotFoundException If the class could not be found
122      */
123     protected synchronized Class loadClass(String name, boolean resolve)
124         throws ClassNotFoundException{
125       Class c = null;
126       //search the parent first
127       if(parent != null){
128         try {
129           c = parent.loadClass(name);
130         }catch (ClassNotFoundException cnfe) {}
131       }
132       //search the cache
133       if(c == null){
134         c = (Class) classCache.get(name);
135       }
136       //search the registered location
137       if (c == null) {
138         //this will trow ClassNotFoundException if necessary
139         c = findClass(name);
140         //save the class for future searches
141         classCache.put(name, c);
142       }
143       if (resolve) {
144         resolveClass(c);
145       }
146       return c;
147     }
148 
149     /**
150      * A cache for classes already found and loaded.
151      */
152     protected Map classCache;
153     /**
154      * The location to be searched for new classes.
155      */
156     protected URL location;
157   }//protected class LocationClassLoader
158 
159 
160   /**
161    * Map that contains the {@link LocationClassLoader} for each registered URL.
162    */
163   protected Map loaders;
164 
165   /**
166    * The parent class loader.
167    */
168   protected ClassLoader parent;
169 }