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.ByteArrayOutputStream;
022import java.io.Serializable;
023import java.util.ConcurrentModificationException;
024import java.util.Iterator;
025import java.util.Map.Entry;
026
027import javax.servlet.http.HttpServletRequest;
028
029import lucee.commons.io.DevNullOutputStream;
030import lucee.commons.io.log.Log;
031import lucee.commons.io.log.LogUtil;
032import lucee.commons.lang.ExceptionUtil;
033import lucee.commons.lang.Pair;
034import lucee.runtime.Page;
035import lucee.runtime.PageContext;
036import lucee.runtime.PageContextImpl;
037import lucee.runtime.PageSourceImpl;
038import lucee.runtime.config.Config;
039import lucee.runtime.config.ConfigImpl;
040import lucee.runtime.config.ConfigWeb;
041import lucee.runtime.config.ConfigWebImpl;
042import lucee.runtime.engine.ThreadLocalPageContext;
043import lucee.runtime.exp.Abort;
044import lucee.runtime.exp.PageException;
045import lucee.runtime.net.http.HttpServletResponseDummy;
046import lucee.runtime.net.http.HttpUtil;
047import lucee.runtime.net.http.ReqRspUtil;
048import lucee.runtime.op.Caster;
049import lucee.runtime.op.Duplicator;
050import lucee.runtime.type.Collection;
051import lucee.runtime.type.Collection.Key;
052import lucee.runtime.type.KeyImpl;
053import lucee.runtime.type.Struct;
054import lucee.runtime.type.StructImpl;
055import lucee.runtime.type.scope.Argument;
056import lucee.runtime.type.scope.ArgumentThreadImpl;
057import lucee.runtime.type.scope.Local;
058import lucee.runtime.type.scope.LocalImpl;
059import lucee.runtime.type.scope.Threads;
060import lucee.runtime.type.scope.Undefined;
061
062public class ChildThreadImpl extends ChildThread implements Serializable {
063
064        private static final long serialVersionUID = -8902836175312356628L;
065
066        private static final Collection.Key KEY_ATTRIBUTES = KeyImpl.intern("attributes");
067
068        //private static final Set EMPTY = new HashSet(); 
069        
070        private int threadIndex;
071        private PageContextImpl parent;
072        PageContextImpl pc =null;
073        private String tagName;
074        private long start;
075        private Threads scope;
076        
077        // accesible from scope
078        Struct content=new StructImpl();
079        Struct catchBlock;
080        boolean terminated;
081        boolean completed;
082        ByteArrayOutputStream output;
083        
084        
085        // only used for type daemon
086        private Page page;
087        
088        // only used for type task, demon attrs are not Serializable
089        private Struct attrs;
090        private SerializableCookie[] cookies;
091        private String serverName;
092        private String queryString;
093        private Pair[] parameters;
094        private String requestURI;
095        private Pair[] headers;
096        private Struct attributes;
097        private String template;
098        private long requestTimeout;
099
100        private boolean serializable;
101
102        String contentType;
103
104        String contentEncoding;
105        
106        
107        public ChildThreadImpl(PageContextImpl parent,Page page, String tagName,int threadIndex, Struct attrs, boolean serializable) {
108                this.serializable=serializable;
109                this.tagName=tagName;
110                this.threadIndex=threadIndex;
111                start=System.currentTimeMillis();
112                if(attrs==null) this.attrs=new StructImpl();
113                else this.attrs=attrs;
114                
115                if(!serializable){
116                        this.page=page;
117                        if(parent!=null){
118                                output = new ByteArrayOutputStream();
119                                try{
120                                        this.parent=ThreadUtil.clonePageContext(parent, output,false,false,true);
121                                }
122                                catch(ConcurrentModificationException e){// MUST search for:hhlhgiug
123                                        this.parent=ThreadUtil.clonePageContext(parent, output,false,false,true);
124                                }
125                                //this.parent=parent;
126                        }
127                }
128                else {
129                        this.template=page.getPageSource().getFullRealpath();
130                        HttpServletRequest req = parent.getHttpServletRequest();
131                        serverName=req.getServerName();
132                        queryString=ReqRspUtil.getQueryString(req);
133                        cookies=SerializableCookie.toSerializableCookie(ReqRspUtil.getCookies(req,parent.getWebCharset()));
134                        parameters=HttpUtil.cloneParameters(req);
135                        requestURI=req.getRequestURI();
136                        headers=HttpUtil.cloneHeaders(req);
137                        attributes=HttpUtil.getAttributesAsStruct(req);
138                        requestTimeout=parent.getRequestTimeout();
139                        // MUST here ist sill a mutch state values missing
140                }
141        }
142
143        public PageContext getPageContext(){
144                return pc;
145        }
146        
147        
148        public void run()  {
149                execute(null);
150        }
151        public PageException execute(Config config)   {
152                PageContext oldPc = ThreadLocalPageContext.get();
153                
154                Page p=page;
155                
156                if(parent!=null){
157                        pc=parent;
158                        ThreadLocalPageContext.register(pc);
159                }
160                else {
161                        ConfigWebImpl cwi;
162                        try {
163                                cwi = (ConfigWebImpl)config;
164                                DevNullOutputStream os = DevNullOutputStream.DEV_NULL_OUTPUT_STREAM;
165                                pc=ThreadUtil.createPageContext(cwi, os, serverName, requestURI, queryString, SerializableCookie.toCookies(cookies), headers, parameters, attributes);
166                                pc.setRequestTimeout(requestTimeout);
167                                p=PageSourceImpl.loadPage(pc, cwi.getPageSources(oldPc==null?pc:oldPc,null, template, false,false,true));
168                                //p=cwi.getPageSources(oldPc,null, template, false,false,true).loadPage(cwi);
169                        } catch (PageException e) {
170                                return e;
171                        }
172                                pc.addPageSource(p.getPageSource(), true);
173                }
174                pc.setThreadScope("thread", new ThreadsImpl(this));
175                pc.setThread(Thread.currentThread());
176                
177                //String encodings = pc.getHttpServletRequest().getHeader("Accept-Encoding");
178                
179                Undefined undefined=pc.us();
180                
181                Argument newArgs=new ArgumentThreadImpl((Struct) Duplicator.duplicate(attrs,false));
182        LocalImpl newLocal=pc.getScopeFactory().getLocalInstance();
183        //Key[] keys = attrs.keys();
184        Iterator<Entry<Key, Object>> it = attrs.entryIterator();
185        Entry<Key, Object> e;
186                while(it.hasNext()){
187                        e = it.next();
188                        newArgs.setEL(e.getKey(),e.getValue());
189                }
190                
191                newLocal.setEL(KEY_ATTRIBUTES, newArgs);
192
193                Argument oldArgs=pc.argumentsScope();
194        Local oldLocal=pc.localScope();
195        
196        int oldMode=undefined.setMode(Undefined.MODE_LOCAL_OR_ARGUMENTS_ALWAYS);
197                pc.setFunctionScopes(newLocal,newArgs);
198                
199                try {
200                        p.threadCall(pc, threadIndex); 
201                }
202                catch (Throwable t) {
203                        ExceptionUtil.rethrowIfNecessary(t);
204                        if(!Abort.isSilentAbort(t)) {
205                                ConfigWeb c = pc.getConfig();
206                                if(c instanceof ConfigImpl) {
207                                        ConfigImpl ci=(ConfigImpl) c;
208                                        Log log = ci.getLog("thread");
209                                        if(log!=null)LogUtil.log(log,Log.LEVEL_ERROR,this.getName(), t);
210                                }
211                                PageException pe = Caster.toPageException(t);
212                                if(!serializable)catchBlock=pe.getCatchBlock(pc);
213                                return pe;
214                        }
215                }
216                finally {
217                        completed=true;
218                        pc.setFunctionScopes(oldLocal,oldArgs);
219                    undefined.setMode(oldMode);
220                    //pc.getScopeFactory().recycle(newArgs);
221            pc.getScopeFactory().recycle(newLocal);
222            
223            if(pc.getHttpServletResponse() instanceof HttpServletResponseDummy) {
224                    HttpServletResponseDummy rsp=(HttpServletResponseDummy) pc.getHttpServletResponse();
225                    pc.flush();
226                    contentType=rsp.getContentType();
227                    Pair<String,Object>[] _headers = rsp.getHeaders();
228                    if(_headers!=null)for(int i=0;i<_headers.length;i++){
229                        if(_headers[i].getName().equalsIgnoreCase("Content-Encoding"))
230                                contentEncoding=Caster.toString(_headers[i].getValue(),null);
231                    }
232            }
233            
234                        ((ConfigImpl)pc.getConfig()).getFactory().releasePageContext(pc);
235                        pc=null;
236                        if(oldPc!=null)ThreadLocalPageContext.register(oldPc);
237                }
238                return null;
239        }
240
241        @Override
242        public String getTagName() {
243                return tagName;
244        }
245
246        @Override
247        public long getStartTime() {
248                return start;
249        }
250
251        public Threads getThreadScope() {
252                if(scope==null) scope=new ThreadsImpl(this);
253                return scope;
254        }
255
256        public void terminated() {
257                terminated=true;
258        }
259
260        /**
261         * @return the pageSource
262         */
263        public String getTemplate() {
264                return template;
265        }
266        
267        
268}