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