zookeeper资料汇集

这几天看了下zookeeper的东西。收获多多。感谢国人的分享精神。使得这一产品能被更多人了解。

zookeeper基础配置与入门:http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/

agapple学习笔记系列:http://agapple.iteye.com/blog/1292473

taobao通用产品团队:http://rdc.taobao.com/team/jm/archives/448

Paxos算法:http://zh.wikipedia.org/zh/Paxos%E7%AE%97%E6%B3%95

工具:https://github.com/killme2008/node-zk-browser 淘宝团队开发的一个zookeeper节点管理工具。nodejs开发。功能简单。基本实现了zookeeper命令行的功能。

文档是最佳的知识沉淀呀~

apache zookeeper

zookeeper

 

 

jmock2学习笔记-续

总结下这几天初步学习mock的心得. 文中大部分内容来自官网文档.

创建Mock对象

Mockery context = new Mockery();
Turtle turtle = context.mock(Turtle.class);
Turtle turtle2 = context.mock(Turtle.class, "turtle2");

创建Mock Expections

... create mock objects ...
public void testSomeAction() {
    ... set up ...
    context.checking(new Expectations() {{
        ... <expections block> ...
    }});
   ... call code being tested ...
}

expections 这里指的是jmock在不同情况下的预期值。expections 是jmcok的最重要部分之一。可以模拟用户在不同情况下返回不同的结果。

<expections block>这里是可以设置多个expections 。

Expectations

expections的结构如下:

invocation-count (mock-object).method(argument-constraints);
    inSequence(sequence-name);
    when(state-machine.is(state-name));
    will(action);
    then(state-machine.is(new-state-name));

上面是一个通用的结构。所有expecions都采用相同或更简洁的结构。

invocation-count mock调用频率。oneOf表示调用一次后这个expections即失效。
mock-object mock对象。即上面Mockery创建的对象。
method 用mock代替实现接口的一个方法。所以mock必须使用接口编程。
argument-constraints mock参数条件。即mock指定方法的参数。
when … will…then … 类似于if .. else… 这样的语句。简单的条件表达式
action mock符合条件后,执行操作。一般是返回结果。或者抛出异常
state-machine 条件表达式。

下面详细讲解下expectations各个部分。

Invocation Count

oneOf 只执行一次
exactly(n).of 执行指定n次
atLeast(n).of 至少执行n次
atMost(n).of 最多执行n次
between(min,max).of 可以执行min-max次
allowing 允许执行,不受限制
ignoring 功能同allowing类似。这里主要字面上区分allowing
never 不允许执行

Actions

will(returnValue(v)) 返回一个值。Object类型任意。集合类型不建议这里返回。虽然也可以
will(returnIterator(c)) 返回一个集合类型的值。
will(returnIterator(v1, v2, …, vn)) 返回一个集合类型的值。可以用多个,来分隔。
will(throwException(e)) 抛出一个异常。
will(doAll(a1, a2, …, an)) 嵌套执行多个actoin。不常用。

Sequences

设定mock方法调用的顺序。简单的看下面代码片段。
定义了先save后countUser。调用的时候也必须找这个顺序。否则用例失败。

        public void testCountUserSequences() {
                final Sequence testSequence = mockery.sequence("testSequence");

                final User _user = genUser();

                mockery.checking(new Expectations() {
                        {
                                oneOf(userDao).save(_user);
                                inSequence(testSequence);
                                will(returnValue(_user));

                                oneOf(userDao).countUser();
                                inSequence(testSequence);
                                will(returnValue(1));
                        }
                });

                assertEquals(userService.save(_user).getUsername(), "fengsage");
                assertEquals(userService.countUser(), 1);
        }

jmock2学习笔记

目前晚上学习jmock。

Issue: spring+annotations使用jmock

遇到了这个场景。stackoverflow上有人问过这个问题,这里贴一下。

http://stackoverflow.com/questions/1638911/mock-object-and-spring-annotations

关键是使用ReflectionTestUtils的方法。这个方法在spring-test工程的相关jar包。

    ...
    @Autowired
    private UserService	userService;
    private UserDao	userDao	= null;
    ...
    ReflectionTestUtils.setField(userService, "userDao", userDao, UserDao.class);
    ...

Issue:入参对象时。mock表达式传入对象必须和实际传入对象为同一句柄。

       
    final User _user = genUser();

    mockery.checking(new Expectations() {
        {
            one(userDao).queryUser(_user);
            List<User> userList = new ArrayList<User>();
            userList.add(genUser());
            userList.add(genUser());
            will(returnValue(userList));
        }
    });

    List<User> users = userService.queryUser(_user);

共用一个_user句柄。否则报错。

Issues:jmock一个异常错误

will(throwException(new RuntimeException("test exception")));

推荐链接:http://superleo.iteye.com/blog/143493

相关:jmock2学习笔记-续

慎用gson

今天是辛苦1个半月代金券上线的日子。但很不幸。因为线上环境和预发布环境存在不一致问题。导致本次发布失败!

抓取到错误的日志。如下:

[01 17:03:48,567 DEBUG] ["http-bio-80"-exec-3] conn.SingleClientConnManager - Releasing connection org.apache.http.impl.conn.SingleClientConnManager$ConnAdapter@287b2e39
[01 17:03:48,568 ERROR] ["http-bio-80"-exec-3] impl.LotteryProcessServiceImpl - 处理异常[order=LotteryUseOrder [lotteryTypeCode=USE, accountId=5556067, cardNo=A00088754, passwd=null, skuId=null, origPayAmount=127.00, favorableAmount=20.00, orderId=1000000090, sceneType=MAYMAY]]:
com.google.gson.JsonSyntaxException: 2011-07-05 17:41:12
        at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserializeToDate(DefaultTypeAdapters.java:376)
        at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserialize(DefaultTypeAdapters.java:351)
        at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserialize(DefaultTypeAdapters.java:307)
        at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:51)
        at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:92)
        at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler(JsonObjectDeserializationVisitor.java:117)
        at com.google.gson.ReflectingFieldNavigator.visitFieldsReflectively(ReflectingFieldNavigator.java:63)
        at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:120)
        at com.google.gson.JsonDeserializationContextDefault.fromJsonObject(JsonDeserializationContextDefault.java:76)
        at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:54)
        at com.google.gson.Gson.fromJson(Gson.java:551)
        at com.google.gson.Gson.fromJson(Gson.java:498)
        at com.google.gson.Gson.fromJson(Gson.java:467)
        at com.google.gson.Gson.fromJson(Gson.java:417)
        at com.google.gson.Gson.fromJson(Gson.java:389)
        at com.mbb.payengine.lottery.integration.AcctransMaymayQueryServiceClientImpl.genAccountMaymayInfo(AcctransMaymayQueryServiceClientImpl.java:146)
        at com.mbb.payengine.lottery.integration.AcctransMaymayQueryServiceClientImpl.getAccountByAccountId(AcctransMaymayQueryServiceClientImpl.java:72)
        at com.mbb.payengine.lottery.domainservice.process.impl.use.UseTransProcessor.process(UseTransProcessor.java:69)
        at com.mbb.payengine.lottery.domainservice.template.LotteryTemplate.process(LotteryTemplate.java:46)
        at com.mbb.payengine.lottery.domainservice.factory.impl.LotteryDomainFactoryImpl.processUseTemplate(LotteryDomainFactoryImpl.java:171)
        at com.mbb.payengine.lottery.domainservice.factory.impl.LotteryDomainFactoryImpl.compose(LotteryDomainFactoryImpl.java:104)
        at com.mbb.payengine.lottery.service.api.impl.LotteryProcessServiceImpl$1.doInTransaction(LotteryProcessServiceImpl.java:101)
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
        at com.mbb.payengine.lottery.service.api.impl.LotteryProcessServiceImpl.process(LotteryProcessServiceImpl.java:85)
        at com.mbb.payengine.lottery.web.api.LotteryProcessServiceController.process(LotteryProcessServiceController.java:114)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:426)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:414)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:164)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:399)
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:317)
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:204)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:311)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:662)
Caused by: java.text.ParseException: Unparseable date: "2011-07-05 17:41:12"
        at java.text.DateFormat.parse(DateFormat.java:337)
        at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserializeToDate(DefaultTypeAdapters.java:374)
        ... 57 more
[01 17:03:48,571 INFO ] ["http-bio-80"-exec-3] impl.LotteryProcessServiceImpl - 代金券使用失败[order=LotteryUseOrder [lotteryTypeCode=USE, accountId=5556067, cardNo=A00088754, passwd=null, skuId=null, origPayAmount=127.00, favorableAmount=20.00, orderId=1000000090, sceneType=MAYMAY],result=LotteryProcessResult [orgiAmount=0.00, cardNo=null, useFrequence=0, alreadyUseFrequence=0, validityBegin=null, validityEnd=null, lotteryDiscount=null, lotteryScope=null, lotteryUseCondition=null, lotteryFee=null, lotteryCarriage=null, activeId=null]]

问题在gson解析json时间时,出错。但日志显示。出差的地方是2011-07-05 17:41:12。

经过部门同事帮忙,找到原因:线上环境采用en_US.UTF-8编码。线下环境一直用的是zh_CN.UTF-8编码。

Gson默认会使用系统环境的时间解析器来解析时间.显然en_US.UTF-8和zh_CN.UTF-8的默认时间不一致。修改代码如下,可以修复问题

Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();

 

建议使用fastjson替代gson. 相关的问题会少很多~

放弃GAE 博客

由于GAE重新调整了策略。导致原博客总是在database write的时候超过上限制。

懒得去优化了。直接在VPS上架设了wordpress了。

ghs.fengsage.com 仍可以正常调用。

要Google+邀请的速速留下你的Gmail!!

要Google+邀请的速速留下你的Gmail!!

一段自动登录淘宝接口Python代码

新公司这边客服流动频繁,为防止密码泄露,写了个小脚本.可以让用户在不知道密码的情况下自动登录淘宝

主要代码:

#!/usr/bin/python2.5

# -*- coding: utf-8 -*-

'''

Created on 2011-5-28

@author: fred <me@fengsage.com>

'''

'''

自动登录淘宝脚本

'''

def login_taobao(username, password):

  from apps.PAM30 import PAMIE

  ie = PAMIE()

  ie.navigate("https://login.taobao.com/member/login.jhtml")

  tmp_element = ie.findElement('input','name','loginType')

  if tmp_element.getAttribute("value")=="4":

    checkBox = ie.findElement('input', 'id', 'J_SafeLoginCheck')

    ie.clickElement(checkBox)

  if ie.textBoxExists('TPL_username') and ie.textBoxExists('TPL_password'):

    ie.setTextBox('TPL_username', username)

    ie.setTextBox('TPL_password', password)

    loginbtn = ie.findElement('button', 'type', 'submit')

    ie.clickElement(loginbtn)

使用PAMIE,可以轻松控制IE做任何操作~~

I love Python~

中国需要更多这样有良知的人

 

一个民族的伟大,需要千千万万这样的人。大家一起努力,而不是总在网上抱怨。

android自定义弹出框(AlertDialog)

网上搜到的一片文章,非常不错. 看一眼就能明白如何操作. 直接贴代码:

public class DialogWithInputBox extends Activity {

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        final AlertDialog.Builder alert = new AlertDialog.Builder(this);

        final EditText input = new EditText(this);

        alert.setView(input);

        alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface dialog, int whichButton) {

                String value = input.getText().toString().trim();

                Toast.makeText(getApplicationContext(), value,

                Toast.LENGTH_SHORT).show();

            }

        });

        alert.setNegativeButton("Cancel",

        new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface dialog, int whichButton) {

                dialog.cancel();

            }

        });

        alert.show();

    }

}

原文链接:http://www.androidpeople.com/android-dialog-input-text/

群星支持打拐,呼吁救救“孩子”

各位机友们~拿起手机拍下身边的乞儿, 也许这侵犯了儿童的肖像权,但为了他们的生存权,我们决不放弃