KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > winstone > classLoader > ReloadingClassLoader


1 /*
2  * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
3  * Distributed under the terms of either:
4  * - the common development and distribution license (CDDL), v1.0; or
5  * - the GNU Lesser General Public License, v2.1 or later
6  */

7 package winstone.classLoader;
8
9 import java.io.File JavaDoc;
10 import java.io.IOException JavaDoc;
11 import java.net.URL JavaDoc;
12 import java.net.URLClassLoader JavaDoc;
13 import java.util.Date JavaDoc;
14 import java.util.Enumeration JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Map JavaDoc;
18 import java.util.Set JavaDoc;
19 import java.util.jar.JarEntry JavaDoc;
20 import java.util.jar.JarFile JavaDoc;
21
22 import javax.servlet.ServletContextEvent JavaDoc;
23 import javax.servlet.ServletContextListener JavaDoc;
24
25 import winstone.Logger;
26 import winstone.WebAppConfiguration;
27 import winstone.WinstoneResourceBundle;
28
29 /**
30  * This subclass of WinstoneClassLoader is the reloading version. It runs a
31  * monitoring thread in the background that checks for updates to any files in
32  * the class path.
33  *
34  * @author <a HREF="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
35  * @version $Id: ReloadingClassLoader.java,v 1.9 2006/09/11 11:35:36 rickknowles Exp $
36  */

37 public class ReloadingClassLoader extends URLClassLoader JavaDoc implements ServletContextListener JavaDoc, Runnable JavaDoc {
38     private static final int RELOAD_SEARCH_SLEEP = 50;
39     private static final WinstoneResourceBundle CL_RESOURCES = new WinstoneResourceBundle("winstone.classLoader.LocalStrings");
40     private boolean interrupted;
41     private WebAppConfiguration webAppConfig;
42     private Set JavaDoc loadedClasses;
43     private File JavaDoc classPaths[];
44     private int classPathsLength;
45
46     public ReloadingClassLoader(URL JavaDoc urls[], ClassLoader JavaDoc parent) {
47         super(urls, parent);
48         this.loadedClasses = new HashSet JavaDoc();
49         if (urls != null) {
50             this.classPaths = new File JavaDoc[urls.length];
51             for (int n = 0 ; n < urls.length; n++) {
52                 this.classPaths[this.classPathsLength++] = new File JavaDoc(urls[n].getFile());
53             }
54         }
55     }
56     
57     protected void addURL(URL JavaDoc url) {
58         super.addURL(url);
59         synchronized (this.loadedClasses) {
60             if (this.classPaths == null) {
61                 this.classPaths = new File JavaDoc[10];
62                 this.classPathsLength = 0;
63             } else if (this.classPathsLength == (this.classPaths.length - 1)) {
64                 File JavaDoc temp[] = this.classPaths;
65                 this.classPaths = new File JavaDoc[(int) (this.classPathsLength * 1.75)];
66                 System.arraycopy(temp, 0, this.classPaths, 0, this.classPathsLength);
67             }
68             this.classPaths[this.classPathsLength++] = new File JavaDoc(url.getFile());
69         }
70     }
71
72     public void contextInitialized(ServletContextEvent JavaDoc sce) {
73         this.webAppConfig = (WebAppConfiguration) sce.getServletContext();
74         this.interrupted = false;
75         synchronized (this) {
76             this.loadedClasses.clear();
77         }
78         Thread JavaDoc thread = new Thread JavaDoc(this, CL_RESOURCES
79                 .getString("ReloadingClassLoader.ThreadName"));
80         thread.setDaemon(true);
81         thread.setPriority(Thread.MIN_PRIORITY);
82         thread.start();
83     }
84
85     public void contextDestroyed(ServletContextEvent JavaDoc sce) {
86         this.interrupted = true;
87         this.webAppConfig = null;
88         synchronized (this) {
89             this.loadedClasses.clear();
90         }
91     }
92
93     /**
94      * The maintenance thread. This makes sure that any changes in the files in
95      * the classpath trigger a classLoader self destruct and recreate.
96      */

97     public void run() {
98         Logger.log(Logger.FULL_DEBUG, CL_RESOURCES,
99                 "ReloadingClassLoader.MaintenanceThreadStarted");
100
101         Map JavaDoc classDateTable = new HashMap JavaDoc();
102         Map JavaDoc classLocationTable = new HashMap JavaDoc();
103         Set JavaDoc lostClasses = new HashSet JavaDoc();
104         while (!interrupted) {
105             try {
106                 Thread.sleep(RELOAD_SEARCH_SLEEP);
107                 String JavaDoc loadedClassesCopy[] = null;
108                 synchronized (this) {
109                     loadedClassesCopy = (String JavaDoc []) this.loadedClasses.toArray(new String JavaDoc[0]);
110                 }
111
112                 for (int n = 0; (n < loadedClassesCopy.length) && !interrupted; n++) {
113                     String JavaDoc className = transformToFileFormat(loadedClassesCopy[n]);
114                     File JavaDoc location = (File JavaDoc) classLocationTable.get(className);
115                     Long JavaDoc classDate = null;
116                     if ((location == null) || !location.exists()) {
117                         for (int j = 0; (j < this.classPaths.length) && (classDate == null); j++) {
118                             File JavaDoc path = this.classPaths[j];
119                             if (!path.exists()) {
120                                 continue;
121                             } else if (path.isDirectory()) {
122                                 File JavaDoc classLocation = new File JavaDoc(path, className);
123                                 if (classLocation.exists()) {
124                                     classDate = new Long JavaDoc(classLocation.lastModified());
125                                     classLocationTable.put(className, classLocation);
126                                 }
127                             } else if (path.isFile()) {
128                                 classDate = searchJarPath(className, path);
129                                 if (classDate != null)
130                                     classLocationTable.put(className, path);
131                             }
132                         }
133                     } else if (location.exists())
134                         classDate = new Long JavaDoc(location.lastModified());
135
136                     // Has class vanished ? Leave a note and skip over it
137
if (classDate == null) {
138                         if (!lostClasses.contains(className)) {
139                             lostClasses.add(className);
140                             Logger.log(Logger.DEBUG, CL_RESOURCES,
141                                     "ReloadingClassLoader.ClassLost", className);
142                         }
143                         continue;
144                     }
145                     if ((classDate != null) && lostClasses.contains(className)) {
146                         lostClasses.remove(className);
147                     }
148
149                     // Stash date of loaded files, and compare with last
150
// iteration
151
Long JavaDoc oldClassDate = (Long JavaDoc) classDateTable.get(className);
152                     if (oldClassDate == null) {
153                         classDateTable.put(className, classDate);
154                     } else if (oldClassDate.compareTo(classDate) != 0) {
155                         // Trigger reset of webAppConfig
156
Logger.log(Logger.INFO, CL_RESOURCES,
157                                 "ReloadingClassLoader.ReloadRequired",
158                                 new String JavaDoc[] {className,
159                                         "" + new Date JavaDoc(classDate.longValue()),
160                                         "" + new Date JavaDoc(oldClassDate.longValue()) });
161                         this.webAppConfig.resetClassLoader();
162                     }
163                 }
164             } catch (Throwable JavaDoc err) {
165                 Logger.log(Logger.ERROR, CL_RESOURCES,
166                         "ReloadingClassLoader.MaintenanceThreadError", err);
167             }
168         }
169         Logger.log(Logger.FULL_DEBUG, CL_RESOURCES,
170                 "ReloadingClassLoader.MaintenanceThreadFinished");
171     }
172
173     protected Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
174         synchronized (this) {
175             this.loadedClasses.add("Class:" + name);
176         }
177         return super.findClass(name);
178     }
179
180     public URL JavaDoc findResource(String JavaDoc name) {
181         synchronized (this) {
182             this.loadedClasses.add(name);
183         }
184         return super.findResource(name);
185     }
186
187     /**
188      * Iterates through a jar file searching for a class. If found, it returns that classes date
189      */

190     private Long JavaDoc searchJarPath(String JavaDoc classResourceName, File JavaDoc path)
191             throws IOException JavaDoc, InterruptedException JavaDoc {
192         JarFile JavaDoc jar = new JarFile JavaDoc(path);
193         for (Enumeration JavaDoc e = jar.entries(); e.hasMoreElements() && !interrupted;) {
194             JarEntry JavaDoc entry = (JarEntry JavaDoc) e.nextElement();
195             if (entry.getName().equals(classResourceName))
196                 return new Long JavaDoc(path.lastModified());
197         }
198         return null;
199     }
200
201     private static String JavaDoc transformToFileFormat(String JavaDoc name) {
202         if (!name.startsWith("Class:"))
203             return name;
204         else
205             return WinstoneResourceBundle.globalReplace(name.substring(6), ".", "/") + ".class";
206     }
207 }
208
Popular Tags