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