001    package railo.commons.lang;
002    
003    import java.io.IOException;
004    import java.io.InputStream;
005    import java.util.HashMap;
006    import java.util.LinkedList;
007    import java.util.Map;
008    
009    import railo.commons.io.res.Resource;
010    import railo.runtime.MappingImpl;
011    import railo.runtime.PageSourceImpl;
012    import railo.runtime.type.util.StructUtil;
013    
014    /**
015     * Directory ClassLoader
016     */
017    public final class PCLCollection {
018        
019        private Resource directory;
020        private ClassLoader pcl;
021    
022            private int maxBlockSize;
023            private MappingImpl mapping;
024            private LinkedList<PCLBlock> cfcs=new LinkedList<PCLBlock>();
025            private LinkedList<PCLBlock> cfms=new LinkedList<PCLBlock>();
026            private PCLBlock cfc;
027            private PCLBlock cfm;
028            private Map<String,PCLBlock> index=new HashMap<String, PCLBlock>();
029    
030        /**
031         * Constructor of the class
032         * @param directory
033         * @param parent
034         * @throws IOException
035         */
036        public PCLCollection(MappingImpl mapping,Resource directory, ClassLoader parent, int maxBlockSize) throws IOException {
037            // check directory
038            if(!directory.exists())
039                directory.mkdirs();
040            
041            if(!directory.isDirectory())
042                throw new IOException("resource "+directory+" is not a directory");
043            if(!directory.canRead())
044                throw new IOException("no access to "+directory+" directory");
045            
046            this.directory=directory;
047            this.mapping=mapping;
048            this.pcl=parent;
049            cfc=new PCLBlock(directory, parent);
050            cfcs.add(cfc);
051            cfm=new PCLBlock(directory, parent);
052            cfms.add(cfm);
053            this.maxBlockSize=100;//maxBlockSize;
054        }
055        
056    
057        private PCLBlock current(boolean isCFC) {
058            if((isCFC?cfc.count():cfm.count())>=maxBlockSize) {
059                    synchronized (isCFC?cfcs:cfms) {
060                            if(isCFC) {
061                                    cfc=new PCLBlock(directory, pcl);
062                                    cfcs.add(cfc);
063                            }
064                            else {
065                                    cfm=new PCLBlock(directory, pcl);
066                                    cfms.add(cfm);
067                            }
068                            }
069            }
070                    return isCFC?cfc:cfm;
071            }
072        
073        
074    
075        public synchronized Class<?> loadClass(String name, byte[] barr, boolean isCFC) throws ClassNotFoundException   {
076            // if class is already loaded flush the classloader and do new classloader
077            PCLBlock cl = index.get(name);
078            if(cl!=null) {
079                    // if can upate class
080                    /*if(InstrumentationUtil.isSupported()){
081                            try{
082                                    Class<?> old = cl.loadClass(name);
083                            InstrumentationUtil.redefineClass(old, barr);
084                            return old;
085                            }
086                            catch(Throwable t){
087                                    t.printStackTrace();
088                            }
089                    }*/
090                    
091                    // flash classloader when update is not possible
092                    mapping.clearPages(cl);
093                    StructUtil.removeValue(index,cl);
094                    if(isCFC){
095                    cfcs.remove(cl);
096                            if(cl==cfc) cfc=new PCLBlock(directory, pcl);
097                    }
098                    else {
099                    cfms.remove(cl);
100                            if(cl==cfm) cfm=new PCLBlock(directory, pcl);
101                    }
102                    
103            }
104            
105            // load class from byte array
106            PCLBlock c = current(isCFC);
107            index.put(name, c);
108            return c.loadClass(name, barr);
109        }
110    
111        public synchronized Class<?> getClass(PageSourceImpl ps) throws ClassNotFoundException {
112            String name=ps.getClazz();
113            PCLBlock cl = index.get(name);
114            if(cl==null) {
115                    cl=current(ps.isComponent());
116                    Class<?> clazz = cl.loadClass(name);
117                    index.put(name, cl);
118                    return clazz;
119            }
120            return cl.loadClass(name);      
121        }
122    
123        public synchronized InputStream getResourceAsStream(String name) {
124            return current(false).getResourceAsStream(name);
125        }
126    
127            public long count() {
128                    return index.size();
129            }
130            
131            /**
132             * shrink the classloader elements
133             * @return how many page have removed from classloaders
134             */
135    
136            public synchronized int shrink(boolean force){
137                    int before=index.size();
138                    
139                    // CFM
140                    int flushCFM=0;
141                    while(cfms.size()>1) {
142                            flush(cfms.poll());
143                            flushCFM++;
144                    }
145            
146                    // CFC
147                    if(force && flushCFM<2 && cfcs.size()>1) {
148                            flush(oldest(cfcs));
149                            if(cfcs.size()>1)flush(cfcs.poll());
150                    }
151                    //print.o("shrink("+mapping.getVirtual()+"):"+(before-index.size())+">"+force+";"+(flushCFM));
152            return before-index.size();
153            }
154    
155            private static PCLBlock oldest(LinkedList<PCLBlock> queue) {
156                    int index=NumberUtil.randomRange(0,queue.size()-2);
157                    return queue.remove(index);
158                    //return queue.poll();
159            }
160    
161    
162            private void flush(PCLBlock cl) {
163                    mapping.clearPages(cl);
164                    StructUtil.removeValue(index,cl);
165                    //System.gc(); gc is in Controller call, to make sure gc is only called once 
166            }
167    }