001    package railo.commons.io.res.util;
002    
003    import java.io.ByteArrayInputStream;
004    import java.io.Closeable;
005    import java.io.IOException;
006    import java.io.InputStream;
007    import java.net.URL;
008    import java.util.ArrayList;
009    import java.util.zip.ZipEntry;
010    import java.util.zip.ZipFile;
011    
012    import railo.commons.io.IOUtil;
013    import railo.commons.io.res.Resource;
014    import railo.commons.lang.SizeOf;
015    import railo.runtime.type.Sizeable;
016    
017    public final class RCL extends ClassLoader implements Sizeable,Closeable {
018        
019        private final ClassLoader pcl;
020            private ZipFile[] zips;
021        
022        /**
023         * constructor of the class
024         * @param file
025         * @param parent
026         * @throws IOException
027         */
028        public RCL(Resource[] files, ClassLoader parent) throws IOException {
029            super(parent);
030            this.pcl=parent;
031            
032            ArrayList list=new ArrayList();
033            ZipFile zip;
034            for(int i=0;i<files.length;i++){
035                    zip=toZip(files[i]);
036                    if(zip!=null)
037                            list.add(zip);
038            }
039            zips=(ZipFile[]) list.toArray(new ZipFile[list.size()]);
040        }
041        
042        private ZipFile toZip(Resource file) {
043            if(!file.exists())
044                return null;
045            if(!file.isFile())
046                return null;
047            if(!file.isReadable())
048                return null;
049            try {
050                            return new ZipFile(FileWrapper.toFile(file));
051                    } catch (Exception e) {
052                            return null;
053                    }
054            }
055    
056            /**
057         * Loads the class with the specified name. This method searches for 
058         * classes in the same manner as the {@link #loadClass(String, boolean)} 
059         * method. It is called by the Java virtual machine to resolve class 
060         * references. Calling this method is equivalent to calling 
061         * <code>loadClass(name, false)</code>.
062         *
063         * @param     name the name of the class
064         * @return    the resulting <code>Class</code> object
065         * @exception ClassNotFoundException if the class was not found
066         */
067       public Class loadClass(String name) throws ClassNotFoundException   {
068           return loadClass(name, false);
069       }
070    
071        /**
072         * Loads the class with the specified name.  The default implementation of
073         * this method searches for classes in the following order:<p>
074         *
075         * <ol>
076         * <li> Call {@link #findLoadedClass(String)} to check if the class has
077         *      already been loaded. <p>
078         * <li> Call the <code>loadClass</code> method on the parent class
079         *      loader.  If the parent is <code>null</code> the class loader
080         *      built-in to the virtual machine is used, instead. <p>
081         * <li> Call the {@link #findClass(String)} method to find the class. <p>
082         * </ol>
083         *
084         * If the class was found using the above steps, and the
085         * <code>resolve</code> flag is true, this method will then call the
086         * {@link #resolveClass(Class)} method on the resulting class object.
087         * <p>
088         * From the Java 2 SDK, v1.2, subclasses of ClassLoader are 
089         * encouraged to override
090         * {@link #findClass(String)}, rather than this method.<p>
091         *
092         * @param     name the name of the class
093         * @param     resolve if <code>true</code> then resolve the class
094         * @return   the resulting <code>Class</code> object
095         * @exception ClassNotFoundException if the class could not be found
096         */
097        protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
098            // First, check if the class has already been loaded
099            Class c = findLoadedClass(name);
100            if (c == null) {
101                c=findClassEL(name);
102                if(c==null) {
103                    c =pcl.loadClass(name);
104                }
105            }
106            if (resolve) {
107                resolveClass(c);
108            }
109            return c;
110       }
111        
112        /**
113         * @see java.lang.ClassLoader#findClass(java.lang.String)
114         */
115        protected Class findClass(String name) throws ClassNotFoundException {
116            Class clazz=findClassEL(name);
117            if(clazz!=null) return clazz;
118            throw new ClassNotFoundException("class "+name+" not found");
119        }
120        
121        private Class findClassEL(String name) {
122            byte[] barr = getBytes(name.replace('.','/').concat(".class"));
123            if(barr!=null) {
124                try {
125                    return defineClass(name,barr,0,barr.length);
126                }
127                catch(Throwable t) {}
128            }
129            return null;
130        }
131    
132        /**
133         * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String)
134         */
135        public InputStream getResourceAsStream(String name) {
136            InputStream is = super.getResourceAsStream(name);
137            if(is!=null) return is;
138            
139            byte[] barr = getBytes(name);
140            if(barr!=null) return new ByteArrayInputStream(barr);
141            
142            return null;
143        }
144        
145        /** 
146         * @see java.lang.ClassLoader#getResource(java.lang.String) 
147         */ 
148        public URL getResource(String name) { 
149            return null; 
150        } 
151    
152        private byte[] getBytes(String name) {
153            ZipEntry entry=null;
154            for(int i=0;i<zips.length;i++){
155                    entry= zips[i].getEntry(name);
156                    if(entry!=null) {
157                            return getBytes(zips[i], entry);
158                    }
159            }
160            return null;
161        }
162    
163        private byte[] getBytes(ZipFile zip,ZipEntry entry) {
164            
165            if (entry == null)return null;
166            
167            int size= (int) entry.getSize();
168            InputStream is=null;
169            try {
170                is= zip.getInputStream(entry);
171                byte[] data= new byte[size];
172                int pos= 0;
173                while (pos < size) {
174                    int n= is.read(data, pos, data.length - pos);
175                    pos += n;
176                }
177                return data;
178            }
179            catch(IOException ioe) {}
180            finally {
181                IOUtil.closeEL(is);
182            }
183            return null;
184        }
185    
186            /**
187             * @see railo.runtime.type.Sizeable#sizeOf()
188             */
189            public long sizeOf() {
190                    return SizeOf.size(zips);
191            }
192    
193            public void close() throws IOException {
194                    for(int i=0;i<zips.length;i++){
195                            closeEL(zips[i]);
196                    }
197            }
198    
199            private static void closeEL(ZipFile zipFile) {
200                    try {
201                            if(zipFile!=null)zipFile.close();
202                    } catch (IOException e) {}
203            } 
204    }