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