权记

一个关于我们生活点滴的网站,一个记录我们酸甜苦辣的日志。

Twitter发布了@anywhere 服务,用户可以通过添加几行代码,就可以把Twitter整个平台功能嵌入你的网站。

怎么做呢?

推荐你看看下面这篇文章,由于内容较多,我就懒的翻译和转载了,大家自己看吧,提供code和截图,很容易理解。

6步使用Twitter的@Anywhere服务:http://net.tutsplus.com/tutorials/javascript-ajax/using-twitters-anywhere-service-in-6-steps/

不过说实话,没感觉到很方便,当然要是有人写个wordpress插件也还不错!

  • 0 Comments
  • Filed under: 技术
  • 本周主题:Java Net and XML Parser

    分两部分:一部Java Net(大部分来自Java Doc), 一部分XML Parser, 通过一个Twitter的例子来描述简单的使用情况。

    一、Java Net

    java.net 包的概述:

    java.net包大致可分为两部分:

    低级API – 用于处理以下抽象:

    • 地址,也就是网络标识符,如 IP 地址。
    • 套接字,也就是基本双向数据通信机制。
    • 接口,用于描述网络接口。

    高级API – 用于处理以下抽象:

    • URI,表示统一资源标识符。
    • URL,表示统一资源定位符。
    • 连接,表示到 URL 所指向资源的连接。

    地址

    在整个 java.net API 中,地址或者用作主机标识符或者用作套接字端点标识符。

    InetAddress 类是表示 IP(Internet 协议)地址的抽象,它拥有两个子类:

    但是,在大多数情况下,不必直接处理子类,因为 InetAddress 抽象应该覆盖大多数必需的功能。

    套接字

    套接字是在网络上建立机器之间的通信链接的方法。java.net 包提供 4 种套接字:

    • Socket 是 TCP 客户端 API,通常用于将 (java.net.Socket.connect(SocketAddress)) 连接到远程主机。
    • ServerSocket 是 TCP 服务器 API,通常接受 (java.net.ServerSocket.accept) 源于客户端套接字的连接。
    • DatagramSocket 是 UDP 端点 API,用于发送和接收 java.net.DatagramPackets
    • MulticastSocket 是 DatagramSocket 的子类,在处理多播组时使用。

    使用 TCP 套接字的发送和接收操作需要借助 InputStream 和 OutputStream 来完成,这两者是通过 java.net.Socket.getInputStream 和 java.net.Socket.getOutputStream 方法获取的。

    高级API

    java.net 包中的许多类可以提供更加高级的抽象,允许方便地访问网络上的资源。这些类为:

    • URI 是表示在 RFC 2396 中指定的统一资料标识符的类。顾名思义,它只是一个标识符,不直接提供访问资源的方法。
    • URL 是表示统一资源定位符的类,它既是 URI 的旧式概念又是访问资源的方法。
    • URLConnection 是根据 URL 创建的,是用于访问 URL 所指向资源的通信链接。此抽象类将大多数工作委托给底层协议处理程序,如 http 或 ftp。
    • HttpURLConnection 是 URLConnection 的子类,提供一些特定于 HTTP 协议的附加功能。

    建议的用法是使用 URI 指定资源,然后在访问资源时将其转换为 URL。从该 URL 可以获取 URLConnection 以进行良好控制,也可以直接获取 InputStream。

    下面是一个示例:

    URI uri = new URI("http://java.sun.com/");
    URL url = uri.toURL();
    InputStream in = url.openStream();
    

    URLConnection 和 HttpURLConnection 的使用:

    抽象类 URLConnection 是所有表示应用程序与 URL 之间通信链路的类的超类。该类的实例可以用来对由 URL 引用的资源进行读取和写入操作

    HttpURLConnection 仅是支持了 HTTP 特定功能的 URLConnection

    1. 通过调用 URL 的 openConnection 方法产生一连接对象。
    2. 操纵设置参数和一般请求属性。
    3. 使用 connect 方法,实现对远程对象的实际连接。
    4. 远程对象一旦可用,就可以访问远程对象的域和内容。

    通过如下的方法修改设置参数:

    • setAllowUserInteraction
    • setDoInput
    • setDoOutput
    • setIfModifiedSince
    • setUseCaches

    通过使用如下的方法修改一般请求属性:

    • setRequestProperty

    AllowUserInteractionUseCaches 参数的缺省值可以通过使用 setDefaultAllowUserInteractionsetDefaultUseCaches 方法来设置。 而一般请求属性的缺省值可以通过方法 setDefaultRequestProperty 来设置 。

    上面的每个 set 方法都有相应 get 方法来获取参数和一般请求属性的值。可用的特定参数和一般请求属性是特定于协议的。

    对远程对象的连接完成后,用下列方法访问报头域和内容:

    • getContent
    • getHeaderField
    • getInputStream
    • getOutputStream

    确认报头域能够被频繁访问。方法:

    • getContentEncoding
    • getContentLength
    • getContentType
    • getDate
    • getExpiration
    • getLastModified

    提供对这些域的便利访问。 getContentType 方法由 getContent 方法调用以确定远程对象的类型; 子类来覆盖方法 getContentType 可能是有利的。

    下面是一个通过Twitter API去获信息的demo:

            URL url = new URL("http://t.bbercn.com/statuses/user_timeline.xml");
            String username = (String) params[0];
            String password = (String) params[1];
            String user_paw = username + ":" + password;
            String encoding = "Basic " + Base64Converter.encode(user_paw.getBytes());
            URLConnection uc = url.openConnection();
            uc.setRequestProperty("Authorization", encoding);
            InputStream content = (InputStream) uc.getInputStream();
    

    剩下的工作就是对InputStream 进行本地处理,Twitter返回的是XML,那么我们下面就对XML进行解析,获取我们要获取的信息。

    XML Parser

    关于 xml 解析的工具有很多,具体可以看这里:http://www.open-open.com/31.htm 罗列的一些。

    下面通过Commons-Digester来处理上面我们获取的InputStream

    public class ParseTools {
    
        private List statuses = new ArrayList<Status>();
    
        public List<Status> getStatuses() {
            Collections.sort(statuses);
            return statuses;
        }
    
        public void parseStatus(InputStream stream) throws IOException, SAXException {
            Digester digester = new Digester();
            digester.setValidating(false);
            digester.push(this);
    
            digester.addObjectCreate("statuses/status", Status.class);
            //status
            digester.addBeanPropertySetter("statuses/status/id");
            digester.addBeanPropertySetter("statuses/status/created_at");
            digester.addBeanPropertySetter("statuses/status/text");
            digester.addBeanPropertySetter("statuses/status/source");
            digester.addBeanPropertySetter("statuses/status/truncated");
            digester.addBeanPropertySetter("statuses/status/in_reply_to_status_id");
            digester.addBeanPropertySetter("statuses/status/in_reply_to_user_id");
            digester.addBeanPropertySetter("statuses/status/favorited");
            digester.addBeanPropertySetter("statuses/status/in_reply_to_screen_name");
            //user
            digester.addObjectCreate("statuses/status/user", User.class);
            digester.addBeanPropertySetter("statuses/status/user/id");
            digester.addBeanPropertySetter("statuses/status/user/name");
            digester.addBeanPropertySetter("statuses/status/user/screen_name");
            digester.addBeanPropertySetter("statuses/status/user/description");
            digester.addBeanPropertySetter("statuses/status/user/location");
            digester.addBeanPropertySetter("statuses/status/user/profile_image_url");
            digester.addBeanPropertySetter("statuses/status/user/url");
            digester.addBeanPropertySetter("statuses/status/user/isProtected");
            digester.addBeanPropertySetter("statuses/status/user/followers_count");
            digester.addBeanPropertySetter("statuses/status/user/friends_count");
            digester.addSetNext("statuses/status/user", "setUser");
            digester.addSetNext("statuses/status", "addStatuses", Status.class.getName());
            digester.parse(stream);
        }
    
        public void addStatuses(Status status) {
            Status d = new Status();
            d.setId(status.getId());
            d.setCreated_at(status.getCreated_at());
            d.setFavorited(status.isFavorited());
            d.setText(status.getText());
            d.setSource(status.getSource());
            d.setTruncated(status.isTruncated());
            d.setIn_reply_to_screen_name(status.getIn_reply_to_screen_name());
            d.setIn_reply_to_status_id(status.getIn_reply_to_status_id());
            d.setIn_reply_to_user_id(status.getIn_reply_to_user_id());
            d.setUser(status.getUser());
            getStatuses().add(d);
        }
    }
    

    近期琐事

    Twitter

    最近Twitter成为我获取信息的主要途径,几乎每天醒来第一件事情是躺床上翻推。

    黑莓

    换黑莓了,本想换android的手机,无奈太贵,忍住诱惑,先玩玩黑莓过些时间再折腾android。

    学车

    囡囡开始学车了,法培已考,办理了上车手续,不过元旦前可能比较小,海淀驾校太火爆了,约车难。

    项目

    年底前又开始了一个紧时间的项目,春节前要完成M1并上线,加上手上的未完项目要同时进行,这段时间几乎是掐着时间算。

    出游

    本来计划1月份出游的,折腾了几天,最终还是计划赶不上变化,黄了,只能安排到年后了。

  • 2 Comments
  • Filed under: 随记
  • 关了一周Twitter总览

    本来一直想偷个懒通过Twitter来更新Blog, 为此还专门将服务器迁到国外, 不过最近发现还是别这样搞了.

    Twitter上的信息有些杂, 搞得博客有点乱糟糟的.

    于是想想还是关了, 不通过Twitter更新了, 目前就留右侧的Twitter实时信息.

    以后还是多写点实在的, 技术类的日志了!

  • 0 Comments
  • Filed under: 随记
  • 关于Twitter这里就不多介绍了, 基本上能看到这篇文章的都用过的, 当然不知道也没关系, Google一下.由于Twitter的API 开放性, 所以其应用现在可以用多如牛毛也描述了, 各种各样的, 本文基于Java, 通过twitter API来获取twitter消息, 并以PDF的形式进行归档.准备工具:

    1. HttpClient 3.x 库文件, 通过此库来从Twitter API获取消息, 同时需要 Comomons LoggingCodec 两个库文件. 请自行下载.

    2. dom4j 库文件, 通过此库来解析Twitter的消息XML和获取中间的数据.

    3. iText 库文件, 用来创建PDF文档.


    每个Twitter应用都是基于twitter API来实现的, 有关Twitter API的使用可以看这里: Twitter API wiki(官方的,需要翻墙), 这里我们假设你只对获取数据感兴趣, 同时想以某种方式显示他们, 至于其他功能, 相信通过本教程的介绍, 你可以自己去钻研了.从TweetConsumer开始:

    import org.dom4j.Element;
    public interface TweetConsumer {
        public String tweet(Element element) throws TweetException;
    }
    

    Twitter API提供XML, JSON 和RSS几种格式, 这里我们选择XML格式, 通过dom4j来解析, 上面是一个叫做TweetConsumer的借口, 作用就是来处理消息的, 其实现在后面会讲到.

    自定义一个消息异常, 当出现问题时抛出:

    public class TweetException extends Exception {
        private static final long serialVersionUID = 7577136074623618615L;
        public TweetException(Exception e) {
            super(e);
        }
    }
    

    接下来是本文的重点, 通过Twitter API去获取消息:

    public class TweetProducer {
        protected HttpClient client;
        protected TweetConsumer consumer;
    
        public void createClient(String username, String password) {
            client = new HttpClient();
            client.getState().setCredentials(
                //由于GFW的原因, 直连twitter肯定是不行了, 建议使用第三方API,比如我的GAE应用:xiao-quan.appspot.com
                new AuthScope("twitter.com", 80, AuthScope.ANY_REALM),
                new UsernamePasswordCredentials(username, password));
            client.getParams().setAuthenticationPreemptive(true);
        }
    
        public void setConsumer(TweetConsumer consumer) {
            this.consumer = consumer;
        }
    
        public String execute(String since_id) throws TweetException {
            String id = null;
            String tmp;
            //由于GFW的原因, 直连twitter肯定是不行了, 建议使用第三方API,比如我的GAE应用:http://xiao-quan.appspot.com/api/statuses/friends_timeline.xml
            GetMethod get = new GetMethod("http://twitter.com/statuses/friends_timeline.xml");
            if (since_id != null &amp;&amp; since_id.length() > 0) {
                get.setQueryString("?count=200&amp;since_id=" + since_id);
            }
            else {
                get.setQueryString("?count=200");
            }
            get.setDoAuthentication(true);
            try {
                client.executeMethod(get);
                SAXReader reader = new SAXReader();
                Document document = reader.read(get.getResponseBodyAsStream());
                Element root = document.getRootElement();
                for (Iterator i = root.elementIterator(); i.hasNext(); ) {
                    tmp = consumer.tweet((Element)i.next());
                    if (id == null) id = tmp;
                }
            } catch (HttpException e) {
                throw new TweetException(e);
            } catch (IOException e) {
                throw new TweetException(e);
            } catch (DocumentException e) {
                throw new TweetException(e);
            } finally {
                get.releaseConnection();
            }
            return id;
        }
    }
    

    TweetProducer 里面有三个方法:
    createClient(), 创建HttpClient对象, 通过HttpClient来连接Twitter API, 由于Twitter使用了HTTP Basic Authentication,
    所以你需要提供你的twitter用户名和密码来登录, 获取你的朋友们的消息.
    setConsumer(), 传一个TweetConsumer接口给TweetProducer, 我们有多种输出方案, 当然这里要通用点.
    excute(), 这是才是做事情的地方, 获取哪个消息ID以后的消息, 返回一个消息ID.

    获取消息:

    GetMethod get = new GetMethod("http://twitter.com/statuses/friends_timeline.xml");
    

    Twitter API默认获取的消息条数是20, 最大200, 你可以自己定义获取的数量, 同时也可以通过一些参数来指定获取第几页的消息等, 具体使用参考Twitter API WIKI上的定义.

    例如, 获取消息ID为since_id以后的200个消息:

    get.setQueryString("?count=200&since_id=" + since_id);
    

    设置完查询条件后, 执行:

    client.executeMethod(get);
    

    将返回的信息传给SAXReader,以便获取消息root:

    SAXReader reader = new SAXReader();
    Document document = reader.read(get.getResponseBodyAsStream());
    Element root = document.getRootElement();
    

    最后迭代每个带 标签的Element:

    for (Iterator i = root.elementIterator(); i.hasNext(); ) {
        tmp = consumer.tweet((Element)i.next());
        if (id == null) id = tmp;
    }
    

    由于Twitter的消息列表是按时间降序排列, 那么第一个消息就是最新的消息了, 这个消息ID就是excute()要返回的String.

    获取到消息后, 接下来我们就输出消息:

    import org.dom4j.Element;
    
    public class SystemOutTweets implements TweetConsumer {
        public String tweet(Element tweet) {
            System.out.println(tweet.asXML());
            return tweet.elementText("id");
        }
    
        public static void main(String[] args) throws TweetException {
            TweetProducer t = new TweetProducer();
            t.createClient("gavinquan", "123456");
            t.setConsumer(new SystemOutTweets());
            System.out.println("Start tweeting:");
            String id = t.execute(null);
            System.out.println("The End; last tweet: " + id);
        }
    }
    

    将上面的介绍的代码整理好, 并执行, 就会在控制台输出, XML格式的消息列表, 如下是其中的一个片段:

    <status>
      <created_at>Wed Sep 09 06:07:04 +0000 2009</created_at>
      <id>3858278078</id>
      <text>gmail服务器错误了?</text>
      <source><a href="http://twitterfox.net/" rel="nofollow">TwitterFox</a></source>
      <truncated>false</truncated>
      <in_reply_to_status_id/>
      <in_reply_to_user_id/>
      <favorited>false</favorited>
      <in_reply_to_screen_name/>
      <user>
        <id>10415752</id>
        <name>hugege</name>
        <screen_name>hugege</screen_name>
        <location>Shantou Guangdong China </location>
        <description>www.gegehost.com 博客主机服务</description>
        <profile_image_url>http://a1.twimg.com/profile_images/329549706/hugegelogo_normal.jpg</profile_image_url>
        <url>http://hugege.com/</url>
        <protected>false</protected>
        <followers_count>1607</followers_count>
        <profile_background_color>C6E2EE</profile_background_color>
        <profile_text_color>663B12</profile_text_color>
        <profile_link_color>1F98C7</profile_link_color>
        <profile_sidebar_fill_color>DAECF4</profile_sidebar_fill_color>
        <profile_sidebar_border_color>C6E2EE</profile_sidebar_border_color>
        <friends_count>1335</friends_count>
        <created_at>Tue Nov 20 16:00:58 +0000 2007</created_at>
        <favourites_count>3</favourites_count>
        <utc_offset>28800</utc_offset>
        <time_zone>Beijing</time_zone>
        <profile_background_image_url>http://s.twimg.com/a/1252448032/images/themes/theme2/bg.gif</profile_background_image_url>
        <profile_background_tile>false</profile_background_tile>
        <statuses_count>1540</statuses_count>
        <notifications>false</notifications>
        <verified>false</verified>
        <following>false</following>
      </user>
    </status>
    

    同时会输出, 你的最后一个消息的消息ID:

    The End; last tweet: 3858279409
    

    当然你也可以直接通过这个ID, 来去获取你的该ID以后的消息:

    t.execute("3858279409");
    

    OK, 到这里, 我们的任务已经过去一半多了, 获取twitter消息, 那么接下来的内容, 我们来看如何将消息以PDF的形式进行归档.

    通过iText生成PDF, 需要5步, 请看下面的Demo :

    import java.io.FileOutputStream;
    import com.lowagie.text.Document;
    import com.lowagie.text.Paragraph;
    import com.lowagie.text.pdf.PdfWriter;
    
    public class HelloWorld {
        public static void main(String args[]) {
            try {
                // step 1: create a Document object
                Document document = new Document();
                // step 2: connect the Document with an OutputStream using a PdfWriter
                PdfWriter.getInstance(document, new FileOutputStream("hello.pdf"));
                // step 3: open the document
    
                document.open();
                // step 4: add content
                document.add(new Paragraph("Hello World"));
                // step 5: close the document
                document.close();
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }
    

    大体工作流程就如上面所说,相信都可以看懂, OK, 下面我就直接给出代码, 重点地方提一下.

    PDFWriter类

    import com.lowagie.text.Document;
    import com.lowagie.text.DocumentException;
    import com.lowagie.text.Element;
    import com.lowagie.text.Font;
    import com.lowagie.text.PageSize;
    import com.lowagie.text.Phrase;
    import com.lowagie.text.Rectangle;
    import com.lowagie.text.pdf.BaseFont;
    import com.lowagie.text.pdf.PdfPTable;
    import com.lowagie.text.pdf.PdfWriter;
    import com.quanlei.exception.TweetException;
    import com.quanlei.twitter.TweetConsumer;
    import com.quanlei.twitter.TweetProducer;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class PdfTwitter implements TweetConsumer {
    
        private int counter = 0;
        private Document document;
        private Font small_white = new Font(Font.HELVETICA, 8, Font.BOLD);
        private Font bold = new Font(Font.HELVETICA, 11, Font.BOLD);
        private BaseFont bfChinese = BaseFont.createFont("STSong-Light",
                "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        private Font FontChinese = new Font(bfChinese, 12, Font.NORMAL);
        private SimpleDateFormat simpleDateTimeFormat =
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        public PdfTwitter() throws DocumentException, IOException {
            document = new Document(PageSize.A4, 72, 36, 36, 36);
    
            PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(
                    "world.pdf"));
            writer.setPageEvent(new TwitterPage());
            document.open();
        }
    
        public void close() {
            document.close();
        }
    
        public String tweet(org.dom4j.Element tweet) throws TweetException {
            try {
                org.dom4j.Element user = tweet.element("user");
                PdfPTable table = new PdfPTable(new float[]{1, 10, 12});
                table.setTableEvent(new TableBackground());
    
                table.setWidthPercentage(100);
                table.setSpacingBefore(8);
                table.getDefaultCell().setPadding(5);
                table.getDefaultCell().setBorder(Rectangle.NO_BORDER);
                table.addCell("");
                table.addCell(new Phrase(user.elementText("screen_name"),
                        small_white));
                table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);
    
                table.addCell(new Phrase(simpleDateTimeFormat.format(
                        new Date(tweet.elementText("created_at"))),
                        small_white));
                table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT);
                table.getDefaultCell().setFixedHeight(38);
                table.addCell(new Phrase(String.valueOf(++counter), small_white));
                table.getDefaultCell().setColspan(2);
                Phrase p = new Phrase();
                p.add(new Phrase(user.elementText("name"), FontChinese));
                p.add(new Phrase(": ", bold));
                p.add(new Phrase(tweet.elementText("text"), FontChinese));
                table.addCell(p);
                document.add(table);
                return tweet.elementText("id");
            } catch (DocumentException e) {
                e.printStackTrace();
                throw new TweetException(e);
            } catch (IOException e) {
                e.printStackTrace();
                throw new TweetException(e);
            }
        }
    
        public static void main(String[] args)
                throws DocumentException, org.dom4j.DocumentException,
                FileNotFoundException, IOException, TweetException {
            PdfTwitter pdf = new PdfTwitter();
            TweetProducer t = new TweetProducer();
            t.createClient("gavinquan", "123456");
            t.setConsumer(pdf);
            t.execute(null);
            pdf.close();
        }
    }
    

    上面的类已经可以输出PDF了, 但是不是很美观, 加点修饰, 每个Twitter消息一个框, 这是背景:

    import java.io.IOException;
    import com.lowagie.text.DocumentException;
    import com.lowagie.text.Element;
    import com.lowagie.text.pdf.BaseFont;
    import com.lowagie.text.pdf.PdfContentByte;
    import com.lowagie.text.pdf.PdfPTable;
    import com.lowagie.text.pdf.PdfPTableEvent;
    
    public class TableBackground implements PdfPTableEvent {
    
        protected BaseFont bf;
    
        public TableBackground() throws DocumentException, IOException {
            bf = BaseFont.createFont(BaseFont.TIMES_BOLDITALIC, BaseFont.WINANSI,
                    BaseFont.NOT_EMBEDDED);
        }
    
        public void tableLayout(PdfPTable table, float[][] width, float[] height,
                int headerRows, int rowStart, PdfContentByte[] canvas) {
            PdfContentByte cb = canvas[PdfPTable.BACKGROUNDCANVAS];
            cb.saveState();
            cb.setRGBColorFill(0x9a, 0xe4, 0xe8);
            cb.roundRectangle(width[0][0], height[2], width[0][3] - width[0][0],
                    height[0] - height[2], 4);
            cb.roundRectangle(width[0][1], height[2] + 3,
                    width[0][3] - width[0][1] - 3, height[1] - height[2] - 6, 4);
            cb.eoFill();
            cb.beginText();
            cb.setRGBColorStroke(0x9a, 0xe4, 0xe8);
            cb.setFontAndSize(bf, 28);
            cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_STROKE);
            cb.showTextAligned(Element.ALIGN_LEFT, "\"", width[0][1],
                    height[1] - 10, 0);
            cb.endText();
            cb.restoreState();
        }
    }
    

    下面再画蛇添足, 加点外围修饰, 每个PDF页面左侧加上文字, 算是起点宣传作用:

    import java.io.IOException;
    import com.lowagie.text.Document;
    import com.lowagie.text.DocumentException;
    import com.lowagie.text.Element;
    import com.lowagie.text.pdf.BaseFont;
    import com.lowagie.text.pdf.PdfContentByte;
    import com.lowagie.text.pdf.PdfPageEventHelper;
    import com.lowagie.text.pdf.PdfWriter;
    
    public class TwitterPage extends PdfPageEventHelper {
    
        protected BaseFont bf;
    
        public TwitterPage() throws DocumentException, IOException {
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",
                    BaseFont.NOT_EMBEDDED);
        }
    
        @Override
        public void onEndPage(PdfWriter writer, Document document) {
            PdfContentByte cb = writer.getDirectContent();
            cb.saveState();
            cb.beginText();
            cb.setRGBColorStroke(0x9a, 0xe4, 0xe8);
            cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_STROKE);
            cb.setFontAndSize(bf, 18);
            cb.showTextAligned(Element.ALIGN_RIGHT, "www.quanlei.com", 68, 806, 90);
            cb.endText();
            cb.restoreState();
        }
    }
    

    最后配个截图, 为本文画上句号, 注, 由于图缩略了, 效果有点虚了:

    PDF效果图

    英文原版出处: http://www.javaworld.com/javaworld/jw-03-2009/jw-03-write-your-own-twitter-app.html , 本文抽取了原英文的重点, 并配以文字介绍,加工而成.

    一周 Twitter 总览

    • 闯关东中篇: 你把沈阳市长想的太好了, 他们不仅想着剿灭共军, 还想着和他的七大姑马大姨怎么升官发财呢! #
    • 猥琐男用精液冒充洗面奶示爱 -http://bit.ly/16isbb
      无语了! #
    • 今天办理了一张西坝河英特健身的会员卡, 15元不限时健身+游泳+台球+兵乓球, 不记名次卡, 有北京北三环附近的推友想一起健身的欢迎联系. #
    • 宁波一超市出现红十字会捐赠物 红十字会称失误 -http://news.163.com/09/0906/14/5IHKDHTV00011229.html //妈的 #
    • RT: @tualatrix: 一位上年纪的大妈在酒店休息室与众游伴聊天,她说:现在的共产党,说说嘛共产党共产党,实际上连过去的国民党也不如。 #
    • 晕, 绝命派对的主演有日本的小泽玛利亚, 怪不得看的那么眼熟! 哈哈! #
    • 正在看:绝命派对, 我靠这电影也太变态了, 虽然有些地方可以看出是做的, 但还是很恐怖! #
    • 1000个Twitter应用以及服务, 封吧, 封吧, 除非把整个互联网都封了. – http://bit.ly/PQtXA #
    • 天气开始变冷了, 晚上盖被子! #
    • 闯关东中篇: 这抗战胜利了, 还能让国民党一家做主吗? 一党独裁, 遍地是灾. //我靠, 看到这里实在是看不下去了! #
    • 9月3日Ubuntu 9.10 Alpha 5 如期发布, beta版10月1日, 10月29日正式版, 期待啊! #
    • 上个周末逛了2天, 今天又是逛了一天! #
    • 满载而归,晚上淘宝继续! #
    • 逛街累啊! #
    • RT: @williamlong: 熟悉php的,邀请参加dabr中文化项目开发,开发目标,1、优化dabr的WEB版界面,使其界面和Twitter网站一样易用;2、中文化dabr; 目前项目地址 http://tinyurl.com/lo6dgb #
    • 闯关东中篇抗日战争完后就没法看了! #
    • RT: @scavin: 这事很诡异,为何官方 dabr 一直安全,而国产山寨的挂了那么多。。。// 那列表有一半挂了! #
    • 今天有点飘飘然,还是忙点好,心里踏实! #
    • 腾讯的QQ空间在Firefox支持不好就支持不好呗, 用Firefox打开就是:尊敬的用户,由于网络繁忙等原因,您当前看到的模块内容可能不是最新. 用IE就没事. #
    • 时刻保持bug list 为空, 看着就舒服. #
    • 某项目完工后尚有余款,市委常委会开会研究是用来改善小学的条件还是监狱的条件,意见分歧较大。最后一老常委一语定乾坤:这辈子你们这班常委还有机会进小学吗?顿时沉默…….有的擦汗,有的低头喝茶。过了一瞬间意见统一:改善监狱环境。 #
    • 今早网速慢成乌龟了 #
    • 我装你, 是为了更好的卸载你. #
    • appspot.com 被墙了吗? 北京打不开 #
    • 最感人的“公车私用” – http://blog.qq.com/qzone/622006444/1251896471.htm #
    • yeah, 创建了自己的twitter API, 不怕是不是登不上twitter了 #
    • 妈的, GFW封android.com又是为何事呢? #
    • Firefox内存占用1,123,456K #
    • 这项目做的, 每次去客户那里都能带些新需求回来. #
    • 整理下GReader里面的RSS, 去掉一些不常看的. #
    • 撞到即将关闭的地铁门上,门弹了回去! #
    • 差一步就可以冲进地铁了,放弃了,算了,小命要紧,回家不差那两三分钟! #
    • 公司楼下一卖书的论斤卖,六块一斤! #
    • RT: @daodao: 百度还不够狠,只是关闭了饭否贴吧和饭否词条而已。至少 2009年9月2日17:39:24 还是可以搜出饭否相关的信息。//看来饭否名气在外啊 #
    • RT @riku: RT @shizhao 豆瓣饭否小组解散,听说是北京新闻办下的令 #
    • RT: @tualatrix: 啊~我有竟争对手了!http://allmyapps.com/ //你应该软硬兼施 #
    • 为了确保国庆期间邮件快递的安全,从即日起,EMS对液体类的邮件不予收寄。 #
    • 希望下次不要接到这样的项目单子了. #
    • RT: @jason5ng32: 中国电信也太幽默了吧? http://www.ctmarket.cn/ //好歹一大公司, 只能说天翼空间项目组的人做事情太不专业了. #
    • 吃粥喝包子 #
    • 不喜欢那些将别人劳动果实据为己有的人, 还有其行为. #
    • 这个月手上有2本书要看, 一本技术相关, 一本非技术励志. #
    • 多实实在在做事情, 少空想! #
    • RT: @zuola: #tips twitterfox变种,支持自定义twitter api proxy,推特自此无国界 http://bit.ly/twitfox #
    • 从twhirl转八哥了, twhirl的那个appspot的API又不能用了. #
    • 合肥联通推出15元WCDMA校园套餐 #
    • G1现在啥价格?还值得买吗? #
    • Powered by reCAPTCHA 的验证码真不好用. #
    • 这几天公司网络质量巨差劲,不知道谁又在download. #
    • 一同事说话, 满口京腔味, 听起来怎么那么不舒服呢. #
    • RT @jason5ng32: 吃饭回来就看到悲剧发生了,爱早报被和谐了 http://www.izaobao.com/ ||操, 经常关注的几个都关了啊. #
    • 早上上班第一件事情, 检查服务器运行正常, 接着看Google Reader和Twitter #
    • @panweizeng 呵呵, 听说过. in reply to panweizeng #
    • Bug List里面还有两个Bug, 上午尽快搞定. #
    • 骤然临之而不惊, 无故加之而不怒. #
    • 晚上看了2小时的书, 难得啊!
      PS:囡囡说想用Twitter, 她说Blog上都是我说的! 嘿嘿 #
    • RT @jason5ng32: 鲜果正在被维护,国庆将近,为营造全国上下、互联网内外一片和谐的气氛,很多网站正在或将会被维护。 http://xianguo.com/ ||唉! #
    • 一直没想明白为啥Twitter会有日文语言支持, 貌似日本用户不多啊, 难道twitter团队里面有日本成员吗? #
    • Resending @chenjunen: 还是用 twhirl 上Twitter来的爽,大家低调点推: http://twitpic.com/fy9d4/full //呵呵,谢谢, 搞定了! #
    • 一到下午5点左右脑子反应就慢了, 发晕! #
    • 今天听听左边说的, 那就这么做, 明天听听右边说的, 再又那么改, 一个破项目需求更改还有完没完了. #
    • 总算知道为什么苹果官方说升级雪豹后硬盘容量会增加了 – http://www.cnbeta.com/articles/92269.htm #
    • 低调点! 低调点! #
  • 0 Comments
  • Filed under: 随记
  • 这里介绍下如何搭建自己用的Twitter API, 为什么叫自己用的呢? 因为我们在这里用Google App Engine 是免费帐户, 有流量限制, 如果你公布出来, 肯定过不了几个小时就死翘翘了.

    我们用的是BirdNest这个开源twitter api proxy, 可以来这里下载源码: http://code.google.com/p/birdnest/, 通过SVN来下载下来. SVN地址: http://birdnest.googlecode.com/svn/branches/gae 是braches里面的, 之前用trunk下面的不能用, 也有可能可以用, 可以自己试试, 我用的是braches的.

    OK, 下载下来后, 我们开始一步步架构:

    第一步: 注册Google App Engine, 地址 http://appengine.google.com/. 如果你有google账号, 很快就建了.

    第二步: 建立Application, 如下图:

    step2.png

    第三步: 给自己的Application设置一个可用的唯一ID(就是上图的Application Identiter那里要填写的), 以后就用 yourid.appspot.com来访问你的app.

    第四步: 下载上传工具, 这里提供一个第三方Google App Engine上传工具, 避免下载App Engine 的SDK和安装Python. 下载地址: http://code.google.com/p/sdapp/downloads/list 是里面那个SDUpload, 具体上传方法里面有介绍.

    第五步: 修改你下载的BirdNest源码文件里面的app.yaml文件, 将第一行的Application:nest 中的nest修改为你自己建立的id(就是刚申请的Application Identiter).

    第六步: 将源码文件夹, 复制到SDUpload所在文件夹, 按照介绍上传即可.


    PS: 如果上传的代码有问题, 不能用, 确定好可以用的源码, 将app.yaml里面的version修改为其他整数, 比如5, 然后再上传, 在App Engine里面找到Administration – Versions, 将你上传的最新的版本设置为默认的版本, 其他版本可以删除.

    本文内容基于:http://www.ifanr.com/2778 , 做了点修改.

  • 0 Comments
  • Filed under: 技术
  • 一周 Twitter 总览

    • 遇上大雨,还要等半个小时才能回家! #
    • 有人知道如何安装下面的补丁吗? 始终无法安装.
      微软名称:KB956572
      安全公告号:MS09-012
      补丁包大小:4.41MB
      漏洞影响:现已确认存在一个安全问题,攻击者可能会利用此问题危及系统的安全并获取对该系统的控制权 #
    • @jason5ng32 强烈要求分享学生3G套餐的具体情况, 谢谢 in reply to jason5ng32 #
    • 刚网上看有网友说Google搜"许志永", 刚无聊百度搜下,结果显示:搜索结果可能不符合相关法律法规和政策,未予显示。不过,不过, 右侧栏显示:给许志永传情 — 祝福、表白、道歉、许愿…百度传情帮您把对他或她的心意放在这里,在千万人面前展现! #
    • 上午10点多出去, 刚到家, 累的半死! #
    • 去一朋友家转了转,收获不小! #
    • 哥呼出的是气,吸进去的菌 #
    • 刚在和平门的南新华街被谷歌地图导航到了South America #
    • 2号线今日前门和和平门站封站 #
    • 五号线司机怎么老急刹车呢?刚又踩别人了 #
    • http://twitpic.com/fnh3whttp://bit.ly/JT98r #
    • http://twitpic.com/fnh3w – 这私家车主真爱车,全副武装! #
    • 接着记者致电王家沟乡乡长刘海红,他说“网上反映情况全是假的,造谣”–http://bit.ly/t3Mjl 唉,现在的政府官员, 说话没人信 #
    • 迷糊着起来翻看Twitter #
    • 亲爱的会员,JavaEye网站正在更新功能, 请您耐心等待5秒钟,网站就可以恢复正常访问了. //呵呵!5秒钟 #
    • 方向加运气,出地铁就是考研院! #
    • 周五一过五点就开始没多少心思干活了! #
    • 好久没打台球了, 想打台球. #
    • 刚才只是幻觉, 其实我用的是AutoProxy+Tor #
    • Twitter被解禁了吗? 我确认我刚才直接打开了 #
    • 最近一直在用大波, 刚刚发现我的twitterfox已经很久不动换了. #
    • Orascom Telecom为何能在朝鲜发展移动业务, 而其他公司不可以呢? #
    • Google Reader这会特特特别慢, 经常就打不开了. #
    • 要开始严格执行减肥计划, 少吃多餐, 加强锻炼. #
    • 胳膊现在很恐怖, 在蜕皮ing. #
    • 两个消息: 大傻走了;缅甸难民入住云南. #
    • 最近QQ校友活动蛮频繁的, 是不是冒个消息. #
    • @young_yang http://www.zoho.com/ in reply to young_yang #
    • 哎, 以后忙的时候不能开Twitter, 不一会儿又走神好一会. #
    • 推特中文圈 用来不错! #
    • @young_yang 打开都是乱码, 需要手动江浏览器编码调到GB2312才能看, 还是把编码改成UTF8吧,通用! in reply to young_yang #
    • 报纸上说中国国防部网站技术先进, 网站首页代码采用了国际上通用的div+css技术,保证了网站的访问速度,这在我国其他政府网站中是不多见的。 #
    • 今天七夕, 有另一半的都早点回家啊! #
    • Dropbox最近老把Windows Explorer搞挂! #
    • RT @robbinfan: JavaEye又被拔线了,这次估计要长久的告别大家了,我没什么可说的了。一个纯技术网站都无法在中国互联网生存下来。//无语了! #
    • JavaEye难道被和谐了吗? 打开出现"载入页面时到服务器的连接被重置。" #
    • @panweizeng 三次太少了,如果三次可以杀了,我们在这个项目已经死了N次了. in reply to panweizeng #
    • 天, 玩聚SR – http://www.ju690.com/ 也被和谐了! 看来最近动静不小. #
    • 国外访问依旧很慢 #
  • 0 Comments
  • Filed under: 随记
  • 一周 Twitter 总览

    • 感冒重了 #
    • 重庆咋那么多黑帮呢? #
    • 出发去大理,三个小时路程,有时间睡觉了! #
    • RT @guao: 谷奥( http://google.org.cn ) : Windows 版 Chrome Dev 升级 4.0.201.1,增加书签同步功能! http://bit.ly/PzunT #
    • 昨晚十点到了大理,安顿下来后去吃夜宵。 #
    • 上船游洱海 #
    • 品大理三道茶,看茶花姑娘的歌舞表演! #
    • 苍山洱海 #
    • 在大理享受了一把足疗,果然舒服! #
    • 中午辣椒太辣,结果不小心吃了三碗米饭,到现在还撑着,他们还叫我去吃晚饭,我不长肉谁长肉!! #
    • 睡觉,早起,明天返回昆明! #
    • 到北京啦,明天还得上班啊! #
    • 使用Google AD的博客网站, 在百度搜索会很靠后, 甚至搜不到, 而在Google搜索一般都在第一页. #
    • prototype 已成过去, 新的Web项目使用JQuery #
    • 感冒好像重了! #
    • 一北京人叹道:北京的地铁真挤,上周一孕妇被挤流产了。上海人不削的说:上海的地铁才叫挤,去年一少女被挤怀孕了。 #
    • 没有明确的任务时, 行动比较迟缓. #
    • 这年头借钱难,找人换钱更难! #
    • 便宜没好货, 好货不便宜! #
    • 围观淘宝的初夜爱爱, 很生活不色不色:http://bit.ly/Y3n4G #
    • 访问国外网站巨慢, 什么时候修好啊! 难不成国庆后?? #
    • 我的网站访问跟龟速一样! 实在是不知咋办, 刚换到国外主机!忍忍吧!过几天就好了 #
    • 貌似速度有点起色了! #
    • 考虑下个手机接触Android平台的 #
    • 淘宝买东西, 即使卖家是皇冠也要看他的好评留言. #
    • 今天跑了趟北京南三环南四环,果然和北三环北四环没法比,甚至和北五环都没法比! #
  • 0 Comments
  • Filed under: 随记
  • 一周 Twitter 总览

    • RT @wyws: 嫌政府信息慢,台湾网民建Twitter灾情中心发布情况,这是他们的twitter帐号 @taiwanfloods http://bit.ly/My4oY || 有必要建一个大陆的灾情通报 #
    • 今年的一个任务了结 #
    • New blog post: 服务器换了 http://bit.ly/68lQO #
    • 服务器升级完毕, 可以twitter了! #
    • 要精益求精, 一次性就做好, 现在的Bug就是回报, 唉! #
    • New blog post: 难得一见: Google Calendar Server Error http://bit.ly/14VGk7 #
    • @panweizeng 嗯! 我都一直没发现, 呵呵,谢谢提醒. in reply to panweizeng #
    • 为何采用android开源免费的操作系统的手机还是那么贵, 和采用WM系统一样贵. #
    • Bug, Bug, Bug #
    • 代码一定要写注释, 不然过段时间都不知道为什么要这么写. #
    • 大伙今天效率低了,
      Deleted: 1
      Added: 1
      Updated: 11 #
    • 刚联系一丽江的朋友, 丽江这会正旺季. #
    • 明天早上6点得起床 #
    • 到了昆明,从桥香园出来,没吃上米线! #
    • 一帮人都困了! #
    • 等待昆明去丽江的飞机,昆明没有想象的好,有点脏乱! #
    • 昆明机场跑道太少,飞机排队一个一个起飞! #
    • 谷歌地图导航不错,去了陌生的地方也能确定方位,不至于丢了! #
    • 快到了丽江,已经快十一点,飞机没有正点过! #
    • 天阴阴,雨蒙蒙! #
    • @hugege 啊,你买主机都不确定是否有ssh啊,呵呵! in reply to hugege #
    • 手机版的twitter没有回复,retwitter等功能,用dabr不错,手机支持很好,功能齐全! #
    • RT @hugege 又花了几百美元买主机啦,希望这次是真的有SSH的 || 买主机,找hugege #
    • 中国政府进入严重的不信任危机 #
    • 出国考察学习又和泰国人妖联系起来了,不知道的速去围观! #
    • RT @zhaojianfei: 《财经》网报道,美司法部称美公司向中石油、中海油员工行贿,http://bit.ly/3RO5aR #
    • 夫妇湖边合影留念 松鼠“抢镜”成主角(图) – http://bit.ly/1SzX2u #
    • RT @scavin: 在手机上用 Hahlo.com 不错 || 用dabr也不错! #
    • 周围一些朋友对近期的一些事件的认识是改革进程中必然会有一些牺牲者! #
    • 晚上不醉不归! #
    • 鼓动束河 #
    • 今天快累死了!丽江火把节人太多了,跟过年似的! #
    • 进入藏区! #
    • 惊险金沙江 #
    • 山里移动信号不好 #
    • 云层太多,看不到玉龙雪山的山顶! #
    • 徒步虎跳峡ing #
    • 从虎跳峡回来,基本上要挂了! #
  • 0 Comments
  • Filed under: 随记
  •