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