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