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.loader.classloader; 020 021 022import java.io.BufferedInputStream; 023import java.io.ByteArrayInputStream; 024import java.io.ByteArrayOutputStream; 025import java.io.File; 026import java.io.FileInputStream; 027import java.io.IOException; 028import java.io.InputStream; 029import java.util.Hashtable; 030import java.util.zip.ZipEntry; 031import java.util.zip.ZipInputStream; 032 033import lucee.loader.util.Util; 034 035 036/** 037 * 038 * This class implements a simple class loader 039 * that can be used to load at runtime 040 * classes contained in a JAR file. 041 */ 042public final class LuceeClassLoader extends ClassLoader { 043 private Hashtable classes = new Hashtable(); 044 private Hashtable resources = new Hashtable(); 045 private ClassLoader pcl; 046 047 /** 048 * Creates a new JarClassLoader that will allow the loading 049 * of classes stored in a jar file. 050 * 051 * @param jarFile the name of the jar file 052 * @param parent parent class loader 053 * @throws IOException 054 * @exception IOException an error happened while reading 055 * the contents of the jar file 056 */ 057 public LuceeClassLoader(File jarFile, ClassLoader parent) throws IOException { 058 this(new FileInputStream(jarFile),parent,isSecure(jarFile)); 059 } 060 private static boolean isSecure(File jarFile) { 061 if(jarFile.getName().toLowerCase().endsWith(".lco")) return false; 062 return true; 063 } 064 public LuceeClassLoader(InputStream jar, ClassLoader parent, boolean secured) throws IOException { 065 super(parent); 066 067 if(secured) 068 throw new IOException("secured core files are not supported"); 069 070 071 this.pcl=parent; 072 ZipInputStream zis = new ZipInputStream(new BufferedInputStream(jar)); 073 074 try { 075 byte[] buffer = new byte[0xffff]; 076 int bytes_read; 077 078 ZipEntry ze; 079 byte[] barr; 080 while((ze = zis.getNextEntry()) != null) { 081 if (!ze.isDirectory()) { 082 ByteArrayOutputStream baos=new ByteArrayOutputStream(); 083 while((bytes_read = zis.read(buffer)) != -1) 084 baos.write(buffer, 0, bytes_read); 085 String name = ze.getName().replace('\\', '/'); 086 barr=baos.toByteArray(); 087 if(name.endsWith(".class")) { 088 String className=name.substring(0,name.length()-6); 089 className=className.replace('/','.'); 090 classes.put(className,barr); 091 } 092 resources.put(name, barr); 093 zis.closeEntry(); 094 baos.close(); 095 } 096 } 097 } 098 finally { 099 Util.closeEL(zis); 100 } 101 } 102 103 /** 104 * Looks among the contents of the jar file (cached in memory) 105 * and tries to find and define a class, given its name. 106 * 107 * @param className the name of the class 108 * @return a Class object representing our class 109 * @exception ClassNotFoundException the jar file did not contain 110 * a class named <code>className</code> 111 */ 112 public Class findClass(String className) throws ClassNotFoundException { 113 byte[] classBytes = (byte[])classes.get(className); 114 if (classBytes == null) throw new ClassNotFoundException("class ["+className+"] not found"); 115 return defineClass(className, classBytes, 0, classBytes.length); 116 } 117 118 private Class findClassEL(String className) { 119 byte[] classBytes = (byte[])classes.get(className); 120 if (classBytes == null) return null; 121 return defineClass(className, classBytes, 0, classBytes.length); 122 } 123 124 125 126 /** 127 * Loads the class with the specified name. This method searches for 128 * classes in the same manner as the "loadClass(String, boolean)" 129 * method. It is called by the Java virtual machine to resolve class 130 * references. Calling this method is equivalent to calling 131 * <code>loadClass(name, false)</code>. 132 * 133 * @param name the name of the class 134 * @return the resulting <code>Class</code> object 135 * @exception ClassNotFoundException if the class was not found 136 */ 137 public Class loadClass(String name) throws ClassNotFoundException { 138 return loadClass(name, false); 139 } 140 141 /** 142 * Loads the class with the specified name. The default implementation of 143 * this method searches for classes in the following order:<p> 144 * 145 * <ol> 146 * <li> Call {@link #findLoadedClass(String)} to check if the class has 147 * already been loaded. <p> 148 * <li> Call the <code>loadClass</code> method on the parent class 149 * loader. If the parent is <code>null</code> the class loader 150 * built-in to the virtual machine is used, instead. <p> 151 * <li> Call the {@link #findClass(String)} method to find the class. <p> 152 * </ol> 153 * 154 * If the class was found using the above steps, and the 155 * <code>resolve</code> flag is true, this method will then call the 156 * {@link #resolveClass(Class)} method on the resulting class object. 157 * <p> 158 * From the Java 2 SDK, v1.2, subclasses of ClassLoader are 159 * encouraged to override 160 * {@link #findClass(String)}, rather than this method.<p> 161 * 162 * @param name the name of the class 163 * @param resolve if <code>true</code> then resolve the class 164 * @return the resulting <code>Class</code> object 165 * @exception ClassNotFoundException if the class could not be found 166 */ 167 protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { 168 // First, check if the class has already been loaded 169 Class c = findLoadedClass(name); 170 if (c == null) { 171 c=findClassEL(name); 172 if(c==null) { 173 c = pcl.loadClass(name); 174 } 175 } 176 if (resolve) { 177 resolveClass(c); 178 } 179 return c; 180 } 181 182 /** 183 * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String) 184 */ 185 public InputStream getResourceAsStream(String name) { 186 name = name.replace('\\', '/'); 187 188 byte[] bytes = (byte[])resources.get(name); 189 if (bytes == null) return super.getResourceAsStream(name); 190 return new ByteArrayInputStream(bytes); 191 } 192} 193