Mega Code Archive

 
Categories / Java / Reflection
 

Zip Class Loader

/* Copyright (c) 2006, 2009, Carl Burch. License information is located in the  * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */   import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.LinkedList; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class ZipClassLoader extends ClassLoader {     // This code was posted on a forum by "leukbr" on March 30, 2001.     // http://forums.sun.com/thread.jspa?threadID=360060&forumID=31     // I've modified it substantially to include a thread that keeps the file     // open for OPEN_TIME milliseconds so time isn't wasted continually     // opening and closing the file.     private static final int OPEN_TIME = 5000;     private static final int DEBUG = 0;         // 0 = no debug messages         // 1 = open/close ZIP file only         // 2 = also each resource request         // 3 = all messages while retrieving resource          private static final int REQUEST_FIND = 0;     private static final int REQUEST_LOAD = 1;          private static class Request {         int action;         String resource;         boolean responseSent;         Object response;                  Request(int action, String resource) {             this.action = action;             this.resource = resource;             this.responseSent = false;         }                  public String toString() {             String act = action == REQUEST_LOAD ? "load"                     : action == REQUEST_FIND ? "find" : "act" + action;             return act + ":" + resource;         }                  void setResponse(Object value) {             synchronized(this) {                 response = value;                 responseSent = true;                 notifyAll();             }         }                  void ensureDone() {             boolean aborted = false;             synchronized(this) {                 if(!responseSent) {                     aborted = true;                     responseSent = true;                     response = null;                     notifyAll();                 }             }             if(aborted && DEBUG >= 1) {                 System.err.println("request not handled successfully"); //OK             }         }                  Object getResponse() {             synchronized(this) {                 while(!responseSent) {                     try { this.wait(1000); } catch(InterruptedException e) { }                 }                 return response;             }         }     }          private class WorkThread extends Thread {         private LinkedList requests = new LinkedList();         private ZipFile zipFile = null;                  public void run() {             try {                 while(true) {                     Request request = waitForNextRequest();                     if(request == null) return;                                          if(DEBUG >= 2) System.err.println("processing " + request); //OK                     try {                         switch(request.action) {                         case REQUEST_LOAD: performLoad(request); break;                         case REQUEST_FIND: performFind(request); break;                         }                     } finally {                         request.ensureDone();                     }                     if(DEBUG >= 2) System.err.println("processed: " + request.getResponse()); //OK                 }             } catch(Throwable t) {                 if(DEBUG >= 3) { System.err.print("uncaught: "); t.printStackTrace(); } //OK             } finally {                 if(zipFile != null) {                     try {                         zipFile.close();                         zipFile = null;                         if(DEBUG >= 1) System.err.println("  ZIP closed"); //OK                     } catch(IOException e) {                         if(DEBUG >= 1) System.err.println("Error closing ZIP file"); //OK                     }                 }             }         }                  private Request waitForNextRequest() {             synchronized(bgLock) {                 long start = System.currentTimeMillis();                 while(requests.isEmpty()) {                     long elapse = System.currentTimeMillis() - start;                     if(elapse >= OPEN_TIME) {                         bgThread = null;                         return null;                     }                     try {                         bgLock.wait(OPEN_TIME);                     } catch(InterruptedException e) { }                 }                 return (Request) requests.removeFirst();             }         }                  private void performFind(Request req) {             ensureZipOpen();             Object ret = null;             try {                 if(zipFile != null) {                     if(DEBUG >= 3) System.err.println("  retrieve ZIP entry"); //OK                     String res = req.resource;                     ZipEntry zipEntry = zipFile.getEntry(res);                     if(zipEntry != null) {                         String url = "jar:" + zipPath.toURI() + "!/" + res;                         ret = new URL(url);                         if(DEBUG >= 3) System.err.println("  found: " + url); //OK                     }                 }             } catch(Throwable ex) {                 if(DEBUG >= 3) System.err.println("  error retrieving data"); //OK                 ex.printStackTrace();             }             req.setResponse(ret);         }                  private void performLoad(Request req) {             BufferedInputStream bis = null;             ensureZipOpen();             Object ret = null;             try {                 if(zipFile != null) {                     if(DEBUG >= 3) System.err.println("  retrieve ZIP entry"); //OK                     ZipEntry zipEntry = zipFile.getEntry(req.resource);                     if(zipEntry != null) {                         if(DEBUG >= 3) System.err.println("  load file"); //OK                         byte[] result = new byte[(int) zipEntry.getSize()];                         bis = new BufferedInputStream(zipFile.getInputStream(zipEntry));                         try {                             bis.read(result, 0, result.length);                             ret = result;                         } catch(IOException e) {                             if(DEBUG >= 3) System.err.println("  error loading file"); //OK                         }                     }                 }             } catch(Throwable ex) {                 if(DEBUG >= 3) System.err.println("  error retrieving data"); //OK                 ex.printStackTrace();             } finally {                 if (bis!=null) {                     try {                         if(DEBUG >= 3) System.err.println("  close file"); //OK                         bis.close();                     } catch (IOException ioex) {                         if(DEBUG >= 3) System.err.println("  error closing data"); //OK                     }                 }             }             req.setResponse(ret);         }                  private void ensureZipOpen() {             if(zipFile == null) {                 try {                     if(DEBUG >= 3) System.err.println("  open ZIP file"); //OK                     zipFile = new ZipFile(zipPath);                     if(DEBUG >= 1) System.err.println("  ZIP opened");  //OK                 } catch(IOException e) {                     if(DEBUG >= 1) System.err.println("  error opening ZIP file"); //OK                 }             }         }     }          private File zipPath;     private HashMap classes = new HashMap();     private Object bgLock = new Object();     private WorkThread bgThread = null;       public ZipClassLoader(String zipFileName) {         this(new File(zipFileName));     }       public ZipClassLoader(File zipFile) {         zipPath = zipFile;     }          public URL findResource(String resourceName) {         if(DEBUG >= 3) System.err.println("findResource " + resourceName); //OK         Object ret = request(REQUEST_FIND, resourceName);         if(ret instanceof URL) {             return (URL) ret;         } else {             return super.findResource(resourceName);         }     }     public Class findClass(String className) throws ClassNotFoundException {         boolean found = false;         Object result = null;         // check whether we have loaded this class before         synchronized(classes) {             found = classes.containsKey(className);             if(found) result = classes.get(className);         }         // try loading it from the ZIP file if we haven't         if(!found) {             String resourceName = className.replace('.', '/') + ".class";             result = request(REQUEST_LOAD, resourceName);             if(result instanceof byte[]) {                 if(DEBUG >= 3) System.err.println("  define class"); //OK                 byte[] data = (byte[]) result;                 result = defineClass(className, data, 0, data.length);                 if(result != null) {                     if(DEBUG >= 3) System.err.println("  class defined"); //OK                 } else {                     if(DEBUG >= 3) System.err.println("  format error"); //OK                     result = new ClassFormatError(className);                 }             }             synchronized(classes) { classes.put(className, result); }         }                  if(result instanceof Class) {             return (Class) result;         } else if(result instanceof ClassNotFoundException) {             throw (ClassNotFoundException) result;         } else if(result instanceof Error) {             throw (Error) result;         } else {             return super.findClass(className);         }     }          private Object request(int action, String resourceName) {         Request request;         synchronized(bgLock) {             if(bgThread == null) { // start the thread if it isn't working                 bgThread = new WorkThread();                 bgThread.start();             }             request = new Request(action, resourceName);             bgThread.requests.addLast(request);             bgLock.notifyAll();         }         return request.getResponse();     } }