001    package railo.runtime; 
002    
003    import java.net.URL;
004    import java.util.Iterator;
005    import java.util.Stack;
006    
007    import javax.servlet.Servlet;
008    import javax.servlet.ServletRequest;
009    import javax.servlet.ServletResponse;
010    import javax.servlet.http.HttpServlet;
011    import javax.servlet.http.HttpServletRequest;
012    import javax.servlet.http.HttpServletResponse;
013    import javax.servlet.jsp.JspEngineInfo;
014    
015    import railo.commons.io.SystemUtil;
016    import railo.commons.io.log.Log;
017    import railo.commons.io.res.util.ResourceUtil;
018    import railo.commons.lang.SizeOf;
019    import railo.commons.lang.SystemOut;
020    import railo.runtime.config.ConfigWeb;
021    import railo.runtime.config.ConfigWebImpl;
022    import railo.runtime.engine.CFMLEngineImpl;
023    import railo.runtime.engine.ThreadLocalPageContext;
024    import railo.runtime.exp.Abort;
025    import railo.runtime.exp.PageException;
026    import railo.runtime.exp.PageExceptionImpl;
027    import railo.runtime.exp.RequestTimeoutException;
028    import railo.runtime.functions.string.Hash;
029    import railo.runtime.lock.LockManager;
030    import railo.runtime.op.Caster;
031    import railo.runtime.query.QueryCache;
032    import railo.runtime.type.Array;
033    import railo.runtime.type.ArrayImpl;
034    import railo.runtime.type.Collection;
035    import railo.runtime.type.Collection.Key;
036    import railo.runtime.type.KeyImpl;
037    import railo.runtime.type.Struct;
038    import railo.runtime.type.StructImpl;
039    import railo.runtime.type.dt.DateTimeImpl;
040    import railo.runtime.type.scope.ArgumentIntKey;
041    import railo.runtime.type.scope.LocalNotSupportedScope;
042    import railo.runtime.type.scope.ScopeContext;
043    import railo.runtime.type.util.ArrayUtil;
044    import railo.runtime.type.util.KeyConstants;
045    import railo.runtime.type.util.ListUtil;
046    
047    /**
048     * implements a JSP Factory, this class produce JSP Compatible PageContext Object
049     * this object holds also the must interfaces to coldfusion specified functionlity
050     */
051    public final class CFMLFactoryImpl extends CFMLFactory {
052            
053            private static JspEngineInfo info=new JspEngineInfoImpl("1.0");
054            private ConfigWebImpl config;
055            Stack<PageContext> pcs=new Stack<PageContext>();
056        private Struct runningPcs=new StructImpl();
057        int idCounter=1;
058        private QueryCache queryCache;
059        private ScopeContext scopeContext=new ScopeContext(this);
060        private HttpServlet servlet;
061            private URL url=null;
062            private CFMLEngineImpl engine;
063    
064            /**
065             * constructor of the JspFactory
066             * @param config Railo specified Configuration
067             * @param compiler CFML compiler
068             * @param engine
069             */
070            public CFMLFactoryImpl(CFMLEngineImpl engine,QueryCache queryCache) {
071                    this.engine=engine; 
072                    this.queryCache=queryCache;
073            }
074        
075        /**
076         * reset the PageContexes
077         */
078        public void resetPageContext() {
079            SystemOut.printDate(config.getOutWriter(),"Reset "+pcs.size()+" Unused PageContexts");
080            synchronized(pcs) {
081                pcs.clear();
082            }
083            
084            if(runningPcs!=null) {
085                    synchronized(runningPcs) {
086                    Iterator<Object> it = runningPcs.valueIterator();
087                    while(it.hasNext()){
088                            ((PageContextImpl)it.next()).reset();
089                    }
090                    }
091            }
092        }
093        
094            @Override
095            public javax.servlet.jsp.PageContext getPageContext(
096                    Servlet servlet,
097                    ServletRequest req,
098                    ServletResponse rsp,
099                    String errorPageURL,
100                    boolean needsSession,
101                    int bufferSize,
102                    boolean autoflush) {
103                            return getPageContextImpl((HttpServlet)servlet,(HttpServletRequest)req,(HttpServletResponse)rsp,errorPageURL,needsSession,bufferSize,autoflush,true,false);
104            }
105            
106            /**
107             * similar to getPageContext Method but return the concrete implementation of the railo PageCOntext
108             * and take the HTTP Version of the Servlet Objects
109             * @param servlet
110             * @param req
111             * @param rsp
112             * @param errorPageURL
113             * @param needsSession
114             * @param bufferSize
115             * @param autoflush
116             * @return return the page<context
117             */
118            public PageContext getRailoPageContext(
119            HttpServlet servlet,
120            HttpServletRequest req,
121            HttpServletResponse rsp,
122            String errorPageURL,
123                    boolean needsSession,
124                    int bufferSize,
125                    boolean autoflush)  {
126            //runningCount++;
127            return getPageContextImpl(servlet, req, rsp, errorPageURL, needsSession, bufferSize, autoflush,true,false);
128            }
129            
130            public PageContextImpl getPageContextImpl(
131                            HttpServlet servlet,
132                            HttpServletRequest req,
133                            HttpServletResponse rsp,
134                            String errorPageURL,
135                                    boolean needsSession,
136                                    int bufferSize,
137                                    boolean autoflush,boolean registerPageContext2Thread,boolean isChild)  {
138                            //runningCount++;
139                                    PageContextImpl pc;
140                            synchronized (pcs) {
141                                if(pcs.isEmpty()) pc=new PageContextImpl(scopeContext,config,queryCache,idCounter++,servlet);
142                                else pc=((PageContextImpl)pcs.pop());
143                                runningPcs.setEL(ArgumentIntKey.init(pc.getId()),pc);
144                                this.servlet=servlet;
145                                if(registerPageContext2Thread)ThreadLocalPageContext.register(pc);
146                                    
147                            }
148                            pc.initialize(servlet,req,rsp,errorPageURL,needsSession,bufferSize,autoflush,isChild);
149                            return pc;
150                            }
151    
152        @Override
153            public void releasePageContext(javax.servlet.jsp.PageContext pc) {
154                    releaseRailoPageContext((PageContext)pc);
155            }
156            
157            /**
158             * Similar to the releasePageContext Method, but take railo PageContext as entry
159             * @param pc
160             */
161            public void releaseRailoPageContext(PageContext pc) {
162                    if(pc.getId()<0)return;
163            pc.release();
164            ThreadLocalPageContext.release();
165            //if(!pc.hasFamily()){
166                            synchronized (runningPcs) {
167                        runningPcs.removeEL(ArgumentIntKey.init(pc.getId()));
168                        if(pcs.size()<100)// not more than 100 PCs
169                            pcs.push(pc);
170                        //SystemOut.printDate(config.getOutWriter(),"Release: (id:"+pc.getId()+";running-requests:"+config.getThreadQueue().size()+";)");
171                    }
172           /*}
173            else {
174                     SystemOut.printDate(config.getOutWriter(),"Unlink: ("+pc.getId()+")");
175            }*/
176            }
177        
178        /**
179             * check timeout of all running threads, downgrade also priority from all thread run longer than 10 seconds
180             */
181            public void checkTimeout() {
182                    if(!engine.allowRequestTimeout())return;
183                    synchronized (runningPcs) {
184                //int len=runningPcs.size();
185                            Iterator it = runningPcs.keyIterator();
186                PageContext pc;
187                Collection.Key key;
188                while(it.hasNext()) {
189                    key=KeyImpl.toKey(it.next(),null);
190                    //print.out("key:"+key);
191                    pc=(PageContext) runningPcs.get(key,null);
192                    if(pc==null) {
193                            runningPcs.removeEL(key);
194                            continue;
195                    }
196                    
197                    long timeout=pc.getRequestTimeout();
198                    if(pc.getStartTime()+timeout<System.currentTimeMillis()) {
199                        terminate(pc);
200                    }
201                    // after 10 seconds downgrade priority of the thread
202                    else if(pc.getStartTime()+10000<System.currentTimeMillis() && pc.getThread().getPriority()!=Thread.MIN_PRIORITY) {
203                        Log log = config.getRequestTimeoutLogger();
204                        if(log!=null)log.warn("controler","downgrade priority of the a thread at "+getPath(pc));
205                        try {
206                            pc.getThread().setPriority(Thread.MIN_PRIORITY);
207                        }
208                        catch(Throwable t) {}
209                    }
210                }
211            }
212            }
213            
214            public static void terminate(PageContext pc) {
215                    Log log = pc.getConfig().getRequestTimeoutLogger();
216            
217                    String strLocks="";
218                    try{
219                            LockManager manager = pc.getConfig().getLockManager();
220                    String[] locks = manager.getOpenLockNames();
221                    if(!ArrayUtil.isEmpty(locks)) 
222                            strLocks=" open locks at this time ("+ListUtil.arrayToList(locks, ", ")+").";
223                    //LockManagerImpl.unlockAll(pc.getId());
224                    }
225                    catch(Throwable t){}
226            
227            if(log!=null)log.error("controler",
228                            "stop thread ("+pc.getId()+") because run into a timeout "+getPath(pc)+"."+strLocks);
229            pc.getThread().stop(new RequestTimeoutException(pc,"request ("+getPath(pc)+":"+pc.getId()+") has run into a timeout ("+(pc.getRequestTimeout()/1000)+" seconds) and has been stopped."+strLocks));
230            
231            }
232    
233            private static String getPath(PageContext pc) {
234                    try {
235                            String base=ResourceUtil.getResource(pc, pc.getBasePageSource()).getAbsolutePath();
236                            String current=ResourceUtil.getResource(pc, pc.getCurrentPageSource()).getAbsolutePath();
237                            if(base.equals(current)) return "path: "+base;
238                            return "path: "+base+" ("+current+")";
239                    }
240                    catch(Throwable t) {
241                            return "";
242                    }
243            }
244            
245            @Override
246            public JspEngineInfo getEngineInfo() {
247                    return info;
248            }
249    
250    
251            /**
252             * @return returns count of pagecontext in use
253             */
254            public int getUsedPageContextLength() { 
255                    int length=0;
256                    try{
257                    Iterator it = runningPcs.values().iterator();
258                    while(it.hasNext()){
259                            PageContextImpl pc=(PageContextImpl) it.next();
260                            if(!pc.isGatewayContext()) length++;
261                    }
262                    }
263                    catch(Throwable t){
264                            return length;
265                    }
266                return length;
267            }
268        /**
269         * @return Returns the config.
270         */
271        public ConfigWeb getConfig() {
272            return config;
273        }
274        public ConfigWebImpl getConfigWebImpl() {
275            return config;
276        }
277        /**
278         * @return Returns the scopeContext.
279         */
280        public ScopeContext getScopeContext() {
281            return scopeContext;
282        }
283    
284        /**
285         * @return label of the factory
286         */
287        public Object getLabel() {
288            return ((ConfigWebImpl)getConfig()).getLabel();
289        }
290        /**
291         * @param label
292         */
293        public void setLabel(String label) {
294            // deprecated
295        }
296    
297            /**
298             * @return the hostName
299             */
300            public URL getURL() {
301                    return url;
302            }
303        
304    
305            public void setURL(URL url) {
306                    this.url=url;
307            }
308    
309            /**
310             * @return the servlet
311             */
312            public HttpServlet getServlet() {
313                    return servlet;
314            }
315    
316            public void setConfig(ConfigWebImpl config) {
317                    this.config=config;
318            }
319    
320            public Struct getRunningPageContextes() {
321                    return runningPcs;
322            }
323    
324            public long getPageContextesSize() {
325                    return SizeOf.size(pcs);
326            }
327            
328            public Array getInfo() {
329                    Array info=new ArrayImpl();
330                    
331                    synchronized (runningPcs) {
332                //int len=runningPcs.size();
333                            Iterator<Key> it = runningPcs.keyIterator();
334                PageContextImpl pc;
335                Struct data,sctThread,scopes;
336                    Collection.Key key;
337                Thread thread;
338                    while(it.hasNext()) {
339                    data=new StructImpl();
340                    sctThread=new StructImpl();
341                    scopes=new StructImpl();
342                    data.setEL("thread", sctThread);
343                    data.setEL("scopes", scopes);
344                    
345                    
346                    key=KeyImpl.toKey(it.next(),null);
347                    //print.out("key:"+key);
348                    pc=(PageContextImpl) runningPcs.get(key,null);
349                    if(pc==null || pc.isGatewayContext()) continue;
350                    thread=pc.getThread();
351                    if(thread==Thread.currentThread()) continue;
352    
353                    
354                    thread=pc.getThread();
355                    if(thread==Thread.currentThread()) continue;
356                    
357                   
358                    
359                    data.setEL("startTime", new DateTimeImpl(pc.getStartTime(),false));
360                    data.setEL("endTime", new DateTimeImpl(pc.getStartTime()+pc.getRequestTimeout(),false));
361                    data.setEL(KeyConstants._timeout,new Double(pc.getRequestTimeout()));
362    
363                    
364                    // thread
365                    sctThread.setEL(KeyConstants._name,thread.getName());
366                    sctThread.setEL("priority",Caster.toDouble(thread.getPriority()));
367                    data.setEL("TagContext",PageExceptionImpl.getTagContext(pc.getConfig(),thread.getStackTrace() ));
368    
369                    data.setEL("urlToken", pc.getURLToken());
370                    try {
371                                            data.setEL("debugger", pc.getDebugger().getDebuggingData(pc));
372                                    } catch (PageException e2) {}
373    
374                    try {
375                                            data.setEL("id", Hash.call(pc, pc.getId()+":"+pc.getStartTime()));
376                                    } catch (PageException e1) {}
377                    data.setEL("requestid", pc.getId());
378    
379                    // Scopes
380                    scopes.setEL(KeyConstants._name, pc.getApplicationContext().getName());
381                    try {
382                                            scopes.setEL(KeyConstants._application, pc.applicationScope());
383                                    } catch (PageException e) {}
384    
385                    try {
386                                            scopes.setEL(KeyConstants._session, pc.sessionScope());
387                                    } catch (PageException e) {}
388                    
389                    try {
390                                            scopes.setEL(KeyConstants._client, pc.clientScope());
391                                    } catch (PageException e) {}
392                    scopes.setEL(KeyConstants._cookie, pc.cookieScope());
393                    scopes.setEL(KeyConstants._variables, pc.variablesScope());
394                    if(!(pc.localScope() instanceof LocalNotSupportedScope)){
395                            scopes.setEL(KeyConstants._local, pc.localScope());
396                            scopes.setEL(KeyConstants._arguments, pc.argumentsScope());
397                    }
398                    scopes.setEL(KeyConstants._cgi, pc.cgiScope());
399                    scopes.setEL(KeyConstants._form, pc.formScope());
400                    scopes.setEL(KeyConstants._url, pc.urlScope());
401                    scopes.setEL(KeyConstants._request, pc.requestScope());
402                    
403                    info.appendEL(data);
404                }
405                return info;
406            }
407            }
408    
409            public void stopThread(String threadId, String stopType) {
410                    synchronized (runningPcs) {
411                //int len=runningPcs.size();
412                            Iterator it = runningPcs.keyIterator();
413                PageContext pc;
414                    while(it.hasNext()) {
415                    
416                    pc=(PageContext) runningPcs.get(KeyImpl.toKey(it.next(),null),null);
417                    if(pc==null) continue;
418                    try {
419                                            String id = Hash.call(pc, pc.getId()+":"+pc.getStartTime());
420                                            if(id.equals(threadId)){
421                                                    stopType=stopType.trim();
422                                                    Throwable t;
423                                                    if("abort".equalsIgnoreCase(stopType) || "cfabort".equalsIgnoreCase(stopType))
424                                                            t=new Abort(Abort.SCOPE_REQUEST);
425                                                    else
426                                                            t=new RequestTimeoutException(pc,"request has been forced to stop.");
427                                                    
428                                    pc.getThread().stop(t);
429                                    SystemUtil.sleep(10);
430                                                    break;
431                                            }
432                                    } catch (PageException e1) {}
433                    
434                }
435            }
436            }
437    
438            @Override
439            public QueryCache getDefaultQueryCache() {
440                    return queryCache;
441            }
442    }