使用 Groovy 构建社交网络混搭(Mashup)应用程序

红薯 发布于 2009/04/20 00:38
阅读 967
收藏 3

基于 Web 的社交网络如今已是一大趋势。Twitter、Facebook 和 LinkedInSocial 等应用程序的迅速普及表明,人们希望能够与志趣相投的人建立联系。另外一种流行的事物就是开放的应用程序接口。例如,Google 和 Twitter 面向全世界的开发人员,使他们能够进行创新。这两种平台都提供了用于查询、以及最终将您可以想象到的任何功能集成到应用程序中的 API。

混搭是典型的 Web 2.0 应用程序;它们实际上是将不相关的应用程序无缝整合到一个有效实体。该实体将驱使它工作的组件完美地隐藏在幕后。希望包含到混搭社区的实体所提供的开放 API 常常依赖于 RESTful 原理(参阅 参考资料),这使构建混搭比想象的要容易。

如今比较流行的混搭应用程序是 Google 地图。Google 地图实际上是一个 JavaScript 库,允许您向应用程序添加表示位置 的概念。通过为 Google 地图提供一些位置信息(使用地址或坐标的形式),就可以构建一个可视化地表示该位置的地图。如果您曾经使用过 Google 的在线地图应用程序(比如为了获知驾驶方向),那么您已经见识过 Google 地图的实际应用。

Twitter 是一个流行的社交网络应用程序。它允许人们向他们的追随者组成的网络发送任意内容的更新(称为 tweet)(在 Twitter 中,好友 是您所追随的人,追随者 指追随您的人)。这常被称为微型博客。 您可以查询 Twitter 的 API,了解 Twitter 数据的各个方面 — 例如,满足特定条件的 tweet(比方说,引用 Java™ 编程的 tweet),或者特定用户的好友甚至追随者的姓名。除此之外,Twitter 还存储位置信息,因为 Twitter 用户可以选择提供他们的位置。

因为 Twitter 公开位置信息,所以可以将 Twitter 与 Google 地图整合,创建一个允许人们根据特定位置查看 Twitter 的混搭。这就是本文将要讲述的内容:构建一个简单的应用程序,允许用户查看他们的好友的地图 — 用户网络的地理视图。执行下面三个步骤实现这个目标:

  1. 通过基于 Java 的第三方库绑定到 Twitter。
  2. 通过 Google 地图实现一个地图。
  3. 通过 Groovlets 和一点 Ajax 将所有内容与 Groovy 绑定在一起。

假定您已经在系统上安装了 Groovy。在操作中,我将陆续指出需要的其他工具。

公开的 Twitter

Twitter 广泛的 API 支持非常多的功能(参阅 参考资料)。 您可以使用它通过位置和关键字搜索 tweet、获得 Twitter 用户的好友和追随者列表,甚至看到这些好友和追随者的 tweet。Twitter API 总体上是 REST 风格的:它公开一系列与功能相映射的统一资源标识符(Uniform Resource Identifiers,URI)。

因为任何人都可以使用 Twitter 的 API,所以有很多开发人员已经创建了便于使用 Twitter 的库。我将使用 Twitter4J 封装 Twitter API,这是一个基于 Java 的库。为继续后面的操作,请 下载 twitter4j-1.1.4.zip 并将 twitter4j-1.1.4.jar 添加到类路径。为了利用 Twitter4J(更确切地说是 Twitter)做有用的事情,需要在 Twitter 上创建一个帐户,并且要追随一些人,以便制作好友的地理视图。

使用 Twitter4J

要了解 easyb 行为驱动开发框架的更多信息,请查看 Andrew Glover 的教程 “用 easyb 驱动开发”。

Twitter4J 库的中心接口是 Twitter 对象。对于给定的 Twitter 帐户,通过使用该帐户的用户名和密码实例化一个新的 Twitter 对象后,就可以连接到 Twitter 网络。清单 1 中为 johndoe 用户实现了此操作(使用 easyb),他的密码是 5555


清单 1. 使用 Twitter4J 连接到 Twitter

				
scenario "Exploring Twitter4J's login functionality'", {
given "an instance of Twitter4J", {
twitter = new Twitter("johndoe", "5555")
}
then "the test method should return true indicating things are working", {
twitter.test().shouldBe true
}
}

 

有了一个有效会话后,就可以查询(甚至更新)Twitter 实例来获得有趣的信息。例如,我可以通过调用 getFriends 获得一个帐户的好友列表(即 Twitter 帐户追随的人),如清单 2 所示。这个方法返回一个 User 对象列表 — 每个对象代表一个有效的 Twitter 帐户。


清单 2. 使用 easyb 通过 Twitter4J 获取好友列表

				
scenario "Twitter4J should support finding a user's network of friends", {
given "an instance of Twitter4J", {
twitter = new Twitter("johndoe", "5555")
}
then "the getFriends method should return a collection of users", {
twitter.getFriends().size().shouldBeGreaterThan 0
}
}

 

如您所见,Twitter4J 的 API 非常简单直观。

在此,可以通过 getLocation 方法确定特定用户的位置。这个位置是一个简单的名称,如 Denver、Colorado,甚至 Virginia。此外,您可以找到其他有趣的信息,如某位 Twitter 用户的肖像图片(通过调用 getProfileImageURL)、用户的真实姓名(通过适当命名的 getName 方法)、屏幕名称,甚至 Twitter 用户可以选择提供的个人简介(通过 getDescription 方法)。

事实上,与某人关联的好友列表、位置、照片和个人简介是创建有趣混搭应用程序所需的全部内容。我会将这些信息与 Google 地图整合并显示某个 Twitter 帐户的网络地理视图。

 




回页首


设置 Google 地图

从 Google 地图入手非常简单,首先必须 获得一个 API 键。 该键是免费的,但它绑定到一个特定的 URL,因此要为本文的应用程序生成一个 API 键需要提前做一点准备。因为它是一个 Web 应用程序,我将利用一个 servlet 容器(如 Tomcat)并(在开发中)为它提供一个上下文名称,如 geotweet。因此,本例中的 Google 地图键要关联的 URL 将是 localhost:8080/geotweet。这个键只是针对开发有效。当我决定使用 acmecorp.biz/geotweet 之类的 URL 将得到的代码移动到生产环境中时,我将需要生成另一个键。

生成键时,使用 Google 就足以提供运行良好的代码片段。将此代码复制到一个 HTML 文件即可。例如,清单 3 是我的初始 index.html:


清单 3. Google 提供的初始代码

				
<!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">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Geotweet!</title>
<script src="http://maps.google.com/maps?file=api&v=2&key=YOURKEY"
type="text/javascript"></script>
<script type="text/javascript">

//<![CDATA[

function load() {
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("map"));
map.setCenter(new GLatLng(37.4419, -122.1419), 13);
}
}

//]]>
</script>
</head>
<body onload="load()" onunload="GUnload()">
<div id="map" style="width:800px;height:500px"></div>
</body>
</html>

 

我之前说过,Google 地图是一个 JavaScript 库;因此,您需要注意两点:加载页面以及在页面加载之后 的地图操作。加载页面实际上是在初始化 Google 地图并相应地显示地图。因此,在清单 4 中,HTML <body> 元素有一个 onload 属性。我在这里调用 loadMap JavaScript 函数,它将加载一个 Google 地图实例。


清单 4. 在页面载入时加载地图

				
<body onload="loadMap()" onunload="GUnload()">

 

如清单 5 所示,在 <body> 元素的 <script> 部分定义 loadMap 函数:


清单 5. loadMap JavaScript 函数

				
<script type="text/javascript">

//<![CDATA[

var map;

function loadMap() {
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("containermap"));
map.setCenter(new GLatLng(38.8920910, -77.0240550), 2);
map.addControl(new GLargeMapControl());
}
}
//]]>

</script>

 

加载地图后,可能需要对它进行操作,例如添加标记。在这种情况下,必须先获得一个地图手柄,然后调用在此地图上获取坐标的 API。因此,在清单 5 中,我在函数外部定义了 map 变量。

当然,我会在浏览器加载地图后显示它。因此,在清单 6 中,我定义了一??? <div> 标记:


清单 6. 一个保存 Google 地图的简单 <div> 标记

				
<div id="containermap"></div>

 

清单 7 包含一个简单的层叠样式表(Cascading Style Sheet,CSS),用于设计 <div> 标记的样式。它将地图稍微向右移动,因为我要在左边添加一个简单的 <form>(理想情况下,用户会在此输入适当的 Twitter 信息)。


清单 7. 用于定位地图的简单 CSS

				
#containermap {
position: absolute;
margin: 5px 0px 0px 210px;
height: 650px;
width: 1000px;
}

 

此时,我的 index.html 文件现在可以查看了 — 例如,无需将它部署到一个 servlet 容器中,只需使用浏览器打开文件即可;该文件类似图 1 所示:


图 1. 在浏览器中打开的初始地图
简单地图

接着,我向地图左边添加一个简单的小 <form>。它有两个字段,与我之前用 Twitter4J 演示的内容直接相关。也就是说,为了实现有趣的功能,应用程序必须使用用户名和密码登录到 Twitter 帐户。清单 8 添加了一个带用户名和密码字段的 <form>


清单 8. 一个捕获信息的简单表单

				
<div id="container">
<div class="form">
<form action="" name="twitter">
<p><label>twitter id:</label><input class="name" type="text" size="12"
id="name" name="name" value=""/></p>
<p><label>password:</label><input class="pword" type="password" size="12"
id="pword" name="pword" value=""/></p>

<div class="buttnz">
<a href="javascript: doSubmit();">Map my friends!</a>
</div>
</form>
</div>
</div>

 

现在我已经对此表单进行了编码,页面看上去更简洁了,如图 2 所示:


图 2. 带有数据条目表单的地图
带有表单的地图

此时,我将添加一些丰富的功能。首先从映射 Twitter 帐户的好友这一功能开始。完成此操作并不是很困难,但是需要使用两项额外的技术:一些服务器端处理和一点 Ajax。

 




回页首


使用 Groovy 进行服务器端处理

Twitter4J 库完全是 Java 代码,在我的 Web 页面中不能很好地运行它们,因此我现在需要进行一些服务器端处理来集成此应用程序。为此,我将使用 Groovy 的一个轻量级框架,称为 Groovlets。Groovlets 只不过是不具备 servlet 结构的 servlet。也就是说,您可以编写简单的 Groovy 脚本并在 servlet 上下文中执行它们。脚本访问您编码 servlet 时经常使用的对象: ServletRequestServletResponseServletContext 等等。

使用并运行 Groovlets 再简单不过了。只需使用一些映射增强 web.xml 文件,将 Groovy 安装中的 groovy-all-1.5.7.jar 文件添加到 Web 应用程序的 WEB-INF/lib 目录中即可。

例如,如清单 9 所示,web.xml 文件中的两条语句将以 .groovy 结束的请求映射到 Groovy 的 GroovyServlet,由它发挥 Groovlets 的作用:


清单 9. 添加 Groovlets:只需向 web.xml 文件添加几个字段

				
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>GroovyServlet</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GroovyServlet</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>
</web-app>

 

现在,任何到达 .groovy URI 的请求都将调用 GroovyServlet,由它获取请求的资源并原样执行它。最棒的一点就是 Groovlets 是非常简单的脚本!

 




回页首


为混搭提供服务

在对任何 Groovlets 进行编码之前,我需要考虑两个关键问题。一个是如何才能最好地从 Twitter 获取所有需要的数据。当然,我知道如何做到这一点,但是我觉得我需要经常这样做;因此,最好将特定于 Twitter 的逻辑本地化(没有任何其他含义)到服务。不要考虑服务 一词的其他含义;在本例中,服务是一个抽象化特定行为的简单类。例如,我的 TwitterService 非常简单,如清单 10 所示:


清单 10. 使用 Twitter4J 库的简单服务

				
import twitter4j.Twitter

public class TwitterService {

def getFriendsForTwitterer(username, password) {
def twitter = getTwitter(password, username)
return twitter.getFriends()
}

private def getTwitter(password, username) {
return new Twitter(username, password)
}

def getLocationForUser(username, password) {
def twitter = getTwitter(password, username)
return twitter.getAuthenticatedUser().getLocation()
}
}

 

第二个问题就复杂一点。Google 地图使用的是坐标 — 纬度和经度,这些数字实际上可以精确定位地球上的任何地方。比如说,Google 地图其实一点也不了解 Washington, D.C.,它只知道纬度 38.8920910 和经度 -77.0240550。但是,Google 确实提供了逻辑名称与坐标之间的映射服务,这称为地理编码。Google 提供的地理编码服务是一个简单的 URL,它获取位置和其他参数,并返回请求位置的坐标。

例如,URL http://maps.google.com/maps/geo?q=washington+DC&output=xml&key=YOUR_KEY 返回一个 XML 文档,类似清单 11 所示:


清单 11. Google 的地理编码响应

				
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.0">
<Response>
<name>washington DC</name>
<Status>
<code>200</code>
<request>geocode</request>
</Status>
<Placemark id="p1">
<address>Washington, DC, USA</address>
<AddressDetails Accuracy="4" xmlns="urn:oasis:names🇹🇨ciq:xsdschema:xAL:2.0">
<Country>
<CountryNameCode>US</CountryNameCode>
<CountryName>USA</CountryName>
<AdministrativeArea>
<AdministrativeAreaName>DC</AdministrativeAreaName>
<Locality>
<LocalityName>Washington</LocalityName>
</Locality>
</AdministrativeArea>
</Country>
</AddressDetails>
<ExtendedData>
<LatLonBox north=
"38.9951100" south="38.7915140" east="-76.9093960" west="-77.1199010"/>
</ExtendedData>
<Point>
<coordinates>-77.0240550,38.8920910,0</coordinates>
</Point>
</Placemark>
</Response>
</kml>

 

因此,我可以编写一项服务,它获取 Twitter 提供的位置字符串并返回坐标。这项服务有一点复杂,因为 Twitter 帐户的位置是可选的。也就是说,用户不一定会提供它,并且 Twitter 不会尝试验证它。因此,即使帐户确实提供了一个 String 值,它也可能是无效的,或者 Google 可能会为它返回多个坐标集。此外,一些用户使用特殊的应用程序(如手机上的 Twitter 客户端),这些应用程序使用实际的坐标频繁更新用户的位置。因此,GoogleMapsService 在返回坐标之前要做一些清理工作,如清单 12 所示:


清单 12. 使用 Google 的地理编码服务

				
package org.disco.geotweet.service

public class GoogleMapsService {

private static String KEY = "YOUR_KEY"

def getLocationCoordinates(location) {
if (location.contains("iPhone:")) {
//iPhone: 39.035248,-77.138687
def iloc = location.replace("iPhone: ", "")
def scoords = iloc.split(",")
return [latitude: scoords[0], longitude: scoords[1]]
} else {
def encdLoc = URLEncoder.encode(location)
def response =
"http://maps.google.com/maps/geo?" +
"q=${encdLoc}&output=xml&key=${KEY}".toURL().getText()
def root = new XmlSlurper().parseText(response)
if (root.Response.Placemark.size() == 1) {
def coords = root.Response.Placemark.Point.coordinates.text()
def scoords = coords.split(",")
if (scoords.size() > 1) {
return [latitude: scoords[1], longitude: scoords[0]]
} else {
return [:]
}
} else {
return [:]
}
}
}
}

 

如清单 12 所示,我需要执行多次检查来确定是否存在空值。同时,使用 Groovy 的 XmlSlurper 解析 Google 的 XML 响应非常简单。

JSON

JSON(参阅 参考资料)是 Web 应用程序的另一种格式,类似于 XML 但简洁得多。由于 JSON 的轻量级特点,它逐渐成为 Web 的通用语言。有关简化服务器端处理和 JSON 创建的方便的库,请下载 JSON-lib(如果要继续操作的话)。

对两项服务进行编码后,就需要通过 Groovlets 将各种组件链接在一起。首先,应当将地图的中心设置为 Twitter 帐户的位置。使用我刚刚编写的服务执行主要的操作,清单 13 创建了一个 Groovlet,它获取两个参数 — Twitter 帐户的用户名和密码,并返回该帐户坐标的 JavaScript Object Notation(JSON)表示:


清单 13. 将 Twitter 帐户作为 Google 地图的中心

				
import net.sf.json.JSONObject
import org.disco.geotweet.service.GoogleMapsService
import org.disco.geotweet.service.TwitterService

def name = request.getParameter('username')
def pword = request.getParameter('password')

def coordinates = getJSONLatAndLng(name, pword)

println coordinates

def getJSONLatAndLng(name, password) {
def googleMapsSevice = new GoogleMapsService()
def twitterService = new TwitterService()
def loc = twitterService.getLocationForUser(name, password)
def coords = googleMapsSevice.getLocationCoordinates(loc)
return this.getJSONForCoords(coords)
}

def getJSONForCoords(coords) {
return new JSONObject().element("latitude", coords.latitude)
.element("longitude", coords.longitude)
}

 

如清单 13 所示,这个 Groovlet 非常简单。同时,Groovlets 可以隐式访问 request 对象,而且使用 println 就足以返回响应。

最后,我需要一种方式来返回基本相同但更加丰富的信息。也就是说,我需要使用 JSON 格式返回 Twitter 用户的好友的地址。之后,JSON 响应将包含到达用户图像、名称和个人简介的 URL。这样,当我把这类信息放在 Google 地图实例上时,我就可以使标记更有趣一点。但在丰富地图之前,我需要对好友 Groovlet 进行编码,如清单 14 所示:


清单 14. 一个用于构建好友列表的 Groovlet

				
import net.sf.json.JSONArray
import net.sf.json.JSONObject
import org.disco.geotweet.service.GoogleMapsService
import org.disco.geotweet.service.TwitterService

def name = request.getParameter('username')
def pword = request.getParameter('password')

def output = getJSONFriends(name, pword)

println output

def getJSONFriends(name, password) {
def friends = getTwitterFriends(name, password)
def output = new JSONObject().element("friends", new JSONArray())
def gservice = new GoogleMapsService()
friends.each {
def location = it.getLocation()
def pictureURL = it.getProfileImageURL().toString()
def bio = it.getDescription()
def tname = it.getName()
if (location.size() > 0) {
def profile = gservice.getLocationCoordinates(location)
profile.pic = pictureURL
profile.bio = bio
profile.name = tname
if (profile.size() > 0) {
output.accumulate("friends", profile)
}
}
}

return output.toString() //JSON format
}


private def getTwitterFriends(name, password) {
def service = new TwitterService()
return service.getFriendsForTwitterer(name, password)
}

 

如清单 14 所示,我利用 GoogleMapsService(与 TwitterService 一致)获取一个地图,其中包含每个 Twitter 帐户的好友的坐标(profile 变量)。该地图会增加一些额外的信息(通过 profile.pic = pictureURL),这些信息最终格式化为 JSON,然后返回给浏览器。

利用这两个 Groovlets 和两项处理 Twitter 和 Google 地图逻辑的服务,我就完成了混搭的服务器端部分。

 




回页首


利用 Ajax 获得相应的 UI

获得成功的最后一步是利用一些 JavaScript 来调用两个 Groovlets 并相应地更新页面中的 Google 地图。

应用程序的 index.html 页面左侧含两个字段的表单在单击 “Map my friends” 链接时调用 doSubmit 方法。在这个函数中将执行一些重要的操作。我将使用 Ajax 异步更新 Web 页面。这将提供一种无需重新加载页面的流畅用户体验。

我将利用 jQuery(一个提供丰富功能的 JavaScript 库)通过 JavaScript 构建一个出色的 UI。这个方便的框架有一个针对 Ajax 的优秀 API。例如,通过调用 jQuery 的 getJSON 调用,我可以轻易地将 Twitter 帐户位置设置为地图的中心,如清单 15 所示:


清单 15. 使用 jQuery 的 getJSON 函数

				
function doSubmit() {
var tid = document.twitter.name.value;
var pwrd = document.twitter.pword.value;

$.getJSON("locatetwitterer.groovy", {"username": tid, "password":pwrd}, function(json) {
if(json.length > 0){
map.setCenter(new GLatLng(json.latitude, json.longitude), 2);
}
});
}

 

清单 15 中的 jQuery getJSON 函数看上去有点奇怪,但如果您明白接下来的操作,就会非常简单:

  • 第一个参数是要调用的 URL。在本例中,它是 locatetwitterer.groovy,一个位于 Web 应用程序根目录中的文件。
  • 接着,获取参数的映射。因此 tid 变量(保存 Twitter 名称)与 username 参数关联。同样地,pwrd 变量也要被映射。
  • 最后一个参数是所谓的回调;也就是说,收到对 locatetwitterer.groovy 请求的响应后,就会调用回调中定义的函数。因此,要检查 JSON 响应的长度;如果含有数据,就会获取 latitudelongitude 属性值并提供给 Google 地图的 GLatLng 对象(它表示一个位置),然后相应调整地图中心。

添加好友!

接下来(最后一步)是在表示好友的位于中心的地图上添加标记。我将使用同样的 jQuery getJSON 调用;不过,这次回调函数的作用更大。清单 16 将向包含一些 HTML 的 Google 地图标记添加一个侦听器。当有人单击这个标记时,会弹出一个小窗口,其中包含所选好友的图标、姓名和个人简介。


清单 16. 异步填充地图

				
$.getJSON("locatefriends.groovy", {"username": tid, "password":pwrd}, function(json) {
$.each(json.friends, function(i, item) {
var marker = new GMarker(new GLatLng(item.latitude, item.longitude));
map.addOverlay(marker)
var picture = "<img src=\"" + item.pic + "\"/> " + item.name + "<br/>" + item.bio

GEvent.addListener(marker, "click", function() {
marker.openInfoWindowHtml(picture);
});
});
});

 

如清单 16 所示,doSubmit 函数的新增部分有点复杂,但仍然易于理解。首先,在回调函数内,jQuery 的 each 函数在收到的 JSON 响应内遍历好友集合。这个函数也有一个回调;因此,对于每次遍历,都会有一些逻辑应用于收集的数据。与之前一样,创建一个新的 GLatLng 对象,然后在地图实例之上覆盖一个 GMarker(在 Google 地图上添加类似小红点的图标)。图 3 显示了带有位置标记的地图:


图 3. 一张标有位置的 Google 地图
标有位置的地图

图 4 是一个弹出窗口,显示好友图像以及一些表示姓名和个人简介(如果有的话)的文本:


图 4. 单击小红点显示其他信息
带有弹出窗口的地图




回页首


结束语

如 您所见,经过一些准备和努力,组合一个包含 Twitter 和 Google 地图的社交混搭应用程序非常简单 — 特别是添加了 Groovy 和一点 Ajax 之后。当然,您还可以做更多的工作。我仅仅涉及了众多可用特性(包括错误处理)的一部分。但是核心思想已经体现出来了:开放的 API 加上一些想象开辟了一个全新的应用程序世界和无限可能。剩下的功能就由您实现吧。

加载中
0
leon_rock
leon_rock

耐心看~~~

红薯大哥

能不能提供工程包

返回顶部
顶部