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 }