001 package railo.runtime.thread; 002 003 import java.io.ByteArrayInputStream; 004 import java.io.InputStream; 005 import java.lang.Thread.State; 006 import java.util.Iterator; 007 008 import railo.runtime.PageContext; 009 import railo.runtime.dump.DumpData; 010 import railo.runtime.dump.DumpProperties; 011 import railo.runtime.dump.DumpTable; 012 import railo.runtime.dump.DumpTablePro; 013 import railo.runtime.dump.DumpUtil; 014 import railo.runtime.dump.SimpleDumpData; 015 import railo.runtime.engine.ThreadLocalPageContext; 016 import railo.runtime.exp.ApplicationException; 017 import railo.runtime.exp.PageException; 018 import railo.runtime.op.Duplicator; 019 import railo.runtime.op.ThreadLocalDuplication; 020 import railo.runtime.tag.Http; 021 import railo.runtime.type.Collection; 022 import railo.runtime.type.KeyImpl; 023 import railo.runtime.type.StructImpl; 024 import railo.runtime.type.dt.DateTime; 025 import railo.runtime.type.dt.DateTimeImpl; 026 import railo.runtime.type.util.StructSupport; 027 028 public class ThreadsImpl extends StructSupport implements railo.runtime.type.scope.Threads { 029 030 private static final Key KEY_ERROR = KeyImpl.intern("ERROR"); 031 private static final Key KEY_ELAPSEDTIME = KeyImpl.intern("ELAPSEDTIME"); 032 private static final Key KEY_OUTPUT = KeyImpl.intern("OUTPUT"); 033 private static final Key KEY_PRIORITY = KeyImpl.intern("PRIORITY"); 034 private static final Key KEY_STARTTIME = KeyImpl.intern("STARTTIME"); 035 private static final Key KEY_STATUS = KeyImpl.intern("STATUS"); 036 private static final Key KEY_STACKTRACE = KeyImpl.intern("STACKTRACE"); 037 038 private static final Key[] DEFAULT_KEYS=new Key[]{ 039 KEY_ELAPSEDTIME, 040 KeyImpl.NAME_UC, 041 KEY_OUTPUT, 042 KEY_PRIORITY, 043 KEY_STARTTIME, 044 KEY_STATUS, 045 KEY_STACKTRACE 046 }; 047 048 private ChildThreadImpl ct; 049 050 public ThreadsImpl(ChildThreadImpl ct) { 051 this.ct=ct; 052 } 053 054 055 public ChildThread getChildThread() { 056 return ct; 057 } 058 059 /** 060 * @see railo.runtime.type.StructImpl#containsKey(railo.runtime.type.Collection.Key) 061 */ 062 public boolean containsKey(Key key) { 063 return get(key,null)!=null; 064 } 065 066 ///////////////////////////////////////////////////////////// 067 068 069 public int getType() { 070 return -1; 071 } 072 073 /** 074 * @see railo.runtime.type.Scope#getTypeAsString() 075 */ 076 public String getTypeAsString() { 077 return "thread"; 078 } 079 080 public void initialize(PageContext pc) { 081 082 } 083 084 public boolean isInitalized() { 085 return true; 086 } 087 088 public void release() { 089 090 } 091 092 public void clear() { 093 ct.content.clear(); 094 } 095 096 097 /** 098 * @see railo.runtime.type.Collection#duplicate(boolean) 099 */ 100 public Collection duplicate(boolean deepCopy) { 101 StructImpl sct=new StructImpl(); 102 ThreadLocalDuplication.set(this, sct); 103 try{ 104 Key[] keys = keys(); 105 Object value; 106 for(int i=0;i<keys.length;i++) { 107 value=get(keys[i],null); 108 sct.setEL(keys[i],deepCopy?Duplicator.duplicate(value, deepCopy):value); 109 } 110 } 111 finally { 112 ThreadLocalDuplication.remove(this); 113 } 114 return sct; 115 } 116 117 118 private Object getMeta(Key key) { 119 if(KEY_ELAPSEDTIME.equalsIgnoreCase(key)) return new Double(System.currentTimeMillis()-ct.getStartTime()); 120 if(KeyImpl.NAME_UC.equalsIgnoreCase(key)) return ct.getTagName(); 121 if(KEY_OUTPUT.equalsIgnoreCase(key)) return getOutput(); 122 if(KEY_PRIORITY.equalsIgnoreCase(key)) return ThreadUtil.toStringPriority(ct.getPriority()); 123 if(KEY_STARTTIME.equalsIgnoreCase(key)) return new DateTimeImpl(ct.getStartTime(),true); 124 if(KEY_STATUS.equalsIgnoreCase(key)) return getState(); 125 if(KEY_ERROR.equalsIgnoreCase(key)) return ct.catchBlock; 126 if(KEY_STACKTRACE.equalsIgnoreCase(key)) return getStackTrace(); 127 return null; 128 } 129 130 private String getStackTrace() { 131 StringBuilder sb=new StringBuilder(); 132 try{ 133 StackTraceElement[] trace = ct.getStackTrace(); 134 if(trace!=null)for (int i=0; i < trace.length; i++) { 135 sb.append("\tat "); 136 sb.append(trace[i]); 137 sb.append("\n"); 138 } 139 } 140 catch(Throwable t){} 141 return sb.toString(); 142 } 143 144 145 private Object getOutput() { 146 if(ct.output==null)return ""; 147 148 InputStream is = new ByteArrayInputStream(ct.output.toByteArray()); 149 return Http.getOutput(is, ct.contentType, ct.contentEncoding,true); 150 151 } 152 153 154 private Object getState() { 155 /* 156 157 158 The current status of the thread; one of the following values: 159 160 */ 161 try { 162 State state = ct.getState(); 163 if(State.NEW.equals(state)) return "NOT_STARTED"; 164 if(State.WAITING.equals(state)) return "WAITING"; 165 if(State.TERMINATED.equals(state)) { 166 if(ct.terminated || ct.catchBlock!=null)return "TERMINATED"; 167 return "COMPLETED"; 168 } 169 170 return "RUNNING"; 171 } 172 // java 1.4 execution 173 catch(Throwable t) { 174 if(ct.terminated || ct.catchBlock!=null)return "TERMINATED"; 175 if(ct.completed)return "COMPLETED"; 176 if(!ct.isAlive())return "WAITING"; 177 return "RUNNING"; 178 179 180 } 181 } 182 183 184 /** 185 * @see railo.runtime.type.StructImpl#get(railo.runtime.type.Collection.Key, java.lang.Object) 186 */ 187 public Object get(Key key, Object defaultValue) { 188 Object meta = getMeta(key); 189 if(meta!=null) return meta; 190 return ct.content.get(key,defaultValue); 191 } 192 193 /** 194 * @see railo.runtime.type.StructImpl#get(railo.runtime.type.Collection.Key) 195 */ 196 public Object get(Key key) throws PageException { 197 Object meta = getMeta(key); 198 if(meta!=null) return meta; 199 return ct.content.get(key); 200 } 201 202 203 /** 204 * @see railo.runtime.type.StructImpl#keys() 205 */ 206 public Key[] keys() { 207 Key[] skeys = ct.content.keys(); 208 209 if(skeys.length==0 && ct.catchBlock==null) return DEFAULT_KEYS; 210 211 Key[] rtn=new Key[skeys.length+(ct.catchBlock!=null?1:0)+DEFAULT_KEYS.length]; 212 int index=0; 213 for(;index<DEFAULT_KEYS.length;index++) { 214 rtn[index]=DEFAULT_KEYS[index]; 215 } 216 if(ct.catchBlock!=null) { 217 rtn[index]=KEY_ERROR; 218 index++; 219 } 220 221 for(int i=0;i<skeys.length;i++) { 222 rtn[index++]=skeys[i]; 223 } 224 return rtn; 225 } 226 227 /** 228 * @see railo.runtime.type.StructImpl#remove(railo.runtime.type.Collection.Key) 229 */ 230 public Object remove(Key key) throws PageException { 231 if(isReadonly())throw errorOutside(); 232 Object meta = getMeta(key); 233 if(meta!=null) throw errorMeta(key); 234 return ct.content.remove(key); 235 } 236 237 238 239 240 /** 241 * @see railo.runtime.type.StructImpl#removeEL(railo.runtime.type.Collection.Key) 242 */ 243 public Object removeEL(Key key) { 244 if(isReadonly())return null; 245 return ct.content.removeEL(key); 246 } 247 248 /** 249 * @see railo.runtime.type.StructImpl#set(railo.runtime.type.Collection.Key, java.lang.Object) 250 */ 251 public Object set(Key key, Object value) throws PageException { 252 253 254 if(isReadonly())throw errorOutside(); 255 Object meta = getMeta(key); 256 if(meta!=null) throw errorMeta(key); 257 return ct.content.set(key, value); 258 } 259 260 /** 261 * @see railo.runtime.type.StructImpl#setEL(railo.runtime.type.Collection.Key, java.lang.Object) 262 */ 263 public Object setEL(Key key, Object value) { 264 if(isReadonly()) return null; 265 Object meta = getMeta(key); 266 if(meta!=null) return null; 267 return ct.content.setEL(key, value); 268 } 269 270 271 /** 272 * @see railo.runtime.type.StructImpl#size() 273 */ 274 public int size() { 275 return ct.content.size()+DEFAULT_KEYS.length+(ct.catchBlock==null?0:1); 276 } 277 278 /** 279 * @see railo.runtime.type.StructImpl#toDumpData(railo.runtime.PageContext, int) 280 */ 281 public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { 282 Key[] keys = keys(); 283 DumpTable table = new DumpTablePro("struct","#9999ff","#ccccff","#000000"); 284 table.setTitle("Struct"); 285 maxlevel--; 286 int maxkeys=dp.getMaxKeys(); 287 int index=0; 288 for(int i=0;i<keys.length;i++) { 289 Key key=keys[i]; 290 if(maxkeys<=index++)break; 291 if(DumpUtil.keyValid(dp,maxlevel, key)) 292 table.appendRow(1,new SimpleDumpData(key.getString()),DumpUtil.toDumpData(get(key,null), pageContext,maxlevel,dp)); 293 } 294 return table; 295 } 296 297 public Iterator keyIterator() { 298 return new railo.runtime.type.it.KeyIterator(keys()); 299 } 300 301 302 public String[] keysAsString() { 303 Key[] keys = keys(); 304 String[] strKeys=new String[keys.length]; 305 for(int i=0;i<keys.length;i++) { 306 strKeys[i]=keys[i].getString(); 307 } 308 return null; 309 } 310 311 312 /** 313 * @see railo.runtime.op.Castable#castToBooleanValue() 314 */ 315 public boolean castToBooleanValue() throws PageException { 316 return ct.content.castToBooleanValue(); 317 } 318 319 /** 320 * @see railo.runtime.op.Castable#castToBoolean(java.lang.Boolean) 321 */ 322 public Boolean castToBoolean(Boolean defaultValue) { 323 return ct.content.castToBoolean(defaultValue); 324 } 325 326 327 /** 328 * @see railo.runtime.op.Castable#castToDateTime() 329 */ 330 public DateTime castToDateTime() throws PageException { 331 return ct.content.castToDateTime(); 332 } 333 334 /** 335 * @see railo.runtime.op.Castable#castToDateTime(railo.runtime.type.dt.DateTime) 336 */ 337 public DateTime castToDateTime(DateTime defaultValue) { 338 return ct.content.castToDateTime(defaultValue); 339 } 340 341 342 /** 343 * 344 * @see railo.runtime.op.Castable#castToDoubleValue() 345 */ 346 public double castToDoubleValue() throws PageException { 347 return ct.content.castToDoubleValue(); 348 } 349 350 /** 351 * @see railo.runtime.op.Castable#castToDoubleValue(double) 352 */ 353 public double castToDoubleValue(double defaultValue) { 354 return ct.content.castToDoubleValue(defaultValue); 355 } 356 357 358 /** 359 * 360 * @see railo.runtime.op.Castable#castToString() 361 */ 362 public String castToString() throws PageException { 363 return ct.content.castToString(); 364 } 365 366 /** 367 * @see railo.runtime.type.util.StructSupport#castToString(java.lang.String) 368 */ 369 public String castToString(String defaultValue) { 370 return ct.content.castToString(defaultValue); 371 } 372 373 /** 374 * @see railo.runtime.op.Castable#compareTo(java.lang.String) 375 */ 376 public int compareTo(String str) throws PageException { 377 return ct.content.compareTo(str); 378 } 379 380 /** 381 * @see railo.runtime.op.Castable#compareTo(boolean) 382 */ 383 public int compareTo(boolean b) throws PageException { 384 return ct.content.compareTo(b); 385 } 386 387 388 /** 389 * 390 * @see railo.runtime.op.Castable#compareTo(double) 391 */ 392 public int compareTo(double d) throws PageException { 393 return ct.content.compareTo(d); 394 } 395 396 397 /** 398 * 399 * @see railo.runtime.op.Castable#compareTo(railo.runtime.type.dt.DateTime) 400 */ 401 public int compareTo(DateTime dt) throws PageException { 402 return ct.content.compareTo(dt); 403 } 404 405 private boolean isReadonly() { 406 PageContext pc = ThreadLocalPageContext.get(); 407 if(pc==null) return true; 408 return pc.getThread()!=ct; 409 } 410 411 private ApplicationException errorOutside() { 412 return new ApplicationException("the thread scope cannot be modified from outside the owner thread"); 413 } 414 415 private ApplicationException errorMeta(Key key) { 416 return new ApplicationException("the metadata "+key.getString()+" of the thread scope are readonly"); 417 } 418 419 }