Ringo.js嵌入集成(二):使用Listener Filter替换官方的JsgiServlet实现

来源:互联网 发布:数据库编辑器有哪些 编辑:程序博客网 时间:2024/05/16 04:54

 就像上回所讲,官方提供了一个样例JsgiServlet。虽然已经在其上stick矿建,但是就个人看来,这个东西确实类似一个样例。
 如果使用serlvet作为js容器(ringo-engine)的载体,那么势必一个webapp可以配置多个servlet,也就是多个js容器。这种方式会导致开发人员在不熟悉环境的情况错误的开发判断。其实,如果要方便开发,那么就应该至少提供一个较为但单纯的环境。降低入门曲线。即,提供一个webapp默认对应的js容器。
另一方面,我后续的需要完成的仿制wordpress plugin的机制也需要用到直接获取默认引擎。

参考spring:
 spring在这块做的比较好,其WebApplicationContext在开发时对于业务开发者来看其实就是被绑定在ServletContext(application)对象上的“单例”。虽然springContext并没有那么简单,其context可以有树形结构的。但至少提供一个默认入口。
 ok,那我现在其实可以参考spring的context绑定方式对Jsgi的进行改造。另外,一方面其实有经验的开发者完全可以自己构造ringo的web-connector接口不一定要符合标准jsgi。完全可以构造一个类似nodejs-express的web框架。我这里是想尽量利用ringo自带的stick框架才这么干的。

架构:
下面是一个粗鲁(是“鲁”)的框线图:

整体结构大致由RingoListener,JsgiFilter,JsgiUtils,RingoEngineHolder(附加),RingoSpringBindingListener(附加)等若干类组成。


RingoListener:
其功能就是读取web.xml中的配置初始化ringo engine。然后将engine绑定到对servletContext。
主要代码是实现一个createRingoEngine方法,初始化engine。
protected RhinoEngine createRingoEngine(ServletContext servletContext){                if (engine == null) {            String ringoHome = getStringParameter(servletContext, "ringo-home", "/WEB-INF");            String modulePath = getStringParameter(servletContext, "ringo-module-path", "WEB-INF/usrmod"); //原来默认WEB-INF/app,可以多个逗号分割。            logger.info("modulePath:"+modulePath);            String bootScripts = getStringParameter(servletContext, "ringo-bootscript", null); //这个似乎不是基于module路径寻找的。            int optlevel = getIntParameter(servletContext, "ringo-optlevel", 0);            boolean debug = getBooleanParameter(servletContext, "ringo-debug", false);            boolean production = getBooleanParameter(servletContext, "ringo-production", false);            boolean verbose = getBooleanParameter(servletContext, "ringo-verbose", false);            boolean legacyMode = getBooleanParameter(servletContext, "ringo-legacy-mode", false);//            ServletContext context = servletContext;            Repository base = new WebappRepository(servletContext, "/");            Repository home = new WebappRepository(servletContext, ringoHome);            try {                if (!home.exists()) {                    home = new FileRepository(ringoHome);                    System.err.println("Resource \"" + ringoHome + "\" not found, "                            + "reverting to file repository " + home);                }                // Use ',' as platform agnostic path separator                String[] paths = StringUtils.split(modulePath, ",");                String[] systemPaths = {"modules", "packages"}; //如果ringo-home下有则ringo会直接用该目录作为系统模块,而取消从jar包中读取。所以如果没有必要不要建立。                RingoConfig ringoConfig =                        new RingoConfig(home, base, paths, systemPaths);                ringoConfig.setDebug(debug);                ringoConfig.setVerbose(verbose);                ringoConfig.setParentProtoProperties(legacyMode);                ringoConfig.setStrictVars(!legacyMode && !production);                ringoConfig.setReloading(!production);                ringoConfig.setOptLevel(optlevel);                if (bootScripts != null) {                    ringoConfig.setBootstrapScripts(Arrays.asList(                            StringUtils.split(bootScripts, ",")));                }                engine = new RhinoEngine(ringoConfig, null);                return engine;            } catch (RuntimeException ex) {            logger.error("Ringo-engine initialization failed", ex);    servletContext.setAttribute(RINGO_ENGINE_ATTRIBUTE, ex);    throw ex;    } catch (Exception e) {    logger.error("Ringo-engine initialization failed", e);    servletContext.setAttribute(RINGO_ENGINE_ATTRIBUTE, e);    throw new java.lang.RuntimeException("Ringo-engine initialization failed",e);    } catch (Error err) {    logger.error("Ringo-engine initialization failed", err);    servletContext.setAttribute(RINGO_ENGINE_ATTRIBUTE, err);    throw err;    }    }        else{        return this.engine;        }}

JsgiUtils:
  这个类其实是一个工具类,通过它可以直接从servletContext中获取engine,非常简单。

JsgiFilter:
 
这个就是处理请求的filter。之所以用Filter主要是可以忽略一些处理。如果js无法处理,还能回到java版本的serlvet来完成。当然,一旦使用filter,js部分如果需要提供filterChain.doFilter()功能则需要二外提供扩展。
  其主要部分当然是doFilter的实现。
/** * 当前没有调用,基本只要filter-mapping url-pattern正确就会执行,并不会执行后续操作。? * chain.doFilter(req, resp); */@Overridepublic void doFilter(ServletRequest prequest, ServletResponse presponse,FilterChain filterChain) throws IOException, ServletException {//httpHttpServletRequest request = (HttpServletRequest) prequest;HttpServletResponse response = (HttpServletResponse) presponse;                JsgiRequest req = new FilterJsgiRequest(request, response, requestProto,                engine.getScope(),this.servletContext ,this,this.filterConfig,filterChain); //如果底层不进行修改需要生成一个模拟的servlet                RingoWorker worker = engine.getWorker();        try {        //filterChain.doFilter也可以由js内部完成。帮助过滤真正需要的内容。        //区别于jsgiservlet一旦符合pattern(比如/XXX/*)必须进行处理。无法要求释放该请求过滤某个后缀。        //servlet无法取消某个特定url的拦截。二filter可以doFilter。            worker.invoke("ringo/jsgi/connector", "handleRequest", module,                    function, req);            //        } catch (Exception x) {            List<ScriptError> errors = worker.getErrors();            boolean verbose = engine.getConfig().isVerbose();            try {                renderError(x, response, errors);                RingoRunner.reportError(x, System.err, errors, verbose);            } catch (Exception failed) {                // custom error reporting failed, rethrow original exception                // for default handling                RingoRunner.reportError(x, System.err, errors, false);                throw new ServletException(x);            }        } finally {            worker.release();        }        }

需要注意的是,其实传递到js中时存在一个request的封装(按照jsgi要求)。
在其env属性下取消原有的servlet,添加filter相关内容。

/** * 用来适配request对象原型类 * @author wfeng007 * @date 2013-10-1 下午08:43:49 */static class FilterJsgiRequest extends JsgiRequest{..........public FilterJsgiRequest(HttpServletRequest request,HttpServletResponse response, JsgiRequest prototype,Scriptable scope,ServletContext servletContext,Filter filter,FilterConfig filterConfig,FilterChain filterChain) {super(request, response, prototype, scope, new JsgiServlet()); //一个空的jsgiservlet之后需要删除。deleteProperty((Scriptable)this.getProperty(this, "env"),"servlet"); //删除该对象引用,与servlet互相排斥?//env-extdefineProperty((Scriptable)this.getProperty(this, "env"),"servletContext", //filter.filterConfig可以得到config?                new NativeJavaObject(scope, servletContext, null), PERMANENT);defineProperty((Scriptable)this.getProperty(this, "env"),"filter", //filter.filterConfig可以得到config?                new NativeJavaObject(scope, filter, null), PERMANENT);defineProperty((Scriptable)this.getProperty(this, "env"),"filterConfig", //filterConfig                new NativeJavaObject(scope, filterConfig, null), PERMANENT);defineProperty((Scriptable)this.getProperty(this, "env"),"filterChain",                new NativeJavaObject(scope, filterChain, null), PERMANENT);}}

RingoEngineHolder与RingoSpringBindingListener
这两个附加内容都是与engine绑定。通过这两个类可以讲engine绑定到一个静态区域,同时也可以绑定到默认的spring Context上。实现原理与参考Spring的listener即可。










原创粉丝点击