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