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