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 }