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