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 @Override 113 protected Class findClass(String name) throws ClassNotFoundException { 114 Class clazz=findClassEL(name); 115 if(clazz!=null) return clazz; 116 throw new ClassNotFoundException("class "+name+" not found"); 117 } 118 119 private Class findClassEL(String name) { 120 byte[] barr = getBytes(name.replace('.','/').concat(".class")); 121 if(barr!=null) { 122 try { 123 return defineClass(name,barr,0,barr.length); 124 } 125 catch(Throwable t) {} 126 } 127 return null; 128 } 129 130 @Override 131 public InputStream getResourceAsStream(String name) { 132 InputStream is = super.getResourceAsStream(name); 133 if(is!=null) return is; 134 135 byte[] barr = getBytes(name); 136 if(barr!=null) return new ByteArrayInputStream(barr); 137 138 return null; 139 } 140 141 @Override 142 public URL getResource(String name) { 143 return null; 144 } 145 146 private byte[] getBytes(String name) { 147 ZipEntry entry=null; 148 for(int i=0;i<zips.length;i++){ 149 entry= zips[i].getEntry(name); 150 if(entry!=null) { 151 return getBytes(zips[i], entry); 152 } 153 } 154 return null; 155 } 156 157 private byte[] getBytes(ZipFile zip,ZipEntry entry) { 158 159 if (entry == null)return null; 160 161 int size= (int) entry.getSize(); 162 InputStream is=null; 163 try { 164 is= zip.getInputStream(entry); 165 byte[] data= new byte[size]; 166 int pos= 0; 167 while (pos < size) { 168 int n= is.read(data, pos, data.length - pos); 169 pos += n; 170 } 171 return data; 172 } 173 catch(IOException ioe) {} 174 finally { 175 IOUtil.closeEL(is); 176 } 177 return null; 178 } 179 180 @Override 181 public long sizeOf() { 182 return SizeOf.size(zips); 183 } 184 185 public void close() throws IOException { 186 for(int i=0;i<zips.length;i++){ 187 closeEL(zips[i]); 188 } 189 } 190 191 private static void closeEL(ZipFile zipFile) { 192 try { 193 if(zipFile!=null)zipFile.close(); 194 } catch (IOException e) {} 195 } 196 }