Ringo.js嵌入集成(三):利用RingoJs仿制wordpress的plugin机制

    之前的实验学习当中已经对RingoJs的容器集成有了定制,利用web的Listener完全可以初始化js容器,以替换JsgiServlet的实现。同时使用holder方式(偷懒方式)维护这个应用唯一的js容器。现在我们要利用这个容器拓展出一个使用的框架功能,plugin模块。

源于对wp的羡慕
    对于php的经典开源产品wordpress估计很多人知道或者用过。他之所以被广泛使用其有两大特色区别于当时的其他blog产品。
1.主题模板机制及大量的界面模板实现
2.插件机制以及大量的附加插件功能
    本人不是phpfans并没有深入研究过wp的这两个机制。只是在一些论坛上以及google有所了解。
    主题模板机制其实技术上感觉与jsp或者诸如freemarker基本类似。确实java jsp的include完全能够完成php的这种切分模板功能,只是java的类似开源产品中很少能够拥有那么大量的skins可用。因为java开发者似乎是为了使用模板而模板,并不考虑将模板能力发展成跟有效的“主题模板”概念。
    而另外一块插件机制,原来以为jsp的taglib可以直接覆盖。不过看了一些wp的插件说明,发现现jsp的taglib有个比较大缺陷。就是taglib无法“在线安装卸载”,需要编译好后与其他class一样部署在lib或classes目录下。启动时载入,然后被jsp页面所引用。其实class动态装卸不是办不到,而是复杂,且引用机制导致动态class装卸jsp的引用也会出现问题。

在线插件机制js-java平台的设计
    基于上面的原因,可以利用js容器弥补在线插件部署的问题。即实现一个插件模块,其下既可以引用普通java实现,也可以使用js容器完成实现。js的插件版本可以在安装卸载,利用cj的package实现甚至可以做到,在线外部库下载安装(从规模后这个非常有用)。
    我们实现一个prototype版本,抛开下载功能,插件模块提供如下功能:
    插件,提供一组钩子/句柄实现的集合可以被容器所管理。钩子/句柄指一个处理方法程序或函数。钩子/句柄拥有名称,用于调用执行的识别。
    插件容器,分配内存空间维护插件的生命周期。
    插件安装,用户可以通过web界面或命令行将在特定位置的插件文件或包载入插件容器。
    插件(钩子)执行,某一段程序调用特定名称钩子执行。执行时将所有拥有该钩子名称的插件均找到并执行其对应钩子程序。

在线插件机制js-java平台的实现
    首先使用Java直接实现一个插件模块框架。这里非常简单使用静态方法作为插件入口。Plugin类只实现基于RingoJs的 js版本实现。
//WebPlugin两个静态方法doHandle、addPlugin作为插件访问入口。
public class WebPlugin {
    ...
        //handleId - Plugin 非常偷懒的实现版本
public static Map> _handleMapping=null;
        static{
_handleMapping=new HashMap>();
}
    ...
    //在需要挂钩钩子的地方调用该方法
public static void doHandle(String handleId,HttpServletRequest req,HttpServletResponse resp){
  if(handleId==null)return;//不做任何事情
  Set plgLs=_handleMapping.get(handleId);
  if(plgLs==null||plgLs.size()<=0)return;//钩子上没有插件也不做任何事情
  if(plgLs.size()>20)_logger.warn("There are too much plugin-handle need to process! q:"+plgLs.size());
  for (WebPlugin plg : plgLs) {
  plg.handle(handleId, req,resp);
  }
}
    ...
    //在控制台执行执行该方法用来安装插件文件
    public static void addPlugin(String pluginId){
if(pluginId==null)return;	
WebPlugin wp=new WebPlugin(pluginId);	
//获取初始化后的名称列表
List funNs=wp.getHandleNameList();
//加入所有关联plg
for (String handleName : funNs) {
//如果没有该handle就创建一个
if(_handleMapping.get(handleName)==null)_handleMapping.put(handleName, new LinkedHashSet());
Set plgLs= _handleMapping.get(handleName);
//	plgLs.contains(wp);
boolean isOk=plgLs.add(wp);
if(isOk)
_logger.info("add plugin:"+pluginId+" handle:"+handleName);
else
_logger.info("The plugin is already exists. plugin:"+pluginId+" at handle:"+handleName);
}
}    
    ...
    
    //
    //插件对象实现
    //
}



以上类的实例就是插件本身,其实现类如下。
private List handleNameList=new ArrayList(2);
public WebPlugin(String id) {
super();
if(id==null)throw new NullPointerException("Plugin Id must be not Null!!");
this.id = id;
this.moduleId=id+scriptingSuffix;
RingoEngineHolder.invoke("plg/connector", "addPlugin", this.moduleId,this.handleNameList);
}
        public void handle(String handleId,HttpServletRequest req,HttpServletResponse resp){
RingoEngineHolder.invoke("plg/connector", "handle", this.moduleId,handleId,req,resp);
}

    根据以上的插件类实现,实例化插件时使用js脚本加载了一个RingoJs模块。而handle方法其实就是执行RingoJs特定模块的特定函数。使用moduleId以及handleId确定模块export的函数。其中moduleId这里直接等于id+scriptingSuffix,可以是一个自定义后缀比如我这里为:scriptingSuffix =".plg"。
/**
 * 为注册模块添加返回export出来的句柄函数。
 * @param moduleId
 * @param reFuncNameLs
 */
function addPlugin(moduleId,reFuncNameLs) {
var m = require(moduleId);
for(var an in m){ //遍历出的就是名称。
var a=m[an];
if(typeof(a)==='function'){
print("+"+an+":"+typeof(an));
reFuncNameLs.add(an);
}
}
}
/**
 * 调用插件(模块)句柄函数。
 * @param moduleId
 * @param funcName
 * @param request
 * @param response
 */
function handle(moduleId,funcName,request,response){
var m = require(moduleId);
var func=m[funcName];
log.info("do handle:"+funcName);
func(request,response);
}

wp插件例子”Hello, Dolly“ RingoJs版本实现。
1.撰写例子插件hi.plg.js
......
/**
 * export 的模块函数,同时也是插件的执行句柄。
 */
export("prtHello"
,"vPosNeck"); //导出插件handle
function prtHello(req,resp){
var out=resp.getWriter();
out.print("hi 我是插件, hello World!以下是hello dolly的歌词!
"); out.print(getLyricsLine()+"
"); //测试导入的其他模块内容 // summjs.foo(); // return; }; var vPosNeck=function(req,resp){ prtHello(req,resp); log.info("prtHello!!"); return; }; function getLyricsLine(){ var lyrics = ["Hello, Dolly", "Well, hello, Dolly", ...... ...... var reN=getRandomNum(0, lyrics.length); return lyrics[reN]; }


2.在某个jsp中放置plugin的钩子。(业务开发时预先放置好。)
使用doHandle方法进行放置。如在neck.jsp最后放置vPosNeck钩子。
Ringo.js嵌入集成(三):利用RingoJs仿制wordpress的plugin机制_第1张图片

3.插件放置
plugin的位置是基于common-js的module标准放置的。如,使用plg目录作为ringo模块的查找库位置之一。
而插件的入口,就是一个module文件。如这里为hi.plg.js。
Ringo.js嵌入集成(三):利用RingoJs仿制wordpress的plugin机制_第2张图片

4.在特定位置执行addPlugin将插件载入插件容器。


5.最后的效果
Ringo.js嵌入集成(三):利用RingoJs仿制wordpress的plugin机制_第3张图片

对于插件安装,一般是直接网上下载插件并执行addPlugin动作。另外demo之外还应该存在卸载插件的功能等。
在实际开发中,最好情况是在产品中固定位置放置钩子。而不是随意放置。应该多考虑如何提供有用的plugin实现,增加复用性。

注意点:
    利用ringo的cg-module以及cg-package标准设计业务上的plugin功能模块。
    使用一个module查找路径作为plugin路径。
    以require导入内容作为plugin。require导入可以是一个单文件模块或者目录形式的(模块)包。当然包导入时默认会寻找一个main入口module。
    则一个插件就是一个单文件module或一个module包。
    为了应用资源方便,其他资源文件与module程序文件,在同一个目录结构中被映射。即,建议module父目录直接放置于web应用资源目录下。如:webapppath/plg/。且plg中包形式的modules 设置directories.lib='.'。当然,用户如果确实希望资源路径分离也可,但是开发者必须执行判断映射文件目录。
    plugin程序文件为防止被客户端浏览,建议使用plg.js后缀作为插件入口模块名称。插件本身handle函数均在入口中定义。而plugin模块应定义过滤器过滤plg.js后缀的访问。   

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