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


 就像上回所讲,官方提供了一个样例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框架才这么干的。

架构:
下面是一个粗鲁(是“鲁”)的框线图:
Ringo.js嵌入集成(二):使用Listener Filter替换官方的JsgiServlet实现_第1张图片

整体结构大致由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);
 */
@Override
public void doFilter(ServletRequest prequest, ServletResponse presponse,
FilterChain filterChain) throws IOException, ServletException {
//http
HttpServletRequest 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 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-ext
defineProperty((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即可。










你可能感兴趣的:(Java,Java,Scripting,Ringo.js)