--- /dev/null
+// AutoReloadServletLoader.java\r
+// $Id: AutoReloadServletLoader.java,v 1.1 2010/06/15 12:24:14 smhuang Exp $\r
+// (c) COPYRIGHT MIT and INRIA, 1998.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+\r
+package org.w3c.jigsaw.servlet;\r
+\r
+import java.io.BufferedInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FilterInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.PrintStream;\r
+\r
+import java.net.MalformedURLException;\r
+import java.net.URL;\r
+import java.net.URLConnection;\r
+\r
+import java.util.Hashtable;\r
+\r
+class ServletClassEntry {\r
+\r
+ long classStamp = 0;\r
+ Class servletClass = null;\r
+ File classFile = null;\r
+ boolean systemClass = false;\r
+\r
+ public boolean isModified() {\r
+ if (! systemClass)\r
+ return (classFile.lastModified() > classStamp);\r
+ return false;\r
+ }\r
+\r
+ public void update() {\r
+ if (! systemClass)\r
+ classStamp = classFile.lastModified();\r
+ }\r
+\r
+ public ServletClassEntry(Class servletClass) {\r
+ this.servletClass = servletClass;\r
+ this.systemClass = true;\r
+ }\r
+\r
+ public ServletClassEntry (File classFile, \r
+ Class servletClass)\r
+ {\r
+ this.classFile = classFile;\r
+ this.servletClass = servletClass;\r
+ if (classFile != null)\r
+ this.classStamp = classFile.lastModified();\r
+ this.systemClass = false;\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ * @version $Revision: 1.1 $\r
+ * @author Benoît Mahé (bmahe@w3.org)\r
+ */\r
+public class AutoReloadServletLoader extends ClassLoader {\r
+ private static final boolean debug = false;\r
+ private Hashtable classes = null;\r
+ private int CLASSES_DEFAULT_SIZE = 13;\r
+\r
+ JigsawServletContext context = null;\r
+\r
+ private void trace(String msg) {\r
+ trace(msg,true);\r
+ }\r
+\r
+ private void trace(String msg, boolean display) {\r
+ if (display)\r
+ System.out.println("["+context.getServletDirectory()+"]: "+msg);\r
+ }\r
+\r
+ private String removeSpace(String string) {\r
+ int start = -1;\r
+ int end = string.length();\r
+ boolean spaces = false;\r
+ while (string.charAt(++start) == ' '){ spaces = true; };\r
+ while (string.charAt(--end) == ' '){ spaces = true; };\r
+ if (spaces)\r
+ return string.substring(start, ++end);\r
+ else\r
+ return new String(string);\r
+ }\r
+\r
+ private String packaged(String name) {\r
+ String cname = removeSpace(name);\r
+ try {\r
+ if (cname.endsWith(".class")) {\r
+ int idx = cname.lastIndexOf('.');\r
+ if (idx != -1)\r
+ cname = cname.substring(0,idx);\r
+ }\r
+ return cname.replace('.', '/')+".class";\r
+ } catch (Exception ex) {\r
+ return name;\r
+ }\r
+ }\r
+\r
+ private String noext(String name) {\r
+ int idx = name.lastIndexOf(".class");\r
+ if ((idx != -1) && \r
+ ((idx+6 == name.length()) || \r
+ (Character.isWhitespace(name.charAt(idx+6)))))\r
+ {\r
+ return name.substring(0,idx);\r
+ }\r
+ return name;\r
+ }\r
+\r
+ protected boolean classChanged(String name) {\r
+ ServletClassEntry entry = (ServletClassEntry)classes.get(noext(name));\r
+ if (entry != null) {\r
+ if (debug) {\r
+ System.out.println("entry : "+name);\r
+ if (!entry.systemClass) {\r
+ System.out.println("file : "+\r
+ entry.classFile.getAbsolutePath());\r
+ System.out.println("Stamp : "+entry.classStamp);\r
+ }\r
+ System.out.println("System : "+entry.systemClass);\r
+ System.out.println("modified : "+entry.isModified());\r
+ }\r
+ return entry.isModified();\r
+ }\r
+ return false; //true\r
+ }\r
+\r
+ /**\r
+ * Get a cached class.\r
+ * @return a Class instance\r
+ * @exception ClassNotFoundException if the Class can't be found\r
+ */\r
+ protected final Class getCachedClass(String name, boolean resolve) \r
+ throws ClassNotFoundException\r
+ {\r
+ ServletClassEntry entry = (ServletClassEntry)classes.get(noext(name));\r
+ if (entry != null) {\r
+ if (! entry.isModified()) {\r
+ trace(name+": not modified",debug);\r
+ return entry.servletClass;\r
+ } else if (! entry.systemClass) {\r
+ entry.servletClass = loadClassFile(entry.classFile);\r
+ if ( resolve )\r
+ resolveClass(entry.servletClass);\r
+ entry.update();\r
+ trace(name+": reloaded",debug);\r
+ return entry.servletClass;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ protected void checkPackageAccess(String name) {\r
+ SecurityManager s = System.getSecurityManager();\r
+ if ( s != null ) {\r
+ int i = name.lastIndexOf('.');\r
+ if ( i >= 0 )\r
+ s.checkPackageAccess(name.substring(0, i));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Given the class name, return its File name.\r
+ * @param name The class to be loaded.\r
+ * @return The File for the class.\r
+ */\r
+ protected File locateClass(String name) {\r
+ File classfile = null;\r
+ File base = context.getServletDirectory();\r
+ String cname = null;\r
+\r
+ cname = packaged(name);\r
+ classfile = new File(base, cname);\r
+ if (classfile != null)\r
+ if (classfile.exists()) return classfile;\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Load a Class from its class file..\r
+ * @return a Class instance\r
+ * @exception ClassNotFoundException if the Class can't be found\r
+ */\r
+ protected Class loadClassFile(File file) \r
+ throws ClassNotFoundException\r
+ {\r
+ byte data[] = null;\r
+ if ( file == null )\r
+ throw new ClassNotFoundException("invalid servlet base");\r
+ trace("located at "+file,debug);\r
+ try {\r
+ BufferedInputStream in = \r
+ new BufferedInputStream( new FileInputStream( file) );\r
+ ByteArrayOutputStream out = new ByteArrayOutputStream(512);\r
+ byte buf[] = new byte[512];\r
+ for (int got = 0 ; (got = in.read(buf)) > 0 ; )\r
+ out.write(buf, 0, got);\r
+ data = out.toByteArray();\r
+ in.close();\r
+ } catch (Exception ex) {\r
+ if (debug) \r
+ ex.printStackTrace();\r
+ throw new ClassNotFoundException(file.getAbsolutePath());\r
+ }\r
+ // Define the class:\r
+ return defineClass(null, data, 0, data.length);\r
+ }\r
+\r
+ /**\r
+ * Get a new class.\r
+ * @return a Class instance\r
+ * @exception ClassNotFoundException if the Class can't be found\r
+ */\r
+ protected final Class getNewClass(File classfile, \r
+ String name, \r
+ boolean resolve) \r
+ throws ClassNotFoundException\r
+ {\r
+ Class c = null;\r
+ c = loadClassFile(classfile);\r
+ if ( resolve )\r
+ resolveClass(c);\r
+ classes.put(noext(name), new ServletClassEntry(classfile,c));\r
+ trace(name+": loading new class done.",debug);\r
+ return c;\r
+ }\r
+\r
+ /**\r
+ * Load a class.\r
+ * @return a Class instance\r
+ * @exception ClassNotFoundException if the Class can't be found\r
+ */\r
+ protected Class loadClass(String name, boolean resolve)\r
+ throws ClassNotFoundException\r
+ {\r
+ Class c = null;\r
+ trace(name+": loading class",debug);\r
+ // Look for a cached class first:\r
+ c = getCachedClass(name,resolve);\r
+ if (c != null) {\r
+ return c;\r
+ }\r
+\r
+ synchronized (this) {\r
+ c = getCachedClass(name,resolve);\r
+ if (c != null) return c;\r
+\r
+ checkPackageAccess(name);\r
+ \r
+ File file = locateClass(name);\r
+ \r
+ if (file == null) {\r
+ // Then look for a system class:\r
+ try {\r
+ String noext = noext(name);\r
+ if ((c = findSystemClass(noext)) != null) {\r
+ trace(name+": system class",debug);\r
+ classes.put(noext, new ServletClassEntry(c));\r
+ return c;\r
+ } else \r
+ throw new ClassNotFoundException(name);\r
+ } catch (Exception ex) {\r
+ throw new ClassNotFoundException(name);\r
+ }\r
+ }\r
+ // load the class\r
+ return getNewClass(file,name,resolve);\r
+ }\r
+ }\r
+\r
+\r
+ public URL getResource(String name) {\r
+ URL resource = getSystemResource(name);\r
+ if (resource != null)\r
+ return resource;\r
+ String url = context.getServletDirectory().getAbsolutePath();\r
+ // Return the location of that resource (name resolved by j.l.Class)\r
+ if ( File.separatorChar != '/' )\r
+ url = url.replace(File.separatorChar, '/');\r
+ File file = new File(url,name);\r
+ if (! file.exists())\r
+ return null;\r
+ try {\r
+ return new URL("file", "localhost", url+"/"+name);\r
+ } catch (MalformedURLException ex) {\r
+ // Shouldn't fall here\r
+ return null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get a resource as a stream.\r
+ * @param name The name of the resource to locate.\r
+ */\r
+\r
+ public InputStream getResourceAsStream(String name) {\r
+ InputStream in = getSystemResourceAsStream(name);\r
+ if (in != null) \r
+ return in;\r
+ URL resource = getResource(name);\r
+ if (resource == null)\r
+ return null;\r
+ try {\r
+ URLConnection c = resource.openConnection();\r
+ return c.getInputStream();\r
+ } catch (IOException ex) {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ protected AutoReloadServletLoader(AutoReloadServletLoader loader) {\r
+ super();\r
+ this.context = loader.context;\r
+ this.classes = loader.classes;\r
+ }\r
+\r
+ protected AutoReloadServletLoader(JigsawServletContext context) {\r
+ super();\r
+ this.context = context;\r
+ this.classes = new Hashtable(CLASSES_DEFAULT_SIZE);\r
+ }\r
+}\r