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