|
PluginClassLoader |
|
package ij.io; import java.io.*; import java.net.*; import java.util.*; import java.util.zip.*; import ij.IJ; /** ImageJ uses this class loader to load plugins and resources from the * plugins directory and immediate subdirectories. This class loader will * also load classes and resources from JAR files. * * <p> The class loader searches for classes and resources in the following order: * <ol> * <li> Plugins directory</li> * <li> Subdirectories of the Plugins directory</li> * <li> JAR and ZIP files in the plugins directory and subdirectories</li> * </ol> * <p> The class loader does not recurse into subdirectories beyond the first level. */ public class PluginClassLoader extends ClassLoader { protected String path; protected Hashtable cache = new Hashtable(); protected Vector jarFiles; /** * Creates a new PluginClassLoader that searches in the directory path * passed as a parameter. The constructor automatically finds all JAR and ZIP * files in the path and first level of subdirectories. The JAR and ZIP files * are stored in a Vector for future searches. * @param path the path to the plugins directory. */ public PluginClassLoader(String path) { init(path); } /** This version of the constructor is used when ImageJ is launched using Java WebStart. */ public PluginClassLoader(String path, boolean callSuper) { super(Thread.currentThread().getContextClassLoader()); init(path); } void init(String path) { this.path = path; jarFiles = new Vector(); //find all JAR files on the path and subdirectories File f = new File(path); String[] list = f.list(); if (list==null) return; for (int i=0; i<list.length; i++) { f=new File(path, list[i]); if (f.isDirectory()) { String[] innerlist = f.list(); if (innerlist==null) continue; for (int j=0; j<innerlist.length; j++) { File g = new File(f,innerlist[j]); if (g.isFile()) addJAR(g); } } else addJAR(f); } } private void addJAR(File f) { if (f.getName().endsWith(".jar") || f.getName().endsWith(".zip")) jarFiles.addElement(f); } /** * Returns a resource from the path or JAR files as a URL * @param name a resource name. */ public URL getResource(String name) { // try system loader first URL res = super.getSystemResource(name); if (res != null) return res; File resFile; //try plugins directory try { resFile = new File(path, name); if (resFile.exists()) { res = makeURL(resFile); return res; } } catch (Exception e) {} //try subfolders resFile = new File(path); String[] list = resFile.list(); if (list!=null) { for (int i=0; i<list.length; i++) { resFile = new File(path, list[i]); if (resFile.isDirectory()) { try { File f = new File(path+list[i], name); if (f.exists()) { res = makeURL(f); return res; } } catch (Exception e) {} } } } //otherwise look in JAR files byte [] resourceBytes; for (int i=0; i<jarFiles.size(); i++) { try { File jf = (File)jarFiles.elementAt(i); resourceBytes = loadFromJar(jf.getPath(), name); if (resourceBytes != null) { res = makeURL(name, jf); return res; } } catch (MalformedURLException e) { IJ.error(e.toString()); } catch (IOException e) { IJ.error(e.toString()); } } return null; } // make a URL from a file private URL makeURL (File fil) throws MalformedURLException { URL url = new URL("file","",fil.toString()); return url; } // make a URL from a file within a JAR private URL makeURL (String name, File jar) throws MalformedURLException { StringBuffer filename = new StringBuffer("file:///"); filename.append(jar.toString()); filename.append("!/"); filename.append(name); //filename.insert(0,'/'); String sf = filename.toString(); String sfu = sf.replace('\\','/'); URL url = new URL("jar","",sfu); return url; } /** * Returns a resource from the path or JAR files as an InputStream * @param name a resource name. */ public InputStream getResourceAsStream(String name) { //try the system loader first InputStream is = super.getSystemResourceAsStream(name); if (is != null) return is; File resFile; //try plugins directory resFile = new File(path, name); try { // read the byte codes is = new FileInputStream(resFile); } catch (Exception e) {} if (is != null) return is; //try subdirectories resFile = new File(path); String[] list = resFile.list(); if (list!=null) { for (int i=0; i<list.length; i++) { resFile = new File(path, list[i]); if (resFile.isDirectory()) { try { File f = new File(path+list[i], name); is = new FileInputStream(f); } catch (Exception e) {} if (is != null) return is; } } } //look in JAR files byte [] resourceBytes; for (int i=0; i<jarFiles.size(); i++) { try { File jf = (File)jarFiles.elementAt(i); resourceBytes = loadFromJar(jf.getPath(), name); if (resourceBytes != null){ is = new ByteArrayInputStream(resourceBytes); return is; } } catch (Exception e) { IJ.error(e.toString()); } } return null; } /** * Returns a Class from the path or JAR files. Classes are automatically resolved. * @param className a class name without the .class extension. */ public Class loadClass(String className) throws ClassNotFoundException { return (loadClass(className, true)); } /** * Returns a Class from the path or JAR files. Classes are resolved if resolveIt is true. * @param className a String class name without the .class extension. * resolveIt a boolean (should almost always be true) */ public synchronized Class loadClass(String className, boolean resolveIt) throws ClassNotFoundException { Class result; byte[] classBytes; // try the local cache of classes result = (Class)cache.get(className); if (result != null) { return result; } // try the system class loader try { result = super.findSystemClass(className); return result; } catch (Exception e) {} // Try to load it from plugins directory classBytes = loadClassBytes(className); //IJ.log("loadClass: "+ className + " "+ (classBytes!=null?""+classBytes.length:"null")); if (classBytes==null) { result = getParent().loadClass(className); if (result != null) return result; } if (classBytes==null) throw new ClassNotFoundException(className); // Define it (parse the class file) result = defineClass(className, classBytes, 0, classBytes.length); if (result == null) { throw new ClassFormatError(); } //Resolve if necessary if (resolveIt) resolveClass(result); cache.put(className, result); return result; } /** * This does the actual work of loading the bytes from the disk. Returns an * array of bytes that will be defined as a Class. This should be overloaded to have * the Class Loader look in more places. * @param name a class name without the .class extension. */ protected byte[] loadClassBytes(String name) { byte [] classBytes = null; classBytes = loadIt(path, name); if (classBytes == null) { classBytes = loadFromSubdirectory(path, name); if (classBytes == null) { // Attempt to get the class data from the JAR files. if (name.startsWith("java.")||name.startsWith("ij.")) return null; for (int i=0; i<jarFiles.size(); i++) { try { File jf = (File)jarFiles.elementAt(i); classBytes = loadClassFromJar(jf.getPath(), name); //IJ.log(i+" "+name+" "+classBytes); if (classBytes!=null) { if (i!=0) { Object o = jarFiles.elementAt(0); jarFiles.insertElementAt(jarFiles.elementAt(i), 0); jarFiles.insertElementAt(o, i); } return classBytes; } } catch (Exception e) { //no problem, try the next one } } } } return classBytes; } // Loads the bytes from file private byte [] loadIt(String path, String classname) { String filename = classname.replace('.','/'); filename += ".class"; File fullname = new File(path, filename); //ij.IJ.write("loadIt: " + fullname); try { // read the byte codes InputStream is = new FileInputStream(fullname); int bufsize = (int)fullname.length(); byte buf[] = new byte[bufsize]; is.read(buf, 0, bufsize); is.close(); return buf; } catch (Exception e) { return null; } } private byte [] loadFromSubdirectory(String path, String name) { File f = new File(path); String[] list = f.list(); if (list!=null) { for (int i=0; i<list.length; i++) { //ij.IJ.write(path+" "+list[i]); f=new File(path, list[i]); if (f.isDirectory()) { byte [] buf = loadIt(path+list[i], name); if (buf!=null) return buf; } } } return null; } // Load class from a JAR file byte[] loadClassFromJar(String jar, String className) { String name = className.replace('.','/'); name += ".class"; return loadFromJar(jar, name); } // Load class or resource from a JAR file byte[] loadFromJar(String jar, String name) { BufferedInputStream bis = null; try { ZipFile jarFile = new ZipFile(jar); Enumeration entries = jarFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = (ZipEntry) entries.nextElement(); if (entry.getName().equals(name)) { bis = new BufferedInputStream(jarFile.getInputStream(entry)); int size = (int)entry.getSize(); byte[] data = new byte[size]; int b=0, eofFlag=0; while ((size - b) > 0) { eofFlag = bis.read(data, b, size - b); if (eofFlag==-1) break; b += eofFlag; } return data; } } } catch (Exception e) {} finally { try {if (bis!=null) bis.close();} catch (IOException e) {} } return null; } }
|
PluginClassLoader |
|