Open for a new job

I am about to leave current company and looking for a new challenge for future. If you are interested, and also looking for a Java or Python developer, please check the hot baked resume:

Actually, I am not good at documenting myself. So if you need more details, feel free to get connected with email: classicning[at]gmail.com

The post is brought to you by lekhonee v0.7

周记

先讲个笑话,然后回顾一下这个星期。

这个星期从周四晚上开始,和一位外国朋友一位台湾女士聊天。第一次经历这样的场面,忐忑得很,起初还尝试和大家说英文的,奈何我的口语和听力实在是难以支撑,最后不知不觉间大家都平滑过渡到中文了。后来我想着听力还可以通过看InfoQ上的会议录像什么的练习一下,口语就鲜有机会和场合提升了。进而又想到长期在一个小环境里做重复的事情,有些能力的退化你甚至都感觉不到,更不要说原先不具备的能力了,你甚至都不能觉察到自己缺乏某一方面的训练。

今天下午又和原先在IBM实习的同事聚会,正好在五角场附近转转。比起张江这种近乎边缘的生活环境,五角场实在是强太多了,更难能可贵的是如果你在附近转一转,询问一下房租,那里的生活成本实际上并不比张江高,生活质量实在是没法比。这是为什么呢?不理解。另外今天看到了samson理解不了的沃尔玛,还有传说中那一片Oracle / EMC / eBao / 百度。上海真是大,地铁走几站就是一个完全不同的世界。

从同事那里了解到了团购网站的一些事情,这年头,好像不做个团购网站都不好意思跟人打招呼似的。于是有了这种东西,又是一窝蜂,这就是我们的互联网,你有什么意见呢。

昨天本想写一个对 Continuous Deployment 的理解来着,结果不料电脑过热死机了。什么原因呢,原来是浏览器里开着一个崔永元的网站,一个基于flash的网站,你懂的,CPU一直占满。这么明显的、严重的、高能耗的问题,这么多年了,Adobe一点动作都没有。另外我发现今天禁用了Adobe的Flashplayer居然Firefox的Ctrl+C/Ctrl+T等等几个快捷键突然恢复了,对于这种情况还是不说什么吧。最近听说开源的实现gnash已经可以播放Youtube的视频了(当然严格地说这事跟咱们没什么关系),一会写完blog我决定配置一下试试。

这周另一个重要的事情是Kindle 3上市了,第一次对一个手持设备产生这么强烈的兴趣。而且尤其是发现你没有那么容易的渠道能够买到它的时候,这种兴趣膨胀得更厉害了。半年前电子墨水的电子书都还在2000人民币以上,这次6寸不带3G的Kindle 3只要139美元,折合人民币连950都不到,这个领域很快又要炸开锅了。

最后,我把Bason放到Sonatype的OSS Repository上了。Sonatype提供了一个免费的Maven仓库,你可以把自己的开源项目deploy到这个仓库中方便分发。详情您可以去看sonatype提供的文档

The post is brought to you by lekhonee v0.7

Bason: A BSON Serialization Code Generator

Bason is a code generator for object to bson serialization and deserialization. Different from tranditional reflection way, bason uses an annotation processor to generate serialization manager at compile time. You just add Bason as compilation dependency and drop it in the runtime.

To use Bason, you simply add annotation to JavaBeans:

/**
 *
 */

package info.sunng.bason.example;


import java.util.Date;

import info.sunng.bason.annotations.BsonAlias;
import info.sunng.bason.annotations.BsonDocument;
import info.sunng.bason.annotations.BsonIgnore;

/**
 * @author SunNing
 *
 * @since Aug 18, 2010
 */

@BsonDocument
public class Passenger {

    private double packageWeight;

    private long ticketId;

    private String name;

    private Date createdDate;

    private Flight flight;

    /**
     * @return the packageWeight
     */

    @BsonIgnore
    public double getPackageWeight() {
        return packageWeight;
    }

    /**
     * @param packageWeight the packageWeight to set
     */

    public void setPackageWeight(double packageWeight) {
        this.packageWeight = packageWeight;
    }

    /**
     * @return the ticketId
     */

    @BsonAlias("ticket")
    public long getTicketId() {
        return ticketId;
    }

    /**
     * @param ticketId the ticketId to set
     */

    public void setTicketId(long ticketId) {
        this.ticketId = ticketId;
    }

    /**
     * @return the name
     */

    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */

    public void setName(String name) {
        this.name = name;
    }

    /**
     * @param createdDate the createdDate to set
     */

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    /**
     * @return the createdDate
     */

    public Date getCreatedDate() {
        return createdDate;
    }

    /**
     * @param flight the flight to set
     */

    public void setFlight(Flight flight) {
        this.flight = flight;
    }

    /**
     * @return the flight
     */

    public Flight getFlight() {
        return flight;
    }

}
  • @BsonDocument marks this bean to be processed by bason processor. Serialization and deserialization support for this bean will be added to the manager. The bean must follow the Java Bean specification that has a getter and a setter for each property.
  • @BsonAlias on the getter allows user the specify a name for bson document instead of the default java bean property name.
  • @BsonIgnore on the getter marks a property to be transient when serialization and deserialization.

Then you need a bason.properties at the root of classpath which looks like

bason.managerClassName=info.sunng.bason.BasonManager

You specify the manager class name here. This name can not be duplicated if you use Bason in multiple modules.

Take maven configuration as an example:

    <dependencies>
        <dependency>
            <groupId>info.sunng.bason</groupId>
            <artifactId>bason-annotation</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>info.sunng.bason</groupId>
            <artifactId>bason-internal</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongo-java-driver</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>

                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>

                    <compilerArguments>
                        <processor>info.sunng.bason.internal.BasonProcessor</processor>
                    </compilerArguments>
                </configuration>
            </plugin>
        </plugins>
    </build>

when everything is ready, run mvn compile to generate the manager source file. By default, in a standard maven project, the generated file will be placed at:
/bason-example/target/generated-sources/annotations/

package info.sunng.bason;
import org.bson.*;
import javax.annotation.Generated;
@Generated({"info.sunng.bason.BasonManager"})
public final class BasonManager{
    public static final BSONObject toBson(info.sunng.bason.example.Passenger o){
        if (o == null) {
            throw new NullPointerException();
        }
        BSONObject bson = new BasicBSONObject();
        bson.put("ticket",o.getTicketId());
        bson.put("name",o.getName());
        bson.put("createdDate",o.getCreatedDate());
        bson.put("flight",toBson(o.getFlight()));
        return bson;
    }
    public static final info.sunng.bason.example.Passenger fromBson(info.sunng.bason.example.Passenger o, BSONObject bson){
        if (o == null || bson == null) {
            throw new NullPointerException();
        }
        o.setTicketId((java.lang.Long)bson.get("ticket"));
        o.setName((java.lang.String)bson.get("name"));
        o.setCreatedDate((java.util.Date)bson.get("createdDate"));
        o.setFlight(fromBson(new info.sunng.bason.example.Flight(), (BSONObject)bson.get("flight")));
        return o;
    }
    public static final BSONObject toBson(info.sunng.bason.example.Flight o){
        if (o == null) {
            throw new NullPointerException();
        }
        BSONObject bson = new BasicBSONObject();
        bson.put("company",o.getCompany());
        bson.put("flightId",o.getFlightId());
        return bson;
    }
    public static final info.sunng.bason.example.Flight fromBson(info.sunng.bason.example.Flight o, BSONObject bson){
        if (o == null || bson == null) {
            throw new NullPointerException();
        }
        o.setCompany((java.lang.String)bson.get("company"));
        o.setFlightId((java.lang.String)bson.get("flightId"));
        return o;
    }
}

The project is hosted at
http://github.com/sunng87/bason

If you have any ideas, just let me know.

The post is brought to you by lekhonee v0.7

Introduction to Amoeba

Amoeba is a distributed database middleware works as mysql proxy, provides sharding and high availability support for large scale applications using multiple mysql servers as backend.

Compatible with mysql protocol, Amoeba is fully transparent to any client using standard mysql drivers, which means, to use Amoeba you don’t have to modify any code of database connector. You just configure rules for amoeba, then the client request will automatically route to certain mysql instance. Amoeba has a flexible set of configuration rules that can satisfy your requirements.

Amoeba is written in Java, and deployed on Linux server in most cases. The IO module is built on the top of Java nonblocking IO, which keeps communication between client, Amoeba and mysql at a high performance.

The project is initialized in 2008. Stable release 1.2.1-GA have been available since July, 2010 . It is now serving on the production environment of the social networks http://t.sdo.com/

Project home:
http://code.google.com/p/amoeba/

Development logs:
http://amoeba.meidusa.com/wordpress/

The post is brought to you by lekhonee v0.7

geb for browser functional testing

虽然现在不做前段了,但是发现好的工具还是很兴奋。今天在twitter上看到Grails in Action的作者 @pledbrook 转了一个geb 0.4的消息,顺带看了一下这个工具

http://geb.codehaus.org

geb项目旨在创造一套groovy dsl帮助人们进行webapp的functional test。它是对selenium的封装,举例:

@Grapes([
    @Grab('org.seleniumhq.selenium:selenium-firefox-driver:latest.release'),
    @Grab('org.codehaus.geb:geb-core:latest.release')
])
import geb.*

println "Dependencies downloaded, ready for testing"
Browser.drive('http://sunng.info:8000/Pacajus'){
    assert title== 'Pacajus'

    assert $("p", 3).text() == 'Population: 41558'
}
println "Tested, bye"

打开页面,执行断言。如果断言失败,driver方法会报null:
Caught: geb.error.DriveException: null

只要在命令行用groovy执行即可,grapes会搞定依赖关系。

很方便吧,文档上说还可以跟grails / junit等等集成,快去看看吧
http://geb.codehaus.org/manual/latest/index.html

The post is brought to you by lekhonee v0.7