001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.loader.engine;
020
021import java.io.BufferedOutputStream;
022import java.io.ByteArrayInputStream;
023import java.io.EOFException;
024import java.io.File;
025import java.io.FileInputStream;
026import java.io.FileOutputStream;
027import java.io.IOException;
028import java.io.InputStream;
029import java.io.OutputStream;
030import java.io.PrintWriter;
031import java.io.UnsupportedEncodingException;
032import java.lang.reflect.InvocationTargetException;
033import java.lang.reflect.Method;
034import java.net.URL;
035import java.net.URLDecoder;
036import java.util.ArrayList;
037import java.util.Arrays;
038import java.util.Date;
039import java.util.Iterator;
040import java.util.List;
041
042import javax.servlet.ServletConfig;
043import javax.servlet.ServletException;
044
045import lucee.Version;
046import lucee.loader.TP;
047import lucee.loader.classloader.LuceeClassLoader;
048import lucee.loader.util.ExtensionFilter;
049import lucee.loader.util.Util;
050import lucee.loader.util.ZipUtil;
051
052import com.intergral.fusiondebug.server.FDControllerFactory;
053
054/**
055 * Factory to load CFML Engine
056 */
057public class CFMLEngineFactory {
058        
059         // set to false to disable patch loading, for example in major alpha releases
060         private static final boolean PATCH_ENABLED = true;
061         
062        private static CFMLEngineFactory factory;
063         private static CFMLEngineWrapper engineListener;
064         private CFMLEngine engine;
065         private ClassLoader mainClassLoader=new TP().getClass().getClassLoader();
066         private int version;
067         private List<EngineChangeListener> listeners=new ArrayList<EngineChangeListener>();
068         
069         
070         private static File luceeServerRoot;
071         private File resourceRoot;
072
073        private PrintWriter out;
074         
075         
076         /**
077        * Constructor of the class
078        */
079         protected CFMLEngineFactory(){
080         }
081
082         /**
083        * returns instance of this factory (singelton-> always the same instance)
084        * do auto update when changes occur
085        * @param config 
086        * @return Singelton Instance of the Factory
087        * @throws ServletException 
088        */
089         public static CFMLEngine getInstance(ServletConfig config) throws ServletException {
090                
091                if(engineListener!=null) {
092                        if(factory==null) factory=engineListener.getCFMLEngineFactory();
093                        return engineListener;
094                }
095                
096                if(factory==null) factory=new CFMLEngineFactory();
097                
098                
099                // read init param from config
100                factory.setInitParam(config);
101                
102                CFMLEngine engine = factory.getEngine();
103                engine.addServletConfig(config);
104                engineListener = new CFMLEngineWrapper(engine);
105                
106                // add listener for update
107                factory.addListener(engineListener);
108                return engineListener;
109         }
110
111         /**
112        * returns instance of this factory (singelton-> always the same instance)
113        * do auto update when changes occur
114        * @return Singelton Instance of the Factory
115        * @throws RuntimeException 
116        */
117         public static CFMLEngine getInstance() throws RuntimeException {
118                if(engineListener!=null) return engineListener;
119                throw new RuntimeException("engine is not initalized, you must first call getInstance(ServletConfig)");
120         }
121
122         /**
123        * used only for internal usage
124        * @param engine
125        * @throws RuntimeException
126        */
127         public static void registerInstance(CFMLEngine engine) throws RuntimeException {
128                if(factory==null) factory=engine.getCFMLEngineFactory();
129                
130                // first update existing listener
131                if(engineListener!=null) {
132                        if(engineListener.equalTo(engine, true)) return;
133                        engineListener.onUpdate(engine);// perhaps this is still refrenced in the code, because of that we update it
134                        factory.removeListener(engineListener);
135                }
136                
137                // now register this
138                if(engine instanceof CFMLEngineWrapper) 
139                        engineListener=(CFMLEngineWrapper) engine;
140                else 
141                        engineListener = new CFMLEngineWrapper(engine);
142                
143                factory.addListener(engineListener);    
144         }
145         
146         
147         /**
148        * returns instance of this factory (singelton-> always the same instance)
149        * @param config
150        * @param listener 
151        * @return Singelton Instance of the Factory
152        * @throws ServletException 
153        */
154         public static CFMLEngine getInstance(ServletConfig config, EngineChangeListener listener) throws ServletException {
155                getInstance(config);
156                
157                // add listener for update
158                factory.addListener(listener);
159                
160                // read init param from config
161                factory.setInitParam(config);
162                
163                CFMLEngine e = factory.getEngine();
164                e.addServletConfig(config);
165                
166                // make the FDController visible for the FDClient
167                FDControllerFactory.makeVisible();
168                
169                return e;
170         }
171         
172         void setInitParam(ServletConfig config) {
173                if(luceeServerRoot!=null) return;
174                
175                String initParam=config.getInitParameter("lucee-server-directory");
176                if(Util.isEmpty(initParam))initParam=config.getInitParameter("lucee-server-root");
177                if(Util.isEmpty(initParam))initParam=config.getInitParameter("lucee-server-dir");
178                if(Util.isEmpty(initParam))initParam=config.getInitParameter("lucee-server");
179                
180                if(Util.isEmpty(initParam))initParam=config.getInitParameter("railo-server-directory");
181                if(Util.isEmpty(initParam))initParam=config.getInitParameter("railo-server-root");
182                if(Util.isEmpty(initParam))initParam=config.getInitParameter("railo-server-dir");
183                if(Util.isEmpty(initParam))initParam=config.getInitParameter("railo-server");
184                
185                
186                
187                
188                initParam=Util.parsePlaceHolder(Util.removeQuotes(initParam,true));
189                
190                try {
191                                if(!Util.isEmpty(initParam)) {
192                                         File root=new File(initParam);
193                                         if(!root.exists()) {
194                                                if(root.mkdirs()) {
195                                                                luceeServerRoot=root.getCanonicalFile();
196                                                                return;
197                                                }
198                                         }
199                                         else if(root.canWrite()) {
200                                                luceeServerRoot=root.getCanonicalFile();
201                                                return;
202                                         }
203                                }
204                }
205                catch(IOException ioe){}
206         }
207         
208
209        /**
210        * adds a listener to the factory that will be informed when a new engine will be loaded.
211        * @param listener
212        */
213         private void addListener(EngineChangeListener listener) {
214                 if(!listeners.contains(listener)) {
215                        listeners.add(listener);
216                 }
217         }
218         
219         private void removeListener(EngineChangeListener listener) {
220                listeners.remove(listener);
221        }
222
223         /**
224        * @return CFML Engine
225        * @throws ServletException
226        */
227         private CFMLEngine getEngine() throws ServletException {
228                if(engine==null)initEngine();
229                return engine;
230         }
231
232         private void initEngine() throws ServletException {
233                
234                int coreVersion=Version.getIntVersion();
235                long coreCreated=Version.getCreateTime();
236                
237                
238                // get newest lucee version as file
239                File patcheDir=null;
240                try {
241                                patcheDir = getPatchDirectory();
242                                log("lucee-server-root:"+patcheDir.getParent());
243                } 
244                catch (IOException e) {
245                        throw new ServletException(e);
246                }
247                
248                File[] patches=PATCH_ENABLED?patcheDir.listFiles(new ExtensionFilter(new String[]{"."+getCoreExtension()})):null;
249                File lucee=null;
250                if(patches!=null) {
251                                for(int i=0;i<patches.length;i++) {
252                                         if(patches[i].getName().startsWith("tmp.lco")) {
253                                                patches[i].delete();
254                                         }
255                                         else if(patches[i].lastModified()<coreCreated) {
256                                                patches[i].delete();
257                                         }
258                                         else if(lucee==null || isNewerThan(Util.toInVersion(patches[i].getName()),Util.toInVersion(lucee.getName()))) {
259                                                lucee=patches[i];
260                                         }
261                                }
262                }
263                
264                
265                if(lucee!=null && isNewerThan(coreVersion,Util.toInVersion(lucee.getName())))lucee=null;
266                
267                // Load Lucee
268                //URL url=null;
269                try {
270                                // Load core version when no patch available
271                                if(lucee==null) {
272                                        tlog("Load Build in Core");
273                                         // 
274                                         String coreExt=getCoreExtension();
275                                         engine=getCore(coreExt);
276                                        
277                                         
278                                         lucee=new File(patcheDir,engine.getVersion()+"."+coreExt);
279                                        if(PATCH_ENABLED) {
280                                                 InputStream bis = new TP().getClass().getResourceAsStream("/core/core."+coreExt);
281                                                 OutputStream bos=new BufferedOutputStream(new FileOutputStream(lucee));
282                                                 Util.copy(bis,bos);
283                                                 Util.closeEL(bis,bos);
284                                         }
285                                }
286                                else {
287                                        try {
288                                                engine=getEngine(new LuceeClassLoader(lucee,mainClassLoader));
289                                        }
290                                        catch(EOFException e) {
291                                                System.err.println("Lucee patch file "+lucee+" is invalid, please delete it");
292                                                engine=getCore(getCoreExtension());
293                                        }
294                                }
295                                version=Util.toInVersion(engine.getVersion());
296                                
297                                tlog("Loaded Lucee Version "+engine.getVersion());
298                }
299                catch(InvocationTargetException e) {
300                                e.getTargetException().printStackTrace();
301                                throw new ServletException(e.getTargetException());
302                }
303                catch(Exception e) {
304                                e.printStackTrace();
305                                throw new ServletException(e);
306                }
307                
308                //check updates
309                String updateType=engine.getUpdateType();
310                if(updateType==null || updateType.length()==0)updateType="manuell";
311                
312                if(updateType.equalsIgnoreCase("auto")) {
313                                new UpdateChecker(this).start();
314                }
315                
316         }
317         
318
319         private String getCoreExtension()  {
320                return "lco";
321        }
322
323        private CFMLEngine getCore(String ext) throws SecurityException, IllegalArgumentException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, IOException {
324                InputStream is = null;
325                try {
326                        is = new TP().getClass().getResourceAsStream("/core/core."+ext);
327                        LuceeClassLoader classLoader=new LuceeClassLoader(is,mainClassLoader,ext.equalsIgnoreCase("rcs"));
328                        return getEngine(classLoader);
329                }
330                finally {
331                        Util.closeEL(is);
332                }
333        }
334
335        /**
336        * method to initalize a update of the CFML Engine.
337        * checks if there is a new Version and update it whwn a new version is available
338        * @param password
339        * @return has updated
340        * @throws IOException
341        * @throws ServletException 
342        */
343         public boolean update(String password) throws IOException, ServletException {
344                if(!engine.can(CFMLEngine.CAN_UPDATE,password))
345                                throw new IOException("access denied to update CFMLEngine");
346                //new RunUpdate(this).start();
347                return update();
348         }
349
350         /**
351        * restart the cfml engine
352        * @param password
353        * @return has updated
354        * @throws IOException 
355        * @throws ServletException 
356        */
357         public boolean restart(String password) throws IOException, ServletException {
358                if(!engine.can(CFMLEngine.CAN_RESTART_ALL,password))
359                                throw new IOException("access denied to restart CFMLEngine");
360                
361                return _restart();
362         }
363
364         /**
365        * restart the cfml engine
366        * @param password
367        * @return has updated
368        * @throws IOException 
369        * @throws ServletException 
370        */
371         public boolean restart(String configId, String password) throws IOException, ServletException {
372                if(!engine.can(CFMLEngine.CAN_RESTART_CONTEXT,password))// TODO restart single context
373                                throw new IOException("access denied to restart CFML Context (configId:"+configId+")");
374                
375                return _restart();
376         }
377         
378         /**
379        * restart the cfml engine
380        * @param password
381        * @return has updated
382        * @throws IOException 
383        * @throws ServletException 
384        */
385         private synchronized boolean _restart() throws ServletException {
386                engine.reset();
387                initEngine();
388                registerInstance(engine);
389                callListeners(engine);
390                System.gc(); 
391                System.gc();
392                return true;
393         }
394
395        /**
396        * updates the engine when a update is available
397        * @return has updated
398        * @throws IOException
399        * @throws ServletException
400        */
401         private boolean update() throws IOException, ServletException {
402                
403                URL hostUrl=getEngine().getUpdateLocation();
404                if(hostUrl==null)hostUrl=new URL("http://stable.lucee.org");
405                URL infoUrl=new URL(hostUrl,"/lucee/remote/version/info.cfm?ext="+getCoreExtension()+"&version="+version);// FUTURE replace with Info.cfc or better move the functionality to core if possible. something like engine.getUpdater a class provided by the core and defined (interface) by the loader.
406                
407                tlog("Check for update at "+hostUrl);
408                
409                String availableVersion = Util.toString((InputStream)infoUrl.getContent()).trim();
410                
411                if(availableVersion.length()!=9) throw new IOException("can't get update info from ["+infoUrl+"]");
412                if(!isNewerThan(Util.toInVersion(availableVersion),version)) {
413                                tlog("There is no newer Version available");
414                                return false;
415                }
416                
417                tlog("Found a newer Version \n - current Version "+Util.toStringVersion(version)+"\n - available Version "+availableVersion);
418                
419                URL updateUrl=new URL(hostUrl,"/lucee/remote/version/update.cfm?ext="+getCoreExtension()+"&version="+availableVersion);
420                File patchDir=getPatchDirectory();
421                File newLucee=new File(patchDir,availableVersion+("."+getCoreExtension()));
422                
423                if(newLucee.createNewFile()) {
424                                Util.copy((InputStream)updateUrl.getContent(),new FileOutputStream(newLucee));
425                }
426                else {
427                                tlog("File for new Version already exists, won't copy new one");
428                                return false;
429                }
430                try {
431                engine.reset();
432                }
433                catch(Throwable t) {
434                        if(t instanceof ThreadDeath) throw (ThreadDeath)t;
435                        t.printStackTrace();
436                }
437                
438                // Test new lucee version valid
439                //FileClassLoader classLoader=new FileClassLoader(newLucee,mainClassLoader);
440                LuceeClassLoader classLoader=new LuceeClassLoader(newLucee,mainClassLoader);
441                //URLClassLoader classLoader=new URLClassLoader(new URL[]{newLucee.toURL()},mainClassLoader);
442                String v="";
443                try {
444                                CFMLEngine e = getEngine(classLoader);
445                                if(e==null)throw new IOException("can't load engine");
446                                v=e.getVersion();
447                                engine=e;
448                                version=Util.toInVersion(v);
449                                //e.reset();
450                                callListeners(e);
451                }
452                catch (Exception e) {
453                                classLoader=null;
454                                System.gc();
455                                try {
456                                         newLucee.delete();
457                                }
458                                catch(Exception ee){}
459                                tlog("There was a Problem with the new Version, can't install ("+e+":"+e.getMessage()+")");
460                                e.printStackTrace();
461                                return false;
462                }
463                
464                tlog("Version ("+v+")installed");
465                return true;
466         }
467         
468         
469         /**
470        * method to initalize a update of the CFML Engine.
471        * checks if there is a new Version and update it whwn a new version is available
472        * @param password
473        * @return has updated
474        * @throws IOException
475        * @throws ServletException 
476        */
477         public boolean removeUpdate(String password) throws IOException, ServletException {
478                if(!engine.can(CFMLEngine.CAN_UPDATE,password))
479                                throw new IOException("access denied to update CFMLEngine");
480                return removeUpdate();
481         }
482         
483
484         /**
485        * method to initalize a update of the CFML Engine.
486        * checks if there is a new Version and update it whwn a new version is available
487        * @param password
488        * @return has updated
489        * @throws IOException
490        * @throws ServletException 
491        */
492         public boolean removeLatestUpdate(String password) throws IOException, ServletException {
493                if(!engine.can(CFMLEngine.CAN_UPDATE,password))
494                                throw new IOException("access denied to update CFMLEngine");
495                return removeLatestUpdate();
496         }
497         
498         
499         
500         /**
501        * updates the engine when a update is available
502        * @return has updated
503        * @throws IOException
504        * @throws ServletException
505        */
506         private boolean removeUpdate() throws IOException, ServletException {
507                File patchDir=getPatchDirectory();
508                File[] patches=patchDir.listFiles(new ExtensionFilter(new String[]{getCoreExtension()}));
509                
510                for(int i=0;i<patches.length;i++) {
511                        if(!patches[i].delete())patches[i].deleteOnExit();
512                }
513                _restart();
514                return true;
515         }
516         
517
518         private boolean removeLatestUpdate() throws IOException, ServletException {
519                File patchDir=getPatchDirectory();
520                File[] patches=patchDir.listFiles(new ExtensionFilter(new String[]{"."+getCoreExtension()}));
521                File patch=null;
522                for(int i=0;i<patches.length;i++) {
523                         if(patch==null || isNewerThan(Util.toInVersion(patches[i].getName()),Util.toInVersion(patch.getName()))) {
524                                        patch=patches[i];
525                                 }
526                }
527                if(patch!=null && !patch.delete())patch.deleteOnExit();
528                
529                _restart();
530                return true;
531         }
532         
533
534        public String[] getInstalledPatches() throws ServletException, IOException {
535                File patchDir=getPatchDirectory();
536                File[] patches=patchDir.listFiles(new ExtensionFilter(new String[]{"."+getCoreExtension()}));
537                
538                List<String> list=new ArrayList<String>();
539                String name;
540                int extLen=getCoreExtension().length()+1;
541                for(int i=0;i<patches.length;i++) {
542                        name=patches[i].getName();
543                        name=name.substring(0, name.length()-extLen);
544                         list.add(name);
545                }
546                String[] arr = list.toArray(new String[list.size()]);
547                Arrays.sort(arr);
548                return arr;
549        }
550         
551
552         /**
553        * call all registred listener for update of the engine
554        * @param engine
555        */
556         private void callListeners(CFMLEngine engine) {
557                Iterator<EngineChangeListener> it = listeners.iterator();
558                while(it.hasNext()) {
559                                it.next().onUpdate(engine);
560                }
561         }
562         
563
564         private File getPatchDirectory() throws IOException {
565                File pd = new File(getResourceRoot(),"patches");
566                if(!pd.exists())pd.mkdirs();
567                return pd;
568         }
569
570         /**
571        * return directory to lucee resource root
572        * @return lucee root directory
573        * @throws IOException
574        */
575         public File getResourceRoot() throws IOException {
576                if(resourceRoot==null) {
577                        File parent = getRuningContextRoot();
578                        
579                        resourceRoot = new File(parent,"lucee-server");
580
581                        // no lucee context
582                        if(!resourceRoot.exists()) {
583                                        // check if there is a Railo context
584                                File railoRoot = new File(parent,"railo-server");
585                                        if(railoRoot.exists()) {
586                                        copyRecursiveAndRename(railoRoot,resourceRoot);
587                                        // zip the railo-server di and delete it (optional)
588                                        try {
589                                                File p=railoRoot.getParentFile().getParentFile();
590                                                if("lib".equalsIgnoreCase(p.getName()) || "libs".equalsIgnoreCase(p.getName()))
591                                                        p=p.getParentFile();
592                                                
593                                                ZipUtil.zip(railoRoot, new File(p,"railo-server-context-old.zip"));
594                                                Util.delete(railoRoot);
595                                        }
596                                        catch(Throwable t){
597                                                if(t instanceof ThreadDeath) throw (ThreadDeath)t;
598                                                t.printStackTrace();
599                                        }
600                                }
601                                else {
602                                        resourceRoot.mkdirs();
603                                }
604                        }
605                }
606                return resourceRoot;
607         }
608         
609         private static void copyRecursiveAndRename(File src,File trg) throws IOException {
610                if(!src.exists()) return ;
611                if(src.isDirectory()) {
612                        if(!trg.exists())trg.mkdirs();
613                        
614                        File[] files = src.listFiles();
615                                for(int i=0;i<files.length;i++) {
616                                        copyRecursiveAndRename(files[i],new File(trg,files[i].getName()));
617                                }
618                }
619                else if(src.isFile()) {
620                        if(trg.getName().endsWith(".rc") || trg.getName().startsWith(".")) {
621                                return;
622                        }
623                                        
624                        if(trg.getName().equals("railo-server.xml")) {
625                                trg=new File(trg.getParentFile(),"lucee-server.xml");
626                                // cfLuceeConfiguration
627                                FileInputStream is = new FileInputStream(src);
628                                        FileOutputStream os = new FileOutputStream(trg);
629                                        try{
630                                                String str=Util.toString(is);
631                                                str=str.replace("<cfRailoConfiguration", "<!-- copy from Railo context --><cfLuceeConfiguration");
632                                                str=str.replace("</cfRailoConfiguration", "</cfLuceeConfiguration");
633                                                
634                                                str=str.replace("<railo-configuration", "<!-- copy from Railo context --><cfLuceeConfiguration");
635                                                str=str.replace("</railo-configuration", "</cfLuceeConfiguration");
636                                                
637                                                
638                                                str=str.replace("{railo-config}", "{lucee-config}");
639                                                str=str.replace("{railo-server}", "{lucee-server}");
640                                                str=str.replace("{railo-web}", "{lucee-web}");
641                                                str=str.replace("\"railo.commons.", "\"lucee.commons.");
642                                                str=str.replace("\"railo.runtime.", "\"lucee.runtime.");
643                                                str=str.replace("\"railo.cfx.", "\"lucee.cfx.");
644                                                str=str.replace("/railo-context.ra", "/lucee-context.lar");
645                                                str=str.replace("/railo-context", "/lucee");
646                                                str=str.replace("railo-server-context", "lucee-server");
647                                                str=str.replace("http://www.getrailo.org", "http://stable.lucee.org");
648                                                str=str.replace("http://www.getrailo.com", "http://stable.lucee.org");
649                                                
650                                                
651                                                ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes());
652                                                
653                                                try {
654                                                        Util.copy(bais, os);
655                                                        bais.close();
656                                                }
657                                                finally {
658                                                        Util.closeEL(is, os);
659                                                }
660                                        }
661                                        finally {
662                                                Util.closeEL(is,os);
663                                        }
664                                return;
665                        }
666                                
667                        FileInputStream is = new FileInputStream(src);
668                        FileOutputStream os = new FileOutputStream(trg);
669                        try{
670                                Util.copy(is, os);
671                        }
672                        finally {
673                                Util.closeEL(is, os);
674                        }
675                }
676         }
677         
678         
679         /**
680        * @return return running context root
681        * @throws IOException 
682        * @throws IOException 
683        */
684         private File getRuningContextRoot() throws IOException {
685                
686                if(luceeServerRoot!=null) {
687                                return luceeServerRoot;
688                }
689                File dir=getClassLoaderRoot(mainClassLoader);
690                dir.mkdirs();
691                if(dir.exists() && dir.isDirectory()) return dir;
692                
693                                
694                        
695                throw new IOException("can't create/write to directory ["+dir+"], set \"init-param\" \"lucee-server-directory\" with path to writable directory");
696         }
697         /**
698        * returns the path where the classloader is located
699        * @param cl ClassLoader
700        * @return file of the classloader root
701        */
702         public static File getClassLoaderRoot(ClassLoader cl) {
703                String path="lucee/loader/engine/CFMLEngine.class";
704                URL res = cl.getResource(path);
705                 
706                // get file and remove all after !
707                String strFile=null;
708                try {
709                        strFile = URLDecoder.decode(res.getFile().trim(),"iso-8859-1");
710                } catch (UnsupportedEncodingException e) {
711                        
712                }
713                int index=strFile.indexOf('!');
714                if(index!=-1)strFile=strFile.substring(0,index);
715                
716                // remove path at the end
717                index=strFile.lastIndexOf(path);
718                if(index!=-1)strFile=strFile.substring(0,index);
719                
720                // remove "file:" at start and lucee.jar at the end
721                if(strFile.startsWith("file:"))strFile=strFile.substring(5);
722                if(strFile.endsWith("lucee.jar")) strFile=strFile.substring(0,strFile.length()-9);
723                
724                File file=new File(strFile);
725                if(file.isFile())file=file.getParentFile();
726                
727                return file;
728         }
729
730         /**
731        * check left value against right value
732        * @param left
733        * @param right
734        * @return returns if right is newer than left
735        */
736         private boolean isNewerThan(int left, int right) {
737                return left>right;
738         }
739
740         /**
741        * Load CFMl Engine Implementation (lucee.runtime.engine.CFMLEngineImpl) from a Classloader
742        * @param classLoader
743        * @return loaded CFML Engine
744        * @throws ClassNotFoundException 
745        * @throws NoSuchMethodException 
746        * @throws SecurityException 
747        * @throws InvocationTargetException 
748        * @throws IllegalAccessException 
749        * @throws IllegalArgumentException 
750        */
751         private CFMLEngine getEngine(ClassLoader classLoader) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
752                Class clazz=classLoader.loadClass("lucee.runtime.engine.CFMLEngineImpl");
753                Method m = clazz.getMethod("getInstance",new Class[]{CFMLEngineFactory.class});
754                return (CFMLEngine) m.invoke(null,new Object[]{this});
755                
756         }
757
758         /**
759        * log info to output
760        * @param obj Object to output
761        */
762         public void tlog(Object obj) {
763                log(new Date()+ " "+obj);
764         }
765         
766         /**
767        * log info to output
768        * @param obj Object to output
769        */
770         public void log(Object obj) {
771                if(out==null){
772                        boolean isCLI=false;
773                        String str=System.getProperty("lucee.cli.call");
774                        if(!Util.isEmpty(str, true)) {
775                                str=str.trim();
776                                isCLI="true".equalsIgnoreCase(str) || "yes".equalsIgnoreCase(str);
777                                
778                        }
779                        
780                        if(isCLI) {
781                                try{
782                                        File dir = new File(getResourceRoot(),"logs");
783                                        dir.mkdirs();
784                                        File file = new File(dir,"out");
785                                        
786                                file.createNewFile();
787                                out=new PrintWriter(file);
788                                }
789                                catch(Throwable t){
790                                        if(t instanceof ThreadDeath) throw (ThreadDeath)t;
791                                        t.printStackTrace();
792                                }
793                        }
794                        if(out==null)out=new PrintWriter(System.out);
795                }
796                out.write(""+obj+"\n"); 
797                out.flush();
798         }
799         
800         private class UpdateChecker extends Thread {
801                private CFMLEngineFactory factory;
802
803                private UpdateChecker(CFMLEngineFactory factory) {
804                                this.factory=factory;
805                }
806                
807                public void run() {
808                                long time=10000;
809                                while(true) {
810                                         try {
811                                                sleep(time);
812                                                time=1000*60*60*24;
813                                                factory.update();
814                                                
815                                         } catch (Exception e) {
816                                                
817                                         }
818                                }
819                }
820         }
821
822}