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