package railo.loader.classloader; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Hashtable; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import railo.loader.util.Util; /** * * This class implements a simple class loader * that can be used to load at runtime * classes contained in a JAR file. */ public final class RailoClassLoader extends ClassLoader { private Hashtable classes = new Hashtable(); private Hashtable resources = new Hashtable(); private ClassLoader pcl; /** * Creates a new JarClassLoader that will allow the loading * of classes stored in a jar file. * * @param jarFile the name of the jar file * @param parent parent class loader * @throws IOException * @exception IOException an error happened while reading * the contents of the jar file */ public RailoClassLoader(File jarFile, ClassLoader parent) throws IOException { this(new FileInputStream(jarFile),parent,isSecure(jarFile)); } private static boolean isSecure(File jarFile) { if(jarFile.getName().toLowerCase().endsWith(".rc")) return false; return true; } public RailoClassLoader(InputStream jar, ClassLoader parent, boolean secured) throws IOException { super(parent); if(secured) throw new IOException("secured core files are not supported"); this.pcl=parent; ZipInputStream zis = new ZipInputStream(new BufferedInputStream(jar)); try { byte[] buffer = new byte[0xffff]; int bytes_read; ZipEntry ze; byte[] barr; while((ze = zis.getNextEntry()) != null) { if (!ze.isDirectory()) { ByteArrayOutputStream baos=new ByteArrayOutputStream(); while((bytes_read = zis.read(buffer)) != -1) baos.write(buffer, 0, bytes_read); String name = ze.getName().replace('\\', '/'); barr=baos.toByteArray(); if(name.endsWith(".class")) { String className=name.substring(0,name.length()-6); className=className.replace('/','.'); classes.put(className,barr); } resources.put(name, barr); zis.closeEntry(); baos.close(); } } } finally { Util.closeEL(zis); } } /** * Looks among the contents of the jar file (cached in memory) * and tries to find and define a class, given its name. * * @param className the name of the class * @return a Class object representing our class * @exception ClassNotFoundException the jar file did not contain * a class named <code>className</code> */ public Class findClass(String className) throws ClassNotFoundException { byte[] classBytes = (byte[])classes.get(className); if (classBytes == null) throw new ClassNotFoundException("class ["+className+"] not found"); return defineClass(className, classBytes, 0, classBytes.length); } private Class findClassEL(String className) { byte[] classBytes = (byte[])classes.get(className); if (classBytes == null) return null; return defineClass(className, classBytes, 0, classBytes.length); } /** * Loads the class with the specified name. This method searches for * classes in the same manner as the "loadClass(String, boolean)" * method. It is called by the Java virtual machine to resolve class * references. Calling this method is equivalent to calling * <code>loadClass(name, false)</code>. * * @param name the name of the class * @return the resulting <code>Class</code> object * @exception ClassNotFoundException if the class was not found */ public Class loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); } /** * Loads the class with the specified name. The default implementation of * this method searches for classes in the following order:<p> * * <ol> * <li> Call {@link #findLoadedClass(String)} to check if the class has * already been loaded. <p> * <li> Call the <code>loadClass</code> method on the parent class * loader. If the parent is <code>null</code> the class loader * built-in to the virtual machine is used, instead. <p> * <li> Call the {@link #findClass(String)} method to find the class. <p> * </ol> * * If the class was found using the above steps, and the * <code>resolve</code> flag is true, this method will then call the * {@link #resolveClass(Class)} method on the resulting class object. * <p> * From the Java 2 SDK, v1.2, subclasses of ClassLoader are * encouraged to override * {@link #findClass(String)}, rather than this method.<p> * * @param name the name of the class * @param resolve if <code>true</code> then resolve the class * @return the resulting <code>Class</code> object * @exception ClassNotFoundException if the class could not be found */ protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { c=findClassEL(name); if(c==null) { c = pcl.loadClass(name); } } if (resolve) { resolveClass(c); } return c; } /** * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String) */ public InputStream getResourceAsStream(String name) { name = name.replace('\\', '/'); byte[] bytes = (byte[])resources.get(name); if (bytes == null) return super.getResourceAsStream(name); return new ByteArrayInputStream(bytes); } }