001 package railo.runtime; 002 003 import java.net.URL; 004 import java.util.Iterator; 005 import java.util.Stack; 006 007 import javax.servlet.Servlet; 008 import javax.servlet.ServletRequest; 009 import javax.servlet.ServletResponse; 010 import javax.servlet.http.HttpServlet; 011 import javax.servlet.http.HttpServletRequest; 012 import javax.servlet.http.HttpServletResponse; 013 import javax.servlet.jsp.JspEngineInfo; 014 015 import railo.commons.io.SystemUtil; 016 import railo.commons.io.log.Log; 017 import railo.commons.io.res.util.ResourceUtil; 018 import railo.commons.lang.SizeOf; 019 import railo.commons.lang.SystemOut; 020 import railo.runtime.config.ConfigWeb; 021 import railo.runtime.config.ConfigWebImpl; 022 import railo.runtime.engine.CFMLEngineImpl; 023 import railo.runtime.engine.ThreadLocalPageContext; 024 import railo.runtime.exp.Abort; 025 import railo.runtime.exp.PageException; 026 import railo.runtime.exp.PageExceptionImpl; 027 import railo.runtime.exp.RequestTimeoutException; 028 import railo.runtime.functions.string.Hash; 029 import railo.runtime.lock.LockManager; 030 import railo.runtime.op.Caster; 031 import railo.runtime.query.QueryCache; 032 import railo.runtime.type.Array; 033 import railo.runtime.type.ArrayImpl; 034 import railo.runtime.type.Collection; 035 import railo.runtime.type.KeyImpl; 036 import railo.runtime.type.List; 037 import railo.runtime.type.Struct; 038 import railo.runtime.type.StructImpl; 039 import railo.runtime.type.dt.DateTimeImpl; 040 import railo.runtime.type.scope.ArgumentIntKey; 041 import railo.runtime.type.scope.LocalNotSupportedScope; 042 import railo.runtime.type.scope.ScopeContext; 043 import railo.runtime.type.util.ArrayUtil; 044 045 /** 046 * implements a JSP Factory, this class produce JSP Compatible PageContext Object 047 * this object holds also the must interfaces to coldfusion specified functionlity 048 */ 049 public final class CFMLFactoryImpl extends CFMLFactory { 050 051 private static JspEngineInfo info=new JspEngineInfoImpl("1.0"); 052 private ConfigWebImpl config; 053 Stack<PageContext> pcs=new Stack<PageContext>(); 054 private Struct runningPcs=new StructImpl(); 055 int idCounter=1; 056 private QueryCache queryCache; 057 private ScopeContext scopeContext=new ScopeContext(this); 058 private HttpServlet servlet; 059 private URL url=null; 060 private CFMLEngineImpl engine; 061 062 /** 063 * constructor of the JspFactory 064 * @param config Railo specified Configuration 065 * @param compiler Cold Fusion compiler 066 * @param engine 067 */ 068 public CFMLFactoryImpl(CFMLEngineImpl engine,QueryCache queryCache) { 069 this.engine=engine; 070 this.queryCache=queryCache; 071 } 072 073 /** 074 * reset the PageContexes 075 */ 076 public void resetPageContext() { 077 SystemOut.printDate(config.getOutWriter(),"Reset "+pcs.size()+" Unused PageContexts"); 078 synchronized(pcs) { 079 pcs.clear(); 080 } 081 } 082 083 /** 084 * @see javax.servlet.jsp.JspFactory#getPageContext(javax.servlet.Servlet, javax.servlet.ServletRequest, javax.servlet.ServletResponse, java.lang.String, boolean, int, boolean) 085 */ 086 public javax.servlet.jsp.PageContext getPageContext( 087 Servlet servlet, 088 ServletRequest req, 089 ServletResponse rsp, 090 String errorPageURL, 091 boolean needsSession, 092 int bufferSize, 093 boolean autoflush) { 094 return getPageContextImpl((HttpServlet)servlet,(HttpServletRequest)req,(HttpServletResponse)rsp,errorPageURL,needsSession,bufferSize,autoflush,true,false); 095 } 096 097 /** 098 * similar to getPageContext Method but return the concrete implementation of the railo PageCOntext 099 * and take the HTTP Version of the Servlet Objects 100 * @param servlet 101 * @param req 102 * @param rsp 103 * @param errorPageURL 104 * @param needsSession 105 * @param bufferSize 106 * @param autoflush 107 * @return return the page<context 108 */ 109 public PageContext getRailoPageContext( 110 HttpServlet servlet, 111 HttpServletRequest req, 112 HttpServletResponse rsp, 113 String errorPageURL, 114 boolean needsSession, 115 int bufferSize, 116 boolean autoflush) { 117 //runningCount++; 118 return getPageContextImpl(servlet, req, rsp, errorPageURL, needsSession, bufferSize, autoflush,true,false); 119 } 120 121 public PageContextImpl getPageContextImpl( 122 HttpServlet servlet, 123 HttpServletRequest req, 124 HttpServletResponse rsp, 125 String errorPageURL, 126 boolean needsSession, 127 int bufferSize, 128 boolean autoflush,boolean registerPageContext2Thread,boolean isChild) { 129 //runningCount++; 130 PageContextImpl pc; 131 synchronized (pcs) { 132 if(pcs.isEmpty()) pc=new PageContextImpl(scopeContext,config,queryCache,idCounter++,servlet); 133 else pc=((PageContextImpl)pcs.pop()); 134 runningPcs.setEL(ArgumentIntKey.init(pc.getId()),pc); 135 this.servlet=servlet; 136 if(registerPageContext2Thread)ThreadLocalPageContext.register(pc); 137 138 } 139 pc.initialize(servlet,req,rsp,errorPageURL,needsSession,bufferSize,autoflush,isChild); 140 return pc; 141 } 142 143 /** 144 * @see javax.servlet.jsp.JspFactory#releasePageContext(javax.servlet.jsp.PageContext) 145 */ 146 public void releasePageContext(javax.servlet.jsp.PageContext pc) { 147 releaseRailoPageContext((PageContext)pc); 148 } 149 150 /** 151 * Similar to the releasePageContext Method, but take railo PageContext as entry 152 * @param pc 153 */ 154 public void releaseRailoPageContext(PageContext pc) { 155 if(pc.getId()<0)return; 156 pc.release(); 157 ThreadLocalPageContext.release(); 158 //if(!pc.hasFamily()){ 159 synchronized (runningPcs) { 160 runningPcs.removeEL(ArgumentIntKey.init(pc.getId())); 161 if(pcs.size()<100)// not more than 100 PCs 162 pcs.push(pc); 163 SystemOut.printDate(config.getOutWriter(),"Release: ("+pc.getId()+")"); 164 } 165 /*} 166 else { 167 SystemOut.printDate(config.getOutWriter(),"Unlink: ("+pc.getId()+")"); 168 }*/ 169 } 170 171 /** 172 * check timeout of all running threads, downgrade also priority from all thread run longer than 10 seconds 173 */ 174 public void checkTimeout() { 175 if(!engine.allowRequestTimeout())return; 176 synchronized (runningPcs) { 177 //int len=runningPcs.size(); 178 Iterator it = runningPcs.keyIterator(); 179 PageContext pc; 180 Collection.Key key; 181 while(it.hasNext()) { 182 key=KeyImpl.toKey(it.next(),null); 183 //print.out("key:"+key); 184 pc=(PageContext) runningPcs.get(key,null); 185 if(pc==null) { 186 runningPcs.removeEL(key); 187 continue; 188 } 189 190 long timeout=pc.getRequestTimeout(); 191 if(pc.getStartTime()+timeout<System.currentTimeMillis()) { 192 terminate(pc); 193 } 194 // after 10 seconds downgrade priority of the thread 195 else if(pc.getStartTime()+10000<System.currentTimeMillis() && pc.getThread().getPriority()!=Thread.MIN_PRIORITY) { 196 Log log = config.getRequestTimeoutLogger(); 197 if(log!=null)log.warn("controler","downgrade priority of the a thread at "+getPath(pc)); 198 try { 199 pc.getThread().setPriority(Thread.MIN_PRIORITY); 200 } 201 catch(Throwable t) {} 202 } 203 } 204 } 205 } 206 207 public static void terminate(PageContext pc) { 208 Log log = pc.getConfig().getRequestTimeoutLogger(); 209 210 String strLocks=""; 211 try{ 212 LockManager manager = pc.getConfig().getLockManager(); 213 String[] locks = manager.getOpenLockNames(); 214 if(!ArrayUtil.isEmpty(locks)) 215 strLocks=" open locks at this time ("+List.arrayToList(locks, ", ")+")."; 216 //LockManagerImpl.unlockAll(pc.getId()); 217 } 218 catch(Throwable t){} 219 220 if(log!=null)log.error("controler", 221 "stop thread ("+pc.getId()+") because run into a timeout "+getPath(pc)+"."+strLocks); 222 pc.getThread().stop(new RequestTimeoutException(pc,"request ("+getPath(pc)+":"+pc.getId()+") is run into a timeout ("+(pc.getRequestTimeout()/1000)+" seconds) and has been stopped."+strLocks)); 223 224 } 225 226 private static String getPath(PageContext pc) { 227 try { 228 String base=ResourceUtil.getResource(pc, pc.getBasePageSource()).getAbsolutePath(); 229 String current=ResourceUtil.getResource(pc, pc.getCurrentPageSource()).getAbsolutePath(); 230 if(base.equals(current)) return "path: "+base; 231 return "path: "+base+" ("+current+")"; 232 } 233 catch(Throwable t) { 234 return ""; 235 } 236 } 237 238 /** 239 * @see javax.servlet.jsp.JspFactory#getEngineInfo() 240 */ 241 public JspEngineInfo getEngineInfo() { 242 return info; 243 } 244 245 /** 246 * @return returns the query cache 247 */ 248 public QueryCache getQueryCache() { 249 return queryCache; 250 } 251 252 /** 253 * @return returns count of pagecontext in use 254 */ 255 public int getUsedPageContextLength() { 256 int length=0; 257 try{ 258 Iterator it = runningPcs.values().iterator(); 259 while(it.hasNext()){ 260 PageContextImpl pc=(PageContextImpl) it.next(); 261 if(!pc.isGatewayContext()) length++; 262 } 263 } 264 catch(Throwable t){ 265 return length; 266 } 267 return length; 268 } 269 /** 270 * @return Returns the config. 271 */ 272 public ConfigWeb getConfig() { 273 return config; 274 } 275 public ConfigWebImpl getConfigWebImpl() { 276 return config; 277 } 278 /** 279 * @return Returns the scopeContext. 280 */ 281 public ScopeContext getScopeContext() { 282 return scopeContext; 283 } 284 285 /** 286 * @return label of the factory 287 */ 288 public Object getLabel() { 289 return ((ConfigWebImpl)getConfig()).getLabel(); 290 } 291 /** 292 * @param label 293 */ 294 public void setLabel(String label) { 295 // deprecated 296 } 297 298 /** 299 * @return the hostName 300 */ 301 public URL getURL() { 302 return url; 303 } 304 305 306 public void setURL(URL url) { 307 this.url=url; 308 } 309 310 /** 311 * @return the servlet 312 */ 313 public HttpServlet getServlet() { 314 return servlet; 315 } 316 317 public void setConfig(ConfigWebImpl config) { 318 this.config=config; 319 } 320 321 public Struct getRunningPageContextes() { 322 return runningPcs; 323 } 324 325 public long getPageContextesSize() { 326 return SizeOf.size(pcs); 327 } 328 329 // FUTURE add to interface 330 public Array getInfo() { 331 Array info=new ArrayImpl(); 332 333 synchronized (runningPcs) { 334 //int len=runningPcs.size(); 335 Iterator it = runningPcs.keyIterator(); 336 PageContextImpl pc; 337 Struct data,sctThread,scopes; 338 Collection.Key key; 339 Thread thread; 340 while(it.hasNext()) { 341 data=new StructImpl(); 342 sctThread=new StructImpl(); 343 scopes=new StructImpl(); 344 data.setEL("thread", sctThread); 345 data.setEL("scopes", scopes); 346 347 348 key=KeyImpl.toKey(it.next(),null); 349 //print.out("key:"+key); 350 pc=(PageContextImpl) runningPcs.get(key,null); 351 if(pc==null || pc.isGatewayContext()) continue; 352 thread=pc.getThread(); 353 if(thread==Thread.currentThread()) continue; 354 355 356 357 358 data.setEL("startTime", new DateTimeImpl(pc.getStartTime(),false)); 359 data.setEL("endTime", new DateTimeImpl(pc.getStartTime()+pc.getRequestTimeout(),false)); 360 data.setEL("timeout", Caster.toDouble(pc.getRequestTimeout())); 361 362 // thread 363 sctThread.setEL(KeyImpl.NAME,thread.getName()); 364 sctThread.setEL("priority",Caster.toDouble(thread.getPriority())); 365 data.setEL("TagContext",PageExceptionImpl.getTagContext(pc.getConfig(),thread.getStackTrace() )); 366 367 data.setEL("urlToken", pc.getURLToken()); 368 data.setEL("debugger", pc.getDebugger().getDebuggingData()); 369 try { 370 data.setEL("id", Hash.call(pc, pc.getId()+":"+pc.getStartTime())); 371 } catch (PageException e1) {} 372 data.setEL("requestid", pc.getId()); 373 374 // Scopes 375 scopes.setEL(KeyImpl.NAME, pc.getApplicationContext().getName()); 376 try { 377 scopes.setEL("application", pc.applicationScope()); 378 } catch (PageException e) {} 379 380 try { 381 scopes.setEL("session", pc.sessionScope()); 382 } catch (PageException e) {} 383 384 try { 385 scopes.setEL("client", pc.clientScope()); 386 } catch (PageException e) {} 387 scopes.setEL("cookie", pc.cookieScope()); 388 scopes.setEL("variables", pc.variablesScope()); 389 if(!(pc.localScope() instanceof LocalNotSupportedScope)){ 390 scopes.setEL("local", pc.localScope()); 391 scopes.setEL("arguments", pc.argumentsScope()); 392 } 393 scopes.setEL("cgi", pc.cgiScope()); 394 scopes.setEL("form", pc.formScope()); 395 scopes.setEL("url", pc.urlScope()); 396 scopes.setEL("request", pc.requestScope()); 397 398 info.appendEL(data); 399 400 401 } 402 return info; 403 } 404 } 405 406 407 408 409 public void stopThread(String threadId, String stopType) { 410 synchronized (runningPcs) { 411 //int len=runningPcs.size(); 412 Iterator it = runningPcs.keyIterator(); 413 PageContext pc; 414 while(it.hasNext()) { 415 416 pc=(PageContext) runningPcs.get(KeyImpl.toKey(it.next(),null),null); 417 if(pc==null) continue; 418 try { 419 String id = Hash.call(pc, pc.getId()+":"+pc.getStartTime()); 420 if(id.equals(threadId)){ 421 stopType=stopType.trim(); 422 Throwable t; 423 if("abort".equalsIgnoreCase(stopType) || "cfabort".equalsIgnoreCase(stopType)) 424 t=new Abort(Abort.SCOPE_REQUEST); 425 else 426 t=new RequestTimeoutException(pc,"request has been forced to stop."); 427 428 pc.getThread().stop(t); 429 SystemUtil.sleep(10); 430 break; 431 } 432 } catch (PageException e1) {} 433 434 } 435 } 436 } 437 }