001 package railo.runtime.engine; 002 003 import java.io.ByteArrayOutputStream; 004 import java.io.IOException; 005 import java.net.MalformedURLException; 006 import java.net.URI; 007 import java.net.URISyntaxException; 008 import java.net.URL; 009 import java.util.ArrayList; 010 import java.util.Iterator; 011 import java.util.List; 012 import java.util.Map; 013 import java.util.Map.Entry; 014 015 import javax.servlet.ServletConfig; 016 import javax.servlet.ServletContext; 017 import javax.servlet.ServletException; 018 import javax.servlet.http.Cookie; 019 import javax.servlet.http.HttpServlet; 020 import javax.servlet.http.HttpServletRequest; 021 import javax.servlet.http.HttpServletResponse; 022 import javax.servlet.jsp.JspException; 023 024 import railo.cli.servlet.HTTPServletImpl; 025 import railo.commons.collections.HashTable; 026 import railo.commons.io.FileUtil; 027 import railo.commons.io.IOUtil; 028 import railo.commons.io.SystemUtil; 029 import railo.commons.io.res.Resource; 030 import railo.commons.io.res.ResourceProvider; 031 import railo.commons.io.res.ResourcesImpl; 032 import railo.commons.io.res.util.ResourceUtil; 033 import railo.commons.io.res.util.ResourceUtilImpl; 034 import railo.commons.lang.Pair; 035 import railo.commons.lang.StringUtil; 036 import railo.commons.lang.SystemOut; 037 import railo.commons.lang.types.RefBoolean; 038 import railo.commons.lang.types.RefBooleanImpl; 039 import railo.commons.net.HTTPUtil; 040 import railo.intergral.fusiondebug.server.FDControllerImpl; 041 import railo.loader.engine.CFMLEngine; 042 import railo.loader.engine.CFMLEngineFactory; 043 import railo.loader.engine.CFMLEngineWrapper; 044 import railo.loader.util.Util; 045 import railo.runtime.CFMLFactory; 046 import railo.runtime.CFMLFactoryImpl; 047 import railo.runtime.Info; 048 import railo.runtime.PageContext; 049 import railo.runtime.PageSource; 050 import railo.runtime.config.ConfigServerFactory; 051 import railo.runtime.config.ConfigServerImpl; 052 import railo.runtime.config.ConfigWeb; 053 import railo.runtime.config.ConfigWebFactory; 054 import railo.runtime.config.ConfigWebImpl; 055 import railo.runtime.exp.ApplicationException; 056 import railo.runtime.exp.PageException; 057 import railo.runtime.exp.PageServletException; 058 import railo.runtime.net.http.HTTPServletRequestWrap; 059 import railo.runtime.net.http.HttpServletRequestDummy; 060 import railo.runtime.net.http.HttpServletResponseDummy; 061 import railo.runtime.net.http.ReqRspUtil; 062 import railo.runtime.op.CastImpl; 063 import railo.runtime.op.Caster; 064 import railo.runtime.op.CreationImpl; 065 import railo.runtime.op.DecisionImpl; 066 import railo.runtime.op.ExceptonImpl; 067 import railo.runtime.op.OperationImpl; 068 import railo.runtime.query.QueryCacheSupport; 069 import railo.runtime.type.StructImpl; 070 import railo.runtime.util.BlazeDSImpl; 071 import railo.runtime.util.Cast; 072 import railo.runtime.util.Creation; 073 import railo.runtime.util.Decision; 074 import railo.runtime.util.Excepton; 075 import railo.runtime.util.HTTPUtilImpl; 076 import railo.runtime.util.Operation; 077 import railo.runtime.util.ZipUtil; 078 import railo.runtime.util.ZipUtilImpl; 079 import railo.runtime.video.VideoUtil; 080 import railo.runtime.video.VideoUtilImpl; 081 082 import com.intergral.fusiondebug.server.FDControllerFactory; 083 084 /** 085 * The CFMl Engine 086 */ 087 public final class CFMLEngineImpl implements CFMLEngine { 088 089 090 private static Map<String,CFMLFactory> initContextes=new HashTable(); 091 private static Map<String,CFMLFactory> contextes=new HashTable(); 092 private static ConfigServerImpl configServer=null; 093 private static CFMLEngineImpl engine=null; 094 //private ServletConfig config; 095 private CFMLEngineFactory factory; 096 private AMFEngine amfEngine=new AMFEngine(); 097 private final RefBoolean controlerState=new RefBooleanImpl(true); 098 private boolean allowRequestTimeout=true; 099 private Monitor monitor; 100 private List<ServletConfig> servletConfigs=new ArrayList<ServletConfig>(); 101 private long uptime; 102 103 //private static CFMLEngineImpl engine=new CFMLEngineImpl(); 104 105 private CFMLEngineImpl(CFMLEngineFactory factory) { 106 this.factory=factory; 107 CFMLEngineFactory.registerInstance(this);// patch, not really good but it works 108 ConfigServerImpl cs = getConfigServerImpl(); 109 110 SystemOut.printDate(SystemUtil.getPrintWriter(SystemUtil.OUT),"Start CFML Controller"); 111 Controler controler = new Controler(cs,initContextes,5*1000,controlerState); 112 controler.setDaemon(true); 113 controler.setPriority(Thread.MIN_PRIORITY); 114 controler.start(); 115 116 touchMonitor(cs); 117 this.uptime=System.currentTimeMillis(); 118 //this.config=config; 119 } 120 121 122 public void touchMonitor(ConfigServerImpl cs) { 123 if(monitor!=null && monitor.isAlive()) return; 124 monitor = new Monitor(cs,controlerState); 125 monitor.setDaemon(true); 126 monitor.setPriority(Thread.MIN_PRIORITY); 127 monitor.start(); 128 } 129 130 /** 131 * get singelton instance of the CFML Engine 132 * @param factory 133 * @return CFMLEngine 134 */ 135 public static synchronized CFMLEngine getInstance(CFMLEngineFactory factory) { 136 if(engine==null) { 137 engine=new CFMLEngineImpl(factory); 138 } 139 return engine; 140 } 141 142 /** 143 * get singelton instance of the CFML Engine, throwsexception when not already init 144 * @param factory 145 * @return CFMLEngine 146 */ 147 public static synchronized CFMLEngine getInstance() throws ServletException { 148 if(engine!=null) return engine; 149 throw new ServletException("CFML Engine is not loaded"); 150 } 151 152 @Override 153 public void addServletConfig(ServletConfig config) throws ServletException { 154 servletConfigs.add(config); 155 String real=ReqRspUtil.getRootPath(config.getServletContext()); 156 if(!initContextes.containsKey(real)) { 157 CFMLFactory jspFactory = loadJSPFactory(getConfigServerImpl(),config,initContextes.size()); 158 initContextes.put(real,jspFactory); 159 } 160 } 161 162 private ConfigServerImpl getConfigServerImpl() { 163 if(configServer==null) { 164 try { 165 ResourceProvider frp = ResourcesImpl.getFileResourceProvider(); 166 Resource context = frp.getResource(factory.getResourceRoot().getAbsolutePath()).getRealResource("context"); 167 //CFMLEngineFactory.registerInstance(this);// patch, not really good but it works 168 configServer=ConfigServerFactory.newInstance( 169 this, 170 initContextes, 171 contextes, 172 context); 173 } catch (Exception e) { 174 e.printStackTrace(); 175 } 176 } 177 return configServer; 178 } 179 180 private CFMLFactoryImpl loadJSPFactory(ConfigServerImpl configServer, ServletConfig sg, int countExistingContextes) throws ServletException { 181 try { 182 // Load Config 183 RefBoolean isCustomSetting=new RefBooleanImpl(); 184 Resource configDir=getConfigDirectory(sg,configServer,countExistingContextes,isCustomSetting); 185 186 QueryCacheSupport queryCache=QueryCacheSupport.getInstance(); 187 CFMLFactoryImpl factory=new CFMLFactoryImpl(this,queryCache); 188 ConfigWebImpl config=ConfigWebFactory.newInstance(factory,configServer,configDir,isCustomSetting.toBooleanValue(),sg); 189 factory.setConfig(config); 190 return factory; 191 } 192 catch (Exception e) { 193 ServletException se= new ServletException(e.getMessage()); 194 se.setStackTrace(e.getStackTrace()); 195 throw se; 196 } 197 198 } 199 200 /** 201 * loads Configuration File from System, from init Parameter from web.xml 202 * @param sg 203 * @param configServer 204 * @param countExistingContextes 205 * @return return path to directory 206 */ 207 private Resource getConfigDirectory(ServletConfig sg, ConfigServerImpl configServer, int countExistingContextes, RefBoolean isCustomSetting) throws PageServletException { 208 isCustomSetting.setValue(true); 209 ServletContext sc=sg.getServletContext(); 210 String strConfig=sg.getInitParameter("configuration"); 211 if(strConfig==null)strConfig=sg.getInitParameter("railo-web-directory"); 212 if(strConfig==null) { 213 isCustomSetting.setValue(false); 214 strConfig="{web-root-directory}/WEB-INF/railo/"; 215 } 216 // only for backward compatibility 217 else if(strConfig.startsWith("/WEB-INF/railo/"))strConfig="{web-root-directory}"+strConfig; 218 219 220 strConfig=Util.removeQuotes(strConfig,true); 221 222 223 224 // static path is not allowed 225 if(countExistingContextes>1 && strConfig!=null && strConfig.indexOf('{')==-1){ 226 String text="static path ["+strConfig+"] for servlet init param [railo-web-directory] is not allowed, path must use a web-context specific placeholder."; 227 System.err.println(text); 228 throw new PageServletException(new ApplicationException(text)); 229 } 230 strConfig=SystemUtil.parsePlaceHolder(strConfig,sc,configServer.getLabels()); 231 232 233 234 ResourceProvider frp = ResourcesImpl.getFileResourceProvider(); 235 Resource root = frp.getResource(ReqRspUtil.getRootPath(sc)); 236 Resource configDir=ResourceUtil.createResource(root.getRealResource(strConfig), FileUtil.LEVEL_PARENT_FILE,FileUtil.TYPE_DIR); 237 238 if(configDir==null) { 239 configDir=ResourceUtil.createResource(frp.getResource(strConfig), FileUtil.LEVEL_GRAND_PARENT_FILE,FileUtil.TYPE_DIR); 240 } 241 if(configDir==null) throw new PageServletException(new ApplicationException("path ["+strConfig+"] is invalid")); 242 if(!configDir.exists()){ 243 try { 244 configDir.createDirectory(true); 245 } 246 catch (IOException e) {} 247 } 248 return configDir; 249 } 250 251 @Override 252 253 public CFMLFactory getCFMLFactory(ServletContext srvContext, ServletConfig srvConfig,HttpServletRequest req) throws ServletException { 254 String real=ReqRspUtil.getRootPath(srvContext); 255 ConfigServerImpl cs = getConfigServerImpl(); 256 257 258 // Load JspFactory 259 CFMLFactoryImpl factory=null; 260 Object o=contextes.get(real); 261 if(o==null) { 262 //int size=sn.getContextCount(); 263 //if(size!=-1 && size <= contextes.size()) 264 //throw new ServletException("the maximum size of "+size+" web contextes is reached, " +"to have more contexes upgrade your railo version, already contextes in use are ["+getContextList()+"]"); 265 o=initContextes.get(real); 266 if(o!=null) { 267 factory=(CFMLFactoryImpl) o; 268 } 269 else { 270 factory=loadJSPFactory(cs,srvConfig,initContextes.size()); 271 initContextes.put(real,factory); 272 } 273 contextes.put(real,factory); 274 275 try { 276 String cp = req.getContextPath(); 277 if(cp==null)cp=""; 278 factory.setURL(new URL(req.getScheme(),req.getServerName(),req.getServerPort(),cp)); 279 } 280 catch (MalformedURLException e) { 281 e.printStackTrace(); 282 } 283 // 284 285 } 286 else { 287 factory=(CFMLFactoryImpl) o; 288 } 289 return factory; 290 } 291 292 @Override 293 public void serviceCFML(HttpServlet servlet, HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException { 294 295 CFMLFactory factory=getCFMLFactory(servlet.getServletContext(), servlet.getServletConfig(), req); 296 297 PageContext pc = factory.getRailoPageContext(servlet,req,rsp,null,false,-1,false); 298 ThreadQueue queue = factory.getConfig().getThreadQueue(); 299 queue.enter(pc); 300 try { 301 /*print.out("INCLUDE"); 302 print.out("servlet_path:"+req.getAttribute("javax.servlet.include.servlet_path")); 303 print.out("request_uri:"+req.getAttribute("javax.servlet.include.request_uri")); 304 print.out("context_path:"+req.getAttribute("javax.servlet.include.context_path")); 305 print.out("path_info:"+req.getAttribute("javax.servlet.include.path_info")); 306 print.out("query_string:"+req.getAttribute("javax.servlet.include.query_string")); 307 print.out("FORWARD"); 308 print.out("servlet_path:"+req.getAttribute("javax.servlet.forward.servlet_path")); 309 print.out("request_uri:"+req.getAttribute("javax.servlet.forward.request_uri")); 310 print.out("context_path:"+req.getAttribute("javax.servlet.forward.context_path")); 311 print.out("path_info:"+req.getAttribute("javax.servlet.forward.path_info")); 312 print.out("query_string:"+req.getAttribute("javax.servlet.forward.query_string")); 313 print.out("---"); 314 print.out(req.getServletPath()); 315 print.out(pc.getHttpServletRequest().getServletPath()); 316 */ 317 318 pc.execute(pc.getHttpServletRequest().getServletPath(),false); 319 } 320 catch (PageException pe) { 321 throw new PageServletException(pe); 322 } 323 finally { 324 queue.exit(pc); 325 factory.releaseRailoPageContext(pc); 326 FDControllerFactory.notifyPageComplete(); 327 } 328 } 329 330 public void serviceFile(HttpServlet servlet, HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException { 331 req=new HTTPServletRequestWrap(req); 332 CFMLFactory factory=getCFMLFactory(servlet.getServletContext(), servlet.getServletConfig(), req); 333 ConfigWeb config = factory.getConfig(); 334 PageSource ps = config.getPageSourceExisting(null, null, req.getServletPath(), false, true, true, false); 335 //Resource res = ((ConfigWebImpl)config).getPhysicalResourceExistingX(null, null, req.getServletPath(), false, true, true); 336 337 if(ps==null) { 338 rsp.sendError(404); 339 } 340 else { 341 Resource res = ps.getResource(); 342 if(res==null) { 343 rsp.sendError(404); 344 } 345 else { 346 ReqRspUtil.setContentLength(rsp,res.length()); 347 String mt = servlet.getServletContext().getMimeType(req.getServletPath()); 348 if(!StringUtil.isEmpty(mt))rsp.setContentType(mt); 349 IOUtil.copy(res, rsp.getOutputStream(), true); 350 } 351 } 352 } 353 354 355 public void serviceRest(HttpServlet servlet, HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException { 356 req=new HTTPServletRequestWrap(req); 357 CFMLFactory factory=getCFMLFactory(servlet.getServletContext(), servlet.getServletConfig(), req); 358 359 PageContext pc = factory.getRailoPageContext(servlet,req,rsp,null,false,-1,false); 360 ThreadQueue queue = factory.getConfig().getThreadQueue(); 361 queue.enter(pc); 362 try { 363 pc.executeRest(pc.getHttpServletRequest().getServletPath(),false); 364 } 365 catch (PageException pe) { 366 throw new PageServletException(pe); 367 } 368 finally { 369 queue.exit(pc); 370 factory.releaseRailoPageContext(pc); 371 FDControllerFactory.notifyPageComplete(); 372 } 373 374 375 } 376 377 378 /*private String getContextList() { 379 return List.arrayToList((String[])contextes.keySet().toArray(new String[contextes.size()]),", "); 380 }*/ 381 382 @Override 383 public String getVersion() { 384 return Info.getVersionAsString(); 385 } 386 387 @Override 388 public String getUpdateType() { 389 return getConfigServerImpl().getUpdateType(); 390 } 391 392 @Override 393 public URL getUpdateLocation() { 394 return getConfigServerImpl().getUpdateLocation(); 395 } 396 397 @Override 398 public boolean can(int type, String password) { 399 return getConfigServerImpl().passwordEqual(password); 400 } 401 402 public CFMLEngineFactory getCFMLEngineFactory() { 403 return factory; 404 } 405 406 public void serviceAMF(HttpServlet servlet, HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException { 407 req=new HTTPServletRequestWrap(req); 408 amfEngine.service(servlet,req,rsp); 409 } 410 411 @Override 412 public void reset() { 413 reset(null); 414 } 415 416 @Override 417 public void reset(String configId) { 418 419 CFMLFactoryImpl cfmlFactory; 420 //ScopeContext scopeContext; 421 try { 422 Iterator<String> it = contextes.keySet().iterator(); 423 while(it.hasNext()) { 424 try { 425 cfmlFactory=(CFMLFactoryImpl) contextes.get(it.next()); 426 if(configId!=null && !configId.equals(cfmlFactory.getConfigWebImpl().getId())) continue; 427 428 // scopes 429 try{cfmlFactory.getScopeContext().clear();}catch(Throwable t){t.printStackTrace();} 430 431 // PageContext 432 try{cfmlFactory.resetPageContext();}catch(Throwable t){t.printStackTrace();} 433 434 // Query Cache 435 try{ cfmlFactory.getDefaultQueryCache().clear(null);}catch(Throwable t){t.printStackTrace();} 436 437 // Gateway 438 try{ cfmlFactory.getConfigWebImpl().getGatewayEngine().reset();}catch(Throwable t){t.printStackTrace();} 439 440 } 441 catch(Throwable t){ 442 t.printStackTrace(); 443 } 444 } 445 } 446 finally { 447 // Controller 448 controlerState.setValue(false); 449 } 450 } 451 452 @Override 453 public Cast getCastUtil() { 454 return CastImpl.getInstance(); 455 } 456 457 @Override 458 public Operation getOperatonUtil() { 459 return OperationImpl.getInstance(); 460 } 461 462 @Override 463 public Decision getDecisionUtil() { 464 return DecisionImpl.getInstance(); 465 } 466 467 @Override 468 public Excepton getExceptionUtil() { 469 return ExceptonImpl.getInstance(); 470 } 471 472 @Override 473 public Creation getCreationUtil() { 474 return CreationImpl.getInstance(this); 475 } 476 477 @Override 478 public Object getBlazeDSUtil() { 479 return new BlazeDSImpl(); 480 } 481 482 @Override 483 public Object getFDController() { 484 engine.allowRequestTimeout(false); 485 486 return new FDControllerImpl(engine,engine.getConfigServerImpl().getSerialNumber()); 487 } 488 489 public Map<String,CFMLFactory> getCFMLFactories() { 490 return initContextes; 491 } 492 493 @Override 494 public railo.runtime.util.ResourceUtil getResourceUtil() { 495 return ResourceUtilImpl.getInstance(); 496 } 497 498 @Override 499 public railo.runtime.util.HTTPUtil getHTTPUtil() { 500 return HTTPUtilImpl.getInstance(); 501 } 502 503 @Override 504 public PageContext getThreadPageContext() { 505 return ThreadLocalPageContext.get(); 506 } 507 508 @Override 509 public void registerThreadPageContext(PageContext pc) { 510 ThreadLocalPageContext.register(pc); 511 } 512 513 @Override 514 public VideoUtil getVideoUtil() { 515 return VideoUtilImpl.getInstance(); 516 } 517 518 @Override 519 public ZipUtil getZipUtil() { 520 return ZipUtilImpl.getInstance(); 521 } 522 523 @Override 524 public String getState() { 525 return Info.getStateAsString(); 526 } 527 528 public void allowRequestTimeout(boolean allowRequestTimeout) { 529 this.allowRequestTimeout=allowRequestTimeout; 530 } 531 532 public boolean allowRequestTimeout() { 533 return allowRequestTimeout; 534 } 535 536 public boolean isRunning() { 537 try{ 538 CFMLEngine other = CFMLEngineFactory.getInstance(); 539 // FUTURE patch, do better impl when changing loader 540 if(other!=this && controlerState.toBooleanValue() && !(other instanceof CFMLEngineWrapper)) { 541 SystemOut.printDate("CFMLEngine is still set to true but no longer valid, Railo disable this CFMLEngine."); 542 controlerState.setValue(false); 543 reset(); 544 return false; 545 } 546 } 547 catch(Throwable t){} 548 return controlerState.toBooleanValue(); 549 } 550 551 @Override 552 public void cli(Map<String, String> config, ServletConfig servletConfig) throws IOException,JspException,ServletException { 553 ServletContext servletContext = servletConfig.getServletContext(); 554 HTTPServletImpl servlet=new HTTPServletImpl(servletConfig, servletContext, servletConfig.getServletName()); 555 556 // webroot 557 String strWebroot=config.get("webroot"); 558 if(Util.isEmpty(strWebroot,true)) throw new IOException("missing webroot configuration"); 559 Resource root=ResourcesImpl.getFileResourceProvider().getResource(strWebroot); 560 root.mkdirs(); 561 562 // serverName 563 String serverName=config.get("server-name"); 564 if(Util.isEmpty(serverName,true))serverName="localhost"; 565 566 // uri 567 String strUri=config.get("uri"); 568 if(Util.isEmpty(strUri,true)) throw new IOException("missing uri configuration"); 569 URI uri; 570 try { 571 uri = railo.commons.net.HTTPUtil.toURI(strUri); 572 } catch (URISyntaxException e) { 573 throw Caster.toPageException(e); 574 } 575 576 // cookie 577 Cookie[] cookies; 578 String strCookie=config.get("cookie"); 579 if(Util.isEmpty(strCookie,true)) cookies=new Cookie[0]; 580 else { 581 Map<String,String> mapCookies=HTTPUtil.parseParameterList(strCookie,false,null); 582 int index=0; 583 cookies=new Cookie[mapCookies.size()]; 584 Entry<String, String> entry; 585 Iterator<Entry<String, String>> it = mapCookies.entrySet().iterator(); 586 while(it.hasNext()){ 587 entry = it.next(); 588 cookies[index++]=new Cookie(entry.getKey(),entry.getValue()); 589 } 590 } 591 592 593 // header 594 Pair[] headers=new Pair[0]; 595 596 // parameters 597 Pair[] parameters=new Pair[0]; 598 599 // attributes 600 StructImpl attributes = new StructImpl(); 601 ByteArrayOutputStream os=new ByteArrayOutputStream(); 602 603 604 605 606 HttpServletRequestDummy req=new HttpServletRequestDummy( 607 root,serverName,uri.getPath(),uri.getQuery(),cookies,headers,parameters,attributes,null); 608 req.setProtocol("CLI/1.0"); 609 HttpServletResponse rsp=new HttpServletResponseDummy(os); 610 611 serviceCFML(servlet, req, rsp); 612 String res = os.toString(rsp.getCharacterEncoding()); 613 System.out.println(res); 614 } 615 616 public ServletConfig[] getServletConfigs(){ 617 return servletConfigs.toArray(new ServletConfig[servletConfigs.size()]); 618 } 619 620 // FUTURE add to interface 621 public long uptime() { 622 return uptime; 623 } 624 625 }