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    }