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.listener; 020 021import java.io.IOException; 022import java.io.OutputStream; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.Map.Entry; 028 029import javax.servlet.http.Cookie; 030 031import lucee.commons.io.DevNullOutputStream; 032import lucee.commons.io.res.Resource; 033import lucee.commons.io.res.util.ResourceUtil; 034import lucee.commons.lang.StringUtil; 035import lucee.commons.lang.mimetype.MimeType; 036import lucee.commons.lang.types.RefBoolean; 037import lucee.commons.lang.types.RefBooleanImpl; 038import lucee.runtime.CFMLFactory; 039import lucee.runtime.Component; 040import lucee.runtime.ComponentPage; 041import lucee.runtime.ComponentPro; 042import lucee.runtime.PageContext; 043import lucee.runtime.PageContextImpl; 044import lucee.runtime.PageSource; 045import lucee.runtime.component.ComponentLoader; 046import lucee.runtime.component.Member; 047import lucee.runtime.engine.ThreadLocalPageContext; 048import lucee.runtime.exp.Abort; 049import lucee.runtime.exp.MissingIncludeException; 050import lucee.runtime.exp.PageException; 051import lucee.runtime.exp.PostContentAbort; 052import lucee.runtime.interpreter.JSONExpressionInterpreter; 053import lucee.runtime.net.http.HttpServletRequestDummy; 054import lucee.runtime.net.http.HttpServletResponseDummy; 055import lucee.runtime.net.http.ReqRspUtil; 056import lucee.runtime.op.Caster; 057import lucee.runtime.op.Decision; 058import lucee.runtime.op.Duplicator; 059import lucee.runtime.orm.ORMUtil; 060import lucee.runtime.type.Array; 061import lucee.runtime.type.ArrayImpl; 062import lucee.runtime.type.Collection; 063import lucee.runtime.type.Collection.Key; 064import lucee.runtime.type.KeyImpl; 065import lucee.runtime.type.Struct; 066import lucee.runtime.type.scope.UndefinedImpl; 067import lucee.runtime.type.util.ArrayUtil; 068import lucee.runtime.type.util.KeyConstants; 069import lucee.runtime.type.util.UDFUtil; 070 071public class ModernAppListener extends AppListenerSupport { 072 073 074 075 076 077 private static final Collection.Key ON_REQUEST_START = KeyImpl.intern("onRequestStart"); 078 private static final Collection.Key ON_CFCREQUEST = KeyImpl.intern("onCFCRequest"); 079 private static final Collection.Key ON_REQUEST = KeyImpl.intern("onRequest"); 080 private static final Collection.Key ON_REQUEST_END = KeyImpl.intern("onRequestEnd"); 081 private static final Collection.Key ON_ABORT = KeyImpl.intern("onAbort"); 082 private static final Collection.Key ON_APPLICATION_START = KeyImpl.intern("onApplicationStart"); 083 private static final Collection.Key ON_APPLICATION_END = KeyImpl.intern("onApplicationEnd"); 084 private static final Collection.Key ON_SESSION_START = KeyImpl.intern("onSessionStart"); 085 private static final Collection.Key ON_SESSION_END = KeyImpl.intern("onSessionEnd"); 086 private static final Collection.Key ON_DEBUG = KeyImpl.intern("onDebug"); 087 private static final Collection.Key ON_ERROR = KeyImpl.intern("onError"); 088 private static final Collection.Key ON_MISSING_TEMPLATE = KeyImpl.intern("onMissingTemplate"); 089 090 091 //private ComponentImpl app; 092 private Map<String,ComponentPro> apps=new HashMap<String,ComponentPro>(); 093 protected int mode=MODE_CURRENT2ROOT; 094 095 @Override 096 public void onRequest(PageContext pc, PageSource requestedPage, RequestListener rl) throws PageException { 097 // on requestStart 098 PageSource appPS=//pc.isCFCRequest()?null: 099 AppListenerUtil.getApplicationPageSource(pc,requestedPage,mode,AppListenerUtil.TYPE_NEW,null); 100 101 _onRequest(pc, requestedPage, appPS,rl); 102 } 103 104 protected void _onRequest(PageContext pc, PageSource requestedPage,PageSource appPS, RequestListener rl) throws PageException { 105 PageContextImpl pci = (PageContextImpl)pc; 106 pci.setAppListenerType(AppListenerUtil.TYPE_NEW); 107 108 if(appPS!=null) { 109 String callPath=appPS.getComponentName(); 110 111 112 ComponentPro app = ComponentLoader.loadComponent(pci,null,appPS, callPath, false,false); 113 114 // init 115 initApplicationContext(pci,app); 116 117 118 apps.put(pc.getApplicationContext().getName(), app); 119 120 if(!pci.initApplicationContext(this)) return; 121 122 if(rl!=null) { 123 requestedPage=rl.execute(pc, requestedPage); 124 if(requestedPage==null) return; 125 } 126 127 String targetPage=requestedPage.getFullRealpath(); 128 RefBoolean goon=new RefBooleanImpl(true); 129 130 // onRequestStart 131 if(app.contains(pc,ON_REQUEST_START)) { 132 try { 133 Object rtn=call(app,pci, ON_REQUEST_START, new Object[]{targetPage},false); 134 if(!Caster.toBooleanValue(rtn,true)) 135 return; 136 } 137 catch(PageException pe){ 138 pe=handlePageException(pci,app,pe,requestedPage,targetPage,goon); 139 if(pe!=null) throw pe; 140 } 141 } 142 143 // onRequest 144 if(goon.toBooleanValue()) { 145 boolean isCFC=ResourceUtil.getExtension(targetPage,"").equalsIgnoreCase(pc.getConfig().getCFCExtension()); 146 Object method; 147 if(isCFC && app.contains(pc,ON_CFCREQUEST) && (method=pc.urlFormScope().get(KeyConstants._method,null))!=null) { 148 149 Struct url = (Struct)Duplicator.duplicate(pc.urlFormScope(),true); 150 151 url.removeEL(KeyConstants._fieldnames); 152 url.removeEL(KeyConstants._method); 153 154 Object args=url.get(KeyConstants._argumentCollection,null); 155 156 // url returnFormat 157 Object oReturnFormat=url.removeEL(KeyConstants._returnFormat); 158 int urlReturnFormat=-1; 159 if(oReturnFormat!=null) urlReturnFormat=UDFUtil.toReturnFormat(Caster.toString(oReturnFormat,null),-1); 160 161 // request header accept 162 List<MimeType> accept = ReqRspUtil.getAccept(pc); 163 int headerReturnFormat = MimeType.toFormat(accept, -1,-1); 164 165 Object queryFormat=url.removeEL(KeyConstants._queryFormat); 166 167 if(args==null){ 168 args=pc.getHttpServletRequest().getAttribute("argumentCollection"); 169 } 170 171 if(args instanceof String){ 172 args=new JSONExpressionInterpreter().interpret(pc, (String)args); 173 } 174 175 if(args!=null) { 176 if(Decision.isCastableToStruct(args)){ 177 Struct sct = Caster.toStruct(args,false); 178 //Key[] keys = url.keys(); 179 Iterator<Entry<Key, Object>> it = url.entryIterator(); 180 Entry<Key, Object> e; 181 while(it.hasNext()){ 182 e = it.next(); 183 sct.setEL(e.getKey(),e.getValue()); 184 } 185 args=sct; 186 } 187 else if(Decision.isCastableToArray(args)){ 188 args = Caster.toArray(args); 189 } 190 else { 191 Array arr = new ArrayImpl(); 192 arr.appendEL(args); 193 args=arr; 194 } 195 } 196 else 197 args=url; 198 199 Object rtn = call(app,pci, ON_CFCREQUEST, new Object[]{requestedPage.getComponentName(),method,args},true); 200 201 if(rtn!=null){ 202 if(pc.getHttpServletRequest().getHeader("AMF-Forward")!=null) { 203 pc.variablesScope().setEL("AMF-Forward", rtn); 204 //ThreadLocalWDDXResult.set(rtn); 205 } 206 else { 207 try { 208 ComponentPage.writeToResponseStream(pc,app,method.toString(),urlReturnFormat,headerReturnFormat,queryFormat,rtn); 209 } catch (Exception e) { 210 throw Caster.toPageException(e); 211 } 212 } 213 } 214 215 216 } 217 //else if(!isCFC && app.contains(pc,ON_REQUEST)) {} 218 else { 219 // TODO impl die nicht so generisch ist 220 try{ 221 222 if(!isCFC && app.contains(pc,ON_REQUEST)) 223 call(app,pci, ON_REQUEST, new Object[]{targetPage},false); 224 else 225 pci.doInclude(requestedPage); 226 } 227 catch(PageException pe){ 228 pe=handlePageException(pci,app,pe,requestedPage,targetPage,goon); 229 if(pe!=null) throw pe; 230 } 231 } 232 } 233 // onRequestEnd 234 if(goon.toBooleanValue() && app.contains(pc,ON_REQUEST_END)) { 235 try { 236 call(app,pci, ON_REQUEST_END, new Object[]{targetPage},false); 237 } 238 catch(PageException pe){ 239 pe=handlePageException(pci,app,pe,requestedPage,targetPage,goon); 240 if(pe!=null) throw pe; 241 } 242 } 243 } 244 else { 245 apps.put(pc.getApplicationContext().getName(), null); 246 pc.doInclude(requestedPage); 247 } 248 } 249 250 251 private PageException handlePageException(PageContextImpl pci, ComponentPro app, PageException pe, PageSource requestedPage, String targetPage, RefBoolean goon) throws PageException { 252 PageException _pe=pe; 253 if(pe instanceof ModernAppListenerException) { 254 _pe=((ModernAppListenerException) pe).getPageException(); 255 } 256 257 if(!Abort.isSilentAbort(_pe)) { 258 if(_pe instanceof MissingIncludeException){ 259 if(((MissingIncludeException) _pe).getPageSource().equals(requestedPage)){ 260 261 if(app.contains(pci,ON_MISSING_TEMPLATE)) { 262 goon.setValue(false); 263 if(!Caster.toBooleanValue(call(app,pci, ON_MISSING_TEMPLATE, new Object[]{targetPage},true),true)) 264 return pe; 265 } 266 else return pe; 267 } 268 else return pe; 269 } 270 else return pe; 271 } 272 else { 273 goon.setValue(false); 274 if(app.contains(pci,ON_ABORT)) { 275 call(app,pci, ON_ABORT, new Object[]{targetPage},true); 276 } 277 } 278 return null; 279 } 280 281 @Override 282 public boolean onApplicationStart(PageContext pc) throws PageException { 283 ComponentPro app = apps.get(pc.getApplicationContext().getName()); 284 if(app!=null && app.contains(pc,ON_APPLICATION_START)) { 285 Object rtn = call(app,pc, ON_APPLICATION_START, ArrayUtil.OBJECT_EMPTY,true); 286 return Caster.toBooleanValue(rtn,true); 287 } 288 return true; 289 } 290 291 @Override 292 public void onApplicationEnd(CFMLFactory factory, String applicationName) throws PageException { 293 ComponentPro app = apps.get(applicationName); 294 if(app==null || !app.containsKey(ON_APPLICATION_END)) return; 295 296 PageContextImpl pc=(PageContextImpl) ThreadLocalPageContext.get(); 297 boolean createPc=pc==null; 298 try { 299 if(createPc)pc = createPageContext(factory,app,applicationName,null,ON_APPLICATION_END); 300 call(app,pc, ON_APPLICATION_END, new Object[]{pc.applicationScope()},true); 301 } 302 finally { 303 if(createPc && pc!=null){ 304 factory.releasePageContext(pc); 305 } 306 } 307 } 308 309 @Override 310 public void onSessionStart(PageContext pc) throws PageException { 311 ComponentPro app = apps.get(pc.getApplicationContext().getName()); 312 if(hasOnSessionStart(pc,app)) { 313 call(app,pc, ON_SESSION_START, ArrayUtil.OBJECT_EMPTY,true); 314 } 315 } 316 317 @Override 318 public boolean hasOnSessionEnd(String applicationName) { 319 ComponentPro app = apps.get(applicationName); 320 return app!=null && app.containsKey(ON_SESSION_END); 321 } 322 323 @Override 324 public void onSessionEnd(CFMLFactory factory, String applicationName, String cfid) throws PageException { 325 ComponentPro app = apps.get(applicationName); 326 if(app==null || !app.containsKey(ON_SESSION_END)) return; 327 328 PageContextImpl pc=null; 329 try { 330 pc = createPageContext(factory,app,applicationName,cfid,ON_SESSION_END); 331 call(app,pc, ON_SESSION_END, new Object[]{pc.sessionScope(false),pc.applicationScope()},true); 332 } 333 finally { 334 if(pc!=null){ 335 factory.releasePageContext(pc); 336 } 337 } 338 } 339 340 private PageContextImpl createPageContext(CFMLFactory factory, ComponentPro app, String applicationName, String cfid,Collection.Key methodName) throws PageException { 341 Resource root = factory.getConfig().getRootDirectory(); 342 String path = app.getPageSource().getFullRealpath(); 343 344 // Request 345 HttpServletRequestDummy req = new HttpServletRequestDummy(root,"localhost",path,"",null,null,null,null,null); 346 if(!StringUtil.isEmpty(cfid))req.setCookies(new Cookie[]{new Cookie("cfid",cfid),new Cookie("cftoken","0")}); 347 348 // Response 349 OutputStream os=DevNullOutputStream.DEV_NULL_OUTPUT_STREAM; 350 351 // File based output stream 352 /*try { 353 Resource out = factory.getConfig().getConfigDir().getRealResource("output/"+methodName.getString()+".out"); 354 out.getParentResource().mkdirs(); 355 os = out.getOutputStream(false); 356 } 357 catch (IOException e) {}*/ 358 359 360 HttpServletResponseDummy rsp = new HttpServletResponseDummy(os); 361 362 // PageContext 363 PageContextImpl pc = (PageContextImpl) factory.getLuceePageContext(factory.getServlet(), req, rsp, null, false, -1, false); 364 // ApplicationContext 365 ClassicApplicationContext ap = new ClassicApplicationContext(factory.getConfig(),applicationName,false,app==null?null:ResourceUtil.getResource(pc,app.getPageSource(),null)); 366 initApplicationContext(pc, app); 367 ap.setName(applicationName); 368 ap.setSetSessionManagement(true); 369 //if(!ap.hasName())ap.setName("Controler") 370 // Base 371 pc.setBase(app.getPageSource()); 372 373 return pc; 374 } 375 376 @Override 377 public void onDebug(PageContext pc) throws PageException { 378 if(((PageContextImpl)pc).isGatewayContext() || !pc.getConfig().debug()) return; 379 ComponentPro app = apps.get(pc.getApplicationContext().getName()); 380 if(app!=null && app.contains(pc,ON_DEBUG)) { 381 call(app,pc, ON_DEBUG, new Object[]{pc.getDebugger().getDebuggingData(pc)},true); 382 return; 383 } 384 try { 385 pc.getDebugger().writeOut(pc); 386 } 387 catch (IOException e) { 388 throw Caster.toPageException(e); 389 } 390 } 391 392 @Override 393 public void onError(PageContext pc, PageException pe) { 394 ComponentPro app = apps.get(pc.getApplicationContext().getName()); 395 if(app!=null && app.containsKey(ON_ERROR) && !Abort.isSilentAbort(pe)) { 396 try { 397 String eventName=""; 398 if(pe instanceof ModernAppListenerException) eventName= ((ModernAppListenerException)pe).getEventName(); 399 if(eventName==null)eventName=""; 400 401 call(app,pc, ON_ERROR, new Object[]{pe.getCatchBlock(pc),eventName},true); 402 return; 403 } 404 catch(PageException _pe) { 405 pe=_pe; 406 } 407 } 408 pc.handlePageException(pe); 409 } 410 411 412 private Object call(Component app, PageContext pc, Collection.Key eventName, Object[] args, boolean catchAbort) throws PageException { 413 try { 414 return app.call(pc, eventName, args); 415 } 416 catch (PageException pe) { 417 if(Abort.isSilentAbort(pe)) { 418 if(catchAbort) 419 return ( pe instanceof PostContentAbort) ? Boolean.TRUE : Boolean.FALSE; 420 421 throw pe; 422 } 423 throw new ModernAppListenerException(pe,eventName.getString()); 424 } 425 } 426 427 private void initApplicationContext(PageContextImpl pc, ComponentPro app) throws PageException { 428 429 // use existing app context 430 RefBoolean throwsErrorWhileInit=new RefBooleanImpl(false); 431 ModernApplicationContext appContext = new ModernApplicationContext(pc,app,throwsErrorWhileInit); 432 433 434 pc.setApplicationContext(appContext); 435 436 // scope cascading 437 438 if(((UndefinedImpl)pc.undefinedScope()).getScopeCascadingType()!=appContext.getScopeCascading()) { 439 pc.undefinedScope().initialize(pc); 440 } 441 442 443 // ORM 444 if(appContext.isORMEnabled()) { 445 boolean hasError=throwsErrorWhileInit.toBooleanValue(); 446 if(hasError)pc.addPageSource(app.getPageSource(), true); 447 try{ 448 ORMUtil.resetEngine(pc,false); 449 } 450 finally { 451 if(hasError)pc.removeLastPageSource(true); 452 } 453 } 454 } 455 456 457 private static Object get(ComponentPro app, Key name,String defaultValue) { 458 Member mem = app.getMember(Component.ACCESS_PRIVATE, name, true, false); 459 if(mem==null) return defaultValue; 460 return mem.getValue(); 461 } 462 463 @Override 464 public void setMode(int mode) { 465 this.mode=mode; 466 } 467 468 @Override 469 public int getMode() { 470 return mode; 471 } 472 473 474 @Override 475 public String getType() { 476 return "modern"; 477 } 478 479 @Override 480 public boolean hasOnSessionStart(PageContext pc) { 481 return hasOnSessionStart(pc, apps.get(pc.getApplicationContext().getName())); 482 } 483 private boolean hasOnSessionStart(PageContext pc,ComponentPro app) { 484 return app!=null && app.contains(pc,ON_SESSION_START); 485 } 486}