作者存档: fred

python模拟飞信(模拟webim)

之前写过一点飞信相关的小程序。很久没关心了。发现现在飞信协议都改掉了。强制了验证码。基本上原来的第三方库都不能使用了。

下面就是现在的飞信验证码。看上去破解难度不大。就一根干扰线。有哪位可以破解的网友贡献下代码to me<me[???]fengsage.com> 替换[???]你懂得。
飞信验证码

废话不多说,开始进入正文。

最近发现飞信官网出了webim版的飞信。而且因为年底工作不多。有点无聊。花了几周无聊时间写写代码。给自己发发短信神马的。没多大意义。

项目地址:http://code.google.com/p/fetionim/

值得注意的地方:

  1. 这个程序不是api库。如果验证码能破解则可以作为API库。
  2. 这个程序需要不关机环境。因为要保持和飞信server的心跳连接。一旦关机需要重新登录。
  3. 很多未知bug

目前实现的功能:

  1. 简单的cron表达式。目前只支持 month day hour minute 四个区间. 支持一个*通配符。一般用用够了。举例:[* * * *]表示每分钟执行到。[* * 20 00]表示每天20点执行。[* 20 * *] 表示每月20日的每一分钟执行到。[12 20 20 1]表示12月20日20点1分执行到。
  2. 只支持2种定时任务。即天气预报(只支持嘉兴.因为我住这)和任意短信。

未来要实现的功能:

  1. 天气预报各地支持。这块实现起来很简单。但打算重新设计下任务模块和cron模块。到时候一起改。
  2. 开放一些api。方便其他程序连接。比如宏机短信……
  3. 做个wordpress插件。方便博友直接通过短信联系作者。

 一些废话:

本来不打算发布这个。个人感觉这玩意不能算是什么好的作品。不过以我个性,如果不发布这玩意。估计后面也懒得去维护了。索性发布。代码写的不好。看到的朋友请口水少喷。多提点实在点的意见。

java和ruby都有基于webim的类似实现。

java参见:litefetion
ruby参见:fetion-robot

python的应该算是独一无二的。我没看过这些现有的代码是如何实现的。纯粹自己编写边想。思路和方式和他们应该都存在较多的不同。算是给python初学者提供一些例子吧。

 

总结2011

每年都给自己写个总结。写给将来的自己看。

不太会写文章,分几个方面总结下2011过去的一年。

生活

离开了生活了5年多的杭州。其实说是5年。但里面有3年大学生活,只有2年的工作。虽然是爸妈的意思,但我知道,其实是我自己的软弱。我害怕孤单的一直生活下去。每天上班下班。不知道前面的路在哪里。回嘉兴的这一年来感觉非常的放松,再也没有了那种漂泊孤单感觉,有着家人的关爱,重新滋润了我那干枯的心床。我不后悔!感谢小龚老师、小王老师、老蒋童鞋。曾经的照顾和关怀~

工作

告别了曾经共同奋斗过得同事们,告别了我最尊敬的王总。感谢你2年前给我一个走出大学的学生机会,让我不至于在茫茫的毕业潮中失去方向。在公司的这段日子是我毕生难忘的日子。你教会了我如何学习,如何沟通,如何写文档……感谢震宇,我的好兄弟,好朋友。感谢所有曾经给我过帮助的同事朋友们。
这1年找了2份工作,这在我以前看来是十分反常的。我很反感经常换工作的人,频繁的换工作即是对公司的不负责任也是对自己的懈怠。然而我犯了这个错误。
回嘉兴的第一份工作是在浙江衣派网络科技公司,一家不上进的公司。从刚开始给人一种勃勃生机的兴奋,到最后给人一种日落黄昏的伤感。不想过于主观的评价一家公司,这里感谢强哥,老陆,小朱,老吴,小彭。感谢技术部同事们给我的帮助和照顾。感谢你们包容我的缺点,祝愿新的一年,你们在各自的岗位上取得新的成就。
第二家公司就是现在的嘉兴麦包包网络科技公司。说实话我刚回嘉兴之前从来没听说过。虽然麦包包在电商领域已经较有名气。来麦包包的理由就简单,一是上一家公司的确经营不善,面临裁员的风险,其次就是这里有我非常感兴趣的项目,搭建自己的支付系统。前面这半年,基本快打磨掉我的积极性,整天准点下班,做些没有挑战的事情。人都懒散了。然而这个项目给了我兴奋的感觉。我太渴望有所提升。感谢我的胖师傅银鲨,二师弟银鲈.(呵呵,麦包包的企业文化的确做得不错,有意思的花名文化,虽然山寨但不错)。你们给了我太多太多的帮助。银鲨师傅的“苛刻”灭了偷懒的借口,重新点燃了我的斗志。银鲈童鞋的固执,坚持,避免了我很多bug的出现。然而好事多磨,年前的1个月,银鲨师傅的突然离职,让我感到希望的破灭,没有领路人的感觉真的不好受。随后项目的暂停和转移,让我有了离开麦包包的想法。12月被安排到日本项目组,我有种被打入冷宫的感觉。为了总监给我的希望,坚持了1个月。回头想来这1个月同样精彩。跟新的同事打了交道,虽然已经来了2个多月,但是除了跟银鲨和银鲈,没有和太多的同事沟通,想来有点可悲。感谢滕枣童鞋,很高兴与你的合作。这个月是轻松愉快的。感谢下糯米同学,你是我见过最有才华和实力的应届毕业生了。希望你在新一年里,新的公司取得更多的成就。年初之际总监重启了支付项目,今年这个将是我努力的目标和方向。
感谢下卷丹童鞋、木虱童鞋、戎麦童鞋、巴叶童鞋、灵猫童鞋、角蒿童鞋、银鲈童鞋、章木童鞋、腾枣童鞋、铁阕童鞋、青柠童鞋、戟天童鞋、白鱼童鞋、翠雀童鞋、戎草童鞋、海椒童鞋、蜂虎童鞋、蝎虎童鞋、龙猫童鞋、简草童鞋、洋姜童鞋、白鲩童鞋、蓝天童鞋、白苏童鞋、西柚童鞋、毛尖童鞋…..(排名按我座位的远近,方便我回想。并且在职的同事。保佑我都记全了)

感情

感情方面一直是我的弱项,25年来就没真正谈过一场恋爱。大学里我不想谈,更不敢谈。承担不起责任的我,没资格谈恋爱。所以我不后悔。这方面今年是可悲一年。一是曾经很喜欢的女生结婚了,我从头到尾都没有勇气去追求过,伤悔。二是好不容易相亲到一个比较中意的女生,准备踏踏实实一起走下去。但对方一句从来没有喜欢过我,把我打落了谷底。我不怪她,如果我能多关心关心她,也许结局会不一样。感情方面一直都是我很回避的方面,但2012年我会改正这一点。遇到喜欢的就尽力去追,”放开”的去追。不想以后来了有后悔。

财产

左边是全年的支出图,总体感觉每一笔钱都是有价值的。
理财方面非常的糟糕。今年基本亏损10%左右。还在坚持定存的习惯,投入财产的资金不多。总体亏损在1-2K之前。但和我2011年初制定的理财计划已经相差太多了。2011理财计划。感觉今年的亏损主要原因还是投机心太重,没有真正的肯去学习理财知识。以为读几本理财方面的书,就能理到财。其次今年全球的经济的确不景气。黄金大大起大落,给人当年中国股市崩盘的感觉。

 

2012的展望

工作上,把东西做好,做漂亮。努力工作,早日升值加薪。
生活上,多做点力所能及的事情,少让爸妈操心。周末多学点菜,为将来做准备。
感情上,希望今年能找到心仪的女朋友,早日踏上婚姻的”坟墓”。
理财上,有空,踏踏实实学点经济常识,少点投机心,多点主观意念。
学习上,今年要好好学好英语。踏踏实实。把阅读和听力提升一个台阶。
身体上,有时间好好锻炼下自己,瘦巴巴的,像柴火一样。我是女人也不会喜欢。

2012欢迎我吧~

wordpress图片搬家脚本

历经几次换域名,以及从GAE-wordpress造成不少历史遗留的图片分布在不同域名或gae目录下。

主要分布
http://oldres.fengsage.com
http://www.fengsage.com
http://fensageblog.appspot.com

原来使用代码实现图片代理访问。但自己越看越不爽。索性写个脚本一次性把原来的图片下载到wordpress本地。并且更新数据库。主要是圣诞节闲的蛋疼。

代码有不少问题:

  1. 不用php,因为懒得看wordpress插件制作。
  2. 因为是python脚本,所以必须是有执行python的权限
  3. 没有生成wordpress media记录。不能后台编辑多媒体

wordpress插件实现:

http://wordpress.org/extend/plugins/velvet-blues-update-urls/  等发现的时候脚本已经写好了。悲哀~~

脚本笔记简单。分享下。

Default
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env python
# --*-- encoding:utf-8 --*--
'''
Created on 2011-12-25
@author: fred <fredzhu.info>
'''
import MySQLdb
import re
import urllib
import time
import os
WP_POST_CONTENT = u"wp_posts"
SIMPLE_IMG_URL = r'<img[^/]*src="([^"]*)"[^/]*/>'
NEW_IMG_URL = u''
DOWNLOAD_PATH = u'wp-content/uploads/%s'%(time.strftime('%m/%d', time.localtime(time.time())))
REPLACE_LIST = ['http://oldres.fengsage.com','../media','http://www.fengsage.com']
conn = MySQLdb.connect(host="localhost",
user="root",
passwd="zhufeng",
db="wordpress",
charset='utf8')
def get_post_list():
cursor = conn.cursor()
cursor.execute("SET NAMES utf8")
cursor.execute("select * from %s"%WP_POST_CONTENT);
return cursor.fetchall()
def find_img_urls(str):
pattern = re.compile(SIMPLE_IMG_URL)
match = pattern.findall(str)
if match:
return match
return None
def process_imgs(img_urls,replace):
result = {}
for img in img_urls:
for rp in replace:
if img.find(rp)>=0:
print "\tdownload picture"
new_img_ath = download_img(img)
result.update({img:new_img_ath})
return result
def download_img(img_url):
IMG_NAME_REG = r'([^./]*).(png|jpg|gif|jpeg|bmp)'
m = re.findall(IMG_NAME_REG, img_url)
if m:
name,suf = m[0]
if os.path.isdir(DOWNLOAD_PATH) == False:
os.makedirs(DOWNLOAD_PATH)
file_path = u'%s/%s.%s'%(DOWNLOAD_PATH,name,suf)
downloaded_image = file(file_path.encode('utf-8'), "wb")
try:
image_on_web = urllib.urlopen(img_url.encode('utf-8'))
while True:
buf = image_on_web.read(65536)
if len(buf) == 0:
break
downloaded_image.write(buf)
downloaded_image.close()
image_on_web.close()
return file_path
except:
print '\tdownload img failture:%s'%img_url
def refresh_content(post_content,result):
for k in result.keys():
post_content = post_content.replace(k,'%s/%s'%(NEW_IMG_URL,result[k]))
return post_content
def update_content(post_id,post_content):
sql = "update "+WP_POST_CONTENT+" set post_content=%s where ID=%s"
cursor = conn.cursor()
cursor.execute("SET NAMES utf8")
cursor.execute(sql,(post_content,post_id))
def go():
post_list = get_post_list()
for post in post_list:
post_id = post[0]
post_content = post[4]
post_title = post[5]
if post_id and post_content and post_title:
print "process article ID:%s"%(post_id)
img_urls = find_img_urls(post_content)
if img_urls:
result = process_imgs(img_urls,REPLACE_LIST)
if result and len(result)>0:
post_content = refresh_content(post_content,result)
try:
update_content(post_id,post_content)
except Exception, e:
print 'update database failture:%s'%e
if __name__ == '__main__':
print go()

javascript实用模板引擎

John Resig 开发的一个简易模板引擎,配合json非常的实用。

Default
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
var cache = {};
this.tmpl = function tmpl(str, data){
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
// Provide some basic currying to the user
return data ? fn( data ) : fn;
};
})();

很少的一点代码。但功能非常实用。

准备json和一个模板即可

Default
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script><script type="text/javascript">// <![CDATA[
$(document).ready(function(){
    var json =
        {
            "key":"1",
            "datas":
                [{"k":"v1","k2":"v2"}],
            "test_if":true,
            "test_for_datas":[1,2,3,4,5,6,7,8,9,10]
        };
    var tmp =     'key:<%=key%></br>'+
                'datas:
<ul>
    <li><%=datas[0].k%></li>
    <li><%=datas[0].k2%></li>
</ul>
'+
                'test_if:'+
                '<%if(test_if){%>'+
                '成立'+
                '<%}else{%>'+
                '失败'+
                '<%}%>'+
                '</br>'+
                '循环:'+
                '<%for(var i=0;i<test_for_datas.length;i++){%>'+
                    '<%=test_for_datas[i]%>'+
                '<%}%>';
    $("#conent").html(tmpl(tmp,json));
});
// ]]></script>

jquery simple template demo

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对象

Default
1
2
3
Mockery context = new Mockery();
Turtle turtle = context.mock(Turtle.class);
Turtle turtle2 = context.mock(Turtle.class, "turtle2");

创建Mock Expections

Default
1
2
3
4
5
6
7
8
... create mock objects ...
public void testSomeAction() {
... set up ...
context.checking(new Expectations() {{
... &lt;expections block&gt; ...
}});
... call code being tested ...
}

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

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

Expectations

expections的结构如下:

Default
1
2
3
4
5
invocation-count (mock-object).method(argument-constraints);
inSequence(sequence-name);
when(state-machine.is(state-name));
will(action);
then(state-machine</var>.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。调用的时候也必须找这个顺序。否则用例失败。

Default
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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包。

Default
1
2
3
4
5
6
7
...
@Autowired
private UserService    userService;
private UserDao    userDao    = null;
...
ReflectionTestUtils.setField(userService, "userDao", userDao, UserDao.class);
...

Issue:入参对象时。mock表达式传入对象必须和实际传入对象为同一句柄。
Default
1
2
3
4
5
6
7
8
9
10
11
12
13
14
       
final User _user = genUser();
mockery.checking(new Expectations() {
{
one(userDao).queryUser(_user);
List&lt;User&gt; userList = new ArrayList&lt;User&gt;();
userList.add(genUser());
userList.add(genUser());
will(returnValue(userList));
        }
});
List&lt;User&gt; users = userService.queryUser(_user);

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

Issues:jmock一个异常错误

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

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

相关:jmock2学习笔记-续

慎用gson

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

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

Default
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
[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的默认时间不一致。修改代码如下,可以修复问题

Default
1
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!!

第 1 页,共 13 页12345...10...最旧 »