这里我们按照惯例将Ruby的事件响应函数名默认为:on+事件名,针对与Zero的Resources Event,有8个默认的事件响应函数:
针对Collection资源的事件响应函数有:onList,onPutCollection,onCreate,onDeleteCollection
针对Member资源的事件响应函数有:onRetrieve,onUpdate,onPostMember,onDelete
在Ruby脚本中利用Zero提供的持久化机制对数据库进行操作
Zero的Core API中提供了zero.data.Manager类封装了对数据库的访问操作,如对数据的增删改查的方法。要想在Ruby脚本中使用Zero平台提供的数据库访问能力,需要做一些改进。因为JRuby脚本编译后,脚本中的字符串类型会转化成RubyString类型,而zero.data.Manager并没有提供对RubyString的支持,所以需要提供一个代理类,将RubyString转化为Java的String类型。本文提供了一个zero.data.ruby.Manager类,部分代码如下:
清单7:zero.data.ruby.Manager.java代码片段
public class Manager {
private zero.data.Manager manager;
public static zero.data.ruby.Manager create(String dbKey) {
Manager manager = new Manager();
manager.manager = zero.data.Manager.create(dbKey);
return manager;
}
…………
public Map<String, Object> queryFirst(RubyString sql) {
String newSql = sql.toString();
Map<String, Object> result = manager.queryFirst(newSql, null);
return result;
}
public List<Map<String, Object>> queryList(RubyString sql) {
String newSql = sql.toString();
List<Map<String, Object>> result = manager.queryList(newSql, null);
return result;
}
………………
}
将执行结果渲染到客户端
Zero平台提供了两种方式来将执行结果渲染到客户端。一种是直接采用OutputStream或PrintWriter将结果写到HTTP response中,比如:
清单8:Zero采用OutputStream向客户端发送HTTP response
PrintWriter writer = (PrintWriter) zget(“/request/writer”);
writer.println(“Hello World.”);
另外一种就是利用Zero提供的ViewEngine API来创建不同形式的Response。目前提供了View,Error,JSON,XML四种renders。
调用一个render需要以下步骤:
·指定相应的render,这个值在Global Context的“/request/view”项中设置
·把和render相关的数据设置到Global Context中
·调用ViewEngine.render()方法
本文将采用JSON render将结果集以JSON的方式渲染到客户端。
比如如下代码就是ViewEngine的基本调用方式。
清单9:Zero采用ViewEngine向客户端发送HTTP response
GlobalContext.zput(“/request/view”,”JSON”)
GlobalContext.zput(“/request/json/output”,result)
ViewEngine.render()
至此我们已经可以将RESTful HTTP URL映射到相应的Ruby脚本中,根据请求的事件映射到Ruby脚本的事件响应函数中,在Ruby的方法中使用Data Manager来访问数据库,使用ViewEngine将结果用多种形式返回给客户端。这样就完成了一个基本的RESTful Web服务的流程。
示例场景:
接下来我们通过一个简单的示例来展示上述对Zero的扩展如何应用到实际的工程中。示例的内容主要有:
创建一个Zero应用程序,定义一个Customer资源,然后利用Ruby脚本为这个资源提供RESTful服务。
创建一个示例应用
实例具体的步骤如下:
在数据库中创建Customer表,提供测试数据
本示例中采用Zero自带的Derby数据库,数据库名为PRO_DB。示例的Customer表的SQL脚本如下:
清单10:create.sql
CREATE TABLE CUSTOMER (
CUSTOMERID INTEGER NOT NULL GENERATED ALWAYS AS
IDENTITY(START WITH 1, INCREMENT BY 1),
FIRSTNAME VARCHAR(50) NOT NULL,
LASTNAME VARCHAR(50) NOT NULL,
COUNTRY VARCHAR(250),
DESCRIPTION VARCHAR(250)
)
INSERT INTO CUSTOMER(FIRSTNAME,LASTNAME,COUNTRY,DESCRIPTION)
VALUES (‘Simpson’,’Homer’,’USA’,’He is a Ruby Programmer’);
INSERT INTO CUSTOMER(FIRSTNAME,LASTNAME,COUNTRY,DESCRIPTION)
VALUES (‘Simpson’,’Maggie’,’USA’,’She is a Groovy Programmer’);
INSERT INTO CUSTOMER(FIRSTNAME,LASTNAME,COUNTRY,DESCRIPTION)
VALUES (‘Simpson’,’Bart’,’USA’,’He is a Java Programmer’);
INSERT INTO CUSTOMER(FIRSTNAME,LASTNAME,COUNTRY,DESCRIPTION)
VALUES (‘Simpson’,’Lisa’,’USA’,’She is a C++ Programmer’);
配置Zero工程的Java Build Path
本示例采用了JRuby作为Ruby的实现语言,利用Apache BSF脚本引擎在Java中调用JRuby脚本,所以需要在classpath中引入这两个jar文件。同时需要引入我们对Zero扩展的jar文件。本文对Project Zero所做的扩展已经打包成jar文件,可以从文后的附件中获得。Java Build Path配置如下:
图3. 示例程序的Java Build Path配置
配置zero.config
在工程的config/zero.config文件中添加使Zero支持Ruby脚本的配置
清单11:示例程序的config/zero.config配置文件
# HTTP port (default is 8080)
/config/http/port = 8080
/config/db/PRO_DB = {
“class”: “org.apache.derby.jdbc.EmbeddedDataSource”,
“databaseName”: “PRO_DB”,
“user”: “APP”,
“password”: “APP”
}
/config/resources/defaultExtensions = [“.rb”]
/config/interpreters/.rb = “zero.extend.ruby.JRubyRestfulInterpreter”
实现customer.rb脚本
customer.rb需要写事件响应函数,来处理Zero内部定义的Resource事件。本例展示了如何获得Collection和Member资源。
onList方法来处理如下的HTTP URL:http://localhost:8080/resources/customer,响应GET customer collection的事件。
onRetrieve方法来处理如下的HTTP URL:http://localhost:8080/resources/customer/1,响应GET customer member的事件。
清单12:customer.rb脚本
require “java”
include_class “zero.data.ruby.Manager”
include_class “zero.core.context.GlobalContext”
include_class “zero.core.views.ViewEngine”
include_class “zero.json.Json”
class Customer
def onRetrieve
data = Manager.create(‘PRO_DB’);
id = GlobalContext.zget(“/request/params/customerId”)
result = data.queryFirst(“select c.customerId,c.firstname,c.lastname,
c.description
from customer as c where c.customerId = #{id}”);
if(result)
GlobalContext.zput(“/request/view”,”JSON”)
GlobalContext.zput(“/request/json/output”,result)
ViewEngine.render()
else
GlobalContext.zput(“/request/view”,”error”)
GlobalContext.zput(“/request/status”,”404″)
GlobalContext.zput(“/request/error/message”,”No Result Found”)
ViewEngine.render()
end
end
def onList
data = Manager.create(‘PRO_DB’)
result = data.queryList(“select * from customer”)
if(result)
GlobalContext.zput(“/request/view”,”JSON”)
GlobalContext.zput(“/request/json/output”,result)
ViewEngine.render()
else
GlobalContext.zput(“/request/view”,”error”)
GlobalContext.zput(“/request/status”,”404″)
GlobalContext.zput(“/request/error/message”,”No Result Found”)
ViewEngine.render()
end
end
………………
end
在customer.rb中可以从GlobalContext中获得request的参数,可以调用zero.extend.data.Manager来访问数据库,可以调用Zero的ViewEengine将Json对象返回到客户端。
执行结果
采用Firefox的Poster插件,可以模拟HTTP的访问,本文的测试基于Poster插件,更多关于Poster的内容可以参考文后的资料。
访问Customer Collection资源:http://localhost:8080/resources/customer
图4. 用Poster插件向服务器发送http://localhost:8080/resources/customer请求
返回结果:
图5. 服务器针对http://localhost:8080/resources/customer请求返回的response信息,返回的是一个JSON对象的数组
访问一个Customer member资源:http://localhost:8080/resources/customer/1
图6. 用Poster插件向服务器发送http://localhost:8080/resources/customer/1请求
返回结果:
图7. 服务器针对http://localhost:8080/resources/customer/1请求返回的response信息,返回的是一个JSON对象。
结束语:
本文介绍了RESTful服务的基本概念,Project Zero提供RESTful服务时内部的事件流,以及如何在Project Zero进行扩展使其支持用Ruby脚本编写的RESTful服务,并结合一个简单的示例展示了如何利用我们提供的Zero扩展用Ruby脚本开发Zero的RESTful服务。您还了解到了如何利用BSF脚本引擎在Java中调用Ruby脚本。
作者简介
赵雄伟是一位IBM软件工程师,工作在IBM中国软件开发实验室企业应用开发部门,现在正从事企业电子商务应用的开发和支持工作,在Java开发和Web开发方面有丰富的经验,特别对Web应用的架构及SOA、SCA等有浓厚的兴趣,您可以通过zhaoxw@cn.ibm.com与他联系。
左超是一位IBM软件工程师,工作在IBM中国软件开发实验室企业应用开发部门,目前从事企业电子商务应用的开发,您可以通过zuochao@cn.ibm.com与他联系。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
相关推荐
-
控制状态下为何对比REST友好与REST架构
要想控制住云中状态,架构师应该先实现一种REST友好界面,而不是考虑设计出一种REST架构模型。
-
REST与SOA集成的优缺点
在我深入探讨问题细节之前,有一点我要事先声明:REST和SOA不是相互竞争的两种集成方法。
-
为了更好的REST接口 请像开发者一样思考
企业组织需要思考,一个开发者在进行开发或REST式的服务开发时的需求。以及思考在应用程序接口(API)部署之后,维持服务水平协议(SLA)所需的基础设施。
-
基于云的BPM需要RESTful服务吗?
ZapThink的分析师Jason Bloomberg声称基于云的业务流程管理(BPM)软件将会给那些传统的、不能很方便地迁移到云交付模式的BPM引擎带来颠覆性影响。