001    package railo.runtime;
002    
003    import java.io.FileNotFoundException;
004    import java.io.IOException;
005    import java.io.InputStream;
006    import java.lang.reflect.Constructor;
007    import java.lang.reflect.InvocationTargetException;
008    
009    import railo.commons.io.IOUtil;
010    import railo.commons.io.res.Resource;
011    import railo.commons.io.res.util.ResourceUtil;
012    import railo.commons.lang.SizeOf;
013    import railo.commons.lang.StringUtil;
014    import railo.commons.lang.types.RefBoolean;
015    import railo.commons.lang.types.RefBooleanImpl;
016    import railo.runtime.config.ConfigWeb;
017    import railo.runtime.config.ConfigWebImpl;
018    import railo.runtime.engine.ThreadLocalPageContext;
019    import railo.runtime.engine.ThreadLocalPageSource;
020    import railo.runtime.exp.MissingIncludeException;
021    import railo.runtime.exp.PageException;
022    import railo.runtime.exp.TemplateException;
023    import railo.runtime.op.Caster;
024    import railo.runtime.type.List;
025    import railo.runtime.type.Sizeable;
026    import railo.runtime.type.util.ArrayUtil;
027    
028    /**
029     * represent a cfml file on the runtime system
030     */
031    public final class PageSourceImpl implements SourceFile, PageSource, Sizeable {
032    
033            private static final long serialVersionUID = -7661676586215092539L;
034            //public static final byte LOAD_NONE=1;
035        public static final byte LOAD_ARCHIVE=2;
036        public static final byte LOAD_PHYSICAL=3;
037        
038        //private byte load=LOAD_NONE;
039    
040            private final MappingImpl mapping;
041        private final String realPath;
042        
043        private boolean isOutSide;
044        
045        private String className;
046        private String packageName;
047        private String javaName;
048    
049        private Resource physcalSource;
050        private Resource archiveSource;
051        private String fileName;
052        private String compName;
053        private PagePlus page;
054            private long lastAccess;        
055            private int accessCount=0;
056        //private boolean recompileAlways;
057        //private boolean recompileAfterStartUp;
058        
059        
060        
061        private PageSourceImpl() {
062            mapping=null;
063            realPath=null;
064        }
065        
066        
067        /**
068             * constructor of the class
069         * @param mapping
070         * @param realPath
071             */
072            PageSourceImpl(MappingImpl mapping,String realPath) {
073                    this.mapping=mapping;
074            //recompileAlways=mapping.getConfig().getCompileType()==Config.RECOMPILE_ALWAYS;
075            //recompileAfterStartUp=mapping.getConfig().getCompileType()==Config.RECOMPILE_AFTER_STARTUP || recompileAlways;
076            realPath=realPath.replace('\\','/');
077                    if(realPath.indexOf('/')!=0) {
078                        if(realPath.startsWith("../")) {
079                                    isOutSide=true;
080                            }
081                            else if(realPath.startsWith("./")) {
082                                    realPath=realPath.substring(1);
083                            }
084                            else {
085                                    realPath="/"+realPath;
086                            }
087                    }
088                    this.realPath=realPath;
089                
090            }
091            
092            /**
093             * private constructor of the class
094             * @param mapping
095             * @param realPath
096             * @param isOutSide
097             */
098        PageSourceImpl(MappingImpl mapping, String realPath, boolean isOutSide) {
099            //recompileAlways=mapping.getConfig().getCompileType()==Config.RECOMPILE_ALWAYS;
100            //recompileAfterStartUp=mapping.getConfig().getCompileType()==Config.RECOMPILE_AFTER_STARTUP || recompileAlways;
101            this.mapping=mapping;
102                this.isOutSide=isOutSide;
103                    this.realPath=realPath;
104                    
105            }
106    
107            
108            /**
109         * @see railo.runtime.PageSource#loadPage(railo.runtime.PageContext)
110         */
111            public Page loadPage(ConfigWeb config) throws PageException {
112                    return loadPage(null,config);
113            }
114            
115            
116            /**
117             * return page when already loaded, otherwise null
118             * @param pc
119             * @param config
120             * @return
121             * @throws PageException
122             */
123            public Page getPage() {
124                    return page;
125            }
126            
127            // FUTURE add to interface without config
128            public Page loadPage(PageContext pc,ConfigWeb config) throws PageException {
129                    PagePlus page=this.page;
130                    if(mapping.isPhysicalFirst()) {
131                            page=loadPhysical(pc,page,config);
132                            if(page==null) page=loadArchive(page); 
133                    if(page!=null) return page;
134                }
135                else {
136                    page=loadArchive(page);
137                    if(page==null)page=loadPhysical(pc,page,config);
138                    if(page!=null) return page;
139                }
140                    throw new MissingIncludeException(this);
141                
142            }
143            
144    
145            /**
146             * @see railo.runtime.PageSource#loadPage(railo.runtime.PageContext, railo.runtime.Page)
147             */
148            public Page loadPage(ConfigWeb config, Page defaultValue) throws PageException {
149                    return loadPage(null,config, defaultValue);
150            }
151            
152            public Page loadPage(PageContext pc,ConfigWeb config, Page defaultValue) throws PageException {
153                    PagePlus page=this.page;
154                    if(mapping.isPhysicalFirst()) {
155                    page=loadPhysical(pc,page,config);
156                    if(page==null) page=loadArchive(page); 
157                    if(page!=null) return page;
158                }
159                else {
160                    page=loadArchive(page);
161                    if(page==null)page=loadPhysical(pc,page,config);
162                    if(page!=null) return page;
163                }
164                return defaultValue;
165            }
166            
167        private PagePlus loadArchive(PagePlus page) {
168            if(!mapping.hasArchive()) return null;
169                    if(page!=null) return page;
170            
171            try {
172                synchronized(this) {
173                    Class clazz=mapping.getClassLoaderForArchive().loadClass(getClazz());
174                    this.page=page=newInstance(clazz);
175                    page.setPageSource(this);
176                    //page.setTimeCreated(System.currentTimeMillis());
177                    page.setLoadType(LOAD_ARCHIVE);
178                            ////load=LOAD_ARCHIVE;
179                            return page;
180                }
181            } 
182            catch (Exception e) {
183                return null;
184            }
185        }
186        
187    
188        private PagePlus loadPhysical(PageContext pc,PagePlus page, ConfigWeb config) throws PageException {
189            if(!mapping.hasPhysical()) return null;
190            
191            // FUTURE change interface loadPage to PageContext
192            pc=ThreadLocalPageContext.get(pc);
193            PageContextImpl pci=(PageContextImpl) pc;
194            //if(pc.isPageAlreadyUsed(page)) return page;
195            
196            if((mapping.isTrusted() || 
197                            pc!=null && pci.isTrusted(page)) 
198                            && isLoad(LOAD_PHYSICAL)) return page;
199                                    //&& isLoad(LOAD_PHYSICAL) && !recompileAlways) return page;
200            
201            Resource srcFile = getPhyscalFile();
202            
203            long srcLastModified = srcFile.lastModified();
204            
205            if(srcLastModified==0L) return null;
206            
207                    // Page exists    
208                            if(page!=null) {
209                            //if(page!=null && !recompileAlways) {
210                                    // java file is newer !mapping.isTrusted() && 
211                                    if(srcLastModified!=page.getSourceLastModified()) {
212                                            this.page=page=compile(config,mapping.getClassRootDirectory(),Boolean.TRUE);
213                            page.setPageSource(this);
214                                            page.setLoadType(LOAD_PHYSICAL);
215                                    }
216                            
217                            }
218                    // page doesn't exist
219                            else {
220                    ///synchronized(this) {
221                        Resource classRootDir=mapping.getClassRootDirectory();
222                        Resource classFile=classRootDir.getRealResource(getJavaName()+".class");
223                        boolean isNew=false;
224                        // new class
225                        if(!classFile.exists()) {
226                        //if(!classFile.exists() || recompileAfterStartUp) {
227                            this.page=page= compile(config,classRootDir,Boolean.FALSE);
228                            isNew=true;
229                        }
230                        // load page
231                        else {
232                            try {
233                                                            this.page=page=newInstance(mapping.touchPCLCollection().getClass(this));
234                                                    } catch (Throwable t) {t.printStackTrace();
235                                                            this.page=page=null;
236                                                    }
237                            if(page==null) this.page=page=compile(config,classRootDir,Boolean.TRUE);
238                                  
239                        }
240                        
241                        // check if there is a newwer version
242                        if(!isNew && srcLastModified!=page.getSourceLastModified()) {
243                            isNew=true;
244                            this.page=page=compile(config,classRootDir,null);
245                                    }
246                        
247                        // check version
248                        if(!isNew && page.getVersion()!=Info.getFullVersionInfo()) {
249                            isNew=true;
250                            this.page=page=compile(config,classRootDir,null);
251                        }
252                        
253                        page.setPageSource(this);
254                                    page.setLoadType(LOAD_PHYSICAL);
255    
256                            }
257                            if(pc!=null)pci.setPageUsed(page);
258                            return page;
259        }
260    
261        private boolean isLoad(byte load) {
262                    return page!=null && load==page.getLoadType();
263            }
264        
265    
266            private synchronized PagePlus compile(ConfigWeb config,Resource classRootDir, Boolean resetCL) throws PageException {
267                    try {
268                            return _compile(config, classRootDir, resetCL);
269            }
270            catch(ClassFormatError e) {
271                    String msg=StringUtil.emptyIfNull(e.getMessage());
272                    if(StringUtil.indexOfIgnoreCase(msg, "Invalid method Code length")!=-1) {
273                            throw new TemplateException("There is too much code inside the template ["+getDisplayPath()+"], Railo was not able to break it into pieces, move parts of your code to an include or a external component/function",msg);
274                    }
275                    throw Caster.toPageException(e);
276            }
277            catch(Throwable t) {
278                    throw Caster.toPageException(t);
279            }
280            }
281    
282            private synchronized PagePlus _compile(ConfigWeb config,Resource classRootDir, Boolean resetCL) throws TemplateException, IOException, ClassNotFoundException, SecurityException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
283            ConfigWebImpl cwi=(ConfigWebImpl) config;
284            byte[] barr = cwi.getCompiler().
285                    compile(cwi,this,cwi.getTLDs(),cwi.getFLDs(),classRootDir,getJavaName());
286            Class<?> clazz = mapping.touchPCLCollection().loadClass(getClazz(), barr,isComponent());
287            return  newInstance(clazz);
288        }
289    
290        private PagePlus newInstance(Class clazz) throws SecurityException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
291            try{
292                            Constructor c = clazz.getConstructor(new Class[]{PageSource.class});
293                            return (PagePlus) c.newInstance(new Object[]{this});
294                    }
295            // this only happens with old code from ra files
296                    catch(NoSuchMethodException e){
297                            ThreadLocalPageSource.register(this);
298                            try{
299                                    return (PagePlus) clazz.newInstance();
300                            }
301                            finally {
302                                    ThreadLocalPageSource.release();
303                            }
304                            
305                            
306                    }
307            }
308    
309    
310            /**
311         * return source path as String 
312         * @return source path as String
313         */
314        public String getDisplayPath() {
315            if(!mapping.hasArchive())       {
316                    return StringUtil.toString(getPhyscalFile(), null);
317            }
318            else if(isLoad(LOAD_PHYSICAL))  {
319                    return StringUtil.toString(getPhyscalFile(), null);
320            }
321            else if(isLoad(LOAD_ARCHIVE))   {
322                    return StringUtil.toString(getArchiveSourcePath(), null);
323            }
324            else {
325                boolean pse = physcalExists();
326                boolean ase = archiveExists();
327                
328                if(mapping.isPhysicalFirst()) {
329                    if(pse)return getPhyscalFile().toString();
330                    else if(ase)return getArchiveSourcePath();
331                    return getPhyscalFile().toString();
332                }
333                if(ase)return getArchiveSourcePath();
334                else if(pse)return getPhyscalFile().toString();
335                return getArchiveSourcePath();
336            }
337        }
338        
339        public boolean isComponent() {
340            return ResourceUtil.getExtension(getRealpath(), "").equalsIgnoreCase(mapping.getConfig().getCFCExtension());
341        }
342        
343        /**
344             * return file object, based on physical path and realpath
345             * @return file Object
346             */
347            private String getArchiveSourcePath() {
348                return "ra://"+mapping.getArchive().getAbsolutePath()+"!"+realPath; 
349            }
350    
351        /**
352             * return file object, based on physical path and realpath
353             * @return file Object
354             */
355        public Resource getPhyscalFile() {
356            if(physcalSource==null) {
357                if(!mapping.hasPhysical()) {
358                    return null;
359                }
360                            physcalSource=ResourceUtil.toExactResource(mapping.getPhysical().getRealResource(realPath));
361            }
362            return physcalSource;
363            }
364        
365        public Resource getArchiveFile() {
366            if(archiveSource==null) {
367                    if(!mapping.hasArchive()) return null;
368                    String path="zip://"+mapping.getArchive().getAbsolutePath()+"!"+realPath;
369                    archiveSource = ThreadLocalPageContext.getConfig().getResource(path);
370            }
371            return archiveSource;
372            }
373        
374    
375        /**
376             * merge to realpath to one
377             * @param mapping 
378             * @param parentRealPath 
379             * @param newRealPath
380             * @param isOutSide 
381             * @return merged realpath
382             */
383            private static String mergeRealPathes(Mapping mapping,String parentRealPath, String newRealPath, RefBoolean isOutSide) {
384                    parentRealPath=pathRemoveLast(parentRealPath,isOutSide);
385                    while(newRealPath.startsWith("../")) {
386                            parentRealPath=pathRemoveLast(parentRealPath,isOutSide);
387                            newRealPath=newRealPath.substring(3);
388                    }
389                    
390                    // check if come back
391                    String path=parentRealPath.concat("/").concat(newRealPath);
392                    
393                    if(path.startsWith("../")) {
394                            int count=0;
395                            do {
396                                    count++;
397                                    path=path.substring(3);
398                            }while(path.startsWith("../"));
399                            
400                            String strRoot=mapping.getPhysical().getAbsolutePath().replace('\\','/');
401                            if(!StringUtil.endsWith(strRoot,'/')) {
402                                    strRoot+='/';
403                            }
404                            int rootLen=strRoot.length();
405                            String[] arr=List.toStringArray(List.listToArray(path,'/'),"");//path.split("/");
406                            int tmpLen;
407                            for(int i=count;i>0;i--) {
408                                    if(arr.length>i) {
409                                            String tmp='/'+list(arr,0,i);
410                                            tmpLen=rootLen-tmp.length();
411                                            if(strRoot.lastIndexOf(tmp)==tmpLen && tmpLen>=0) {
412                                                    StringBuffer rtn=new StringBuffer();
413                                                    for(int y=0;i<count-i;y++) rtn.append("../");
414                                                    isOutSide.setValue(rtn.length()!=0);
415                                                    return (rtn.length()==0?"/":rtn.toString())+list(arr,i,arr.length);
416                                            }
417                                    }
418                            }
419                    }
420                    return parentRealPath.concat("/").concat(newRealPath);
421            }
422    
423            /**
424             * convert a String array to a string list, but only part of it 
425             * @param arr String Array
426             * @param from start from here
427             * @param len how many element
428             * @return String list
429             */
430            private static String list(String[] arr,int from, int len) {
431                    StringBuffer sb=new StringBuffer();
432                    for(int i=from;i<len;i++) {
433                            sb.append(arr[i]);
434                            if(i+1!=arr.length)sb.append('/');
435                    }
436                    return sb.toString();
437            }
438    
439            
440            
441            /**
442             * remove the last elemtn of a path
443             * @param path path to remove last element from it
444             * @param isOutSide 
445             * @return path with removed element
446             */
447            private static String pathRemoveLast(String path, RefBoolean isOutSide) {
448                    if(path.length()==0) {
449                            isOutSide.setValue(true);
450                            return "..";
451                    }
452                    else if(path.endsWith("..")){
453                        isOutSide.setValue(true);
454                            return path.concat("/..");//path+"/..";
455                    }
456                    return path.substring(0,path.lastIndexOf('/'));
457            }
458            
459            /**
460         * @see railo.runtime.PageSource#getRealpath()
461         */
462            public String getRealpath() {
463                    return realPath;
464            }       
465            /**
466         * @see railo.runtime.PageSource#getFullRealpath()
467         */
468            public String getFullRealpath() {
469                    if(mapping.getVirtual().length()==1 || mapping.ignoreVirtual())
470                            return realPath;
471                    return mapping.getVirtual()+realPath;
472            }
473            
474            /**
475             * @return returns a variable string based on realpath and return it
476             */
477            public String getRealPathAsVariableString() {
478                    return StringUtil.toIdentityVariableName(realPath);
479            }
480            
481            /**
482         * @see railo.runtime.PageSource#getClazz()
483         */
484            public String getClazz() {
485                    if(className==null) createClassAndPackage();
486                    if(packageName.length()>0) return packageName+'.'+className;
487                    return className;
488            }
489            
490            /**
491             * @return returns the a classname matching to filename (Example: test_cfm)
492             */
493            public String getClassName() {
494                    if(className==null) createClassAndPackage();
495                    return className;
496            }
497    
498        /**
499         * @see railo.runtime.PageSource#getFileName()
500         */
501        public String getFileName() {
502                    if(fileName==null) createClassAndPackage();
503            return fileName;
504        }
505            
506            /**
507         * @see railo.runtime.PageSource#getJavaName()
508         */
509            public String getJavaName() {
510                    if(javaName==null) createClassAndPackage();
511                    return javaName;
512            }
513    
514            /**
515             * @return returns the a package matching to file (Example: railo.web)
516             */
517            public String getPackageName() {
518                    if(packageName==null) createClassAndPackage();
519                    return packageName;
520            }
521            /**
522         * @see railo.runtime.PageSource#getComponentName()
523         */
524            public String getComponentName() {
525                    if(compName==null) createComponentName();
526                    return compName;
527            }
528            
529            
530            private synchronized void createClassAndPackage() {
531                    String str=realPath;
532                    StringBuffer packageName=new StringBuffer();
533                    StringBuffer javaName=new StringBuffer();
534                    
535                    String[] arr=List.toStringArrayEL(List.listToArrayRemoveEmpty(str,'/'));
536                    
537                    String varName;
538                    for(int i=0;i<arr.length;i++) {
539                            if(i==(arr.length-1)) {
540                                    int index=arr[i].lastIndexOf('.');
541                                    if(index!=-1){
542                                            String ext=arr[i].substring(index+1);
543                                            varName=StringUtil.toVariableName(arr[i].substring(0,index)+"_"+ext);
544                                    }
545                                    else varName=StringUtil.toVariableName(arr[i]);
546                                    varName=varName+"$cf";
547                                    className=varName.toLowerCase();
548                                    fileName=arr[i];
549                            }
550                            else {
551                                    varName=StringUtil.toVariableName(arr[i]);
552                                    if(i!=0) {
553                                        packageName.append('.');
554                                    }
555                                    packageName.append(varName);
556                            }
557                            javaName.append('/');
558                            javaName.append(varName);
559                    }
560                    
561                    this.packageName=packageName.toString().toLowerCase();
562                    this.javaName=javaName.toString().toLowerCase();
563    
564                    
565                    
566            }
567            
568            
569    
570            private synchronized void createComponentName() {
571                    Resource res = this.getPhyscalFile();
572                String str=null;
573                    if(res!=null) {
574                            
575                            str=res.getAbsolutePath();
576                            str=str.substring(str.length()-realPath.length());
577                            if(!str.equalsIgnoreCase(realPath)) {
578                                    str=realPath;
579                            }
580                    }
581                    else str=realPath;
582                
583                    StringBuffer compName=new StringBuffer();
584                    String[] arr;
585                    
586                    // virtual part
587                    if(!mapping.ignoreVirtual()) {
588                            arr=List.toStringArrayEL(List.listToArrayRemoveEmpty(mapping.getVirtual(),"\\/"));
589                            for(int i=0;i<arr.length;i++) {
590                                    if(compName.length()>0) compName.append('.');
591                                    compName.append(arr[i]);
592                            }
593                    }
594                    
595                    // physical part
596                    arr=List.toStringArrayEL(List.listToArrayRemoveEmpty(str,'/')); 
597                    for(int i=0;i<arr.length;i++) {
598                        if(compName.length()>0) compName.append('.');
599                            if(i==(arr.length-1)) {
600                                compName.append(arr[i].substring(0,arr[i].length()-4));
601                            }
602                            else compName.append(arr[i]);
603                    }
604                    this.compName=compName.toString();
605            }
606    
607        /**
608         * @see railo.runtime.PageSource#getMapping()
609         */
610        public Mapping getMapping() {
611            return mapping;
612        }
613    
614        /**
615         * @see railo.runtime.PageSource#exists()
616         */
617        public synchronized boolean exists() {
618            if(mapping.isPhysicalFirst())
619                    return physcalExists() || archiveExists();
620                return archiveExists() || physcalExists();
621        }
622    
623        /**
624         * @see railo.runtime.PageSource#physcalExists()
625         */
626        public boolean physcalExists() {
627            return ResourceUtil.exists(getPhyscalFile());
628        }
629        
630        private boolean archiveExists() {
631            if(!mapping.hasArchive())return false;
632            try {
633                    String clazz = getClazz();
634                    if(clazz==null) return getArchiveFile().exists();
635                    mapping.getClassLoaderForArchive().loadClass(clazz);
636                    return true;
637            } 
638            catch(ClassNotFoundException cnfe){
639                    return false;
640            }
641            catch (Exception e) {
642                return getArchiveFile().exists();
643            }
644        }
645    
646        /**
647         * return the inputstream of the source file
648         * @return return the inputstream for the source from ohysical or archive
649         * @throws FileNotFoundException
650         */
651        private InputStream getSourceAsInputStream() throws IOException {
652            if(!mapping.hasArchive())               return IOUtil.toBufferedInputStream(getPhyscalFile().getInputStream());
653            else if(isLoad(LOAD_PHYSICAL))  return IOUtil.toBufferedInputStream(getPhyscalFile().getInputStream());
654            else if(isLoad(LOAD_ARCHIVE))   {
655                StringBuffer name=new StringBuffer(getPackageName().replace('.','/'));
656                if(name.length()>0)name.append("/");
657                name.append(getFileName());
658                
659                return mapping.getClassLoaderForArchive().getResourceAsStream(name.toString());
660            }
661            else {
662                return null;
663            }
664        }
665        /**
666         * @see railo.runtime.PageSource#getSource()
667         */
668        public String[] getSource() throws IOException {
669            //if(source!=null) return source;
670            InputStream is = getSourceAsInputStream();
671            if(is==null) return null;
672            try {
673                    return IOUtil.toStringArray(IOUtil.getReader(is,getMapping().getConfig().getTemplateCharset()));
674            }
675            finally {
676                    IOUtil.closeEL(is);
677            }
678        }
679    
680        /**
681         * @see java.lang.Object#equals(java.lang.Object)
682         */
683        public boolean equals(Object obj) {
684            if(this==obj) return true;  
685            if(!(obj instanceof PageSource)) return false;
686            return getClassName().equals(((PageSource)obj).getClassName());
687            //return equals((PageSource)obj);
688        }
689        
690        /**
691         * is given object equal to this
692         * @param other
693         * @return is same
694         */
695        public boolean equals(PageSource other) {
696            if(this==other) return true;  
697            return getClassName().equals(other.getClassName());
698        }
699    
700            /**
701         * @see railo.runtime.PageSource#getRealPage(java.lang.String)
702         */
703            public PageSource getRealPage(String realPath) {
704                if(realPath.equals(".") || realPath.equals(".."))realPath+='/';
705                else realPath=realPath.replace('\\','/');
706                RefBoolean _isOutSide=new RefBooleanImpl(isOutSide);
707                
708                
709                    if(realPath.indexOf('/')==0) {
710                        _isOutSide.setValue(false);
711                    }
712                    else if(realPath.startsWith("./")) {
713                            realPath=mergeRealPathes(mapping,this.realPath, realPath.substring(2),_isOutSide);
714                    }
715                    else {
716                            realPath=mergeRealPathes(mapping,this.realPath, realPath,_isOutSide);
717                    }
718                    return mapping.getPageSource(realPath,_isOutSide.toBooleanValue());
719            }
720            
721            /**
722         * @see railo.runtime.PageSource#setLastAccessTime(long)
723         */
724            public final void setLastAccessTime(long lastAccess) {
725                    this.lastAccess=lastAccess;
726            }       
727            
728            /**
729         * @see railo.runtime.PageSource#getLastAccessTime()
730         */
731            public final long getLastAccessTime() {
732                    return lastAccess;
733            }
734    
735            /**
736         * @see railo.runtime.PageSource#setLastAccessTime()
737         */
738            public synchronized final void setLastAccessTime() {
739                    accessCount++;
740                    this.lastAccess=System.currentTimeMillis();
741            }       
742            
743            /**
744         * @see railo.runtime.PageSource#getAccessCount()
745         */
746            public final int getAccessCount() {
747                    return accessCount;
748            }
749    
750        /**
751         * @see railo.runtime.SourceFile#getFile()
752         */
753        public Resource getFile() {
754            Resource res = getPhyscalFile();
755            if(res!=null) return res;
756            return getArchiveFile();
757        }
758    
759    
760        public void clear() {
761            if(page!=null){
762                    page=null;
763            }
764        }
765        
766        /**
767         * clear page, but only when page use the same clasloader as provided
768         * @param cl
769         */
770        public void clear(ClassLoader cl) {
771            if(page!=null && page.getClass().getClassLoader().equals(cl)){
772                    page=null;
773            }
774        }
775    
776            /**
777             * @see railo.runtime.SourceFile#getFullClassName()
778             */
779        public String getFullClassName() {
780            String s=_getFullClassName();
781            return s;
782        }
783        
784            public String _getFullClassName() {
785                    String p=getPackageName();
786                    if(p.length()==0) return getClassName();
787                    return p.concat(".").concat(getClassName());
788            }
789            
790            public boolean isLoad() {
791                    return page!=null;////load!=LOAD_NONE;
792            }
793    
794            /**
795             * @see java.lang.Object#toString()
796             */
797            public String toString() {
798                    return getDisplayPath();
799            }
800            
801            /**
802             * @see railo.runtime.type.Sizeable#sizeOf()
803             */
804            public long sizeOf() {
805                    return SizeOf.size(page,0)+
806                    SizeOf.size(className)+
807                    SizeOf.size(packageName)+
808                    SizeOf.size(javaName)+
809                    SizeOf.size(fileName)+
810                    SizeOf.size(compName)+
811                    SizeOf.size(lastAccess)+
812                    SizeOf.size(accessCount);
813            }
814    
815            public static PageSource best(PageSource[] arr) {
816                    if(ArrayUtil.isEmpty(arr)) return null;
817                    if(arr.length==1)return arr[0];
818                    for(int i=0;i<arr.length;i++) {
819                            if(pageExist(arr[i])) return arr[i];
820                    }
821                    return arr[0];
822            }
823    
824            public static boolean pageExist(PageSource ps) {
825                    return (ps.getMapping().isTrusted() && ((PageSourceImpl)ps).isLoad()) || ps.exists();
826            }
827    
828            public static Page loadPage(PageContext pc,PageSource[] arr,Page defaultValue) throws PageException {
829                    if(ArrayUtil.isEmpty(arr)) return null;
830                    Page p;
831                    for(int i=0;i<arr.length;i++) {
832                            p=((PageSourceImpl)arr[i]).loadPage(pc,pc.getConfig(), null);//FUTURE remove cast
833                            if(p!=null) return p;
834                    }
835                    return defaultValue;
836            }
837    
838            public static Page loadPage(PageContext pc,PageSource[] arr) throws PageException {
839                    if(ArrayUtil.isEmpty(arr)) return null;
840                    
841                    Page p;
842                    for(int i=0;i<arr.length;i++) {
843                            p=((PageSourceImpl)arr[i]).loadPage(pc,pc.getConfig(), null);//FUTURE remove cast
844                            if(p!=null) return p;
845                    }
846                    throw new MissingIncludeException(arr[0]);
847            }
848            
849            
850            
851    }