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