|
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.
for (int i=0; i<jarFiles.size(); i++) {
try {
File jf = (File)jarFiles.elementAt(i);
classBytes = loadClassFromJar(jf.getPath(), name);
if (classBytes != null)
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 |
|