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