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