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}