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    }