用 Geronimo 和 REST 构建服务器端 mashup

来源:developerWorks 中国 作者:J. Jeffrey Hanson
  
探索使用 Apache Geronimo、基于 REST 的协议和来自各种来源的数据构建 mashup 应用程序时可以使用的技巧与技术。该 mashup 将组合 Google Maps 和 Twitter tweet 中的数据以在 Twitter 用户更新其 Twitter 状态时精确定位他们的位置。

mashup 一词最初被定义为混合两种或多种乐曲从而创作出全新音乐作品的技术。在软件工程中,mashup 指组合数据、UI 组件和流程以创建新 Web 应用程序和站点的技术和模式。

在站点开发人员之间,Mashup 十分流行,因为它可以轻松地组合数据与内容。这种特性源于动态的且具有丰富语义的 Web 技术的普遍使用 — 这些技术包括 XML、JavaScript Serialized Object Notation(JSON)、资源描述框架(Resource Description Framework,RDF)、动态 JavaScript 和 Ajax。这些技术和其他技术为开发创造性内容的开发人员提供了无限可能性。

常用缩写词
  • Ajax:Asynchronous JavaScript + XML
  • API:应用程序编程接口(Application program interface)
  • CSS:层叠样式表(Cascading Style Sheet)
  • DOM:文档对象模型(Document Object Model)
  • HTML:超文本标记语言(Hypertext Markup Language)
  • HTTP:超文本传输协议(Hypertext Transfer Protocol)
  • UI:用户界面(User interface)
  • XML:可扩展标记语言(Extensible Markup Language)
  • XSD:XML 模式文档(XML Schema Document)

通常,可以通过组合 UI 组件、服务/过程以及数据创建 mashup。Mashable UI 组件包括动态 JavaScript、HTML 代码片段、RSS 摘要和 Web 服务 API 调用的结果。Mashup 使用数据转换、动态 JavaScript、DOM 处理及其他技术,混合来自一个或多个站点的松散耦合的 UI 组件、过程、或数据。当前的典型 mashup 包括将 Google Maps 中的地图与位置数据结合,例如犯罪统计数字和给定地区的房地产价格。

本文将讨论如何将 Twitter 和 Google Maps 提供的 API 与 Ajax 和 Java™ 语言代码结合使用,构建可以在 Apache Geronimo 环境中部署和执行的 mashup。

了解 Geronimo

Geronimo 是可用于构建企业服务和应用程序的完全兼容的 Java Platform, Enterprise Edition(Java EE)平台。Geronimo 是围绕使用反转控制(Inversion of Control,IoC)技术高度解耦服务和组件的架构设计的。这种高度解耦造就了真正的模块化且可配置的部署和执行环境。Geronimo 提倡使用 Java Management Extension(JMX)和一个类似的特定于 Geronimo 的 MBean 风格的框架,该框架使 Geronimo 成为功能强大的、可管理的企业平台。

您将使用由轻量级内核引擎组装和管理的可自定义模块集创建 Geronimo 运行时环境。内核引擎是 Geronimo 模块,该模块是由类、其他模块和可序列化配置组成的集合。在启动 Geronimo 运行时实例后,Geronimo 内核将载入、装配并组织模块。装配的模块将决定 Geronimo 部署和执行环境的功能。Geronimo 运行时实例中的所有服务都被部署为模块。

Geronimo 模块在 XML 文档中称为部署计划。最终的 Geronimo 部署计划包含原始部署计划、Maven project.properties 文件和 Maven Project Object Model(POM)文件的组合。图 1 显示了如何处理这些工件以创建最终部署计划。


图 1. 处理 Geronimo 部署计划
处理 Geronimo 部署计划

部署计划的内容是由 XSD 文件控制的。部署计划将定义模块 ID、模块的环境属性、模块的依赖关系、模块提供的服务及模块的 Geronimo Beans(GBeans)等等。清单 1 演示了一个简单的 Geronimo 部署计划。


清单 1. 示例 Geronimo 部署计划
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="http://geronimo.apache.org/xml/ns/deployment-1.1">
  <environment>
    <moduleId>
      <groupId>geronimo</groupId>
      <artifactId>example</artifactId>
      <version>1.0.0</version>
      <type>car</type>
    </moduleId>  
    <dependencies>
      <dependency>
        <groupId>geronimo</groupId>
        <artifactId>j2ee-server</artifactId>
        <type>car</type>
      </dependency>
    </dependencies>
    <hidden-classes/>
    <non-overridable-classes/>
  </environment>
  
  <gbean name="ExampleService" class="com.example.myservices.MyServiceGBean">
    <attribute name="prop1">12345</attribute>
    <attribute name="prop2">This is a value for a property</attribute>
  </gbean>
</module>

在构建过程编译清单 1 中所示的部署计划时,将用一个惟一名称创建配置归档(Configuration Archive,CAR)文件。为清单 1 中的配置生成的惟一名称是 geronimo/example-1.0.0/car。

Geronimo CAR 文件

Geronimo CAR 文件是 Java Archive(JAR)文件,其中包含一系列部署计划和任何其他额外资源。部署计划系列包含在 CAR 的 META-INF 目录下名为 config.ser 的文件中。CAR 文件都是在构建过程中由 Geronimo 的 Maven 封装插件创建的。

Geronimo 存储库

Geronimo 存储库 是一张工件注册表,通常表现为文件系统中的文件夹层次结构。Geronimo 的二进制版本将提供名为 repository 的文件夹,其中包含组成核心 Geronimo 平台的模块的所有依赖关系。

在 Geronimo 中,工件是任意的组件,例如存储于 Geronimo 存储库中的 Web Archive(WAR)文件、JAR 文件或者 CAR 文件。工件存储于命令行部署实用程序或 Geronimo 发行版附带的 Geronimo Web 控制台中。

下载并安装 Geronimo

下载 Geronimo 平台(请参阅 参考资料),然后将文件解压缩到名为 GERONIMO_HOME 的目录中。在安装了 Geronimo 之后,打开命令行控制台并切换到 GERONIMO_HOME/bin 目录,然后运行 startup.sh。执行此命令将在一个新控制台窗口中启动服务器。在服务器启动后,您应当会看到类似图 2 中所示的屏幕。


图 2. Geronimo 启动控制台
Geronimo 启动控制台

在建立 Geronimo 运行时环境时,启动控制台将显示装入和启动的模块、连接器和应用程序。您可以使用同样在 Geronimo 安装的 GERONIMO_HOME\bin\ 目录中找到的 shutdown.sh 脚本来停止 Geronimo 运行时。





了解 REST

具象状态传输(Representational State Transfer,REST)是用于访问和更新网络资源的软件架构模式和调用样式。Roy Thomas Fielding 所撰写的论文(请参阅 参考资料)中定义的 REST 一词,通常用于描述使用默认的 HTTP 方法集(GET、POST、DELETE 和 PUT)所定义的标准请求和响应通过 HTTP 传输数据。

REST 架构样式强烈建议使用统一资源标识符(URI)来定位和访问资源的表示 — 称为资源的具象状态。使用有限的创建/读取/更新/删除(CRUD)动作集,并且除 HTTP 之类标准协议提供的内容之外只需极少的额外开销,就可以访问和修改具象状态。基于 REST 的约定应当保持无状态,从而使其成为诸如 RSS、RDF、Web Ontology Language(OWL)和 Atom 之类语义数据格式的主要促进者。





了解 Google Maps

Google Maps API 提供了若干种处理地图或者创建和检索可以嵌入到 Web 页面中的地图的方法。本文旨在讨论 Google Maps API 提供的基于给定位置字符串检索地图的功能。

Google Maps 的公共接口

Google Maps 提供了使用标准 HTTP 请求就可以访问的 API。每个 API 请求要求事先取得 API 密钥。Google Maps API 支持 KML 和 GeoRSS 数据格式的 HTTP 响应。

使用 Google Maps API

要开始使用 Google Maps API,必须先在 Google Code 站点中注册以获得 API 密钥。收到的密钥将对于单个 Web 域有效。

用 Google Maps 检索地图

Google Maps 将公开能够检索给定位置的地理编码转换的 API。要检索用户的位置信息和其他信息,只需使用 http://maps.google.com/maps/geo?q=Salt+Lake+City%2C+UT&output=xml&key={apikey} 发出标准 HTTP GET 请求。必须用 Google 分配给您的 API 密钥替换 {apikey}。下例显示了如何通过命令行用 curl 使用 API。

C:\>curl -G http://maps.google.com/maps/geo?q=Salt%20Lake%20City
   %2C%20UT%26output=xml%26key=ABQIAAAA7kHuyDenRy7D_
   kXwDkUfhBQCabR54RQscLxLTjQlrb8wKm07EBRSANMlyMuVIxp6jUQazrN52Pzp3w

响应将返回基于 XML 的数据,称为包含关于给定位置信息的 KML。KML 是由 Open Geospatial Consortium Inc.(OGC)维护的开放标准。下面显示了盐湖城的 KML 数据示例。


清单 2. KML 文档示例
	
<kml xmlns="http://earth.google.com/kml/2.0">
  <Response>
    <name>Salt Lake City, UT</name>
    <Status>
      <code>200</code>
      <request>geocode</request>
    </Status>
    <Placemark id="p1">
      ...
      <Point>
        <coordinates>-111.888189,40.771592,0</coordinates>
      </Point>
    </Placemark>
  </Response>
</kml>

在此清单中,<coordinates> 元素包含分别表示盐湖城的经度、纬度和海拔的值。





了解 Twitter

Twitter 提供了允许用户使用标准 HTTP 请求、即时消息和文本消息相互联系的服务。Twitter 服务的前提是让用户发送短消息,称为 tweets,用于描述用户正在做什么。tweets 将被分发到 Twitter 服务器并转发给发送 tweets 的用户的 “跟随者(follower)”。

Twitter 的公共接口

Twitter 提供了使用标准 HTTP 请求可以访问的一组 API。Twitter API 围绕 REST 架构松散构建。每个 Twitter API 目前都支持 XML、JSON、RSS 和 Atom 数据格式的 HTTP 响应。下面的例子通过命令行结合使用 Twitter API 与 curl 来检索 RSS 格式的 Twitter 公共历史:C:\>curl -G http://twitter.com/statuses/public_timeline.rss。

其他 API 可用于执行诸如检索朋友的历史、发布内容状态更新、更新配置文件位置和检索用户的配置文件数据之类的任务。

检索 Twitter 用户的位置

Twitter 将公开能够检索用户配置文件的 location 属性的 API。要检索用户的位置和其他信息,只需使用 http://twitter.com/users/show/{targetUserID}.xml 创建一个标准的 HTTP GET 请求。必须用要检索其信息的用户的 Twitter ID 替换 {targetUserID}。下面显示了通过命令行用 curl 使用此 API 的示例:

C:\>curl -G http://twitter.com/users/show/jhanson583.xml

响应将返回 XML 格式的数据,其中包含用户信息,包括在用户的配置文件中配置的用户位置。下面显示了用户 jhanson583 的 XML 数据示例。


清单 3. Twitter 用户配置文件的 XML 数据示例
<?xml version="1.0" encoding="UTF-8"?>
<user>
  <id>10852552</id>
  <name>Jeff Hanson</name>
  <screen_name>jhanson583</screen_name>
  <location>Salt Lake City, UT</location>
   …
</user>

在这里,<location> 元素将包含输入到用户 jhanson583 的配置文件中的数据。注意,由于可以键入任意格式的字符串,因此不能够保证可以有效地应用于 Google Maps 查询中。因此,只能检索用户配置文件中在 <location> 元素中包含有效位置数据的地图数据。以上配置文件中所示的位置数据可以有效地应用于 Google Maps 查询中。

用 Java 代码检索 Twitter 用户的位置

检索用 Java 语言编写的用户配置信息只需发送 HTTP GET 请求并解析响应。清单 4 中的代码片段提供了发送通用 HTTP GET 请求的 Java 代码。


清单 4. 用 Java 语言编写的 HTTP 请求示例
  public static String sendHTTPRequest(String url)
    throws Exception
  {
    String res = null;

    try
    {
      HttpURLConnection con = null;
      InputStream inStream = null;
      OutputStream outputStream = null;

      try
      {
        con = (HttpURLConnection) new URL(url).openConnection();
        con.setDoInput(true);
        con.setRequestMethod("GET");
        inStream = con.getInputStream();
        res = parseHTTPResponse(inStream);
      }
      finally
      {
        try
        {
          inStream.close();
          outputStream.close();
          con.disconnect();
        }
        catch (Exception e)
        {
        }
      }
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }

    return res;
  }

  public static String parseHTTPResponse(InputStream inStream)
    throws IOException
  {
    BufferedReader br = null;
    br = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
    StringBuffer buf = new StringBuffer();
    String line;
    while (null != (line = br.readLine()))
    {
      buf.append(line).append("\n");
    }
    return buf.toString();
  }

在此清单中,HttpURLConnection 元素用于发送 GET 请求。请求的响应将以 InputStream 形式被检索,然后逐行读取以访问 XML 数据。使用上面用于发送 HTTP 请求的代码,您可以向 Twitter API 发出检索用户配置文件的调用,如下所示:


清单 5. 检索用 Java 语言编写的 Twitter 配置文件数据的 HTTP 请求
public class Twitter
{
  private static final String API_URL = "http://twitter.com/users/show/";

  public static String getUserLocation(String targetUserID)
    throws Exception
  {
    String response =
      HTTP.sendHTTPRequest(API_URL + targetUserID + ".xml", null);
    DocumentBuilder docBuilder =
       DocumentBuilderFactory.newInstance().newDocumentBuilder();
    Document doc =
      docBuilder.parse(new InputSource(new StringReader(response)));
    if (null != doc)
    {
      NodeList nodeList = doc.getElementsByTagName("location");
      if (null != nodeList && nodeList.getLength() > 0)
      {
        Node locationNode = nodeList.item(0);
        if (null != locationNode)
        {
          return locationNode.getTextContent();
        }
      }
    }

    throw new Exception("Invalid HTTP response content encountered");
  }
}





了解 Ajax

Ajax 包括一系列技术和概念,用于在支持 JavaScript 的浏览器与 HTTP 服务器之间进行交互,不需要直接刷新页面就可调用服务器请求。Ajax 还是使用 JavaScript、CSS、DOM 处理等动态更新浏览器页面的技术和概念集。通过浏览器页面检索服务器数据以及应用数据而不刷新页面的功能为创建基于浏览器的应用程序提供了方便的环境,其丰富的 UI 体验可以与桌面应用程序相媲美。

基于 Ajax 的页面需要使用名为 XMLHttpRequest 对象的 JavaScript 对象。此对象用于同步或异步地将 HTTP 请求从浏览器传输到服务器并且接收来自服务器的响应。

清单 6 中的代码片段将在 ajaxPost 方法中创建 XMLHttpRequest 对象的实例。XMLHttpRequest 对象随后用于在服务器框架中调用标准的 HTTP POST 方法。在服务器框架中检索的数据是通过 XMLHttpRequest 对象的 send 方法控制的,将在其中检索、解析数据并把数据传递给 createMap 方法,从中创建 Google Maps 地图并应用于页面。


清单 6. 处理 XMLHttpRequest 请求和响应
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
   Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
  <title>Google Maps</title>
  
  <script src="http://maps.google.com/maps?file=api&v=2&key=..."
    type="text/javascript"></script>

  <script type="text/javascript">
    //<![CDATA[

    if (GBrowserIsCompatible())
    {
      // =============================================
      // createMarker function
      // =============================================
      function createMarker(point, html)
      {
        var marker = new GMarker(point);
        GEvent.addListener(marker, "click", function()
        {
          marker.openInfoWindowHtml(html);
        });
        return marker;
      }

      // =============================================
      // createMap function
      // =============================================
      function createMap(latitude, longitude, twitterUser)
      {
        // Display the map, with some controls, and set the initial location
        var map = new GMap2(document.getElementById("map"));
        map.addControl(new GLargeMapControl());
        map.addControl(new GMapTypeControl());
        map.setCenter(new GLatLng(latitude, longitude), 8);

        // Set up marker

        var point = new GLatLng(latitude, longitude);
        var marker = createMarker(point, twitterUser)
        map.addOverlay(marker);
      }
      
      // =============================================
      // ajaxPost function
      // =============================================
      function ajaxPost(apiURL)
      {
        var twitterUser = document.getMapForm.TwitterUser.value;
        var xmlRequest = new XMLHttpRequest();

        // The false parameter indicates a synchronous call
        //
        xmlRequest.open("POST", apiURL + '?TwitterUser=' + twitterUser, false);
        xmlRequest.send(twitterUser);
        if (xmlRequest.status == 200)
        {
          if (xmlRequest.responseText)
          {
            var xmlDoc = xmlRequest.responseXML;

            var latitude =
              xmlDoc.getElementsByTagName('latitude')[0].firstChild.data;
            var longitude =
              xmlDoc.getElementsByTagName('longitude')[0].firstChild.data;

            createMap(latitude, longitude, twitterUser);
          }
        }
        else
        {
          alert("ajaxPost failed with status: " + xmlRequest.status);
        }
      }
    }
    else
    {
      // display a warning if the browser was not compatible
      alert("Sorry, the Google Maps API is not compatible with this browser");
    }

    //]]>
  </script>
</head>

<body onunload="GUnload()">

<p/>

<form method="POST"
    action="javascript:ajaxPost('http://localhost:8080/twoogle/service/getMap')"
    name="getMapForm">
  Twitter user name: <input type="text" value="" name="TwitterUser"/>
  <input type="submit" value="Get Map" name="submit"/>
</form>

<p/>

<div id="map" style="width: 550px; height: 450px"></div>
</body>

</html>

代码将使用针对给定 Twitter 用户检索的纬度和经度创建 Google Maps 地图。该地图随后将被应用到页面的 DOM 中,它将成为地图 <div> 元素的子元素。





使用 REST 和 Ajax 技术处理 Twitter 和 Google Maps 数据

使用检索 Twitter 用户位置数据和 Google Maps 的地理编码数据的功能,您可以构建相关服务以发布 Twitter 数据和 Google Maps 数据,mashup 可以使用并组合这些数据,并通过组合的数据生成聚合的 UI。

本文所示的框架将组合来自服务调用的数据,这些服务调用检索 Twitter 用户配置文件的位置数据,以及 Google Maps 中位置数据的经度和纬度数据。随后将在 mashup 页面的 JavaScript 中使用 Ajax 技术检索数据,在该页面中将把数据动态应用到页面的 DOM 中,以显示带有 Twitter 用户位置的地图。在 下载 小节中可以获得本例中的源项目。

框架的控制流程十分简单:从 mashup 到服务器端 Ajax-savvy Java servlet 使用 Ajax 来实现基于同步 REST 的服务调用。服务调用的结构为 http://{host}:{port}/twoogle/service/getMap。该 servlet 将把服务调用分派到 Twitter 和 Google Maps 中以检索 Twitter 用户的位置。该用户位置的经度和纬度将被传递回 mashup 并应用于页面中以创建地图。图 3 中显示了该框架。


图 3. Twitter/Google Maps mashup 框架
Twitter/Google Maps mashup 框架

框架将从 mashup 客户机中接收基于 Ajax 的 HTTP 请求,然后使用请求中的数据以从 Twitter 和 Google Maps 检索位置数据。给定 Twitter 用户的经度和纬度值将以 XML 格式传递回 mashup 客户机,然后进行解析并应用到 mashup 页面中。





部署和运行框架

Twitter/Google Maps Ajax 和 REST 框架将公开一个 servlet,接收基于 Ajax 的、REST 结构的 HTTP 请求。请求中的数据将被传递给 Twitter 和 Google Maps 服务以检索给定 Twitter 用户的位置数据。

要部署框架,需要把框架的编译类以及静态 HTML 文件和其他资源封装到一个文件夹中,结构类似 WAR 文件。然后把下面所示的命令应用到 WAR 文件文件夹中,从而把框架部署到 Geronimo 中。

GERONIMO_HOME\bin>deploy --user system 
    --password manager deploy --inPlace MYAPPDIR\MYAPPNAME

确保分别用应用程序的位置及应用程序名称替换 MYAPPDIR 和 MYAPPNAME。您可以使用下面所示的命令从 Geronimo 中取消部署框架:

GERONIMO_HOME\bin>deploy --user system 
    --password manager undeploy MYAPPNAME

同样,确保用应用程序名称替换 MYAPPNAME。





测试框架

在成功地部署应用程序后,请在 Web 浏览器中键入以下 URL:http://host:port/twoogle。当出现页面后,在页面顶部的框中键入 Twitter 用户名,然后单击 Get Map。如果用户的配置文件包含有效的 <location> 元素,则 Google Maps 地图将显示在页面中。

结束语

Apache Geronimo 是可用于构建企业服务和应用程序的 Java EE 平台。Geronimo 是围绕解耦的服务和组件设计的,可实现一种真正模块化的、可管理且可配置的部署和执行环境。在站点开发人员之间,Mashup 十分流行,因为它能提供更多可能性并且可以轻松地组合数据与内容。这种特性源于动态的且具有丰富语义的 Web 技术的普遍使用 — 这些技术包括 XML、JSON、RDF、动态 JavaScript 和 Ajax。

本文讨论了如何组合 REST 样式的服务调用与 Java 框架中基于 Ajax 的客户机和服务器以检索给定 Twitter 用户的位置数据。位置数据随后用于从 Google Maps 中检索经度和纬度数据,然后使用客户端 Ajax 技术应用这些数据以创建动态应用到 mashup 页面中的 Google Maps 地图。

您现在的任务是下载最新版本的 Geronimo,熟悉 Twitter 的 API 和 Google Maps API,并且熟悉 mashup 的一般概念。使用 REST 和 Ajax 提供的简化的 Web 应用程序编程模型,您现在可以开始使用功能强大的 Geronimo、Twitter 和 Google Maps 来构建健壮且高度灵活的 mashup。(责任编辑:A6)


时间:2008-11-11 09:10 来源:developerWorks 中国 作者:J. Jeffrey Hanson 原文链接

好文,顶一下
(0)
0%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量