IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Web development | Open source | SOA and Web services  >

Project Zero 简介,第 1 部分: 为 Web 应用程序构建 RESTful 服务

探索可用来开发和执行现代 Web 应用程序的一种功能强大却又十分简单的平台

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

讨论

样例代码


级别: 初级

Roland Barcia (barcia@us.ibm.com), 资深技术人员, IBM 
Steve Ims ( steveims@us.ibm.com), 资深技术人员, IBM

2007 年 10 月 29 日

Project Zero 系列的第 1 部分中,获得 Project Zero 的动手指导以便创建、组装和部署功能强大的 Web 应用程序。首先,您将接触到受社区驱动的 Project Zero 的简单介绍以及它对创建 RESTful Web 服务的一些约定。之后,通过一个逐步示例,向您介绍如何设置环境、创建 Zero 项目、构建 RESTful 服务来公开数据、测试应用程序并导入示例应用程序以便使用此 RESTful 服务。

引言

Project Zero 是 IBM 的一个孵化器项目,侧重遵从面向服务的体系结构( Service-Oriented Architecture,SOA)的 Web 2.0 应用程序的敏捷开发。将 Web 2.0 应用于 SOA,可以利用 Web 工件来扩展 SOA 的能力范围。可以将其看成是经过 Web 扩展的 SOA。

Project Zero 引入了一种简单的环境来基于流行的 Web 技术创建、组装和执行应用程序。Project Zero 环境包括面向 GroovyPHP 的脚本运行时,且具有应用程序编程接口,可加以优化用来生成 Representational State Transfer (REST) 风格的服务、集成 mashup 和富 Web 接口。Project Zero 是一个孵化器项目,发起于 IBM® 内部,侧重于下一代动态 Web 应用程序的敏捷开发。

Project Zero 现在已经可以通过社区驱动的商业开发进行公开开发。

Web 扩展的 SOA 是 SOA 的一个子集,侧重于超文本传输协议(Hypertext Transfer Protocol,HTTP)和基本的 RESTful 原理。Web 扩展的 SOA 倡导设计模式的使用,设计模式曾让 Web 大获成功,所以在这里也很有意义,应用程序设计也遵从这种模式对大家都有好处。好处包括:

  • 可伸缩性:通过 缓存 和无状态交互
  • 简单性:其前提条件要求必须是典型的 HTTP 和 XML 或 JavaScript Object Notation(JSON) 解析和呈现
  • 源于共同标准的广泛 “网络效应”:比如,站点可以使用和聚合 Atom 或 RSS 提要,而无需知道关于内容的更多细节

Project Zero 的目标既有技术方面的也有社会方面的。技术方面的目标是提供能在三个重要层面简化应用程序开发的可伸缩平台:

创建
利用对脚本语言(当前为 Groovy 和 PHP)的支持、能促使 RESTful 模式的约定以及可重用资源的编目简化开发。
组装
允许快速访问并将全异的服务集成到统一的应用程序,包括数据流、编排和自定义中介。
部署
基于稳定的 Java™ Virtual Machine (JVM) 提供一种以应用程序为中心的运行时环境,JVM 被优化以便进行敏捷开发(占用内存少,重启迅速)。

社会方面的目标与开发过程本身相关。Zero 将会作为社区驱动的商业开发(CD/CD)进入公开领域。用户社区将能参与和影响有关 Zero 的技术决策。用户能够直接接触到 Zero 开发团队和源代码本身。这种程度的商业软件开发透明性在 IBM 还不多见;我们期望它能够带来高效的产出。

为了与 CD/CD 一致,所有有关 Zero 的信息都可以在 www.projectzero.org 找到。本系列文章旨在对底层概念进行结构性介绍,并不揭示有关 Zero 的最新信息。





回页首


RESTful 服务

术语 REST 描述了可用来实现联网系统的一种设计模型。REST 既不是一种技术也不是一种标准;它是用来通过 Web 公开资源的一种架构风格。RESTful 架构遵从以下几个原则:

  • 请求是客户-服务器 式的,并很自然地使用一种基于拉的交互风格。对组件的使用会从服务器中拉出状态的表示。
  • 请求是 无状态的。每个从客户端到服务器端的请求都必须包含理解此请求所需的全部信息,而且不能利用服务器上所存储的上下文。

    REST 并不一定就意味着在中间层没有任何状态;其真正的含义是为实现对某资源的请求的这个状态并不依赖于该状态。

  • 客户机和服务器都遵从统一的接口。所有的资源都可通过 Web 扩展的 SOA 世界中的普通接口进行访问 — HTTP 及 HTTP 方法:GETPOSTPUTDELETE
  • 客户机负责访问命名的资源。此系统由使用 URL(比如 HTTP URL)命名的资源组成。

Representational State Transfer(REST)是面向分布式超媒体系统(例如 World Wide Web)的软件架构风格。

REST 是表示 Web 中的服务的关键技术。REST 在公开基于数据的服务方面效果最好,理解这一点非常重要。这些数据服务进而可以混合和匹配以便构建新的应用程序(通常称为 mashup)。如下的示例展示了客户机是如何对待 RESTful 服务的。

http://<host>/customer

  • GET:返回客户列表
  • POST:创建客户记录

http://<host>/customer/roland

  • GET:返回 Roland 客户记录
  • PUT:更新 Roland 记录
  • DELETE:删除 Roland 记录

在本例中,所公开的资源是 Customer。此 Customer 用 URI /customer 表示。特定的客户则通过在 Customer 之后追加标识符加以表示,例如 /customer/ims。HTTP 报头方法定义了访问此资源的意图。





回页首


Project Zero 的核心编程概念

Project Zero 是一种新的、专用平台的基础,该平台将 Web 2.0 和 SOA 结合在一起以支持下一代基于 Web 的业务应用程序。本节涵盖了其中一些核心的概念。Project Zero Developer's Guide(参见 参考资料)提供了有关此概念的很多细节。

脚本编写和以 Java 作为系统语言

简化开发的一个重要趋势是脚本语言。Zero 的目标是通过提供围绕脚本的简化应用程序编程接口(API)来帮助减少开发服务的负荷。默认的脚本语言是 Groovy,它基于的是 Java 并让 Java 程序员能够轻松转换到 Groovy。通过扩展模块,Project Zero 也支持将 PHP 作为一种脚本语言。

事件

Zero 是一种基于事件的系统。该系统的所有关键行为都以事件集及合适的事件状态的形式公开给应用程序。作为应用程序开发人员,您的主要的工作是通过提供可钩挂到已知的系统事件的处理程序集来获得所需的应用程序行为。标准 Zero 事件对应用程序来说就是一些有意义的行为。

例如,此系统的一个 HTTP 请求可以发出一组事件,开发人员可针对这些事件编写处理程序。图 1 展示了这个概念,并且还包括了处理安全性方面问题的事件和特定的 HTTP 方法(GET | POST)事件。(有关事件处理的更多信息,请参阅 Project Zero Developer’s Guide 中事件处理一节


图 1. 事件
事件

全局上下文

Project Zero 中的事件处理程序没有显式的输入和输出参数,例如请求和响应。由于 Project Zero 中的所有事件处理程序都不能跨不同的调用保持同一个变量状态,因而是无状态的。Project Zero 提供了全局上下文 来作为访问和维持所有状态的一种手段。全局上下文提供了有关应用程序当前事件的所有有趣数据,此外还包括了在应用程序的组件间存储和共享这些信息的机制。

全局上下文被划分成几个区,每个区都保存具有不同可见性和不同生命周期的数据,如 图 2 所示。例如,User 区保存的是给定客户的会话状态,只对该客户可见,而且会保持此数据直到用户变为不活动的。

Request 区保存的是当前活动请求的状态,只对为该请求而执行的组件可见,而且此状态只会保持到该请求结束。


图 2. 全局上下文
图 2. 全局上下文




回页首


约定和应用程序目录结构

Zero 环境提供了几个约定,如能遵循,一定会极大地简化 Zero 应用程序开发和减少所需指定的配置信息。Zero 的目标是拥有尽可能少的配置信息。某些约定非常常见,而且也在意料之中。例如,可以将脚本放在应用程序的公共文件夹内并对应于没有配置数据的某个 HTTP 资源运行该脚本。

其他的约定则针对于特定的某个模式。图 3 显示的是将 Groovy 脚本存储在某个特定文件夹下的例子。这种方式会自动将这些方法注册在此文件夹中以响应 HTTP REST 事件。在本文稍后的内容中,还会介绍如何使用 RESTful 模式快速开发 RESTful 服务以公开数据。更多信息,请参阅 Project Zero Developer's Guide 的 Virtual Directory 一节。


图 3. 虚拟目录
图 3. 虚拟目录




回页首


示例场景

现在您就可以使用 Zero 平台构建自己的第一个 RESTful 服务了。让我们首先通过如下的示例先来全面地浏览一下其中的概念。

概览

贯穿本系列,将通过构建一个公共的示例来展示各种不同的概念。该示例的主题是折扣优惠。客户经常会竭力想让自己符合享受折扣优惠的条件,而提供商则需要让优惠折扣可用。图 4 所示的这个用例图表展示了各种行为。在本文中,我们将侧重于优惠提供者这一角色及其折扣优惠管理用例。


图 4. 用例
图 4. 用例

提供者需要拥有一种将优惠放入系统并进行维护的途径;一个优惠就是我们所感兴趣的一条主要数据。图 5 显示了此优惠应用程序的数据模型。


图 5. 数据模型
图 5. 数据模型

开始时只有单一一个表。在后续的文章中,我们将会扩展这个数据模型以加入各种关系并通过使用 REST 范型来探究更加复杂的数据。

设计 RESTful 端点

数据集大体准备完毕后,就可以开始将数据映射到 RESTful 名称空间。通常,为映射某资源而针对此特定条目创建一个表的做法将非常有用。表 1 给出了到我们的这个资源的 RESTful 映射。您将亲自构建此 RESTful 服务。


表 1. REST 数据
资源 URI 方法 表示 描述
Incentive 表 /incentiveGETJSON 对象数组检索优惠列表
Incentive /incentivePOSTJSON 对象创建新优惠
Incentive /incentive/<incentiveId>GETJSON 对象检索某个优惠
Incentive /incentive/<incentiveId> PUT JSON 对象 更新单个优惠
Incentive /incentive/<incentiveId> DELETE 删除单个优惠

这里选择 JSON 表示数据格式,这是因为我们将会使用富 Internet 应用程序(RIA)作为系统的前端。很多基于 Ajax 的工具箱都能很好地理解 JSON,原因是 JavaScript 是浏览器内面向 Ajax 的主要编程语言。有关 JSON 的更多信息,请参看 参考资料

系统需求

要运行本文中的示例,需要如下内容:

  • Eclipse 3.2 或更高版本。本文中使用的是 Eclipse 3.3 版。
  • 针对 Eclipse 的 Project Zero Java 和 Groovy 插件。

    本示例使用 Zero 的 M1 发布版构建。请确保将 Eclipse Update Site 设为 http://www.projectzero.org/update/zero.eclipse.M1

    本练习不需要 PHP 插件

  • 本文附带的 可下载的 zip 文件。将其解压缩到 C: 盘。
  • Derby 网络数据库。
  • 针对 Eclipse 的 Derby 插件
  • Firefox 浏览器。

    虽然也可以使用 IE 进行本练习,但我们使用了 Firefox 插件来测试 POST/PUT/DELETE 请求。如果想要使用 IE,您需要在浏览器中测试 GET 请求,然后再使用 UI 客户机测试服务,该客户机在本文稍后介绍。

  • 针对 Firefox 的 POSTER 插件

其他有用的工具还有:





回页首


用 Project Zero 构建第一个 RESTful 服务

大致介绍了 RESTful 映射之后,现在就可以着手构建该示例了。

  1. 首先,创建一个 Zero 项目,如下所示。
    1. 从 Eclipse 主菜单,选择 New > Project,如图 6 所示。

      图 6. 创建新项目
      图 6. 创建新项目

    2. 从 Project Wizard,选择 Zero > Project Zero Application。单击 Next,如图 7 所示。

      图 7. Project Zero 应用程序
      图 7. Project Zero

    3. 将项目命名为 RebateIncentiveServices,并单击 Finish。

      图 8. RebateIncentiveServices
      图 8. RebateIncentiveServices

  2. 下一步要做的是检查 Project Zero 目录结构和工具。
    1. 图 9 给出了 Zero 项目中的几个重要目录的概览。突出显示的文件夹是能在其中编写代码的几个地方。大多数这类目录都位于项目的 app 文件夹之下。在 Java 透视图的 Explorer 视图下,这些目录显示为 Java 源文件夹。

      图 9. 应用程序目录结构
      图 9. 应用程序目录结构

      Java
      Java 文件夹是提供可供应用程序使用的 Java 代码的地方。可以编写基于 Java 的处理程序并将这些程序放在这里。
      Public
      public 文件夹是放置您希望服务于浏览器的所有工件的地方。示例中包含了 HTML 文件、JavaScript 文件、Groovy 文件和可在浏览器执行的模板或 PHP 脚本。
      Resources
      /app/resources 文件夹包含为响应 RESTful 调用而被调用的脚本(Groovy 或 PHP)。它们对应于各种 HTTP 方法。此目录中的脚本会自动作为处理程序注册。在本例中将用到此目录。
      Errors
      /app/errors 文件夹包含脚本模板文件,比如可基于错误自动呈现的 Groovy 模板。 此文件的命名模式定义了所呈现的是哪个响应。例如,error5xx.gt 就是对应于服务器 500 错误而被调用的 Groovy 模板。
      Scripts
      /app/scripts 目录让您可以定义私有的 Groovy 脚本或类(与 Java 目录很像)。在这里,可以定义可由公共或资源 Groovy 脚本调用的 GroovyBeans 或方法。这种方式让您可以在构建应用程序时遵循 model-view-controller (MVC)模式。
      Views
      在 /app/views 目录中放置可对应于某些事件而呈现的模板文件。与 public 目录中的脚本(基于直接的 URL 访问而运行)不同,这些视图可对应于另一个脚本而执行。控制器脚本可访问某些后端逻辑,然后再将控制权转给视图脚本以加以呈现。

      Project Zero Developer’s Guide(请参见 参考资料)中可以获得有关应用程序目录结构的更多信息。

    2. 图 10 中突出显示了 Config 目录,它包含 Zero 应用程序所需的一些重要文件。

      图 10. Config 目录
      图 10. config 目录

      Ivy.xml
      Zero 平台使用 Ivy 框架 维护依赖项。Zero 应用程序被打成多个包。利用 ivy.xml 可以定义所依赖于的是哪些包。Zero 包存储于 Ivy 存储库,Ivy 运行时则会分解这些包。

      Ivy 是管理(记录、跟踪、分解和报告)项目依赖项的工具。其特征是:

      • 灵活性和可配置性

        Ivy 基本上是过程不可知的且不会受约于任何方式方法或结构。它提供了所需的灵活性和可配置性,可适应很多依赖项管理和构建过程。

      • 与 Apache Ant 的紧密集成

        除了可作为单独的工具使用外,Ivy 还可以很好地与 Apache Ant 协作,提供了几个功能强大的 Ant 任务,从依赖项分解到依赖性报告和发布都会涵盖。

      更多有关 Ivy 的信息,请参阅 Apache incubatorProject Zero Developer's Guide 的 Dependency and Packaging 节。

      logging.properties
      logging.properties 文件是配置属性的地方。Zero 使用 Java Development Kit (JDK) 进行日志记录(有关 Zero logging 的更多信息,请参看 Project Zero Developer's Guide 的 Logging 一节)。
      Zero.config
      zero.config 文件定义了 Zero 应用程序中的主要配置文件。在这里可以配置工件,比如处理程序、安全性、数据资源和所需要的其他任何配置。可以有不止一个 zero.config 文件。在本例中将使用 zero.config 定义某些数据库配置。由于本例使用的是 /app/resources 文件夹,所以无需显式地配置处理程序,但要依赖于 Zero 的预定义的处理程序进行 REST 调用。(有关配置的更多信息,请参阅 Project Zero Developer's Guide 中的 Configuration 一节。)

      图 11 突出显示了 Eclipse 工具的一些方面。Zero 插件提供了可帮助管理依赖项的工具。借助它就能管理 Ivy 框架可用来下载和分解 Zero 应用程序依赖项的本地和远端存储库。分解依赖项就意味着向 Zero 应用程序中添加包。



      图 11. 依赖项管理
      图 11. 依赖项管理

      Manage repositores
      提供了查看本地存储库和从远端存储库下载包的工具,如图 12 所示。


      图 12. 管理存储库
      图 12. 管理存储库

      Resolve 图标
      resolve 图标会在工作区分解来自本地和配置了的远端存储库的依赖项。
      Resolve 菜单项
      resolve 菜单项会分解来自本地存储库的依赖项。
  3. 为了构建 RESTful 服务,需要在数据库中创建一个表。此外,为本例还提供了一个动态链接库(DLL)。在这个练习中使用的是 Derby 的网络版(还记得吧,我们也已经安装了针对 Eclipse 的 Derby 插件)。
    1. 右键单击 RebateIncentiveServices 并选择 Import,如图 13 所示。

      图 13. 导入工件
      图 13. 导入工件



    2. 图 14. 文件系统
      图 14.  文件系统

    3. 若已经将 示例代码 zip 文件解压缩到 C 盘中,C:\ProjectZeroArticleSeries\Part1 就应该已经存在于 From directory 字段。Into folder 字段应该为 RebateIncentiveServices。选中 dbscripts 文件夹,如图 15 所示,然后单击 Finish

      图 15. DB 脚本
      图 15. DB 脚本

    4. 在项目中应该能够看到一个文件夹,名为 dbscripts。在这个文件夹中,打开 Incentives.sql。此脚本创建了优惠 表并在该表中插入了两条记录。

      图 16. Incentives.sql
      图 16. Incentives.sql

      此脚本如清单 1 所示。



      清单 1. 数据库脚本
                                  
      CONNECT 'jdbc:derby://localhost:1527/INCENTIVEDB;create=true;';
      
      drop table Incentive;
      
      create table Incentive (
          incentiveid INTEGER NOT NULL
       GENERATED ALWAYS 
      AS IDENTITY,
          name VARCHAR
      (256) 
      NOT NULL,
          description VARCHAR
      (256) 
      NOT NULL,
          providername VARCHAR
      (50) 
      NOT NULL,
          incentivetype VARCHAR(128),
          validfrom TIMESTAMP,
          validto TIMESTAMP,
          website VARCHAR(256)
      );
      
      alter table Incentive 
      ADD CONSTRAINT Incentive_PK 
      PRIMARY KEY (incentiveid);
      
      INSERT INTO Incentive (name,description, providername,incentivetype,validfrom,validto,website) 
      values (
          'Key Account Energy Efficiency Grant Program',
          'Alameda P&T offers its commercial customers grants of up to $25,000
              for energy efficient building design and equipment in new commercial
              construction, or other approved unique energy efficient projects.',
          'Alameda Power and Telecom','Energy',
          TIMESTAMP('2007-06-22 00:00:00.000'),
          TIMESTAMP('2007-10-30 00:00:00.000'),
          'http://www.alamedapt.com/electricity/com_savings.html' );
      
      INSERT INTO Incentive (name,description, providername,incentivetype,validfrom,validto,website) 
      values (
          'Power Management for Personal Computer (PC) Networks',
          'Avista Utilities Power Management for Personal Computer (PC) 
              Networks offers incentives to commercial customers.',
          'Avista Utilities','Energy',
          TIMESTAMP('2007-09-01 00:00:00.000'),
          TIMESTAMP('2008-06-30 00:00:00.000'),
          'http://www.avistautilities.com/saving/conservation/power_management.asp'  );
       

    5. 若已经安装了针对 Eclipse 的 Apache Derby 插件,右键单击此项目,如图 17 所示,并选择 Apache > Add Apache Derby nature。

      此外,也可以使用命令行启动 Derby 并手动执行 ij



      图 17. Apache Derby 插件


    6. 再次右键单击此项目并选择 Apache Derby > Start Derby Network Server,如图 18 所示。

      图 18. 启动 Derby 网络服务器
      图 18. 启动 Derby 网络服务器

    7. 检查控制台以确保数据库启动并正在运行。

      图 19. 运行 Derby 数据库
      图 19. 运行 Derby 数据库

    8. 右键单击 Incentive.sql 脚本并选择 Apache Derby > Run sql script running 'ij'。(这是运行 Derby 数据库脚本的命令行工具。)

      图 20. 使用 ij 运行 SQL 脚本
      图 20. 使用 ij 运行 SQL 脚本

    9. 检查控制台以确保表已创建且记录已经插入。

      图 21. 数据库脚本结果
      图 21. 数据库脚本结果

  4. 之前,您曾经见过 ivy.xml 文件。在创建 Zero 项目时,就得到了核心的 Zero 包和 Webtools 包。核心包中包含必需的库以创建事件处理程序和 RESTful 服务及与全局上下文和安全性进行交互。Web Tools 项目则包含一组有用的工具,比如默认索引页面、请求检查器、错误发现工具等等。更多信息,可以在 Project Zero Developer's Guide 的 Developer Web tools 一节中找到(参见 参考资料)。

    在本步骤中,可以向应用程序中添加两个包。数据访问库和 Derby 客户机 Java Archive (JAR) 文件都需要与此数据库相联系。

    1. 打开 ivy.xml 文件,如图 22 所示。

      图 22. ivy.xml
      图 22. ivy.xml

    2. 本步骤操作会打开 ivy.xml 编辑器。单击 Add,如图 23 所示。

      图 23. 添加 Zero 包
      图 23. 添加 Zero 包

    3. 找到 zero:zero.data 包,如图 24 所示。确保过滤器设为 Latest minor version 并单击 OK。

      图 24. zero.data 包
      图 24. zero.data 包

    4. 重复之前的步骤并添加 org.apache.derby:derbyclient。 本步操作会添加连接到 Derby 所需的 Java Database Connectivity (JDBC) 客户机 JAR。

      图 25. Derby 客户机
      图 25. Derby 客户机

    5. 选择 Update 依赖项图标。该工具会读取 ivy.xml 并查找远端和本地存储库的最新版本(在本例的稍后部分,还会使用 Resolve 选项。)

      图 26. 更新依赖项
      图 26. 更新依赖项

    6. 单击 OK 以允许该工具查找远端版本。

      图 27. 选择要运行的依赖项
      图 27. 选择要运行的依赖项

  5. 应用程序具有了所需的库之后,就需要设置运行时的数据库配置。zero.config 文件使用的是一种 INI 风格的配置。config/zero.config 文件的内容被组织成多个相关键/值对 “节(stanza)”。

    这些节与指令相关,比如 “存储到全局上下文” 和 “包括另一个配置文件”。虽然 Project Zero 的目标之一就是最小化运行应用程序所需的配置,但某些配置还是必需的。

    1. 展开 Config 文件夹并打开 zero.config 文件,如图 28 所示。

      图 28. zero.config
      图 28. zero.config

    2. 图 29 显示的是需要添加的那个节。

      图 29. 配置数据库
      图 29. 配置数据库

      注意到这个节是以一个指令开始的。在本例中,我们指向了一个位置,在这个位置中我们衡量所要存储的键/值对。这个测试如清单 2 所示。也可以将其从 C:\ProjectZeroArticleSeries\Part1\dbconfig.txt 粘贴过来。



      清单 2. 数据库配置
                                  
      [/app/db/incentiveDB/config]
      class=org.apache.derby.jdbc.ClientDataSource
      serverName=localhost
      portNumber=1527
      databaseName=INCENTIVEDB
      connectionAttributes=create=true
      

      Project Zero 数据库在访问时会读取此配置。注意 图 29 中突出显示的 /app/db 后的 incentiveDB 名称。这也是此代码中使用的名称。稍后,在构建 RESTful 服务时还会看到。





回页首


创建 RESTful 服务以公开数据

至此,就可以构建 RESTful 服务来公开 incentive 数据了。在 Project Zero 中构建服务有多个方法。一个目标是将对自动公开 Groovy 脚本的支持应用到 RESTful 服务。

需要使用针对 Project Zero 的 Eclipse 插件。请记住,这些约定在 Eclipse 之外、使用 Zero 的命令行版本时也可用。您很可能会发现使用 Project Zero 可以快速创建基于 REST 的服务。

创建 Groovy 脚本

Groovy 脚本会匹配您希望调用 REST 服务所用的那个 URL 的名称。对于 /resources/incentive,所需的文件名为 incentive.groovy。

  1. 右键单击文件夹 /app/resources 并选择 New > File,如图 30 所示。

    图 30. 新建文件
    图 30. 新建文件

  2. 将文件命名为 incentive.groovy

    图 31. incentive.groovy
    图 31. incentive.groovy

  3. 如果系统提示要向项目中添加 Groovy 支持,单击 Yes

    图 32. Groovy 支持
    图 32. Groovy 支持

管理优惠

要管理优惠,就是要能够列出所有的优惠;获得各个优惠;创建、更新和删除优惠。第一个任务就是创建列出优惠的功能。客户机能够调用 /resources/incentive 以查看优惠列表。

  1. 打开刚刚创建的 incentive.groovy 文件。

    图 33. Incentive.groovy
    图 33. Incentive.groovy

  2. 如果直接加入脚本代码,就会创建对应于任一 HTTP 方法的脚本。但我们想要的是基于 HTTP 方法有不同的动作。可以定义几个特殊的能自动响应 HTTP 方法的处理程序方法。如下所示的表总结了这些回调。

    URI HTTP 方法 Groovy 方法 描述
    /incentive GET onList() 返回优惠列表
    /incentive POST onCreate() 创建新的优惠并返回资源位置
    /incentive PUT onPutCollection() 更新优惠集
    /incentive DELETE onDeleteCollection() 删除优惠集
    /incentive/<incentiveId> GET onRetrieve() 根据 ID 检索优惠
    /incentive/<incentiveId> PUT onUpdate() 更细单个优惠
    /incentive/<incentiveId> DELETE onDelete() 删除单个优惠
    /incentive/<incentiveId> POST onPostMember() 向所选定的位置发布单个成员


    在本文中,将要构建第 1、第 2、第 5、第 6 和第 7 个回调。

  3. 要构建和测试这个列表用例,需要添加清单 3 所示的代码片段。或者,也可以从 C:\ProjectZeroArticleSeries\Part1\list.txt 粘贴。

    清单 3. 优惠列表
                            
    import zero.data.groovy.Manager;
    
    def onList()
    {
    	def dm = Manager.create("incentiveDB");
    	request.json.output = dm.queryList("select * from incentive");
    	request.view="JSON";
    	render();
    }
    

    此段代码:

    • 使用了 Project Zero 的数据 API 的 Manager 对象。Manager 对象提供了一些很方便的方法来执行 SQL 查询。有两种版本可用:Java 版和 Groovy 版。虽然 Java 版在 Groovy 也可以使用,但 Groovy 版则更充分利用了 Groovy 的捷径,比如将 GStrings 作为输出传递、闭包的使用等等。当发出 create 时,我们会传递进在配置 /app/db/incentiveDB/config 中使用的字符串。

    • Manager 选择完整列表并将其赋值给 request.json.output。这看起来像是我们在将结果赋值给常规 Groovy 变量,但事实远远不止于此。还记得么,所有状态都在全局上下文中保持。Project Zero 提供了一个 Java API,用来从 GlobalContext 存储和检索数据。

      例如,代码

      String data = GlobalContext.get("/request/myData");
      

      会从全局上下文的 Request 区获得一个字符串。Project Zero groovy 脚本会进一步对全局上下文数据添加构建支持。它们已经为每个区(app、user、request 和 event)都预定义了 4 个变量。所以在 Groovy,如下代码:

      request.myData = "Hello"
      

      GlobalContext.put("/request/myData","Hello);
      

      是相同的。 利用 Groovy 这些捷径将减少所需编写的代码量。在本例中,我们会将列表存储进 /request/json/output 下的 request 区。更多有关全局上下文的信息,请参阅 Project Zero Developer's Guide参考资料)。

    GlobalContext 内存储结果之后,会用到另一个名为 ViewEngine 的对象。Project Zero 支持以不同的方式呈现结果上下文。可以显式地写输出流(直接呈现)。应用程序代码和事件处理程序可使用 OutputStream/request/outputstream)和 PrintWriter/request/writer)对象直接向响应条目中写。

    如下所示的是一个 Java 示例:

    // Content of a Java class
    PrintWriter writer = (PrintWriter) GlobalContext.get("/request/writer");
    writer.println("Hello World.");

    针对所有 Groovy 脚本(包括模板)的默认输出是响应编写器,这就让应用程序开发人员能够利用 println 方法直接向该响应写。有关呈现的更多信息,请参阅 Project Zero Developer's Guide 的 Rendering Results 一节。

    Project Zero 提供了呈现器库以处理共同响应。调用呈现器总的来说会涉及到如下步骤:

    1. 将全局上下文中 /request/view 的值设置为对应的呈现器。
    2. 将其他一些特定于呈现器的值设置到 GlobalContext 内。
    3. 调用
      zero.core.views.ViewEngine.render() 

      在 Groovy REST Handler 内,单独调用 render() 会导致调用 View Engine 呈现器。

    zero.core 包括 View、Error、XML 和 JSON 呈现器。也可以添加 Atom 包,其中包含了 Atom 呈现器。

    此示例使用了 JSON 处理程序。另一个可选项是使用 Groovy 模板。可以将 Groovy 模板存储于 /app/views 文件夹下并将 request.view 设为此模板的名称。

    正如您所见,这些约定极大地降低了所需编写的代码量。

通过启动应用程序进行测试

通过启动应用程序可以很容易地测试 onList

  1. 右键单击该项目并选择 Run as > Project Zero Application,如图 34 所示。

    图 34. 运行 Project Zero 应用程序
    图 34. 运行 Project Zero           应用程序

  2. 检查此控制台。应用程序应该已经启动且正在端口 8080 侦听。

    图 35. 在端口 8080 上运行的应用程序
    图 35. 在端口 8080 上运行的应用程序

  3. 启动浏览器并指向 http://localhost:8080,这应该会开启默认的 zero 页面。(这就是之前提到的 Web Tools 库中的工具之一。)将会看到默认的索引页面。如果您添加了自己的 index.html 文件,该文件就会优先于默认的文件。

    图 36. 默认主页
    图 36. 默认主页

  4. 通过选择 View > SideBar > Poster 启动 Firefox Poster 工具。

    图 37. 运行 Poster 插件
    图 37. 运行 Poster 插件

  5. 为此 URL 输入 http://localhost:8080resources/incentive,并单击 GET

    图 38. GET 优惠
    图 38. GET 优惠

  6. 带有响应的有效负载的 Response 窗口应该会弹出,如下所示。 有效负载的格式应该是一个 JSON 数组,其中每一项都是一个 JSON 对象。

    图 39. JSON 结果
    图 39. JSON 结果

  7. 也可以选择测试 XML 呈现的效果。将视图类型更改为 XML,如清单 4 所示。

    清单 4. XML 结果
                            
    def onList()
    {
    		def dm = Manager.create("incentiveDB");
    request.xml.output = 
        dm.queryList("select * from incentive");
    		request.view="XML";
    		render();
    }
                        

  8. 从 Poster 插件再次单击 GET 以查看 XML 中的结果,如图 40 所示。

    图 40. XML 结果
    图 40. XML 结果

    请确保将代码更改回 JSON。

访问服务

接下来,将会创建 onRetrieve 方法以通过传递其标识符来访问特定的 RESTful 服务。

  1. 在 incentive.groovy 文件内,输入 onRetrieve 方法和如图 41 或清单 5 所示的代码。也可以将其从 C:\ProjectZeroArticleSeries\Part1\retrieve.txt 粘贴过来。

    图 41. 检索优惠
    图 41. 检索优惠



    清单 5. 检索单一优惠
                            
    def onRetrieve()
    {
    def dm = Manager.create("incentiveDB");
    request.json.output = dm.queryFirst(
    "select * from incentive where incentiveid = 
     ${request.params.incentiveId[0].get()}");
    	request.view="JSON";
    	render();
    } 
    

    与利用 onList 方法所做的类似,现在将会创建 Manager 的一个实例并执行查询。本例有如下几个要点:

    • 该方法通过执行如下 URI 得以执行: /resources/incentive/1。1 是所需优惠的标识符。将此传递进 where 子句,需要从此 URI 对其进行访问。Zero 会用路径信息自动填充 GlobalContext。例如,request.params.incentiveId[0] 会返回 1。如下的表给出了如何访问参数的几个例子。

      URI 参数
      /incentive/1 request.params.incentiveId[0] == 1
      /incentive/1/provider?location=NJ request.params.location[0] == NJ


    • 查询内用到了对 Groovy GStrings 的内置支持。GString 让您可以使用表达式语言在字符串字母内引用变量,所以如下所示的查询字符串 literal:
      select * from incentive where incentiveid = ${request.params.incentiveId[0].get()} 
      

      就能够直接访问 GlobalContext。此外,数据访问库中也有一些 API 可以让您能够传递进 Plain Old Java Objects (POJO) 并使用 :property 风格的输入,此外还有对 ? 类型参数的支持。更多信息,请参阅 Project Zero Developer's Guide参考资料)的 Project Zero and the Data Access 一节中的数据部分的 JavaDocs
  2. 保存文件。回到 Firefox Poster 工具,输入 URL http://localhost:8080/resources/incentive/1 并单击 GET,如图 42 所示。结果应该是一个 JSON 对象,只有单一一条记录。

    图 42. 单一优惠的 JSON 结果
    图 42. 单一优惠的 JSON 结果

向示例中添加

现在,可以添加 onCreate()onUpdate()onDelete() 方法。

  1. 将清单 6 中的代码输入到 incentive.groovy 或从 C:\ProjectZeroArticleSeries\Part1\updateMethods.txt 中粘贴过来。

    清单 6. 创建、更新和删除优惠
                            
    def onCreate()
    {
    	def dm = Manager.create("incentiveDB");
    	def incentive = zero.json.JsonType.fromData(request.input.get()).getJson();
    	def newKey = dm.insert("insert into incentive
     (name,description,providername,incentivetype,validfrom,validto,website)
     values(${incentive.name},${incentive.description},${incentive.providername},
    ${incentive.incentivetype},${incentive.validfrom},${incentive.validto},
    ${incentive.website})",['incentiveid']);
        
    	locationUri = zero.core.utils.URIUtils.getAbsoluteUri(request.path) + '/' + newKey
        request.headers.out.Location = locationUri
        request.status = HttpURLConnection.HTTP_NO_CONTENT
    }
    
    def onUpdate()
    {
    	def dm = Manager.create("incentiveDB");
    	def incentive = zero.json.JsonType.fromData(request.input.get()).getJson();
    	dm.update("update incentive set name =
     ${incentive.name},description = $incentive.description ,providername =
     $incentive.providername,incentivetype=$incentive.incentivetype,validfrom=
    $incentive.validfrom,validto=$incentive.validto,
    website=$incentive.website where
     incentiveid=${request.params.incentiveId[0].get()}");
    	request.status = HttpURLConnection.HTTP_NO_CONTENT
    	
    } 
    
    def onDelete()
    {
    	def dm = Manager.create("incentiveDB");
    def newKey = dm.update("delete from incentive where incentiveid=
    ${request.params.incentiveId[0].get()}");
    	request.status = HttpURLConnection.HTTP_NO_CONTENT;
    }
    

    在这段代码中:
    • 数据访问 API 具有一些可用来执行更新的方法。 onDeleteonUpdate 使用的是 generic 更新方法,这就让您可以传递进任何的数据库 InsertUpdateDelete 语句。onCreate() 也使用了一种特殊定义的可返回主键的插入方法。这在由数据库生成主键的情况下非常有用。
    • onCreate 方法会设置新创建的资源的 URL。这是 REST 模式所要求的。
    • 所有更新都会将返回类型设置为 no Content
    • 对特定的 zero.json.JsonType.fromData 的使用会将消息主体转变成 JSON 对象的 Java 表示。
  2. 使用 Poster,输入 http://localhost:8080/resources/incentive。 在 Content to Send 内,添加清单 7 中的 JSON 对象(也可以从 C:\ProjectZeroArticleSeries\Part1\insertUpdatePayload.txt 粘贴过来)。

    清单 7. 创建 JSON 有效负载
                            
    {"providername":"Hawaii Gas Company ",
    "website":"http://www.hawaiigas.com/rebate/commercial.html",
    "description":"Existing Commercial Utility Gas customers of The Gas Company 
    may apply for a rebate for new gas equipment..",
    "name":"Commercial Switch to Gas Program",
    "incentivetype":"Energy","validto":"2007-09-01 13:01:23.123456",
    "validfrom":"2008-06-30 13:01:23.123456"}
    

  3. 选择 POST,如图 43 所示。

    图 43. POST 优惠
    图 43. POST 优惠

  4. 响应应该是 204,位置则应设置为新的资源:http://localhost:8080/resources/incentive/3,如图 44 所示。

    图 44. POST 结果
    图 44. POST 结果

  5. 选择此响应并回到 Poster 工具。输入新 URL http://localhost:8080/resources/incentive/3,然后单击 GET 以验证此项已创建。

    图 45. 用 GET 创建的优惠
    图 45. 用 GET 创建的优惠

  6. 所得到的响应应该与所创建的记录匹配。

    图 46. GET 新优惠
    图 46. GET 新优惠

  7. 从 C:\ProjectZeroArticleSeries\Part1\insertUpdatePayload.txt 重新粘贴此有效负载。
  8. incentiveType 更改为 Gas,如清单 8 中的 JSON 对象所示。

    清单 8. 更新 JSON 有效负载
                            
    {"providername":"Hawaii Gas Company ",
    "website":"http:\/\/www.hawaiigas.com\/rebate\/commercial.html",
    "description":"Existing Commercial Utility Gas customers of The Gas
     Company may apply for a rebate for new gas equipment..",
    "name":"Commercial Switch to Gas Program",
    "incentivetype":"Gas",
    "validto":"2007-09-01 13:01:23.123456",
    "validfrom":"2007-09-01 13:01:23.123456"}
    

  9. 输入资源的 URL http://localhost:8080/resources/incentive/3 和有效负载,然后单击 PUT

    图 47. PUT 优惠
    图 47. PUT 优惠

  10. 应该得到一个 204 响应。

    图 48. 204 结果
    图 48. 204 结果

  11. 在此资源上发起另一个 GET 以确保更新成功。

    图 49. 删除优惠
    图 49. 删除优惠

  12. 最后,在 URL http://localhost:8080/resources/incentive/3 上,单击 Delete。应该得到一个 204 响应,如下所示。

    图 50. Delete 的 204 结果
    图 50. Delete 的 204 结果

  13. 发起一个 GET。得到的结果应该为空。(本系列后续文章将会将错误处理添加到代码中。)




回页首


导入一个示例 RIA 客户机

RESTful 服务已就绪,现在就可以导入一个能使用 RESTful 服务的示例 RIA。本文无意过多介绍该客户机,但会对此导入的客户机以及 REST 为何是 mashup 应用程序的基础之一做简要说明。

本系列的后续文章会论及 Zero 中的 UI 支持。
目前,Project Zero 包含 Dojo Toolkit 4.3。对于本例,我们会使用基于 Dojo 的应用程序。您将使用基本的、带某些导入的帮助程序的 Dojo 支持。

要向此项目中添加 Dojo 包:

  1. 正如之前所做的,打开 Config 文件夹下的 ivy.xml 并向应用程序中添加 dojo:dojo(0.4.3)

    图 51. 添加 Dojo 包
    图 51. 添加 Dojo 包

  2. 右键单击此项目并选择 Zero Tools Resolve。这会让 Dojo 从本地存储库中分解出来。

    也可以使用 Update Dependency 图标来从远端存储库进行加载,正如之前所做的那样。



    图 52. 分解 Dojo
    图 52. 分解 Dojo

  3. 从控制台视图终止此应用程序,方法是单击 Stop,如图 53 所示。与代码更改不同,当添加新库或更新 zero.config 时,必须要重启此应用程序。

    图 53. 终止应用程序
    图 53. 终止应用程序

在此处,可以导入或检查此代码:

  1. 右键单击项目的 public 文件夹并选择 Import

    图 54. 导入 UI 代码
    图 54. 导入 UI 代码

  2. 在 From 目录,输入 C:\ProjectZeroArticleSeries\Part1\webapp。 选中 webApp 框并确保 Into 文件夹为 public,如图 55 所示。

    图 55. Import 向导
    图 55. Import 向导

让我们简要地查看一下此代码:

  1. 打开 index.html,如图 56 所示。

    图 56. 查看索引
    图 56. 查看索引

  2. 我们已经将此优惠客户机作为小部件构建完毕。将 RESTful 客户机作为 Dojo 小部件构建对于提供模块式的客户机至关重要,这类客户机可以在一定的基础上糅合在一起以便集成。图 57 表明了优惠 小部件为何是页面上的单一声明。

    此示例还有另外一部分 JavaScript 以模拟另外一个小部件。该小部件会订阅优惠小部件并显示该 RESTful 事件。



    图 57. 两个小部件
    图 57. 两个小部件

  3. 在 zero.incentive.widget 文件夹下,会找到构建 Dojo 小部件所需的代码。一个 Dojo 小部件主要由 HTML 模板、CSS 文件和 JavaScript 函数构成。就如何构建 Dojo 小部件完全可以单独撰写一篇文章。更多有关 Dojo 工具箱和小部件的信息,请参见 参考资料。图 58 突出显示了这些小部件文件。

    图 58. Dojo 小部件代码
    图 58. Dojo 小部件代码

  4. 清单 9 突出显示了 IncentiveWidget.js 文件的一部分,尤其突出了 Dojo 小部件是如何调用 RESTful 服务的。此处提供了示例 JSONRestService JavaScript 类,稍候会详细加以介绍。此示例展示了这个小部件是如何调用 listData 方法以调用该服务的。另外一个重要的方面是 Dojo 发布和订阅函数的使用以便发出有关 RESTful 服务何时被调用的事件。这也是相邻的小部件如何能发觉数据何时被访问的原因。

    请务必记住,有些数据可能本身就是安全的。来自不可靠资源的纯 JavaScript mashup 有可能会十分危险。本系列后续文章会详细探究更多的安全模式。



    清单 9. JSON rest 调用
                            
    postCreate: function(){
    		
    		dojo.debug("postCreate, setting subscriptions");
    		
    		//Load Widget
    		dojo.event.topic.subscribe(this.inputTopic, this, this.listIncentives);
    		
    		//Get Incentives Events
    		dojo.event.topic.subscribe(this.getAllTopic, this, this.bindIncentives);
    
    listIncentives:function (){
    		zero.rest.JsonRestService.listData(this.restUri,this.getAllTopic,this.errorListTopic);
    	},
    	bindIncentives:function(data){
    			dojo.debug("Binding to -> " + this.incentiveTable);
    			for(i = 0; i < data.length ; i++)
    			{
    				data[i].validto = dojo.date.fromIso8601 (data[i].validto);
    				data[i].validfrom = dojo.date.fromIso8601 (data[i].validfrom);
    				data[i].website = '<a href=\"' +
      data[i].website + '\" target="_blank">' + data[i].website + '</a>';
    			}
    			this.incentiveTable.store.setData(data);
    			this.showIncentiveFormButton.show();
    			this.editIncentiveFormButton.show();
    			this.deleteIncentiveButton.show();	
    	}
    

  5. 打开 zero.rest 文件夹下的 JsonRestService.js 文件,如图 59 所示。 在代码中,请注意针对我们所有 RESTful 事件的 callback 方法。我们使用 dojo.io.bind() 方法和 dojo publish/subscribe 来在事件完成时进行通知。该代码也可在 Developer's Guide 中找到。

    图 59. JsonRestService.js
    图 59. JsonRestService.js

现在,若要运行此客户应用程序:

  1. 重启此应用程序。方法是使用 Eclipse 运行箭头,如下所示。

    图 60. 重启应用程序
    图 60. 重启应用程序

  2. 启动浏览器并指向 http://localhost:8080。注意到调用的是我们的索引页而非 Web Tools 索引页。incentive 小部件调用了列表数据,而相邻的小部件则显示此事件。

    图 61. 所加载的应用程序
    图 61. 所加载的应用程序

  3. 通过单击 Add New Incentive 添加新的优惠。

    图 62. 添加新的优惠
    图 62. 添加新的优惠

  4. 输入一些数据(可以参照 JSON 有效负载,也可以查看图 63)。

    图 63. Add new incentive 表单
    图 63. Add new incentive 表单

  5. 单击 Submit。新创建的资源应该已经被添加到此表。

    图 64. 更新优惠
    图 64. 更新优惠

  6. 突出显示新创建的资源并单击 Edit Selected Incentive

    选择此资源时,会执行 onRetrieve 方法以获得单个资源。

  7. 更新所需字段并单击 Submit

    在 Dojo 的 4.3 版,下拉数据小部件以显示表单数据的功能有一个小 bug。请访问 Dojo 站点以获得相应的更新,或直接填充这个小部件。



    图 65. Dojo 4.3 中的 bug
    图 65. Dojo 4.3 中的 bug

  8. 最后,突出显示此资源并单击 Delete Selected Incentive

    图 66. 删除优惠
    图 66. 删除优惠

  9. 此列表应该已经更新,如图 67 所示。

    图 67.优惠删除后
    图 67. 删除后

  10. 查看相邻的小部件,它会显示 REST 事件和有效负载。 。

    图 68. Mashed-up 向导
    图 68. Mashed-up 向导

祝贺您!您已经用 Project Zero 构建了自己的第一个 RESTful 服务!

结束语

分享本篇文章……

digg 将本文提交到 Digg
del.icio.us 发布到 del.icio.us
Slashdot 提交到 Slashdot!

本文向您简单介绍了 Project Zero,一种用来构建面向 Web 的应用程序或 Web 扩展的 SOA 的新平台。您了解了 Project Zero 的体系结构以及如何构建 RESTful 服务以便有助于 Web 2.0 开发。本文给出的这个相对简单的示例使用的是一种经常执行的模式。

请继续关注本系列的第 2 部分,该部分将向您展示如何为更复杂的数据构建 RESTful 服务,并将介绍该平台其他方面的内容以帮助您组装、保护和交付功能完善的经过 Web 扩展的 SOA。






回页首


下载

描述名字大小下载方法
示例代码i-zero1code.zip13KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术

讨论


作者简介

Photo: Roland Barcia

Roland Barcia 是一名 IBM 资深技术人员和 IBM Software Services for WebSphere 的首席 Web 2.0 架构师。他也是 IBM WebSphere: Deployment and Advanced Configuration 和即将发表的 Persistence within the Enterprise 的合著者之一。


Steve Ims

Steve Ims 是一名 IBM 资深技术人员和 Project Zero 运行时 “core” 的领头人。在 Project Zero 论坛 上可以找到 Steve。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

将您的建议发给我们或者通过参加讨论与其他人分享您的想法.




回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款