001 package railo.runtime.engine; 002 003 import java.io.IOException; 004 import java.io.PrintWriter; 005 import java.util.Map; 006 007 import railo.commons.io.IOUtil; 008 import railo.commons.io.SystemUtil; 009 import railo.commons.io.res.Resource; 010 import railo.commons.io.res.filter.ExtensionResourceFilter; 011 import railo.commons.io.res.filter.ResourceFilter; 012 import railo.commons.io.res.util.ResourceUtil; 013 import railo.commons.lang.PCLCollection; 014 import railo.commons.lang.SystemOut; 015 import railo.commons.lang.types.RefBoolean; 016 import railo.runtime.CFMLFactoryImpl; 017 import railo.runtime.Mapping; 018 import railo.runtime.MappingImpl; 019 import railo.runtime.PageSource; 020 import railo.runtime.PageSourcePool; 021 import railo.runtime.config.ConfigImpl; 022 import railo.runtime.config.ConfigServer; 023 import railo.runtime.config.ConfigServerImpl; 024 import railo.runtime.config.ConfigWeb; 025 import railo.runtime.config.ConfigWebImpl; 026 import railo.runtime.lock.LockManagerImpl; 027 import railo.runtime.net.smtp.SMTPConnectionPool; 028 import railo.runtime.op.Caster; 029 import railo.runtime.type.scope.ScopeContext; 030 import railo.runtime.type.scope.client.ClientFile; 031 import railo.runtime.type.util.ArrayUtil; 032 033 /** 034 * own thread how check the main thread and his data 035 */ 036 public final class Controler extends Thread { 037 038 private int interval; 039 private long lastMinuteInterval=System.currentTimeMillis(); 040 private long lastHourInterval=System.currentTimeMillis(); 041 042 private final Map contextes; 043 private final RefBoolean run; 044 //private ScheduleThread scheduleThread; 045 private final ConfigServer configServer; 046 047 /** 048 * @param contextes 049 * @param interval 050 * @param run 051 */ 052 public Controler(ConfigServer configServer,Map contextes,int interval, RefBoolean run) { 053 this.contextes=contextes; 054 this.interval=interval; 055 this.run=run; 056 this.configServer=configServer; 057 058 059 // Register Memory Notification Listener 060 //MemoryControler.init(configServer); 061 062 } 063 064 /** 065 * @see java.lang.Runnable#run() 066 */ 067 public void run() { 068 //scheduleThread.start(); 069 boolean firstRun=true; 070 071 CFMLFactoryImpl factories[]=null; 072 while(run.toBooleanValue()) { 073 try { 074 sleep(interval); 075 } 076 catch (InterruptedException e) { 077 e.printStackTrace(); 078 } 079 long now = System.currentTimeMillis(); 080 //print.out("now:"+new Date(now)); 081 boolean doMinute=lastMinuteInterval+60000<now; 082 if(doMinute)lastMinuteInterval=now; 083 boolean doHour=(lastHourInterval+(1000*60*60))<now; 084 if(doHour)lastHourInterval=now; 085 086 // if(doMinute) System.gc(); 087 // broadcast cluster scope 088 factories=toFactories(factories,contextes); 089 try { 090 ScopeContext.getClusterScope(configServer,true).broadcast(); 091 } 092 catch (Throwable t) { 093 t.printStackTrace(); 094 } 095 096 if(doHour) { 097 try{ConfigServerImpl.checkPermGenSpace(configServer,true);}catch(Throwable t){} 098 } 099 100 for(int i=0;i<factories.length;i++) { 101 run(factories[i], doMinute, doHour,firstRun); 102 } 103 if(factories.length>0) 104 firstRun=false; 105 } 106 } 107 private CFMLFactoryImpl[] toFactories(CFMLFactoryImpl[] factories,Map contextes) { 108 if(factories==null || factories.length!=contextes.size()) 109 factories=(CFMLFactoryImpl[]) contextes.values().toArray(new CFMLFactoryImpl[contextes.size()]); 110 111 return factories; 112 } 113 114 private void run(CFMLFactoryImpl cfmlFactory, boolean doMinute, boolean doHour, boolean firstRun) { 115 116 try { 117 boolean isRunning=cfmlFactory.getUsedPageContextLength()>0; 118 if(isRunning) { 119 cfmlFactory.checkTimeout(); 120 } 121 ConfigWeb config = null; 122 123 if(firstRun) { 124 if(config==null) { 125 config = cfmlFactory.getConfig(); 126 ThreadLocalConfig.register(config); 127 128 } 129 config.reloadTimeServerOffset(); 130 checkOldClientFile(config); 131 132 //try{checkStorageScopeFile(config,Session.SCOPE_CLIENT);}catch(Throwable t){} 133 //try{checkStorageScopeFile(config,Session.SCOPE_SESSION);}catch(Throwable t){} 134 try{config.reloadTimeServerOffset();}catch(Throwable t){} 135 try{checkTempDirectorySize(config);}catch(Throwable t){} 136 try{checkCacheFileSize(config);}catch(Throwable t){} 137 try{cfmlFactory.getScopeContext().clearUnused();}catch(Throwable t){} 138 } 139 140 if(config==null) { 141 config = cfmlFactory.getConfig(); 142 ThreadLocalConfig.register(config); 143 } 144 145 //every Minute 146 if(doMinute) { 147 if(config==null) { 148 config = cfmlFactory.getConfig(); 149 ThreadLocalConfig.register(config); 150 } 151 // clear unused DB Connections 152 try{((ConfigImpl)config).getDatasourceConnectionPool().clear();}catch(Throwable t){} 153 // clear all unused scopes 154 try{cfmlFactory.getScopeContext().clearUnused();}catch(Throwable t){} 155 // Memory usage 156 // clear Query Cache 157 try{cfmlFactory.getQueryCache().clearUnused();}catch(Throwable t){} 158 // contract Page Pool 159 //try{doClearPagePools((ConfigWebImpl) config);}catch(Throwable t){} 160 //try{checkPermGenSpace((ConfigWebImpl) config);}catch(Throwable t){} 161 try{doCheckMappings(config);}catch(Throwable t){} 162 try{doClearMailConnections();}catch(Throwable t){} 163 // clean LockManager 164 if(cfmlFactory.getUsedPageContextLength()==0)try{((LockManagerImpl)config.getLockManager()).clean();}catch(Throwable t){} 165 166 } 167 // every hour 168 if(doHour) { 169 if(config==null) { 170 config = cfmlFactory.getConfig(); 171 ThreadLocalConfig.register(config); 172 } 173 // time server offset 174 try{config.reloadTimeServerOffset();}catch(Throwable t){} 175 // check file based client/session scope 176 //try{checkStorageScopeFile(config,Session.SCOPE_CLIENT);}catch(Throwable t){} 177 //try{checkStorageScopeFile(config,Session.SCOPE_SESSION);}catch(Throwable t){} 178 // check temp directory 179 try{checkTempDirectorySize(config);}catch(Throwable t){} 180 // check cache directory 181 try{checkCacheFileSize(config);}catch(Throwable t){} 182 } 183 } 184 catch(Throwable t){ 185 186 } 187 finally{ 188 ThreadLocalConfig.release(); 189 } 190 } 191 192 private void doClearMailConnections() { 193 SMTPConnectionPool.closeSessions(); 194 } 195 196 private void checkOldClientFile(ConfigWeb config) { 197 ExtensionResourceFilter filter = new ExtensionResourceFilter(".script",false); 198 199 // move old structured file in new structure 200 try { 201 Resource dir = config.getClientScopeDir(),trgres; 202 Resource[] children = dir.listResources(filter); 203 String src,trg; 204 int index; 205 for(int i=0;i<children.length;i++) { 206 src=children[i].getName(); 207 index=src.indexOf('-'); 208 209 trg=ClientFile.getFolderName(src.substring(0,index), src.substring(index+1),false); 210 trgres=dir.getRealResource(trg); 211 if(!trgres.exists()){ 212 trgres.createFile(true); 213 ResourceUtil.copy(children[i],trgres); 214 } 215 //children[i].moveTo(trgres); 216 children[i].delete(); 217 218 } 219 } catch (Throwable t) {} 220 } 221 222 223 private void checkCacheFileSize(ConfigWeb config) { 224 checkSize(config,config.getCacheDir(),config.getCacheDirSize(),new ExtensionResourceFilter(".cache")); 225 } 226 227 private void checkTempDirectorySize(ConfigWeb config) { 228 checkSize(config,config.getTempDirectory(),1024*1024*1024,null); 229 } 230 231 private void checkSize(ConfigWeb config,Resource dir,long maxSize, ResourceFilter filter) { 232 if(!dir.exists()) return; 233 Resource res=null; 234 int count=ArrayUtil.size(filter==null?dir.list():dir.list(filter)); 235 long size=ResourceUtil.getRealSize(dir,filter); 236 PrintWriter out = config.getOutWriter(); 237 SystemOut.printDate(out,"check size of directory ["+dir+"]"); 238 SystemOut.printDate(out,"- current size ["+size+"]"); 239 SystemOut.printDate(out,"- max size ["+maxSize+"]"); 240 int len=-1; 241 while(count>100000 || size>maxSize) { 242 Resource[] files = filter==null?dir.listResources():dir.listResources(filter); 243 if(len==files.length) break;// protect from inifinti loop 244 len=files.length; 245 for(int i=0;i<files.length;i++) { 246 if(res==null || res.lastModified()>files[i].lastModified()) { 247 res=files[i]; 248 } 249 } 250 if(res!=null) { 251 size-=res.length(); 252 try { 253 res.remove(true); 254 count--; 255 } catch (IOException e) { 256 SystemOut.printDate(out,"cannot remove resource "+res.getAbsolutePath()); 257 break; 258 } 259 } 260 res=null; 261 } 262 263 } 264 265 private void doCheckMappings(ConfigWeb config) { 266 Mapping[] mappings = config.getMappings(); 267 for(int i=0;i<mappings.length;i++) { 268 Mapping mapping = mappings[i]; 269 mapping.check(); 270 } 271 } 272 273 private PageSourcePool[] getPageSourcePools(ConfigWeb config) { 274 return getPageSourcePools(config.getMappings()); 275 } 276 277 private PageSourcePool[] getPageSourcePools(Mapping... mappings) { 278 PageSourcePool[] pools=new PageSourcePool[mappings.length]; 279 int size=0; 280 281 for(int i=0;i<mappings.length;i++) { 282 pools[i]=((MappingImpl)mappings[i]).getPageSourcePool(); 283 size+=pools[i].size(); 284 } 285 return pools; 286 } 287 private int getPageSourcePoolSize(PageSourcePool[] pools) { 288 int size=0; 289 for(int i=0;i<pools.length;i++)size+=pools[i].size(); 290 return size; 291 } 292 private void removeOldest(PageSourcePool[] pools) { 293 PageSourcePool pool=null; 294 Object key=null; 295 PageSource ps=null; 296 297 long date=-1; 298 for(int i=0;i<pools.length;i++) { 299 try { 300 Object[] keys=pools[i].keys(); 301 for(int y=0;y<keys.length;y++) { 302 ps = pools[i].getPageSource(keys[y],false); 303 if(date==-1 || date>ps.getLastAccessTime()) { 304 pool=pools[i]; 305 key=keys[y]; 306 date=ps.getLastAccessTime(); 307 } 308 } 309 } 310 catch(Throwable t) { 311 pools[i].clear(); 312 } 313 314 } 315 if(pool!=null)pool.remove(key); 316 } 317 private void clear(PageSourcePool[] pools) { 318 for(int i=0;i<pools.length;i++) { 319 pools[i].clear(); 320 } 321 } 322 323 /*private void doLogMemoryUsage(ConfigWeb config) { 324 if(config.logMemoryUsage()&& config.getMemoryLogger()!=null) 325 config.getMemoryLogger().write(); 326 }*/ 327 328 329 static class ExpiresFilter implements ResourceFilter { 330 331 private long time; 332 private boolean allowDir; 333 334 public ExpiresFilter(long time, boolean allowDir) { 335 this.allowDir=allowDir; 336 this.time=time; 337 } 338 339 public boolean accept(Resource res) { 340 341 if(res.isDirectory()) return allowDir; 342 343 // load content 344 String str=null; 345 try { 346 str = IOUtil.toString(res,"UTF-8"); 347 } 348 catch (IOException e) { 349 return false; 350 } 351 352 int index=str.indexOf(':'); 353 if(index!=-1){ 354 long expires=Caster.toLongValue(str.substring(0,index),-1L); 355 // check is for backward compatibility, old files have no expires date inside. they do ot expire 356 if(expires!=-1) { 357 if(expires<System.currentTimeMillis()){ 358 return true; 359 } 360 else { 361 str=str.substring(index+1); 362 return false; 363 } 364 } 365 } 366 // old files not having a timestamp inside 367 else if(res.lastModified()<=time) { 368 return true; 369 370 } 371 return false; 372 } 373 374 } 375 376 377 }