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    }