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.runtime.component; 020 021import java.util.Map; 022 023import javax.servlet.jsp.tagext.BodyContent; 024 025import lucee.commons.io.res.Resource; 026import lucee.commons.io.res.filter.DirectoryResourceFilter; 027import lucee.commons.io.res.filter.ExtensionResourceFilter; 028import lucee.commons.io.res.filter.OrResourceFilter; 029import lucee.commons.io.res.filter.ResourceFilter; 030import lucee.commons.lang.MappingUtil; 031import lucee.commons.lang.StringUtil; 032import lucee.runtime.Component; 033import lucee.runtime.ComponentImpl; 034import lucee.runtime.ComponentPage; 035import lucee.runtime.InterfaceImpl; 036import lucee.runtime.InterfacePage; 037import lucee.runtime.Mapping; 038import lucee.runtime.MappingImpl; 039import lucee.runtime.Page; 040import lucee.runtime.PageContext; 041import lucee.runtime.PageContextImpl; 042import lucee.runtime.PageSource; 043import lucee.runtime.PageSourceImpl; 044import lucee.runtime.config.ConfigImpl; 045import lucee.runtime.debug.DebugEntryTemplate; 046import lucee.runtime.exp.ApplicationException; 047import lucee.runtime.exp.ExpressionException; 048import lucee.runtime.exp.PageException; 049import lucee.runtime.op.Caster; 050import lucee.runtime.type.util.ArrayUtil; 051import lucee.runtime.writer.BodyContentUtil; 052 053public class ComponentLoader { 054 055 056 private static final ResourceFilter DIR_OR_EXT=new OrResourceFilter(new ResourceFilter[]{DirectoryResourceFilter.FILTER,new ExtensionResourceFilter(".cfc")}); 057 058 public static ComponentImpl loadComponent(PageContext pc,PageSource child,String rawPath, Boolean searchLocal, Boolean searchRoot) throws PageException { 059 return (ComponentImpl)load(pc,child, rawPath, searchLocal, searchRoot,null,false); 060 } 061 062 public static InterfaceImpl loadInterface(PageContext pc,PageSource child,String rawPath, Map interfaceUDFs) throws PageException { 063 return (InterfaceImpl)load(pc,child, rawPath, Boolean.TRUE, Boolean.TRUE,interfaceUDFs,false); 064 } 065 066 public static Page loadPage(PageContext pc,PageSource child,String rawPath, Boolean searchLocal, Boolean searchRoot) throws PageException { 067 return (Page)load(pc, child,rawPath, searchLocal, searchRoot,null,true); 068 } 069 070 071 private static Object load(PageContext pc,PageSource child,String rawPath, Boolean searchLocal, Boolean searchRoot, Map interfaceUDFs, boolean returnPage) throws PageException { 072 ConfigImpl config=(ConfigImpl) pc.getConfig(); 073 boolean doCache=config.useComponentPathCache(); 074 075 //print.o(rawPath); 076 //app-String appName=pc.getApplicationContext().getName(); 077 rawPath=rawPath.trim().replace('\\','/'); 078 String path=(rawPath.indexOf("./")==-1)?rawPath.replace('.','/'):rawPath; 079 String pathWithCFC=path.concat(".cfc"); 080 boolean isRelPath=!StringUtil.startsWith(pathWithCFC,'/'); 081 PageSource currPS = pc.getCurrentPageSource(); 082 Page currP=((PageSourceImpl)currPS).loadPage(pc,(Page)null); 083 PageSource ps=null; 084 Page page=null; 085 086 // no cache for per application pathes 087 Mapping[] acm = pc.getApplicationContext().getComponentMappings(); 088 if(!ArrayUtil.isEmpty(acm)) { 089 Mapping m; 090 for(int y=0;y<acm.length;y++){ 091 m=acm[y]; 092 ps=m.getPageSource(pathWithCFC); 093 page=((PageSourceImpl)ps).loadPage(pc,(Page)null); 094 if(page!=null) { 095 return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 096 } 097 } 098 } 099 100 101 102 103 104 105 106 if(searchLocal==null) 107 searchLocal=Caster.toBoolean(config.getComponentLocalSearch()); 108 if(searchRoot==null) 109 searchRoot=Caster.toBoolean(config.getComponentRootSearch()); 110 111 //boolean searchLocal=config.getComponentLocalSearch(); 112 // CACHE 113 // check local in cache 114 String localCacheName=null; 115 if(searchLocal && isRelPath){ 116 localCacheName=currPS.getDisplayPath().replace('\\', '/'); 117 localCacheName=localCacheName.substring(0,localCacheName.lastIndexOf('/')+1).concat(pathWithCFC); 118 if(doCache){ 119 page=config.getCachedPage(pc, localCacheName); 120 if(page!=null) return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 121 } 122 } 123 124 // check import cache 125 if(doCache && isRelPath){ 126 ImportDefintion impDef = config.getComponentDefaultImport(); 127 ImportDefintion[] impDefs=currP==null?new ImportDefintion[0]:currP.getImportDefintions(); 128 int i=-1; 129 do{ 130 131 if(impDef.isWildcard() || impDef.getName().equalsIgnoreCase(path)){ 132 page=config.getCachedPage(pc, "import:"+impDef.getPackageAsPath()+pathWithCFC); 133 if(page!=null) return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 134 135 //app-page=config.getCachedPage(pc, "import:"+appName+":"+impDef.getPackageAsPath()+pathWithCFC); 136 //app-if(page!=null) return load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRealPath,interfaceUDFs); 137 } 138 impDef=++i<impDefs.length?impDefs[i]:null; 139 } 140 while(impDef!=null); 141 } 142 143 if(doCache) { 144 // check global in cache 145 page=config.getCachedPage(pc, pathWithCFC); 146 if(page!=null) return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 147 148 // get pages from application mappings 149 //app-page=config.getCachedPage(pc, ":"+appName+":"+pathWithCFC); 150 //app-if(page!=null) return load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRealPath,interfaceUDFs); 151 } 152 153 // SEARCH 154 // search from local 155 if(searchLocal && isRelPath) { 156 // check relpath 157 PageSource[] arr = ((PageContextImpl)pc).getRelativePageSources(pathWithCFC); 158 page=PageSourceImpl.loadPage(pc, arr,null); 159 if(page!=null){ 160 if(doCache)config.putCachedPageSource(localCacheName, page.getPageSource()); 161 return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 162 } 163 } 164 165 // search with imports 166 Mapping[] cMappings = config.getComponentMappings(); 167 168 if(isRelPath){ 169 170 ImportDefintion impDef = config.getComponentDefaultImport(); 171 ImportDefintion[] impDefs=currP==null?new ImportDefintion[0]:currP.getImportDefintions(); 172 PageSource[] arr; 173 174 175 int i=-1; 176 do{ 177 if(impDef.isWildcard() || impDef.getName().equalsIgnoreCase(path)){ 178 179 // search from local first 180 if(searchLocal){ 181 arr = ((PageContextImpl)pc).getRelativePageSources(impDef.getPackageAsPath()+pathWithCFC); 182 page=PageSourceImpl.loadPage(pc, arr,null); 183 if(page!=null) { 184 if(doCache)config.putCachedPageSource("import:"+impDef.getPackageAsPath()+pathWithCFC, page.getPageSource()); 185 return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 186 } 187 } 188 189 // search local component mappings 190 /*app-if(lcMappings!=null) { 191 Mapping m; 192 for(int y=0;y<lcMappings.length;y++){ 193 m=lcMappings[y]; 194 String key=appName+":"+impDef.getPackageAsPath()+pathWithCFC; 195 ps=m.getPageSource(impDef.getPackageAsPath()+pathWithCFC); 196 page=((PageSourceImpl)ps).loadPage(pc,pc.getConfig(),null); 197 if(page!=null) { 198 if(doCache)config.putCachedPageSource("import:"+key, page.getPageSource()); 199 return load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRealPath,interfaceUDFs); 200 } 201 } 202 }*/ 203 204 // search mappings and webroot 205 page=PageSourceImpl.loadPage(pc, ((PageContextImpl)pc).getPageSources("/"+impDef.getPackageAsPath()+pathWithCFC), null); 206 if(page!=null){ 207 String key=impDef.getPackageAsPath()+pathWithCFC; 208 if(doCache && !((MappingImpl)page.getPageSource().getMapping()).isAppMapping()) 209 config.putCachedPageSource("import:"+key, page.getPageSource()); 210 return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 211 } 212 213 // search component mappings 214 Mapping m; 215 for(int y=0;y<cMappings.length;y++){ 216 m=cMappings[y]; 217 ps=m.getPageSource(impDef.getPackageAsPath()+pathWithCFC); 218 page=((PageSourceImpl)ps).loadPage(pc,(Page)null); 219 if(page!=null) { 220 if(doCache)config.putCachedPageSource("import:"+impDef.getPackageAsPath()+pathWithCFC, page.getPageSource()); 221 return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 222 } 223 } 224 } 225 impDef=++i<impDefs.length?impDefs[i]:null; 226 } 227 while(impDef!=null); 228 229 } 230 231 String p; 232 if(isRelPath) p='/'+pathWithCFC; 233 else p=pathWithCFC; 234 235 236 237 // search local component mappings 238 /* app-if(lcMappings!=null) { 239 Mapping m; 240 for(int i=0;i<lcMappings.length;i++){ 241 m=lcMappings[i]; 242 ps=m.getPageSource(p); 243 page=((PageSourceImpl)ps).loadPage(pc,pc.getConfig(),null); 244 String key=":"+appName+":"+pathWithCFC; 245 if(page!=null){ 246 if(doCache)config.putCachedPageSource(key, page.getPageSource()); 247 return load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRealPath,interfaceUDFs); 248 } 249 } 250 }*/ 251 252 // search mappings and webroot 253 page=PageSourceImpl.loadPage(pc,((PageContextImpl)pc).getPageSources(p),null); 254 if(page!=null){ 255 String key=pathWithCFC; 256 if(doCache && !((MappingImpl)page.getPageSource().getMapping()).isAppMapping()) 257 config.putCachedPageSource(key, page.getPageSource()); 258 return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 259 } 260 261 // search component mappings 262 Mapping m; 263 for(int i=0;i<cMappings.length;i++){ 264 m=cMappings[i]; 265 ps=m.getPageSource(p); 266 page=((PageSourceImpl)ps).loadPage(pc,(Page)null); 267 268 // recursive search 269 if(page==null && config.doComponentDeepSearch() && path.indexOf('/')==-1) { 270 ps=MappingUtil.searchMappingRecursive(m, pathWithCFC, true); 271 if(ps!=null) { 272 page = ((PageSourceImpl)ps).loadPage(pc,(Page)null); 273 if(page!=null) doCache=false;// do not cache this, it could be ambigous 274 } 275 } 276 277 if(page!=null){ 278 if(doCache)config.putCachedPageSource(pathWithCFC, page.getPageSource()); 279 return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 280 } 281 } 282 283 284 // search relative to active cfc (this get not cached because the cache get ambigous if we do) 285 if(searchLocal && isRelPath) { 286 if(child==null) { 287 Component cfc = pc.getActiveComponent(); 288 if(cfc!=null) child = cfc.getPageSource(); 289 } 290 291 292 if(child!=null) { 293 ps=child.getRealPage(pathWithCFC); 294 if(ps!=null) { 295 page=((PageSourceImpl)ps).loadPage(pc,(Page)null); 296 297 if(page!=null){ 298 return returnPage?page:load(pc,page,page.getPageSource(),trim(path.replace('/', '.')),isRelPath,interfaceUDFs); 299 } 300 } 301 } 302 } 303 304 // translate cfide. to org.lucee.cfml 305 if(StringUtil.startsWithIgnoreCase(rawPath, "cfide.")) { 306 String rpm="org.lucee.cfml."+rawPath.substring(6); 307 try{ 308 return load(pc,child,rpm, searchLocal, searchRoot, interfaceUDFs,returnPage); 309 } 310 catch(ExpressionException ee){ 311 throw new ExpressionException("invalid "+(interfaceUDFs==null?"component":"interface")+" definition, can't find "+rawPath+" or "+rpm); 312 } 313 } 314 throw new ExpressionException("invalid "+(interfaceUDFs==null?"component":"interface")+" definition, can't find "+(interfaceUDFs==null?"component":"interface")+" ["+rawPath+"]"); 315 316 317 318 } 319 320 private static String getPagePath(PageContext pc, Resource res, String dir,String name, ResourceFilter filter) { 321 if(res.isFile()) { 322 if(res.getName().equalsIgnoreCase(name)) { 323 return dir+res.getName(); 324 } 325 } 326 else if(res.isDirectory()) { 327 Resource[] _dir = res.listResources(filter); 328 if(_dir!=null){ 329 if(dir==null) dir="/"; 330 else dir=dir+res.getName()+"/"; 331 String path; 332 for(int i=0;i<_dir.length;i++){ 333 path=getPagePath(pc, _dir[i],dir, name,DIR_OR_EXT); 334 if(path!=null) return path; 335 } 336 } 337 } 338 339 return null; 340 } 341 342 private static String trim(String str) { 343 if(StringUtil.startsWith(str, '.'))str=str.substring(1); 344 return str; 345 } 346 347 /*private static Page getPage(PageContext pc,String path, RefBoolean isRealPath, boolean searchLocal) throws PageException { 348 Page page=null; 349 isRealPath.setValue(!StringUtil.startsWith(path,'/')); 350 // search from local 351 PageSource[] arr; 352 353 if(searchLocal && isRealPath.toBooleanValue()){ 354 arr = ((PageContextImpl)pc).getRelativePageSources(path); 355 page=PageSourceImpl.loadPage(pc, arr, null); 356 } 357 // search from root 358 if(page==null) { 359 if(isRealPath.toBooleanValue()){ 360 isRealPath.setValue(false); 361 arr=((PageContextImpl)pc).getPageSources('/'+path); 362 } 363 else { 364 arr=((PageContextImpl)pc).getPageSources(path); 365 } 366 page=PageSourceImpl.loadPage(pc,arr,null); 367 } 368 return page; 369 }*/ 370 371 372 // 373 374 public static ComponentImpl loadComponent(PageContext pc,Page page, PageSource ps,String callPath, boolean isRealPath, boolean silent) throws PageException { 375 /*if(page==null && ps instanceof PageSourceImpl) { 376 page=((PageSourceImpl)ps).getPage(); 377 }*/ 378 if(silent) { 379 // TODO is there a more direct way 380 BodyContent bc = pc.pushBody(); 381 try { 382 return loadComponent(pc,page,ps,callPath,isRealPath); 383 } 384 finally { 385 BodyContentUtil.clearAndPop(pc, bc); 386 } 387 } 388 return loadComponent(pc,page,ps,callPath,isRealPath); 389 } 390 391 392 private static Object load(PageContext pc,Page page, PageSource ps,String callPath, boolean isRealPath, Map interfaceUDFs) throws PageException { 393 if(interfaceUDFs==null) return loadComponent(pc,page, ps,callPath, isRealPath); 394 return loadInterface(pc,page, ps, callPath, isRealPath, interfaceUDFs); 395 } 396 397 public static Page loadPage(PageContext pc,PageSource ps, boolean forceReload) throws PageException { 398 if(pc.getConfig().debug()) { 399 DebugEntryTemplate debugEntry=pc.getDebugger().getEntry(pc,ps); 400 pc.addPageSource(ps,true); 401 PageContextImpl pci=(PageContextImpl) pc; 402 long currTime=pci.getExecutionTimeLong(); 403 long exeTime=0; 404 long time=System.currentTimeMillis(); 405 try { 406 debugEntry.updateFileLoadTime((int)(System.currentTimeMillis()-time)); 407 exeTime=System.currentTimeMillis(); 408 return ((PageSourceImpl)ps).loadPage(pc,forceReload); 409 } 410 finally { 411 long diff= ((System.currentTimeMillis()-exeTime)-(pci.getExecutionTimeLong()-currTime)); 412 pci.setExecutionTimeLong(pci.getExecutionTimeLong()+(System.currentTimeMillis()-time)); 413 debugEntry.updateExeTime(diff); 414 pc.removeLastPageSource(true); 415 } 416 } 417 // no debug 418 pc.addPageSource(ps,true); 419 try { 420 return ((PageSourceImpl)ps).loadPage(pc,forceReload); 421 } 422 finally { 423 pc.removeLastPageSource(true); 424 } 425 } 426 427 public static ComponentImpl loadComponent(PageContext pc,Page page, PageSource ps,String callPath, boolean isRealPath) throws PageException { 428 ComponentImpl rtn=null; 429 PageContextImpl pci=(PageContextImpl) pc; 430 if(pc.getConfig().debug()) { 431 DebugEntryTemplate debugEntry=pc.getDebugger().getEntry(pc,ps); 432 pc.addPageSource(ps,true); 433 long currTime=pci.getExecutionTimeLong(); 434 long exeTime=0; 435 long time=System.nanoTime(); 436 try { 437 debugEntry.updateFileLoadTime((int)(System.nanoTime()-time)); 438 exeTime=System.nanoTime(); 439 if(page==null)page=((PageSourceImpl)ps).loadPage(pc); 440 rtn=initComponent(pc,page,callPath,isRealPath); 441 442 443 } 444 finally { 445 if(rtn!=null)rtn.setLoaded(true); 446 long diff= ((System.nanoTime()-exeTime)-(pci.getExecutionTimeLong()-currTime)); 447 pci.setExecutionTimeLong(pci.getExecutionTimeLong()+(System.nanoTime()-time)); 448 debugEntry.updateExeTime(diff); 449 pc.removeLastPageSource(true); 450 } 451 } 452 // no debug 453 else { 454 pc.addPageSource(ps,true); 455 try { 456 if(page==null)page=((PageSourceImpl)ps).loadPage(pc); 457 rtn=initComponent(pc,page,callPath,isRealPath); 458 } 459 finally { 460 if(rtn!=null)rtn.setLoaded(true); 461 pc.removeLastPageSource(true); 462 } 463 } 464 465 return rtn; 466 } 467 468 public static InterfaceImpl loadInterface(PageContext pc,Page page, PageSource ps,String callPath, boolean isRealPath, Map interfaceUDFs) throws PageException { 469 InterfaceImpl rtn=null; 470 if(pc.getConfig().debug()) { 471 DebugEntryTemplate debugEntry=pc.getDebugger().getEntry(pc,ps); 472 pc.addPageSource(ps,true); 473 PageContextImpl pci=(PageContextImpl) pc; 474 long currTime=pci.getExecutionTimeLong(); 475 long exeTime=0; 476 long time=System.nanoTime(); 477 try { 478 debugEntry.updateFileLoadTime((int)(System.nanoTime()-time)); 479 exeTime=System.nanoTime(); 480 if(page==null)page=((PageSourceImpl)ps).loadPage(pc); 481 rtn=initInterface(pc,page,callPath,isRealPath,interfaceUDFs); 482 } 483 finally { 484 long diff= ((System.nanoTime()-exeTime)-(pci.getExecutionTimeLong()-currTime)); 485 pci.setExecutionTimeLong(pci.getExecutionTimeLong()+(System.nanoTime()-time)); 486 debugEntry.updateExeTime(diff); 487 pc.removeLastPageSource(true); 488 } 489 } 490 // no debug 491 else { 492 pc.addPageSource(ps,true); 493 try { 494 if(page==null)page=((PageSourceImpl)ps).loadPage(pc); 495 rtn=initInterface(pc,page,callPath,isRealPath,interfaceUDFs); 496 } 497 finally { 498 pc.removeLastPageSource(true); 499 } 500 } 501 return rtn; 502 } 503 504 505 506 /* * 507 * load a component 508 * @param rawPath 509 * @return loaded component 510 * @throws PageException 511 * / 512 public static InterfaceImpl loadInterface(PageContext pc,String rawPath,boolean allowRemovingExt, Map interfaceUDFs) throws PageException { 513 // MUSTMUST sync code with extends 514 515 String fullName=rawPath; 516 boolean hasRemovedExt=false; 517 518 fullName=fullName.trim().replace('\\','/').replace('.','/'); 519 String path=fullName.concat(".cfc"); 520 521 522 boolean isRealPath=!StringUtil.startsWith(fullName,'/'); 523 524 PageSource res=pc.getRelativePageSource(path); 525 Page page=null; 526 //try { 527 page=((PageSourceImpl)res).loadPage(pc,pc.getConfig(),null); 528 if(page==null && isRealPath) { 529 isRealPath=false; 530 PageSource resOld = res; 531 res=pc.getPageSource('/'+path); 532 page=((PageSourceImpl)res).loadPage(pc,pc.getConfig(),null); 533 if(page==null) { 534 if(hasRemovedExt)return loadInterface(pc,rawPath, false,interfaceUDFs); 535 String detail="search for "+res.getDisplayPath(); 536 if(resOld!=null)detail+=" and "+resOld.getDisplayPath(); 537 538 if(StringUtil.startsWithIgnoreCase(rawPath, "cfide.")) { 539 String rpm="org.lucee.cfml."+rawPath.substring(6); 540 try{ 541 return loadInterface(pc,rpm,allowRemovingExt, interfaceUDFs); 542 } 543 catch(ExpressionException ee){ 544 throw new ExpressionException("invalid interface definition, can't find "+rawPath+" or "+rpm,detail); 545 } 546 } 547 else throw new ExpressionException("invalid interface definition, can't find "+rawPath,detail); 548 549 550 } 551 } 552 return loadInterface(pc,page,res,fullName.replace('/', '.'),isRealPath,interfaceUDFs); 553 }*/ 554 555 556 557 558 559 private static InterfaceImpl initInterface(PageContext pc,Page page,String callPath,boolean isRealPath, Map interfaceUDFs) throws PageException { 560 if(!(page instanceof InterfacePage)) 561 throw new ApplicationException("invalid interface definition ["+callPath+"]"); 562 InterfacePage ip=(InterfacePage)page; 563 InterfaceImpl i = ip.newInstance(callPath,isRealPath,interfaceUDFs); 564 return i; 565 } 566 567 private static ComponentImpl initComponent(PageContext pc,Page page,String callPath,boolean isRealPath) throws PageException { 568 if(!(page instanceof ComponentPage)){ 569 if(page instanceof InterfacePage) 570 throw new ApplicationException("can not instantiate interface ["+callPath+"] as a component"); 571 throw new ApplicationException("invalid component definition ["+callPath+"]"); 572 } 573 574 ComponentPage cp = (ComponentPage)page; 575 576 ComponentImpl c = cp.newInstance(pc,callPath,isRealPath); 577 c.setInitalized(true); 578 return c; 579 580 } 581}