内容


在 Twitter 上更新项目的构建状态

使用一个自定义 Ant 任务和开放式身份验证更新 Twitter 帐户的构建完成状态

Comments

Apache Ant 中内置的邮件任务可以向所提供的一系列用户发送电子邮件,通知他们构建过程的完成状态。然而,久而久之,由于人们更改电子邮件地址、离开项目等行为,该特性变得不是那么有用了。

Twitter.com 提供一个公共 API 来让用户从各种平台(比如移动电话、电视和游戏机)更新其帐户。通过发送一个 Twitter 帐户构建完成更新信息,您的团队可以即时通知项目开发人员和测试人员新构建已完成。另外一个好处是,相关用户就可以可以订阅和取消订阅摘要,而无需构建工程师的帮助。

本文描述如何创建一个 Twitter 客户端 Ant 任务 — 借助于可与 Twitter 交互的开源 Java™ 库 — 来更新相关 Twitter 帐户的构建状态。本文的样例代码可从 下载 部分获取。

开始使用 Twitter

要对您的构建通知(build-notification)系统使用 Twitter,您需要创建一个 Twitter 帐户,然后注册客户端应用程序。

创建一个 Twitter 帐户

第一步最简单。导航到 twitter.com 并创建一个帐户。我创建了一个名为 tweet_task 的帐户,以作演示之用。您可以在 http://twitter.com/tweet_task 上查看最新状态。

注册客户端应用程序

如同所有用于更新 Twitter 帐户状态的客户端应用程序,本文中要构建的这个也需要在使用之前在 Twitter 上注册。打开 Twitter OAuth 客户端注册页面 http://twitter.com/oauth_clients/new,并完成表单,如图 1 所示:

图 1. 使用 Twitter 注册一个应用程序
用于注册应用程序的 Twitter 表单屏幕截图
用于注册应用程序的 Twitter 表单屏幕截图

您可以使用表单的 Application Icon 字段输入或浏览到一幅图像,将其作为客户端应用程序的图标 — 不大于 700k 的 GIF、JPG 或 PNG 文件。对于 Application Name,您可以输入不包含 twitter 一词的任何内容。(要谨记,使用该客户端所做的任何更新都会将客户端名称作为副标题包含在内。出于演示之用,我选择将 tweet_task_client 作为其名称,您可以在所有状态上看到 via tweet_task_client 副标题。)在 Description 字段中输入一个对应用程序的简短描述。注意,Application Website 字段不能为空,因此输入应用程序的 URL(即使它没有什么作用)。Organization 字段及其相关的 Website 字段是可选的。选择 Client 作为 Application Type,Read & Write 作为 Default Access Type。在 Use Twitter for login: 旁边,选择 Yes, use Twitter for login

注册了应用程序之后,会出现一个界面,包含 Consumer 密钥(Consumer key)和 Consumer 机密令牌(Consumer secret tokens)。注意并安全存储它们。

创建一个 AccessToken 对象

Twitter4J 是一个开源、非官方的 Java 库,以内置的 OAuth 支持为特色。从 Twitter4J 站点下载并添加最新版本(参见 参考资料)并将其添加到您的类路径中。

在客户端更新帐户状态之前,帐户必须赋予它这种权限。清单 1 是一个简短的程序,用于为一个帐户生成应用程序授权 URL:

清单 1. Twitter4JRegister.java
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;

import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;
import twitter4j.http.RequestToken;

public class Twitter4JRegister {


  public static void main(String args[]) throws Exception {

    Twitter twitter = new TwitterFactory().getInstance();
    twitter.setOAuthConsumer("consumer key", "consumer secret");
    RequestToken requestToken = twitter.getOAuthRequestToken();
    AccessToken accessToken = null;
    BufferedReader bufferedReader = new BufferedReader(
        new InputStreamReader(System.in));
    while (null == accessToken) {
      System.out
          .println("Open the following URL and grant access to your account:");
      System.out.println(requestToken.getAuthorizationURL());
      System.out
          .print("Enter the generated PIN:");
      String pin = bufferedReader.readLine();
      try {
        if (pin.length() > 0) {
          accessToken = twitter
              .getOAuthAccessToken(requestToken, pin);
        } else {
          accessToken = twitter.getOAuthAccessToken();
        }

      } catch (TwitterException e) {
        if (401 == e.getStatusCode()) {
          System.out.println("Unable to get the access token.");
        } else {
          e.printStackTrace();
        }
      }
    }
    storeAccessToken(accessToken);
    Status status = twitter.updateStatus("Client installed");
    System.out.println("Successfully updated the status to ["
        + status.getText() + "].");
    System.exit(0);
  }

  private static void storeAccessToken(AccessToken accessToken) {

    try {
      FileOutputStream fileOutputStream = new FileOutputStream(
          "token.txt");
      ObjectOutputStream objectOutputStream = new ObjectOutputStream(
          fileOutputStream);
      objectOutputStream.writeObject(accessToken.getToken());
      objectOutputStream.flush();
      fileOutputStream = new FileOutputStream("tokenSecret.txt");
      objectOutputStream = new ObjectOutputStream(fileOutputStream);
      objectOutputStream.writeObject(accessToken.getTokenSecret());

      objectOutputStream.flush();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

  }

}

清单 1 中的代码输入到一个名为 Twitter4JRegister.java 的文件中,用您自己的密钥和机密替换用户密钥和用户机密占位符,并编译代码。运行代码,然后将生成的 URL 输入到浏览器的地址栏。使用用户名和密码登录之前创建的 Twitter 帐户。生成并显示 PIN 时,在控制台上输入它。

令牌和令牌机密对象被序列化到两个独立文件,分别是 token.txt 和 tokenSecret.txt。客户端将使用这些对象在今后所有的事务中验证帐户。为这些文件保留一个备份;如果它们被删除,您需要重新运行程序来生成一个新的 URL,以便重新创建它们。注意,帐户的用户名和密码在代码中从未用过。令牌和令牌机密可在任何地方(比如数据库或注册表)存留;将它们序列化到一个文件仅是本文建议的一种解决方案。

使用令牌更新状态

现在令牌可供用户使用了,您可以编写客户端应用程序来测试其功效并更新帐户状态。清单 2 显示客户端应用程序的代码,名为 Twitter4JUpdate

清单 2. Twitter4JUpdate.java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;

public class Twitter4JUpdate {

  public static void main(String args[]) throws Exception {

    TwitterFactory factory = new TwitterFactory();
    AccessToken accessToken = loadAccessToken();
    Twitter twitter = factory.getOAuthAuthorizedInstance("consumer key",
        "consumer secret", accessToken);
    Status status = twitter.updateStatus("Client registered");
    System.out.println("Successfully updated the status to ["
        + status.getText() + "].");
    System.exit(0);
  }

  private static AccessToken loadAccessToken() {
    String token = null;
    String tokenSecret = null;
    try {
      FileInputStream fileInputStream = new FileInputStream("token.txt");
      ObjectInputStream objectInputStream = new ObjectInputStream(
          fileInputStream);
      token = (String) objectInputStream.readObject();
      fileInputStream = new FileInputStream("tokenSecret.txt");
      objectInputStream = new ObjectInputStream(fileInputStream);
      tokenSecret = (String) objectInputStream.readObject();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return new AccessToken(token, tokenSecret);

  }

}

像之前一样用您自己的密钥和机密替换 清单 2 中的用户密钥和用户机密。如果您将令牌和令牌机密对象存留在建议文件之外的其他地方,您需要重写 loadAccessToken() 方法来获取它们。

编译 Twitter4JUpdate.java。要确认帐户状态将得到正确更新,对您希望显示的状态更新使用该命令:

java Twitter4JUpdate update

确认您在 twitter.com 上输入的更新正确显示。

编写一个自定义 Ant 任务来更新帐户状态

编写一个自定义 Ant 任务所需做的仅是扩展 org.apache.tools.ant.Task 任务并重写 execute() 方法(参见 参考资料)。任何运行时变量都被定义为私有类属性,带有适当的 set 和 get 方法。不要使用命令行参数来设置帐户状态,您要在构建文件中设置一个字符串值,任务会使用字符串值更新状态。

在一个名为 TweetTask.java 的文件中输入清单 3 中的代码,像之前那样替换用户密钥和用户机密:

清单 3. TweetTask.java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

import org.apache.tools.ant.Task;

import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;

public class TweetTask extends Task {

  private String status;

  public void execute() {

    try {

      TwitterFactory factory = new TwitterFactory();
      AccessToken accessToken = loadAccessToken();
      Twitter twitter = factory.getOAuthAuthorizedInstance(
          "consumer key", "consumer secret", accessToken);
      Status updatedStatus = twitter.updateStatus(status);
      System.out.println("Updated status to: "
          + updatedStatus.getText());

    } catch (TwitterException e) {

      e.printStackTrace();

    }

  }

  private AccessToken loadAccessToken() {

    String token = null;
    String tokenSecret = null;
    try {
      FileInputStream fileInputStream = new FileInputStream(
          "accessToken.txt");
      ObjectInputStream objectInputStream = new ObjectInputStream(
          fileInputStream);
      token = (String) objectInputStream.readObject();
      fileInputStream = new FileInputStream("accessTokenSecret.txt");
      objectInputStream = new ObjectInputStream(fileInputStream);
      tokenSecret = (String) objectInputStream.readObject();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return new AccessToken(token, tokenSecret);

  }

  public String getStatus() {
    return status;
  }

  public void setStatus(String status) {
    this.status = status;
  }

}

注意,清单 3清单 2 中的 main() 方法重构为任务的 execute() 方法。而且它重构 loadAccessToken() 方法的方式是,删除 static 修饰符,将其从类方法更改为实例方法。(如果您将令牌和令牌机密对象存留在建议文件之外的其他地方,那么要重用在 清单 2 中使用过的同一方法。编译 TweetTask.java。)

现在写入 Ant build.xml 文件,如清单 4 所示,以测试自定义的 TweetTask 类:

清单 4. build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project default="default" basedir=".">

  <taskdef name="tweet"
     classname="TweetTask"
     classpath="Twitter4J Jar file" />

  <target name="default">
  
    <tstamp>
      <format property="build.completed" 
        pattern="HH:mm:ss z dd-MM-yyyy" />
    </tstamp>
    <tweet status="build completed at ${build.completed}" />
    
  </target>
  
</project>

注意,从 Twitter4J 网站上下载的同一个 JAR 文件被添加到自定义任务的类路径。使用您下载的文件的名称替换 classpath 属性的值。如果您将 TweetTask.java 文件放在了一个程序包中,比如 mypackage.customtasks,那么 classname 属性必须将这一点反映出来。如果您遵循了本文的建议,将令牌和令牌机密对象序列化到本地文件,那么 token.txt 和 tokenSecret.txt 文件需要位于与该 build.xml 文件相同的目录中,才能顺利完成构建过程。

使用 Ant 执行 build.xml 文件。当执行完成时,以构建完成的时间和日期 检查 Twitter 帐户,以确认自定义任务正确地更新了帐户状态,,如图 2 所示:

图 2. Twitter 上的一个构建完成更新
Twitter 帐户构建完成更新的屏幕截图
Twitter 帐户构建完成更新的屏幕截图

现在项目的相关各方都可以关注 Twitter 提要,以便在构建完成时收到通知。

结束语

本文展示了如何使用 Twitter 中的 OAuth 机制来从一个客户端 Java 程序更新帐户状态。该技术无需开源项目中的构建工程师维护和更新某一构建任务的邮件列表 — 这反正也不在他们的职权范围内。开发人员、测试人员和任何对项目感兴趣的人员都可以订阅(或 “关注”)Twitter 帐户,以便在构建完成时收到即时通知。如果 twitter.com 遇到网络中断,或项目的帐户被泄露,那么邮件任务可用一种回退方法来警惕用户当前的构建状态。

Twitter 的使用率在过去 4 年中呈指数级上升,从而衍生了许多微博替代方案,比如 status.net、Jaiku 和 NotePub。本文的更新任务可以进一步利用这些服务,更新一个项目的 Facebook 状态。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=608301
ArticleTitle=在 Twitter 上更新项目的构建状态
publish-date=01172011