001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.lang;
020
021import java.io.ByteArrayOutputStream;
022import java.io.IOException;
023import java.io.InputStream;
024import java.net.URL;
025
026import lucee.commons.io.IOUtil;
027import lucee.commons.io.SystemUtil;
028import lucee.commons.io.res.Resource;
029import lucee.runtime.type.Sizeable;
030
031/**
032 * Directory ClassLoader
033 */
034public final class PCLBlock extends ExtendableClassLoader implements Sizeable  {
035    
036    private Resource directory;
037    private ClassLoader pcl;
038        private int size=0;
039        private int count;
040        
041
042    /**
043     * Constructor of the class
044     * @param directory
045     * @param parent
046     * @throws IOException
047     */
048    public PCLBlock(Resource directory, ClassLoader parent) {
049        super(parent);
050        this.pcl=parent;
051        this.directory=directory;
052    }
053    
054    /**
055     * Loads the class with the specified name. This method searches for 
056     * classes in the same manner as the {@link #loadClass(String, boolean)} 
057     * method. It is called by the Java virtual machine to resolve class 
058     * references. Calling this method is equivalent to calling 
059     * <code>loadClass(name, false)</code>.
060     *
061     * @param     name the name of the class
062     * @return    the resulting <code>Class</code> object
063     * @exception ClassNotFoundException if the class was not found
064     */
065   public Class<?> loadClass(String name) throws ClassNotFoundException   {
066           return loadClass(name, false);
067   }//15075171
068
069    /**
070     * Loads the class with the specified name.  The default implementation of
071     * this method searches for classes in the following order:<p>
072     *
073     * <ol>
074     * <li> Call {@link #findLoadedClass(String)} to check if the class has
075     *      already been loaded. <p>
076     * <li> Call the <code>loadClass</code> method on the parent class
077     *      loader.  If the parent is <code>null</code> the class loader
078     *      built-in to the virtual machine is used, instead. <p>
079     * <li> Call the {@link #findClass(String)} method to find the class. <p>
080     * </ol>
081     *
082     * If the class was found using the above steps, and the
083     * <code>resolve</code> flag is true, this method will then call the
084     * {@link #resolveClass(Class)} method on the resulting class object.
085     * <p>
086     * From the Java 2 SDK, v1.2, subclasses of ClassLoader are 
087     * encouraged to override
088     * {@link #findClass(String)}, rather than this method.<p>
089     *
090     * @param     name the name of the class
091     * @param     resolve if <code>true</code> then resolve the class
092     * @return   the resulting <code>Class</code> object
093     * @exception ClassNotFoundException if the class could not be found
094     */
095    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
096        //if(!name.endsWith("$cf")) return super.loadClass(name, resolve); this break Webervices
097        // First, check if the class has already been loaded
098        Class<?> c = findLoadedClass(name);
099        //print.o("load:"+name+" -> "+c);
100        if (c == null) {
101            try {
102                c =pcl.loadClass(name);//if(name.indexOf("sub")!=-1)print.ds(name);
103            } 
104            catch (Throwable t) {
105                ExceptionUtil.rethrowIfNecessary(t);
106                c = findClass(name);
107            }
108        }
109        if (resolve) {
110            resolveClass(c);
111        }
112        return c;
113   }
114    
115    
116
117    
118    public static long lastModified(Resource res, long defaultValue)  {
119        InputStream in = null;
120        try{
121                in=res.getInputStream();
122                byte[] buffer = new byte[10];
123                in.read(buffer);
124                if(!ClassUtil.isEncryptedBytecode(buffer)) return defaultValue;
125                
126                 byte[] _buffer = new byte[]{
127                                 buffer[2],
128                                 buffer[3],
129                                 buffer[4],
130                                 buffer[5],
131                                 buffer[6],
132                                 buffer[7],
133                                 buffer[8],
134                                 buffer[9],
135                 };
136                
137                
138                return NumberUtil.byteArrayToLong(_buffer);
139        }
140        catch(IOException ioe){
141                return defaultValue;
142        }
143        finally {
144                IOUtil.closeEL(in);
145        }
146        
147    }
148    
149    @Override
150    protected Class<?> findClass(String name) throws ClassNotFoundException {//if(name.indexOf("sub")!=-1)print.ds(name);
151        //if(!name.endsWith("$cf")) return super.findClass(name); this break Webervices
152        //File f = getFile(name.replace('.',File.separatorChar).concat(".class"));
153        //print.e("directory:"+directory+"->"+name);
154        Resource res=directory
155        .getRealResource(
156                        name.replace('.','/')
157                        .concat(".class"));
158        //File f = new File(directory,name.replace('.',File.separatorChar).concat(".class"));
159        //if(f==null) throw new ClassNotFoundException("class "+name+" not found");
160        
161        ByteArrayOutputStream baos = new ByteArrayOutputStream();
162        try {
163            IOUtil.copy(res,baos,false);
164        } 
165        catch (IOException e) {
166                throw new ClassNotFoundException("class "+name+" is invalid or doesn't exist");
167        }
168        
169        byte[] barr=baos.toByteArray();
170        size+=barr.length;
171        count++;
172        //print.o(name+":"+count+" -> "+(size/1024));
173        IOUtil.closeEL(baos);
174        return loadClass(name, barr);
175        //return defineClass(name,barr,0,barr.length);
176    }
177    
178
179    public Class<?> loadClass(String name, byte[] barr)   {
180        int start=0;
181        //if(ClassUtil.hasCF33Prefix(barr)) start=10;
182        size+=barr.length-start;
183        count++;
184        try {
185                return defineClass(name,barr,start,barr.length-start);
186                } 
187        catch (Throwable t) {
188                ExceptionUtil.rethrowIfNecessary(t);
189                        SystemUtil.sleep(1);
190                        try {
191                        return defineClass(name,barr,start,barr.length-start);
192                        } 
193                        catch (Throwable t2) {
194                ExceptionUtil.rethrowIfNecessary(t2);
195                                SystemUtil.sleep(1);
196                                return defineClass(name,barr,start,barr.length-start);
197                        }
198                }
199        //return loadClass(name,false);
200    }
201    
202    @Override
203    public URL getResource(String name) {
204        /*URL url=super.getResource(name);
205        if(url!=null) return url;
206        
207        Resource f =_getResource(name);
208        if(f!=null) {
209            try {
210                return f.toURL();
211            } 
212            catch (MalformedURLException e) {}
213        }*/
214        return null;
215    }
216
217    @Override
218    public InputStream getResourceAsStream(String name) {
219        InputStream is = super.getResourceAsStream(name);
220        if(is!=null) return is;
221        
222        Resource f = _getResource(name);
223        if(f!=null)  {
224            try {
225                return IOUtil.toBufferedInputStream(f.getInputStream());
226            } 
227            catch (IOException e) {}
228        }
229        return null;
230    }
231
232    /**
233     * returns matching File Object or null if file not exust
234     * @param name
235     * @return matching file
236     */
237    public Resource _getResource(String name) {
238        Resource f = directory.getRealResource(name);
239        if(f!=null && f.exists() && f.isFile()) return f;
240        return null;
241    }
242
243    public boolean hasClass(String className) {
244        return hasResource(className.replace('.','/').concat(".class"));
245    }
246    
247    public boolean isClassLoaded(String className) {
248        //print.o("isClassLoaded:"+className+"-"+(findLoadedClass(className)!=null));
249        return findLoadedClass(className)!=null;
250    }
251
252    public boolean hasResource(String name) {
253        return _getResource(name)!=null;
254    }
255
256        /**
257         * @return the directory
258         */
259        public Resource getDirectory() {
260                return directory;
261        }
262
263        @Override
264        public long sizeOf() {
265                return 0;
266        }
267        
268        public int count() {
269                return count;
270        }
271
272}