给javadoc增加uml类图

今天准备开始写点介绍我的contact-list jar包的文章,自然要放些直观的UML图看看,google了一下,找到了一个名叫apiviz的maven插件,可以为javadoc生成uml类图。用起来也很方便,首先安装graphviz包

sudo apt-get install graphviz

然后修改pom.xml文件

  1. 增加jboss的repository
<repositories>
    <repository>
        <id>jboss.releases</id>
        <name>JBosss releases</name>
        <url>http://repository.jboss.org/maven2 </url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
  1. 修改maven-java-doc的配置
<reporting>
    <plugins>
        <plugin>
            <groupid>org.apache.maven.plugins</groupid>
            <artifactid>maven-javadoc-plugin</artifactid>
            <version>2.5</version>
            <configuration>
                <doclet>org.jboss.apiviz.APIviz</doclet>
                <docletartifact>
                    <groupid>org.jboss.apiviz</groupid>
                    <artifactid>apiviz</artifactid>
                    <version>1.3.0.GA</version>
                </docletartifact>
                <usestandarddocletoptions>true</usestandarddocletoptions>
                <charset>UTF-8</charset>
                <encoding>UTF-8</encoding>
                <docencoding>UTF-8</docencoding>
                <breakiterator>true</breakiterator>
                <version>true</version>
                <author>true</author>
                <keywords>true</keywords>
                <additionalparam>
                    -sourceclasspath ${project.build.outputDirectory}
                </additionalparam>
            </configuration>
        </plugin>
    </plugins>
</reporting>

最后,在生成javadoc之前必须先compile

mvn compile javadoc:javadoc

再看看新生成的javadoc吧,头部都有一张类图的哦

Posted in  maven uml java


contact-list类库依赖包之commons-httpclient

commons-httpclient是apache下的一个开源项目,提供了一个纯java实现的http客户端,使用它可以很方便发送HTTP请求,接受HTTP应答,自动管理Cookie等等。

对于contact-list类库来说,需要使用的功能有,自动管理Cookie,设置HTTP头,发送HTTP请求,接受HTTP应答,转发HTTP重定向,还有输出HTTP请求/应答日志,下面对这些功能的实现进行解释:

  1. 自动管理Cookie
public EmailImporter(String email, String password, String encoding) {
    ......
    client = new HttpClient();
    client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
    client.getParams().setParameter("http.protocol.single-cookie-header", true);
}

其中将HttpClient的Cookie策略设置为CookiePolicy.BROWSER_COMPATIBILITY,即表示java client将按照浏览器的方式来自动处理Cookie。当然你也可以在运行过程中手动调整cookie,比如:

hotmail登录之前需要设置当前时间的Cookie:

client.getState().addCookie(new Cookie("login.live.com", "CkTst", "G" + new Date().getTime()));

不过,httpclient似乎没有提供删除cookie的功能,于是我增加了两个cookie管理的接口,一个是保留指定的cookies,一个是删除指定的cookies:

protected void retainCookies(String[] cookieNames) {
    Cookie[] cookies = client.getState().getCookies();
    ArrayList<Cookie> retainCookies = new ArrayList<Cookie>();
    for (Cookie cookie : cookies) {
        if (Arrays.binarySearch(cookieNames, cookie.getName()) >= 0) {
            retainCookies.add(cookie);
        }
    }
    client.getState().clearCookies();
    client.getState().addCookies(retainCookies.toArray(new Cookie[0]));
}

protected void removeCookies(String[] cookieNames) {
    Cookie[] cookies = client.getState().getCookies();
    ArrayList<Cookie> retainCookies = new ArrayList<Cookie>();
    for (Cookie cookie : cookies) {
        if (Arrays.binarySearch(cookieNames, cookie.getName())  0) {
            retainCookies.add(cookie);
        }
    }
    client.getState().clearCookies();
    client.getState().addCookies(retainCookies.toArray(new Cookie[0]));
}
  1. 设置HTTP头:

http头的设置,可以让邮件服务器认为是在和浏览器打交道,而避免被refuse的可能:

private void setHeaders(HttpMethod method) {
    method.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;");
    method.setRequestHeader("Accept-Language", "zh-cn");
    method.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3");
    method.setRequestHeader("Accept-Charset", encoding);
    method.setRequestHeader("Keep-Alive", "300");
    method.setRequestHeader("Connection", "Keep-Alive");
    method.setRequestHeader("Cache-Control", "no-cache");
}

另外,在GET和POST的时候设置referer值,以及在POST的时候设置Content-Type:

protected String doPost(String actionUrl, NameValuePair[] params, String referer) throws HttpException, IOException {
    ......
    method.setRequestHeader("Referer", referer);
    method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    ......
}
  1. 发送HTTP请求,接收HTTP应答。在contact-list中只使用了GET和POST请求,我也做了简单的封装:
protected String doGet(String url, String referer) throws HttpException, IOException {
    GetMethod method = new GetMethod(url);
    setHeaders(method);
    method.setRequestHeader("Referer", referer);
    // log request
    client.executeMethod(method);
    String responseStr = readInputStream(method.getResponseBodyAsStream());
    // log response
    method.releaseConnection();
    lastUrl = method.getURI().toString();
    return responseStr;
}

protected String doPost(String actionUrl, NameValuePair[] params, String referer) throws HttpException, IOException {
    PostMethod method = new PostMethod(actionUrl);
    setHeaders(method);
    method.setRequestHeader("Referer", referer);
    method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    method.setRequestBody(params);
    // log request
    client.executeMethod(method);
    String responseStr = readInputStream(method.getResponseBodyAsStream());
    // log response
    method.releaseConnection();
    if (method.getResponseHeader("Location") != null) {
        // do redirect
    } else {
        lastUrl = method.getURI().toString();
        return responseStr;
    }
}
  1. HTTP重定向,主要是两种,一种是根据HTTP头的Location
if (method.getResponseHeader("Location").getValue().startsWith("http")) {
    return doGet(method.getResponseHeader("Location").getValue());
} else {
    return doGet("http://" + getResponseHost(method) + method.getResponseHeader("Location").getValue());
}

另一种是根据javascript中的window.location.replace。

  1. 输出请求/应答日志,这个对调试非常重要:
private void logGetRequest(GetMethod method) throws URIException {
    logger.debug("do get request: " + method.getURI().toString());
    logger.debug("header:\n" + getHeadersStr(method.getRequestHeaders()));
    logger.debug("cookie:\n" + getCookieStr());
}

private void logGetResponse(GetMethod method, String responseStr) throws URIException {
    logger.debug("do get response: " + method.getURI().toString());
    logger.debug("header: \n" + getHeadersStr(method.getResponseHeaders()));
    logger.debug("body: \n" + responseStr);
}

private void logPostRequest(PostMethod method) throws URIException {
    logger.debug("do post request: " + method.getURI().toString());
    logger.debug("header:\n" + getHeadersStr(method.getRequestHeaders()));
    logger.debug("body:\n" + getPostBody(method.getParameters()));
    logger.debug("cookie:\n" + getCookieStr());
}

private void logPostResponse(PostMethod method, String responseStr) throws URIException {
    logger.debug("do post response:" + method.getURI().toString());
    logger.debug("header:\n" + getHeadersStr(method.getResponseHeaders()));
    logger.debug("body:\n" + responseStr);
}

private String getHeadersStr(Header[] headers) {
    StringBuilder builder = new StringBuilder();
    for (Header header : headers) {
        builder.append(header.getName()).append(": ").append(header.getValue()).append("\n");
    }
    return builder.toString();
}

private String getPostBody(NameValuePair[] postValues) {
    StringBuilder builder = new StringBuilder();
    for (NameValuePair pair : postValues) {
        builder.append(pair.getName()).append(":").append(pair.getValue()).append("\n");
    }
    return builder.toString();
}

private String getCookieStr() {
    Cookie[] cookies = client.getState().getCookies();
    StringBuilder builder = new StringBuilder();
    for (Cookie cookie : cookies) {
        builder.append(cookie.getDomain()).append(":")
               .append(cookie.getName()).append("=").append(cookie.getValue()).append(";")
               .append(cookie.getPath()).append(";")
               .append(cookie.getExpiryDate()).append(";")
               .append(cookie.getSecure()).append(";\n");
    }
    return builder.toString();
}

掌握好以上这些方法,基本上就可以很容易地模拟浏览器访问页面了,剩下的就是通过抓包工具观察,发送什么请求,获取什么应答,以及对应答进行字符串解析了。

Posted in  java contact-list http


养小仓鼠

昨天买了两个小仓鼠,太可爱了。

给它们配备了笼子、鼠粮、沙子、浴盐、磨牙的,装备齐全啊,两个小家伙上蹿下跳的,一会在楼梯上跑上跑下,一会圈圈里跑步健身。小家伙吃东西喝水的时候很好玩,两只手拿着,小嘴巴不停地啃啊啃的。

不过它们拉屎的频率好快,今天帮它们洗了澡,洗了笼子。不过两个家伙好像不怎么喜欢对方,今天打架,叽叽得叫个不停,睡觉的时候也各自躲在笼子的角落里,奇怪了,明明是一公一母的呢。

Posted in  life


完成Nike预备跑

上周六参加了Nike预备跑,休息了两天,今天才有时间上来写点。

首先,感谢小笨蛋的陪同,给了我很大的支持。帮我拿东西,拍照片,还在体育场等了我一个小时多。

这次长跑的结果是,耐力和意志力比去年都要差了,70分钟完成10公里,比上次慢了10分钟,中间停下过N次,走了估计有500米。看来晚上的运动量从10圈降到5圈,影响还是很大的呢。

这次活动似乎是受到金融危机的影响,赛后的奖品从T-shirt变成了毛巾,饮料从佳得乐变成了矿泉水。

赛前的领导讲话变得骂声一片,完全没有时间观念,起跑时间从15分拖到30分最后再拖到45分。途中露头的太阳还是让人跑得很难受,复旦大学那段大概4-7公里是最累的一段,时不时就停下来走走,然后强忍再跑起来。中间走的时候还被一位老奶奶超过,汗颜啊!

最终冲过终点的感觉还是很爽的!

Posted in  life


Nike预备跑准备中

周六参加Nike预备跑,昨天去南京路领了跑步的装备,一件橙色的Nike T-shirt(总觉得有点劣质产品的样子),一个计时器,一个手臂的带子(不知道是干嘛的)。前天晚上在华师大的操场跑了十圈,感觉状态不错,左脚小腿也疼了,看来没什么问题了。就是周六安排早上7点15开始跑,从我这里到江湾体育场又不是很方便,看来得起个大早了。希望那天早上下毛毛雨,这样跑起来会舒服些。

Posted in  life


Fork me on GitHub