准备开始
首先要保证安装了 Apache Abdera 的当前版本。源代码可以从 Apache Subversion 资料库http://svn.apache.org/repos/asf/incubator/abdera/java/trunk 下载。要检索源代码,需要安装 subversion 客户机并使用下面的命令:
> svn co http://svn.apache.org/repos/asf/incubator/abdera/java/trunk
下载源代码的镜像之后就可以用 Ant version 1.6.5 或更高版本构建 Abdera 了。
> cd trunk > ant -f build/build.xml dist
构建完成后,编译后的 jar 和 dependency 放在新建的 dist 目录中。要运行这些例子,需要在类路径中添加下列 jar:
Abdera (dist) | Dependency (dist/lib) |
---|---|
|
|
Weblog
根据 IETF Atom Publishing Format and Protocol 工作组的章程,Atom 发布协议(Atom Publishing Protocol)的设计目标主要用于发布和管理 weblog 记录。毫不奇怪,随后很多 blogging 软件提供商如 Google、SixApart 和 Roller 已经开始初步支持该协议。
Google 的 Blogger Beta
2006 年 8 月初,Google 宣布将对其提供的网络日记服务进行期待已久的升级。服务新增加的一个特性就是支持使用 Atom 发布协议创建和编辑公告。
创建公告非常简单。首先需要知道新记录所要发送到的 Atom 集合的 URL。对于 Blogger 来说,Atom 提要用于连锁 blog 的内容组成 Atom 发布协议集合供提要阅读器和聚合器使用。因此要确定集合的 URI,只要查看 weblog 主页头部的替代链接即可。
清单 1. Blogger 主页的头部
<html> <head> <title>testing</title> <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/> <meta content='true' name='MSSmartTagsPreventParsing'/> <meta content='blogger' name='generator'/> <link rel="alternate" type="application/atom+xml" title="testing - Atom" href="http://beta.blogger.com/feeds/7352231422284704069/posts/full" /> <link rel="alternate" type="application/rss+xml" title="testing - RSS" href="http://beta.blogger.com/feeds/7352231422284704069/posts/full?alt=rss" /> <link rel="service.post" type="application/atom+xml" title="testing - Atom" href="http://beta.blogger.com/feeds/7352231422284704069/posts/full" /> ...
知道 Atom 提要的地址之后就可以向博客上发表记录了。清单 2 显示了要发送的贴子示例:
清单 2. Blogger 贴子
POST /feeds/7352231422284704069/posts/full HTTP/1.1 Host: beta.blogger.com Content-Type: application/atom+xml Content-Length: 349 Authorization: GoogleLogin auth={auth token} <?xml version="1.0" encoding="UTF-8"?> <entry xmlns="http://www.w3.org/2005/Atom"> <id>urn:uuid:1332534422684714363</id> <title>Posting to Blogger</title> <author><name>James</name></author> <updated>2006-09-02T12:12:12Z</updated> <content type="xhtml"> <div xmlns="http://www.w3.org/1999/xhtml"> <p>This is an example post to the new blogger beta</p> </div> </content> </entry>
清单 2 中的请求有几点需要注意。首先也是最重要的是 Authorization 头部。Google 中支持 Atom Publishing Protocol 的服务要求使用一种私有的身份验证方法,所幸的是,实现起来并不麻烦。其次,Google 的实现要求请求中包含 Content-Length 头部。虽然看起来似乎无关紧要,但要求使用 Content-Length 带来了一个明显的副作用,即向服务器发送记录的客户机在发送之前必须计算请求的大小,因而降低了请求的效率。最后,发出的记录包含了 Blogger 将完全忽略的 ID、author 和 updated 元素。
第 1 步. 身份验证
向 Blogger 发送帖子的第一步是用 GoogleLogin 身份验证方法进行身份验证。为此需要创建 GoogleLogin 工具类。该工具需要输入访问 Google 帐户的用户 ID 和口令以及要访问的服务名称。该例中的服务名称为“blogger”。
清单 3. GoogleLogin 身份验证方法需要向身份验证服务器发送简单的 HTTP POST 请求
public final class GoogleLogin { ... public static String getAuth( Client client, String service, String id, String pwd) { try { StringRequestEntity stringreq = new StringRequestEntity(getRequest(id,pwd,service)); RequestOptions options = client.getDefaultRequestOptions(); options.setContentType("application/x-www-form-urlencoded"); ClientResponse response = client.post(URI, stringreq, options); String auth = read(response.getInputStream()); response.release(); return auth.split("\n")[2].replaceAll("Auth=", "auth="); } catch (Exception e) {} return null; } ... }
用适当的证书调用 GoogleLogin.getAuth(...) 方法将得到一个身份验证标记,可用于验证发表或者编辑 Blogger 记录的请求。
第 2 步. 创建记录
向 Blogger 发帖子的第二步是用 Apache Abdera 的 Feed Object Model API 创建发送的 Atom 记录:
清单 4. 创建 Blogger 记录
Abdera abdera = new Abdera(); Factory factory = abdera.getFactory(); Entry entry = factory.newEntry(); entry.setId(FOMHelper.generateUuid()); entry.setUpdated(new java.util.Date()); entry.addAuthor("James"); entry.setTitle("Posting to Blogger"); entry.setContentAsXhtml( "<p>This is an example post to the new blogger beta</p>");
第 3 步. 发表记录
最后一步是把记录发送到 Blogger 服务器上:
清单 5. 向 Blogger 发送记录
Client client = new CommonsClient(abdera); String auth = GoogleLogin.getAuth( client, "blogger", "your@user.id", "your.password"); RequestOptions options = client.getDefaultRequestOptions(); options.setAuthorization("GoogleLogin " + auth); BaseRequestEntity bre = new BaseRequestEntity(entry, false); Response response = client.post( "http://beta.blogger.com/feeds/7352231422284704069/posts/full", bre, options); if (response.getStatus() == 201) System.out.println("Success!"); else System.out.println("Failed!");
在 清单 5 中可以看到把上述各步骤都结合起来了。首先检索得到身份验证标记并作为请求选项设置。然后使用 Abdera 的 BaseRequestEntity 工具类作为记录的包装器,以保证正确地计算所需 Content-Length 头部。最后将记录发送到 Blogger 主页头部给出的 Atom 提要 URL。如果成功发布,服务器将返回 201 HTTP 状态码。
图 1. Blogger 中成功发帖
编辑和删除记录
通过 Atom 发布协议也可编辑和删除已有的记录:
清单 6. 更新已有的记录
String location = // get the URI of the entry to edit Document<Entry> entry_doc = client.get(location).getDocument(); entry = (Entry) entry_doc.getRoot().clone(); entry.setTitle("This is the changed title"); response = client.put( location, new BaseRequestEntity(entry,false), options);
清单 7. 删除记录
String location = // get the URI of the entry to delete response = client.delete(location, options);
Roller Weblogger
但 Google 并不是惟一准备支持 Atom 发布协议的博客提供商。流行的开放源码 Roller Weblogger 包是很多大型公司博客网络的后端,如 Sun 的 http://blogs.sun.com 网站和 IBM 的内部 Intranet 博客服务,目前正准备升级以便支持 APP。除了一两处值得一提的区别外,向 Roller 上发贴基本上与 Blogger 相同:
清单 8. Roller 发帖的例子
POST /app/myblog/entries HTTP/1.1 Host: example.org Content-Type: application/atom+xml Authorization: Basic {user:password} <entry xmlns="http://www.w3.org/2005/Atom"> <id>urn:uuid:4BA4E6A3334F88813011571535258641</id> <title type="text">Posting to Roller</title> <updated>2006-09-01T23:32:05.880Z</updated> <author><name>James</name></author> <content type="html"> <p>This is an example post to Roller</p> </content> </entry>
Roller Atom 接口和 Blogger 的主要区别是 Roller 提供了 APP Service Document 来发现可用的集合(而不是在 blog 主页中使用的替代链接),另外 Roller 使用的是标准 Basic 身份验证方案:
清单 9. 向 Roller 发贴
String start = "http://roller.example.org/app"; Abdera abdera = new Abdera(); Factory factory = abdera.getFactory(); Entry entry = factory.newEntry(); entry.setId(FOMHelper.generateUuid()); entry.setUpdated(new java.util.Date()); entry.addAuthor("James"); entry.setTitle("Posting to Roller"); entry.setContentAsHtml("<p>This is an example post to Roller</p>"); Client client = new CommonsClient(abdera); client.addCredentials( start, null, null, new UsernamePasswordCredentials( "username", "password")); // Get the collection URI from the service document Document<Service> service_doc = client.get(start).getDocument(); Service service = service_doc.getRoot(); Collection collection = service.getWorkspaces().get(0) .getCollections().get(0); String uri = collection.getHref().toString(); Response response = client.post(uri, entry); if (response.getStatus() == 201) System.out.println("Success!"); else System.out.println("Failed!");
发布媒体资源
Roller Atom Publishing 实现的另一个重要特性是支持上传任意的媒体资源。比方说,向服务器上传播客只需要将上例中的记录替换为代表要上传的 MP3 的请求实体:
清单 10. 向 Roller Weblog 发布音频资源
FileInputStream fis = new FileInputStream( "mypodcast.mp3"); InputStreamRequestEntity re = new InputStreamRequestEntity(fis, "audio/mp3"); Client client = // init the client String uri = // get the collection uri RequestOptions options = client.getDefaultRequestOptions(); options.setHeader("Title", "mypodcast.mp3"); Response response = client.post(uri, re, options); if (response.getStatus() == 201) System.out.println("Success!"); else System.out.println("Failed!");
还可以用 Atom 发布协议操作来更新、删除和列出上传到服务器上的所有资源。
日历管理
Atom Publishing 工作组的一个重要目标就是要设计这样一种协议,该协议虽然主要针对向 Weblog、Wiki 及类似应用程序发布内容,但也能用于其他多种应用程序。目前对 Atom Publishing 的支持已经延伸到很多不属于博客的应用程序。一个例子就是 Google 的 Calendar 应用程序。
Google Calendar
Google Calendar 是一种托管的日历管理服务,让用户能够通过基于浏览器的界面或者 Atom 发布协议管理公共或个人的日程表:
清单 11. Google Calendar 的 Atom 贴子
POST /calendar/feeds/default/private/full HTTP/1.1 Host: www.google.com Authorization: GoogleLogin auth={auth} Content-Length: 834 Content-Type: application/atom+xml <entry xmlns="http://www.w3.org/2005/Atom" xmlns:gd="http://schemas.google.com/g/2005" > <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event" /> <id>urn:uuid:421D94DE83D298A91211571550147641</id> <title type="text">Tennis with Beth</title> <content type="text">Meet for a quick lesson</content> <updated>2006-09-01T23:56:54.772Z</updated> <author><name>James</name></author> <gd:transparency value="http://schemas.google.com/g/2005#event.opaque" /> <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed" /> <gd:where valueString="Rolling Lawn Courts" /> <gd:when startTime="2006-09-01T23:56:54.932Z" endTime="2006-09-01T23:56:54.932Z" /></entry>
需要注意的是,Google Calendar 和新的 Blogger Beta 一样都基于相同的后端 Atom 发布协议基础设施。和前面的 Blogger 例子一样,Calendar 也使用私有的 GoogleLogin 身份验证方法,也要求 Content-Length 头部。此外 Atom 记录中还需要一些扩展元素:
清单 12. 建立 Calendar 记录
Abdera abdera = new Abdera(); Factory factory = abdera.getFactory(); Entry entry = factory.newEntry(); entry.setId(FOMHelper.generateUuid()); entry.setUpdated(new java.util.Date()); entry.addAuthor("James"); entry.setTitle("New Calendar Event"); entry.setContentAsXhtml("<p>A new calendar event</p>"); entry.addExtension(TRANSPARENCY).setAttributeValue( "value", "http://schemas.google.com/g/2005#event.opaque"); entry.addExtension(EVENTSTATUS).setAttributeValue( "value", "http://schemas.google.com/g/2005#event.confirmed"); entry.addExtension(WHERE).setAttributeValue( "valueString", "Rolling Lawn Courts"); Element el = entry.addExtension(WHEN); el.setAttributeValue("startTime", AtomDate.valueOf(new Date()).toString()); el.setAttributeValue("endTime", AtomDate.valueOf(new Date()).toString());
建立记录后,剩下的就与向 Blogger 发贴基本一样了:
清单 13.发布到 Google Calendar
Client client = new CommonsClient(abdera); String auth = GoogleLogin.getAuth(client, "cl", "username", "password"); RequestOptions options = client.getDefaultRequestOptions(); options.setAuthorization("GoogleLogin " + auth); BaseRequestEntity bre = new BaseRequestEntity(entry, false); String uri = "http://www.google.com/calendar/feeds/default/private/full"; Response response = client.post(uri, bre, options); // calendar may return a 302 with a new URI to post to if (response.getStatus() == 302) { uri = response.getLocation().toString(); response = client.post(uri, bre, options); } if (response.getStatus() == 201) System.out.println("Success!"); else System.out.println("Failed!");
另一种方法
Google 日历管理所采用的 Atom 方法面临的一个挑战是记录中需要使用厂商专用的扩展。IBM® Lotus® 的研究人员提出了一种替代方法,即利用 Atom 支持任何媒体类型这个特点使用现有的标准日历格式如 iCal 和 xCal:
清单 14. 向 Atom 集合发送 xCalendar 事件
POST /calendar HTTP/1.1 Host: example.org Content-Type: application/calendar+xml Content-Length: nnnn <?xml version="1.0" encoding="UTF-8"?> <vcalendar version="2.0" prodid="-//hacksw/handcal//NONSGML 1.0//EN"> <vevent> <uid>19970901T130000Z-123401@host.com</uid> <dtstamp>20060901T130000Z</dtstamp> <dtstart>20060901T163000Z</dtstart> <dtend>20060901T164000Z</dtend> <summary>Tennis with Beth</summary> <class>PUBLIC</class> </vevent> </vcalendar>
发送到集合后,服务器创建表代表该事件的 Atom Entry:
清单 15. Atom 服务器创建的日历项
HTTP/1.1 201 Created Date: Fri, 7 Oct 2005 17:17:11 GMT Content-Length: nnn Content-Type: application/atom+xml; charset="utf-8" Content-Location: http://example.org/calendar/1.atom Location: http://example.org/calendar/1.atom <?xml version="1.0"?> <entry xmlns="http://www.w3.org/2005/Atom"> <title>Tennis with Beth</title> <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> <updated>2006-09-01T18:30:02Z</updated> <author><name>James</name></author> <summary type="text">Tennis with Beth</summary> <link rel="alternate" type="text/calendar" href="http://example.org/calendar/1.ics" /> <content type="application/calendar+xml" src=http://example.org/calendar/1.xml/> <link rel="edit" type="application/atom+xml" href="http://example.org/calendar/1.atom" /> <link rel="edit-media" type="application/calendar+xml" href="http://example.org/calendar/1.xml" /> </entry>
请注意,这种方法不需要对 Atom 格式做任何扩展,实现可以继续使用已有的日历格式。
存储数据
还有人尝试使用 Atom 发布协议作为一种通用的前端 API 实现各种数据存储服务。其中包括文档和内容管理服务、软件资料库、数据库服务器、工作流和情景应用程序等等。
比如,Google 最近宣布新的 Google Base 服务 beta 版本将为各种应用程序服务存储任意数据:
清单 16. 发布到 Google Base
POST /base/feeds/items HTTP/1.1 Host: www.google.com Authorization: GoogleLogin auth={auth} X-Google-Key: key={key} Content-Type: application/atom+xml Content-Length: 632 <?xml version='1.0'?> <entry xmlns="http://www.w3.org/2005/Atom" xmlns:g="http://base.google.com/ns/1.0"> <id>urn:uuid:1215d395-cfb1-4faa-b1cd-12da123e3a7a</id> <category scheme="http://base.google.com/categories/itemtypes" term="products"/> <title type='text'>Acme 2000 series laptop</title> <content type='xhtml'> <div xmlns='http://www.w3.org/1999/xhtml'> The fastest Acme Laptop yet... </div> </content> <link rel='alternate' type='text/html' href='http://www.provider-host.com/123456789'/> <g:label>Computer</g:label> <g:label>Laptop</g:label> <g:label>fastest laptop</g:label> <g:item_type>products</g:item_type> </entry>
由于灵活的设计以及对任意媒体资源的支持,Atom 发布协议有望成为一种通用 Web 数据管理 API。
结束语
本文介绍了一些实际的 Atom 发布实现,这些实现已经部署并被成千上万的用户使用。为了支持文中的例子,本文使用开放源码的 Apache Abdera 项目示范了与各种博客、日历和数据管理服务的交互。本系列文章的下一篇将详细介绍 Apache Abdera 项目,包括对 Feed Object Model API 与各种特性(如对 XPath、XSLT 转换、内容筛选和 XML 数字签名的支持)的介绍。