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