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