001 package railo.commons.lang; 002 003 import java.io.ByteArrayInputStream; 004 import java.io.Closeable; 005 import java.io.FileNotFoundException; 006 import java.io.IOException; 007 import java.io.InputStream; 008 import java.net.URL; 009 import java.util.zip.ZipEntry; 010 import java.util.zip.ZipFile; 011 012 import railo.commons.io.IOUtil; 013 import railo.commons.io.res.Resource; 014 import railo.commons.io.res.util.FileWrapper; 015 import railo.runtime.type.Sizeable; 016 017 // TODO umbauen auf ZipInputStream oder ein wrapper schreiben f゚r resorces der das file interface einhハlt 018 019 020 public final class ArchiveClassLoader extends ClassLoader implements Sizeable,Closeable { 021 022 private final ZipFile zip; 023 private final ClassLoader pcl; 024 025 /** 026 * constructor of the class 027 * @param file 028 * @param parent 029 * @throws IOException 030 */ 031 public ArchiveClassLoader(Resource file, ClassLoader parent) throws IOException { 032 super(parent); 033 this.pcl=parent; 034 //this.file=file; 035 036 //print.ln("archive:"+file.getPath()); 037 if(!file.exists()) 038 throw new FileNotFoundException("file "+file.getAbsolutePath()+" doesn't exist"); 039 if(!file.isFile()) 040 throw new IOException(file.getAbsolutePath()+" is not a file"); 041 if(!file.isReadable()) 042 throw new IOException("no access to "+file.getAbsolutePath()+" file"); 043 044 045 this.zip=new ZipFile(FileWrapper.toFile(file)); 046 } 047 048 /** 049 * Loads the class with the specified name. This method searches for 050 * classes in the same manner as the {@link #loadClass(String, boolean)} 051 * method. It is called by the Java virtual machine to resolve class 052 * references. Calling this method is equivalent to calling 053 * <code>loadClass(name, false)</code>. 054 * 055 * @param name the name of the class 056 * @return the resulting <code>Class</code> object 057 * @exception ClassNotFoundException if the class was not found 058 */ 059 public Class loadClass(String name) throws ClassNotFoundException { 060 return loadClass(name, false); 061 } 062 063 /** 064 * Loads the class with the specified name. The default implementation of 065 * this method searches for classes in the following order:<p> 066 * 067 * <ol> 068 * <li> Call {@link #findLoadedClass(String)} to check if the class has 069 * already been loaded. <p> 070 * <li> Call the <code>loadClass</code> method on the parent class 071 * loader. If the parent is <code>null</code> the class loader 072 * built-in to the virtual machine is used, instead. <p> 073 * <li> Call the {@link #findClass(String)} method to find the class. <p> 074 * </ol> 075 * 076 * If the class was found using the above steps, and the 077 * <code>resolve</code> flag is true, this method will then call the 078 * {@link #resolveClass(Class)} method on the resulting class object. 079 * <p> 080 * From the Java 2 SDK, v1.2, subclasses of ClassLoader are 081 * encouraged to override 082 * {@link #findClass(String)}, rather than this method.<p> 083 * 084 * @param name the name of the class 085 * @param resolve if <code>true</code> then resolve the class 086 * @return the resulting <code>Class</code> object 087 * @exception ClassNotFoundException if the class could not be found 088 */ 089 protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { 090 // First, check if the class has already been loaded 091 Class c = findLoadedClass(name); 092 if (c == null) { 093 c=findClassEL(name); 094 if(c==null) { 095 c =pcl.loadClass(name); 096 } 097 } 098 if (resolve) { 099 resolveClass(c); 100 } 101 return c; 102 } 103 104 @Override 105 protected Class findClass(String name) throws ClassNotFoundException { 106 Class clazz=findClassEL(name); 107 if(clazz!=null) return clazz; 108 throw new ClassNotFoundException("class "+name+" not found"); 109 } 110 111 private Class findClassEL(String name) { 112 byte[] barr = getBytes(name.replace('.','/').concat(".class")); 113 if(barr!=null) { 114 try { 115 int start=ClassUtil.hasCF33Prefix(barr)?10:0; 116 return defineClass(name,barr,start,barr.length-start); 117 } 118 catch(Throwable t) {} 119 } 120 return null; 121 } 122 123 @Override 124 public InputStream getResourceAsStream(String name) { 125 InputStream is = super.getResourceAsStream(name); 126 if(is!=null) return is; 127 128 byte[] barr = getBytes(name); 129 int start=ClassUtil.hasCF33Prefix(barr)?10:0; 130 if(barr!=null) return new ByteArrayInputStream(barr,start,barr.length-start); 131 132 return null; 133 } 134 135 @Override 136 public URL getResource(String name) { 137 return null; 138 } 139 140 141 private byte[] getBytes(String name) { 142 143 ZipEntry entry= zip.getEntry(name); 144 145 if (entry == null)return null; 146 147 int size= (int) entry.getSize(); 148 InputStream is=null; 149 try { 150 is= zip.getInputStream(entry); 151 byte[] data= new byte[size]; 152 int pos= 0; 153 while (pos < size) { 154 int n= is.read(data, pos, data.length - pos); 155 pos += n; 156 } 157 return data; 158 } 159 catch(IOException ioe) {} 160 finally { 161 IOUtil.closeEL(is); 162 } 163 return null; 164 } 165 166 @Override 167 public long sizeOf() { 168 return SizeOf.size(zip); 169 } 170 171 public void close() throws IOException { 172 zip.close(); 173 } 174 }