用Ruby脚本在Project_Zero平台上构建Restful应用(一)

日期: 2009-06-21 作者:赵雄伟左超 来源:TechTarget中国 英文

  Project Zero是一个利用动态脚本语言来开发Web2.0应用的敏捷开发环境。它是一个开放的平台,可以很方便地对它进行扩展,最新的版本支持Groovy和PHP两种动态脚本语言。Ruby脚本语言是目前主流的脚本语言之一,有广大的用户群。本文通过对Project Zero进行扩展,使它不仅能够支持Ruby脚本,而且能够利用Ruby脚本在Project Zero上提供RESTful服务。本文还阐述了Project Zero支持基础的RESTful服务的内部机制。

  背景知识

  本文针对的是有Project Zero和Ruby开发经验的人员,并对RESTFul服务有一定的了解。本文假设您在阅读之前已经了解Project Zero的基本情况,RESTful服务的概念以及了解Ruby脚本的语法。您可以从“Project Zero简介,第一部分:为Web应用程序构建RESTful服务”这篇文章中获得关于Project Zero和RESTful服务的基本概念。

  RESTful服务的特征及优点

  在Web服务中,有两个最基本的问题,一是客户端如何把自己的意图传递给服务器;服务器如何知道该请求是要获得数据,删除数据,还是改写数据呢?服务器为什么要做这个操作,而不是那个操作呢?这个问题可以称为方法信息。二是客户端如何告诉服务器对哪些数据进行操作,假如服务器已经知道客户端要删除数据了,但它怎么知道客户端要删除的是哪些数据呢?为什么服务器要对这些数据,而不是那些数据进行操作呢?这个问题称为作用域信息。

  根据对这两个问题处理方式的不同,可以区分一个Web服务是RESTful服务,还是基于SOAP的Web服务。

  RESTful服务是这样来处理上述的两个问题的:

  1.方法信息放在HTTP方法中,常见的HTTP方法有GET,PUT,POST,DELETE,Head

  2.作用域信息放在HTTP域中,如放在URI路径里。

  基于SOAP的Web服务提供了另一种解决方案:方法信息和作用域信息封装在SOAP中,用XML来描述方法名,参数,返回值等信息。这种方式在Web上把HTTP协议作为传输协议,SOAP在HTTP协议之上传递。

  基于SOAP的Web服务都采用自己的词汇(定义的函数名称都不一样)。而RESTful Web服务则是共用一套标准词汇,即HTTP方法,RESTful服务里的每个对象都具有统一的基本接口。

  RESTful服务基于HTTP协议,充分利用了HTTP方法,URI,Cache等HTTP协议作为应用协议所具备的能力,相比于基于SOAP的Web服务,减少了一层封装,更好得挖掘了HTTP协议的潜力,是一种轻量级的Web服务的实现方式。

  RESTful服务的优点有:

  ·可以利用缓存Cache来提高响应速度
  ·通讯本身的无状态性可以让不同的服务器的处理一系列请求中的不同请求,提高服务器的扩展性
  ·浏览器即可作为客户端,简化软件需求
  ·相对与其他叠加在HTTP协议之上的机制,REST的软件依赖性更小
  ·不需要额外的资源发现机制
  ·在软件技术演进中的长期的兼容性更好

  Ruby脚本语言的特点

  Ruby是一个完全面向对象的动态脚本语言,可以采用虚拟机实现跨平台应用。它的语法简洁,灵活,便于扩展,拥有功能强大和完善的标准类库。

  JRuby是面向Ruby、基于Java虚拟机(JVM)的一种解释程序,它结合了Ruby语言的简易性和功能强大的JVM的执行机制,包括与Java库全面集成,可以直接调用Java和Ruby的资源。本文将采用JRuby作为Ruby脚本的实现方式。

  Ruby与Project Zero结合的优势

  Ruby语言是目前最流行的动态脚本语言之一,有大量的开发人员在基于Ruby进行Web 2.0和相关应用的开发。Project Zero作为Web2.0的敏捷开发环境,提供了诸如安全性、数据库访问接口、视图层渲染接口等大量的平台底层支持。把Ruby语言引入到Project Zero中,可以无缝地利用Project Zero平台提供的底层支持,将Ruby语言动态、高效的能力和Project Zero敏捷的特性很好地结合在一起。

  Project Zero对RESTful服务有很好的支持,默认采用的实现技术是Groovy和PHP。它对基于资源的URI映射和对HTTP协议的事件机制提供了内部支持。Ruby语言能够很方便地利用Project Zero的这些内建机制,同时结合Ruby强大的动态特性,从而快速敏捷地在Project Zero上构建起RESTFul服务。如图1所示客户端、外部应用、Ruby、Project Zero以及底层数据库之间的关系。

  图1. 系统结构图

          
 
  在Project Zero中用Ruby实现RESTful服务

  在Project Zero中用Ruby实现基本的RESTful服务有以下步骤:

  ·将resource的HTTP URL映射到Ruby脚本
  ·为Ruby脚本配置Interpreter
  ·将Project Zero的事件映射到Ruby脚本中的事件响应函数
  ·在Ruby脚本中利用Zero提供的持久化机制对数据库进行操作
  ·将执行结果渲染到客户端

  将resource的HTTP URL映射到Ruby脚本

  这个过程需要两个配置:

  为resource的HTTP URL配置事件响应的handler

  将处理资源的script的文件名后缀配置为“.rb”

  为resource的HTTP URL配置事件响应的handler

  在Zero中,默认的RESTful的URL是用“/resources/”做前缀。Zero会将“/resources/”为前缀的URL映射到Zero工程目录下的“/app/resources”,在这个目录中存放着对资源处理的脚本。脚本的文件名和资源名相同。

  在Zero中合法的RESTful的URL有如下规则:

/resources/<collection name>[/<member identifier>[/<path info>]]

  “collection name”指定了处理该URL的handler,“member identifier”和“path info”是可以选的,作为参数保存在GlobalContext的request域中。

  注:URL不能以“/”结尾,否则会抛出URL不合法的异常

  Project Zero用一种松耦合的事件机制来处理HTTP请求的事件和内部触发的事件。Zero默认的处理RESTful HTTP URL的Handler是zero.core.resource.ResourceHandler类。在zero.config里可以配置事件的处理流程,将HTTP URL请求触发的事件映射到ResourceHandler。

  清单1:zero.config中对ResourceHandler的配置

# Resource handling
/config/handlers += {
 “events” : [“GET”, “POST”, “PUT”, “DELETE”],
 “handler” : “zero.core.resource.ResourceHandler.class”,
 “conditions” : [“/request/path matches /resources/.+”]
}
 
  拿这个配置项说明:“events”配置了要处理的事件,“handler”指定了处理上述事件的Handler,“conditions”通过正则表达式指定了要对哪些模式的URLs进行拦截处理。

  Zero中将RESTful Resource分为Collection和Member两种,对这两种类型的资源,Zero根据HTTP Events分别给它们定义了Zero内部调用的Event类型,见下表:

           

  ResourceHandler负责将HTTP Event转化为Zero内部定义的Event,并调用EventEngine.fire()方法,将事件传递给后端的EventDispatcher来处理。

  将处理资源的script的文件名后缀配置为“.rb”

  Zero默认的对resource处理的脚本语言是Groovy,可以在zero.config中配置。只需要做如下配置,就可以将RESTful HTTP URL映射到Ruby脚本中:

  清单2:zero.config中对RESTful服务采用的默认脚本文件后缀名的配置
/config/resources/defaultExtensions = [“.rb”]
 
  Zero Core API中的zero.core.resource.ResourceResolver类负责从配置文件中读取“/config/resources/defaultExtension”配置项,然后结合资源名称拼出一个脚本文件名,Zero会检测这个脚本文件是否存在,如果不存在,将抛出“No resource handler found”异常。如果存在这个目标脚本文件,就将脚本文件的物理路径保存到HandlerInfo中。

  zero.core.events.HandlerInfo封装了将要执行的目标脚本的文件路径以及HTTP request中保存的数据和事件触发后设置的一些数据。它的数据将被传递到脚本解释器中。

  为Ruby脚本配置Interpreter

  Zero提供了一个动态脚本语言扩展的接口:zero.core.Interpreter。这个接口只提供了一个方法:

  清单3:Zero Core API中Interpreter接口的方法
public void invoke(HandlerInfo handlerInfo);
 
  Zero默认提供的Interpreter实现类GroovyInterpreter支持对Groovy脚本的处理。我们要想在Zero中使用Ruby脚本,就要提供处理Ruby脚本的Interpreter,这里我们提供一个JRubyRestfulInterpreter类来实现Interpreter接口,这个类将对“.rb”后缀的脚本文件进行处理。需要在zero.config中做如下配置将JRubyRestfulInterpreter集成到Zero中来。

  清单4:zero.config中针对Ruby脚本的Interpreter实现类JRubyRestfulInterpreter的配置
/config/interpreters/.rb = “zero.extend.ruby.JRubyRestfulInterpreter”
 
  zero.core.interprerter.InterpreterFactory负责从zero.config配置文件中读取这段配置,并实例化JRubyRestfulInterpreter对象。InterpreterFactory是在zero.core.events.dispatcher.EventDispatcher中被调用的。

  Zero在处理RESTful URL时内部的基本调用流程如下:

                 

  图2.Zero处理RESTful URL时内部的基本调用流程
 
  注:Zero读取配置文件时准循“First one win”的原则,会先读取当前工程的zero.config配置项信息,再读取所依赖工程的zero.config配置项信息,以最先读到的配置为准。

  将Project Zero的事件映射到Ruby脚本中的事件响应函数

  JRuby能够在Ruby脚本中调用Java API,从而可以获得Zero的全局上下文,因此本文采用JRury来编写Ruby脚本。关于JRuby的更多信息请查看文章末尾的参考资料。

  Ruby语言有很强大的反射机制,因此本文在处理“将Zero的事件映射到Ruby脚本中的事件响应函数”时采用如下策略:

  1.创建一个JRubyRestfulDispatcher.rb脚本,使用JRuby类来作为映射控制器,可以充分利用Ruby的反射机制,动态地实现对Ruby函数的调用,同时又能访问到Zero的GlobalContext中的数据,从而可以很简洁地将Zero定义的事件映射到Ruby的事件响应函数中。

  2.在JRubyRestfulInterpreter解释器类中执行JRubyRestfulDispatcher.rb脚本。用Java调用JRuby脚本有多种方式,本文采用Apache提供的开源的脚本引擎:Apache BSF(Bean Script Framework)来执行JRuby脚本。

  JRubyRestfulInterpreter解释器完成的任务如下:

  ·读取JrubyRestfulDispatcher.rb脚本文件的内容
  ·使用BSF的脚本引擎执行JRuby脚本

  清单5: JRubyRestfulInterpreter.java
public class JRubyRestfulInterpreter implements Interpreter {
 private static final String JRUBYRESTFULDISPATHER = “jrubyRestfulDispatcher.rb”;

 public void invoke(HandlerInfo handlerInfo) {
  restfulInvoke(handlerInfo);
 }
 public void restfulInvoke(HandlerInfo handlerInfo) {
  //load “jrubyRestfulDispatcher.rb” script
  BufferedReader br = new BufferedReader(new InputStreamReader(getClass()
    .getResourceAsStream(JRUBYRESTFULDISPATHER)));
  StringBuffer sb = new StringBuffer();
  try {
   String str;
   while ((str = br.readLine()) != null) {
    sb.append(str).append(“n”);
   }
   BSFManager.registerScriptingEngine(“ruby”,
     “org.jruby.javasupport.bsf.JRubyEngine”,
     new String[] { “rb” });
   BSFManager manager = new BSFManager();   
 //use BSFManager execute “jrubyRestfulDispatcher.rb” script
   manager.exec(“ruby”, “(java)”, 1, 1, sb.toString());
  } catch (IOException e) {
   e.printStackTrace();
  } catch (BSFException ex) {
   ex.printStackTrace();
  } 
  GlobalContext.zput(“/request/status”, 200);
 }
}
 
  JrubyRestfulDispatcher.rb脚本完成的任务主要有:

  ·从Zero的GlobalContext中取得要执行的目标脚本的文件名
  ·通过Ruby的反射机制,创建目标脚本的Ruby类实例
  ·从Zero的GlobalContext中取得要响应的事件,利用Ruby的反射机制,将Event映射到目标Ruby类实例的事件响应方法上。
  ·进行异常处理,如果发生异常,就调用Zero的ViewEngine将异常渲染到客户端

  清单6:JrubyRestfulDispatcher.rb脚本
require “java”
include_class “zero.core.context.GlobalContext”
include_class “zero.core.views.ViewEngine”
class JrubyRestfulDispatcher
 # convert a string to a class
 def class_for_name(class_name)
 return eval(class_name.capitalize).new
 end

 def get_method_name(event_name)
 return “on” + event_name.capitalize
 end


 # get the target script name from GlobalContext
 def get_target_script_name
  path = GlobalContext.zget(‘/request/path’) + ‘/’
  resource_path = path[11,path.length]
  return resource_path[0,resource_path.index(‘/’)]
 end

 # dispatch the request to a resource handler
 def do_dispatch
 require get_target_script_name
 object = class_for_name(get_target_script_name)
 target_method = get_method_name(GlobalContext.zget(‘/event/_name’))
 if(object.respond_to?(target_method))
 object.send(target_method)
 else
 GlobalContext.zput(“/request/view”,”error”)
 GlobalContext.zput(“/request/status”,”404″)
 GlobalContext.zput(“/request/error/message”,”No Method Found”)
 ViewEngine.render()
 end
 end
end

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐