认识 Atom 发布协议,第 2 部分: 应用 Atom 发布协议

准备开始

首先要保证安装了 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:

表 1. 运行例子需要的 jar
Abdera (dist) Dependency (dist/lib)
  • abdera.client.0.2.0-incubating-SNAPSHOT.jar
  • abdera.core.0.2.0-incubating-SNAPSHOT.jar
  • abdera.parser.0.2.0-incubating-SNAPSHOT.jar
  • abdera.protocol.0.2.0-incubating-SNAPSHOT.jar
  • axiom-api-1.0.jar
  • axiom-impl-1.0.jar
  • commons-codec-1.3.jar
  • commons-httpclient-3.0.1.jar
  • commons-logging-1.0.4.jar
  • geronimo-activation_1.0.2_spec-1.1.jar
  • geronimo-javamail_1.3.1_spec-1.1.jar
  • log4j-1.2.12.jar
  • stax-1.1.2-dev.jar
  • stax-api-1.0.jar

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 中成功发帖
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 数字签名的支持)的介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值