001    package railo.runtime.engine;
002    
003    import java.io.IOException;
004    import java.net.MalformedURLException;
005    import java.net.URL;
006    import java.util.Iterator;
007    import java.util.Map;
008    
009    import javax.servlet.ServletConfig;
010    import javax.servlet.ServletContext;
011    import javax.servlet.ServletException;
012    import javax.servlet.http.HttpServlet;
013    import javax.servlet.http.HttpServletRequest;
014    import javax.servlet.http.HttpServletResponse;
015    
016    import railo.commons.collections.HashTable;
017    import railo.commons.io.FileUtil;
018    import railo.commons.io.IOUtil;
019    import railo.commons.io.SystemUtil;
020    import railo.commons.io.res.Resource;
021    import railo.commons.io.res.ResourceProvider;
022    import railo.commons.io.res.ResourcesImpl;
023    import railo.commons.io.res.util.ResourceUtil;
024    import railo.commons.io.res.util.ResourceUtilImpl;
025    import railo.commons.lang.StringUtil;
026    import railo.commons.lang.SystemOut;
027    import railo.commons.lang.types.RefBoolean;
028    import railo.commons.lang.types.RefBooleanImpl;
029    import railo.intergral.fusiondebug.server.FDControllerImpl;
030    import railo.loader.engine.CFMLEngine;
031    import railo.loader.engine.CFMLEngineFactory;
032    import railo.loader.engine.CFMLEngineWrapper;
033    import railo.runtime.CFMLFactory;
034    import railo.runtime.CFMLFactoryImpl;
035    import railo.runtime.Info;
036    import railo.runtime.PageContext;
037    import railo.runtime.config.ConfigServerFactory;
038    import railo.runtime.config.ConfigServerImpl;
039    import railo.runtime.config.ConfigWeb;
040    import railo.runtime.config.ConfigWebFactory;
041    import railo.runtime.config.ConfigWebImpl;
042    import railo.runtime.exp.ApplicationException;
043    import railo.runtime.exp.PageException;
044    import railo.runtime.exp.PageServletException;
045    import railo.runtime.net.http.HTTPServletRequestWrap;
046    import railo.runtime.net.http.ReqRspUtil;
047    import railo.runtime.op.CastImpl;
048    import railo.runtime.op.CreationImpl;
049    import railo.runtime.op.DecisionImpl;
050    import railo.runtime.op.ExceptonImpl;
051    import railo.runtime.op.OperationImpl;
052    import railo.runtime.query.QueryCacheSupport;
053    import railo.runtime.util.BlazeDSImpl;
054    import railo.runtime.util.Cast;
055    import railo.runtime.util.Creation;
056    import railo.runtime.util.Decision;
057    import railo.runtime.util.Excepton;
058    import railo.runtime.util.HTTPUtil;
059    import railo.runtime.util.HTTPUtilImpl;
060    import railo.runtime.util.Operation;
061    import railo.runtime.util.ZipUtil;
062    import railo.runtime.util.ZipUtilImpl;
063    import railo.runtime.video.VideoUtil;
064    import railo.runtime.video.VideoUtilImpl;
065    
066    import com.intergral.fusiondebug.server.FDControllerFactory;
067    
068    /**
069     * The CFMl Engine
070     */
071    public final class CFMLEngineImpl implements CFMLEngine {
072        
073        
074            private static Map initContextes=new HashTable();
075        private static Map contextes=new HashTable();
076        private static ConfigServerImpl configServer=null;
077        private static CFMLEngineImpl engine=null;
078        //private ServletConfig config;
079        private CFMLEngineFactory factory;
080        private AMFEngine amfEngine=new AMFEngine();
081        private final RefBoolean controlerState=new RefBooleanImpl(true);
082            private boolean allowRequestTimeout=true;
083            private Monitor monitor;
084        
085        //private static CFMLEngineImpl engine=new CFMLEngineImpl();
086    
087        private CFMLEngineImpl(CFMLEngineFactory factory) {
088            this.factory=factory; 
089            CFMLEngineFactory.registerInstance(this);// patch, not really good but it works
090            ConfigServerImpl cs = getConfigServerImpl();
091            
092            SystemOut.printDate(SystemUtil.PRINTWRITER_OUT,"Start CFML Controller");
093            Controler controler = new Controler(cs,initContextes,5*1000,controlerState);
094            controler.setDaemon(true);
095            controler.setPriority(Thread.MIN_PRIORITY);
096            controler.start();  
097            
098    
099            touchMonitor(cs);  
100            
101            //this.config=config; 
102        }
103    
104    
105            public void touchMonitor(ConfigServerImpl cs) {
106                    if(monitor!=null && monitor.isAlive()) return; 
107                    monitor = new Monitor(cs,controlerState); 
108            monitor.setDaemon(true);
109            monitor.setPriority(Thread.MIN_PRIORITY);
110            monitor.start(); 
111            }
112    
113        /**
114         * get singelton instance of the CFML Engine
115         * @param factory
116         * @return CFMLEngine
117         */
118        public static synchronized CFMLEngine getInstance(CFMLEngineFactory factory) {
119            if(engine==null) {
120                    engine=new CFMLEngineImpl(factory);
121                    
122            }
123            return engine;
124        }
125        
126        /**
127         * get singelton instance of the CFML Engine, throwsexception when not already init
128         * @param factory
129         * @return CFMLEngine
130         */
131        public static synchronized CFMLEngine getInstance() throws ServletException {
132            if(engine!=null) return engine;
133            throw new ServletException("CFML Engine is not loaded");
134        }
135        
136        /**
137         * @see railo.loader.engine.CFMLEngine#addServletConfig(javax.servlet.ServletConfig)
138         */
139        public void addServletConfig(ServletConfig config) throws ServletException {
140            String real=config.getServletContext().getRealPath("/");
141            if(!initContextes.containsKey(real)) {             
142                    CFMLFactory jspFactory = loadJSPFactory(getConfigServerImpl(),config,initContextes.size());
143                initContextes.put(real,jspFactory);
144            }        
145        }
146        
147        private ConfigServerImpl getConfigServerImpl() {
148            if(configServer==null) {
149                try {
150                    ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
151                    
152                    Resource context = frp.getResource(factory.getResourceRoot().getAbsolutePath()).getRealResource("context");
153                    //CFMLEngineFactory.registerInstance(this);// patch, not really good but it works
154                    configServer=ConfigServerFactory.newInstance(
155                            this,
156                            initContextes,
157                            contextes,
158                            context);
159                } catch (Exception e) {
160                    e.printStackTrace();
161                }
162            }
163            return configServer;
164        }
165        
166        private  CFMLFactoryImpl loadJSPFactory(ConfigServerImpl configServer, ServletConfig sg, int countExistingContextes) throws ServletException {
167            try {
168                // Load Config
169                Resource configDir=getConfigDirectory(sg,configServer,countExistingContextes);
170                
171                QueryCacheSupport queryCache=QueryCacheSupport.getInstance(configServer);
172                CFMLFactoryImpl factory=new CFMLFactoryImpl(this,queryCache);
173                ConfigWebImpl config=ConfigWebFactory.newInstance(factory,configServer,configDir,sg);
174                queryCache.setConfigWeb(config);
175                factory.setConfig(config);
176                return factory;
177            }
178            catch (Exception e) {
179                ServletException se= new ServletException(e.getMessage());
180                se.setStackTrace(e.getStackTrace());
181                throw se;
182            } 
183            
184        }   
185    
186        /**
187         * loads Configuration File from System, from init Parameter from web.xml
188         * @param sg
189         * @param configServer 
190         * @param countExistingContextes 
191         * @return return path to directory
192         */
193        private Resource getConfigDirectory(ServletConfig sg, ConfigServerImpl configServer, int countExistingContextes) throws PageServletException {
194            ServletContext sc=sg.getServletContext();
195            String strConfig=sg.getInitParameter("configuration");
196            if(strConfig==null)strConfig=sg.getInitParameter("railo-web-directory");
197            if(strConfig==null)strConfig="{web-root-directory}/WEB-INF/railo/";
198            else if("/WEB-INF/railo/".equals(strConfig))strConfig="{web-root-directory}/WEB-INF/railo/";
199            
200            // static path is not allowed
201            if(countExistingContextes>1 && strConfig!=null && strConfig.indexOf('{')==-1){
202                    String text="static path ["+strConfig+"] for servlet init param [railo-web-directory] is not allowed, path must use a web-context specific placeholder.";
203                    System.err.println(text);
204                    throw new PageServletException(new ApplicationException(text));
205            }
206            strConfig=SystemUtil.parsePlaceHolder(strConfig,sc,configServer.getLabels());
207            
208            
209            
210            ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
211            Resource root = frp.getResource(sc.getRealPath("/"));
212            Resource configDir=ResourceUtil.createResource(root.getRealResource(strConfig), FileUtil.LEVEL_PARENT_FILE,FileUtil.TYPE_DIR);
213            
214            if(configDir==null) {
215                configDir=ResourceUtil.createResource(frp.getResource(strConfig), FileUtil.LEVEL_GRAND_PARENT_FILE,FileUtil.TYPE_DIR);
216            }
217            if(configDir==null) throw new PageServletException(new ApplicationException("path ["+strConfig+"] is invalid"));
218            if(!configDir.exists()){
219                    try {
220                                    configDir.createDirectory(true);
221                            } 
222                    catch (IOException e) {}
223            }
224            return configDir;
225        }
226        
227        /**
228         * @see railo.loader.engine.CFMLEngine#getCFMLFactory(javax.servlet.ServletContext, javax.servlet.ServletConfig, javax.servlet.http.HttpServletRequest)
229         */
230        
231        public CFMLFactory getCFMLFactory(ServletContext srvContext, ServletConfig srvConfig,HttpServletRequest req) throws ServletException {
232            String real=srvContext.getRealPath("/");
233            ConfigServerImpl cs = getConfigServerImpl();
234            
235            
236            // Load JspFactory
237            CFMLFactoryImpl factory=null;
238            Object o=contextes.get(real);
239            if(o==null) {
240                //int size=sn.getContextCount();
241                //if(size!=-1 && size <= contextes.size())
242                    //throw new ServletException("the maximum size of "+size+" web contextes is reached, " +"to have more contexes upgrade your railo version, already contextes in use are ["+getContextList()+"]");
243                o=initContextes.get(real);
244                if(o!=null) {
245                    factory=(CFMLFactoryImpl) o;
246                }
247                else {
248                    factory=loadJSPFactory(cs,srvConfig,initContextes.size());
249                    initContextes.put(real,factory);
250                }
251                contextes.put(real,factory);
252                
253                try {
254                    String cp = req.getContextPath();
255                    if(cp==null)cp="";
256                                    factory.setURL(new URL(req.getScheme(),req.getServerName(),req.getServerPort(),cp));
257                            } 
258                catch (MalformedURLException e) {
259                                    e.printStackTrace();
260                            }
261                //
262                
263            }
264            else {
265                factory=(CFMLFactoryImpl) o;
266            }
267            return factory;
268        }
269        
270        /**
271         * @see railo.loader.engine.CFMLEngine#serviceCFML(javax.servlet.http.HttpServlet, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
272         */
273        public void serviceCFML(HttpServlet servlet, HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException {
274            
275            CFMLFactory factory=getCFMLFactory(servlet.getServletContext(), servlet.getServletConfig(), req);
276            PageContext pc = factory.getRailoPageContext(servlet,req,rsp,null,false,-1,false);
277            try {
278                    /*print.out("INCLUDE");
279                    print.out("servlet_path:"+req.getAttribute("javax.servlet.include.servlet_path"));
280                    print.out("request_uri:"+req.getAttribute("javax.servlet.include.request_uri"));
281                    print.out("context_path:"+req.getAttribute("javax.servlet.include.context_path"));
282                    print.out("path_info:"+req.getAttribute("javax.servlet.include.path_info"));
283                    print.out("query_string:"+req.getAttribute("javax.servlet.include.query_string"));
284                    print.out("FORWARD");
285                    print.out("servlet_path:"+req.getAttribute("javax.servlet.forward.servlet_path"));
286                    print.out("request_uri:"+req.getAttribute("javax.servlet.forward.request_uri"));
287                    print.out("context_path:"+req.getAttribute("javax.servlet.forward.context_path"));
288                    print.out("path_info:"+req.getAttribute("javax.servlet.forward.path_info"));
289                    print.out("query_string:"+req.getAttribute("javax.servlet.forward.query_string"));
290                    print.out("---");
291                    print.out(req.getServletPath());
292                    print.out(pc.getHttpServletRequest().getServletPath());
293                    */
294                    
295                    
296                    
297                    pc.execute(pc.getHttpServletRequest().getServletPath(),false);
298            } 
299            catch (PageException pe) {
300                            throw new PageServletException(pe);
301                    }
302            finally {
303                factory.releaseRailoPageContext(pc);
304                FDControllerFactory.notifyPageComplete();
305            }
306        }
307    
308            public void serviceFile(HttpServlet servlet, HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException {
309                    req=new HTTPServletRequestWrap(req);
310                    CFMLFactory factory=getCFMLFactory(servlet.getServletContext(), servlet.getServletConfig(), req);
311            ConfigWeb config = factory.getConfig();
312            Resource res = ((ConfigWebImpl)config).getPhysicalResourceExisting(null, null, req.getServletPath(), false, true, true); 
313            
314                    if(res==null) {
315                    rsp.sendError(404);
316            }
317            else {
318                    ReqRspUtil.setContentLength(rsp,res.length());
319                    String mt = servlet.getServletContext().getMimeType(req.getServletPath());
320                    if(!StringUtil.isEmpty(mt))rsp.setContentType(mt);
321                    IOUtil.copy(res, rsp.getOutputStream(), true);
322            }
323            }
324        
325    
326        /*private String getContextList() {
327            return List.arrayToList((String[])contextes.keySet().toArray(new String[contextes.size()]),", ");
328        }*/
329    
330        /**
331         * @see railo.loader.engine.CFMLEngine#getVersion()
332         */
333        public String getVersion() {
334            return Info.getVersionAsString();
335        }
336    
337        /**
338         * @see railo.loader.engine.CFMLEngine#getUpdateType()
339         */
340        public String getUpdateType() {
341            return getConfigServerImpl().getUpdateType();
342        }
343    
344        /**
345         * @see railo.loader.engine.CFMLEngine#getUpdateLocation()
346         */
347        public URL getUpdateLocation() {
348            return getConfigServerImpl().getUpdateLocation();
349        }
350    
351        /**
352         * @see railo.loader.engine.CFMLEngine#can(int, java.lang.String)
353         */
354        public boolean can(int type, String password) {
355            return getConfigServerImpl().passwordEqual(password);
356        }
357    
358        public CFMLEngineFactory getCFMLEngineFactory() {
359            return factory;
360        }
361    
362        public void serviceAMF(HttpServlet servlet, HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException {
363            req=new HTTPServletRequestWrap(req);
364                    amfEngine.service(servlet,req,rsp);
365        }
366    
367        /**
368         * @see railo.loader.engine.CFMLEngine#reset()
369         */
370        public void reset() {
371            reset(null);
372        }
373        
374        /**
375         * @see railo.loader.engine.CFMLEngine#reset(String)
376         */
377        public void reset(String configId) {
378            
379            CFMLFactoryImpl cfmlFactory;
380            //ScopeContext scopeContext;
381            try {
382                    Iterator it = contextes.keySet().iterator();
383                    while(it.hasNext()) {
384                            try {
385                                cfmlFactory=(CFMLFactoryImpl)contextes.get(it.next());
386                                if(configId!=null && !configId.equals(cfmlFactory.getConfigWebImpl().getId())) continue;
387                                    
388                                // scopes
389                                try{cfmlFactory.getScopeContext().clear();}catch(Throwable t){t.printStackTrace();}
390                                
391                                // PageContext
392                                try{cfmlFactory.resetPageContext();}catch(Throwable t){t.printStackTrace();}
393                                
394                                // Query Cache
395                                try{ cfmlFactory.getQueryCache().clear();}catch(Throwable t){t.printStackTrace();}
396                                
397                                // Gateway
398                                try{ cfmlFactory.getConfigWebImpl().getGatewayEngine().reset();}catch(Throwable t){t.printStackTrace();}
399                                
400                            }
401                            catch(Throwable t){
402                                    t.printStackTrace();
403                            }
404                    }
405            }
406            finally {
407                // Controller
408                controlerState.setValue(false);
409            }
410        }
411        
412        /**
413         * @see railo.loader.engine.CFMLEngine#getCastUtil()
414         */
415        public Cast getCastUtil() {
416            return CastImpl.getInstance();
417        }
418    
419        /**
420         * @see railo.loader.engine.CFMLEngine#getOperatonUtil()
421         */
422        public Operation getOperatonUtil() {
423            return OperationImpl.getInstance();
424        }
425    
426        /**
427         * @see railo.loader.engine.CFMLEngine#getDecisionUtil()
428         */
429        public Decision getDecisionUtil() {
430            return DecisionImpl.getInstance();
431        }
432    
433        /**
434         * @see railo.loader.engine.CFMLEngine#getExceptionUtil()
435         */
436        public Excepton getExceptionUtil() {
437            return ExceptonImpl.getInstance();
438        }
439    
440        /**
441         * @see railo.loader.engine.CFMLEngine#getCreationUtil()
442         */
443        public Creation getCreationUtil() {
444            return CreationImpl.getInstance();
445        }
446    
447            /**
448             * @see railo.loader.engine.CFMLEngine#getBlazeDSUtil()
449             */
450            public Object getBlazeDSUtil() {
451                    return new BlazeDSImpl();
452            }
453    
454            /**
455             * @see railo.loader.engine.CFMLEngine#getFDController()
456             */
457            public Object getFDController() {
458                    engine.allowRequestTimeout(false);
459                    
460                    return new FDControllerImpl(engine,engine.getConfigServerImpl().getSerialNumber());
461            }
462    
463            public Map getCFMLFactories() {
464                    return initContextes;
465            }
466    
467            /**
468             * @see railo.loader.engine.CFMLEngine#getResourceUtil()
469             */
470            public railo.runtime.util.ResourceUtil getResourceUtil() {
471                    return ResourceUtilImpl.getInstance();
472            }
473    
474            /**
475             * @see railo.loader.engine.CFMLEngine#getHTTPUtil()
476             */
477            public HTTPUtil getHTTPUtil() {
478                    return HTTPUtilImpl.getInstance();
479            }
480    
481            /**
482             * @see railo.loader.engine.CFMLEngine#getThreadPageContext()
483             */
484            public PageContext getThreadPageContext() {
485                    return ThreadLocalPageContext.get();
486            }
487    
488            /**
489             * @see railo.loader.engine.CFMLEngine#getVideoUtil()
490             */
491            public VideoUtil getVideoUtil() {
492                    return VideoUtilImpl.getInstance();
493            }
494    
495            /**
496             * @see railo.loader.engine.CFMLEngine#getZipUtil()
497             */
498            public ZipUtil getZipUtil() {
499                    return ZipUtilImpl.getInstance();
500            }
501    
502            /**
503             * @see railo.loader.engine.CFMLEngine#getState()
504             */
505            public String getState() {
506                    return Info.getStateAsString();
507            }
508    
509            public void allowRequestTimeout(boolean allowRequestTimeout) {
510                    this.allowRequestTimeout=allowRequestTimeout;
511            }
512    
513            public boolean allowRequestTimeout() {
514                    return allowRequestTimeout;
515            }
516            
517            public boolean isRunning() {
518                    try{
519                            CFMLEngine other = CFMLEngineFactory.getInstance();
520                            // FUTURE patch, do better impl when changing loader
521                            if(other!=this && controlerState.toBooleanValue() &&  !(other instanceof CFMLEngineWrapper)) {
522                                    SystemOut.printDate("CFMLEngine is still set to true but no longer valid, Railo disable this CFMLEngine.");
523                                    controlerState.setValue(false);
524                                    reset();
525                                    return false;
526                            }
527                    }
528                    catch(Throwable t){}
529                    return controlerState.toBooleanValue();
530            }
531    
532    }