001    package railo.runtime;
002    
003    import java.io.IOException;
004    import java.net.MalformedURLException;
005    import java.util.Map;
006    
007    import javax.servlet.ServletContext;
008    
009    import org.apache.commons.collections.map.ReferenceMap;
010    
011    import railo.print;
012    import railo.commons.io.FileUtil;
013    import railo.commons.io.res.Resource;
014    import railo.commons.io.res.filter.ExtensionResourceFilter;
015    import railo.commons.lang.ArchiveClassLoader;
016    import railo.commons.lang.PCLCollection;
017    import railo.commons.lang.StringUtil;
018    import railo.runtime.config.Config;
019    import railo.runtime.config.ConfigImpl;
020    import railo.runtime.config.ConfigServer;
021    import railo.runtime.config.ConfigWebImpl;
022    import railo.runtime.config.ConfigWebUtil;
023    import railo.runtime.dump.DumpData;
024    import railo.runtime.dump.DumpProperties;
025    import railo.runtime.dump.DumpTable;
026    import railo.runtime.dump.DumpUtil;
027    import railo.runtime.dump.SimpleDumpData;
028    import railo.runtime.listener.ApplicationListener;
029    import railo.runtime.op.Caster;
030    import railo.runtime.type.util.ArrayUtil;
031    
032    /**  
033     * Mapping class
034     */
035    public final class MappingImpl implements Mapping {
036    
037            private static final long serialVersionUID = 6431380676262041196L;
038            
039            //private static final Object NULL = new Object();
040            private String virtual;
041        private String lcVirtual;
042        private boolean topLevel;
043        private short inspect;
044        private boolean physicalFirst;
045        private ArchiveClassLoader archiveClassLoader;
046        //private PhysicalClassLoader physicalClassLoader;
047        private PCLCollection pclCollection;
048        private Resource archive;
049        
050        private boolean hasArchive;
051        private ConfigImpl config;
052        private Resource classRootDirectory;
053        private PageSourcePool pageSourcePool=new PageSourcePool();
054        
055        private boolean readonly=false;
056        private boolean hidden=false;
057        private String strArchive;
058        
059        private String strPhysical;
060        private Resource physical;
061        //private boolean hasPhysical;
062        
063        private String lcVirtualWithSlash;
064        //private Resource classRoot;
065        private Map<String,Object> customTagPath=new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT);
066        //private final Map<String,Object> customTagPath=new HashMap<String, Object>();
067            private int classLoaderMaxElements=1000;
068            /**
069             * @return the classLoaderMaxElements
070             */
071            public int getClassLoaderMaxElements() {
072                    return classLoaderMaxElements;
073            }
074    
075            private boolean appMapping;
076            private boolean ignoreVirtual;
077    
078            private ApplicationListener appListener;
079    
080        public MappingImpl(ConfigImpl config, String virtual, String strPhysical,String strArchive, short inspect, 
081                boolean physicalFirst, boolean hidden, boolean readonly,boolean topLevel, boolean appMapping,boolean ignoreVirtual,ApplicationListener appListener) {
082            this(config, virtual, strPhysical, strArchive, inspect, physicalFirst, hidden, readonly,topLevel,appMapping,ignoreVirtual,appListener,5000);
083            
084        }
085    
086        /**
087         * @param configServer 
088         * @param config
089         * @param virtual
090         * @param strPhysical
091         * @param strArchive
092         * @param trusted
093         * @param physicalFirst
094         * @param hidden
095         * @param readonly
096         * @throws IOException
097         */
098        public MappingImpl(ConfigImpl config, String virtual, String strPhysical,String strArchive, short inspect, 
099                boolean physicalFirst, boolean hidden, boolean readonly,boolean topLevel, boolean appMapping, boolean ignoreVirtual,ApplicationListener appListener, int classLoaderMaxElements) {
100            this.ignoreVirtual=ignoreVirtual;
101            this.config=config;
102            this.hidden=hidden;
103            this.readonly=readonly;
104            this.strPhysical=StringUtil.isEmpty(strPhysical)?null:strPhysical;
105            this.strArchive=StringUtil.isEmpty(strArchive)?null:strArchive;
106            this.inspect=inspect;
107            this.topLevel=topLevel;
108            this.appMapping=appMapping;
109            this.physicalFirst=physicalFirst;
110            this.appListener=appListener;
111            this.classLoaderMaxElements=classLoaderMaxElements;
112            
113            // virtual
114            if(virtual.length()==0)virtual="/";
115            if(!virtual.equals("/") && virtual.endsWith("/"))this.virtual=virtual.substring(0,virtual.length()-1);
116            else this.virtual=virtual;
117            this.lcVirtual=this.virtual.toLowerCase();
118            this.lcVirtualWithSlash=lcVirtual.endsWith("/")?this.lcVirtual:this.lcVirtual+'/';
119    
120            //if(!(config instanceof ConfigWebImpl)) return;
121            //ConfigWebImpl cw=(ConfigWebImpl) config;
122            ServletContext cs = (config instanceof ConfigWebImpl)?((ConfigWebImpl)config).getServletContext():null;
123            
124            
125            // Physical
126            physical=ConfigWebUtil.getExistingResource(cs,strPhysical,null,config.getConfigDir(),FileUtil.TYPE_DIR,
127                    config);
128            // Archive
129            archive=ConfigWebUtil.getExistingResource(cs,strArchive,null,config.getConfigDir(),FileUtil.TYPE_FILE,
130                    config);
131            if(archive!=null) {
132                try {
133                    archiveClassLoader = new ArchiveClassLoader(archive,getClass().getClassLoader());
134                } 
135                catch (Throwable t) {
136                    archive=null;
137                }
138            }
139            hasArchive=archive!=null;
140    
141            if(archive==null) this.physicalFirst=true;
142            else if(physical==null) this.physicalFirst=false;
143            else this.physicalFirst=physicalFirst;
144            
145            
146            //if(!hasArchive && !hasPhysical) throw new IOException("missing physical and archive path, one of them must be defined");
147        }
148        
149        @Override
150        public ClassLoader getClassLoaderForArchive() {
151            return archiveClassLoader;
152        }
153        
154        public synchronized PCLCollection touchPCLCollection() throws IOException {
155            
156            if(pclCollection==null){
157                    pclCollection=new PCLCollection(this,getClassRootDirectory(),getConfig().getClassLoader(),classLoaderMaxElements);
158                    }
159            getConfig().checkPermGenSpace(true);
160            return pclCollection;
161        }
162            public synchronized PCLCollection getPCLCollection() {
163                    return pclCollection;
164            }
165    
166        
167        
168    
169            /**
170             * remove all Page from Pool using this classloader
171             * @param cl
172             */
173            public void clearPages(ClassLoader cl){
174                    pageSourcePool.clearPages(cl);
175            }
176            
177        @Override
178        public Resource getPhysical() {
179            return physical;
180        }
181    
182        @Override
183        public String getVirtualLowerCase() {
184            return lcVirtual;
185        }
186        @Override
187        public String getVirtualLowerCaseWithSlash() {
188            return lcVirtualWithSlash;
189        }
190    
191        @Override
192        public Resource getArchive() {
193            //initArchive();
194            return archive;
195        }
196    
197        @Override
198        public boolean hasArchive() {
199            return hasArchive;
200        }
201        
202        @Override
203        public boolean hasPhysical() {
204            return physical!=null;
205        }
206    
207        @Override
208        public Resource getClassRootDirectory() {
209            if(classRootDirectory==null) {
210                    String path=getPhysical()!=null?
211                                    getPhysical().getAbsolutePath():
212                                    getArchive().getAbsolutePath();
213                    
214                    classRootDirectory=config.getDeployDirectory().getRealResource(
215                                            StringUtil.toIdentityVariableName(
216                                                            path)
217                                    );
218            }
219            return classRootDirectory;
220        }
221        
222        /**
223         * clones a mapping and make it readOnly
224         * @param config
225         * @return cloned mapping
226         * @throws IOException
227         */
228        public MappingImpl cloneReadOnly(ConfigImpl config) {
229            return new MappingImpl(config,virtual,strPhysical,strArchive,inspect,physicalFirst,hidden,true,topLevel,appMapping,ignoreVirtual,appListener,classLoaderMaxElements);
230        }
231        
232        @Override
233            public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
234                    maxlevel--;
235            
236                    
237                    
238                    DumpTable htmlBox = new DumpTable("mapping","#ff6600","#ffcc99","#000000");
239                    htmlBox.setTitle("Mapping");
240                    htmlBox.appendRow(1,new SimpleDumpData("virtual"),new SimpleDumpData(virtual));
241                    htmlBox.appendRow(1,new SimpleDumpData("physical"),DumpUtil.toDumpData(strPhysical,pageContext,maxlevel,dp));
242                    htmlBox.appendRow(1,new SimpleDumpData("archive"),DumpUtil.toDumpData(strArchive,pageContext,maxlevel,dp));
243                    htmlBox.appendRow(1,new SimpleDumpData("inspect"),new SimpleDumpData(ConfigWebUtil.inspectTemplate(getInspectTemplateRaw(),"")));
244                    htmlBox.appendRow(1,new SimpleDumpData("physicalFirst"),new SimpleDumpData(Caster.toString(physicalFirst)));
245                    htmlBox.appendRow(1,new SimpleDumpData("readonly"),new SimpleDumpData(Caster.toString(readonly)));
246                    htmlBox.appendRow(1,new SimpleDumpData("hidden"),new SimpleDumpData(Caster.toString(hidden)));
247                    htmlBox.appendRow(1,new SimpleDumpData("appmapping"),new SimpleDumpData(Caster.toBoolean(appMapping)));
248                    htmlBox.appendRow(1,new SimpleDumpData("toplevel"),new SimpleDumpData(Caster.toString(topLevel)));
249                    htmlBox.appendRow(1,new SimpleDumpData("ClassLoaderMaxElements"),new SimpleDumpData(Caster.toString(classLoaderMaxElements)));
250                    return htmlBox;
251        }
252    
253        /**
254         * inspect template setting (Config.INSPECT_*), if not defined with the mapping the config setting is returned
255         * @return
256         */
257        public short getInspectTemplate() {
258                    if(inspect==ConfigImpl.INSPECT_UNDEFINED) return config.getInspectTemplate();
259                    return inspect;
260            }
261        
262        /**
263         * inspect template setting (Config.INSPECT_*), if not defined with the mapping, Config.INSPECT_UNDEFINED is returned
264         * @return
265         */
266        public short getInspectTemplateRaw() {
267                    return inspect;
268            }
269        
270        
271            
272    
273            @Override
274        public PageSource getPageSource(String realPath) {
275            boolean isOutSide = false;
276                    realPath=realPath.replace('\\','/');
277                    if(realPath.indexOf('/')!=0) {
278                        if(realPath.startsWith("../")) {
279                                    isOutSide=true;
280                            }
281                            else if(realPath.startsWith("./")) {
282                                    realPath=realPath.substring(1);
283                            }
284                            else {
285                                    realPath="/"+realPath;
286                            }
287                    }
288                    return getPageSource(realPath,isOutSide);
289        }
290        
291        @Override
292        public PageSource getPageSource(String path, boolean isOut) {
293            PageSource source=pageSourcePool.getPageSource(path,true);
294            if(source!=null) return source;
295    
296            PageSourceImpl newSource = new PageSourceImpl(this,path,isOut);
297            pageSourcePool.setPage(path,newSource);
298            
299            return newSource;//new PageSource(this,path);
300        }
301        
302        /**
303         * @return Returns the pageSourcePool.
304         */
305        public PageSourcePool getPageSourcePool() {
306            return pageSourcePool;
307        }
308    
309        @Override
310        public void check() {
311            //if(config instanceof ConfigServer) return;
312            //ConfigWebImpl cw=(ConfigWebImpl) config;
313            ServletContext cs = (config instanceof ConfigWebImpl)?((ConfigWebImpl)config).getServletContext():null;
314            
315            
316            // Physical
317            if(getPhysical()==null && strPhysical!=null && strPhysical.length()>0) {
318                physical=ConfigWebUtil.getExistingResource(cs,strPhysical,null,config.getConfigDir(),FileUtil.TYPE_DIR,config);
319                
320            }
321            // Archive
322            if(getArchive()==null && strArchive!=null && strArchive.length()>0) {
323                try {
324                    archive=ConfigWebUtil.getExistingResource(cs,strArchive,null,config.getConfigDir(),FileUtil.TYPE_FILE,
325                            config);
326                    if(archive!=null) {
327                        try {
328                            archiveClassLoader = new ArchiveClassLoader(archive,getClass().getClassLoader());
329                        } 
330                        catch (MalformedURLException e) {
331                            archive=null;
332                        }
333                    }
334                    hasArchive=archive!=null;
335                } 
336                catch (IOException e) {}
337            }
338        }
339    
340        @Override
341        public Config getConfig() {
342            return config;
343        }
344        
345        public ConfigImpl getConfigImpl() {
346            return config;
347        }
348    
349        @Override
350        public boolean isHidden() {
351            return hidden;
352        }
353    
354        @Override
355        public boolean isPhysicalFirst() {
356            return physicalFirst;
357        }
358    
359        @Override
360        public boolean isReadonly() {
361            return readonly;
362        }
363    
364        @Override
365        public String getStrArchive() {
366            return strArchive;
367        }
368    
369        @Override
370        public String getStrPhysical() {
371            return strPhysical;
372        }
373    
374        @Override
375        @Deprecated
376        public boolean isTrusted() {
377            return getInspectTemplate()==ConfigImpl.INSPECT_NEVER;
378        }
379    
380        @Override
381        public String getVirtual() {
382            return virtual;
383        }
384    
385            public boolean isAppMapping() {
386                    return appMapping;
387            }
388    
389    
390            public boolean isTopLevel() {
391                    return topLevel;
392            }
393    
394            /*public PageSource getCustomTagPath(String name, boolean doCustomTagDeepSearch) {
395                    String lcName=name.toLowerCase().trim();
396                    Object o = customTagPath.get(lcName);
397    
398                    if(o==null){
399                            PageSource ps=searchFor(name, lcName, doCustomTagDeepSearch);
400                            if(ps!=null){
401                                    customTagPath.put(lcName,ps);
402                                    return ps;
403                            }
404                            
405                            customTagPath.put(lcName,NULL);
406                            return null;
407                            
408                    }
409                    else if(o==NULL) return null;
410                    
411                    return (PageSource) o;
412            }*/
413            
414            public PageSource getCustomTagPath(String name, boolean doCustomTagDeepSearch) {
415                    return searchFor(name, name.toLowerCase().trim(), doCustomTagDeepSearch);
416            }
417            
418            public boolean ignoreVirtual(){
419                    return ignoreVirtual;
420            }
421            
422            
423            private PageSource searchFor(String filename, String lcName, boolean doCustomTagDeepSearch) {
424                    if(!hasPhysical()) return null;
425    
426                    
427                    PageSource source=getPageSource(filename);
428                    if(isOK(source)) {
429                    return source;
430            }
431            customTagPath.remove(lcName);
432            
433            if(doCustomTagDeepSearch){
434                    String path = _getRecursive(getPhysical(),null, filename);
435                    if(path!=null ) {
436                            source=getPageSource(path);
437                            if(isOK(source)) {
438                            return source;
439                    }
440                            customTagPath.remove(lcName);
441                    }
442            }
443            return null;
444            }
445    
446            public static boolean isOK(PageSource ps) {
447                    return (ps.getMapping().isTrusted() && ((PageSourceImpl)ps).isLoad()) || ps.exists();
448            }
449    
450            public static PageSource isOK(PageSource[] arr) {
451                    if(ArrayUtil.isEmpty(arr)) return null;
452                    for(int i=0;i<arr.length;i++) {
453                            if(isOK(arr[i])) return arr[i];
454                    }
455                    return null;
456            }
457            
458            private static String _getRecursive(Resource res, String path, String filename) {
459            if(res.isDirectory()) {
460                    Resource[] children = res.listResources(new ExtensionResourceFilter(new String[]{".cfm",".cfc"},true,true));
461                    if(path!=null)path+=res.getName()+"/";
462                    else path="";
463                    String tmp;
464                    for(int i=0;i<children.length;i++){
465                            tmp= _getRecursive(children[i], path, filename);
466                            if(tmp!=null) return tmp;
467                    }
468            }
469            else if(res.isFile()) {
470                    if(res.getName().equalsIgnoreCase(filename)) return path+res.getName();
471            }
472            return null;            
473            }
474            
475            @Override
476            public int hashCode() {
477                    return toString().hashCode();
478            }
479    
480            @Override
481            public String toString() {
482                    return "StrPhysical:"+getStrPhysical()+";"+
483                     "StrArchive:"+getStrArchive()+";"+
484                     "Virtual:"+getVirtual()+";"+
485                     "Archive:"+getArchive()+";"+
486                     "Physical:"+getPhysical()+";"+
487                     "topLevel:"+topLevel+";"+
488                     "inspect:"+ConfigWebUtil.inspectTemplate(getInspectTemplateRaw(),"")+";"+
489                     "physicalFirst:"+physicalFirst+";"+
490                     "readonly:"+readonly+";"+
491                     "hidden:"+hidden+";";
492            }
493    
494            public ApplicationListener getApplicationListener() {
495                    if(appListener!=null) return appListener;
496                    return config.getApplicationListener();
497            }
498    }