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        @Override
113        protected Class findClass(String name) throws ClassNotFoundException {
114            Class clazz=findClassEL(name);
115            if(clazz!=null) return clazz;
116            throw new ClassNotFoundException("class "+name+" not found");
117        }
118        
119        private Class findClassEL(String name) {
120            byte[] barr = getBytes(name.replace('.','/').concat(".class"));
121            if(barr!=null) {
122                try {
123                    return defineClass(name,barr,0,barr.length);
124                }
125                catch(Throwable t) {}
126            }
127            return null;
128        }
129    
130        @Override
131        public InputStream getResourceAsStream(String name) {
132            InputStream is = super.getResourceAsStream(name);
133            if(is!=null) return is;
134            
135            byte[] barr = getBytes(name);
136            if(barr!=null) return new ByteArrayInputStream(barr);
137            
138            return null;
139        }
140        
141        @Override 
142        public URL getResource(String name) { 
143            return null; 
144        } 
145    
146        private byte[] getBytes(String name) {
147            ZipEntry entry=null;
148            for(int i=0;i<zips.length;i++){
149                    entry= zips[i].getEntry(name);
150                    if(entry!=null) {
151                            return getBytes(zips[i], entry);
152                    }
153            }
154            return null;
155        }
156    
157        private byte[] getBytes(ZipFile zip,ZipEntry entry) {
158            
159            if (entry == null)return null;
160            
161            int size= (int) entry.getSize();
162            InputStream is=null;
163            try {
164                is= zip.getInputStream(entry);
165                byte[] data= new byte[size];
166                int pos= 0;
167                while (pos < size) {
168                    int n= is.read(data, pos, data.length - pos);
169                    pos += n;
170                }
171                return data;
172            }
173            catch(IOException ioe) {}
174            finally {
175                IOUtil.closeEL(is);
176            }
177            return null;
178        }
179    
180            @Override
181            public long sizeOf() {
182                    return SizeOf.size(zips);
183            }
184    
185            public void close() throws IOException {
186                    for(int i=0;i<zips.length;i++){
187                            closeEL(zips[i]);
188                    }
189            }
190    
191            private static void closeEL(ZipFile zipFile) {
192                    try {
193                            if(zipFile!=null)zipFile.close();
194                    } catch (IOException e) {}
195            } 
196    }