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