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    }