posts - 193,  comments - 520,  trackbacks - 0
  2005年11月16日

    对新创项目而言,是idea更重要,还是执行力更重要?在没有用户时,我们该如何冷启动?团队、人、技术、产品、推广和拜春哥,哪一个更重要?到底是什么决定了一个项目的生存或者毁灭?

    来吧,一起书写51daifan的成长史吧。51daifan是一个同事之间分享午餐、特产的公益平台。每天带饭的同学,可以多带几份爱心便当,分享给身边的同事。大家中午一起热饭,一起桌上足球,一起品尝同事带来的爱心便当。让午餐变成每天最快乐的时光。


    51daifan5月底开始有想法,一周后6月初web版本上线,四周后7月中旬android版本上线,现在,8月初,ios版本即将上线。2个月时间,有过快乐,有过迷茫,也有过痛苦。它到底会不会成功,谁也不知道,这也许就是生活最富有魅力的部分吧。关注我们的微信公共号,一起来品味产品成长中的酸甜苦辣吧:


    一起来品味产品成长中的酸甜苦辣吧,每周一,我们将把51daifan上周所有滋味倾诉在这里:产品的运营数据、开发进度、对产品方向的思考、产品运营遇到的问题和想法。加入我们,一起来决定产品的进程吧,把你的想法告诉我们,我们一起来尝试,不管结果如何,当我们的额头爬满皱纹,我们能做到的就是,不让它们也爬在心里。

一、产品源起

产品想法缘起每天的午饭:




但是,我们周围还有这样一群人:



于是,我们想:






产品运营&数据

    目前产品上线8周,注册用户160人,app下载用户22人,日活跃用户在25人。有过订饭/带饭行为的用户为62人,订饭数据371人次。

产品运营分为3个阶段:

  1. 6月份中上旬在部门里试用,用户数到达30人,日活跃用户在20人,2人带饭,日均订饭数12
  2. 6月下旬在公司BBS进行了第一次推广,一周内注册用户数达到90人,但带饭人数依旧是2人,拓展新的带饭同事困难(之前假设妹子有时间带饭的路证明是不通的),日均订饭数16
  3. 7月开始转换思路,同时鼓励同事可以分享自己家产的特产,进行了两次自家蜂蜜和蜂王浆的团购,产品推广集中在bbs发帖,人数缓慢增长到160,一位带饭的同事退出(确实是太累了,纯爱心活),为了增加网站粘度,增加了711免费带饭服务,但效果并不明显。因为没有找到新的带饭同事,目前日均订饭数下降至10

    对当前产品运营来说,最重要的工作就是如何找到更多愿意分享的同事。目前有两种思路:一是降低分享的门槛,例如我们可以分享自己15分钟家常菜的菜谱,通过降低门槛拉动社区的活跃度;二是推广我们的网站,认为没有更多分享同事是因为我们用户数还很少。

    9月份有个大的分享活动是nanana同学家在五常,10月她家新稻米收割,我们会组织一次团购,五常稻花香大米在超市的价格在10元左右是陈米并且一定掺有其他米(这已经是所有大米收购时就有的潜规则),我们初步考虑的价格在7元左右,成本在于物流,真是个大问题,目前考虑是长途车送至四惠客运站,然后再包车拉到公司(可行性还在讨论)。

 

对产品方向的不懈思考

    我始终认为产品大的方向绝对正确,就是基于社交关系的O2O(这里是同事关系)。在这个全国造假、连政府信用都已经破产的当下,冷漠、造假似乎是我们这个时代的代名词。我们一定能做点什么,通过基于社交/同事关系的O2O,我们认为一定能重建人们之间的关心和温暖。一方面我们能获得绿色、环保、健康的食品,另一方面,能够拉近同事间的关系。

 

当前开发进度

    目前webandroidios开发全部在同步进行,全部是业余时间开发,代码开源在:https://github.com/ronghao100Android第一个版本已经发布,第二个版本将包含上传图片和消息通知,计划下周发布,ios本周提交审核,计划一周后发布。

 

对这个项目发展你有什么想法,来吧,一起决定它的未来!扫描二维码立刻关注我们的微信公共账号:



 

posted @ 2013-08-22 16:33 ronghao 阅读(1751) | 评论 (0)编辑 收藏

一、我们要解决的问题

无论是什么样的解决方案,一定要牢记我们要解决的问题是什么,切不能将解决方案当做问题本身。具体到过程改进,不管是何种方式的改进,它们所要解决的问题永远只有一个:缩短从产品想法到可用软件之间的时间周期。自动化发布正是如此,如果软件发布只做一次,我们说根本不需要自动化,但如果三次以上,那么软件开发的黄金法则DRY就必须遵守,让时间真正用到开发当中去。

 

二、与发布相关的问题

所谓自动化只不过是将原先手工做的工作谦让给机器做,所以自动化之前一定要先清楚与发布相关的问题有哪些,即使不自动化,这些工作也一个也不能少:

  1. 应用程序如何打包? 发布包能否追踪到SVN版本号?
  2. 对目标机器环境有什么样的要求?
  3. 配置信息是否需要根据目标机器信息做出调整?
  4. 应用程序如何安装和启动?
  5. 应用程序启动后如何切流量?
  6. 应用程序如何升级? 旧版本程序数据如何迁移?
  7. 升级过程中和结束后如何切流量?
  8. 应用程序如何卸载?

 

三、我们的方案

李安的少年Pi正在狂刷票房,我们的自动化发布方案也要跟上潮流:Puppet+CI我们的少年Pi

Ø 使用CI自动化打包,追踪每个发布包的SVN版本;

Ø 使用Puppet管理发布包、目标机器环境、应用程序配置信息以及应用程序线上生命周期;

Ø 使用伽利略系统提供应用程序的命名服务和进行流量切换。

现在应用程序的发布需要两步:CI一键打包、puppet指定应用程序版本SVN提交。




 

四、具体方案

具体方案也就是如何解决与发布相关八个问题的过程。

1.         如何安装、升级和卸载应用程序

我们使用操作系统原生包管理系统来安装、升级和卸载应用程序,我们的应用程序打出RPM二进制包。免安装,所有机器自带,绿色的,有机的。

打包:rpm -ba ./team_member-1.spec

安装:rpm –ivh team_ member-2.0.1-48.x86_64.rpm

升级:rpm –U team_ member-2.0.1-49.x86_64.rpm

卸载:rpm –e team_ member-2.0.1-48.x86_64.rpm

程序升级前要停旧版本服务怎么办?旧版本数据要做处理怎么办?RPM已经帮我们料理好这一切,只要写出spec文件,剩下的交给我们。尽情的插入吧:


 

2.         如何管理目标机器环境和应用配置信息

应用程序已经打好rpm包了,但这还不够,应用程序发布到哪台机器上?应用程序对目标机器有什么要求?发布时需要修改哪些配置和参数?实际发布如何执行,难道需要登陆到每台目标机器运行rpm命令吗?

我们使用Puppet来搞定这一切,Puppet是现在应用第一的devops工具,它通过master/agent的工作模式管理机器。我们通过声明来控制我们的机器达到目标状态。同时,所有puppet文件全部在SVN里,所有对机器的修改全部codereview和可审计。

 


如何管理应用程序发布到哪台机器上?在回答这个问题前我们必须将应用程序在线上的生命周期再进行一次封装。


应用程序TeamMember被我们封装成一个puppet module,配置文件和参数被封装在对应templatesfiles里,每次发布前都要修改配置文件和传递不同的参数?out了吧,puppet帮你传参搞定:


Teammember.conf文件内容:


封装完成后的效果是这样的:


最后在管理部署的site.pp文件里声明一下,应用程序TeamMember2146版本就被自动部署到10.128.34.141.test.back.shequ这台机器上了,我们后续的工作也就是维护这个site.pp文件了,所有应用程序的部署信息都在SVN被集中管理起来:


登陆到每台目标机器运行rpm命令?No!现在TeamMember已经被封装,我们修改完毕site.pp并提交后,puppet就自动执行命令了,要不怎么说是自动化呢。(现在puppet默认在agent每半小时同步一次,但同时支持马上触发执行)。

 

3.         如何追踪每次发布的SVN版本号

我们使用CI进行应用程序的打包,将build号作为包命名的一部分:


 

4.         如何在发布过程中切换流量

这是另外一个很大的话题,参见伽利略计划。

 

五、下一步工作

使用CI将环境的自动化部署与自动化测试串联起来,搭建起整个研发流程自动化平台:


 

六、小结

没有银弹,自动化所做的只是将之前手工工作交给计算机完成,需要做的工作一个都不能少,此外,我们还要多做一些封装或脚本工作,但是,当我们需要重复做这些事情的时候,价值就出现了。我们的目标永远是缩短从产品想法到可用软件之间的时间周期。让时间真正用到开发当中去。

posted @ 2012-12-14 15:54 ronghao 阅读(2706) | 评论 (1)编辑 收藏

张小庆离职了,他想停下来一段时间,他问了王晓丽的意思,出乎他的意料,王晓丽什么都没说就表示了同意。王晓丽能够感觉的到,她的男人正一天一天的沉默下来,这不是以前的他,以前的他,不管在什么时候,都会谈论他的技术,他的工作,自己也常常埋怨他,除了工作其他什么都不会,这次,他第一次不再说工作,第一次厌恶了工作,想休息一段时间,凭女人的直觉,他肯定是遇到迈不过去的坎了,他是需要停下来了。

张小庆去了亦庄,那里,是他开始的地方,他去了自己曾经住过的鹿圈,已经面目全非,整个村庄都变成了一个巨大的建筑工地,高高的吊塔,正在勤奋的作业,也难怪,这里的房价都2万多了。凉水桥依旧还在,桥下的河水依旧在臭烘烘的流淌,桥上依旧是车水马龙,只不过,那时是上下班的人们,此时则是运送建筑材料的人们。只是不知道,那座桥上,是否依稀还能看见那个当初充满希望年轻人的身影。

张小庆去了亦庄活动中心,在活动中心的台阶上,他仿佛看到一个年轻女人的身影,她站在最高的台阶上等张小庆,天气很热,她穿了件白色体恤,下面是一条碎花裙子,露出匀称的小腿,一双高跟凉拖,露出高高的脚倮,她低着头,在台阶上来回踱着步子。那是自己当时多么爱着的一个女人啊!这四周的草地上,都留下了他们的脚印,当时,那个年轻的男人这样安慰她:今天比昨天好,明天比今天好,这就是希望。一晃6年过去了,现在的那个男人是否还记得这句话,是否还有着希望呢?现在这个女人,又在哪里,生活的怎么样呢?她是他记忆中美丽的春天,是他难以忘怀的昨天,她像鲜花一样开放在那里,让他心动。刚开始还偶尔过节互发过短信,后来就再没收到过她的回信。想到这里,张小庆突然就很想给王碧薇发个短信,想了想,他还是发了一条问候短信,等了一会儿,没有回复,又等了一会儿,还是没有回复。于是,张小庆就直接给这个号码拨了电话,如果是个男人,他想,就说打错了。出乎意料,这个号码竟然是空号,这个号码是什么时候开始空号的?不知道。张小庆突然就笑起来,原来自己一直活在自己虚拟的世界里,和王晓丽谈恋爱,给这个号码发消息,强调王晓丽比自己大;换了工作,给这个号码发消息,强调自己挣得多了,结果人家早就不知道什么时候停机了。从活动中心出来,阳光很好,走在熙熙攘攘的人群中,突然就有那么一瞬间,特别的想念她,也许就在这一瞬间,她的微笑依然如当初一样,在这川流不息的时光中,依旧神采飞扬。

张小庆去了西钓鱼台,那里,和周扬一起挤过地下室,一起打过1毛钱一把的斗地主。只是,周扬此时已经不在了,那个当过保安、当过送货员、当过烤瓷工也自己创过业的男人,此刻正在老家实现着自己的梦想。张小庆就突然特别想给周扬打个电话,他拿出手机,想起来周扬回家后曾经给自己发过一条短信里面有他的新电话,于是,他开始一条条的将短信从前翻到后,终于还让他给找到了。对周扬,张小庆的心情是复杂的,最开始,自己是感谢他的,周末一有空就会找他打牌一起看球,后来,就有些看不起他,毕竟是没上过学的,处于社会的最底层,送货员,一个月才700块钱,再后来,联系就少了,直到妈妈摔断了腿,这个男人,陪自己一直坐到天亮,突然就特别感动,但后面还是联系少了,即使联系也都是他打过电话来,后来知道他在创业,心里就有些敬意了,最后回家,突然就发现原来他一直都在坚持自己的梦想。

电话接通了,张小庆说,周扬吧,干嘛呢?

还是老样子,周扬说,狗日的,怎么突然想起来给我打电话了?

张小庆说,我在钓鱼台。

周扬沉默了一会儿,现在是上班时间,张小庆现在在钓鱼台,他一定碰到什么问题了,他说,出什么事了?

张小庆想,总不能说自己把希望搞没了吧,于是,说,想你了。

周扬说,听说你要当爹了,具体什么时候?

气氛轻松起来,张小庆说,还有一个月呢。

从钓鱼台回来,穿过一座过街天桥,桥底下是车水马龙,此时此刻,站在这个繁华的街道,张小庆突然一阵慌乱,他找不到未来的方向,前面,一个年轻人正在弹着吉他为他的音乐梦想而歌唱,他是如此的年轻,连嘴角旁的胡须都还是软软的,不知道许多年过后,他是否还在坚持自己曾经的音乐梦想。

王晓丽是在凌晨2点住进医院的,预产期提前了,在此之前,王晓丽的爸妈和张小庆的爸妈都刚到,张小庆一切都是那么的手忙脚乱:手忙脚乱地收拾东西;手忙脚乱地冲爸妈喊,爸,那个东西带了没有?妈,那个东西带了没有?手忙脚乱地给出租车打电话,师傅,麻烦您快一点,我老婆要生了!手忙脚乱地给王晓丽穿衣服把她包成一颗大粽子,口罩!帽子!手忙脚乱地在产科走廊里跑,医生,我老婆肚子疼,破水了!

手术在下午1点,王晓丽很紧张,一言不发,父母们都在门外,时间很难熬,一会儿就问下张小庆,几点了。张小庆说,刚过5分钟。王晓丽说,怎么这么慢啊,你手机是不是坏了?张小庆说,没有。王晓丽叹口气,不再说话。张小庆抓住王晓丽的手,里面渗出很多的汗来,他轻轻的揉她的手,突然就体会到了她的紧张,对手术的担心、对小孩的担心。张小庆想,老婆真是辛苦了,怀胎十月,都是自己照顾自己,最开始是先兆性流产,后来4个月腿就开始浮肿,走路十分困难,最后就是整夜整夜的失眠。自己上班很远,每天7点出门,晚上8点半才能回家,她一个人在家,既要上班,下了班又要挺个肚子给我买菜,洗好菜等我回来,中间的无助和委屈又有谁能体会到呢。好几次上班时接到她的电话总是欲言又止,说,我很好。而自己总是很粗心,上班时很少给她打电话,晚上回到家吃饭完刷完碗就快10点,基本上讲个故事很快就独自睡了,留下老婆一个人翻来覆去的睡不着,老婆却总是说,你快睡吧,不用管我,明天还要那么早上班。想到这里,张小庆的眼圈突然就红了,自己欠王晓丽的实在是太多了。张小庆突然又想起了和王晓丽相处的整个过程,最开始多少是因为王碧薇的原因才和她相处的,王碧薇拒绝自己的原因是她比自己大,那么自己就偏要找个比自己大的女朋友,后来就渐渐发现她的好来,上进,节俭,虽然每次在她的父母和弟弟面前都要展现出一种城市的优越感来,但她是爱他们的,自己从来都舍不得吃舍不得穿,也曾困惑过她的家庭,但有时候就是这样,等待时间的过程中,一切都会发生变化。

王晓丽进了手术室,手术室外有几个凳子,张小庆刚开始还能坐会,过了几分钟,就坐不住了,不停的在手术室门口踱步,不断的看表,时而将耳朵凑到门上听里面的动静,有好几次,都好像听见小孩的哭声,还有一次,把从里面出来的一个护士吓了一大跳,他心情非常的复杂,各种情绪搓揉在一起,一会是紧张、搓手,一会又是担心,一会又在想会是个男孩还是女孩呢,如果是女孩的话,一定要像她妈妈,有一双大眼睛,要给她扎非常漂亮的小辫,恩,学生头也不错,穿漂亮的小衣服、小裙子,每天上班的时候和她挥手说再见听话,下班进门的时候亲她一口说爸爸回来了,哈,女孩真好,想到这里他不禁轻轻笑出来了。可是,女孩也有不好的地方,不放心啊,特别是未来谈恋爱,那么多坏人,如果被骗了怎么办,真是让人担心啊,想到这里他不禁微微皱了皱眉头。还是男孩好,放心,只会骗别人,不会吃亏,恩,男孩好,可是,男孩容易学坏,学坏了就麻烦了,抽烟喝酒上网,真是纠结。正想着,走廊里一位父亲扶着走过来一个小小的儿子,挪着小小的步子,嘟哝着小小的小嘴,在空中摇摆着小小的小手。张小庆不禁想,什么时候我的小孩也这么大啊,走到哪儿,他都在后面用小小的手抓住我的衣角,做我的跟屁虫,看着喜欢极了,随时就可以亲上一口,抱上一会儿,他甚至能够想到儿子被胡须扎着痒痒的样子,一定是左晃右晃,嘻嘻笑个不停,真好啊。恩,生两个就更好了,一男一女,一定要说服王晓丽再生一个。正乱糟糟的想着,门突然开了,护士说,谁是王晓丽的家属?张小庆一着急,大声说,到!护士看了这个男人一眼,说,男孩,6斤2两,14点01分生的,健康。张小庆仔细的看他的儿子,小家伙脸蛋红红的,眼睛还没有太睁开,微微皱着眉头,似乎对这个世界不太满意,额头上都是皱纹,像个小老头。幸福来得如此突然,瞬间就将他的心脏充满了,涨着他生疼,他想,我的整个世界就是他的了。护士小声对他说,恭喜啊,最近都是女孩。这句话加剧了张小庆的幸福,他似乎感到自己飘到了空中,一切都是那么的不真实,后来回病房他才知道其实最近全是男孩。岳父抱过小孩回病房,看得出他对小子非常满意,走路都踏着调子,看着儿子离开,张小庆的心突然又重新抓紧开来,那是为王晓丽担心,不知道她怎么样了,是否一切顺利。继续等待,这会心里全是王晓丽,一定很疼,会不会大出血,会不会有意外这么长时间,心情越想越糟糕,越糟糕越往坏处想,期间有工人送仪器走进手术室没穿消毒衣,不禁狠狠的咒骂:这卫生水平也太他妈差了吧!终于把王晓丽等出来,一把抓住她的手,都有点语无伦次,老婆,我爱你,你是我们家的大功臣。王晓丽笑了一下,清晰的从嘴里挤出一个字:屁!然后质问,你什么时候上班?!

王晓丽躺在大床上,儿子躺在小床里,一个说困了要睡会,一个一直在酣睡,一个身上挂满了管,一个蜷着小腿保持了出生之前的状态,这个世界对他来说是崭新的,张小庆突然就找回了曾经的动力,他不再是一个人的奋斗了,他有了儿子,要为儿子奋斗了。

出院回到家,给儿子洗完尿片,张小庆重新坐在了电脑前面,他打开电脑,打开熟悉的编辑器,写下一段代码,他重新体会到编写代码的快感,他是爱代码的。他想到杨晓常说的话,编码量,编码量,自己离10万行代码还差得很远,怎么就说到头了呢。他又想到自己对这个社会的抱怨,为什么抱怨,实际是缺少希望和信心啊,人什么时候都不能没有希望和信心,今天比昨天好,明天比今天好,这就是希望。

全文完,谢谢观赏!

posted @ 2012-01-29 22:36 ronghao 阅读(2877) | 评论 (8)编辑 收藏

给张小庆打完电话,周扬走到窗边,看着外边的天渐渐黑透,路上的路灯都亮起来,汽车开始排成长龙,红色的尾灯连起很远,年前的最后一天,下班时间,照例是堵车,旁边,行李已经打好,很简单,就是一个背包,那年他刚到北京时的那个背包,落寞的偎依在那里。要离开这个生活了快十年的城市了,奇怪,竟然有一种怀念的感觉,回忆这个东西,总是在离开的时候涌现出来。周扬突然就有些烦躁,他知道自己在烦躁什么,他不耐烦的给自己点燃一支烟,烟却也跟着烦躁起来。

去年的这个时候,张雨拽周扬参加了她们公司的年会,那是一个大学的体育馆,周扬第一次去这种地方,偌大的体育馆里挤满了人,现场嘈杂而又热烈。张雨一开始并没有和周扬坐在一起,当主持人报到第三个节目时,张雨她们出场了,年轻的姑娘们穿着漂亮的裙子,露出平坦而细腻的腰身,露出修长而青春的大腿,在场上健康的舞蹈,全场的气氛立刻就随着舞动着的腰身高潮了,那些搞程序的内向男人们,从座位上站起来,吹起口哨,面红耳赤,尖叫外向起来。周扬旁边的两个男生热烈的议论,那个穿红裙子的是谁啊,有没有男朋友啊?!周扬知道他们议论的是谁,他们议论的是张雨,她是这个节目的领舞,现场的大屏幕上,张雨那俏丽的身段一次次在捕获着所有人的眼睛,活泼而健康,简单而热烈,无数的灯光打在她的身上,此时此刻,这个地方,舞台无疑是属于她的,是属于她一个人的。热烈的灯光下,周扬却沉默了,他沉默的坐在那里,一动也没有动,旁边大屏幕旁边的一个小屏幕,那里,程序员们现场互动的短信和微博一条条的刷新出来,他看到了急求红裙妹妹电话号码,他看到了张雨我爱你,他看到了张雨嫁给我吧,他终于明白,张雨经常说的一句话,心有多大,舞台就有多大,舞台上这个光芒四射的女孩子,她的舞台是在这座城市的,她的梦想就是融入这座城市,成为这座城市的一部分,现在,她的梦想就要实现了。她开始化些淡妆,买一些东西时开始去大的商场,开始议论正在或将要上映的电影们,开始上微博,开始说话偶尔会带一两个英文单词,甚至,有一次,她对周扬说,我们一起努力在北京买个房吧。

五月的一天,周扬回了趟家,他订亲了。每次打电话,周实总要隐隐表达出对儿子终身大事的担忧,对于这件事,周扬总是表现出不耐烦的情绪,说我的事不用你管,这次,周扬竟然突然给他打了电话告诉他帮自己找个对象,周实是知道周扬在北京有个女朋友的,但他也不敢去细问,周扬叹口气,说,找个性格和你一样的。

姑娘是周实他朋友的女儿,比他小四岁,不知道为什么,第一眼见到这个姑娘,周扬心里就猛然跳动了一下,姑娘的话不多,很安静。

周扬说,你叫什么名字?

姑娘的脸微微涨红了,低声说,我叫张静。

周扬说,你有什么爱好吗?

张静摇摇头,脸又红了,说,没什么爱好。

张静中专毕业,毕业后就在市棉纺厂做工,指头细长细长的。见过几次面,周扬问了姑娘的意思,就提了亲,把姑娘带到了北京。其实和张静定亲还有另外一层意思,2月份的时候,周扬和两个人出来单干了,他们在石景山租了一处平房,周扬负责找客户和送货,那两个人负责做牙齿,很快,那两个人做牙齿的速度就跟不上周扬的速度了,要找人,周扬他们刚起步,没有注册公司,什么都不正规,客户也只对私人诊所,工资不可能很高,所以找人就很难,于是,一有时间,周扬也自己做牙齿,终于一天,一连做坏了好几颗牙齿,吹灭了酒精灯,周扬突然就想,做牙齿是个细致活,自己还是需要有个女人来帮忙的,又想到明年要回家自己干,这个女人就很重要了。现在,这个女人就是张静。

和所有的女人一样,张雨的第一句话是,为什么?

周扬躲开张雨幽怨的眼睛,慢慢的说,我们不合适。

周扬没有回答她的问题,张雨加重了语气,眼泪快要顺着眼眶流出来,她努力使它噙在眼眶里,说,为什么?

这次,周扬决定正面回答这个问题了,他盯住张雨的眼睛,说,我们真的不合适,你不觉得吗?你是一个有想法的人,这是我喜欢你的原因,但,我们的想法是不同的,你是属于这座城市的,你懂吗,你是属于这里的,这里有你喜欢的一切,商场、电影、街道、写字楼、周围的人们,你喜欢它们,因为你就是它们中间的一部分,因为它们就是你想要的。而我,注定是要回去的,尽管在这里生活了十年,但我从来都没有喜欢过这里,从来没有,这座城市太大,所以大部分时间我都奔波在路上,我不喜欢这种感觉,不喜欢,孤单、奔命,连吃个饭都在赶时间,这座城市最热闹的地方是什么,不是商场,不是KTV,也不是马路,是快餐店,我不喜欢快餐,我喜欢小时候,坐在自己家的院子里,一屋子的人,所有人一起吃饭,说话,大笑,很简单,这是我想要的。我爸打工砸断了脚,我不想过后很久才知道,我不想,我不想只是问两句然后寄点钱回去,钱是个什么东西,面子、地位还是女人,它其实什么都不是,它能补偿什么吗,它其实什么都不能补偿,很多年之后,当我们老去,我不想自己能谈的只剩下一点钱了。

眼泪,终于遏制不住的流下来,张雨说,我终于明白了,在你心里,我只是一个有想法的女人,只是这样。那么多的日日夜夜,那么多的白天和晚上,在你这里,只是有想法这么轻飘飘的一句话,我就那么的不堪吗?!我就那么的不女人吗?!

周扬想起来,那些日子,每天上完夜班,张雨都会为自己煲上热粥,去东莞的日子里,又是谁一天天的给自己打电话,让自己在落寞的夜晚里突然有了牵挂,想到这里,他心里一酸,过去的一幕一幕如放电影一样一张一张的绽放出来,只是,这电影胶片现在是黑白的。

张雨说,你说我喜欢这座城市,是的,我是喜欢这里,这里的街道,这里的楼房,这里的人们,为什么,因为公平,在这里,你想要什么,如果你去奋斗,只要你努力,你就会得到,这里是自由的,没有人束缚着你,没有人强迫你嫁人,也没有人强迫你做你不喜欢做的事情,一切都是你自己做主,你的命运取决你自己。我真的喜欢这里的街道吗,街道不过就是一走路的地儿,我真的喜欢这里的商场吗,商场不过就是一买东西的地儿,我真的喜欢这里的写字楼吗,写字楼不过就是一工作的地儿,这些不过都是生活方式,生活方式是可以改变的,但我真正喜欢的其实是这里的生活态度,公平、自由,只要你足够努力,你就能得到自己想要的东西。而你,就是我最想得到的。

有那么一瞬间,周扬很想一把抱住眼前这个可伶的女人,对她说,跟我走吧,但他克制住了自己,不能这样,他对自己说,不能这样,这样对她太不公平,她为了今天已经付出了太多,这里有她的梦想,有她的未来,自己这么做就太自私了,虽然爱情看上去是个神奇的东西,但一旦和牺牲联系起来,就一定没有好的结局。

张雨抽搐着她那好看的小鼻子,说,你要回家和我商量过吗,你怎么就断定我不会和你一起回去呢,我现在就告诉你,年底我要和你一起回家,我决定了,这是我这辈子最重要的决定。

周扬等了一会,等张雨的情绪稍微平静,然后一字一句的说,我订婚了。

半晌,张雨才反应过来,她被这个消息惊呆了,很久,才故作轻松的说,这不可能,你骗我,我怎么不知道!

周扬冷冷的说,她叫张静,就在厂里,你要不要见见?

张雨语言中开始慌张,说,我不相信,我不相信,她长的好看吗,她很年轻吗?

周扬说,比你好看,是我最喜欢的那个样子。

张雨一边摇头一边往后退,泪水再一次流了出来,一个女人,不管她是否年轻,也不管她是否有钱,最打击她的永远只有一样,那就是相貌。周扬突然就有些慌张,他从来就没见过张雨这个样子过,心里一痛就向前走了一步,张雨却尖叫起来,不要碰我!一边说着,一边转身就跑,一边跑一边对自己说,这不可能,这不可能。周扬想去追张雨,却站在那里迈不开步子,泪水,突然就流了下来,张雨的身影越来越模糊,一眨眼,就那么看不见了。就这样吧,周扬对自己说。

接下来的日子,忙碌而又漫长,工商注册,开通银行账户,开通电话,联系会计公司,开始去医院联系客户,周扬一次又一次的让自己拼命忙起来,只有这样,他才会忘记她,有点时间,他就把他的好全部倾泻在张静的身上,他带她去商场买很贵的衣服,他带她去看电影,他带她去各个景点吃各种东西,而这些,从前都是张雨拽着他去的,他从未主动过。

快到年底,周扬给两个兄弟说了要回家的事,两个兄弟都很震惊,公司第一年收入很不错,已经进入正轨,现在也有十个人了,他们说每个人都匀出些股份给周扬他在里面占大头,周扬笑笑说还不如多给我些钱吧。接着,周扬把他的决定告诉了周实,周实也不敢相信自己的耳朵,他说,你想好了?

周扬说,从第一天起就想好了。

放下电话,周实的心情是复杂的。不知从什么时候起,他们这代人就那么老去了,人一老,什么荣誉,什么地位,什么金钱,突然就看得淡了,即使看不开也心有余而力不足了,李秀也不再向以前那样说他,疾病慢慢开始找上他们。郭树得了癌症,之前他在市里提到了局长,儿子大学毕业也安排到了市里,每到逢年过节,他家里总是人满为患,人们都觉得他混的好,但是突然查出病,家里就不再有一个人去探望,反倒是周实,他去医院看了郭树,郭树拉住他的手,半天说不出话来,人,不就这么一回事吗,年轻时总要争个位置争个面子拼命往上爬,很多年过去,重新看这些东西,真的那么重要吗?同样,对于周扬这些人,家里人总觉得在外边混的好的人才有出息,当然,有关系在市里做公务员的除外,于是,每到过年,那些平时难得一见的中年男人们,拉着老婆和小孩,开着各式各样的小汽车拥挤在家乡的泥巴地上,他们把喇叭按的清响,把家里的小楼盖得老高,他们那是在说,看,我在外边混得很好哩。其实,对每个人来说,冷暖自知,又有谁知道他们在外边的辛酸呢。现在,周扬要回来了,他是怎么想的呢,他是在外边混不下去了吗?

走在站台上,周扬接到了一个电话,这个电话让他的心跳动加快起来,他明白,这段时间一直在烦躁什么了,电话是张雨打来的,上次分手后他们就再没联系过,现在,她却突然打过电话来。她在哪里?她为什么打这个电话?

周扬说,喂。

手机里传来张雨气喘嘘嘘的声音,你们在哪节车厢?

周扬说,你在哪儿?

张雨说,不用问了,我看见你了!

不远处,一个围着红围巾的年轻女人迎面跑过来,正是张雨。于是,在一个年轻的午后,北京西站的站台上,张雨、周扬和张静三个人见面了。张雨看见张静了,她长大了嘴巴,周扬说他最喜欢那个样子的张静,竟然,竟然,和自己长得那么的像,而对面的张静,也同样长大了嘴巴,两个女人突然就都明白周扬的心思了。

张雨和张静打过招呼,说,我想和周扬单独说一句话,可以吗?

张静看看周扬,她看到周扬点点头,于是说你们聊然后就先上了火车。

张雨幽幽的说,要走了也不打声招呼吗?

周扬说,时间太紧了,没来得及。

张雨说,再抱我一次可以吗?

周扬摇摇头,说,不好。

张雨笑起来,她笑起来依旧是那么好看,她说,想起来了,你已经是个有家的男人了。

周扬点点头,也笑起来。

张雨说,好吧,我其实来就想给你说一句话,这句话就是,谢谢!

周扬说,也谢谢你!

好,张雨伸出手,说,再见!

周扬说,再见!

posted @ 2012-01-29 22:35 ronghao 阅读(2476) | 评论 (3)编辑 收藏

 四月的一天,张小庆和王晓丽去了北航旁边的北医三院,生殖医学中心,到这种地方去,让张小庆觉得很尴尬。三个月了,王晓丽没有怀孕,这让她很着急,她说,我都30了,再不生就很难生了。张芳也很着急,她找了村里一个据说对不孕症最擅长的小学还没毕业的土医生,那个医生说是有祖传的偏方,于是,王晓丽喝了三天从老家寄来的不知道是些什么东西的草药,又一个月过去了,还是没有动静,于是,王晓丽最后还是决定带上他的男人去西医院检查。早上京通快速特别的堵,这说明又开会了,王晓丽很着急怕挂不上号,张小庆却是一脸轻松,他安慰王晓丽,这种地方,没多少人去,随去随看。从地铁口出来,眼前的场面却让张小庆大吃一惊,生殖中心排队的人竟然都排到北航门口去了,不得已,北航的保安都出来了维持次序。

       靠,这是怎么了?张小庆问自己,他突然想起来,每次走在路上,包括在药店里,在电线杆上,到处都是各种中药壮阳的广告,他以前从来没有在意过,这次,他终于明白了,原来,在这个国度,男人大部分都是阳痿。

       11点的时候,张小庆排上了号,下午的号。医生都没等张小庆把情况说完就已经开出了检查单,交完钱,在厕所门前,男人们继续排队,三个坑,竟然还有一个坏了,所有的男人都面无表情不说话,一个出来一个进去,竟然没人插队,在这种地方,唯一有成就感男人只能是那些医生。终于轮到张小庆了,就在他准备进去时,突然有护士说了声检验科下班了不再收标本。张小庆张大了嘴巴,从早上排队到现在,怎么说下班就下班了?他去找护士,护士根本就爱理不理他,想到下次还要多来一次,张小庆的头都大了,这边,王晓丽刚从诊室出来,她带着笑容,说,我没事,你呢?张小庆说,我好像还要再来一次。

       后来,张小庆交过钱的化验单就成了废纸,当他准备再去北航时,王晓丽怀孕了。王晓丽让他去退钱,他却不愿意去,那种地方,太压抑。王晓丽怀孕后的反应很大,腿很快就肿了,不想吃东西。这天上班的时候,张小庆突然接到王晓丽的电话,说是流血了,赶紧请了假,去王晓丽公司接了她去医院,医生说是先兆性流产,不能上班必须休息。两个人商量了很久,王晓丽的公司在北京,每天上班都需要4个小时,怀孕了,不能去工地,接不了活,基本工资就800块钱,930上不会有人让座,人太辛苦,最后,他们决定王晓丽不上班了就在家休息。张小庆的生活就此发生了变化,每天早上6点半的时候,张小庆起床了,他先是给王晓丽做上早饭,然后洗漱,7点半的时候,他出门去挤930,9点半到达公司,晚上6点半下班,稍稍耽误一下,7点钟出发,8点半到家,王晓丽已经把菜摘好,然后开始做晚饭,吃完陪王晓丽看会电视就10点,然后洗碗和洗漱,11点,他的一天就结束了。最开始的时候,张小庆还在930上站着看会书,但他很快发现,他最想做的事其实是能有个座睡会,周末的时候,他还想打开电脑写会程序,但这也变得不再现实,一天,王晓丽突然大声的对张小庆发了脾气,把电脑关上!你什么时候完整陪过我一天的?!张小庆理解王晓丽,怀孕了,一个人在家,日子很难过,王刚刚生了小孩,岳父母过不来,这边,妈妈的腿还需要拐杖,本身就需要照顾,父亲还有几个月才能退休。于是,张小庆不再看书,除了工作,也不再写程序,每天,他感到最幸福的时候就是和王晓丽一起看会电视,他突然发现,很久不看电视,那些电视剧还是有些看头的。

       这天一起吃午饭的时候,项目经理突然说了句:我们公司工资是不高,但是对于混日子的人来说是够了。

       张小庆的心突然就被刺疼了,他知道经理在说谁,他已经不止一次的在自己面前说过类似的话,他是在说自己。老实说,这也不能怪经理,项目中间换了技术方案,每个人都分配了工作之外的学习任务,进度很紧张,但是自己却总是不能抽出时间来学习,好几次,他都把经理分配的课外作业给忘记了。

       经理终于单独找了张小庆,他说,我觉得,你不适合做技术。气氛有些压抑,有那么一瞬间,张小庆感到自己被深深侮辱了,奇怪,他突然想起了秦涛,想起来自己当时当着所有人的面批评他选择的技术方案,他当时的感受一定和现在的自己一样吧,这难道就是报应,你相信因果循环么?

       和经理谈完话,张小庆想着自己也许是应该做点什么了,但转念一想,他的内心突然就阴暗起来了,你不过就是一个项目经理,你凭什么这样说我,你算什么呢,我就这样了,你也没有权利解雇我,大不了涨不了工资而已,我怕什么呢,是啊,我怕什么呢。这么想着,他的情绪就激动起来,我拿着8小时的工资,我就干8小时的活,谁他妈规定8小时以外还要做和工作相关的事呢。再过一会,发泄完了,他又想,这其实还是自己的问题的,做任何工作,要想做好,都是要不断学习和思考的,现在的自己,真的很差劲,换成我是经理的话,也会很不痛快的,但是,现在有什么办法呢?有什么办法呢?为什么自己会成现在这个样子呢?为什么呢?他问自己,他找不到答案。突然,他想起来,自己刚写博客的时候,对什么问题都喜欢刨根问底,有个评论是这么说的,你用的很仔细,我以前和你有得一比,但是现在我30了,没时间那样学习,即时有也懒得学了,真羡慕你。原来是这样!张小庆突然觉得自己就找到答案了,这个答案就是自己快30了!因为30,所以就成了现在这个样子!这个答案很合理,30岁了,结婚了,有家庭了,日子不再像以前那样简单,上有老下有小,要考虑的事情多了,所以,就中年危机了。

       日子就这样一天天过着,期间张小庆尝试着改变了几次,但最后都失败了。他终于变成一个顾家的中年男人了,买菜、做饭、陪老婆、逛超市、看电视、挤车睡觉,他开始变得心安理得,变得懒懒散散了,他忘记自己曾经的梦想了,他只是看着日子是日历上的一个个数字,翻过去就过去了,没有惊喜也没有期望了。只有一件事是一个例外,这件事就是他买房了,10年底的房价再一次瓶颈了,他在燕郊买了一套70平米的两居室,7000一平米,首付了20万,20万,如果放在几年前对他绝对是个大数目,但是随着通货膨胀,这钱怎么看都不值钱了。房子12年下来,交完首付,按道理说他应该高兴才是,毕竟这是他和王晓丽好几年奋斗的目标,但是真的到了这一刻,他却一点都高兴不起来,对他来说,这似乎是顺理成章的一件事,没有兴奋也没有激动,他甚至问了自己,这真的是自己想要的吗?!自己来到北京,目标就是一套房子?这是自己想要的吗?也许是吧,可是,为什么自己一点都不高兴呢?为什么呢?也许,这并不是我真正想要的?是的,这不是我想要的!那么,我想要的是什么呢?他再一次找不到答案了。一旁的王晓丽则显得非常兴奋,她说,我们终于有自己的房子了,完全靠我们自己挣出来的房子!小庆,你高兴吗?!

       张小庆没有正面回答,他陷入30的慌乱中去了,他只是默默点点头。

      王晓丽继续说,小庆,这里是河北,我们一定要在北京买套房子,你同意吗?!

      张小庆还是只是默默点点头。王晓丽太兴奋了,她甚至没有留意到张小庆的情绪。

     年前的最后一天,很意外,张小庆接到了周扬的电话,周扬说,兄弟,最近咋样?

     张小庆说,还好,你呢?

     周扬说,我要回家了,不来了。

posted @ 2012-01-12 23:36 ronghao 阅读(2515) | 评论 (5)编辑 收藏

五月的一天,张小庆、王晓丽和高晨晨一起在东直门公交车站等车,他们要去怀柔的雁西湖,五月的北京,虽然还挂着春天的名号但已然是夏天,天很热,人很多。

王晓丽的心情是愉悦的,房价还在下降,现在手里已经有了5万块钱,比预想的要顺利,年底10万块钱应该不是问题。但是也有烦心事,那就是房东要涨房租了,从现在的1200涨到1800,因为是合租,所以每月要多支出300块,房价在降房租却在涨,不知道是什么道理,电视里说是中介联手在操纵市场,要行政干预。昨天的时候,王晓丽和王超打了电话,主要是过节问候一下,但也告诉他手里现在有了5万块钱,年底能够攒到10万,让父亲觉得女儿在北京混的还不差。

张小庆的心情却是复杂的,公司由于主要是外包所以受金融危机的影响很大,上半年几乎没有什么项目,不断有人离开,另外一件事就是他现在所做的项目,这是一个咨询项目,巧得很,客户竟然就是高晨晨所在的公司。项目很不顺利,国企,项目开始第一天的启动会议上,竟然就有人让他们下不下台,开发经理直接质疑他们的能力。这件事让张小庆很意外,他理解的咨询项目就是帮助客户找到问题并进行改进,很简单的一件事,客户竟然有人不支持,他不能理解。后来,随着项目的进行,他突然就明白了,既然是找问题,那么必然就会有人要为问题负责,客户并不是看不清问题,只是想借用咨询的方式来推动问题的解决罢了,说到底,客户是需要借用外力来处理人的问题。这个咨询项目的发起人是公司的一个副总,抵制者是公司的开发部门经理,而这个开发经理是另外一个副总的手下,两个副总一直不和,高晨晨这么一说,关系就非常清楚了。

项目实施不顺利,生活中却有意外发现,张小庆竟然和他高中同学罗勤意外见面了。那天,张小庆从高晨晨公司出来,心情郁闷,顺着清河小营桥走出很远,经过一个修车铺时,突然听到了熟悉的乡音,再一看,惊讶的下巴差点掉下来,那不是罗勤吗!罗勤没有考上大学,后来听说当兵去了,然后就没了消息,这一别就是9年,现在,老同学见面,都非常激动。

张小庆说,狗日的,操!

罗勤说,妈的!

再后来,高晨晨、张小庆和罗勤就一起吃了饭。罗勤个子不高,皮肤黑黑的,应该是去青海当兵晒得,人很结实。从罗勤嘴里,张小庆知道,他高中毕业后就去当了兵,然后就是转司机又做了2年士官,后来就一个人到北京来,先是帮别人修了几年车,攒了些钱,现在是租个10来平米的门面在自己干。门面小,靠的是回头客,所以罗勤一直把“实在”这个词挂在嘴上,有很多出租车司机找他修车。

张小庆突然说,罗勤,你结婚了吗?

罗勤说,没呢,还没有朋友,没有时间啊。

回去的路上,张小庆和罗勤说起了高晨晨,说她现在和男朋友分了手也单着,说记得上高中时你不是很喜欢高晨晨吗。罗勤没有否认,他笑了笑,露出洁白的门牙,说,是啊,现在也喜欢。张小庆说,追一下!罗勤说,人家北京户口,大国企,那看得上我呀。张小庆说,不试怎么知道呢?罗勤不再说话。

高晨晨的心情是低落的,年初的时候她和恋爱5年的何鑫分手了,这还并不是最糟糕的,最糟糕的是她突然就发现自己已经28岁了,28岁,真是一个尴尬的年龄,找朋友时,30岁的男人,事业有成的都愿意找年轻一些的,还在奋斗的她又看不上,她现在7000块,总不能找一个比自己收入还低的男人吧,于是,她就一日又一日的单着。刚和何鑫分手那阵,和所有感情失意的女人一样,她拼命的给自己买衣服,一到周末就看电影吃东西,晚上就上开心和QQ,日子一长,情绪就焦虑起来,她明白,还是要找个人结婚的。她恨何鑫,分手后很长一段时间她都不愿意见到他,但终究还是要见的,因为房子,买房时他们做过公正,她有40%的所有权,房子100个平方,买的时候7000,现在市场价11000,她自己投有10万块钱进去,现在,何鑫答应给她40万,一方面算是还钱,另一方面算是对她的感情补偿,但是40万也不是一次性付清而是分好几年还清。

       张小庆突然对高晨晨说,你觉得罗勤怎么样?

       高晨晨犹豫了一下,她不明白张小庆为什么问这个,她说,还不错吧。

       说话的时候,张小庆的手机突然响了,是王超,张小庆感到有些奇怪,因为王超一般是不给自己打电话的,打都是给王晓丽打。他接了电话,喊了声爸。王超说,小庆吧,有个事要麻烦你一下。

       张小庆说,爸你说。

       王超说,爸有点事,想找你借5万块钱。

       张小庆愣了一下,他不知道说什么才好,一时间没了言语,想了半天,才说,好的,这个,这个,我做不了主,您和王晓丽说一下吧。说完,他把电话递给了王晓丽。

       王晓丽接了电话,说了没两句,她的脸色就沉下来了。事情是这样的,王超在家要请个农耕机,以前是说一声就可以,这次说了好几次却不见人来,太忙了,再去找的时候两个人就吵起来,吵起来说起话就难听。

       王超说,你他妈面子还挺大。

       对方说,我就面子大怎么了,你他妈混了几十年连个农耕机都买不起,你还好意思。

       对男人来说,最怕别人说自己没本事,王超就更是了,自从从砖瓦厂回来,自己就一直被村里人嘲笑,有好几次打牌输了钱找人借钱都不给借说他玩不起就别玩,这次,他又一次感到自己受到了侮辱,他一把抓住对方的衣领,但是,他已经是一个50岁的老男人了,对方还不到40岁,只轻轻一推,他就软绵绵的倒在地上,从地上慢慢爬起来,他说,你等着瞧。于是,他想到了王晓丽,想到了他在城里享福的女儿,他想起来,王晓丽在他面前说过,张小庆一个月能挣1万块,他想起来,当时自己为了给王晓丽上大学到处低三下四借钱的情景,现在,凭什么她在城里享福,自己却在这里吃苦,这么想着,心里狠狠起来,但他也知道,之前张芳生病都是王晓丽拿得钱,他不知道他们现在有多少钱。正在犹豫的时候,王晓丽突然打过电话,说他们现在手里有了5万块钱,正好,村里现在要通省道,之前有个朋友说可以一起凑钱买个大车拖材料,每人出5万,这样刚好可以用这5万,于是,第二天,他就给张小庆打了电话。

       在怀柔,王晓丽的心情很不好,张小庆也是,他想不通王超为什么一开口就是5万,这是自己全部的积蓄啊。

       王晓丽说,其实我爸也不容易,就想做点生意,赚点钱,老都老了,被别人骂没本事了几十年。

       张小庆不说话,想了想,说,那就借吧。

       王晓丽说,全借可不行,这是我们买房的钱,这样吧,借2万,反正现在房价正在降,年底说不定8万也够了。

       张小庆说,好。

       于是,五月末的一天,王刚从镇里开回了一台崭新的农耕机。

       事情并没有向着王晓丽期望的方向发展,刚过六月,房价突然又开始猛涨,看过新闻,张小庆发现,政府的4万亿很多投向了房地产。周末的时候,王晓丽和张小庆又去了回龙观,7千的房子没有了,最便宜也要1万,中介说了,每天都在涨,再不出手就没这个价了。王晓丽有些绝望,张小庆突然想起来什么,他说,我们去燕郊看看吧。于是,他们又去了燕郊,2年没见,燕郊又盖起了更多的房子,价格也是从之前的4千涨到了5千多,刚好有个楼盘在排号,真巧,那个楼盘正是之前他们交过定金又退掉的那个楼盘,王晓丽几乎是想都没想,立刻交了1万块钱的定金。开盘日期定在1个月之后,那些天,王晓丽天天在看电视,电视里说现在房价涨得比07年那阵更厉害,王晓丽天天在祈祷,她对张小庆说,开盘时不会涨得太离谱吧。张小庆安慰她,说,不会,前一期才4000呢。

       开盘那天是人山人海,开发商在门口用铁栅栏绕了好几个圈,和霍营地铁站一模一样,房子有300套,700多人交了定金,于是,有人插队,有人不让插,就打起来了,110也来了,营养不良的保安们根本维持不住次序。轮到王晓丽他们,一看开盘价格,傻眼了,要7000,即使最小的1居室也要40万,现在手里只有5万多,王晓丽突然就剧烈后悔起来,如果不借那2万,现在应该首付就够了。没办法,这就是天意。从售楼处出来,两个人又都默默不语,王晓丽特别失望,张小庆突然就想起来,新年的时候,自己和王晓丽那么充满希望的在王府井的大街上大声呼喊,这个时候,刚刚过去8个月,希望就破灭了。生活似乎又一次玩弄了他们,2年前,也是在同一个地方,他们无奈的退回定金,这次,又是这样。

       王晓丽说,我们换房子吧,现在租的房子太贵了。

       张小庆说,好,换到哪儿?

       王晓丽说,燕郊。

       和高晨晨吃饭的时候,高晨晨问为什么换的那么远。王晓丽苦笑了一下,说,我们租了个一居室,自己住,我快30了,该要小孩了。你呢,什么时候结婚?

高晨晨说我还没想好。高晨晨谈朋友了,不是罗勤,罗勤后来也找过她,但她几乎想都没想就把他排除掉了,她从他身上看不到房子的希望,至少是目前。她的新男友在国家剧院,工资不高,只有3000多,这是她犹豫的地方,但他们单位有经济适用房的指标。何鑫一直都没有给钱,自己也找过他几次,但他每次都说没钱。

       09年,似乎对每个人都不顺利。

posted @ 2011-12-18 22:35 ronghao 阅读(1858) | 评论 (3)编辑 收藏

张小庆向金鹏提出了离职,和他第一次向比尔提出离职一样,他是矛盾的,对科技动力,他是有感情的,特别是妈妈摔伤那一次,公司给了他很大的帮助,对于这一点他是记在心里的,3年时间,他从一个菜鸟成为一个他自认为刚刚入门的程序员,和公司给自己的培养是离不开的,他是感恩的,但另一点让他痛苦的是,他想写出真正有价值的代码,而公司提供不了这一点,他不想再做政府和国企的面子项目。他去面试思考加速时,面试他的程序员说,没写够10万行代码,是不够格称程序员的。这句话让他喜欢,他就想这样,能够自由的写代码,做自己喜欢的事情。

和比尔一样,金鹏问了张小庆的下一家公司,然后,他叹口气,说,好公司,去吧。

离开科技动力之前,张小庆突然就特别想为公司做点什么,他在自己的博客写了篇招聘文章,很快就收到了一些简历,其中一个四川的小伙子,刚毕业一年,普通大专,经验也不多,但对写代码特别有热情,这打动了张小庆,他想,有时候,经验真的并不重要,重要的是你对自己做的事情一定要有热情。他想,能够找到这样的人,也算是自己对公司的一个弥补吧。

走的时候,和刘哥、杨晓分别吃了饭。

刘哥说,你终于脱离苦海了。

杨晓说,连你都走了。

张小庆说,你呢?

杨晓说,我还没有干够,在一家公司,我想好好干上好多年。

张小庆去思考加速上班了,新公司以离岸外包为主包括一部分咨询,让他喜欢,扁平的组织结构,敏捷的开发方式,随时的交流和分享,免费的各种饮料和点心,最最重要的是,周围的同事都是牛人,他们对代码都有着异乎寻常的热爱,单元测试、持续集成、数据库版本控制,一个都不能少,看见不爽的代码,他会很快走过来,说,我们一起做个重构吧。这正是张小庆想要的。

年底的一天,坐在温暖的办公室里,抬起头的时候,外边的天气很好,目光从明亮的窗户扔出去刚好能够触到西直门,这应该算是北京的好天气。张小庆突然回想起去年的这个时候,也是坐在办公室里,在上地,不远处的信息环岛,运通105在缓缓挪动。他很喜欢运通105,尽管有很多车可以选择,但是运通的司机总是很生猛,他能够骂骂咧咧地迅速变线超车,也能够抢在绿灯的最后一秒穿过路口,上他的车你需要确实坐稳扶好。下雨的时候张小庆会去坐105,而平时则是骑车,他的那辆自行车是刘哥给的,几乎每个月都要去修理一次,修车师傅也早和他成了老熟人,最近一次修理是刹车时用力过大结果闸应声而断了。自从12月换了工作,坐城铁上班,自行车就开始生锈,每天上班时经过车棚,看见布满灰尘的自行车,张小庆突然就会有一种极不真实的感觉,我认识它吗,为什么它会显得如此之陌生,生活,似乎和电视频道一样,一瞬间就能完成转换。

新公司让张小庆兴奋,但他也发现了一些他不是那么认可的事情,第一是人们都不喜欢写文档,在自己的项目团队里,需求是写在一张卡片上的,只有一个标题,这样的好处是强迫需求分析人员与程序员面对面交流,但交流后却没有人去把需求写完整保存起来,这样如果后面项目维护起来就困难了;第二是人们似乎对开源软件有着异乎寻常的喜爱,项目中需要用到工作流引擎,几乎不用想,一定是基于开源工具进行封装,很多时候,很多时间都用在了对工作流引擎的二次开发上,这个时候,面对这个以流程为核心的系统,似乎采购一个商业版本是成本更低的选择。

元旦的时候,张小庆拿到了第一个月的工资,有些出乎他的意料,拿到手的工资没有他想象中的那么多,他这才想起来,新公司没有避税。不管怎么说,新年了,也该小小庆祝一下,张小庆叫了王晓丽一起去了王府井,他们一起逛了街,本来张小庆是打算给王晓丽买件衣服的,但最后,王晓丽觉得都太贵了,一开始两个人想去吃必胜客,最后也变成了肯德基。人很多,天气很冷,但走在人很多的大街上,两个人却感到很幸福,他们算了账,房价正在下降,回龙观的房子最便宜的7千多,60平米的话,需要45万,这样10万块钱就能首付了,按现在的工资,再省吃俭用一些,明年年底也该能够攒够。王晓丽很高兴,她的脸冻的通红,但她再一次看到了希望,她大声的说,张小庆,我们能有自己的房子了,你说对吗?!

张小庆大声的回答,对,明年这个时候,我们就有自己的房子了!

同样是这一天,高晨晨和何鑫在他们装修好但还没入住的新房子里大吵了一架,还是那件事情,何鑫不愿意结婚。

高晨晨说,不结婚就分手!

何鑫说,分手就分手!

从房子里一个人出来,路灯坏了,下台阶的时候,高晨晨跌了一跤,摔得很重,半天起不来,摸一下膝盖,慢慢的都肿起来了,肿的老高。这样一个时候,所有的恋人都在享受新年快乐的时候,高晨晨一个人跌坐在台阶上,想起自己一个人,何鑫一直都不愿意结婚,眼泪突然就从她眼睛里流下来,她不知道该怎么办了。

周扬的新年则是在布满粉尘的车间里度过的,他在赶一批活。08年对周扬来说发生了很多事情,最开始是新的劳动法实施,他们之前是没有合同也没有保险的,这次,周扬第一时间找了何林,何林最开始态度很坚决,不签,但周扬立马去找了劳动仲裁,这下,何林有些罩不住了,但他最先做的不是和所有人签合同,他做的第一件事是裁人和降基本工资,对业务员来说,他们的工作量突然就加重了,对烤瓷工来说,基本工资降了100块。正如周扬想到了,有人骂他多管闲事,我签不签合同管你屁事,这下可好,一个月700块钱都没了。

第二件事是周扬去四川当了一个月的志愿者,地震一发生,看到电视的第二天周扬就买了去成都的火车票。在那里,和许许多多的年轻人一样,每天十几个小时都在清除瓦砾寻找幸存者,有两件事深深的触动了他,一件是搜寻一幢倾倒的楼房时,他发现了一条狗,这条原本被拴在地板上的狗由于房子倾斜变成被锁链紧紧的吊起来,它被活活吊死了,已经死去很久了,但死前显然挣扎了很久,脖子上都是伤痕,眼睛里全是绝望。周扬突然就强烈感受到生命的渺小,死亡是如此的容易,如此的让人绝望,与突然消逝的生命比起来,金钱、面子、一切自以为重要和过不去的事情突然都变得无足轻重起来,很久以后,当你回过头来,重新看那些当时遇到任何的慌张抑或美好,低谷抑或高潮,你都会告诉自己,这只是你生命中的一段经历而已,仅仅是经历。生命的旅程不会因为这些事情而停止,它依旧会不停歇的往前走,每个路口的绊脚石仅仅是路边的一处小风景,跨过去就没什么了;即使是摔倒,爬起来也没什么了,只要,你还活着。第二件事情是在一处房屋的废墟前,他碰到了一个失魂落魄的年轻人,这个年轻人的父母妻儿都埋在他面前的这堆废墟中,而他,则刚刚从广东赶回来,在外边挣再多钱又有什么用呢,有什么用呢,没用,失去了家人,钱又有什么用呢,年轻的时候总想着出人头地,怎么出人头地?就是挣钱,把钱寄回家里,这样父母就能脸上有光,真是这样吗,他们需要的真是钱吗。这么想着,周扬就坚定了回家的想法,北京只是一段经历罢了,他的终点,在家里。

 

第三件事是张丽考上成教了,每个周六都要去旁边的工商大学上课,另外,就是,七月份的时候,她换工作了,去了中关村的一家IT公司。去新公司上班的前一天,张丽请周扬吃了饭,在钓鱼台的天外天烤鸭店里,张丽的情绪很高,脸蛋红红的,很高兴,她说,这是我们新的开始!很奇怪,周扬的心里却有一种酸酸的感觉,这天,他破天荒的喝了点酒,还抽了烟。一顿饭下来,都是小姑娘在那里不停的说,不停的憧憬,周扬则只是喝酒,直到最后,小姑娘才突然意识到面前的这个男人不像平时的他,她讪讪的住了口,问面前这个她爱着的男人,说,你怎么了,不高兴吗?

 

周扬说,没有,为你高兴。

 

张丽是何其的聪明,她说,为什么是为我高兴而不是为我们高兴呢?

 

周扬没有说话,他了解他面前的这个女孩,他很早就知道,这不是个简单的女孩子,她有着她自己的理想,这个理想在北京,是属于北京的,是属于这个城市的。

 

张丽等待了一会儿,幽幽的说,你要是不愿意,我可以不去的。

 

这次周扬打断了她,说,瞎说什么。

 

于是,北京的秋天里,那些新来的男人们,一次又一次流着哈喇子看着张丽亮晃着她那修长而健康的大腿出现在他们的过道里,他们会窃窃私语,他们会嫉妒,他们会因为嫉妒而发狂,而当他们知道这个女人的男人是周扬时,他们又立刻会收回他们邪恶的念想,周扬的,这就没错了,就应该是这样的。

 

元旦照旧是加班的,过年是一定要回家的,面对越来越漂亮和自信的张丽,周扬却越来越清楚他们是不可能在一起的,他们根本就是两路人,而正因为两个人都是有想法的人,所以每个人都是不会妥协的,那么,就只剩下一条路,那就是,分手。

posted @ 2011-12-04 23:08 ronghao 阅读(1734) | 评论 (3)编辑 收藏

张小庆打车到医院的时候,王晓丽正在医院花坛上安静的坐着,上午的太阳暖洋洋的照在她的身上,她的情绪已经从最初的慌乱中走出来,看不到一丝的痕迹,她更像是在享受这个夏天的早上。看到张小庆过来,她从花坛沿上站起来,平静的说,小庆,我想好了,我们分手吧。

尽管如此的场景在两个人的脑海里都出现过很多次,但这个时候出现还是让张小庆很生气,他不耐烦的打断她,说,瞎说什么呢?!

张小庆拿过化验单,B超诊断上写着:结节可见血流信号,怀疑恶性,请医生结合临床。张小庆说,这不还没确诊吗?话虽这么说,但这可是一家三甲医院出的检查报告,张小庆的心脏剧烈跳动起来。

第二天的早上,张小庆请了假和王晓丽一起去了北医三院,他查到那里有着一个诊断乳腺癌最好的老医生,同时,那里有北京最好的检查设备。去医院的公交车上,人很多,没有座位,两个人挤在车的最后一排,张小庆不停的说些他能想到的笑话逗王晓丽开心,王晓丽也配合的跟着笑,但实际上张小庆心里非常的紧张,他甚至经常忘了自己说到哪里了,不过这已经并不重要了。

找这位教授的人很多,什么样的年龄都有,张小庆他们排60多号。和其他的医生不同,他并没有开检查单,他只是摸了摸,然后就说没事定期复查就可以了。张小庆拿出前一家医院的检查报告,说,可这是前一家医院的报告。老医生看了看,说,你要相信我的手,我已经摸过几十年的乳房了,只有没有经验的医生才需要做非常多的检查。

从医院出来,张小庆觉得怪怪的,总觉得什么地方不对劲,最后,他还是折回去,又挂了一个年轻医生的号,那个医生给开了检查单做了钼靶检查下周拿结果。结果正如老医生所说,没事,定期复查。年轻医生说,建议还是做了,切出来看,病理结果一出来什么都清楚了。两个人商量了一下,又去找了老医生,老医生没有要挂号,他还是坚持他的判断,手一挥,说,不要随便动手术,相信我的手吧。纠结了好久,最后两个人还是决定回老家去做这个手术,北京的费用太高而王晓丽她们公司都没有上保险。

同一所医院,同一个科室,同一个病理室,一年之后,张小庆又站在了同一扇门前,同样是焦急的等待最后的病理结果,唯一的不同,是上次是他和王晓丽两个人,这次是他一个人。结果是良性的,从长长的走廊出来,眼泪突然从张小庆的眼眶中一滴一滴的流下来,还好,是良性的,要不然,真不知道如何是好。回到病房,王晓丽正在瞅着门口,张小庆想笑,却笑不出来,他走过去,紧紧抱住王晓丽,说,我们结婚吧。

张小庆和王晓丽结婚了,王超要了两万块钱的彩礼,随后又送回一万来,都知道,都不容易。回到北京,张小庆给杨晓付江他们发了消息,然后约好一起吃个饭,让他没有想到的是孙伟竟然也在北京。

张小庆说,你不是说回去吗?

孙伟说,是啊,这不又回来了吗?

张小庆说,家里不好吗?

孙伟说,哎,别提了,你知道吗,我回家想当个中学老师,找了很多关系,最后时刻竟然被顶下来了。家,拼的是关系,北京,虽然压力很大,但相对还是公平,所以我就回来了。

听完孙伟的话,张小庆有些黯然,他想起了黄晶晶,县城里的那些公务员,早已经都是官二代了。孙伟说起了他现在的情况,在一家还算较大的公司里当项目经理,带着两三个刚毕业的程序员,说是经理,大部分的活还是要靠他,几乎没有周末,加班是家常便饭。张小庆说,为什么不考虑换一家公司。孙伟推一推眼镜,笑笑,说,我呆过四家公司,那家公司都一样,不换了。

奥运要来了,他们的话题很快转移到奥运上面。几个人当中,付江最积极,把所有喜欢的比赛都订了票。杨晓一张都没有订,他是属于避孕的,他说,你们最近被上门查过暂住证吗?张小庆被查过,几个戴着红袖章的老头老太上的门,几乎把他的整个身世都询问了一遍,最后还签订了责任保证书,并且,他还发现,晚上下班道路两旁突然空旷了许多,那些麻辣烫烧烤水果摊们统统不见了踪影,空气也好了很多,是他来北京这些年以来最好的时候,他知道,附近的工厂包括河北都被要求停了工,还有,很多网站突然就能访问了。杨晓说,举国奥运,花这么多的钱,我反感。付江说,反感归反感,政府给你提供了这么好的机会,我们不应该浪费,我喜欢科比。

张小庆也没有订票,他的心情很复杂,他是赞成杨晓说的的,但是,他认为付江说的也有道理。高晨晨他们也订了票,他们中了一张开幕式!88号那天,早早的吃了饭,尽管告诉自己奥运其实和自己的关系不大,但随着时间的临近,心情还是一点点的激动起来,开始放烟花了,电视上映出大脚印,能够亲耳听见烟花的声音,脚印越来越近,张小庆再也克制不住,在阳台上什么都看不见,只能听到声音,他一把抓住王晓丽,两个人呼呼的跑下楼,冲小区外跑去,他们碰到了同样的很多年轻人,他们一起聚集在小区门口,远方,那个鸟窝,烟火辉煌,这里,还是只能听见声音,但那么多的人在一起,每个人都能感觉到内心的激动,有人喊起来:中国万岁!所有人都跟着喊起来:中国万岁!

奥运结束了,张小庆突然就开始考虑另外一个问题,这个问题就是要不要换工作。老实说,王总金鹏他们对自己都是很不错的,但工作确实不是自己想要的,不想要面子工程,不想做没有价值的软件。杨晓负责的那个房地产项目结束了,王总最初希望公司能够通过这个项目进入房地产行业,但他又一次失望了,张小庆他们很快发现,除去流程,他们差的太多,比如各种专业知识,各种专业术语,他们都不知道,都需要学习,软件是对现实问题的建模,如果对现实问题都没有很好的理解,谈何建模,谈何优化,谈何解决呢?这期间,张小庆还作为售前去了一趟北京交通大学,在那里,他见到一位教授,他们在做一个铁路信号系统,这个系统的核心部分是一个流程引擎,张小庆了解到,这是一个一千万的863项目,一千万的项目最后外包出来几十万做了,真是讽刺,钱都到哪里去了。金鹏说,一千万一般一半要打发审批该项目的各级领导,剩下的,院里还要分点,就没多少了。

最后还是康威帮了张小庆,他给张小庆介绍了一个朋友,这个朋友在思考加速,这是非常有名的一家外企,对张小庆来说,简直就是如雷贯耳,里面有很多他所崇拜的牛人。于是,在一次openparty上,他们见面了,张小庆也第一次在openparty上分享了一个话题,这个话题是关于一个失败的项目的,就是张小庆那次失败产品经理的经历,效果很好,在那个朋友的推荐下,张小庆也很快拿到了思考加速的offer,第一次看到月薪过万的offer,张小庆很激动,他第一时间给王晓丽打了电话,说,我们又可以考虑买房了!

posted @ 2011-11-21 00:02 ronghao 阅读(1856) | 评论 (5)编辑 收藏

王晓丽收拾了东西,给高晨晨打了电话,高晨晨来接了她。留下张小庆一个人在房间里空荡荡的呆着,一切都是空荡荡的,衣柜是空荡荡的,房间是空荡荡的,连灯泡也变得空荡荡的起来,张小庆想了半天,最后给张岩打了电话,第一句话是,我该怎么办?

接下来的几天,王晓丽的手机一直处于关机的状态,去她公司,去高晨晨那里,她都不在,后来,高晨晨告诉他,王刚要结婚她回家了。第一天晚上张小庆是自己睡的,但睡的心神不宁,一躺下就想起和王晓丽在一起的日子,然后就浮现出那一巴掌,后来几天,张岩叫他去了他家,这个男人见到张小庆的第一句话是,屁大个事,女人都这样,走,吃烧烤去。

张岩过得并不幸福,因为他的女人。张岩最初到北京是在一个大学当保安,年轻人,没事的时候喜欢到处逛,后来就认识了黄英,一个在旁边歌舞厅陪酒的女人,尽管黄英不说但张岩心里也清楚她是做什么的,黄英是个漂亮的女人,岁数也和张岩差不多,她是如此的爱着这个男人,几乎是对张岩百依百顺,挣得钱都给他买了衣服,她白天休息晚上上班,张岩每次回家都能吃到她做好的热呼的饭菜,后来王晓丽过来,张岩也换了工作,黄英就经常带王晓丽出去玩给王晓丽买衣服,有一次过年,她甚至给张岩的父母一人买了一件几千块的皮衣,但即使是这样,即使他们已经同居了好几年,张岩却始终下不了决心娶她,他心里对她的工作一直有着障碍,直到一次过年,黄英突然独自一人找到了他的家,晚上的时候,跪在他的面前,央求他,说自己岁数大了父母一定要自己结婚了要他娶她自己也攒了些钱不再陪酒一起做些小生意,张岩想了好久,最后还是拒绝了她,他过不了那一关,她痛哭着离开了他。后来,又过几年,张岩开始在老家找对象,于是就遇到了他现在的女人李萍,李萍是大专毕业,毕业后一直在县城一个律师事务所里帮忙,和一个律师在一起,但却是个有妇之夫,后来岁数也大了,这样也不是个办法,律师证也一直没拿到,于是开始想找个人结婚,刚好有人介绍张岩,于是见了一面就来了北京,不到一个月,她就怀了孕,张岩说,打了吧,她说,这不是我第一次打胎,打了就怀不上了,我一定会生下来。张岩没办法,于是就结了婚。张岩不幸的生活就这样开始了,李萍很快发现自己对张岩的判断有错误,她以为张岩在北京工作了快十年,会有些钱,再加上媒人一顿乱吹,于是对这个男人的期望就高过了头,现在,结了婚,她才发现这个对朋友花钱大方的男人根本就没有什么积蓄,每个月的工资也不稳定,她跌落到地上,再接下来的,就是对这个男人的失望和看不起,中专毕业,没什么文化,最大的爱好就是和几个狐朋狗友一起喝点酒吹会牛,不懂得生活也不懂得浪漫,总之,这个男人实在是差到了极点,她开始有意无意的在他朋友面前不给他面子,一次去他同事家吃饭,他同事刚刚炒股买了套房子,吃着饭,她突然就说她男人的没本事,旁边人陪着笑说会有的都会有的,她却越说越激动,说,就凭他,我这一辈子都不想了,嫁给他是我最大的失败。大学毕业生觉得自己和中专毕业生在一起亏了,于是,中专毕业生的脸挂不住了,又不好发作,于是跑出去,一个大男人,站在大街上哭着给王晓丽打电话,说,要不是儿子就和她离婚了。

现在,李萍带孩子回了老家,张岩和张小庆坐在马路丫子上吃着烧烤。本来是安慰张小庆的,后来说起女人说起自己,却变成张小庆安慰张岩了。张岩说,没事,你给我舅打个电话,屁大的事,我和你嫂子打架还打110呢。

第一次没有王晓丽在的时候给王刚打电话,张小庆有点紧张,他打了他女儿,他不知道他会怎么样。电话接通了,王刚喝了点酒,他的第一句话是,屁大个事,女人都这样,该收拾时就要收拾。这句话多少出乎张小庆的预料,他对王刚的印象突然就好了起来。一会儿,王刚叫王晓丽接了电话,打她后第一次和她说话,张小庆不知道该说些什么,于是犹豫半天后小心翼翼的问了下天气,王晓丽说了句还可以就挂了电话。

从家回来后王晓丽还是住在高晨晨那儿,每天下班后,张小庆就会去高晨晨的宿舍,王晓丽不开门,张小庆就站在门外央求她原谅他。张小庆想,即使不和好,最起码得让王晓丽接受自己的道歉,自己打她是不对的。时间就这样一天天过去,高晨晨还是不理他,这天中午,张小庆在位置上写着代码,突然,他惊讶的发现窗外的大厦门口突然集聚了很多的人,他一开始还没有在意,人很快就多起来,QQ上突然就有很多头像在闪烁,所有人都在发一条消息:地震了!地震了吗?张小庆公司在三楼,他并没有感觉,他开始打开所有的QQ消息,这些发消息的人有的在东北,有的在云南,有的在广东,一种不好的预感在他心里缠绕,这是一个大地震!他连忙给家里打了电话,父亲说他刚刚把学生疏散没事,他连忙又给王晓丽打了电话,这次王晓丽接了他的电话,她说她们刚刚疏散下楼没事,他又挨个的给所有认识的人打电话,都没事。再过一会,正式的消息出来了,震中在四川!

晚上的时候,新闻陆续就开始报道了,张小庆在网上看了一会图片,眼泪突然就遏制不住的下来了,心里说不出来的难受,一种从没有过的难受。骑车走在去高晨晨宿舍的路上,一路上,他感觉所有的人都神情难过,连路灯都跟着难过起来,来到门前,一敲门王晓丽就开了门,电视里正在放现场的滚动报道,两个人紧紧抱在了一起,眼睛里都含着眼泪,王晓丽哭着说,地震了,张小庆拍着她的背,说,我知道,我知道,没事了,没事了。

现场情况源源不断的通过电视直播回来,王晓丽和张小庆一起回了家,每天他们都要看很长时间电视,他们都觉得自己应该做点什么。于是,就是捐款,给红十字会捐款,尽量多捐一些。这天下午,管财务的黄梅突然气呼呼的从办公室里冲出来,大声的说,你们到底有没有一点良心啊,到底爱不爱国啊,一些人就捐200块,还有一些人,杨晓,你一份钱都没捐,你还是不是中国人啊?!

张小庆知道杨晓捐了钱,但他捐给了一个民间组织,没有捐给红十字会,杨晓说官方机构根本不能信,他说,你信不信,捐款多少很快就要变成衡量你爱不爱国的标志了,他还说,你看着吧,官方很快就要把坏事变成好事了。张小庆不信,他说,这样的国难,红十字会不会这样的。

杨晓后面说的话很快就变成现实了,电视上,各个晚会都在举办,每个企业每个名人都在捐款,镜头对准最多的是他们捐款的数额,支票背后那些人的脸或喜或悲都变得那么不那么重要和模糊不清了,网上,开始有人制作出各个企业和名人的捐款排行榜。现场报道不见了,对校舍质量追问的帖子不见了,取而代之的是对各路英雄的宣传,各种表彰也开始了,国难是可以兴邦的。张小庆们终于对电视和各个门户网站感到不耐烦了,他去的是天涯,只有那里还有一些现场的消息,而这些消息也总是不总是能看到的,他们一起嘲笑了郭跳跳,电视和郭跳跳一起让人感到厌恶。

生活还要继续,张小庆进的是刘哥的保险公司的项目,他们遇到了严重的性能问题,系统运行的比在开发机器上还慢。张小庆开始排查问题,他第一个想到的是是不是数据库访问太频繁,于是,他把所有操作的数据库语句全部打印出来,一个一个的调优,接下来,他又给频繁访问的数据加上缓存,在公司开发机器上,系统性能几乎是立刻得到了大幅度的提升,但在产品环境,系统还是那么慢。问题似乎陷入了僵局,产品环境是IBM小型机,本地开发环境是PC机,本地根本就没有办法模拟产品环境。于是就开始猜,会不会是运行环境的问题,是不是操作系统的问题,是不是数据库的问题,是不是WEB容器的问题。最后,在一个weblogic原有开发人员的帮助下,他们终于发现现场部署的weblogic默认是运行在32位机器上的,与64位机器存在一定的不兼容。通过替换一个IO包,问题得到了解决。替换完毕后,速度立刻提升了30%。保险公司使用的是盗版weblogic如果没有这个私人关系,他们根本就不会得到这个64位下的jar包的。这个问题花了张小庆整整一个月的时间,他骂了人,妈的,这么大的一个保险公司,中间件、数据库、企业IM竟然全部是盗版,软件在他们眼里就那么一文不值。

性能问题解决后,问题还没完,访问量一上来,系统开始频繁宕机,出现内存泄漏了。继续找问题原因,还是老样子,在本地重现不了内存泄漏,继续把眼睛放到产品环境盗版的数据库和中间件上面,没有技术支持,去搜索各个帖子,一项一项的查看配置参数,终于发现内存泄漏出现在数据库驱动和连接池的一个设置上面,一个缓存选项被系统集成商的实施人员设到了最大,直接导致内存溢出。

项目结束了,尽管王总和金鹏都表扬了他,但张小庆却感到深深的失望,他在想,为什么在这样的大公司里面,还会如此到处用盗版软件,项目总价300万,硬件占了230万,软件才占70,而软件中根本就没有考虑用正版数据库和中间件,而这些硬件,在张小庆眼里,根本就是用不上的,这样的访问量根本就用不了这么好的机器。张小庆在想,这个收入排名前列的保险国企尚且如此,其他国企是否也是如此呢。当然,张小庆也是有收获的,第一就是性能调优之前一定要先找出出现问题的瓶颈,而不是想当然的马上进行优化,第一次调优花了一个月时间,其实对代码的调优完全是不必要的;第二是一定要对系统运行的环境配置信息也管理起来,如果可以,测试环境一定要与产品环境一致。不管怎么样,张小庆还是难过,他开始回想起他在科技动力所做过的所有项目,除了第一个一汽丰田项目,其他的都不算成功,新业务平台开发没有取得预期的销售,准确的说只售出一单,那一单幸亏还是客户换领导取消否则还真很难说剩下的都变成内部项目使用了,新工作流开发吧,就根本没有完成就取消了,现在,这个保险项目,又是滥用盗版软件。这不是我想要的,张小庆对自己说,他想能够开发真正产生价值的软件,这个软件被客户真正使用,不是政绩和面子工程,而又获得客户好评。有这样的公司吗,他问自己。

工作上感到失望,和王晓丽的关系似乎也微妙起来。上次因为地震的关系,两个人又生活在了一起,但没多久,谁都能感觉到他们回不到过去了,还是一起吃饭,还是一起骑车回家,还是一起买菜,但两个人多少有些心不在焉,话少了很多,晚上张小庆写自己的代码,王晓丽看自己的电视,王晓丽不再要求张小庆陪自己看电视,王晓丽也不再追问什么时候结婚的事情,张小庆也不想去主动提这件事,两个人似乎都在等一个最后时刻的来临,这个时刻就是当一个人提出分手的这个时候,一个人说,好吧,这样真的很没意思,你看,我们不如分开一段时间吧,另一个人很礼貌的回答,好的,我也是这么想的,我之前不说是怕你有想法,你现在这么说我就放心了。于是,两个人礼貌的握手,就此变为朋友。这个时刻什么时候来临呢,谁都不知道,都在等待。

这天张小庆刚到公司打开电脑不久突然就接到了王晓丽的电话,这是这段时间很少出现的事情。这天早上王晓丽去了医院,她去复查身体,和她妈妈一样,她的乳房里有个硬结,不过已经很多年了,一直都没事。刚接通电话就传来王晓丽哽咽的声音,她说,小庆,你快过来,医生说,恶变了,呜呜。

posted @ 2011-11-06 00:14 ronghao 阅读(2017) | 评论 (5)编辑 收藏
     摘要:       事实证明,所谓中小企业联合会就是一个骗子。过完年后光头就再没出现过,他给王总打过一个电话说是今年因为奥运会的缘故人民大会堂的会被取消了,所有的相关开发也就停止了。听张小庆说完这事,张岩大笑起来,说,他和我们就是一路的。张岩是王晓丽的表哥,中专毕业来北京已经快十年了,最开始在某大学做保安,中专读的就是保安专业,还是在河南找关系读的,后来就从事了现在的这一行。...  阅读全文
posted @ 2011-10-19 23:23 ronghao 阅读(1947) | 评论 (4)编辑 收藏

张小庆当产品经理了。

尽管张小庆很高兴,但实事求是的说,如果他当不上这个产品经理才是失败。第一是金鹏的职责发生了变化,随着公司的扩大,金鹏需要管理全部的技术人员,事务性工作越来越多,另外也是最重要的,公司新产品的销路并不好,离预期差得很远,上半年只有普科一个单子,当时公司还能靠去年签下的项目维持,但到了下半年,以前的项目基本上都收尾了,新项目却还没有踪影,这样,公司需要金鹏去经常参与售前工作,需要一个技术好又沉稳又懂业务的人,这样的人除了金鹏没有第二个;第二是孙伟走了,除了金鹏,张小庆成了产品部资格最老的人,按照中国人论资排辈的话,也该他了,看很多大公司的高层管理,无一不是把曾经的同事都熬走才有了今天的位置,坚持才能成功;第三是张小庆本身做得还可以,好学,热情,经常在公司分享技术知识,还翻译了一本技术书,这些都是金鹏喜欢的。所以综合以上因素,张小庆当上产品经理是正常,当不上,只能说做的太差。

张小庆重新忙起来,之前曾经有段时间他对公司很有情绪,因为打卡的事情,他不再加班,周末的时间他在捣鼓一个Javascript的流程设计器,但是遇到了很多的困难,最大的困难就是很难找到一个所有浏览器都兼容的绘图方案。现在,新的职位让他感到责任,他又开始加班了。激励一个人其实是很简单的一件事,就是放手给他更大的责任。小公司的产品经理和大公司的产品经理不一样,在大公司里,产品经理只是负责产品的需求和规划,与客户和项目经理打交道,但在小公司里,这是一个全能职位,首先你需要确定产品一段时期需要开发的功能列表,然后你需要分配任务带领一个团队完成这些功能,最后你还得是团队中技术最好的人遇到问题你得解决。

张小庆首先决定将产品代码好好的整理一次,之前由于计划乐观后期赶工期的原因,代码变得混乱,膨胀的很快,由一个单一工程拆分成了多个互相依赖的工程,不再有静态检查,也不再有单元测试,有一些文件甚至已经超过2千行。张小庆花了好几个周末来重新整理这些代码并在团队中统一代码风格,但他很快发现,与写代码相比,最困难的是和人打交道。最开始,新产品的开发是技术驱动的,更换了整个产品的技术框架,至于功能,则是原封原从老版本移植过来,那段时间,他们是封闭的,一边使用老版本列出功能列表一边使用新的技术实现;后来,产品发布,在实际项目应用中,BUG被源源不断的发现出来,他们主要的工作变成了修复BUG和增加项目部反馈需要的新功能,那时,金鹏还在,由他决定做什么不做什么,没有外部的干扰;现在,张小庆接手了,每天不再是只写代码,还需要经常和项目部讨价还价,而他又没有金鹏的权威,于是,需求变成了每天和项目部心烦意燥的打交道,总是有做不完的事情,并且,都很紧急,排定的优先级根本没用,总是被打断。张小庆觉得,项目部对产品部的依赖太重了,客户要求一个新功能,只要涉及一点点平台功能的增强,项目部就立刻将需求转到产品部,他们的理由很简单,这是你们开发的代码,我们不懂,可是,产品代码早就对项目部开放了,另外,产品的技术支持似乎总是不够的,有一次,一个项目部的程序员一个配置文件写错了,导致系统不能正常工作,他根本不看日志,就直接说是产品的BUG,张小庆过去看了,找到原因后有些生气,但他也没有办法,招人困难,项目部很多人都是不那么满意的,都是凑合着使用,再说,给人家的工资也不高。

尽管公司人数并不多,但产品部和项目部之间的矛盾是存在的,张小庆以前没有太多感觉,现在,感受一下子深刻起来。项目部觉得产品实现的不成熟,在实际项目中,总是有这样和那样的功能没有完成,还有就是BUG太多和技术支持不够。产品部觉得项目部的程序员似乎不能自己解决问题,一有问题都往产品上推责任,产品部的人都成了救火队员,他们觉得所谓的项目需求不就是一些功能模块,而这些功能模块在抛除掉基础模块支持后只是简单的增删改查,建张表,应用代码生成器生成脚手架代码,再修修改改就可以了,他们的兴趣在增强产品的新特性,例如对全文检索的支持,对产品运行期性能的监控,分布式扩展部署等等。张小庆有时候喜欢访问一些竞争对手的网站,在那里,他总是被他们的产品特性打击的自叹不如,他感到自己做的产品有太多的东西需要做,而又根本没有时间去做。终于有一天,在一次程序员聚会里,他认识了另外一家做相似产品公司的产品经理张映,张映他们公司是大公司,经常在杂志上做广告,他问了张映他们的产品,问了自己觉得很酷的几个特性。听完张小庆的话,张映突然笑起来,说,网站上的宣传你也相信吗?

张小庆说,没有实现也能打广告?

张映说,那是市场部的需要,再说,如果用户真的需要这些功能,我们也不会到现在还没做。

张小庆又问了张映他们产品销售的情况,张映说,基本没有销售,都是我们按项目给他们做。

原来都一样,张小庆想,本来想做产品公司,最后还是变成了项目公司。为什么会这样?是环境变了吗?还是其他原因?张小庆想起了科技动力03年刚发布产品第一个版本时的荣光,那段时光看起来永远都不可能再来了。为什么呢?首先,需要知道公司产品能够给客户带来什么样的价值,产品能够给客户带来什么价值呢?是的,对于协同办公和电子政务类的应用,使用该产品能够快速实施,产品本身包括了工作流,包括了内容管理和门户,还包括了很多开箱即用的业务模块,此外,对于开发人员,能够马上生产出脚手架代码,提高生产力。接下来,需要知道产品的客户会是那些人呢?有两类客户,一类是大的系统集成商,他们拿到项目后会购买产品以减少成本,另外一类是小的软件公司,他们通过关系外包到项目,却没有足够的人力和能力完成,那么,采购一套成熟的产品也是明智之举。可是,为什么产品提供的价值没变,所面向的客户也没变,怎么就销售不出去了呢?这就需要比较当时和现在的环境变化了,03年的时候,正是java正火的时候,大量的应用正从CS结构向BS结构迁移,而国内的java公司和程序员还很少,此时推出基于javaWEB应用必然会具有技术上的优势,另外,国内的电子政务应用也正是从03年的时候开始兴起,大家都没有太多实施经验,而产品所包括的不仅仅只是一套开发工具,而是包括了门户和内容管理的整个解决方案,这两个因素叠加起来,产品不火才怪;而现在是07年底,07年底是一个什么状况呢?满大街的都是java公司和java培训机构,java已经不构成技术门槛,而像工作流这样的产品,开源世界已经有应用非常广泛的产品了,东西没变,但时间将它的技术含量降低了,另外更重要一点是,协同办公应用也开始烂大街成熟了,一些公司消失了,一些公司做大了,经过几年的发展,大的公司必然都有了自己的积累,有自己通用的库,而对他们来说,在这些通用库上开发一套自己标准的开发平台几乎是水到渠成的事情,此时,如果整个解决方案都外包反而是一件风险极高的事情,张小庆知道,有家老工作流公司就向很多大公司出售了产品的源代码,也有公司找科技动力谈过,但没有合作,这样,客户中的大公司消失了,也是最主要的客户消失了,接下来的小软件公司,几乎是有一单没一单,本来钱都不多,技术支持就能把人耗死。于是,这两个因素加起来,产品卖不出去就毫不奇怪了,但公司还要生存下去,就只能自己接项目做。

这个世界就是这样,需要抓住正确的时机,早了,替别人铺了路,晚了,别人已经玩大了,没了机会。

如果公司发展最好的时候没有分裂多好,张小庆想,但他很快又为自己的这个想法笑起来。历史已经证明,同甘苦易,共富贵难,所谓伟人尚且如此,更别说公司了。那么,以现在的情况看,当初重写这个产品也许是错误的,更好的方法是在老版本上增强?马后炮谁都会,公司现在要怎么发展呢,找个大公司,刚好他们在协同办公这块没有经验,搞好关系,形成良好的合作关系,这样应该不错,最后再被收购,就完美了。张小庆再一次笑起来。

大公司王总没有找到,他找到了一个叫中小企业联合会的组织,这个组织的头来公司张小庆见过,光头,中年人,说话大大咧咧,满嘴跑火车,让他第一时间想起了那个军方信息中心主任。光头说,我们明年初要在人民大会堂举办中小企业高峰论坛,在人民大会堂,光头强调到,王总在那里点着头,说,是;光头说,我们邀请到国家部级的领导出席,部级领导,光头强调到,王总在那里点着头,说,是;光头说,上千家企业老板出席,都是付费的,上千家,光头强调到,王总在那里点着头,说,是;光头说,所以你们帮我们做这套系统实际上是在为你们做广告,想想吧,那么多企业家,还有部级领导,演示完毕时打上你们的logo,多么难得的一件事,广告效应,光头强调到,王总在那里点着头,说,是。

光头走后,王总有了动力,王总总是充满激情的,他又一次说,加油啊,同志们,搞好了我们后半辈子就不用干活了。这是不知道第几次听王总这么说了,张小庆有些麻木,他对那个光头怎么也提不起好感,怎么看都像一个骗子,拉个关系让领导露个脸,然后就开始骗那些企业们的钱了,人民大会堂,怎么听都应该是个神圣的地方,但似乎是人不是人都能在里面开个会,张小庆想起来,在路边里,那些壮阳粉就在里面开过发布会。张小庆问了王总,给我们多少钱?王总反问,钱,不要提钱,人家帮我们免费做广告呢。原来是免费的。

给中小企业联合会做项目是坏事也是好事,坏事是可能被骗了,好事是产品部有了一个机会来做项目,张小庆挑了一个模块告诉王总他们来做,他决定做出一个示范模块来。这是一个发文模块,被无数项目做烂的模块,张小庆团队现在一共有5个人,除了他自己,还有4个人,其中秦涛是技术最好的,毕业于天津大学,张小庆真是很难相信金鹏是如何将他搞到手的,杨柔则是一个聪明的姑娘,她就坐在张小庆的旁边工位,喜欢问问题,最开始的问题比较初级,但很快,她就学会了,学得很快,张小庆想,一个女程序员已经难能可贵了,一个聪明的女程序员简直就是稀有动物。每次张小庆帮杨柔调试程序的时候,杨柔都会去张小庆的工位把他的水杯取过来,然后倒满水,放到张小庆的手边。这次,张小庆他们将所有与发文相关的文档都收集起来,叫上了刘哥和杨晓,画出了mockup,和预想的完全不一样,他们评估了工作量,最后发现竟然需要2个人月,张小庆自己也很吃惊,因为之前他也是认为那些项目模块只是简单的增删改查的,他意识到,所有技术最后都是来解决现实问题的,不管这种技术如何先进,如何有生产率,当你熟悉它后,就会发现真正影响进度的还是要解决的现实问题本身,你对这个问题的熟悉程度,你对这个问题的理解程度,都会影响到后边的实现,此时采用何种技术反而不是最重要的。

公司不大,每次有新的程序员入职,金鹏就会叫上所有的程序员一起吃个饭,张小庆突然注意到,每次吃饭,杨柔都会坐到他的旁边,每次上菜时,她都会先帮自己细心的夹菜,这个细节让张小庆的心里泛起一阵涟漪。杨柔是个温柔的姑娘。

快到年底,又是一年,张小庆的工资涨了1千块,这样,每个月他就能收入7千块了,抛开一个月工资回家,年底能够攒到1万块钱,不过,金鹏也委婉的表示,公司今年的效益不好,年底可能就没有奖金了,张小庆能够理解,他突然在想,都说小公司3年是个坎,这个说法不是没有道理的,第一年公司通过原有客户发展的比较好,第二年公司开始扩张做出乐观的计划和投资,第三年原有客户消失新的客户开拓困难而由于第二年的乐观扩张公司就此陷入困境。

在潇湘府,张小庆把涨工资的好消息告诉了王晓丽,每个月,他们都会在西三期的潇湘府打一顿牙祭,一般情况下,他们会点两个菜,一个特色菜,一个青菜,两碗米饭,一共不超过50块钱。张小庆其实并不在意一顿饭多少钱,他在意的是终于有一天能够不用做饭和刷碗,但王晓丽在意,她规定一定不能超过50。我们得攒钱买房,她提醒张小庆说。这次,饭吃到中间,王晓丽突然说,我们结婚吧。张小庆被王晓丽的这个提议吓了一跳,他还从来没有仔细考虑过这个问题,他磕磕跘跘的说,不急吧,我们都还年轻。王晓丽说,年轻的是你,我已经不年轻了。过了这个年,张小庆25,王晓丽就28了。张小庆这才发现当初所谓年龄不是问题的问题现在就是个问题了。张小庆是不想结婚的,起码在吃这顿饭的时候是这样,于是,他就不再说话,默默吃着饭,王晓丽没有等到张小庆的回答,叹口气,扔下筷子,说,吃饱了。

真的要和王晓丽结婚吗,张小庆第一次问了自己。自己真的爱这个女人吗?其实这个问题王晓丽也问过自己,当时自己的回答是和你在一起能感觉到一种亲情,没有骗人,这就是自己的真实感觉,没有那种所谓触电般的爱情感觉,真没有,当时只是觉得应该找个女朋友,于是,她就出现了,至于她比自己大也没关系,也多少有和王碧薇赌气的意思在里边,没有心跳的感觉,一切感觉就像一杯白开水,经过了18层过滤了的那一种,没有一点心里的波澜,那么,自己爱她吗?也许是不爱的,只是两个人都觉得要谈个朋友而已吧。可是,自己为什么又经常挂念着她,在车站等她下公交车,然后驮着她一起回家,一起买菜,一起做饭,有时等的时间长一点,就开始胡思乱想会不会出了什么事,她母亲生病那些天,她晚些回北京,突然就觉得心里空空的,这些感觉不是骗人的,那么这是爱情吗?不,似乎不是,应该是亲情,但是,为什么,那么多爱情小说里,又说这是爱情呢?妈的,爱情到底是个什么东西?为什么就没有和程序一样的精准定义呢?另外,结个婚真的一定需要爱情吗?又有谁那么走运,真的和爱情结了婚?

她脾气不好,说什么事都是命令的口气,一点都不温柔,张小庆想,很多事情,为什么不能换一种说法呢,衣服忘了翻过来晾,撒个娇,说辛苦翻过来晾呗,多好,她不,她总是很生气的说怎么又忘了翻过来晾每次都把我的话当耳边风!上次她弟弟过来找工作,最后一句话差点没把张小庆憋死。不喜欢看书,也不怎么喜欢看电影,张小庆带她看过两次电影,一次是《哈利波特》,一次是《变形金刚》,第一次她睡着了,第二次她也睡着了,她唯一喜欢的就是看后宫剧,说,我们公司就是这样的,勾心斗角,她认为同事间不存在友谊,如果勉强存在像友谊样的东西,那只是没有利益冲突罢了。张小庆最初的梦想是去微软和IBM,王晓丽耻笑了他,说,真单纯,现在这社会挣到钱才是真的。怎么说呢,王晓丽给张小庆的感觉就是,很现实。在家人面前,她经常展现出一种北京的优越感来,家里这也不好那也不好,北京这也好那也好,如果一种有名的牌子在家里有卖,她会说那一定是冒牌的,只有北京的才是真的,上次王刚来,她嘲笑了他的衣服,但王刚不知道的是,她姐姐穿的衣服其实都是在集市里淘的,比他的便宜太多了。同时,她又是敏感的,她最受不了家里人拿她和其他人比,比如张芳有时会说她们村里有个人在北京做的不错,一个月能赚好几万,过年回家时还开了辆小轿车回来,王晓丽立刻就能从她妈的语气中嗅到一丝比较的异味,她尖刻的讽刺她妈妈,你怎么知道他一个月挣那么多钱呢,他亲口给你说了吗,你们就是会传,你传我我传你,本来几千块钱到你们嘴里最后就成了几万。张芳说,那小轿车总归是我亲眼看见的吧。王晓丽说,哎哟哟,QQ也是小轿车,北京这种车四环都不让进。高晨晨曾经和张小庆说过,说王晓丽其实是刀子嘴豆腐心,说要理解她,她其实很不容易,家里条件不好,上大学都是自己借钱读的,工作后才把钱一点点还清,一个女人在北京,完全没有人照顾,全部靠自己,家里还经常要些钱,就这样,她还是靠自己的努力进了这个行业最好的公司,到这个公司里,大公司,关系又比较复杂,她不会搞关系,不会妥协,经理不给她派单,所有单子全靠自己去小区拉客户,就这样,产值还是在公司里排前几位,一个女孩,受着这些委屈,自己哭过很多次,又没有人可以讲,于是就一天天坚韧起来。张小庆相信高晨晨说的话,王晓丽的家庭情况也是他所担心的,他并不指望她的家庭多么多么有钱,农村的也没有关系,自己本来也就是个小县城的,问题的关键是王超和王刚,一个中年受到打击后就一蹶不振,几十年来完全靠老婆养活,天天打麻将,一个20多岁了似乎还没有长大,吃不了苦,还在混着,并且,他们都有依靠王晓丽的心理,向王晓丽要钱,让人看不到希望,而现在,张芳又病了。其实,王晓丽的展现优越感和敏感也和她的家庭有关,自从王超从单位下来回家后,她们家就落败了,一个曾经最风光的人转眼成了最落魄的人,成了需要老婆养的男人,被村里人背后嘲笑是免不了的,农村里,很多事就是面子那点事,从小被人们嘲笑的王晓丽变得敏感,她害怕别人拿她比,她连大学学费王超都拿不出,此外,她又成了王超心里唯一的希望,他和张芳都将希望寄托在她的身上,希望她能够多挣钱,好让自己在村里人面前找回失去多年的面子,于是,她就不停的强迫自己要好好干,多挣钱,当和家人打电话时,她需要表现出自己过得很好,而从心底里,她却又是憎恶这个样子的,她觉得很累,于是,她就一天一天的刻薄起来,处处表现出北京的优越来,北京很好,这也好那也好,你们的女儿没有给你们丢脸,只是,她没有意识到的是,北京的好多少和她没有太大的关系。后来,高晨晨买了房,张芳口里高晨晨出现的频率开始高起来,村里人也都拿她们两个说事,村里人说,在北京算什么,买的上房子才是真的,于是,王晓丽再次需要证明自己了。

王晓丽有很多优点,她很节俭,舍不得吃舍不得穿,这符合张小庆的性格,只是,张小庆骨子里还有浪漫的一面,他会突然心血来潮,对王晓丽说,认识一年了,我们也去云南玩吧,徒步,才不到3千块钱,然后他开始认真的整理攻略,但,他们没有一次成行的,最后关头,王晓丽总是说有事。王晓丽只是在不忙的时候,去爬爬百望山,门票6元。一次,王晓丽生日,张小庆激动的对王晓丽说,我们去全聚德吧,来北京这么久了,也该尝一尝,然后再看场电影。一会,他就磨拳擦掌的拽着王晓丽往潇湘府外走,王晓丽却说,你和钱过不去吗,一句话浇灭了张小庆的情绪。王晓丽饭做的不错,很多菜,在潇湘府吃一次然后自己就琢磨出来在家里做出来了。家里很多该张小庆干的事她都给干了,客厅的灯泡坏了,给张小庆说了几次,他却总是记不住,答应的很好,手却一直没有离开过键盘,最后,自己买了灯泡爬上椅子给换了,看见她换,张小庆这才停止写代码屁颠屁颠的跑出来,她叹口气,说,除了工作,你什么都不会。

第二天是周六,王晓丽要上班,出门的时候她对张小庆说,你好好想想吧,如果不想结婚我们就分手,我和你耗不起。

posted @ 2011-10-14 22:28 ronghao 阅读(2007) | 评论 (6)编辑 收藏

      终究还是没有在燕郊买成房,王晓丽的妈妈张芳病了,乳腺癌。这个农村的女人,一年前就发现自己的乳房有了个不大不小的肿块,她却并没有在意,村里只有一个小小的私人诊所,看不出什么,到镇上医院需要一个多小时的摩托车,于是就没有管它,家里所有的农活都在她一个人的身上,王超是什么都不做的,也做不来,王刚年龄还小,还处于混的年龄,谈了个小女朋友想结婚,于是,家庭的重担加上落后的医疗条件,肿块就一天一天的大起来。终于一天早上,突然就疼得起不了床,这才心慌起来,心里却这样想,为什么要检查?如果是恶性就是死,还不如不查,知道了死得更快,如果是良性就更不用查了。王超这时却突然捡起他遗失多年的丈夫的责任来,硬拖着妻子去了医院,直接去的是县医院,不是镇医院,可惜,他们去的是县中医医院。一个年纪很大的老医生简单号了个脉,问了问情况,就给开了一大堆的中成药,说是血瘀没事,也没有拍片。从中医院出来,张芳长长出了口气,原来是一场虚惊,只是小小几盒中成药就花了1千多块钱让她觉得心疼,就这样,在家里又吃了几个月的中药,问题终于严重了,短短几天,肿块变大的特别厉害,她还是自我安慰说人家老中医都说了没事,一家人还是继续过着生活:王超打着麻将、王刚耍着朋友、张芳干着农活,直到最后这事被她的妹妹偷偷告诉了王晓丽。

       和王晓丽一起查完电脑,张小庆立刻感到了问题的严重。他们一起去了几家医院,几乎是刚说完情况,医生都下了诊断:正在扩散,要马上手术。王晓丽的第一想法是把张芳接过北京来治疗,但很快她就打消了这一想法,第一是怕时间来不及,第二是问了一下,4万多块钱在北京也就只够手术和一次化疗的费用,最后也是最重要的,根本就没有床位,要排队,还得有关系。这个结果多少让王晓丽很黯然,上了这几年的班,连母亲病了接来北京看下都做不到。张小庆安慰王晓丽,为什么要来北京呢,县医院是二级甲,也不差,再说,这是一个很常见的病,哪里都是一样的,在家照顾还方便些。王晓丽说,你懂什么,家里和北京怎么会一样呢,技术差远了,要不为什么连住院都住不上呢,只能怪我们没出息没有钱。几句话说得张小庆说不出一句话来。

       王晓丽请了假,张小庆也请了假,两个人一起回家。王晓丽给王超打了电话,说,在干什么呢?

       王超说,在高头店子里。

       王晓丽差点被气坏了,说,这都什么时候了,还在打牌?!还没送妈去住院啊?!

       王超说,你妈有什么病啊?

       王晓丽说,癌症,你不知道啊,在扩散,你不知道啊!

       活检的结果出来了,恶性。在拿到结果之前,尽管大家都有了心理准备,但却都存在着一丝幻想,也许搞错了呢,直到医生走出来,没有表情的说,是恶性的,淋巴上有扩散,好吧,你们走吧,报告一会儿我送病房。走出检验科阴暗的走廊,外边是一派阳光,刺得人眼睛生疼,王晓丽突然就控制不住自己的情绪,大声哭泣起来,这是张小庆第一次见王晓丽哭,这个一向表面强势的女人,突然就哭了起来。好半天,王晓丽的情绪才平息下来,她说,知道吗,我们家谁都可以得癌症,但就是不能我妈得,你说老天怎么这么不长眼啊。从小到大家里就全部靠她一个人,我爸什么事都不做,好不容易辛苦大半辈子要享福了,却得了这个病。她恶狠狠的说,其实,最该得这个病的是他。

手术完了紧接着就是第一次的化疗,需要做六次,一次在六千块钱左右。家里没有钱,王晓丽他们先拿了1万块钱,然后后续每次化疗前都寄六千。后来,王晓丽又让张小庆找了他在地区市医院工作的同学,将张芳转院到了市医院,张芳参加了农村医疗保险,按照规定,在县级市医院报销40%,在地区市医院报销20%,另外,对药物也有限制,第一次化疗结束时,王超去报销过一次,退回200多块钱,大多数的化疗药物都不属于报销范围。

        尽管妈妈得病这件事让王晓丽很难过,但接受这个事实后,一些事情还是让她心情逐渐好起来。一是尽管淋巴上有癌细胞的扩散,但医生依然把癌症诊断为早期,这意味着有着比较高的治愈率;二是她高兴的看到王超又负起了一个男人应该负起的责任,这是她很久都没有看到过的事情,印象中,除去在砖瓦厂的那段短暂时光,这个男人从来就是一个不顾家的男人,除了麻将,他的生活中就没有其他,但这次,他跑前跑后,为张芳端尿端屎,晚上就睡地上,没有一句怨言,还想方设法的亲自在小餐馆里给妻子做饭。王晓丽对张小庆说,知道吗,关键时候还是要靠我爸,他是爱我妈的。第三是王晓丽的行为在他们村里被很多人说起来:你看人家王家闺女,真是孝顺,县里医院不看还要弄到市里医院去。在他们村,去年刚有一个人因为癌症去世,儿女都在外面打工,得了病却没有足够的钱治疗,最后确诊后只好接回家,就眼睁睁的看着人没了。

期间,张小庆去了一趟王晓丽的家,那是他第一次去她家。首先从县里坐了2个小时的车到了镇上,然后再坐1个多小时的车颠簸到山里,到山里的车一天只有两趟,他们幸运的赶上了下午的一趟,路是石子路,刚下过雨,泥泞不堪,但路两旁的风景却是非常的好,到处都是树木,那些树一看就知道已经有很多年的历史。王晓丽的三间砖房就砌在路旁边,前面对着水库,最外边是猪圈和厕所,里面是个水泥院子,院子里打着一口井,然后就是三间大瓦房和两间厢房,厢房是厨房和堆放农具杂物的储物间,在厨房里,张小庆惊奇的发现这里还是小时候烧柴禾的灶。这房子修的早,那是还是王超风光的时候,院子、红砖、黑瓦,那个时候,人们走进村子,抬起头来的第一眼就是这间房子,你不得不注意它,但是现在,四周都盖起了两层和三层的楼房,它不再是眼睛的宠儿。

        其他时间,张小庆和王晓丽住自己家里,张小庆的家就在县城边上,骑车十分钟能到县里,妈妈的腿不方便就在家做饭,每天,张小庆的爸爸负责送饭。王晓丽也是第一次到张小庆的家,学校盖的楼房,五层中的第二层,没有庭院,也没有水井,地砖、自来水和管道燃气。有一天,爸爸突然对张小庆说,对了,你同学肖东死了你知道吗?

张小庆说,肖东是谁?

        好半天他才想起来肖东就是那个鼎鼎大名的威震天,这些年,他只记得威震天,倒忘了他的真名。他的第一反应是,威震天死了?他不黑社会吗?

        爸爸递过来一张几个月前的报纸,在一个小角落里他看见了这么一行字:昨晚,我市发生一起斗殴事件,被害者肖东,绰号威震天,社会零散人员,在与他人斗殴中被刺中心脏,当场死亡。看完这则消息,张小庆突然心里就很不是滋味,他并不是可伶威震天,他是在想人生命的无常,他还记得威震天的那句话,那个下午,威震天走进教室,脸上还淌着鲜血,说,别担心,现在是属于他们的,但未来是属于我们的。

        余鹏对这个事情知道的更多,他说,什么威震天,就是一个傻X,太看重自己,也太看重义气了,小弟受了欺负,人都不带,一个人就去了,以为自己能搞定,结果被一群人围着砍,最后一刀刺中心脏,倒地就死了,小弟倒是跑了,还真以为未来是属于他的,傻X!

        张小庆继续无聊的翻报纸,在报纸的第一版,他又一次看到一个熟悉的名字:黄晶晶。这次的标题很醒目:热烈祝贺黄晶晶同志当选县共青团书记。张小庆想起来,黄晶晶曾经和威震天签过手的,这个事情让自己难过了好多天。他把报纸翻过来,那里,威震天畏缩在角落,已经没有了气息,再翻过去,这里,黄晶晶坐在主席台上,面色潮红,热烈鼓掌。张小庆想,威震天大概说的是没错的,未来是属于我们的,但是是属于我们当中的他们的。

        从家回来,张小庆和王晓丽一起去了燕郊,他们去把交的定金取回来。中间王晓丽接到了两个电话,一个是高晨晨的,她的房子快要下来了,想请王晓丽帮忙装修,另一个是丹丹,是曾经和王晓丽合租过房子的女孩。丹丹的经历很是特别,重点大学毕业,工作后先是把大学男朋友给蹬了,喜欢上一个快50岁的中年男人,几乎是毫无保留的爱着他,连开房都是自己掏钱,分开后就开始拼命相亲,爱过了,不再爱,相亲的条件只有一个,就是在北京有套房,短短几年间,她带过好几个男人回过家,却最后都没成。不过,这次,她真正要结婚了,对象是一个在联想工作的老程序员,认识2个月后,他们领证了,对方在上地刚买了房。

        张小庆问,她说什么?

        王晓丽说,买房了,想让我帮忙装修。

        张小庆说,挺好,又有活了。

        王晓丽说,屁!晒幸福罢了。

        这是王晓丽最近接到了第三个电话了,之前两个电话都是曾经合租过房子的女孩打来,她们一脸幸福的说,我要结婚了,接下来,她们继续幸福的说,我买房子了,这句话才是整个电话的重点,能够想象到她们说这话时的灿烂表情,这个表情一定比结婚本身更让人欣喜。最后,她们拖长了音调,说,到时候请你一定帮忙哈。于是,挂电话,于是,从此再一次杳无音讯。

张小庆对丹丹印象深刻,原因不仅仅在于她,也在于被她甩掉的大学男友小强,小强在被丹丹甩掉后疯狂追求丹丹的亲妹妹小薇,这个被抛弃的男人,这个继续在重点大学读研的男人,这个戴着眼镜说话慢条斯理的男人,做了中专毕业来北京打工小薇的男朋友,从此,他又和丹丹经常见面了,丹丹和小薇住在一起。张小庆曾经问过王晓丽,作为一个男人,小强是如何神经坚强的经常面对丹丹?作为一个女人,丹丹又是如何神经坚强的经常面对小强?而作为父母,又是如何神经坚强的面对去年还是大女儿男友的人转眼间又成了小女儿的男友?王晓丽没有回答,她说,看着吧,他们成不了。果然,小强研究生毕业进入中石油的第一件事就是踹了小薇,末了,他不忘当着丹丹的面对小薇说一句:谢谢你三年来的陪伴,但,我们的差距实在太大了,我们不合适。

从燕郊回来,王晓丽的心情有些账然,高晨晨的房子下来了,曾经的姐妹都有了自己的房,而她,还不知道自己的房子在何方,自己年纪也不小了,该结婚了,结婚后还租房住,这不是她希望的。望着窗外,她感到自己在北京的这些年都白忙活了。

        张小庆的心里却多少还是乐观的,他相信奥运后房子会降价,这次没买成并不代表着以后没有了机会,再说,房子在燕郊,那里是河北不是北京。吃午饭时,张小庆他们说起了这事,杨晓说政府的医保政策有问题,付江说不急以后说不定在北京买套房呢。后来,他们的话题就转移到正在热播的电视剧《奋斗》上面。

        付江呵呵笑了一下,说,哎,你们发现没有,这部剧真贫。

        杨晓说,两个爹,一个爹是开发商,一个爹管开发商,一个是现在最有钱的人,一个是现在最有权的人,奋他妈个比的斗。

posted @ 2011-10-09 21:21 ronghao 阅读(1752) | 评论 (0)编辑 收藏

张小庆和王晓丽最后还是去了燕郊。张小庆的翻译书稿交上去了,他大大松了一口气,这是他第一次翻译东西,如果没有康威的帮忙和鼓励,他都不知道自己能不能坚持下来,他是感激康威的,同时,他也剧烈盼望着书籍出版,编辑说还需要2个月的时间走流程,他已经想好了,要在公司做一次ajax的演讲,同时还要送一本书给公司。翻译完成后的日子突然明亮起来,周日不再苦恼,不再为痛苦不堪的打开文档而纠结,也终于有了时间和王晓丽一起去燕郊。

地铁里到处都是这个楼盘的广告,最耀眼的还是它的广告词:3万买一居,4万买两居。这句广告词才是重点,什么花园小区、什么80后的梦想、什么北京的后花园,这都是他妈扯淡,价格才是王道。

张小庆他们和一群同龄人一道被塞进开发商的看房班车-一辆面包,然后司机狠狠吐一口唾沫一踩油门,燕郊就扑面而来了。宣传彩页上说30分钟直达国贸,面包开动的一刹那,张小庆开始计时。东边就是发展的比北边差,车出东五环,一过传媒大学,迎面而来的就是平房了,过了一个收费站,两边就只剩下绿化带了。这个变化让张小庆的心情开始变得很差了,太远了。再过一个收费站,手机突然震动了一下,收到一条河北联通的欢迎短信,刚好30分钟。专车还是周末,需要30分钟,如果是平时上班坐公交车呢?

道路两旁全部是售楼处,它们被打扮的花枝招展,等着顾客上门挑选,这哪里是什么国道,分明是妓院一条街。作为宣传页上号称的京东第一大盘,张小庆们要去的楼盘自然也是富丽堂皇的,刚下车,张小庆就憋不住了,他要去厕所,让他想不到的是,整个两层楼的售楼大厅只有一个厕所,男女通用,两个坑,竟然还有一个坏了,没人修,吱吱向外冒着水。

这次预售的是四期,三期前两天刚刚开盘,均价3200,据销售说当时情况很火爆,还有人为争房子打了架,一扇玻璃门都被挤坏了。王晓丽看中了一套70平米的两居室,销售给他们算了总价,按3800计算,是26万多,首付20%的话,需要5万多,两个人现在有3万多块钱,一个月后开盘,再算上下个月工资,勉强能到4万多,家里是指望不上的,再找朋友凑凑,说好一个月后还,也许能够到5万。就这样,王晓丽表示了同意,需要交1万块的排号定金,张小庆却纠结了,他看了看手机中的河北欢迎短信,想,这也太远了。再加上来之前刚看了北京经济频道的投资节目,几位专家唾沫四溅的说,房价现在泡沫很大,奥运过后肯定会降,其中一位专家还专门提到了燕郊,说燕郊连睡城都算不上,根本就是一座死城。于是,张小庆表示了反对,这也是他第一次反对王晓丽的意见。

王晓丽很生气,说,你在想什么?!

张小庆说,这也太远了,上班的话需要很长时间。

王晓丽说,别人能跑,你就不能跑吗?这点苦都吃不了?

听完这话,张小庆就默不作声了。于是,交了1万块钱,两个人就往回走,一路上,两个人都没有说话,每个人都在默默想着自己的心事。张小庆觉得王晓丽从来都不尊重自己的意见,王晓丽则觉得张小庆太幼稚,还把专家的话真当会事。

听说张小庆买了房,杨晓说,哈哈,有钱人。

付江说,恭喜啊,你也是有房一族了。

孙伟推一推眼镜,说,不错,能在北京买个房真好。

张小庆纠正孙伟,说,不是北京,是河北。

又一个周末,张小庆和王晓丽约了孙伟和他女朋友四个人一起去逛了圆明园。张小庆和孙伟的周末都不再加班了,对孙伟来说,原因是普科的项目结束了,这个项目在普科和科技动力都痛苦不堪的时候戏剧般的结束了,原因是客户某部委换了部长,于是,前任部长做的所有项目全部取消,重新招标重新做,对于已经开始但还没有交付的项目一律付全款,真是他妈的好消息,政府真有钱,孙伟松了一口气,王总松了一口气,普科老板松了口气,但张小庆却一点都高兴不起来,相反,他有些郁闷,自己交的税就这样被花掉了。对张小庆来说,周末不再加班的原因是他有些不爽,公司继续在招人,项目部刘哥那边去年的项目需要人手,产品部这边因为孙伟一直在普科的项目上,实际只剩下金鹏和他两个人,所以也在招人,对小公司来说,招人总是困难的,合适的付不起薪水,不合适的那就真的不合适,尽管困难,但公司还是扩大起来,新招入了好几个程序员,金鹏竟然在如此困难的情况下还招入了一个女程序员,并且新来的女程序员还很漂亮,说话柔柔的,一听就知道是南方女孩,自己说自己叫杨柔,真是人如其名,新有了市场部,新有了人力部,公司也开始制定规则,上班开始打卡,迟到3次要扣工资,王总每天都说着要有执行力要有执行力。对公司其他的变化张小庆都没有意见,但对打卡,他却非常反感,他想一直给公司加了那么多的班,现在突然就要打卡,早上迟到一会儿又怎么呢,哪次下班后不是加会班呢,于是,嘴上不说什么,心里决定周六就不再去公司了。

正是圆明园的荷花节,荷花开得争艳。孙伟却扭过头来说,我要离开科技动力了。

张小庆一惊,说,为什么?

孙伟叹口气,说,我的情况你也清楚。

张小庆清楚孙伟的情况。孙伟的家庭情况并不好,家里有两个弟弟都还在上大学,需要他接济,孙伟的女朋友身体不好,一直在吃药,也没有固定工作,在小区的打字复印室里工作过一段时间,突然就犯了病,把周围人都吓了一跳,急忙叫孙伟回去,后来,孙伟就不让她上班了,在家做做饭,再后来,看她闲着难受,孙伟去了趟木须园批了些小玩意回来,在小区前面和别人一起合租了个小门面,一个月100块钱,卖些2元的东西,谁想,开业第一天,只来一个顾客,拿了个发卡,递上的却是100元的大面额,晚上回家一看,女朋友收下的是假钞,女朋友当时就情绪激动起来,要去找那个女孩说清楚为什么要这样对待她,孙伟连忙拦下她,她的病不能情绪激动。事后,孙伟推一推眼镜,说,我总算明白了,开店最重要的第一件事就是买个验钞机。孙伟一直不明白的是,对方也是一个打工的女孩,并且看起来也不坏,为什么要拿一张假钞骗人呢,后来,他想明白了,她一定也是一个受害者,想到这里,他把那张假钞自己给撕掉了。店面又维持了几天,没有顾客,后来就关掉了。

张小庆很好奇,说,你知道你老婆有病,为什么还会和她交往呢?

孙伟推一推眼镜,说,我也不知道,开始是可怜,后来是责任吧。

张小庆说,为什么要离开?

孙伟说,其实是我想离开北京了。之前来北京,是因为这边挣得钱多些,我有两个弟弟需要我的帮忙,现在,一个毕业了,一个自己勤工俭学,我想,是时候了。我想回家当个老师,县城里,上班下班骑个车就可以,中午还能回家吃饭睡会觉,不像这里,每天上班都要一个多小时,太累。来北京后,经过几家公司,都是不停的加班,好像我就是一个加班的命,白天加,晚上加,周末也加,想往上爬,却找不到方向,就是一个最底层的码农。

孙伟的话说的有些伤感,他说,在北京时间越长越感到陌生,这不是我们的城,这是他们的城。

张小庆说,这就回去吗?

孙伟说,不是,换了一家公司,干到年底然后回去,那边工资比这边要高一些。

张小庆说,你和金鹏说了吗,让公司给加些工资?

孙伟说,说了,他们不同意加。工作快4年了,现在工资才6500,唉。

张小庆其实想说自己才6000,但最后他还是没有说,他觉得孙伟换工作有些频繁,但是每个人都有自己的难处,自己这个层次,说是为了钱,其实只是为了生存而已。

孙伟离开了,公司要打卡了,燕郊首付还差钱,这些事情让张小庆变得烦躁起来,他终于失去耐心了,看东西的时候不再能够静下心,看新闻都是只看标题,然后就下了结论,看技术贴都是直接复制代码,不再看原理,开始频繁的上牛博网,重新喜欢上韩寒,一有什么事情第一件事是去刷韩寒的博客看看他这么说,然后自己就怎么说。对社会开始不满,开始抱怨。

情绪就这样累积着,一天晚上下班,骑车驮王晓丽回家,路过一个路口,绿灯,刚骑了没几步,被一辆闯红灯的出租车给撞了,跌坐在地上,双腿钻心般的疼,自行车完全变了形,司机是个北京人,一出来就质问张小庆他们为什么不看路把自己的车给碰了,张小庆就那样坐在地面上,一言不发,他的脑子里一片空白,王晓丽从地上爬起来开始和司机理论,周围围拢上看热闹的人,张小庆还是坐在那里,一言不发,他想,也许孙伟说的对,这是他们的城。

撞车最后的结果是司机赔了100块钱,也就是那辆二手自行车的钱,王晓丽要了司机的身份证号码和手机号,张小庆一直是沉默的,司机走后,有人说应该报警,就是1000块他都会给,张小庆还是没有言语。第二天张小庆自己去了医院,还好,腿只是大面积擦伤,骨头没有事。这事还没有完,过了几天,晚上睡觉时,王晓丽突然说,知道吗,我觉得你根本就不爱我。

张小庆说,为什么?

王晓丽说,那天晚上被撞后你就坐在那里在乎你自己,一点都没有关心我。

posted @ 2011-09-26 21:52 ronghao 阅读(1587) | 评论 (1)编辑 收藏

和以前相比,从东莞回来后,周扬的北京生活发生了巨大的变化。变化有一部分来自于他工作内容的变化,白天变的空闲,晚上变的忙碌,上午业务员们拿着他们做好的牙齿刚出去,没有活,下午随着业务员的返回他们才开始忙起来,一直忙到晚上1、2点,他们是昼夜颠倒的,是昼伏夜出的,是北京的蝙蝠侠,车间里到处都是机器的声音,到处都是粉尘,每个人都戴着厚厚的口罩,只露出一双疲惫的眼睛,有人困了,有人开始讲黄色段子,讲完一段,人们就会大声笑起来,干活就会有精神些,车间里有很多的女孩,不过这时候男女已经不再重要,女人们也会会意的笑起来,眼睛眯成一条线。男人们一下班倒床就睡,臭袜子横躺,鼾声四起,一直持续到第二天上午10、11点,和其他人不同,周扬依旧很好保持了干净和早起的习惯,下班后,他会先去厨房吃点东西,那里有张雨给他熬的粥,黏黏的,躺在一只小小的亮着保温灯的电饭煲里,吃完粥他会去厕所刷牙洗脸洗换下来的袜子然后再去睡觉,早上8点,他起床了,他不会错过免费的早饭,吃完饭他会出去转一转,绕着钓鱼台,沿着昆玉河,买一份报纸,然后回来再睡觉,中午吃完午饭他又会各个车间里乱转,和一帮男人或女人说话,大笑,直到下午开始上班。

其实,这并不是他生活中最大的变化,他生活中最大的变化是女人,是张雨。在东莞打了一年的长途电话,他是那么的想见到张雨,但等到真正回到北京,等到张雨亭亭玉立的站在他面前的时候,他却又千言万语放不出一个屁来,变的不再是以前的周扬了。

做烤瓷一个月休息3天,周末的一天,周扬和张雨去了一趟颐和园。在北京呆了好几年的周扬除去几次车过天安门时看到永久不变而了无生气的巨幅伟人肖像以外去的地方却是贫乏的可怜,北京本来就不是属于他们的,根本就是和他们无关的。周扬原本是不打算去的,但经不过张雨的软磨硬泡,她硬是要拉他出来,张雨倔起来就像一头小牛。女孩子总是喜欢照相的。两个人借了台相机,买了点张雨喜欢吃的薯片就一大早坐上开往颐和园的374汽车。车开得很快,清晨的北京还有一点儿薄雾,公路上还很干净没什么车,它们隐藏在这座城市的各个角落里,一会儿功夫后就会从四面八方钻出来,然后和北京的道路一起纠结在一起。周扬把头靠在车窗上,昨天赶个单子太晚了,还有点头晕,他此刻的心情就象窗外的天气。北京这个家伙正以惊人的速度膨胀着,半年前这里还是一片片树林和平房现在却早被一块块的工地所代替,说不清楚这种变化是坏还是好,他是不是像一块巨大的恶性肿瘤呢?周扬为自己的比喻笑了笑。太阳出来了,城市开始纠结起来了。

园里人不多,周扬从打着很大哈欠的售票员处购完票两个人就手牵着手进了园子。这个上午,张雨像一只被放归大自然的小兽敏捷而灵活地跑来跑去,健康而又匀称的小腿踏在结实的地面上“叭叭”直响。两个人一起划了船、照了相还买了不少纪念品;他们还恭恭敬敬地拜了佛张雨给周扬求了个菩萨,拜佛的时候他们的神情是虔诚的。张雨少女的灵动在这个上午被额外地放大了,她健康而有朝气的身体激起了周扬的不少感觉,周扬感到有点力不从心,常常跑上一段小路就累得气喘吁吁。张雨站在不远的前方等他,她大声地说:你该锻炼啦!周扬想:我很老吗?不啊,我也才二十二岁啊。周扬有一点失落。经过一座小山的时候,张雨坚持要从山上走,周扬选择了绕山的小路。走到山脚时张雨下不去了,那是一个有着一米多高的陡坡。张雨冲站在坡下的周扬说:帮我。

周扬知道张雨是想让他抱她下去,曾经的感觉勃发了,他多少有点幸灾乐祸地说:怎么帮啊?

张雨是何其聪明,她一下子看出周扬是有意的,她索性告诉他说:抱我下去。

周扬说:你重不重啊?

张雨说:为什么不试一试呢?很多东西都要试一试才知道。张雨话中有话。

中午周扬破天荒地要了点啤酒,自从上次跟过何林后他就从来没有自己喝过酒,他一看到酒就想吐。今天是个例外,因为他的心情很好,对面坐着的张雨的脸红扑扑的,她显然还未从刚才兴奋中走出来。周扬突然就有着那么一点心动:真是个美丽而可爱的女人!但周扬的好心情并没有持续多久,张雨说,知道吗,我报名参加了今年的成人高考,我一定会考上。

没有张雨想象中的回答,周扬只是淡淡的回了一句,是吗?

张雨说,怎么,你不喜欢吗?

周扬说,没有,这是好事。

周扬想他是明白对面这个女孩子的想法了,她想留在这里,她想留在北京,为此她在不断努力,和工厂里其他的女孩子不同,她们一开始就认为自己只是北京的一个过客,挣点钱,然后回家去,结婚,生子,然后养孩子,一辈子,而她,则在努力使自己成为北京的一部分,记得有一次路过一幢漂亮的写字楼,张雨曾经认真的对自己说,知道吗,总有一天我也会在这里上班的,当时自己只是笑笑并未当真,但是,现在,她正在让它变为现实,从某个点上说,她和自己是何其的相似,为了一个目标一定要想办法达到。但是,这根本和自己的目标是矛盾的,自己是要回家的,不是北京。想到这里,周扬的心情沉重起来,也许,从一开始,这就是一个没有结局的开始。

每天早上,周扬都会在钓鱼台旁的那座八里庄桥上站上一小会儿,桥下是静静的流水,桥上是人来人往车水马龙,不时有一艘满载乘客的汽船从桥下穿过,在平静的水面上拉出长长的涟漪。他扒在桥栏上,任河风拂起他的头发,他的思绪飘得很远,他是在休息。人行道上经常会有一两个卖些小玩意诸如钥匙链指甲刀之类的小摊,东西很便宜但质量并不怎么的。周扬买过一回,他便跟那人认识了,但很快那人就消失了,没人知道他,周扬也没去问。北京太大了,人又太渺小了。现在,周扬满脑子想的都是张雨,真的要在北京吗,北京到底好在哪里呢,为什么那么多人都想着往北京来呢,他们从老家带着五颜六色的梦想来到这里,真正实现的又有多少呢,又有多少人最后留在了这里,又有多少人依旧坚持着当初的梦想呢,又有多少人其实只是为生活而奔波呢。周扬的思绪有些乱,桥下,一阵小孩的哭闹声吸引了他的目光,那是个卖盗版光盘的年轻妇女,怀里抱着一个小孩,正在哭闹,旁边,两个警察要带她走,女人说好话被警察不耐烦的打断,于是,女人也和小孩一起哭闹起来。警察厉声说,别给我装了,我们盯你好几天了!这个场景让周扬下定了决心,于是,他又变回了以前的那个周扬了,他要和张雨说清楚,他不做没有结果的事情。

周扬去张雨寝室找她,门没锁好,从外边能够扭开,周扬没有敲门就进去了,然后他看到了惊讶的一幕:张雨刚洗完澡正在换衣服,她背对着周扬解开乳罩的搭扣,粉红色的乳罩轻轻滑落,露出她那光滑的脊梁,少女光洁的背部把周扬的眼睛刺伤了,有那么两秒周扬脑袋里一片空白,他所有的感觉器官在那一刻都迟钝了,他就那么呆呆得站着,他能想象得到那光洁背部的前面有着怎样的一对,挺拔、温暖、柔软甚至是可亲。张雨的裸体随着身体的转动在透过窗帘的阳光中或明或暗变换着各种角度焕发出无限的光彩。周扬感到自己的勃起,这惊醒了他,他为自己的失态感到不安,他想离开但却又喉咙发干挪不开步子,于是就在那里怔怔的站着。张雨也发现周扬了,她没有表现出惊慌,相反,她用冷静的语气告诉周扬说让他把门锁上,张雨红着脸说,没事,背过去就行了。上下不安的周扬站在张雨的背后,紧张、无序、口干得厉害。实际上的周扬,脸涨满复杂的红色,全身皮肤一阵紧是一阵,手心难以置信地挤满汗水。他才二十二岁,青春原始的激情在此时无限的勃发了。穿好衣服的张雨从后面抱住了他,柔顺的头发从她的头上垂落下来掉到周扬的脖子里,周扬甚至能感觉到她轻盈的呼吸,张雨调皮的脸上有着可爱的一层细细的绒毛,上面罩上淡淡的洁光。张雨俏皮的说:找我干吗?周扬没想到张雨会突然从后面抱住他,这让他急促,他说,我。话吐了一个字,然后谁都没空再去理它,他们抱在了一起,接吻。张雨的嘴里甜甜的好像有股冰激凌味儿。周扬感觉自己的身体在不停地哆嗦,他控制不住自己,张雨也在哆嗦,这是他们的第一次,他们像秋天寒风里同一颗枝上的两片叶子紧紧搂在一起。晚上周扬失眠了,他知道他需要什么,白天张雨的裸体一遍又一遍地鲜活地呈现在他面前,他开始抚摸自己,最后达到高潮的时候他几乎忍不住要叫出来--张雨背对他慢慢转过身。他终于违背了自己的理智,陷进去了。

posted @ 2011-09-18 23:48 ronghao 阅读(1818) | 评论 (4)编辑 收藏

在周扬的牙齿工厂,张小庆第一次见到了王刚,个子不高,头发理的有些奇怪,前面的很短,后面却故意留住了,两个鬓角也都没有剪,穿着一身蓝色的运动服,贵人鸟的,脚上一双白色旅游鞋,也是贵人鸟的。正在宿舍里和一帮人打牌,看到张小庆和王晓丽来,摸一张牌站起来,喊了声姐,然后喊了声哥,接下来问王晓丽,我的L7买了没?

自从接到王刚的电话,王晓丽就张罗起来,想到哪里给弟弟找份工作,王刚初中没上完就回了家,忙的时候就帮张芳,大部分时间,和农村里其他的小年轻一样,骑着摩托,到处逛荡,打麻将,上网聊QQ,将音响放的老大,穿山寨的名牌,抽烟,喝酒,偶尔打个架,可随着日子的过去,越来越多的同龄人开始出去打工,没有人想在家种地因为没钱,于是,逛到最后只剩下他一个人,他就给姐姐打了电话,对城市,对北京,他是有期望的。最后,张小庆给周扬打了电话,其时,周扬正在车间里做烤瓷,戴在大大的口罩,到处都是粉尘,和3年前一样,周扬说,没事,来吧。

都以为来的是王刚一个人,结果却是一双。王刚把他的小女朋友带来了,女朋友叫石兰兰,是王刚1年前剪头时认识的,当时还不到16岁,在理发馆里帮人洗头。石兰兰没有叫哥,叫了一声姐。周扬安排王刚先做业务员送货,石兰兰则学做烤瓷。

在肯德基里吃饭时,王晓丽展示了她在北京生活的优越感。

王晓丽说,看你穿的这身衣服,真俗!

王刚说,贵人鸟名牌的好吧,这一身加鞋子花了700多呢。

王晓丽说,哎哟,还贵人鸟的呢,从没听说过,北京就没有这个牌子。

王刚说,刘德华做广告的。

王晓丽说,就会骗你们这些没见过世面的老农民。

王晓丽说,从没吃过肯德基吧,好吃吗?

王刚说,吃过好吧,在襄樊。

王晓丽说,襄樊那种小地方也有肯德基?麦肯鸡吧。

不管怎么说,王刚和石兰兰都工作了,王晓丽的心情很好,因为这是她安排的工作,这件事如果在家里被说起来,是很长脸的事。但好心情并没有持续多久,很快,王刚打过来了电话,说,姐,我不干了!

王晓丽说,为什么?

王刚说,他们欺负我。

在家野惯了,突然要按时上班,受人管,王刚很不习惯,送货员其实是很辛苦的,每天6点就要起床,吃早饭,然后出发,要错开早高峰,晚上,如果刚好分的送货点远要很晚才能回来,一天中的大部分时间是在公交车上度过,一瓶水,一个包,一张月票,就是他们的生活。这天早上,王刚起床晚了,领牙齿的时候已经快9点了,主管批评了他,说你现在才过来今天怎么把活送完?!主管的话有些重,王刚很快表示了自己的不满,从小到大,除了王超,还没有人如此斥责过他,这几天天天在外边奔波的委屈一起涌上心头,他立刻就说,我不干了!不仅如此,他还去车间一把把石兰兰扯出来,说,收拾东西,我们不干了!于是,两个人收拾了大小包等王晓丽和张小庆来接,王晓丽和石兰兰一起睡,张小庆和王刚打地铺。

继续找工作,没有人,找工作的事情并不顺利,王晓丽她们最先去了小区周围的饭馆,一家一家的问,因为石兰兰不会普通话,饭馆都不要。最后,王晓丽突然想起来一个人,那是她曾经的客户,也是老乡,但是很久都没联系了,犹豫了好久,最后还是打了电话,那人在一家医院管后勤,王晓丽想石兰兰去饭堂打饭,老乡很爽快说没问题过来吧,商量了一下,王晓丽和张小庆买了500块的超市卡送过去。于是,石兰兰上班了,包吃住,一个月1000块,王晓丽很满意。接下来就剩王刚一个人,周日的时候,张小庆带他去了苏宁,那里招聘司机,王刚会开车,但是笔试一直没有考过没有驾驶证,只有一个换了照片的假驾驶证,管不了那么多了,先去面试。

面试官问,对北京的路况熟吗?

王刚说,不熟。

面试官问,会看地图吗?

王刚说,不会。

从苏宁出来,张小庆有些无语,他对王刚说你不开了很长时间的车怎么地图都不会看吗?王刚说,你知道我的看到书就头疼地图也一样,在家里开车都有人带。王刚说,哥,我刚才看到L7了,真漂亮。

对于王刚,张小庆是深深失望的,这个只比自己小2岁的男人似乎还停留在男孩阶段。他原以为农村出来的人都是能吃苦淳朴的,但是王刚让他改变了想法,不能吃苦,在外边一点委屈不能受,只剩下对名牌的向往,没有工作也不着急,天天上QQ,开两个,一个是自己的,一个是石兰兰的,一次因为石兰兰的一个网友说了一些暧昧的话,抓住石兰兰的衣领在小区里大吵大闹一定要个解释。于是,张小庆就懒得和王刚说话,就是这样,对不喜欢的人,张小庆就会沉默。

石兰兰也没有干多长时间,王刚吵着不和张小庆他们一起住了,要去石兰兰那边住,没有地方,住地下室招待所,他第一天去,第二天早上就给王晓丽打了电话,姐,我们不干了!

王晓丽问,出什么事了?

王刚说,有个男人调戏兰兰!

这次王晓丽发火了,说,你不要给我编了!你怎么想的我清楚的很!

王刚说,反正我们不干了,你给我们买票,我们要回家。

王晓丽说,不买,自己挣钱买。

王刚说,你还差我的L7,我不要了,你给我买票。

挂了电话,王晓丽气的直哆嗦,为王刚也为她自己,一会儿,冷静下来,她突然又可怜起王刚来,这个人,从小似乎就在王超的挨打中度过,王超很不喜欢他,只要他一调皮就打他,往死里打,一次,因为掏别人鸡窝的事,王超一板凳拍在王刚的脖子上,一下子就把这个可怜的孩子打晕过去,王超以为他在装,还接着踢了他一脚,直到发现孩子口吐白沫这才慌张起来急忙送去卫生所。从小到大,王晓丽什么都不缺的,衣服,有新的,想吃什么,马上就给买的,农活,也是从不做的,而王刚,为了一双几块钱的新凉鞋,摘了一个夏天西瓜,把自己晒脱了一身皮。想到这里,王晓丽给王刚打了电话,明天我们送你去火车站。

从火车站出来,张小庆轻松了一口气,这些天为王刚找工作的事,两个人都忙死了。没想到王晓丽突然转过一张脸来,说,瞧你那张对我弟弟的死脸,真恶心!

posted @ 2011-09-05 21:44 ronghao 阅读(1904) | 评论 (1)编辑 收藏

BPMN的流程模型

我们使用业务流程建模来交流信息,正如在上一节里所述,根据不同模型的用户(客户、业务人员、分析人员、开发人员),建模有着不同的风格。BPMN被设计用来涵盖各种风格的流程模型(以满足不同角色人员交流的需要)和创建端到端的业务流程,它支持三种基本类型的流程模型:

Ø 流程编制(Process Orchestration),包含:

 

  • 私有的不可执行的(内部的)业务流程;
  • 私有的可执行(内部的)业务流程;
  • 公开流程。

 

Ø 编排(Choreography)
Ø 协作(Collaborations),包含流程与/或编排。

 

  • 具有会话视图(Conversation)。

 

私有的(内部的)业务流程

私有的业务流程是指某一组织的内部工作流程,我们通常称之为称为工作流,在WEB服务领域,我们也称之为服务的编制(Orchestration)。存在两种类型的私有业务流程:可执行的和不可执行的。可执行的私有业务流程以被计算机执行为建模目的,由相应的BPMS系统来自动化流程的执行,它包含了足够的执行细节,这些细节包括执行规则、条件表达式等计算机解释执行所需要的技术信息,该模型最直接的用户是开发人员。不可执行的私有业务流程则以文档化为建模的目的,它缺少执行细节,但是包括足够的交流信息,该模型的用户包括了业务人员与分析人员。

作为一个例子,我们一起来看看我在公安局户籍科为儿子办理户口的流程,如下图所示:

 

图1公安局户籍科上户口的流程

当我来到户籍科,递上足够的资料,然后就开始等待。我能看到共有四个工作人员,第一个工作人员负责接收资料,查看资料是否完备,接下来,她将所有的资料传递到下一个工作人员,第二个工作人员对资料进行审核,在计算机上查看我们的户口信息是否正确,接下来,如果资料无误,他将资料传递到第三个工作人员,第三个工作人员负责在计算机上为我的儿子录入新的户口,最后打印出一张户口页,第四个工作人员是职位最大的警员,她负责盖章,然后将儿子的户口页传递给在窗口外等待的我。

根据上面对私有业务流程的定义,我们很容易的判断出这个流程是个不可执行的私有业务流程,因为该流程是户籍科的内部工作流程,作为该流程服务对象的我,我根本不用关心户籍科内部是如何对我的申请进行处理的,所以它是私有业务流程。该流程是由规章或制度所规定的,由工作人员来驱动,并非通过计算机协调,所以这是个不可执行的私有业务流程。

因为私有业务流程是内部流程,所以它只能存在于一个池(pool,池代表一个参与者)里,如下图所示,我们可以将私有业务流程建模在一个池里,但通常这样做没有太大的意义,更经常的情况是,我们选择将池忽略。

图2公安局户籍科私有业务流程的另一种建模形式

公开流程

公开流程表现一个私有业务流程与其他流程或参与者之间的交互。

还是以户口办理作为例子,作为户口申请人,我来到公安局户籍科,我心揣揣,我不知道我该做些什么,于是我看到大厅里如下图所示的流程,于是我立刻就明白了,我只需要将资料交给办事人员,然后等待取新的户口页即可。

注意下图所示的公开流程与图1所示的私有流程有哪些不同。第一是图中出现了多个参与者,参与者间通过消息流连接(图中虚线箭头连线);第二是户籍科的办理流程被缩减到只剩两个与外部参与者交互的活动,两个原有的内部活动被忽略了。这两点不同即是公开流程的特点:表现与外部参与者、流程间的交互,忽略内部活动。联想到我们实际的编程,总结成一句话,就是隐藏内部实现细节,仅仅展现对外接口,表现流程的外部行为。


图3公安局户籍科办理户口的公开流程

协作

协作描绘两个或多个业务实体间的交互。一个协作通常包含两个或多个池,每个池代表一个参与者(业务实体)。参与者之间的消息交换通过连接两个池(或池中的对象)的消息流表现。协作可以表现为两个或多个公开流程之间的交互,在上一节里,我们提到,与对应的私有流程相比,公开流程隐藏了内部细节活动。池也可以是黑盒,即里面什么对象都没有。

那么,这里有一个问题,公开流程与协作有什么区别?区别在于表现的范围,公开流程只是表现一个私有流程与外部的交互,而协作则能表现多个流程/参与者之间的交互。

还是看户口办理的例子,在前面的例子中,我们看到了公安局户籍科办理户口的私有流程和公开流程,似乎办理户口是一件很简单的事情,但这仅仅只是办理户口中的一步而已。在此之前,我先要去医院办理小孩出生证明,接下来,我要去居委会登记小孩信息,再接下来,我要去计生办办理符合计划生育政策的证明,最后,我才来到户籍科。下图是整个户口办理的协作图,作为简化,这里将除申请人和户籍科之外的池都黑盒了:


图4户口办理协作图

编排

同样是表现多个参与者之间的交互,编排做的更为纯粹,它取消掉了池的概念,改由编排活动直接表现多个参与者之间的消息交互,为协作模型提供了一种基于流程图的视图。户口办理的编排图如下图所示,其中每个活动都能看到上下的方形区域有参与者信息,这表明这个活动的参与者,浅色部分为活动的发起者,深色部分为活动的响应者,我们会在接下来的BPMN元素小节里详细描述这一活动类型:


图5户口办理编排图

与协作图相比,编排图省略掉了更多的细节,例如与各个参与者具体的交互过程,它关心谁和谁产生了交互,至于如何交互,分几步交互,它并不关心。例如办理户口这个活动,实际上我是分别和两个警官进行了交互,一个是负责接受资料的年轻女警官,一个是负责盖章复核的领导警官,在协作图中,我可以通过公开流程展现出这一点,但是在编排图中,这并不是要表现的重点。

协作图表现出参与者之间的交互,并包含交互的细节信息(交互的接口、如何交互);编排图则以流程图的形式表现出参与者之间的交互,它关心的是某个任务需要哪些参与者发生交互,交互的细节不是其表现的重点。

         编制与编排的区别

在上文中,我们提到了服务的编制(Orchestration),这里,我们又提到了编排(Choreography),这两者是有很大区别的。

WS-BPEL将SOA中的服务按照一定的顺序灵活组装在一起的流程就是编制(Orchestration)。编制后的WS-BPEL流,通常代表了某个特定的业务中的服务的执行流。而编排(Choreography)则是描述参与者之间交互关系的流程。与编制不同的是,编排并不需要一个执行引擎,它只是描述关系。编制代表的是一个可执行流程,它必须通过执行引擎来执行。而编排实质上是代表一种描述,即参与者之间如何互相协调来完成一个目标。

John Reynolds在其博客中是这样描述编制和编排的区别[1]

编制 == 可执行过程

Web服务编制与执行特定的业务流程相关。WS-BPEL是一种用来定义可以在一个编制引擎中执行的流程语言。

编排 == 多方合作

因此编制必须对应一个执行引擎,而编排由于涉及到多方合作,所以它是不能被直接部署的。

 

协作的会话视图

会话视图为协作图提供了另外一种非正式的表现形式,与编排图一样,它的目标同样在于表现参与者之间的关系,它将一系列相关的信息交互定义为一次会话。户口办理的会话图如下图所示,图中只存在池与会话(Communication)元素,会话元素由图中的六边形代表,它代表两个或多个参与者之间一系列相关的信息交互,我们可以看到,办理户口需要申请人与四个组织发生四次会话:


图6户口办理会话图

会话视图的作用之一是能够有效减少模型中消息流的数量,便于我们理解。


图7会话视图简化交互模型

posted @ 2011-08-31 23:39 ronghao 阅读(1991) | 评论 (0)编辑 收藏

年初的一个周末,张小庆和王晓丽一起去了昌平,说的是去十三陵水库,更重要的是去看看昌平的房子,他们惊讶的发现昌平的房子也已经涨到6000了,这个结果多少让十三陵亮丽的水库显得黯然失色。

在买房这件事上,张小庆其实是不再着急的,他觉得房价在奥运过后会降的,但他也知道王晓丽相信自己女人的自觉胜过专家,于是,他说,相信我,我工资增长的速度肯定会超过房价的。

王晓丽哼了一声,说,就你们那十来个人的小公司?

公司情况比预想中的还要糟糕,去年年底还有几家公司对公司的新平台表达了购买意向,今年就只剩下一家叫普科的小公司,并且他们说如果到3月份还不能发布出第一个版本,那么他们也将转求其他的解决方案。话虽这么说,但张小庆和金鹏都知道这家比科技动力还小的微型公司根本没有选择的余地,普科软件加上老板只有3个人,其中2个是程序员,他们完全是为这个部委的电子政务项目而成立的,老板在原先的公司有些关系,于是原先的公司就将这个项目外包给了他,他就拉了2个人租了个2居室开始干活,他显然隐瞒了些什么,有一次他行色匆匆的来到科技动力要求借科技动力的办公室用用,说是有领导要来普科看看,自然是不能去小区里的2居室的。很快,在王总的同意下,张小庆们一起行动起来,他们更换了门口的公司招牌,把带有科技动力标识的宣传册和条幅都挪进了机房,并改口称自己是普科的员工。在王总屁股热气还没有完全消失的座位上,普科老板一屁股增加上自己的气味。

3月份的时候,尽管jira上还存在长长的bug单,但TechFocus还是发布了,新产品很快就被普科采用了。金鹏、张小庆和孙伟三个人开始分任务写TechFocus的使用文档和设计文档,吃饭的时候,孙伟推一推眼镜,对张小庆说,写文档可比写代码难搞多了。让孙伟没有想到的是,他很快就不再有写文档的机会,TechFocus实际开发时发现的问题比jira上要多得多也严重的多,孙伟被派去了2居室做技术支持,最开始是隔天去,后来就是半天去,最后他上班就直接去2居室了,再过一段时间,2居室的程序员只剩下1个,于是2居室就直接全体来科技动力现场办公了,反正就多需要2个座位。

2居室老板说,老兄啊,你可把我坑惨了,我算发现了,我就是你们的小白鼠。

王总呵呵一笑,说,老弟啊,话可不能这么说,我们已经把我们公司最能干的一个程序员免费给你使用了,做人要讲良心。

公司没有实现希望中的增长,张小庆的生活就是上班、下班、加班外加业务时间看看一些开源软件的源代码,他的生活似乎就要这样持续下去了,但是一个人改变了他的生活,这个人叫康威。认识康威是在一次openparty的聚会上,张小庆对各种程序员活动都很热心,只要有时间,他都会去参加,绝大部分的时候,他作为听众安静的坐在下面,认真的听上面的人讲,心里偶尔也会掀起波澜,他也想有机会在上面讲。话题演讲的间隙,演讲者会和与会者进行交流,每当这个时候,张小庆是沉默的,他变成了一个内向的人,他觉得自己是渺小的,和那些人比起来,自己是微不足道的。于是,尽管一次又一次的参与openparty,他却没有认识任何人,每次都是一个人来,带个小本子,记录完,然后又一个人走。但这次情况却发生了变化,因为下雨的原因,到会的人并不多,于是主持人康威让每个人都用一分钟介绍一下自己,然后每个人都要提交一个话题,然后投票。这个变化让张小庆心里慌了神,他想了半天,觉得实在是没有什么拿得出手的话题提交,轮到他介绍自己时,他结结巴巴的说,我曾经是一个军人,乐观,不放弃;轮到他提交话题时,他歪歪扭扭的写道,工作流系统介绍。他的话题自然是没被选上的,工作流这个话题,如果放到3年前,自然是极热的,但是现在是07年。话题间隙,康威突然向张小庆走过来,说,你当过兵?

康威是社区里的牛人,张小庆认识,他不仅是好几个技术社区的斑竹,而且经常在杂志和会议上发表文章和演讲,张小庆说,是。

康威笑起来,说,我也当过兵。

共同的经历突然让两个人熟识了,两个人聊起那曾经的生活,某种东西在他们的眼睛里闪烁,而且更巧的是,康威也是炮兵,他们甚至聊起了部队最新自行火炮的射程。聚会结束的时候,康威再次找到了张小庆,要了他的手机号,说要常联系。张小庆说,好,常联系。

康威很快联系了张小庆,他正在翻译一本ajax的书籍,问张小庆有没有时间一起翻译。张小庆一口答应下来,其时他正看完了prototype的源代码,对使用javascript在客户端实现mvc模式很有兴趣,而这本书里刚好有一整章讲这个模式,几乎是一拍即合。于是,周六的时候,王晓丽去上班,张小庆洗完衣服打开电脑开始翻译书,张小庆的英语并不好,他几乎是一个句子一个句子的翻译,翻译完了再用google翻译一个句子一个句子的对照翻译理解,书中出现的谚语他也都要去一个一个查询字典,尽管如此,第一章的翻译却不尽如人意,主要原因是翻译的太生硬,很多不需要翻译的主语,不需要的助词都被他翻译出来了,康威很耐心,他将张小庆翻译的第一章几乎是重新又翻译了一遍,然后写了一份翻译注意事项发给张小庆,于是,在翻译第二章的时候,张小庆突然就有了很大的提高。

周末不再有时间陪王晓丽,王晓丽有些小小的意见,说,翻译一章多少钱?

张小庆说,这是按字数付费的,一千个字60块钱。

王晓丽说,真廉价。

王晓丽说,下周日一起去燕郊看房。

张小庆说,真没时间,下个月就要交稿了。

去燕郊看房是高晨晨提的建议。上周四是高晨晨的生日,她很早就和王晓丽约好了,下班后,张小庆没有直接骑车回家,他去清河小营的味多美取了生日蛋糕,接下来他去了高晨晨的单位宿舍,高晨晨、王晓丽、何鑫已经在那里等他了,四个人一起打了车去了西三环航天桥的湘鄂春酒楼,那里的湖北菜很正宗,能吃到家乡的味道。接下来,吃完饭四个人又打车去了后海,在那里他们挑了一只小船点起灯笼在水里划船。旁边,不时有大一些的船飘过,船首坐着一个穿着旗袍的姑娘,叮叮咚咚的弹着古筝;远处,岸上一片灯火辉煌,悠扬的音乐由远及近的飘荡过来。有那么一瞬间,张小庆仿佛觉得自己穿越到了电视里古代的秦淮河畔,那时的情景,也不过如此吧。张小庆又想起了自己第一次到北京找工作,第一次来到的景点就是后海,那时和余鹏、王娓娓三个人每个人拿着一瓶矿泉水在河提上漫无目的的走,虽然一无所有,但是充满理想,现在,3年过去了,依旧怀有理想,但两个理想好像不是一回事。

生日聚会的高潮出现在高晨晨吹灭蜡烛许愿的时候,三个人给她唱起生日歌,当她许完愿睁开眼睛,何鑫突然在她眼前展开手掌,那里躺着一只漂亮的戒指,于是高晨晨的眼睛随着她的笑容一起荡开了。

高晨晨和王晓丽一个村子长大,一起上小学,一起上初中,又一起来北京,一起回家,这一切都让所有人不由自主的将她们放在一起比较。除去王晓丽父亲王超在镇上砖瓦厂里当主任那段短暂的美好时光之外,王晓丽在大部分时候都是处于下风的,小学没有太大的区别,初中开始,王晓丽家里挣钱的重担就由她母亲张芳一人承担了;初中毕业,高晨晨考取了市里的重点高中,王晓丽则不得不继续在镇上读高中;高中毕业,高晨晨考取了一本,王晓丽则名落孙山,她选择了转读艺术复读最后考取的是大专;大学毕业,高晨晨在她大伯的帮助下没有费任何功夫就进入了国家电网,转了北京户口,王晓丽则从一个很小的装修公司开始做起;现在,高晨晨买了房,交了在中石油工作的男朋友,王晓丽则找了一个程序员,买房对他们来说还是奢望。每次回家,不管走到哪里,有她的地方就有人说到高晨晨,说到高晨晨的好,买了房,找了有钱的男朋友,还转了北京人,言下之意就是王晓丽混得差。对这一切,王晓丽都是沉默不语的,她本来就是一个内向的人,这些话让她更加不爱说话,她最不能忍受的不是别人,是张芳也在她面前说高晨晨的好,她几乎是叫起来,你给我闭嘴行不行,烦死了。

除了生日蛋糕,其他所有的费用,吃饭、打车、划船,都是何鑫给的钱。最后高晨晨她们还一直把他们送回了小区,回去的路上,高晨晨的兴致很高,她突然对王晓丽说,对了,燕郊的房子很便宜,你们去看了没有?

张小庆说,燕郊在哪儿?

高晨晨说,河北。

张小庆他们终究还是没有去成燕郊,原因不是张小庆没空,王晓丽决定的事,他是拗不过的,是王晓丽的弟弟王刚要来北京了。之前只要王刚给王晓丽打电话,他都会问同样一句话,姐,什么时候给我买L7?他看上了摩托罗拉的L7,于是,理所当然的,姐姐应该给他买。每当这时,王晓丽都没有好气,说,你都22岁的人了,自己挣。王刚说,我在家里上哪儿去挣钱?在王刚心里,他是为姐姐做出了牺牲的,自己初中没有毕业,王超把所有钱都花在了姐姐身上,王晓丽差自己的。

但是这次,通话的内容发生了些变化,王刚说,姐,我要来北京了,家里干活太累,我要来北京了,我要来和你一样在城市里享福了。

末了,王刚说,记得给我买L7。

posted @ 2011-08-29 00:00 ronghao 阅读(1827) | 评论 (2)编辑 收藏
在上一章里我们了解了BPM的基本概念,什么是业务流程管理,业务流程管理是一种以规范化地构造端到端的业务流程为中心,以持续地提高组织业务绩效为目的的系统化管理方法。同时,我们也了解了BPM活动的五个阶段:设计、建模、执行、监控和优化。在本章中,我们将把关注点放到BPMN上,什么是BPMN,在BPMN1.X里,BPMN是Business Process Modeling Notation的缩写,即业务流程建模符号,而在BPMN2.0里,BPMN变成了Business Process Model And Notation的缩写,即业务流程模型和符号,一个单词的增加却标示着BPMN本身发生了巨大的变化。到底是怎样的变化呢,让我们一起进入BPMN那段悲催的历史。 

BPMN最早是由业务流程管理倡议组织(BPMI, Business Process Management Initiative)开发的,这个组织的领导者是Intalio公司。提到BPMI组织,不得不提 BPML(Business Process Modeling Language) 业务流程建模语言。在敏锐的认识到Web将成为未来分布式系统架构的平台后,BPMI组织创建了BPML,一种全新的流程执行语言,该语言不与任何供应商绑定,而BPMN则作为BPML的可视化表现符号被创建。BPMI组织的会员在高峰期达到了200多家公司,除了IBM和微软,几乎所有的主要软件供应商都加入了该组织。 

BPMN则反映出BPMI组织的另一个具有前瞻性的观点,即业务人员(多是非技术人员)对IT执行流程的可视化和管理将成为未来BPM系统的关键。通过授权,业务人员能够管理自己的流程。在BPMN出现之前,市面上已经存在流程建模图的标准例如UML的活动图(UML由对象管理组织OMG维护管理,很快,我们将再次看到这一组织),但这些标准被认为过于技术化,而BPMN在被设计之初就强调要对业务人员友好。BPMN1.0在2004年5月由BPMI组织正式发布,其全称是Business Process Modeling Notation,即仅仅作为业务流程建模的一系列符号标准。 

对BPMN和BPML来说,两者的遭遇截然不同,在BPMI组织的会员中,BPMN受到了大多数流程建模工具厂商的欢迎,他们认为统一的建模标准能够使他们围绕核心建模工具提供其他更多的价值,而BPML则遭到了很多工作流厂商的痛恨,因为一个统一的流程执行语言标准将使得他们重新竞争,而私有的流程执行语言已经将市场分割,他们想维持现状。因此,矛盾从一开始就存在了,BPMI组织原计划是建立一套业务人员能够自管理的流程系统标准,BPMN关注业务流程的描述和分析,它建立的模型是面向业务人员的,是不可以直接执行的,而BPML则由BPMN自动生成可执行的流程语言,交由IT系统执行,但是现在,BPML被工作流厂商们认为是对自己的一种威胁。 

事实上,厂商们对BPML是多虑了。IBM和微软很快开始了反击,他们在2002年8月推出了BPEL-WS规范,一个与BPML有稍许不同的语言,基于新的WSDL标准。BPML与BPEL-WS之争也被看作是Betamax与VHS格式之争,Betamax品质优秀,但VHS得到数量众多的制造商支持,Betamax战败,于是BPML被消灭。 

2005年,BPMI组织被OMG组织合并,BPML停止维护,2006年OMG组织正式通过BPMN1.0规范,2008年2月发布BPMN1.1。 

记忆里,有那么多的规范、标准,从开始炒作的沸沸扬扬,到最后的逐渐淡出,不过几年光景。但BPMN却在2008年大爆发,得到了极大的普及。具有讽刺意味的是,BPMN的流行完全归功于那些当初反对BPML的工作流厂商们,恩恩,现在他们都改名叫BPMS厂商了。原因很简单,业务人员对IT执行流程的可视化和管理已经成为BPMS系统的关键,BPMI组织猜到了结局,却忘了猜猜自己。 

BPMN被BPMS厂商们大量采用,他们使用它来进行流程的建模,至于模型的执行和存储,则由他们各自不同的流程执行语言实现。时至今日,BPMN1.x被大多数的建模工具和BPMS厂商所支持,他们关心的是建模,没有人关心BPMN的直接执行,也是,BPMN的主要用户是业务人员和流程分析人员。 

那么,BPMN的故事结束了吗?显然没有,BPMN1.x只是一些建模符号,不支持元模型,不支持存储和交换,也不支持执行。那么围绕着BPMN1.x的存储、交换和执行,必然会产生新的竞争,这次的主角换成了XPDL、BPEL和BPDM。 

XPDL作为WfMC提出的流程定义语言规范,本身就是一个元模型,可以存储,并且具备执行语义,因此理论上来讲,将BPMN转换为XPDL就可以解决存储、交换和执行的问题。XPDL2.0于2005年10月发布,在规范里,WfMC直接将XPDL的目标定义为BPMN的XML序列化格式。2008年4月23日发布的XPDL2.1规范,直接支持BPMN1.1到XPDL2.1的转换。XPDL是面向图的,BPMN也是面向图的,因此BPMN到XPDL的转换有着天然的优势。如今有超过80个的不同公司的产品使用XPDL来交换流程定义,同时也有一些厂商在自己提供的BPMN工具中使用了XPDL作为交换和存储格式。 

但XPDL的流行是大厂商们所不愿看到的,他们的规范自然还是BPEL,我辛辛苦苦PK掉BPML,您XPDL抢位来了,我情何以堪,情何以堪啊。BPEL-WS规范在2003年4月提交给了OASIS(Organization for the Advancement of Structured Information Standards,结构化信息标准促进组织)并更名为WSBPEL(Web Services Business Process Execution Language)规范, 2007年4月发布WSBPEL2.0版本,除了Microsoft、 BEA、 IBM、 SAP 和Siebel,Sun Microsystems和甲骨文公司也相继加入了OASIS组织。除去政治因素,BPEL的流行还在于Web正成为分布式系统架构的平台以及SOA的雄起,SOA强调服务的分解和解耦,而BPEL则对这些WEB服务进行编制,两者密不可分。但BPMN到BPEL的转换存在着先天上的缺陷,原因是BPMN是基于图的,而BPEL是基于块的,BPEL是一个结构化(块[Block])和非结构化(控制链和事件)的混合体。这个缺陷导致有些BPMN建模的流程无法映射到BPEL,两者的双向工程更是存在问题。这个缺陷成为人们反复诟病的对象。许多支持BPEL的产品为了解决这一问题,不得不在用户建模时做出种种限制,让用户绘制不出无法转换的模型。 

而BPDM(业务流程定义元模型,Business Process Definition Metamodel)则是OMG组织自己提出来解决BPMN存储和交换问题的规范。于2007年7月形成初稿,2008年7月被OMG最终采用。BPDM是一个标准的概念定义,用来表达业务流程模型。元模型定义了用来交换的概念,关系和场景,可以使得不同的建模工具所建模出来的流程模型进行交换。BPDM超越了BPMN和BPEL所定义的业务流程建模的要素,它定义了编排和编制。 

三者的竞争关系似乎还将继续,但,BPMN2.0出现了,BPMN2.0 beta1版本于2009年8月发布,BPMN2.0 beta2版本于2010年5月发布,BPMN2.0正式版本于2011年1月3日发布。BPMN2.0正式将自己更名为Business Process Model And Notation(业务流程模型和符号),相比BPMN1.x,最重要的变化在于其定义了流程的元模型和执行语义,即它自己解决了存储、交换和执行的问题,BPMN由单纯的业务建模重新回归了它的本源,即作为一个对业务人员友好的标准流程执行语言的图形化前端。BPMN2.0一出手,竞争就结束了,XPDL、BPEL和BPDM各自准备回家钓鱼。看起来胜利者似乎是BPMN,但看看BPMN2.0的领导者,就会发现最后的胜利者还是IBM, Oracle和SAP这些大厂商们,他们提交的草案明确要赋予BPMN2.0以执行语义,这迫使BPDM团队撤回了其提交,并将他们的提议与BPDM团队想法合并,这就是BPMN2.0最后内容的由来。 

BPMN的目标是期望通过一套统一的建模、执行模型填起业务人员与开发人员之间的那道鸿沟。但问题是它真的能够如它期望般的做到这一点吗,对业务人员友好的模型对开发人员同样友好吗,反过来,对开发人员友好的模型对业务人员同样友好吗,尽管他们使用的都是同一套符号?我们在后续的建模实例里将看到这样的问题,这涉及到建模的风格。同一个流程模型能够使用多种建模方式,哪种方式才是最有效的?这就需要考虑模型的用户是谁(业务人员、分析人员、开发人员),才能界定是否有效了。此外,工具毕竟只是工具,促进业务人员与开发人员之间的沟通,除了工具,还有公司文化、组织结构等等其他人的因素,这也才是最重要的因素。 

不管怎样,BPMN2.0是BPMN历史上最重要的一个版本,BPMN继续向正确的方向迈进了一大步。在下一节里,我们将一起看看BPMN所支持的三种基本类型的流程模型。 
posted @ 2011-08-23 23:24 ronghao 阅读(2195) | 评论 (2)编辑 收藏

这个春节周扬回家了,没有什么心理斗争,只是突然很想家,于是就回家了。想起要面对周实的尴尬,周扬骂了自己,去他妈的面子,想回就该回。在东莞的这一年周扬过得很充实,他了解了整个烤瓷牙和烤瓷冠桥的制作流程,甚至用到哪些设备,需要购买哪些材料,上哪儿购买,什么价格,他都知道的清清楚楚,他有个小本子,上面记满了各种信息,男人们发现他经常在写些东西都嘲笑他,我靠,还写日记呢。他自己做的烤瓷牙也不错,但是他清楚,这些细活还是女人更拿手。

周扬的心情在年前的最后几天被破坏了,事情起源于阿信,阿信说,马上要放假了,你也要回北京,唱歌去吧。周扬知道阿信的意思,他笑笑,说,你又憋不住了?于是,几个人一起去了一家新开的KTV,要了果盘和酒,男人们开始唱歌,一会儿,就该女人们进来,问,需要陪唱吗。自然是需要的,阿信在沙发的角落里搂了个女人坐在他的腿上两个人说些暧昧的话,周扬在沙发的这边正在点歌,点完歌就那么无意间一回头他却立刻惊呆了,他几乎不敢相信自己的眼睛,是她吗,是那个三年前为自己煨莲藕排骨的女人吗,是那个为了她不惜打断人肋骨的女人吗。包间里的灯光有些昏暗,周扬看不清女人的脸,只是随着电视屏幕的闪烁,女人的脸时隐时现,但他能肯定就是她,她的两只手已经伸到阿信的衣服里,阿信的手也已经伸到她的衣服里,两个人在那里互相摸着什么。很奇怪,周扬竟然坐在那里一动没有动,这不是平时的他自己,作为一个男人现在应该这么做呢,像以前一样吗,提个酒瓶过去,一酒瓶砸在阿信的头上,然后像老鹰抓小鸡一样把女人给提到KTV外边的大街上,然后大声的质问她:你他妈那些天去了哪儿,我找了你整整一个月,你他妈倒是说话啊,你现在哑巴了啊!然后女人开始可怜的哭泣,是这样吗?沙发开始随着他们的节奏晃动起来,若有若无的呻吟声在耳边响起,一个男人在旁边声嘶力竭的吼着:天不下雨天不刮风天上出太阳,妹不开口妹不说话妹心怎么想,最初的气愤随着沙发有规律的节奏开始肿胀,半空的啤酒瓶就在手边躺着,一手就能抄起来,终于,周扬站起来,他到外边去吸支烟。

南方的冬天很潮湿很冷,街上没有几个人,周扬的情绪慢慢冷却下来。一个外来的女人,那样的情况,除了害怕和躲避,你让她怎么做呢?每个人都有自己的生活,其他人那怕是父母都只是自己生命中的过客而已,每个人都是在为自己活,那些宣称为别人活的人不过都是些骗子。

一会儿,阿信从心满意足的KTV里出来,他一拍周扬的肩,说,每次都这样,你老婆在北京,怕个屁。周扬没有说话,阿信打个酒嗝,又说,这女人真不赖,屁股大,够劲。

在回家的火车上,周扬对面坐着一对回家的小夫妻,和所有从农村出来打工的年轻人一样,他们早早就在家结了婚然后再一起出来打工。两个人看起来很兴奋,应该是第一年出来打工,不停的说话和看窗外,手里的手机声音特别大,带了好几个包的东西,里面塞满了便宜的衣服、花哨的食品和街头小摊的玩具,他们要将这些劣质的东西带给他们的亲戚,同时迎来期望中羡慕的眼光。和他们相比,周扬只带了一个随身的包,他讨厌负担。

男人问周扬,你为什么出来打工?

为什么?周扬问自己,张雨也对他问过同样的问题,张雨说她讨厌农村,她还有两个弟弟,在她初中还没毕业时,父亲已经迫不及待的要她工作,然后挣钱供弟弟上学,后来在她强烈的反对下勉强读了中专,父亲却又开始给她张罗对象,在她们那里,彩礼是一份巨大的数额。我讨厌农村,张雨说,我喜欢城市,这里你想干什么就能干什么。

和张雨一样,周扬最初出来也是为了躲避,他讨厌周实的懦弱,也厌恶李秀的凶悍。从小到大,印象里似乎总是李秀在抱怨在责骂,而周实则躺在他封闭的精神空间里沉默不语甚至挂着胜利的微笑。周实高中毕业后来还自修了大专,他喜欢看书看小说,几个姐姐都在城里事业单位上班,母亲当过党委书记,李秀则是勉强初中毕业,家里是地地道道的农民,这两个人怎么就结了婚呢。周扬想起来,在他很小的时候,李秀在家门口的门框上悬挂了一面镜子,说是算过的辟邪用,几个姑妈来串门的时候都笑起来,李秀在厨房的时候,她们说,就是农村的啊,这么迷信。这话传到了李秀的耳朵,姑妈们走后,她立刻落下了脸,说,周实,收桌子!这几乎形成了一个恶性循环,姑妈们瞧不起农村女人李秀,农村女人就对周实没有好脸色,而周实这个老实人受欺负就越让姑妈们对农村女人不满,于是在这个循环中,李秀的脾气就越来越暴躁起来,她把自己的不幸都怪罪到周实家庭的头上,她从小就告诉周扬:奶奶从来都没有照顾过他都是外婆照顾的,姑妈家都很有钱但是不实在,城里人就是虚伪漂亮话说一大堆没有农村人淳朴,周实从来就不管家里的事什么事都要自己操心。她没有想到的是,她说得越多,周扬反而越反感起来,周实喜欢看小说怎么了,看小说就是不务正业不管家里事了吗。而周实每次的沉默也让周扬的不满日益增长,周实有一张喜欢的字画,上面写道:知足常乐。

但这就是生活,农村妇女李秀和城市好脾气男人周实他们结婚了,不仅结婚了,他们还生了周扬。尽管在其他几乎所有事情上存在分歧,但在周扬这件事情上,他们达成了一致,这个一致就是不能离婚。这不是谁的错,错的是婚姻,他们就不该结婚,门当户对在什么时候都是婚姻的第一基石,灰姑娘和青蛙王子的故事只会在童话中出现,即使出现,也只是交代到结婚为止,至于婚后幸不幸福,鬼知道。

为什么出来打工?周扬继续问自己,继续躲避吗?不是!他清楚现在家里最大的问题是钱,什么地方都缺钱,爷爷病了需要用钱,家里开支需要用钱,周实干活不行,李秀年纪大了,所有农活都是她干逐渐吃不消,所以第一步就是挣钱,他想周实有时间能够继续看些书,那是他年轻时的爱好,至于李秀,姑妈们都年纪大了,人一老许多年轻时放不下的事情就能放下了,多走动一下,至于自己呢,则是回家,对于北京,周扬没有好感,他被查过好几次暂住证,几乎一开什么会暂住证就查得勤起来,街边多了许多带着红袖头的老头老太他们拿着不许乱来的眼光检视着周扬们,而北京似乎一年四季都在开会,堪称会都。那么最现实的方法就是,回家开个牙齿厂!

家里的周扬晚上熬夜,早晨睡懒觉,爬起来冲两袋方便面吃完后碗都不洗搭上车就去城里。他首先去了市人民医院,几乎没费多大工夫也就是几根烟的事他就和医生们混熟了,他们说话很直接,在这一点上家里的医生显然要比北京医生淳朴。几天下来周扬发现这里的人们已经开始有牙齿保健的习惯。接下来的几天,周扬像一只高速旋转的陀螺一旦停下来就失去了平衡,他开始有意无意地发脾气,瞧这也不痛快瞧那也不痛快,经常把家里的东西碰得哐哐响。作为一个男人他总觉得什么地方空荡荡的。他突然想起来了,张雨叮嘱过到家要给她打电话的,原来是这个。搞清楚原因后的周扬为自己的情绪感到很好笑,他就给张雨挂了电话。周扬说,为什么不跟我电话?

张雨说,我一直在等你电话。

周扬说,要是我不跟你打电话呢?

张雨说,我就一直等下去等到死!

周扬的心里一震,泛上很多复杂的情感,空空的、甜甜的、酸酸的,周扬的喉咙就有点哽咽,说,不是我不想给你打电话,这段时间太忙。

张雨幽幽地说,我知道。

周扬突然想到了什么,说,你还是在北京么?

张雨说,是啊。

周扬的心里就浮现出张雨一个人孤孤单单的景象。一个女孩子,工厂里都放假了,没人上班,一个人,该是多么寂寞和害怕啊。周扬的话里就有了许多关爱的成分在里面。一个人在那儿要小心啊。

张雨说,恩,我知道了。好吧,你很忙,就这样吧,知道你很好我就放心了。

听完这话周扬突然说,等等。

张雨说,恩?

周扬慌忙掩饰自己说,没事,以后我再跟你打电话。

张雨叹了口气说,好吧。

周扬其实还想和张雨多聊一会儿,东莞的生活使他的历史掀开了新的一页。在无数个日日夜夜里,在厂子旁边的马路上,人们都会看见一个幸福的年轻人愉快地在和另外一个她煲着电话粥,手机真好,按下一组号码,那个她就会送到你的耳边。周扬的天空不再单调,他不再是漂浮在这座城市的一棵浮萍,他是有根的,他是有牵连的。有时候,在冬天的被窝里,周扬还能收到张雨的短信,很是温暖。

和周扬相比,张小庆则在发短信的问题上犹豫了,每到春节,他都会群发短信,但是这次,他卡在了一个名字上,这个名字是王碧薇。一年没有联系了,也不知道她现在怎么样,最后,张小庆还是下了决心把短信送了出去。很快,他收到了回信,还是老样子,王碧薇说,啊哈,竟然还有一个人想起我啊,难得难得。

张小庆说,现在怎么样?还在亦庄?

王碧薇说,是啊,当项目经理了,忙死了。

张小庆想问她男朋友的情况,想了想,没问,说,有时间见见面吧。

王碧薇说,好!

两个人都知道有时间表示的是没时间,于是就不再互发短信。同样是祝福,在另外一件打电话的事情上,张小庆则和妈妈发生了分歧,妈妈让张小庆给常阿姨打个电话,张小庆不愿意,在他的意思里,对常阿姨就永远不要再联系,妈妈却说要保持联系。妈妈的腿恢复的差强人意,年前拍过片,骨头还没有长合。

年就这样过去,两个男人,要重新出发。对张小庆来说,他想融入北京这座城市,而融入城市的第一步就是买房;对周扬来说,北京只是他的暂时之处,他是要回家的。于是,怀着不同的目标,两个人都去了相同的城市-北京。

posted @ 2011-08-22 00:02 ronghao 阅读(1884) | 评论 (1)编辑 收藏

妈妈晚上搬货时从常阿姨美容馆的梯子上摔下来了,右腿关节粉碎性骨折。那梯子张小庆见过,为了放东西,常阿姨在库房里隔出两层,中间架了个木质的梯子,没有扶手。

没有看到常阿姨,妈妈躺在病床上,整个右腿肿起老高,上面敷满了冰袋,一个店里的小姑娘坐在旁边,看到张小庆来,她站起来,松一口气,说你可来了常姐还要上班就先走了。小姑娘接着说,我也要回店里了,你先看着你妈妈,常姐请了陪护晚上会过来。

之前在路上张小庆多少是愤怒的,他埋怨妈妈不该为了一点钱来北京打工,但在看到妈妈肿胀的右腿之后,一切情绪都云消雾散了,他把凳子从床脚搬到床边,靠近妈妈坐下,轻轻的问,疼吗?

妈妈说,刚摔下来的时候很疼,现在已经不疼了。

主治医生是个和蔼的中年人,张小庆问他,能治好吗?

医生说,不能。

医生打了一个比方,说关节粉碎性骨折就像一只碗掉在地上打碎了,我们只能尽量修补,但是恢复到原来的样子是不可能的,妈妈的骨折非常严重,到处都是骨头的碎片,以后能够恢复到靠拐杖走路已经是最好的结果,并且随着年纪的增长,修补的关节会重新变得糟糕并逐渐失去功能。

医生的话让张小庆心情非常低落,末了,医生问,你妈妈有医保吗?张小庆说,没有。不仅没有医保,因为是过来帮忙的缘故,劳动合同都没有。医生叹口气,说,我知道了。

晚上的时候,常阿姨过来了,她买了很多的水果,说,姐姐,感觉好点了没有?

妈妈说,妹妹,好多了。

常阿姨再三叮嘱张小庆说,先不要告诉家里人,别让他们担心。

手术定在三天后,张小庆给公司请了假,王总、张总和金鹏都过来看了妈妈,临走的时候,他们给张小庆塞了一千块钱,说这是公司的补助,让张小庆在这里好好照顾妈妈,请假的这些天公司也不会扣工资。王晓丽一下班就坐公交车过来和张小庆一起陪着妈妈,手术那天她也请了假。手术的日子定了,但是在另外一件事情上张小庆却和常阿姨发生了矛盾,医生告诉张小庆手术中植入的关节有两种选择,一种是国产的,1万块钱左右,一种是进口的,3万块钱左右,两者相差了2万块钱。

医生说,我知道你们没有医保,在这边打工不容易,但从我个人的角度,还是建议你们使用进口的关节,第一是它的使用年限要比国产的长很多,第二是不必再动一次手术将它取出来,你妈妈年纪大了,最好避免第二次手术。

张小庆相信医生的话,他认为应该用进口关节,但在使用国产关节还是进口关节的问题上,常阿姨却没有直接表态,她和张小庆先说起了自己,说现在钱并不好挣,丈夫一直没有正经工作,今天到那里打工明天到这里打工,却都干不长,儿子在上大学正是花钱的时候,家里完全靠自己的一点工资,好不容易和别人一起开了一家美容馆,房子还贷着款,生意却并不好。说到这里,常阿姨的眼圈红了,说,你知道吗,有时候我真想和你常叔离婚,但一想到儿子就又下不了决心。张小庆不是傻子,他立刻明白了常阿姨的意思,到目前为止所有的医疗费都是她付的,她不想多花钱。

常阿姨说,其实我们有时候也不能过分崇拜洋货。

常阿姨说,其实你妈妈这次摔这么重很大一部分原因是因为她自己缺钙。

病房走廊上有一盏灯坏了,一会亮一会灭,常阿姨的脸就在那里忽明忽暗,让张小庆看不清楚,这是那个自己熟悉的常阿姨吗?是那个和妈妈以姐妹相称的常阿姨吗?

周扬知道这件事情了,他拖张丽带过500块钱来,给张小庆打了电话,他说,你妈妈是属于工伤,不仅医药费要你常阿姨全部出,她还要进行工伤赔偿,这点她是公务员应该很清楚。

张小庆说,可是并没有劳动合同。

周扬说,那就更是你常阿姨的错了,你去劳动部门,一告一个准。

张小庆说,可是我们和常阿姨的关系很好,这样就没办法维护关系了。

周扬说,操!你竟然现在还想着维护关系,你们现在对她来说就是包袱,想甩都来不及呢。

张小庆说,我就想她能帮我们用进口关节,其他费用我们都不要。

周扬不耐烦了,说,你废话真多,把你那个所谓常阿姨的电话给我,我给她打电话!

妈妈的意思和张小庆类似,毕竟以前常阿姨对我们不错,不想因为这件事情把关系搞坏了,她家里有困难也应该体谅。于是,张小庆去找公司预支了一个月工资,从王晓丽那里拿了1万块钱,一起凑了2万多块钱,其实张小庆还是抱有希望的,他想常阿姨也应该能够体谅到自己的难处,他向常阿姨表达了想用进口关节的意愿,常阿姨也没有直接拒绝,但手术前一天和当天常阿姨都没有出现,替她来的是她的丈夫常叔,于是张小庆知道一切都不可能了,他去住院部交了钱,满心难过,周扬说的很对,自己现在对常阿姨来说就是包袱。

手术完成后的第7天,张小庆的爸爸和大舅从家里来了,他们背了一块门板过来,把妈妈从海淀医院抬到北京西站,然后再抬上火车回家,临走时常阿姨给了1千块钱说是一个月的工资补助。从火车站送完妈妈回来,走在西站宽大的广场里,太阳明晃晃的照着人眼睛疼,张小庆突然就一屁股坐在广场上,嚎啕大哭起来,他是那么的旁若无人,压抑已久的眼泪顺着脸颊直往下淌,周围的人都用一种诧异的眼光看着这个不知所措的年轻人,他就那么坐在那里哭了大概有半个多小时,泣不成声,直到一个警察走过来问发生了什么事情。

妈妈回家后,张小庆情绪低落了很长一段时间,他觉得自己好像看明白了一些事情。另外,尽管他和王晓丽都不说,但他们都知道在明年攒齐10万块钱是一件不可能的事情了,在向王晓丽借钱这件事上,张小庆是抱有感激之情的,所以他对不能实现王晓丽的愿望感到更加难过。

时间就这样一天天滑向年底,买房的希望也似乎在一天天渺茫,最初他是抱有希望的,一方面是期望政府能够控制房价,另一方面则是憧憬着公司新产品能够大卖,但是现在,这两个方面都让他失望。政府在西三旗面粉厂建设了限价房,尽管自己不是北京户口并没有资格购买限价房,但价格的限定应该对周边的商品房产生影响,事实是产生了影响,只不过这个影响进一步推动了房价,在周围的新房都是六千左右,二手房都是五千左右时,这个限价房的价格却是七千,于是,在政府宣布这个价格的第二天,周围所有的房价都涨了。工作方面,所有人都知道,新产品在六个月内注定是完不成了,张小庆他们已经很努力了,金鹏、孙伟和他自己几乎每个星期六都在公司加班,困难来自于两方面,一方面是开发完成的功能似乎总是离实际的需求差那么一点点,反复修改,影响了新功能的开发;另一方面是金鹏新招了一个测试,bug被源源不断的发现出来,一个功能是在列表页面弹出编辑框,在列表达到20条数据页面需要滚动时,编辑框没有跟着滚动,造成最后几条数据用户看不见编辑框而不知所措,他们都没有发现这个bug,但新来的测试几乎是立刻发现。新产品发布日期的推后影响了公司的销售,张总已经谈好的好几个单子不得不面临取消的窘境,此时的张总,虽然有意见,但公司已经在新产品上投入了18个人月,没有人有勇气提出终止开发,另外,此时停止开发也并不是明智的选择。尽管如此,公司在第一年还是实现了小小的盈利,盈利来自于原先的老客户和遗留项目。

年底的一天,金鹏找了张小庆私下聊了聊天,金鹏对他一年的表现表示了肯定,认为他勤奋、好学、进步很快,公司很重视他。最后,很自然,说到工资,金鹏说,公司的情况你很清楚,新产品一直没有发布,公司压力很大,因为这块的开发只有投入没有产出,所以,工资这块我们调整的幅度不大,希望你能理解。

张小庆说,我理解。因为妈妈住院这件事,张小庆对公司是非常感激的,他觉得公司有难处,自己作为公司的一份子是应该主动分担的。

和张小庆聊完天,金鹏将他的工资从5500涨到了6000,另外考虑到避税,他告诉张小庆说让他再拿一张身份证来,这样每张身份证发3000块钱,拿到手里的能够多一些。孙伟同样涨了工资,从6000涨到6500。

政府对限制房价的暧昧态度,公司新产品的进度延迟,一方面是房价继续上涨,另一方面是工资增长缓慢,张小庆最初的两个期望都遇到了麻烦,但是他再次坚强的找到了希望:易宪容教授说了,奥运开始前主办城市的房价都会上涨,但是奥运过后都会大跌。张小庆相信教授,他对王晓丽说,我觉得他说的很有道理,世界上很多举办过奥运的城市都是这样。

王晓丽哼了一声,说,可是这里是中国。

还有一个途径,那就是将目光放长远一些,到昌平去买房。对这个想法,王晓丽表示了认可,她们公司的首席设计师就住在昌平,每天开车从八达岭高速过来,没有车,919也方便,唯一担心的是堵车,但是和房子比,堵车似乎又只是一个小问题。

和张小庆比,王晓丽年前的几个月也过得不痛快,原因自然还是出在她的经理身上。到了冬天,装修的顾客减少,设计师们开始严重依赖经理的派单,设计师们分为三个级别:普通、资深和首席,公司根据设计师每个季度的产值来决定级别,级别越高底薪越高越容易推销给顾客,这次,王晓丽还差一个单就可以完成资深设计师的产值,但是经理就是不派单给她,于是,在年前的最后一天,王晓丽降级了,这是她进公司来第一次成为普通设计师。吃饭的时候,王晓丽开始生闷气,说,那个没人要的老女人。

张小庆说,我们还是给经理送些东西吧。

王晓丽没好气的说,没送过,不送。

张小庆说,过完年来,你就说是家乡特产,她是不会拒绝的。

王晓丽说,要送你送。

话虽这么说,但两个人都知道是应该给领导送些东西了,在送什么东西的问题上,两个人说来说去,最后都觉得送钱还是最有效的方法,但是两个人又都没有送过,于是如何送两个人又说了半天,最后,他们决定送购物卡。张小庆感叹,购物卡真是一个伟大的发明。

张小庆的06年就这样过去了,他将自己的QQ签名从“加油,买房!”改为了“奋斗,希望!”。06年的最后一天,他和孙伟、杨晓、付江一起观看了萨达姆被绞死的视频。他多少觉得有些残忍,这个在上大学时被自己捧为反美英雄的人,就这么屈辱的死了,当然,他该死,只是这个死法有些残忍。

孙伟推一推眼睛,说,自作自受,他在位时杀了多少人。

付江笑了一下,说,呵呵,不该得罪美国。

杨晓冷笑了一声,说,英雄死于独裁。

posted @ 2011-08-14 23:17 ronghao 阅读(2046) | 评论 (2)编辑 收藏

星期天的早上,张小庆和王晓丽一起去了附近的链家地产,他们询问了回龙观附近二手房的价格,如果是两居室那么一般在六千左右,超过100平米则便宜一些,在五千左右。他们计算了一下,一个80平米的房子,单价按六千计算,那么需要48万,二手房需要首付50%,那么就是24万,这个数字是他们不可承受的。张小庆现在的积蓄不到1万,他还要去买一台自己的计算机,王晓丽工作比张小庆早一年,但大部分钱都寄回了家里,她的积蓄有2万。两个人有些失望的往外走,销售丝毫没有注意到他们失落的神情,她热情的让王晓丽留下电话号码,说有更好的房子就给她打电话,张小庆觉得没有这个必要,但王晓丽想了想还是留下了手机号。门口,到处都是散发房产宣称彩页的销售们,他们把张小庆和王晓丽的手里塞得满满的,房子是房山和昌平的,单价很便宜,1500到3000之间,小产权房。张小庆知道政府正在打击小产权房。

二手房需要首付50%,于是去看看新房。附近有两处新盘,一处是西三旗的上奥中心,一处是富力地产的富力桃园。和所有的楼盘一样,地基还没有开始施工,富丽堂皇的销售中心已经强先屹立在那里。去上奥销售中心的时候,张小庆刚下班从旁边的菜市场买了几根黄瓜回来,他上身穿着那件20块钱的山寨耐克T恤,下面套着一条大裤衩,没穿袜子,一双沾着很多灰尘的凉鞋露出脚趾头,手上拎着一个塑料袋,葱绿的黄瓜从里面探出生涩的头来。大厅里人并不多,但张小庆和王晓丽在沙盘前看了很久也没有一个销售上来问他们。终于,张小庆去问了一个售楼小姐,说多少钱一平米。售楼小姐正在修理自己漂亮的指甲,她瞟一眼张小庆手里拎着的黄瓜们,将吝啬的目光重新收回到自己的指甲上,嘴爱张不张,一字一字的说,七-三-零-零。她说的并不是数字七千三,而是四个汉字,七-三-零-零四个字优雅的从嘴里蹦跳出来。王晓丽的脸刷的绿了,她大声的说,你们经理呢?你就这态度搞什么销售?!售楼小姐被吓了一跳,她还没吭声,王晓丽又大声说了一遍,你们经理呢?我要见你们经理!一个穿西服的中年男人往这边看了一下,走过来,售楼小姐这才明白事情的严重性,她收回自己的指甲,连忙小声说,对不起对不起刚才是我态度不好。

从销售中心出来,王晓丽心情很不好,说,不就是个售楼的吗,都是服务行业,牛什么?!

单价7300,户型50-100,70平米需要50万,首付20%,需要10万。张小庆一个月税后4600,扣掉房租550,生活费水电费买书的费用,一个月能剩下3000。王晓丽的工资并不固定,室内装修,基本工资800,主要靠项目提成,而由于在总部工作室,项目又主要靠部门经理的分配,她是个内向的人,不会搞关系,这让她很吃亏,大一点的项目经理从来不分给她,这造成她很辛苦,都是小单子,常常一天要跑好几个工地,而往往越没钱的客户越苛刻,这反而造成了她坚毅的性格,你不给单子,没关系,我自己争取,我做给你看,想要我给回扣给你,门都没有!平均下来,王晓丽一个月税后能有5000,能剩下3500。这样,乐观的估计,到明年年底的时候,两个人似乎能够凑到10万。如果房价不再上涨,那么就能在附近买到房。

对于房价,张小庆虽然不像去年那样觉得不是问题,但他觉得还是有希望的。他的希望来自于政府,政府正在打压房价,他相信政府。工作的间隙,他会看易宪容的博客,当时易宪容正和任志强在新浪博客上打得不可开交,易宪容的博客他基本上每篇都会看,而任志强的博客,他几乎都只是扫一眼标题。在他心目中,易宪容是那种典型的有良心的知识分子,而任志强,则是唯利是图资本家的典型代表。

吃午饭的时候,张小庆问了大家,说,你们会考虑在北京买房吗?

孙伟推一推眼镜,说,我不会,北京的房价太高了,我们穷人根本买不起。

杨晓说,那得看情况,听未来老婆的,这事我们做不了主。

付江是有产阶级,这个问题显然没有把他包括在内,他呵呵笑了一声,继续吃饭。

张小庆说,你们看易宪容和任志强的博客吗?你们觉得房价会降吗?

孙伟说,我觉得易宪容说的还是有道理的,再这样下去,经济会崩盘的,像日本和香港一样,所以,政府一定会管。

杨晓笑了一声,说,这是制度问题,房价上涨最大的受益者是政府,你觉得它会降低房价吗?至于易宪容,你们听过爱国贼这个词吗,我觉得他是在借着所谓大众代言人炒作自己。

这次付江可以插话了,他说,我觉得杨晓说的有理,房价撑死不涨但绝不会降,当然,他怕说他屁股决定脑袋,补充到,我的房子是自住的。

与付江的自住相比,金鹏则又买房了,这次是投资,新房子在亚运村,一居室,8千多一平,张小庆认为房价的主要推动者是房产商和投资客,这让他对金鹏的心情有一点点复杂。王总、张总、金鹏和刘宏都有自己的房子,稍微不同的是,王总、张总和金鹏他们买的都是经济适用房,他们花2-3万块钱从他人手里购买到经济适用房指标,然后再花3000一平的价格购买到房子,而刘宏则是商品房,刘宏家是农村的,老婆家出的首付,则也是他在家地位低下的原因之一。

除了政府,张小庆还把希望寄托在正在开发的产品上面,他希望新产品能够恢复王总嘴里科技动力曾经的荣耀,王总说等新产品发布时还要开个发布会,这些憧憬激励着张小庆,使这个经验不多的年轻人充满干劲,每天下班后都会加会班,每个周六都会来公司自愿加班。与原先乐观的六个月计划相比,张小庆他们遇到一些进度上的困难,困难并不是来自于技术上,他们几乎很快就熟悉了新的各种技术框架,困难来自于对要实现功能的理解,为什么要实现临时部门、岗位,它们和部门、角色又是一种什么样的关系,一个员工如果属于多个部门,他的权限又是一种什么样的关系,这些问题困扰着张小庆,除了问刘宏,他不得不翻看以前的产品代码,他惊讶的发现,那些原有代码真的写得很好,没有spring,没有ioc,也没有aop,但是看起来非常清晰,包、类与函数的命名,关键地方的注释,都非常易懂。他开始想,我们为什么要重写产品呢,仅仅是因为要采用新技术吗。

八月的一天,妈妈给张小庆打了电话,她要到北京来打工了,在一家美容馆,负责做饭和打杂,一个月包吃包住后是1000块,店主是常阿姨。常阿姨是公务员,是张小庆住院时认识的,当时和常爷爷一个病房,他女儿常阿姨下班后就来看他,妈妈在医院陪床,就这样认识了。后来张小庆出院,妈妈就在医院做了陪护,常爷爷来,她就负责照顾常爷爷,直到他去世,她和常阿姨也以姐妹相称。妈妈一直在北京打工,每个月都去学校看儿子,直到张小庆毕业,这才回家。每次到学校,张小庆是不情愿的,他不愿意他妈妈来,他觉得很丢人,都这么大的人了,还要家长在附近照顾。于是妈妈每次来,他都没有什么好脸色,但是每次妈妈走,他却黯然伤神,觉得对不起她。在这种矛盾的情绪中,张小庆读完了他的大学四年级。现在,妈妈又要来了,张小庆条件反射般的表示了反对,他说,我现在又不需要你们帮我花钱,你为什么还要来打工。妈妈说常阿姨给自己打了电话需要帮忙。想起常阿姨在住院时经常给自己带些东西和鼓励自己,张小庆一时也没了言语,不过他心里还是很不痛快的,觉得让妈妈过来打工说明自己很没有本事。

张小庆没有和王晓丽说妈妈来到了北京,每个月去医院检查身体的时候他就会顺道去中关村的美容馆看妈妈。生活似乎到此就恢复了平静,张小庆更新了QQ签名:加油,买房!他对政府、对公司、对自己是有信心的,他一方面期望着政府能够控制房价,另一方面则憧憬着公司新产品能够大卖,为此,他工作格外的努力。王晓丽则行进在另外一个空间里,她不相信房价会降,所以只有努力挣钱一条路,她不喜欢她的经理,依旧不愿搭理她,她给她爸爸打了电话,说谈朋友了要在北京买房不再有钱寄回家。每个月中的一天,张小庆和王晓丽会到西三旗的潇湘府打一顿牙祭,每次点一个特色菜和一个青菜,不会超过50块钱,在饭桌上他们会谈论起房子,他们相信是有希望的。

十月的一天,张小庆坐在电脑前,他和平常一样打开电脑,第一次却没有正常启动,这让他显得心神不宁,他隐隐约约觉得要有什么不好的事情发生。果然,刚写下几行代码,一个电话打断了他,打电话的是常阿姨。

常阿姨说,张小庆,我们在海淀医院,你来一下。

张小庆立刻紧张起来,他急急的说,怎么了,出什么事了?

常阿姨说,没什么事,你过来一下。

常阿姨越不肯说,张小庆越觉得问题严重起来,他连忙向金鹏请了假,挂了电话就向车站跑,站在站牌下,腿一阵发软,各种不好的想法占据了他的头脑,使他不能呼吸,妈妈被车撞了?被人抢劫了?肯定是受伤了,伤的重不重?越想越难受,眼泪就顺着眼眶流了下来。一会儿,他又想,活该,叫你不来非要来,出事了吧,为了该死的1000块钱。

车来了,张小庆第一次插了队,他第一个冲上了公交车,后面有个大妈对这个横冲直撞的年轻人很不满,说,现在年轻人真是一点礼貌都不懂。张小庆没有理会她,他甚至根本没有听到她在说什么,他走到后门,倚住一个扶杆,整个身体这才没有跌下去。远处,海淀医院越来越近。

posted @ 2011-08-07 16:07 ronghao 阅读(2253) | 评论 (5)编辑 收藏

尽管张小庆的爱情勃起了,但王晓丽却很快认为两个人不再适合交往,她给张小庆发了短信,然后关了手机。原因很简单,王晓丽比张小庆大两岁,她认为不合适。又是年龄!张小庆想起了王碧薇对自己的拒绝,同样是年龄,年龄这个问题此刻激起了张小庆一遍又一遍的愤恨,说什么爱情,原来都是有条件的!此时的张小庆,被爱情冲刷了头脑,他不断的给王晓丽发消息:我不在乎,真的不在乎,爱情是没有年龄限制的。王晓丽一条短信都没有回。于是,夏日的下午,张小庆给金鹏请了假,从上地坐上了到西三旗的运通105,接下来在西三旗换上了到德胜门的315,他要去德胜门接王晓丽下班。王晓丽显然没有料到张小庆会来到她的公司,高高瘦瘦的张小庆站在楼下的大厅里等她,让她始料不及,同事笑着对她说这是谁啊,她不知道该怎么回答,张小庆就那么站在那里,没有说话只是看着她,她突然就想起这个男人的身体,那么严重的病,一个人在北京,没有人关心,心里突然就涌起一股巨大的同情,这股同情淹没了她,使她可怜起这个男人来。两个人一起去公司对面的集天小吃吃了饭,一人一份石锅拌饭,吃饭的时候,两个人似乎都有些心不在焉,勺子有气无力的在锅里翻动,都在等待着对方说些什么。时间也似乎凝固了,最后,还是张小庆先说了话,他说我们可以试着先交往吧。王晓丽说好。

张小庆很快发现王晓丽是一个很内向的女人,她说话不多,高兴的时候话多一些,不高兴的时候就一句话没有,只是给高晨晨不停发短信。一天晚上,张小庆去找王晓丽,发现她又是一脸闷闷不乐,怎么和她说话就是不理。于是,张小庆有了经验,他给高晨晨发了短信问是怎么回事,高晨晨回短信说还不是家里的事,张小庆说什么事,高晨晨说她爸向她要钱。

王晓丽和高晨晨是同一个村子里的人,两个人一起长大。王晓丽的家境并不好,一家四口人,她还有一个弟弟王刚,书没有读出来,留在家里,之前完全靠她妈妈张芳一个人在农田里操劳,他爸爸王超什么都不干,也干不了,喜欢打牌。王超什么都不干是有原因的,之前八十年代,王晓丽一家还算是富裕的,王超在镇上砖瓦厂里当主任,王晓丽小的时候就没有喝过水,从来就是汽水,家里也从来没有断过肉,很早,他们就有了村里第一张的摩托车嘉陵,厂里不忙的时候,王超就会骑上嘉陵,大手一挥,发动机器,他要去流浪了,他去很远的城市,谁都不知道他去干什么,只知道他有很多外面的朋友,他和他的朋友在一起,那是他的生活。但是他的生活在八十年代末的一天突然终结了,砖瓦厂要变为私人承包,王超已经和厂里说好要自己承包了,没想到却遭到了张芳的剧烈反对,这个平时对自己百依百顺的农村传统女人,此刻却突然反对了,甚至不惜说要离婚。张芳的理由很简单,她怀上王刚了,王晓丽刚上小学,家里的活又太多了,她需要她的男人在家里帮她一段。女人的理由是很充足的,其实她心里明白,最大的原因并不是怀孕,而是她发现厂里的年轻女会计爱上王超了,尽管没有证据,但女人的直觉是很准的,他们如果继续在一起,就会有问题。于是,在张芳剧烈的反对下,机会就逝去了。当进入九十年代,周围的人们都开始陆续骑上雅马哈时,王超还骑着他的嘉陵一日一日的穿行在田间村头,那些城市似乎离他也越来越遥远,他的脾气也终于变得和旧嘉陵一样糟糕了。他恨张芳,不管农田里再忙,他也不去帮忙;他恨王刚,是他不适当的来临毁了自己的前途,王刚调皮时,他就狠狠的揍他,往死里揍;他恨自己,觉得混到这份田地没脸见人,一日一日的在麻将桌上挥霍时间。唯一例外的,是王晓丽,高中学习不好,就转读艺术,第一次高考没取,就借钱复读,填报的第一志愿有操作的空间,就跑去武汉招生办,一守就是好几天。现在,王晓丽工作了,挣钱了,该还了。

张小庆没有办法帮王晓丽,因为她家的情况太复杂了,他能做的就是在旁边陪着王晓丽默默的坐。

因为王晓丽的关系,张小庆和高晨晨他们的关系突然紧密起来。经常,张小庆会和王晓丽一起到高晨晨的宿舍吃饭,这天,高晨晨的男朋友何鑫也在,他们买房了,房子在回龙观,110平米,6千多一平,一共不到70万,首付30万,剩下40万10年还清,房子写得两个人的名字,还做了公证,高晨晨和何鑫分别占四成和六成。看到长长的十年还款清单,张小庆长大了嘴巴,每个月还款接近5千块,比自己一个月的工资还高。高晨晨很高兴,她亲自做了饭,四菜一汤,期间不断的给张小庆和王晓丽夹菜,来,多吃点。两个女人聊起来,都认为房价是不会降了,早买早安生,高晨晨和何鑫都是北京户口,他们账面上的工资也都不高主要靠奖金可以申请经济适用房,但他们不愿再等了。两个男人也聊起来,但似乎没有太多的共同语言,于是话题转到NBA,转到姚明。回去的时候,高晨晨一定要打车送张小庆他们,说何鑫他们单位可以报销。王晓丽说没事,我做315习惯了。

回来的315上,人不多,两个人都有座位。一直看着窗外的王晓丽突然回过头来说,我们也买房吧。

posted @ 2011-08-01 00:24 ronghao 阅读(1827) | 评论 (3)编辑 收藏

走在去ktv路上的时候,周扬接到了张雨的电话。

张雨说,我听说那边诱惑挺多的。张雨是个聪明的姑娘,她知道什么时候适可而止,剩下的话她需要周扬来接上。

周扬说,我知道。

自从上次周扬找何林要求学做牙齿后,让他没有想到的是,何林要他去东莞学,而且一去就是一年。何林说是那边缺人,但周扬清楚,他这是报复,报复自己临出门时说张雨是自己的女朋友。其实也没什么,对周扬这种人来说,哪里不都是一样,他们就是大海里众多微生物里的一种,哪里有钱,像光亮一样,他们就会凑上去。张雨每个星期都会给他打电话,问他这边的情况,问他吃的可好,过的可好。其实周扬明白,每次张雨都要仔细问他在这边每天过的情况,这才是重点,她对他并不放心。对于张雨,周扬有一种奇怪的感觉,尽管自己表现的不冷不热,但一旦有个星期这位老乡没给自己打电话,自己就会觉得心里少了点什么,但是,他又告诉自己,不能要一个太聪明太有主见的姑娘,这让他想起自己的母亲李秀。

过来东莞才知道,传说中的小江南确实不假,同时这里也有着大量的工厂,伴随着工厂的是无数和自己一样的年轻人。做牙齿并不简单,包括了烤瓷牙和烤瓷冠桥,烤瓷牙要简单一些但也包括了四种不同的瓷牙类型,烤瓷冠桥则操作过程更为复杂,需要更高的技术。何林让周扬学习的是烤瓷牙中的一种,只有跟随他干了很多年的人才可以学习做烤瓷冠桥,并且只有一个大师傅才精通整个制作流程,剩下的工人也只是了解其中某个环节的制作工艺,阿信就是这边的大师傅。周扬开始有事没事的和阿信搭话,他很快发现,阿信有找姑娘的爱好,尽管已经有两个小孩,大的已经上小学,但他却乐此不疲。一起出去喝酒吃饭自然是经常的了,自然是用不着阿信买单的了,但是周扬知道,这还不够,尽管熟识了,但一旦开始工作,阿信不允许任何人在他身旁。直到一天晚上,阿信突然给周扬打了电话让他一个人带点钱去接他,原来这边新开了一家洗浴城,阿信去结账时却发现价格比其他的地方要高出不少,他的钱不够了。付完钱去接阿信回来的时候,周扬突然感到很好笑,这个平时很威武的小个中年男人此刻萎缩在沙发里,头发杂乱,后边站着一个高大的黑衣保安,像一只老鹰把阿信笼罩在沙发上,看到周扬,他的眼睛突然亮了。

他妈的,什么破地方,水平一般价格还贵,阿信悻悻的说。周扬知道阿信结账时和洗浴城吵了起来。

末了,阿信突然说,千万不要和其他人说。

周扬说,我知道。

最后的障碍消失了,周扬开始有意进入阿信的操作间,每次都会聊些关于女人的话题。阿信刚开始还是警惕的,他会停下手中的活。周扬说,要是我聊天就把你的技术学会了,那这活也就没什么技术含量了。阿信就笑了,得意洋洋,说,我学了好几年呢。

和周扬的犹豫相比,张小庆则是恋爱了。六月的一天,高中同学高晨晨突然给他打了电话,说是在校友录上看到了他的联系方式,在北京有几个同学,大家要聚一聚。聚会地点在玉渊潭公园,五个人,三男两女,见面了。他们一起回忆起曾经的高中岁月,每个人都兴奋、大笑、激动起来。不时有人大声的说着,对对,没错,就是那样,哈哈。很快,他们说到了现在的工作,高晨晨在国家电网工作,正式带编制的员工,转了北京户口。张小庆说自己是程序员,现在在开发一个中间件。说这话的时候他很自豪,用的都是新技术,他强调说。高晨晨住在清河,张小庆在西三旗,两个人于是一起回家。在公交车上的时候,高晨晨突然说,张小庆,你谈朋友了没有?

张小庆说,没有。

高晨晨说,我给你介绍一个怎么样,我最好的朋友。

张小庆说,这。他犹豫了,他想起了自己的身体。

高晨晨看出了张小庆的犹豫,她说,先见个面,认识一下也没坏处。

处于爱情幸福中的女人就这样,她们特别喜欢做的一件事就是给别人介绍对象。高晨晨的男朋友在中石油。

于是,在香山,张小庆、高晨晨和王晓丽三个人见面了。老实说,王晓丽就是一个很普通的女孩,大脸盘,个子不高,也不怎么说话,在这边做装修设计的工作,王晓丽和高晨晨在同一个村子里长大。张小庆有些稍稍的失望,但是,想到自己的身体,他突然就释然了,就这样已经很不错了。两个人互留了手机号,然后每天晚上会发发短信,聊聊今天有没有什么特别的事情,王晓丽教张小庆做一些简单的菜,很平淡,似乎不会再有更进一步的发展。一天晚上,张小庆突然给王晓丽发了短信说自己身体有些问题。王晓丽说什么问题。张小庆说了实情,当前晚上王晓丽没再回消息,第二天却突然打了电话,说我查了电脑,不是恶性病,没有问题,要注意休息。张小庆突然就被感动了,在他二十几年的生命中重来没有一个女孩子出现,也不是没有机会,但一想到自己的身体,他就自卑了。这一刻,爱情突然就从他身上喷发了,他一遍又一遍的对自己说,我是爱她的,是的,我是爱她的!他迫不及待的想见到她,想对她说,做我女朋友吧。


【下一章7月30号发布】


附注:对不起,这是我写的最短的一章,其实是没有写完,原因很简单,写不下去了,这两天实在是太愤怒了,前所未有的愤怒。

posted @ 2011-07-25 00:27 ronghao 阅读(2046) | 评论 (4)编辑 收藏

 

金鹏计划重新开发新的业务平台产品,这个想法遭到了张总的反对。张总说,小公司开发产品就是找死,公司哪有那么多的资金。 

金鹏哼了一声,说,看看现在做的几个项目,哪个不是边界失控,反复折腾?看着吧,项目没做完公司就被耗死了。

王总坐在一旁,两边都不得罪,说,金鹏说得有道理,我们要做产品型公司,但是现在资金也确实紧张,这样,我们再商量商量,哈哈。 

对项目需求的反复折腾,张小庆有很深的体会,军方项目告一段落后,他和惠软科技的一个项目经理一起去了趟广东惠州进行需求调研,那是一个县政府的电子政务项目。公司的项目逐渐多起来,张小庆观察了一下,基本都是与其他软件公司的合作项目,公司太小,签不了单,都必须与其他大的系统集成商合作,由他们出面签单,然后再外包到科技动力,甚至,有个电信的单子,外包了三层,最后才到公司这里,最初的金额有两百万,到科技动力这里只有二十万。刘宏笑着说,就是这样,大公司吃肉,我们喝汤。 

第一次和县长、一大堆的各部门领导们坐在一起,第一次被人称为张经理和专家,张小庆感到有些口干,他不停的喝水。县长出现的时间很短,所有人来齐,他说了句县委高度重视这件事情要支持信息化办公室他们的工作就匆匆走人,很快,各部门的领导也陆续走人,留下各自的代表。这个项目是由信息化办公室的新科长牵头的,刚到县里,他就一个人请张小庆他们吃了饭,反复强调这个项目的重要性,末了,他突然插了句不相关的话,说,这里飞车党很多,出县委大院后一定不要挎包,要注意安全。演示系统时,每到一个模块,都有人打断,说,不是这样的,我们实际的工作不是这样的。会议变得冗长而嘈杂,张小庆想起刘宏教他的话,咽下一大口水,说,我们在北京几个部委都实施过类似的项目,部委领导都是高度肯定的,我们不是现实工作的照搬,而是规范化。这句话收到了一定的效果,现场安静了一会,科长出来打圆场,说,是啊,他们在北京实施过很多大项目,我们要相信他们。 

项目启动大会预示了接下来工作的不顺利,接下来的一周里,张小庆每天都要忙碌到晚上12点,白天和客户交流,中午和晚上全部在写需求文档。各种各样的情况很多:我们的领导不会打字,你们要想想办法;流程已经到下一个任务,上一个任务的办理人员可不可以强行把流程回退,到处都是人为干扰流程。更糟糕的是即使在同一个部门,每个工作人员理解的流程都不一样,最后不得不依靠部门领导签字才最终确定下来。如何引导客户,这对写代码的张小庆来说是个新问题。而客户对文档的要求到了字字必究的地步,每一个按钮每一个链接都要作出详尽的描述。流程的描述更是繁琐到要面面俱到,每一步都由谁处理,哪些人可以看见,哪些人可以编辑,哪些人能够取消都要写清楚。在看一段文字的时候,科长皱了皱眉毛,很认真的对张小庆说,县委一定要在县政府的前面,这是政治问题,一定要注意,剩下的地方你再过一遍一定都改了。 

需求迟迟定不下来,张小庆以为五一前回不了北京了,没想到峰回路转,五一前一天所有的领导几乎同时把字签了。同来的项目经理说,他们也想过五一,比我们急。但张小庆并不乐观,他想项目如果实际开发,那又是一种什么样的场景。这个开发交给了刘宏,于是,又看到刘宏在那里不停的接电话,好的,好的,把这块再改一下,知道了,啪。好的,好的,把那块再改一下,知道了,啪。张小庆问刘宏,说,每天这么多电话你烦不烦? 

刘宏笑嘻嘻的说,烦,怎么不烦,人家是客户嘛,客户是上帝嘛,所以要开发产品嘛。 

项目边界不容易控制,需求反复折腾,周期长,本来是赚钱的到最后都耗到不赚钱。产品边界清晰,周期短,卖出去后培训几天就可以,比如说空中网。张小庆也是支持开发产品的。但是,为什么要重新写新的产品?现有产品升级不可以?金鹏说不行,原有产品掺杂了太多的业务逻辑,这些逻辑分散的到处都是,很难维护,就拿工作流来说,在业务平台代码里几乎重新实现了一遍跳转逻辑,一个提交页面多达1万行的代码,整个公司只有他一个人看的懂,此外,整个数据持久层全部是手写的,为了支持多个数据库,不得不经常更新代码,而如果使用第三方的ORM工具,这部分的工作将会全部释放出来。一句话,原有的平台产品腐化了,维护困难,而新技术的采用将会大大减少平台开发的工作量。金鹏提到的新技术让张小庆兴奋,新的版本控制工具、构建工具、jira加wiki、代码风格的自动检查、单元测试的引入、MVC框架、IOC框架、ORM框架、经典的三层模型、AJAX。这些都是张小庆了解过并向往实践的。金鹏开出一个长长的书单,张小庆的时间再次被充满了。 

张总的态度依旧强硬,他说,现在项目越来越多,本来就缺人,哪来的人去开发产品? 

金鹏说,项目越来越多,你敢接吗,给你人你敢接吗,接的越多死的越快。再说你们搞的那些项目,那个不是就挣点苦力钱,上次那个军方项目,我把话放在这里,绝对不会再有后续项目。 

为什么项目接的越多死的越快,张小庆不明白,他去问了刘宏。刘宏说,因为这些项目都集中在年底回款,我们接的越多,你看要招人,要培训,这都需要钱,如果中间出现什么问题,我们现金流就断了,就解散啦。刘宏笑嘻嘻的说,你的工资都是按月发,我们都是推后一个半月发,目的就是维持公司的现金流。 

公司项目需要人,于是孙伟来了,孙伟的个子不高,瘦瘦的,戴一副眼睛,说话的时候经常习惯性的向上推一推镜框,说话细声细气;杨晓来了,高高的,引人注目的是他的肚子,啤酒肚,说话声音洪亮,笑起来大声。给他们培训的时候,张小庆问了刘宏怎么招了这两个反差这么大的男人,因为他们互补嘛,好吧,刘宏笑嘻嘻的说。为什么招个啤酒肚的中年人?张小庆继续问。这次刘宏大声笑起来,说,他听到你的问题后会跳楼自杀的,好吧,人家是80后。

后来张小庆、付江、孙伟和杨晓四个人出去吃饭时,杨晓说到了自己的啤酒肚,知道吗,我以前是体制内的。张小庆抬起头来看杨晓,孙伟推了推眼镜,付江喝了一口啤酒,是吗?然后笑一笑,说,呵呵。杨晓开始讲他的故事,大学毕业后先去的是市信息化办公室。知道吗,技术没搞什么,光喝酒了,上午喝完了,下午接着喝,总有人请你喝酒,去下面县里检查工作,那都是各级领导陪着,一次,一个主要领导没来,我发了脾气,结果晚上专门赶过来赔不是,哈哈。 

孙伟推一推眼镜,说,那为什么不干了呢? 

杨晓哈哈大笑,说,围城。在那边基本上喝废了,过来还上了个培训班,哈哈。 

付江继续喝一口啤酒,天气热了,每天中午吃饭,他都会给自己小资的来一杯扎啤,他关心的说,那得喝多少酒才能达到你的级别啊,呵呵,你可真够腐*败的。 

杨晓大声的说,这不是人的问题,是体制的问题。你看现在都在说高房价,都在骂开发商,其实开发商有什么错,根本原因就是政府的土地政策,是体制的问题,一切问题都是体制的问题。 

孙伟推一推眼镜,细声细气的说,我们就是一群屁民,做什么都是没有用的。 

杨晓说,就是要早买房,房价还会涨,付江,你现在就赚到了吧。 

听到有人叫他,付江从扎啤中拉回思绪,说,什么?一会回味过来,呵呵笑一笑,说,喔,我是自住的。 

金鹏在与张总的斗争中取得胜利,刘宏的意见起了决定性的作用,他赞成新产品的开发,他一个人同时负责着好几个项目的开发,他有一个台式机和一个笔记本,两台机器上运行着两个截然不同的项目,哪个项目催着急了,他就却换到哪台机器工作。王总看到刘宏赞成开发产品,就顺水推了舟,说,那先就这样吧,开发新产品。 

张小庆和孙伟进了产品部,杨晓在项目部。刘宏笑嘻嘻的对张小庆说,跟着金鹏好好干,早日把我解脱出来。新产品取名叫做TechFocus,意思是我们负责关注技术,客户只需要关注业务,预计六个月后11月份发布第一个版本。金鹏说,让我们从一个测试开始我们的代码。

 

posted @ 2011-07-17 16:13 ronghao 阅读(1816) | 评论 (5)编辑 收藏

过完年周扬竟然收到了周实来的一封信,大意是这样的:家里一切都好勿念,有空就给家里打个电话或写写信吧。周扬看完信一言不发地把信用打火机给点燃了。家里的情况周扬其实很清楚,周实的信让周扬感到厌烦。爷爷生病住院了,做儿子的自然要担大头,周实年龄越来越大,零活不太好找了,上次去一个工地跳砖还把脚趾头给砸断了,这些周扬都是通过周实他姐知道的,每次一打电话她们就在抱怨你也不问一问,都是我们出的钱,物价又涨了,现在的日子越来越难过了。每当这时周扬就很气愤,他是气愤周实为什么不对他实话实说。这个有隐瞒的必要吗?这到底是个什么意思?!我他妈又不是小孩?!


周扬烧信的时候张雨刚好从门外进来。她抽抽鼻子说,什么东西烧着了?你怎么把信给烧了?周扬没有回答诧异的张雨。火苗慢慢的小下去,信纸一寸寸地弯曲、收缩、变形,最后化成灰烬落在地板上。房间里弥漫开燃烧的气息。


张雨把头发捋到耳后,露出狐狸般的眼睛。她本身就是一尾矫捷的狐狸,身段娇小,漂亮灵动。同屋的女伴问她:“张雨、张雨,你要去那儿?”


张雨眨眨妩媚的狐眼,说:“我去看电视。”


女伴说:“不对,不对,你去看周扬吧。”


张雨说:“瞎说。”回头对着镜子把最后一个发卡卡好。“你要再说我就不理你了。”


卡好发卡的张雨仪态大方地向周扬房间走去,她的脚蹼很高,脚面很柔软,走起路来像狐狸一样轻盈。张雨说:“周扬,周扬,明天出去帮我带点东西。”


周扬说:“就你多事。”


张雨说:“那有什么办法,谁让你是我老乡呢?”


周扬知道她是故意的,周扬就说:“好吧,好吧。”


于是张雨就从口袋里掏出早已准备好的纸条,上面写满了要买的各种东西:可怜可俐、飘柔、北京果脯……上面甚至具体表明了要买的地方。周扬不得不暗暗佩服女孩子的精细,周扬说:“这么多啊!”


张雨脸上就闪显出狡颉的笑容:“又不是我一个人的,有报酬的。”张雨故意把最后两个字咬得很重。


张雨走后屋子里的男人们就放肆起来,他们说:周扬你小子有种啊。唉,好比都让狗日了!周扬你他妈要请客啊。有报酬有什么报酬啊?


周扬说:“去你妈的!”第二天周扬的活果然就很少。周扬买完东西一看表,时间还早,就去了邮局,犹豫了一下,他想等父亲开口,但是他很快就放弃了这个想法,他给周实汇了两千块钱。


张雨是厂里过完年新来的会计,洪湖人,周扬的老乡。张雨刚来厂里不久,男人们都开始交头接耳:我日,新来个会计,真他妈漂亮,哎,周扬,她不是你老乡吗?!老乡老乡,背后一枪。周扬说,关我屁事。说这话的时候他将被子叠好,整理他的头发。自从何林叫他陪酒后,他就从地下室搬到厂里的集体宿舍,这样何林叫着方便。男人们继续说,周扬你再不下手就让何林给操了。周扬说,那是她自己的事,她如果自己愿意谁都阻止不了。


张雨却让这件事变成了周扬的事,她说,周扬,这次你得帮你的老乡。


周扬说,什么事?


张雨说,老板找了我,我不同意,我说我们在谈朋友。


张雨到工厂没几天就注意到周扬,他是那样地与众不同:他不爱说话,脸上似乎永远保持着一种沉默的神态。他沉默地穿行在工厂长长的走廊里,沉默地和人打招呼,沉默地行进在他自己的空间里。沉默得让人厚重。刘丽惊讶地发现他竟然还叠被子!天!还有人叠被子!这一切都让张雨新鲜。新鲜的张雨就对她的老乡生出好感来。这是一件多么不可思异的事啊,然而这就是事实,感情其实就是一种发现。老实说周扬长得并不坏,他的个儿很高,脸上的线条很坚硬。经常有女孩子躲在门后偷偷地看他然后相互发出不可言传的笑声。


周扬没有说行也没有说不行,他也有事情刚好要找何林,他不想再喝酒了。何林眯起眼来看对面的周扬,他想起自己第一次见他的时候,那是在八里庄。由于外来人员的增多,八里庄一带已经形成了一个简单的劳工市场。周扬认真地看各式各样的招工广告,报纸上的,贴电线杆上的还有挂店铺门口的。周扬失望地发现没有一项工作是适合他的,这些活都是一致的简单粗糙象打扫卫生送水送电器一类要么就是保安,而他,讨厌保安。这时他碰到了何林。衣着普通的何林站在人群中观察周扬已经有很长时间了,在他眼里这个陌生的而又急着找工作的外地人正是他想要的。他们年轻、情绪激动、有冲劲,还有一种说不出来的感觉,总之让人一看就喜欢。于是何林主动走上前跟周扬搭话:找工作?


周扬回过头来看何林,他发现自己并不认识他但他说,是。


看到周扬眼睛的第一眼印证了何林最初的判断:这是一个迫切想改变自己命运的年轻人。他说,想不想跟我干?


周扬反问,为什么?


别紧张,何林敏锐地捕捉到了周扬的犹豫和不信任,他从口袋里掏出一包烟弹出一根递给周扬:我不是人贩子,是我也不会贩男人。说完这话何林笑了笑,两个人间窘迫的气氛就变得轻松起来。我要一个业务员,送货,包吃包住一个月七百。


作为老板何林给了周扬极好的印象,他甚至表现出了和他身份不符的热情,这让周扬感到很惊讶。三天后周扬又像几个月前他刚到北京那样背着他全部的家当来到了何林的牙齿工厂。何林的工厂坐落在离三环不远的一片居民区里,城市的扩展早把这片原属市郊的地方变成了一番车龙水马的景象,美中不足的是这里的居民楼还是六七十年代旧房改造留下来的。何林的工厂就隐藏在这群楼的某一栋里,他租用了整整一层。工厂在五楼,到工厂要穿过一条长长的走廊然后还要爬上窄窄的五层楼梯。楼里的原住户早搬出去了,各个房间的门都紧紧的闭着,上面贴着字条:本房出租,只有走廊尽头的厕所门大开着,年久失修,臭气冲天。周扬沿着楼梯拾级而上,扶手早已锈迹斑斑,每踏一步就能从地面上激起厚厚的一层尘土。走到四楼的时候几滴水滴下来准确地贯穿了周扬的脸,周扬抬起头,几件刚晾上的胸衣正在迎风飘扬,周围密密麻挂满了女人的衣物,这些平时难得一见的东西此时就无比鲜明地出现在周扬的面前,周扬忽然就有一种想笑的感觉。


何林说,你想好了?学做牙齿要从学徒工开始,这样你的工资就又只有八百了。


周扬说,想好了。


何林了解对面这个人,他一旦做出决定就很难改变。他说,好吧。


出门时,周扬突然回头说,对了,我和张雨在谈朋友。


何林的眼皮跳了一下,周扬的话让他很恼火,他想说点什么,周扬却没有等他,走掉了。


周扬的工资重新变回八百块,张小庆也领到了他换工作后的第一个月工资,3千八百块。这个数字让他有些小小的不满,这和他想的不一样,在上一家公司,虽然工资只有3500,但拿到手能有3400,在这里,虽然是试用期4500,也不至于只有3800吧。他去问了公司新来的人力兼财务夏陶,夏陶说就是这么多啊,你的上一家公司没有给你交保险呢。拿到工资条,张小庆不由恶狠狠的诅咒了这些不知道什么时候能用上的保险和税率们。


刘宏感觉到了张小庆的情绪,他说,王总给你多少工资?


张小庆说,5000,试用期4500。


刘宏说,扣完税后是有些少,你和王总再说说去。


张小庆却不想去,他就是这样的一个人,不想去找领导,想着的是只要工作努力,领导一定会发现自己的优点,涨工资自然也是这样,自己是不提的,那样就变成自己要求了,而不是领导肯定的。所以尽管心里有些不高兴,但是他会忍耐。


张小庆开始他的第三个项目,这是个军方项目,是张总通过关系找到的,这个项目寄托了张总的希望。对方是某部信息中心的主任,项目是校园的管理系统,最开始定制两套,如果可以的话,主任承诺会在部里几千所学校推广,如果一套1万块的话,利润会非常可观,当然,当前的两套是需要低价交付的,一套1万块。王总和张总对这个项目都非常看重,那段时间,他们基本上天天往信息中心跑,王总说,加油啊,同志们,搞好了我们后半辈子就不用干活了。金鹏却对他们的行为嗤之以鼻:就是瞎搞,想想一个信息中心主任能有那么大的能耐?即使有这么大的馅饼掉下来就刚好砸在科技动力头上?!


张小庆问付江,说,你怎么想?


付江笑一笑,舔一口手中的雪糕,说,呵呵,那是领导的事情,我们就是干活的。


张小庆问刘宏,说,你怎么想?


刘宏说,还能怎么想,干活。


张小庆见过那个主任,尽管自己也当过兵,但是却太不喜欢他,说话太满。他想,真正的好东西,带给人的似乎并非狂喜,但持久。 所有让人突生狂喜的, 都让人会想这能否持久。他想把自己的想法说给王总,但是想了想,又放弃了,王总属于那种很有激情的人,现在正处在激情当中,张总已经对金鹏表达了不满,自己只是一个程序员,何必呢。这么想着,就又开始敲着手中的代码。王总订的鸿毛饺子送到了,王总招呼大家吃饭,项目的交付压力很大,主任要求一个月后上线,于是,张小庆他们开始加班,每天晚上干到12点,第二天早上8点继续,张总家远,要回家,金鹏丢下一句瞎搞也回了家,剩下王总陪着他们,王总在房间里看ppt,王总说,只要有一点点希望我们也要抓住,加油。张小庆是不想加班的,他怕自己的身体扛不住,但是,他想了想,就一个月,也许没有问题,心里有了侥幸,就什么都不说。


说的是开发,其实上最大一部分时间耗在测试上,建好表,自动生成代码,再改点逻辑,一个模块就完成了,接下来的时间就是测试,点点没有问题还需要测试和其他模块的交互。这个工作让张小庆很烦恼,自己测试自己开发的东西,这很奇怪,公司没有测试人员,于是,他和付江交换着测试对方代码。


项目交付的时候公司给每个人发了1000块钱作为加班费。早上洗脸的时候,张小庆突然发现脸盆里有红色的东西滴下来,他的心顿时缩紧了,心想,糟糕,连忙去照镜子,果然,出鼻血了。给刘宏打电话请了假,去医院的路上,张小庆的心一点一点的紧张起来,这些年来,他最恐惧的事情就是去医院,最大的噩梦就是重新住院。他想起张海宁来了,他最常说的一句话是:知道吗,我得活着,我还没谈女朋友呢。那段时间,他就两件事情,一是给一个年轻的女孩写信,二是趴在骨髓移植室的窗户上看,那是一个18岁的小伙子,他经常和小伙子的父亲说话,那个父亲是个40多岁的中年人,个子不高,背略微有些驼,不爱说话,每天早晨,他沉默的提着尿壶从儿子的移植室出来,沉默的走到走廊的尽头,倒尿,然后再沉默的返回他儿子的病房,一坐,就是一天,偶尔,也看到他一个人坐在楼层的楼梯上,弯着腰,把整个身子都埋到两腿中间,不知道在想些什么。从张海宁那里,张小庆知道,他很早就和老婆离了婚,好不容易一个人把孩子拉大,却不想得了白血病,为了治病,他借遍了亲戚,最后都没有人愿意理他,最后,他把房子卖了,带着儿子,两个人,从广东到北京来,做骨髓移植。一切似乎很顺利,孩子的情况很好,甚至,他们搬出了层流间,搬进了普通单间病房,解放军报也有记者过来进行了采访,但是搬出来的第三天晚上,情况却急转直下,植入的细胞全部死亡了,检测不到白细胞,于是,迅速的转进层流间,却没抢救过来。整个抢救的过程中,那个父亲一直站在门口紧张的看,最后,医生摇摇头,他却突然笑起来,那是人们见他第一次笑,一个人踉踉跄跄的朝科室外边跑,所有的人都叫不住,所有的人都拉不住,他只是那么笑着,踉跄的往外走,不再看他的儿子,不再管他的儿子了。几天后,他变成了一个干缩的老头,来领儿子的尸体,也正是在那天,新的军报送过来,整整一版报道医院第一例移植成功的新闻,上面,他的儿子戴着口罩,却笑容灿烂。


张海宁几乎是立刻要求出院。医生和护士轮流做他的工作,说他的条件非常好,他和他弟弟的骨髓配型完全符合,不能这样放弃,否则就是等死。两个星期后,他终于又开口说话了:知道吗,我得活着,我还没谈女朋友呢。他见到谁都是这一句话。他能有骨髓移植非常不容易,家在甘肃农村,没有钱,只能依靠部队。为了让部队出钱,他想尽了办法,团长躲着他,他就冒充总部参谋给团里打电话指明要团长接;团里不给钱,他就说要带着两把菜刀去天安门。终于部队拿出了20万,他几乎是凭着一己之力争取到这个机会。


想到张海宁,张小庆的心情有些平静下来,医院到了,周扬就在附近,看完医生见见周扬好了。

 

posted @ 2011-07-09 14:20 ronghao 阅读(1581) | 评论 (3)编辑 收藏

新的工作在上地,第一次听到上地这个名字,张小庆差点想笑起来,这个名字实在是太能让人听错。公司在上地环岛嘉华大厦,从空中网面试出来,刚好坐上运通105直接到。公司很小,办公地点只有不到80个平方,张小庆是从论坛里看到这个公司招聘的,技术性公司这个描述吸引了他,更重要的是他们有自己的开发平台产品和工作流产品,这让张小庆心里塞满了对技术的向往。没有笔试,面试的是一个高高瘦瘦的年青人,两个人握了手,年青人自我介绍说叫金鹏,两个人似乎是随意的聊天,金鹏问了张小庆去年的工作,有什么感触和收获,然后简单介绍了一下自己的公司,张小庆问了公司产品的开发情况,用了哪些技术,很快,技术面试就结束了。接下来是老板的面试,自然,先把张小庆肯定一下,说看过他所有的博客,觉得很不错,金鹏也说他不错,接下来,再把自己公司肯定一下,说公司是技术性公司,工程师文化,每周四下午提前下班组织羽毛球活动,咖啡和茶都是免费的,从这个公司出去的员工都去了大公司或者自己创了业,最后,最重要的,就是工资,张小庆想了想,要了5500,老板还价到5000,于是前后不到一个小时张小庆就面试通过了,老板看看表,说快到12点了一起下去吃个饭,张小庆想自己还没决定加入这家公司有些犹豫,老板看出了张小庆的担心说没事不加入我们也没关系就算交个朋友。一起吃饭的时候,张小庆想起了两小时前,自己在空中网和一帮人一起等了半小时,然后一起做了一个小时的笔试题,最后人力告诉他们回去等消息也不告诉具体时间,中间连一口水都没有喝上,这前后的差别真是大啊。

从嘉华大厦出来,张小庆继续坐上了运通105,他在网上看到西三旗育新家园的房子有找合租的。车过上地桥,桥上挂着一条长长的横幅:热烈欢迎百度入驻上地,运通拐进安宁庄,这里是另一番景象,到处是低矮和交错的平房,让张小庆恍惚回到了鹿圈,上地也是一农村啊,张小庆自嘲的想。育新家园的房子合租要900,这让张小庆选择了放弃,他去了附近的小区,认真的看周围的电线杆,最后,他在北新家园找到了一个两居室,房东是个胖胖不高的中年人,房子是他父亲分的房,老房子,基本上没有装修,也没有任何家具除了两张床,房东说1200,张小庆想了想,说太高了。房东说,那你说多少?张小庆再一次想了想,最后,认真的说1150。房东说,行。

当张小庆把这个砍价的故事说给周扬听的时候,周扬放声大笑起来,说,你真会划价啊,还认真想了一想,哈哈。

张小庆说,别笑了,现在着急的是赶快把这房子合租出去。

找到工作后就是回亦庄吃散伙饭,比尔先付了钱,说,要好好干。王碧薇也去了,她问了张小庆新工作在哪儿。张小庆说上地。上次表白被拒绝后没多久,张小庆和王碧薇又恢复了朋友的关系,两个人还是正常说话,偶尔还一起吃个饭,甚至有时还发发短信,王碧薇问张小庆是否相信男女之间存在纯真的友谊,张小庆回短信说相信,只是,发消息的时候张小庆总觉得怪怪的。

新公司叫科技动力软件有限公司,张小庆是公司的第7名员工,前面6个是王总公司总经理、张总公司副总经理负责销售和市场、金鹏产品部经理、刘宏项目部经理、刘倩销售、付江程序员比张小庆早来公司一个礼拜。张小庆先到项目部,归刘宏管,刘宏个子很高,很瘦,山东人,好脾气的男人,很喜欢笑,但是牙齿不好看,一笑就露出参差不齐的牙齿。张小庆仔细观察了刘宏,他似乎做什么事都有条不紊,一天要接好多电话,有抱怨的,有发脾气的,但每次他都十分耐心和平静,说,好,我知道了,丝毫没有任何情绪。不过大家都嘲笑他怕老婆,张小庆听到过因为一次喝醉酒了回家开水龙头把地板泡了的事,从此以后,老婆命令他再不准在外边喝一口酒。这事张小庆开始不信,可是刘宏真的是不喝酒,直到一次见到他的老婆,一个和刘宏一样高而又比他强壮许多的女人,张小庆突然就相信了。

张小庆问了刘宏,说,刘哥,我网上查到科技动力是个老公司了,还是曾经的电子政务百强,怎么现在感觉像刚创业似的?

刘宏露出他不好看的牙齿,说,这都被你看出来了?确实是刚创业。

从刘宏那里,张小庆了解到,公司原名叫科技动力网络信息技术有限责任公司,办公地点在中关村,50多人,但是去年公司发生了变故,原来的总经理周小兵成立了自己的公司侵吞公款,被发现后公司就分裂了,周小兵拉出一帮人自己干,原有的副总经理也就是现在的王总则和张总一起出资继续经营这家公司,因为公司最后几个月没有发出工资,所以王总为了免除债务,就将公司名字改为了科技动力软件有限公司,算是新的创业。说到这里,张小庆突然想起来,怪不得老有人找上门来向王总要工资呢。还有,周小兵也来要过呢。

张小庆说,我们的项目从哪里来?

刘宏说,原公司遗留下来的一个政府部门项目,每年的维护费用在30万,这些能够满足公司平时运营成本,其他项目,靠刘倩和张总的关系。

张小庆说,还是政府有钱啊。

刘宏转换了话题,说,金鹏可是我们公司的牛人啊,好好干,争取进产品部。

因为是前后脚加入公司和同龄的缘故,张小庆和付江马上就黏糊起来,付江是北京人,刚刚在立水桥买了房子,5000一平米,家里出得首付,比张小庆大一岁,胖乎乎的脸,憨憨的,大眉毛,小眼睛,笑起来眼睛就寻不见了,第一句话总是:是吗?然后一笑,呵呵。

张小庆的第一个项目是和付江一起做一汽丰田的在线销售系统,各个经销商和4S店通过这套系统上报销售情况,项目不大,10万块钱,2个月交付。因为基于公司开发平台开发的缘故,开发速度很快,建立好数据库表,平台就能自动生成好所有的代码和默认页面文件,同时将表单的编辑过程自动绑定到工作流上,马上就能测试。开发平台是如何实现这一点的,这让张小庆着迷,这符合他写高质量软件的期望。但对这个项目来说,最有难度的是客户要求一个最后的销售情况统计功能,需求类似于在线的电子表格,刘宏问了张小庆是否实现过类似的功能,张小庆说没有,不过,他说,我可以实现,他想起了去年写过的那些javascript,实现这个正好。

项目开始之前,张小庆和付江一起去了国贸,去见了客户,客户是一个年轻漂亮的女人,一身职业装,说话柔柔的但是有力,对自己想要什么描述的很清楚。回来的路上,张小庆对付江说,这个姐姐很漂亮啊。

付江说,是吗?然后笑一笑,说,呵呵。过了好一会,当张小庆已经开始想如何实现这块功能的时候,付江突然转过脸来,说,是,是很漂亮。

付江说,给我介绍个女朋友吧。

张小庆说,你不有女朋友吗?

付江想了一会,说,那个呀,哦,分手了。

张小庆说,你不要她了?

付江说,不是,是她不要我了。

过了几天,吃午饭的时候,张小庆突然想起这个事,就对付江说,给你介绍个女朋友吧。付江抬起头很诧异的看着张小庆,说,为什么?这一问把张小庆搞糊涂了,他说,不是你说的吗?付江想了好一会,说,哦,我们又和好了。张小庆差点没被嘴里的饭憋死。

因为公司人不多,每个人都是多线程的。第二天的时候,张小庆又跟刘宏一起去了空中网,他们购买了一套公司的开放平台,5万块钱,张小庆他们过去做售后支持。车到白石桥的时候,张小庆想起了那次糟糕的空中网面试,那次笔试过后到现在已经快3个礼拜了,一直没了消息,应该是没有通过,没想到,自己很快能够再回这里,却不是复试。系统有个模块不能正常工作,刘宏坐下来,张小庆站在后边,让他没想到的是,刘宏操作起linux来竟然是那么的熟练,他敲击键盘很快,很快就找到了系统日志的出错位置,几分钟后,他改动了一行配置文件,系统重启,模块工作正常了,是人力招聘模块。出门的时候,张小庆问了刘宏说,你怎么知道错误在什么地方?刘宏笑笑说,不是我知道,是系统的日志写得好,非常容易定位错误。张小庆突然就想起来去年的那次自己没有充分测试的修改。

走出大厦的时候,张小庆的手机突然响了,张小庆接了电话,电话里说您好是张先生吗我们是空中网很高兴的通知您的笔试已经过了约您下周一来公司复试。张小庆说,噢不用了你们给消息太晚了。电话说,是这样的我们的系统出了些故障刚刚修好。汗立刻就从张小庆脸上淌了下来,这报应也太快了吧。

不管怎么说,张小庆的生活算是步入了正规。公司9点钟上班,每天早上7点半张小庆定的闹钟响,十分钟闹钟再响时起床,十分钟穿衣洗漱出门,在小区门口买个鸡蛋灌饼,8点钟的时候能够准时站在公交车站,有3路车都到,运通105、623和814,张小庆最喜欢坐的是运通105,623开得太保守,814起步价2块不合算。项目是一汽丰田的那个美女项目,付江负责和美女沟通以及其他模块,自己负责实现那个电子表格,他也再一次体会到了纯粹技术带来的快乐,因为是页面编程,所以刷新一下就能看到效果,没有好的调试工具,就一段一段的写上alert,自己去年买的那本犀牛书带过来,不明白的地方就翻一翻,还使用上了ajax,直接和后台异步交互,这一切都让张小庆兴奋,经常是刚刚觉得进入状态就到了午饭时间,再写一段就到了下班时间,时间变得飞快。午饭在楼下负一层,是10元的套餐,公司补助,两荤两素加一个水果,张小庆很满意,吃完午饭张小庆会和付江、刘宏一起围着大厦绕一圈,大厦西边,还是一片片的平房,道路很狭窄,一个个的小馆子开在路两旁,中间隔着污水沟,刘宏说这一片都在拆迁计划中。晚上6点下班,张小庆一般会多待1到两个小时写代码,在楼下吃完晚饭再回家,自己没有电脑,他不知道自己回那么早家该干点什么,每个周六,张小庆都会来到公司,写代码,每次过来,除了付江,他开始装修房子了,几乎所有的人都在。张小庆还找到合租了,一个做销售的哥们,没有对象,经常一出差就半个月不见人影,张小庆租给他550。周六晚上看电影频道的外国片和德甲,周日的时候,张小庆会睡到10点,然后极不情愿的爬起来去小区后边的菜市场吃早饭,那里用过的碗都是放到门口的大盆里浸一浸就直接提出来再用,不过味道倒是还好,张小庆会买些水果回来,那里每天早上都有早集,周围各个小区的人们都拥到这里,买各种各样的蔬菜、水果、鱼肉和衣服,同时,扔下各式各样的果皮、垃圾和沸燥的声音,每次张小庆出小区门的时候,小区里的大爷大妈们已经回来很长时间了。

项目进行的很顺利,付江每天都和客户聊一个小时的QQ,将做好的页面截图发过去,客户也总是及时的回馈消息,在截图上添加上各式各样的箭头和说明文字,项目按时上线,客户很满意,觉得那个电子表格做得很好。付尾款的时候,王总买了一件啤酒,说立刻停下手中的工作要庆祝一下。所有人都喝了点酒,除了张小庆和刘宏,下班的时候,刘宏突然叫住张小庆,神神秘秘的说,你也怕老婆吗?说完同病相怜发现知音般的笑起来。张小庆笑起来,猜测得到了证实,原来刘哥真是妻管严啊。

此时的张小庆,心中充满了快乐和希望,公司会越来越好,产品会越做越好,而其中,就有自己的代码。只是,他不知道的是,这个项目竟然会是他今后几年唯一一个算得上成功的项目,直到他3年后离开科技动力,客户还给他们打电话问能不能私下技术支持一下,真不知道他如果此时知道这个消息又会怎么想,事实就是这样,软件开发从来都是一件困难的事情。

posted @ 2011-07-02 22:28 ronghao 阅读(2225) | 评论 (4)编辑 收藏

站在路边拦车回家的时候,张小庆的心情一点一点的激动起来。这条从县城回家的路和十年前相比没有任何的变化,甚至连周围来来往往的人们都没有任何的变化,他们熙熙攘攘,奔向各自的方向,变化的是等车的人。5年前,张小庆站在这里,那时还是高中生的他,心中塞满了对假期的渴望;4年前,张小庆站在这里,那时刚上军校的他,穿着整整齐齐的军装,红色的肩章在他的肩膀上闪闪发光;3年前,张小庆站在这里,那时刚刚出院的他,充满自卑,不想碰到任何认识的人,焦急的盼望车的到来;现在,张小庆站在这里,刚刚从汽车站出来,时光仿佛回到了4年前,他想起来那天,他和父亲走在北京的大街上,太阳很明亮,父亲背着一大麻袋从老家带来的土特产走在前面,他一个人默默的跟在后边,他们去人民医院找一位经过好几个人介绍到的血液科教授,他们迷路了。他们在路边问了一位刚刚晨练回来的老大爷,老大爷双手背在后边,手里拿着一个收音机,他热情的给他们指了路,末了,他问:你们从哪儿来?


父亲说,我们从湖北来。


老大爷点点头,说,广播里说长江涨水了,湖北人民还好吗?


父亲说,挺好的,谢谢您的关心。


现在,张小庆心里就有这种莫名的优越感,尽管张小庆自己觉得很可笑,他不知道这种情绪来自于哪里,他也经常把这件事当做笑话讲给其他人听,但是,它就是莫名的存在,它让张小庆在这个回家的下午眯起眼睛微微的满足起来。


假期在上班后突然就变得短暂,特别是当人们为日子附上特定的含义之后:除夕、初一、初二、初三,时间经不起度量。张小庆不想走亲戚,更多的时候,他和他的同学们在一起打麻将。他们说到了镇上的那座化工厂,他们说,妈的,终于要开工了。他们共同回忆起来十年前的那个冬天的下午,一帮人在寒风中等待了足足两个小时,终于等来了领导和香港商人的驾临,他们从开着暖风的车里钻出来,眨动几下嘴巴,挥动一下剪刀,马上又回到温暖的车里,从车屁股里喷出来热乎乎的尾气很让穿着校服戴着红领巾的他们温暖了一阵。他们说,领导都是一傻帽,那个香港人根本就是一个骗子。说到这里,他们一阵大笑。他们说到了肖东,他们说,还记得那个叫威震天的家伙吗,现在牛逼大了,黑社会老大,只要提到他的名字,根本就没人敢拿正眼瞧我们。他们共同回忆起那个初中一年级的早上,镇上游戏室的老板找了几个人来打偷他游戏币的学生,威震天挺身而出,被一次又一次的放倒在篮球场上,然后又一次又一次的爬起来,他嘴角和鼻子里都淌着血,回教室时却挂着微笑,说,别担心,现在是属于他们的,但未来是属于我们的。张小庆想起黄晶晶和威震天牵着的手。他们说到了县里正在建的火车站,他们说,本来是在我们县建货运站的,结果市里领导向中铁要钱,人家一生气就把货运站修到了不要钱的高水县,这下傻了吧,许多要建的工厂都跑到高水去了,没脑子的领导啊。


刚到家的时候,周实和李秀来找过张小庆,他们是周扬的爸妈。周扬已经3年没有回家了,换句话说,自从他去了北京就没有回过家。两个人问了周扬在北京的情况,张小庆不知道该对他们说些什么,因为当他从周扬那里拿火车票听周扬说不回家时他很吃惊,他说,为什么啊?周扬只是笑笑,说,没有为什么,只是习惯了。李秀叹了口气,说,这孩子从来都不知道主动给家里打个电话,又说,他爸也是,从来不知道给孩子打个电话。


在有些问题上周扬是永远不肯原谅周实的。周实的性格注定了他的懦弱,这些懦弱反映到周扬的身上就是他从小到大从未挨过周实的打。套用一句话来说,没挨过打的人生是不健全的人生。周实总是用一种博大和深远的眼光来看待这些问题,这一点可以从他五十年的人生路途中从未和人红过脸打过架得到证明,见过周实的人无一例外地都说他是个大好人。人生不健全的周扬在他五年级的时候终于第一次挨了打,为他补上这一课的是他的同班同学刘博。周实给周扬新买了块电子表刘博要要周扬不给于是刘博就上前推了周扬一个趔趄,刘博整整比周扬高出一个头自以为有持无恐,但周扬立刻冲上去扬起了他干瘦的胳臂。结果很明显从一开始就注定了,从未挨过打的周扬绝不是刘博的对手,镇静下来的刘博只几个回合就让周扬鲜嫩的小脸上多出几个掌印。得意洋洋拿着周扬电子表的刘博威胁周扬说:不要和你爸爸讲,听见没?周扬果然就没有告诉周实,他找了一整块砖头放在书包里,第二天上课前只一书包刘博就晕倒在走廊里,趁班里女生尖叫的空儿周扬从容地从刘博手腕上取下手表然后没事般的端坐在教室里。他若无其事地拿出课本等待上课了。这件事的直接后果是刘博的妈抱着被子在周扬家住了一礼拜还口口声声地说:这事没完。周实的软弱暴露无余。在此期间他跑前跑后,又是向老师道歉,又是安抚刘博他妈,又是提着点心去看望本大可不必住院的刘博,最后还偷偷塞给刘博他妈四十块钱。四十块钱啊!在90年代初几乎相当于周实半个月工资,周实那时在镇上的砖瓦厂上班。即便是这样周实也没有动周扬一指头,他用自己的实际行动在帮儿子道着歉,而在周扬心里,这事根本就没错。倒是李秀不依不饶地把周实大骂了一通,李秀是一个地地道道的庄稼女人,她说,你钱多得没地方花啦!


周实还想抵赖,说,你说什么呢。


李秀说,狗屁!


周实就没话说了,说,不就是四十块钱吗?


李秀说,我的妈啊,四十块啊,明天我就不干活了。不就是四十块吗,你去挣啊,去挣啊?!


在女人骂他的过程中,周实保持了一种微笑的情绪这是他一贯的笑容。他对一边的周扬说,别理她她没受过多少教育我们不和她一般见识。周实上过高中。


尽管面对种种不快,周实却对当前的生存状态表示了满意,他认为生活作为其本身是没有什么值得指责的地方,该摆正的应该是人的心态。五六十年代多困难的时期不都挺过来了么,还有什么值得抱怨的呢?现在人都是私欲膨胀,要知道在这个世界还有1/3的人吃不饱饭呢!周实就这样怀着一种善意的目光打量这个世界,他工作努力,他与世无争,他年年都是优秀党员和优秀工作者,抽屉里的一大沓荣誉证书是他事业的见证。他还成功地和李秀相安无事地生活了几十年。李秀却从不把周实的这些荣誉放在眼里,在她眼里这些几毛钱一个的证书与纯粹的废纸没有更多的区别,不能吃不能穿还占用抽屉。意识上的差别终于使周实和他的女人在周扬初二时爆发了结婚以来最大的冲突。事情源于周实所在砖瓦厂厂长的退休,组织上经过考虑决定提拔年轻干部,周实作为年轻优秀车间主任的代表榜上有名,还有一个候选人是原先的副厂长郭树。在那段特殊而敏感的时间里,周实听到的都是他又到谁家送礼谁谁又是他熟人等等,这都让他感到心烦,更有甚者一些工人开始悄悄地往他家跑跟他说给谁谁谁送礼比较好。这是周实所绝对不能容忍的!骂我可以但绝对不能侮辱我的人格!作为女人,李秀不止一次在后面提醒周实要活动活动,但每次周实都义正严辞的拒绝了,这关系到他的尊严。作为一个男人,没有钱可以,但没有自尊怎么行?!提拔的结果自然不会出人意料。李秀一回来就指着周实的脸说:嫁给你,我算瞎了眼了。


周实的脸色铁青,说,我是党员!


女人反问,那姓郭的不是党员?!


周实说,是,但不合格!


女人冷冷说,是吗?你合格为什么不提你提他呢?


周实被憋得说不出话来,他的知识,他的文化,他的学历都让他静下心来。他说,不就是个厂长吗,有那么重要吗?


女人冷笑了,说,你除了会这么说还会怎么说,那你就等着瞧好吧!


周实和女人争吵的时候周扬在房间写作业,他自然没能写出一个字。以他当时的水平自然还不能分辨出谁对谁错。但这场争吵却给了他还不成熟的心里以深刻的印象,父亲的软弱,母亲的凶狠都让他感到心烦。吵个屁!周扬说。春天再来的时候郭厂长新买了辆摩托车开始骑着摩托上下班。他笑呵呵地对人说,不贵不贵才三千多一点。郭厂长的儿子也由普通高中转到了重点高中。有人看不惯了:郭树这家伙不就是当厂长后才发的么?是啊,当初说不定还是周实呢!说到这里人们看周实的眼光就有了某种同情的成分在里面,好象这些本来是属于周实的东西却被姓郭的平白抢走了一样。周实装着什么都没有听见可他的女人不行。什么事只要李秀说声--看人家郭树!周实立刻就会妥协下来,他当初所谓的自尊在这句话面前一文不值。


在这期间周扬的身体发生了巨大的变化。他的胡须长出来了,他的嗓门开始变粗,喉结开始悄悄隆起,更重要的是他发现自己的下身开始长毛了,长长的,弯曲的,他还第一次偷看了黄色录相。他不再细声细气地和人说话,去打乒乓球发现球台被人占了直接走上去用还不成熟的嗓子吼道:走开!放学后周扬不再马上回家,他更宁愿在马路上乱逛,他已经有了自己的思考,他讨厌周实的软弱,更确切的说是厌恶,同样厌恶的还有李秀的尖刻。


相比而言,周实的几个姐姐就要过得比周实好得多。每次到周实的几个姐姐家里,吃过饭,周扬掳出光滑的胳臂,戴上深蓝色的围裙,走到水池旁边,开始刷碗。整个房间的气氛就热闹起来,男人们和女人们开始搓麻将,孩子们则愉快地坐在沙发里看电视。等周扬刷完碗拖完地从厨房出来,周实已经坐在牌桌上了,他是替他姐姐上的,赢的算他的输的算他姐姐的。像平常一样,周实打麻将的神情是专注的,一丝不苟的。周扬却越来越感到恶心。他为他爸感到羞耻,他为自己感到羞耻。他有一种感觉那就是自己像是打长工的,这种关系通过亲戚这个堂而皇之的名称固定下来。他讨厌周实的姐姐也就是他所谓的姑妈。既然是客人那有让客人打扫卫生的道理?一次两次倒还罢了为什么总是这样?!她们的孩子比我大却在悠闲地看着电视,这又是什么道理?!家里的彩电是她们换代后送的,沙发也是她们买新的后给的,连周实平时穿得一些衣服也是她们给的,她们给这些东西时的表情周扬永世难忘,难道就因为这个?就因为这个?!搓麻将的杂乱的声响有气无力地从关紧门的房间里挤出来,电视机在客厅里昏暗地响着。洗完碗的周扬站在厨房里,姑妈的笑容展开在他的面前:洗完了?好!你们看我们的扬扬多能干!去你妈的!周扬想。


周扬中专毕业时周实又忙了起来,他开始托关系想帮周扬找份工作,此时的周实已经下了岗,四处打点工赚些钱,李秀也不再提郭树,因为人家已经把整个砖瓦厂买下来成了郭老板了,郭老板还在县里开了水泥厂。周扬毫不犹豫的拒绝了周实的好意。周扬说,你挺有钱的是不是?


周实一时没有反应过来,说,为了你钱算什么?


周扬冷笑了一声,说,我是说我从今天开始不再花你的一分钱。


周实语塞,儿子的态度使他气愤。周实说,什么?你怎么能用这种口气跟我说话?现在找工作容易吗?


周扬说,你即使送钱也找不到什么好工作。


周实说,你怎么知道?


周扬说,我当然知道,我一直都知道,有本事你和郭树一样?周扬的话毫不客气直接戳到周实最敏感的部位。


周实的脸立马涨得通红,说,你给我住口!


周实的话激起了周扬的愤恨,他一次又一次地把积压已久的不满发泄到声音,他也想看看周实什么时候能够真正喷发出他男性的尊严,周扬说:这个时候叫我住口了,有本事你也冲别人喊啊。电视是别人的,沙发是别人的,连穿得衣服也是别人的,有本事你跟人家郭树一样!优秀工作者,优秀党员,先进个人全他妈扯淡,他们除了这些给了你多少钱?我受够了,我发誓我决不和你一样,说到这里周扬顿了顿,他考虑该不该把这个词说出来,但他还是说了,他大声地说:窝囊!


房间里静悄悄的,周扬期待着周实的爆发,爆发的想法使他浑身颤抖呼吸急促,他很想看到周实发火的样子,哪怕就那么一小会儿,他需要一个关于他爸的崭新形象,但他深深地失望了。周实只呆了一会儿就转身默默向卧室走去,他关上了门,他甚至连关门也是轻轻的。剩下周扬一个人空洞地站着,他的四周是冰冰冷的墙壁,他想哭又想笑,父亲这个形象在他心中已经彻底倒塌了。我要走!周扬对自己说。


很快周扬就出现在北京西站的广场上。临走时他固执地什么都不带,他坚决不让周实去送他,但上汽车的时候他还是敏锐地发现了周实花白的头发。周实也发现儿子在看自己了,于是,他就迅速隐到人群中看不见了,周扬冲周实的方向说了声操!就突然很想哭,但这种想法一晃就过去了。汽车开动了。


3年没有回家,也没有主动给家里打电话,他是担心接电话的是周实,他不知道该如何开口。周实又何尝不是如此,于是,两个人就这样对持着。好在春节并不长,情绪在那几天堆积在一起,让人抓狂,但那几天一过,很快就消逝了,日子也再次变得如潺潺溪水。张小庆也再次被这潺潺的溪水不情愿的冲刷回来,他面临的问题是要不要换工作。答案其实在老板那次谈话后已经有了,但是,张小庆不知道自己应该如何向比尔开口,他想起了比尔对自己的照顾,想起了自己走后比尔就剩下他一个人了,想起了自己采用了很多新的技术这些都需要比尔再去学习,想到这里,他突然憎恶起自己来,憎恶起自己为了满足自己而采用了那么多的新技术,这些技术都需要比尔一个人一点一点的去理解,而很多代码还都是实验性质的。他想,也许只需要比尔一句话,自己一定会留下来的。


比尔果然就找了张小庆,出乎意料的是,他说,你走吧。

 

posted @ 2011-06-20 00:06 ronghao 阅读(2164) | 评论 (4)编辑 收藏

昏暗的空气里,周扬点燃一支烟,红色的烟丝一闪一闪,映出他那张疲惫的脸,对面,坐着同样无精打采的张小庆,一个被爱情挫败的年轻男人。周扬昨天晚上陪老板何林喝了酒,喝多了,直到现在还泛起一阵阵的恶心,那是突然清醒后的恶心。


周扬的命运跳转于一次廊坊之行。那天,周扬坐在从六里桥长途客运站到廊坊的依维柯上。他的穿着很得体,头发刚刚洗过,皮鞋擦过一遍又仔细地打过光。和其他人不同,周扬特别重视自己的衣着和头发,有时甚至过了份。有人说我们打工又不是搞对象穿这么好干吗,周扬不这样认为,衣着关系到给别人的第一印象,是绝对容不得半点马虎的,两年前刚到北京时那件冒满线头的假耐克已经永远的成为了历史。这一切,都让他在一群送货员里显得格格不入,这些何林都看在眼里,不过他从不说什么,他只是告诉总管每个月给他多发一百块钱。


汽车在京津塘高速公路上飞快地行驶,整个车厢的人都昏昏欲睡,车头电视里放着年代久远而略略发黄的香港剧。周扬突然就想起来了自己第一次走下火车踏上这片陌生的土地的时候,那时迎接他的还是北方的寒冷。还未从车厢躁热的气氛中缓过劲的周扬连打几个哆嗦,然后他张开嘴从牙齿缝里挤出他到北京的第一个声音:真他妈冷!单薄而细长的周扬站在宽大的北京西站广场上紧张地四处张望。他的眼睛稍稍有些浮肿,那是因为火车上没睡好的缘故,车厢里人实在是太多了。他上身穿着件冒牌的耐克,很多地方都冒出线头来了,下面一条黑色裤子,脚上是双回力鞋,已经很脏了。背上背着两个大包,里面有着他的全部家当:一床褥子两套换洗的衣服和临走时李秀硬塞给他的四百块钱。


现在呢?现在不也挺好么?不用担心什么也不用想什么,一个月八百块钱,要知道还有多少人没工作啊。可明年呢?后年呢?不,这不是我想要的!周扬恶狠狠地对自己说。自从上次用半年的工资买了个最新款的诺基亚,周扬就告诉自己,已经20岁了,该考虑些其他的东西了。


送完牙齿后周扬并没有马上回去,他去了另外一家医院,这才是他的目的,他径直来到了二楼的牙科。这个从来就是何林自己的事,周扬观察何林已经有很长时间了,他看起来很悠闲:白天带首师大女生出去玩吃海鲜,晚上很迟才回来,有空就到车间里转转,不看书也不看报,他不识字。然而就是他在短短几年间从无到有在北京打开了市场,还把生意做到了廊坊,他不动声色,他衣着随便,他放屁打嗝,仅此而已。周扬看上的也正是这一点。


牙科医生是一个胖胖白皙的中年人。周扬发现在这里几乎所有的医生都保养得很好,他们衣着优雅,略显富态,膳食合理,他们都有着良好的个人习惯,他们从不吸烟。周扬递上自己的名片,上面印有公司的名称和自己的手机号码,这年头名片和身份证一样泛滥。医生漫不经心地扫一眼名片,随手把它扔到桌子上,告诉周扬说很忙没空。周扬没有放弃,他从医生的语言中敏锐地捕捉到什么,他以一种平静但有点探询的口气说:听口气你好象是湖北的吧。医生用一种奇怪的眼光看着周扬,这种眼光证实了周扬的判断,周扬说:襄樊的?这次医生说话了:你怎么知道?周扬说:我也是襄樊的。医生说:那又怎么样?医生的语气很好地保持了职业的冰冷,但这对周扬来说已经够了,起码医生已经注意地和他说话了,很好,就这样。


中午的时候,周扬在医院外面等到了医生,他给他详细地介绍了公司的情况并直接说出了回扣点,比一般公司要高,医生点点头,认可这个数字,然后两个人一起吃了饭就算认识了。喝过几杯酒后医生突然向周扬述说起了自己的种种不易:妻子企业效益不好发不出工资,儿子调皮考高中要上重点得花一大笔钱,还有个弟弟又不能不管。说到动情的地方医生的眼睛湿润了。医生前后判若两人的表现让周扬感到惊讶。周扬礼貌地听着不时发出一声叹息然后不停地给医生倒酒。最后医生喷着酒气说没问题你的事就是我的事。周扬安慰他说,没事,想开点,一切都会好的。第二天周扬又给廊坊打了个电话,医生却改了主意,他要求回扣点再高一点,周扬在心里恨恨的骂了自己的老乡,他做不了主,要向何林请示。何林冷冷地看着周扬擅自主张拿来的定单,脸上没有任何表情,何林说:会喝酒吗?周扬说:会。何林说:好吧,以后有事我会叫你。


只要什么时候何林打个招呼周扬的正常送货生活就结束了,这种时候不多但不知什么时候会来。何林和他的那些客户的关系通过吃喝开始又通过吃喝固定下来,而这其中喝无疑是关键,周扬的新工作就是陪酒。这和周扬所想相去甚远。何林根本就不给他单干的机会,他也根本发不了言,何林只是在开始时简单地介绍一下他:这是我助理。饭桌上何林才是主角,是皇帝,他谈笑风生,他成熟幽默,他会恰如其分地开一些黄色的和政治的玩笑。相比之下,周扬是可有可无的,是暗淡无光的,他的身份注定了他只是个配角。这种饭局严格意义上说就是一场看不见硝烟的战争。双方表面上觥筹交错称兄道弟,暗地里却是搭弓拔剑暗暗较着劲。吃这种饭很累,很费神。周扬中专混乱的经历帮助了他,他酒量惊人,他喝酒从来就是脸不红心不跳端起酒杯二话不说就干然后满脸歉意地对那些目瞪口呆的大小医师说:对不起喝高了上个厕所。在厕所里周扬开始呕吐,周扬知道,其实自己只是喝酒不上脸罢了。有一次周扬前脚刚进洗手间后面就跟进个医生,医生惊讶地说:啊?你吐了?周扬忙用水冲去污物:喔,刚才鱼刺卡住了。医生半信半疑说:原来是这样啊。出了洗手间周扬小声地骂道:操你妈!


何林深谙生意之道,和何林比周扬深深感到了自己的浅薄,他的一言一行都让他发烧、惭愧。那次廊坊之行实在只能算运气,如果不是那个医生的妻子拿不到工资,如果不是老乡,结果还是很难说。没过多久周扬就和这帮医生熟识了,他可以随意地出入他们的办公室,没大没小地和他们开着玩笑甚至坐在椅子上的时候可以高高地跷起腿。周扬也清楚,支撑在他们背后的有一个关键的东西--钱,没有这个什么都白扯!吃喝归吃喝谈价格的时候他们决不含糊,一颗假牙的价格可以压到很低甚至还低。何林表面上微笑着,回到公司没哪一次不大骂这群医生没良心,然后他告诉总管让把这批假牙的含金量下调十几个百分点。他妈的搞我?看谁厉害!周扬同样痛恶了这种陪酒的生活,他经常感到自己的胃部隐隐作痛,他去了医院医生告诉他胃有些溃疡不能再喝酒了,他想到了撤退,更重要的是他明白了生意场上很多交道,这其实也是一门学问,他学得很快。周扬觉得自己离自己的目标正越来越近。这个月周扬破天荒地拿到了两千元,但是他却没有一点惊喜,他说,妈的。


看着对面的张小庆,周扬说,给你讲个故事吧。你知道的,我比你早两年来北京,我的第一份工作不是业务员,我的第一份工作是保安,在清河小营,那里有个老乡开得KTV,那种地方,保安和打手没有什么区别,总有上门滋事的,总有不守规矩的,如果说他不听,那么就我们上。第一次到北京,身边没有任何亲人,这时有个姐姐对我特别好,她是我们的老乡,比我早2年来北京,比我大5岁,在KTV做服务员。


张小庆说,后来呢?


周扬说,后来她就经常叫我去她租的房子里吃饭,她做得饭非常好吃,最喜欢的就是她做得莲藕炖排骨,真的真的非常好吃,特别地道,吃着吃着就想起了我的外婆。休息的时候,她带我一起去菜市场买排骨,买菜,然后回到家就开始炖,要炖上3、4个小时。她和几个姐妹一起合租的房子,就她的房间最干净。我知道她们挣得不多,需要从客人消费的酒水中提成,但她有很多很多的漂亮衣服。


张小庆说,后来呢?


周扬说,后来我就特别喜欢和她在一起,但她似乎总是很忙,经常见不到她,不过,一旦她有时间,她就会找我,我想她也是喜欢我的吧。


张小庆说,你爱上她了吗?她漂亮吗?


周扬说,我不喜欢爱这个字,我觉得这个字挺俗,是那种表面光鲜实质烂大街的东西。漂亮?也许吧,这是她们的资本。


张小庆说,后来呢?你们在一起了吗?


周扬说,后来发生了一件事,这件事非常严重。那天晚上我值班,我听到一个包间里有吵闹和摔碎东西的声音,于是我就去看了,我看到了她,一个客人在她身上摸着什么。


张小庆说,后来呢?


周扬说,后来我就控制不住自己了,什么都没有想,冲进去抡起桌子上的酒瓶罩着那个家伙的脑袋就一下子,血立刻就下来了。有个家伙站起来要还手,我几下就把他掀翻在地,那时年轻,下手重,一顿打,把他肋骨打断了好几根。


张小庆说,后来呢?


周扬说,后来警察就来了,我被拘留了。最后还是老板力量大,他是北大的教授,其实也就初中毕业,因为有关系,据说认识一个大人物,他搞定了这一切,我就被放了,我知道,这份工作算是丢了,但是我当时一点都不后悔,我认为自己是英雄。


张小庆说,后来呢?


周扬说,后来?没有后来。


张小庆说,那个女人啊。


吸完最后一口烟,周扬把红光扔到地上,用脚把它踩碎了,说,出来后我去找了她,我原以为她会很感激我,但是她好像人间蒸发了一样,哪里都找不到她。后来我想,这也许是她工作内容的一部分吧,后来我又想起了她那么多的漂亮衣服,她的钱哪能买那么多的衣服。所以,那根本就不是喜欢,只是一种心理需要罢了。


周扬讲完他的故事,两个人都默默无语。周扬又点燃了一支烟,这次他没有吸,看着它自己燃尽。一时间张小庆的思维有些混乱,他认为周扬的故事太极端,没有代表性,他想起了张海宁,第一次见张海宁是在自己第一次住院的时候,那是个光头的年青士兵,奇怪,那时候刚刚住院,竟然还有一丝高兴,因为终于可以不用训练了,不用队列训练,不用跑五公里了。每天上午9点是治疗时间,只有张海宁一个人跟没有事的一样,在走廊里到处乱逛,几乎所有的人都认识他,和他打招呼。张小庆问了同病房的常爷爷,常爷爷说,他呀,是这个病区的名人,慢性粒细胞白血病,准备做骨髓移植。


相同的身份,让张小庆和张海宁很快熟悉起来,张海宁说,小庆,按照兵龄,你应该叫我班长。张小庆说,我毕业了是干部,你应该叫我排长,班长和排长哪个大?


张海宁没事的时候就趴在桌子上写字,他写的字并不好看,歪歪扭扭,但是他买的是格子纸,一笔一画写的很认真,好几天才能写完一份,然后整整齐齐折好,用信封封好,寄到遥远的甘肃去。张小庆说,寄给谁啊?


张海宁说,不告诉你。


张小庆说,我知道,你的相好,她长得漂亮吗?


每当这时,张海宁的语气就特别斩钉截铁,当然,长得很漂亮!


后来,张海宁把一张自己的军装照小心翼翼的寄过去,姑娘也寄来了自己的照片,张小庆要看,张海宁却不给,自己有事没事就拿出来一脸情深的看。终于,趁张海宁出去打饭的功夫,张小庆偷看了他的照片,照片上是一个大脸盘的姑娘,因为气候的缘故,姑娘的皮肤很干燥,还有常见的高原红,总之,看到照片的一刹那,张小庆感到了失望。不过张海宁不这么认为,他最高兴的时候就是收到姑娘信的时候,他也总是在写信寄信盼望收信中徘徊,剩下的时间就是等待,等待医院历史上第一例的骨髓移植病例出无菌室,他将是第二例。


这才是爱情吧,张小庆想。


两个礼拜的痛苦过后,生活似乎又恢复了正常,爱情就是那么一点时间的事。张小庆每天上班下班,周末的时候就去图书馆,有几个周末,他去参加了bea组织的开发者活动,在那里,他认识了好多人,还被抽到过一次一等奖,奖品是一本weblogic的书,他想,什么时候自己也能有机会在上面讲。系统正式上线了,比尔请自己一起吃了饭,上次那次事情过后,张小庆就在想如何让测试尽可能的覆盖到尽可能多的情况,他开始写一些测试用例,但是用例越写越多,如果每次改动都回归一次需要的时间很多,这样,他想到了自动化测试,他写过一些细粒度的单元测试,感到作用并不明显,他在一次活动中听到了selenium,但是数据准备又成了一个问题,如何分离开发环境和测试环境,他没有答案。同样没有答案的还有很多问题:为什么项目后期加人会影响项目进度,为什么手术刀式的主程序员人员搭配能收到比较好的效果,这是看人月神话后的疑惑;为什么要极限编程,为什么要测试驱动开发,为什么当前项目的架构并不符合企业应用架构模式里的模式,这是他看了一堆书后的疑惑;为什么系统上线后还需要反复修改,难道修改本身就是软件开发的常态,这是最新的疑惑。这些问题困扰着张小庆,他得不到答案,他问了比尔,比尔说这需要实践,他去查阅了相关书籍,但正如比尔所说的,如果不在项目中实际体验的话,是没有答案的。于是,张小庆想,是否该离开这里了?


到了年底,就只剩下三件事:买票、回家和加薪。同事给了票贩子的电话,一张票要加价30,张小庆给周扬打了电话问要不要一起买,周扬在电话那头说,便宜你了,只要你给我买包烟,我就帮你买。


张小庆说,你怎么买?


周扬说,去火车站排队。


张小庆还向周扬借了五百块钱,一年下来,除去房租、药费和书费,竟然只剩下不到3千块钱,张小庆想给家里2千块钱,于是,他得借钱。但不管怎么说,不再花家里的钱,这就是进步,今天比昨天好,明天比今天好,这就是希望。张小庆现在的工资是3500,转正后比尔给加过一次500,半年的时候比尔又给加过500。现在,张小庆找到比尔,这是他第一次主动提加薪,他说,新的一年不知道能不能长些工资?比尔点点头,说,我对你很满意,这是应该的,不过我得给老板说,这需要他的批准。


如果能够加些工资,不管多少,我就留下来再干一年,张小庆对自己说。尽管存在这样那样的疑惑,尽管也想过是否该离开,但一想到比尔,张小庆就想着留下来,做所有事情最重要的不就是要跟对人吗,不管做什么事情,比尔总是说,做吧,试试看。从来都没有责怪,包括那次出事故的事情。管理一个程序员其实很简单,不是吗。


老板找了张小庆,和上次一样,他坐在宽大的办公桌后边,见到张小庆,他挪动了一下屁股,脸上挂着笑容,说,怎么样,一年下来工作感觉如何?


张小庆说,挺好的,比尔非常照顾我。


两个人说了一大堆乱七八糟无关紧要的事情,最后,终于切入到了正题,老板说,我听比尔说了,他想给你再加些工资,你怎么想?


我怎么想?这个问题很奇怪,这个问题似乎不应该问我,我还能想什么,我当然想可以啊。张小庆有些不好的预感,说,恩,我知道自己犯过一些错误,但我一直在努力。


老板说,这也是我的想法,你还很年轻,也犯过错误,所以,我认为你现在的水平和工资是很匹配的,未来还有机会,年轻人嘛,是有希望的,你说呢?


张小庆没了言语,他不知道做翻译的老板是如何评价一个程序员的水平的,他的话很委婉,但是让自己不能接受。他就站在那里,没有表情,一动不动。


老板看一眼张小庆,说,我们是不会亏待任何一个有水平的员工的,好吧,我还要发一封邮件,你先出去吧。


老板的意思已经很明确,我们不会亏待任何一个有水平的员工,而你,显然是不够水平的,你是不够水平的,就是这样!可是,3500对一个一年经验的程序员是否足够,这不是关键,关键是老板认为你不行,就是这样!

 

posted @ 2011-06-12 00:13 ronghao 阅读(1713) | 评论 (1)编辑 收藏

张小庆在本机测试了上传附件,问题重现了,在项目页面找不到这些附件了。什么原因?张小庆打开本地文件,附件都在,他又打开数据库,这次他发现与附件相关的字段没有保存上数值,他最不希望的事情发生了,hibernate没有被正确使用。不知什么时候,项目总监站在了他的背后,盯着屏幕,问,找到原因了吗?


不知道是不是空调坏了,空气燥热起来,张小庆说,找到了,我没有正确使用一个软件。


项目总监说,比尔知道吗?


张小庆说,他不知道。


老板很快找了张小庆,办公室里没有开灯,也没有拉开窗帘,显得有些昏暗,老板坐在他宽大的办公桌后边,第一句话是,你知道我们周末两天损失了多少钱吗?


空气也跟着昏暗下来,张小庆吸不上气,说,不知道。


老板说,没有正确使用一个软件,这是你说的这次事故的原因吧。


张小庆觉得有些不对劲,但是,他又不知道哪里不对劲,他说,是。


老板坐在那里,一动没动,说,就因为你没有正确使用一个软件导致了我们一个项目没有按时交付,我现在要给客户打电话解释,这个项目已经拖了很久了,你让我怎么解释。


老板顿了顿,桌子对面的年青人此刻一句话都说不出来,站在那里,神色紧张,不停的咽口水,就这样的人,比尔还老说他优秀,他有些讽刺的想。他说,比尔呢?


比尔第二天来的第一件事就是和老板谈了话,接下来他和张小庆一起修复系统。比尔说,什么原因导致这个缺陷?


张小庆说,没有正确使用hibernate,附件字段忘了配置持久化。


比尔说,这不是原因。


张小庆有些惊讶,说,那什么是原因?


比尔说,原因是你没有进行充分的测试,这也是我建议不要修改已工作代码的原因,因为一旦修改则意味着必须进行彻底的测试,这块工作量往往比修改本身大的多。


接下来,两个人试着恢复数据,附件都在,只是它们之间的关联关系丢失了。比尔打开日志,开始查找日志。张小庆想了想,他想起来,比尔的代码里有大量的日志代码,甚至多到他认为繁琐的地步,当使用上hibernate,删掉大量处理异常和日志代码后,整个文件由一千行缩短到一百行,他好半天才说,我的新代码没有加日志,因为我觉得日志只是开发时做调试用。


比尔说,调试只是一部分原因,日志是任何系统最重要的组成部分,运行的系统如何排错、如何优化、如何监控,全部要靠日志。对一个向用户收费的系统来说,日志的作用更是重要,因为当用户对费用进行怀疑时,唯一有说服力的就是日志,日志关系到公司盈利。


中午吃完公司提供的午饭,比尔突然说,我们出去走走吧。


两个人顺着小区旁边的马路走,天气很热,两个人在一棵树下站定,张小庆想,出了这么大的事情,比尔一定会责备自己吧,责备没什么,重要的是自己让他失望了。比尔果然就开了口,他说,你和老板说这件事是你的责任?


张小庆说,是啊,系统因为我丢失了数据。


比尔叹口气,说,你知道吗,这件事和我们根本没有任何关系。系统现在是试用期,也就是任何错误都可能发生的,翻译那边是必须做备份的,现在,老板却认定是你一个人的责任,根本就是他们没有遵守规定,因为系统还没有正式交付。你和老板说什么了吗?


有那么一瞬间,张小庆的大脑里一片空白,他说,就是项目总监问了我系统出错的原因,我回答了,然后老板就找我直接说是不是因为错误使用了一个软件导致了这个问题,我不知道怎么回答就回答说是。


比尔轻轻笑一下,摇摇头,说,他把所有问题都推给了你。


奇怪,竟然没有一点点气愤的感觉,反而想笑一下,原来这里也是这样的。去年的这个时候,坐在学校临街的一家餐馆里,张小庆请营长吃了他们之间的最后一顿饭,营长负责把他从部队送回来,任务完成了,要回部队去。营长把部队退回的各种原件给张小庆看了,他让张小庆去复印一份作为存根。张小庆看到了那封部队退回自己的情况说明信,看到最后一段时,他的手禁不住哆嗦起来,简直就是污蔑!上面写道,张小庆同志经过我们的测试,各项军体测试全部不达标,身体素质极差。这都是什么啊?!整个大学四年,从出院开始,一直在坚持锻炼,最开始是每天坚持慢跑完2公里,然后是3公里、4公里和5公里,接下来是快跑一段再慢跑一段,到毕业时,只要咬咬牙,5公里就能达标,而短跑和单双杠,从来就是优秀的,这么写不是污蔑又是什么?!张小庆感到自己被侮辱了,这短短的一句话把自己大学四年所有的种种努力全部给轻轻抹去了,躺在这个纸面上的自己,是那么的不堪,那么的龌龊,就是一个废物。他说,营长,您知道的,这不是实情。


营长扫一眼那封信,没有一点奇怪,说,这个啊,只是一个说法,没人会在意。


可是张小庆在意了,也许他不在营长所说的那些人当中吧。晚上躺在床上,重新拿出那封信的复印件,他遏制不住的看它,它也再一次次深深刺痛了他,眼泪,就那么毫无预兆的淌下来。张小庆想起了那些个晚上,每天晚上,跑完5公里,就开始在寝室里煨中药,他是那么的恨那个中药罐子,每次跑步跑不动的时候,他就狠狠的骂自己,活该,你就是那个该死的罐子,是个废物,这样想着,跑步的速度就会快一点点。现在,在他们的眼里,自己还是个废物,是个皮球,被踢来踢去。


比尔说,我已经和老板说过了,这个改动是我让你做的,但是,他对你的印象可能需要一些时间才能改变。下次如果遇到这样的事情,记得先给我打电话,千万不要自己顶。


张小庆长久的沉默了,那里都是一样的,是的,是这样的。


比尔转移了话题,说,上次你说要用版本管理工具,正好这个事情暴露出版本管理的问题,你试一试,我们一起搭建个环境。


下班和王碧薇一起往家走的时候,王碧薇批评了张小庆,说,瞧瞧你们开发的烂系统,害的我们周末的数据都丢了。


张小庆没有说话。王碧薇说,哈哈,幸好我都有备份,也不怪你们,他们都没有按要求备份。对了,李宇春、周笔畅和张靓颖你支持谁?


张小庆对这个问题感到很奇怪,说,她们是谁?


王碧薇很惊讶,说,哎呀,你连她们是谁都不知道吗,超级女声啊!快买份报纸投票,后天就总决赛了,我是玉米。


只从那个晚上过后,张小庆的夏天突然多彩起来,给他的夏天涂上色彩的是王碧薇。每个晚上,吃过晚饭,洗过澡,张小庆给王碧薇发过短信,老地方见!生活多了期盼,便多了生气。大多数的时候,他们沿着活动中心的广场慢慢的走,有些时候,他们也会走远一些,绕着贵园北里走。他们说到了博客,张小庆说到博客中国刚进行了第一轮融资,方东兴正是意气风发的时候,自己也刚刚开了一个博客。王碧薇访问了张小庆的博客,说,每天记录一些东西,真好,你都什么时间写博客。张小庆红了脸,小声的说,上班时间。王碧薇哈哈大笑,说,背着比尔吧。张小庆说,没有,是比尔鼓励我开的,因为我没有电脑。他允许我上班时间写博客,不过要是和技术相关的,他说他也在看。张小庆说到了自己给牛人发信问问题的事情,说都石沉大海没有回应。王碧薇说,牛人们不屑回答吗。张小庆说,开始我也不理解,但是想想,和我一样给他发信的菜鸟一定很多,他一定是处理不过来。他们说到了房价,王碧薇说政府正在对快速上涨的房价进行第一次调控。张小庆说我也听到了,那次从首图回来在公交车上听到的,广播里说最近一段时间办理二手房过户手续的人激增。他们说到了篮球,每个中午,比尔都会喊上张小庆去楼下的球场打球,王碧薇站在远远的树荫下看。王碧薇说,你知道我们公司最帅的男人是谁吗?张小庆说,不知道。王碧薇说,每次你都说不知道,你就不能猜一次吗?张小庆说,猜不到。王碧薇叹口气,说,是比尔!说完,和张小庆一起哈哈大笑。他们自然还说到了超女。张小庆说,我不明白超女为什么这么火。王碧薇说,这都不明白,从小到大你什么时候真正意义上投过票,除了超女?!


两个人周末的时候还一起去了美廉美,超市在天宝南街,两个人在小区门口见面,一起坐班车过去。张小庆不得不承认,美廉美和小白羊是两种风格完全不同的超市。两个人在美廉美吃了饭,王碧薇要了两份卤煮,张小庆没有吃过卤煮,王碧薇说,很好吃的,老北京特色,我特别喜欢吃。端上来,黏黏的,火烧、豆腐、小肠和肺头掺杂在一起,上面生长着些绿意葱葱的韭菜花。王碧薇说,这是补心肝的,快吃。张小庆却一下子适应不了这个味,皱着眉头,想咽咽不下,想吐对面坐着的却是王碧薇,于是就犯了难,含在嘴里,半天不动。最后还是王碧薇解了围,哈哈笑起来,说,我第一次也是吃不下,吐了吧,吐了吧。


两个人在超市逛了很久,王碧薇买了很多东西,其中有瓶六神的沐浴露,张小庆想,原来她身上好闻的沐浴露味是六神的啊。从超市出来,已经8点半了,班车停了,于是两个人只好步行回家,天气有些阴,走到天宝中街的时候,突然下起雨来,两个人跑到一家临街的小商店里躲起来。雨越下越大,两个人站在小店的窗户前向外望,雨点打在窗户上,窗户渐渐模糊起来,映射出两个人的身影。王碧薇着急起来,过一会就让张小庆出去看看雨是不是停了,张小庆走到门外,把手伸到屋檐外,心里说,不要停,不要停,嘴里说,小了些,但是走不了。一会儿,王碧薇也走出来,两个人并排站着,把手都伸出去,背后,小店温暖的灯光从玻璃里挤出来,洒在两个人的身上,张小庆突然就想抓住旁边这只纤细的手,然后,慢慢的走。路上开始弥漫起水气味,不远处,体育中心的草坪刚刚剪过,水气味夹杂着青草的清香,真是个美好的夏天晚上。


张小庆把自己对王碧薇矛盾的情感和周扬说了,他说,我要不要向她说呢?


周扬说,又是一个办公室熟女勾引小男生的老套故事。


张小庆不喜欢周扬的回答,说,我说正经的呢。


周扬说,喜欢就去做,怕个屁啊!


张小庆说,可是我的身体,要不要先和她说呢?


周扬说,你傻啊,怎么也要交往一段时间之后。


从周扬那儿回来,偎依在公交车最后一排的座位上,看着窗外的路灯一盏盏往后移动,张小庆一动也不想动,他想得最多的还是王碧薇,怎么办呢,自己似乎越来越盼望着和她在一起,他也越来越肯定这就是爱情,但,身体,这是个大问题,这个问题是一定要告诉她的,什么时间呢?如果后来才告诉会不会是一种欺骗?一会儿他又狠狠嘲笑了自己,人家对自己还不知道怎么想呢,也许和周扬说的,人家只是一时空虚只是想找人排除内心的寂寞呢,那就搞笑了。想到这里,张小庆决定不再想王碧薇这个问题,也许自己和她之间本来是没有爱情的,自己想多了,两个寂寞的人,在一个孤单的城市,在一起互相需要一下,仅此而已。爱情在这里恢复了俗气的本色。一会儿,他又想到了工作上的事情,老板对自己的印象应该是变坏了,为什么不要修改已工作的代码?最关键的问题不是修改本身,而是测试,那么如果有足够的测试,是否就可以修改呢?那又为什么要修改呢?更好的代码,更高的可维护性,但是,自己觉得更好的代码在其他人眼里是否是好代码呢?比如说这次一味追求减少代码行数,将日志代码删了个干干净净,在修改之前是否要彻底明白这段代码之前是做什么的呢?没有无缘无故的爱,同样,也没有无缘无故的代码。最后,在项目进行中间引入新技术是否是合适的呢?比尔对自己的代码理解了很长时间,自己曾要求删掉这些代码,但是比尔却不同意,他是怕打击自己的积极性,新技术在解决一个问题的同时必然带来新的问题,现在,这个问题是复杂性,这个复杂性抵消掉了新技术所带来的好处,甚至,带来了风险。永远不要对新技术期望太高,也许,这是正确的答案。


回到家里,早上四点多的时候,张小庆给王碧薇发了短信,做我女朋友吧。


可以想象的到,从四点到天亮七点这段时间里,这个可怜的男人是如何的难熬。一会儿,他想到王碧薇同意做了他的女朋友,那是一种怎样的幸福情景,一起走路,一起说话,再不用担心失去什么,因为她就是你的,想到这里,笑容爬上了他的嘴角;一会儿,他想到王碧薇拒绝了他,从此一切都变了,以前还能说说话,现在连说话都不可能了,想到这里,忧愁爬上了他的眼角;再过一会儿,他想到了自己的身体,他咒骂自己的行为,觉得自己没有资格奢望爱情,想到这里,绝望充满了他的眼睛。被爱情折磨的可怜的人,大概都像此刻的他一样吧。一会儿想到同意,一会儿想到拒绝,有一会儿想到身体,几种截然不同的情感搅拌在一起,他就在床上翻来覆去的睡不着,他哪还有心思睡觉啊,再过一会儿,天就亮了,王碧薇就会看到他的短信了,他巴不得天早点亮,但是,一会儿,他又希望时间过得慢一点,正如那天在车站等待周扬。


七点半的时候,张小庆收到了王碧薇回来的短信:小庆,你是个挺不错的人,我相信一定会有一个比我更好的女孩子在等着你的。

posted @ 2011-06-05 17:12 ronghao 阅读(2230) | 评论 (2)编辑 收藏

怎么办呢?人力给了检查通知,在航天医院。不行,这事一定不能让公司知道,找周扬代替,但是医院会不会检查很严格,会不会要身份证,自己的照片和周扬还是差别很大的。体检这件事让张小庆苦恼,他翻来覆去的想,最坏的情况是护士当场戳穿他,但这种情况怎么想都会发生。最后他还是给周扬打了电话,他没有选择。


周六的早上,周扬从西钓鱼台来到东高地的航天医院。阴天,在公交车站等周扬的时候,张小庆有些慌张,不停的看手机,他一边觉得时间过得太慢,周扬还没有到,一边又觉得时间过得太快,他不想马上面对医院那未知的判决。最后,他开始玩一个奇怪的游戏:数从公交车上下来的人数,如果是偶数,那么就有戏,如果是奇数,那么就会被戳穿。他一遍又一遍的玩这个游戏,当是偶数时,他想没这么幸运吧,再看看下一辆,当是奇数时,他又想这不可能一定是数错了,需要看看下一辆再确认。他一遍遍这样固执的数着,突然之间,他就想起张海宁来了,他现在一定在某个地方看着自己吧,这样想着,张海宁的话就在耳边响起了:活着真好,真想活着啊,你真幸运。是啊,自己现在站在这里,能够等待,能够自由的呼吸,能够大声的说话,甚至还能够感觉到忧愁和担心,这不是很好吗,自己已经很幸运了,因为那些人,已经断断续续的死去了,活着,就已经很好了。这么想着,时间突然失去了概念,天气似乎也变得好起来,张小庆长长呼出一口气,不再为公交车下来的人数而紧张,周扬在后面拍了他的肩膀:我到了。


负责体检登记的是一个满脸青春痘的护士,护士说:张小庆吗?


周扬四处看看,吹吹头发,说,是。


护士头都没抬,说,身份证。


周扬“啪”的一声将张小庆的身份证扔到台子上。


护士稍微抬了抬头,眼光扫过,说,四号诊室。扔出一张检查单。


张小庆的经理叫比尔,是个子高高的年轻人,穿一条洗得发白的牛仔裤,上身是一件灰色的衬衣,方脸,浓眉毛,衬衣整整齐齐的扎在裤子里。系统还未上线,张小庆访问了比尔机器上正运行着的实例,这是公司内部的业务流程系统,公司主要的业务是翻译和本地化,系统主要对公司内部的工作进行协调。技术很简单,Jsp加JavaBean,这让张小庆有些小小的失望。


为什么要开始这个项目,张小庆观察了其他人的工作,当接到一个翻译项目时,项目总监会将它分配给一个项目经理,项目经理再把这个项目文档进一步分解成几个小的文档,再把它们分配给具体的翻译人员,这些文档通过263电子邮件发送。他们之间通过一个纸质的项目登记单进行沟通,项目总监在最初的项目单上填上项目的起止时间和需要翻译的总字数,项目经理分解任务,填上每个任务的字数和完成时间,由领取任务的翻译人员确认签字,翻译完成后,项目经理负责对整个翻译进行校对并为每个任务评定翻译等级,继续签字,最后由项目总监签字并提交给财务归档。每到月底,财务就忙碌起来,他需要统计所有已回款的纸质项目单为每个员工计算工资,一般都需要3、4天时间,这让他一计算完工资就过来问比尔系统什么时候能够正式上线。另外,一次翻译项目出现了重大事故,当需要查找所有相关文档时,都散落在各个邮件里,非常麻烦。老板很重视这个系统,因为除非项目结束,否则他基本看不到公司项目即时的进展情况。了解了这些,张小庆也就明白了系统分为四大块的原因,这四块分别是:翻译流程与任务分配、文件归档、基于个人的绩效统计和项目报表。


吃午饭的时候,张小庆问了比尔:为什么公司要自己开发不请外部软件公司呢?


比尔说,第一是需求,他们可能并不了解我们需要什么,第二是时间,我们对他们的交付时间没有信心,第三是维护,我们总是需要即时改很多东西,维护别人开发的系统总是很困难,除非是相当成熟的产品,最后是价格,我们之前咨询过很多家公司,最便宜的都需要50万,如果是自己招人开发根本不需要这么多。


张小庆说,所以如果是自己核心的系统首选自己开发。


比尔笑了笑,说,对也不对,关键在于我们需要什么样的系统,这个系统并不复杂技术也很简单,所以自己开发是对的。


张小庆说,我想想,如果系统是公司的核心系统,如果系统需要很多的定制化开发,如果系统对时间的要求不那么高,如果系统的维护工作量大,如果系统的技术简单,如果系统并不复杂,那么应该自己开发。


比尔说,关键是成本与收益,把所有的影响因素都列出来,比较一下,就能得到答案。


张小庆的上午基本是在奔波中度过,他拿着一个笔记本和一支笔,在办公室里跑来跑去,回来的时候本子上密密麻麻记满了文字和手绘的草图,接下来,他会把需求整理一下然后绘制新的一副草图,给比尔确认,然后再跑回去让提需求的项目总监和财务确认,最后,他将这张草图撕下来,贴在显示器旁边,草图上涂满了箭头和说明文字。下午是编码时间,比尔喜欢这个年轻人,因为他从他身上看到了年轻时的自己:热情、好学,对未来充满希望,一刻都不愿意停下来。有一次,程序里要实现字符串的切分,下班的时候,张小庆告诉比尔说可以使用Apache的开源包,结果第二天上班的时候,比尔惊讶的发现代码里所有涉及到字符串处理的部分全部被替换成了这个开源包,不仅如此,所有涉及到数组、集合处理的部分也全部被替换成了这个开源包。打开张小庆的博客,新增了关于这个包的使用说明,不只是字符串切分,他把所有的API都使用并说明了。


项目进展的很顺利,当草图被实现后,张小庆会告诉项目总监试用,项目总监对频繁的试用感到很满意。最初的新鲜感过后,张小庆却越来越感到沮丧,他是为Jsp和JavaBean难过,在他电脑的文件夹里,下载了大量的开源项目,这些项目包括了struts、spring、ibatis和hibernate,有时间的时候,他就翻看这些项目的源代码和写一些小的demo程序,甚至,他已经把整个系统的持久层都用hibernate重写了。什么是高质量的软件,高质量的软件需要具有高可维护性,代码的可维护性体现在哪里,体现在分层清楚、逻辑清晰、人人可理解,但是现在,Jsp里夹杂了太多逻辑,页面应该只负责显示才对,还有,原生的jdbc让整块持久代码臃肿不堪,数据组装逻辑占据了大部分代码。张小庆把自己的苦恼告诉了比尔,比尔说,如果代码工作的很好,那么就不要动它,如果是新代码,我鼓励你一试。但是,几乎都是修改的工作。于是,张小庆一天天苦恼起来,这不是我想象中的高质量软件,他对自己说。


与工作的苦恼相比,张小庆的生活也发生了变化,生活的变化总是和女人有关。上班第一天,旁边隔间里突然站起位年轻的女孩,女孩对张小庆说,早啊。张小庆忙站起来,说,早啊。女孩说,你是新来的吧。张小庆说,对,今天第一天上班。女孩笑起来,这让她的那双毛毛眼好看的眯成了一条弯弯的线,女孩说,啊哈,终于找到比我还新的新员工了,我是上周来的,哈哈。张小庆也被感染的笑起来,不过他的笑是羞涩的,他努力让笑容处于自己的控制中,这让他的脸慢慢的涨红了。


女孩伸出手,那是只很细的手,说,你好,我叫王碧薇,认识你很高兴!


张小庆不知道该不该握住那只手,他犹豫了半响,最后还是抓住了那只手,说,你好,我叫张小庆,认识你也很高兴。


女孩说,好,中午陪我一起找房子吧。


那个春日的中午,王碧薇像一只被放归大自然的小兽敏捷而灵活地跑来跑去,拖着张小庆前行。对一个女人来说,最重要的不是漂亮的外表,也不是各种名贵的衣服和首饰,而是光线,是走在千千万万女人群中一眼就能标新立异的那种光线。王碧薇的个子并不高,她长得严格意义上算不上漂亮,但她拥有光线,无限的光彩从她轻盈的身体和愉快的笑声里溢出来,光芒四射,流光溢彩,照得张小庆眼睛生生的疼。他们一起去了广德苑小区,一起去了贵园东里,一起去了星岛嘉园,一路上都是王碧薇在不断的问问题,张小庆不停的回答,他们一起去看了小区公告板上的出租广告,一起去社区服务站问了大妈们有没有出租信息,一起给广告上的电话号码打了电话,一路上都是王碧薇走在前面,张小庆就是个跟屁虫的份。


最后,王碧薇在贵园北里找到了房子,和另外两个女孩合租。她请张小庆在小区旁边的一家小餐馆里吃了饭,以后,每个周末,王碧薇都会叫上张小庆和另外一个女同事一起去这家餐馆吃饭,两个人就这样熟识起来。王碧薇问了张小庆住在哪里,张小庆说自己住在鹿圈。他其实最先来到的是贵园南里,那里离公司最近,有公司人力推荐的房屋信息,可是几个电话过后,他就泄了气,最便宜的房子都要1200,而自己身上只有不到400,更可况要交三押一呢。正在发愁的时候,他看见了一个老太太,老太太问他是不是在找房子,如果是的话可以和她的孩子合租,一个月800。张小庆点点头又摇摇头,咬咬牙,问,这里哪有更便宜的房子?老太太想了想,手往南一指,说,去鹿圈吧。走在鹿圈的路上,张小庆顿时想起一首诗来: 借问酒家何处有? 牧童遥指杏花村。他觉得改为:借问房子何处有?老妪遥指鹿圈村,挺合适。这样想着,他不禁笑了出来。


跨过凉水桥,果然就是两个不同的世界,一边是楼房林立,一边则全是低矮的平房,一边道路宽广整洁,一边则是污水横流,张小庆甚至看见一个中年男人在一个靠墙的角落里满不在乎的小便,那架势,就好像这里是他家的私人厕所,上完厕所,打个激灵,抖一抖,这才慢慢的将裤子提上,旁若无人的离开。张小庆找到了一家四合院里的一间,房东是买大蒜的,他要租的房子原先是堆大蒜的,没有床,只有一个木板,中间还有一个大窟窿,四个角用砖头码起来,一碰就吱嘎吱嘎作响,见到有租客过来,房东拿扫把把木板上厚厚的灰尘扫了扫,于是木板就不停的叫起来。张小庆站在一旁邪恶的想,这每天晚上北京又有多少块床板在欢乐的歌唱啊。张小庆和房东商量好价格,一个月100,一月一付,包水不包电,此外每月需要交掏厕所的费用2元,厕所在院子外边,用半拉子的砖头胡乱切成,不仅透风还透光,张小庆觉得在里面上厕所跟在大马路大广场上亮出屁股没有太大区别。


不知不觉之中,张小庆就特别期待起周五下班来,因为那时可以和王碧薇一起吃饭,他不知道那算不算喜欢。两个人的关系在一次偶然的碰面后得到了发展。那天下午,张小庆在小白羊超市的门口看见王碧薇了,她和一个男人在一起,这让他有些稍稍的失落,第二天早上他说昨天看见她了然后假装漫不经心的问了那个男人是谁,王碧薇犹豫了一下,说是她的男朋友。晚上的时候,王碧薇突然就给张小庆发了短信问能不能陪她散散步。两个人在亦庄的活动中心见了面,活动中心的门口有几级台阶,王碧薇站在最高的台阶上等张小庆,天气很热,她穿了件白色体恤,下面是一条碎花裙子,露出匀称的小腿,一双高跟凉拖,露出高高的脚倮,她低着头,在台阶上来回踱着步子。看到张小庆,她停下来,说,吃饭了吗,没有打扰到你吧。


那个晚上,两个人绕着活动中心转了好久,王碧薇说了自己,她比张小庆早毕业3年,一毕业就来到北京,一直做得就是翻译工作,自己现在最大的愿望就是能在北京有个自己的家,不再租房,不再东搬西搬。张小庆说了自己的愿望,说要写出让很多的人都使用的高质量软件。王碧薇问张小庆他会一直在北京吗,张小庆说那是肯定的,因为这里有他的希望,今天比昨天好,明天比今天好,这就是希望。


天色已经很晚了,周围都安静下来,偌大的广场上早没了人影,小区也已经睡着了,只剩下橘红色的路灯孤寂的发出滋滋的声响,空气中开始弥漫着一股凉气,有点冷了。王碧薇在一个路边的靠椅上坐下,说起了她现在的男朋友,张小庆在她旁边站着,由于光线的缘故,他只能看到她长长眼睫毛投下的阴影,看不见她的眼睛。王碧薇说,他是她大学的同学,大学毕业后一起来到北京,工作了半年就考了研,现在还在读研。张小庆说,挺好的,读个研究生挺好的。这话说得酸酸的。王碧薇说,不是你想的那个样子,他其实是想逃避,他不喜欢北京,他想回成都,为此我和他吵过好几架,但是没有效果,他很坚决。张小庆说,成都也挺好的。王碧薇说,可是我喜欢北京,我在这里生活了3年,我喜欢这里,第一年工作给他的打击很大,他只想着离开,他为什么就不理解我呢。说到这里,王碧薇的声音有些哽咽,可以想到,她长长眼睫毛的阴影下,可怜的眼泪一定流下来了,这不是张小庆所认识的王碧薇,有一段时间,他站在那里,看着面前这个漂亮女孩的肩膀随着情绪一动一动,他却束手无策,不知道怎么办才好,他想递给她一个纸巾,却没有带,他想去搂住这个可怜的姑娘,但却喉咙发干,挪不动步子。时间就这样一分一秒的过去,慢慢的,王碧薇的情绪平静下来,两个人都没有说话,又待了一会,她站起来,擦擦眼睛,说,对不起,我没控制住情绪。张小庆说,我陪你回去吧。一路无言,走到王碧薇住的楼下的时候,张小庆说,没事的,明天一定比今天好,不要放弃希望。王碧薇说,恩,好,再见。


回到家里,张小庆却无法平复自己此刻的心情,一躺到床上,就浮现出王碧薇肩膀随着情绪抽动的情景,真是个可怜的女人。心中便生出许多爱怜来。甚至有一阵,他突然想自己是否是爱上这个女人了,但是,想起自己的身体,他又回到了现实中,现实是冰冷的,这样的身体,如果爱上了,就是对别人的不负责任。他不禁恶狠狠的咒骂起自己的身体来,觉得世界真是不公平,觉得整个世界都是灰暗的,希望没有了,明天也没有了,他嫉妒起每一个人,每一个周围的人,甚至是自己的亲人,很多鼓励的话他们说起来都是那么的简单都是那么的自然,但是,得病的又不是你们,你们凭什么说的这么轻松。要保重身体,怎么保重,你告诉我怎么保重,每天都要在厕所里背着同事用唾沫咽下药片,每隔一段时间都要去一趟医院提心吊胆自己的血像,每个月都要去一次王府井那里有个药店价格最便宜一个月算下来只要一千块钱,当拿着一盒盒的药片在繁华的王府井街上穿行时,谁能体会自己当时的心境呢,反正又不是你,说说总是简单,妈的。


当情绪褪去,生活还得继续。爱情是个什么东西,张小庆问自己,是种情绪?那么情绪消失了,爱情还在不在?是种责任?但这话总听起来文绉绉的,别扭极了。什么人才有资格追求爱情?身体健康的人?事业有成的人?或者这个问题本身就是一个伪问题,爱情应该是每个人都能追求的,不存在资格不资格,是这样吗?那么,自己有资格吗?什么才是高质量的软件?能工作的就是高质量的软件吗?不是这样,这个要求太低,那么,采用很多新技术的软件就是高质量的软件么?似乎也不是这样的,采用新技术的目的是什么?让代码更清晰,更好读,更容易维护,能够提供更好的体验,那么让代码更清晰,更好读,更容易维护的目的又是什么呢?让系统容易修改,能够迅速响应变化,那么这是高质量的定义吗?为什么要自己开发不外包给专业的软件公司呢?因为是核心业务?因为他们不了解业务?因为他们经常延期?因为和他们构成一种合同关系,而这种关系不值得信任,双方的目的并不一致?


爱情让张小庆苦恼,工作也让张小庆苦恼。这个周一的上午,办公室的气氛有点怪怪的,项目经理们纷纷和项目总监说些什么,他们一边说还一边向张小庆这边看一眼。张小庆有些心神不宁,觉得什么不好的事情和自己有关,比尔请假了,项目总监叫了张小庆,说,我们周末两天加班的数据丢了。


张小庆第一反应是怎么可能,他说,我需要看看。


项目总监有些责备的说,你快看看,一直是正常的,从周五晚上开始就不正常了,这是一个很严重的问题,相当严重,我一直让他们做备份,但系统一直很稳定,所以他们都没有备份。


张小庆心一沉,周五晚上,靠,自己加班将系统的持久化代码全换成了hibernate,没有告诉比尔,自己本机测试过应该是没有问题的,这下完了。

posted @ 2011-05-29 23:29 ronghao 阅读(2066) | 评论 (4)编辑 收藏

到达公司刚好是9点30分,长安街又交通管制了。开会,部门经理主持,这意味着这个项目团队里要出事情,果然,部门经理宣布项目经理和技术经理的双双离职。张小庆把身体深深陷在椅子里,他看一眼坐在对面的项目经理和技术经理,他们都面无表情,接下来,张小庆的眼光就发散了,周围的一切都模糊起来,只看见部门经理的嘴唇在那里枯燥的一张一合。这是预料中的事情,只是没想到来得这么快,这个项目的第一个发布目标已经失败,和以往一样,一定会有人离开。从巨大的落地玻璃往外看去,对面来福士的楼顶泳池里,一个年轻的女人正在舒缓的仰泳,她是谁?为什么上班时间她会在这里轻松的游泳?她需要工作吗?她此刻在想些什么呢?她有没有遭遇过失败?这似乎是她的第三个来回了,她累吗?泳池旁的小桌上躺着一杯冷饮,好看的玻璃杯子,上面还带一把小伞,看样子很好喝,她喝过吗?

张小庆想起了他一直以来的愿望:写出让很多的人都使用的高质量软件。这个愿望似乎一直都实现的很艰难。他还有一个愿望:帮老婆在北京买套房子,这个愿望此刻也是那么的遥不可及。

这一切都是从什么地方开始的呢,它似乎开始于那扇门,开始于那句话。那个下午,张小庆在那扇门前徘徊了好久,最后才犹豫着敲开它。他被部队退回学校了,这真是丢人的一件事。里面的男人正在看报纸,看见张小庆进来,他放下手中的报纸,有些奇怪的说,你怎么回来了,出差吗。张小庆张了张嘴,他不知道如何回答这个问题,他想起了小时候去老师办公室认错,盯住自己的脚尖,不敢看老师的眼睛,一个微弱的声音像蚊子一样从喉咙中挤出来,队长,我被退回来了。

队长伸了伸身体,说,你把你得病的情况和他们说了?

张小庆张了张嘴,房间里似乎刚刚抽过烟,有些不能呼吸,说,是。

张小庆其实是不想说的,因为一个月的集训还有几天就结束了,但恐惧让他失去了理智。每天两个十公里,晚饭的时候,张小庆惊恐的发现自己开始尿血了,他回到寝室,从被子下哆嗦着摸出藏着的药片,多吃了一片,但是第二天,尿血没有减轻,加重了,一瞬间,面前冰冷的白色小便池扩大了,墙壁,是白色的,灯光,是白色的,连水管,也变成了白色的,这白色化成了白色的床单、白色的被罩和白色的病号服把他包围了,一切都是白色的,这色彩向他微笑着,越来越大,想要吞噬掉他,他不禁打了个冷噤,笑容消失了,面前依旧是冰冷的白色小便池,里面,鲜红的尿液正渐渐淡去。

队长的语气没有任何波澜,说,这就授衔了,你就不能再坚持几天吗?

张小庆没有说话,外边,一辆汽车驶过,激起一阵灰尘。

队长重新拿起了报纸,眼睛停留在张小庆进来之前没看完的一则新闻上,说,你真让我失望。

从队长办公室出来,走廊里空空的,没有一个人,没有一个声响,张小庆一个人站在那里,眼泪立刻就流下来了,他想起了队长的话,你真让我失望。这句话反复在心里撕咬。

张小庆要复员回家了,对张小庆的前景,亲戚们开始讨论。和以往任何一次的家庭会议一样,吃完饭的桌子在那里孤零零的躺着,上面摆满了大大小小各式各样的盘子、碟子和碗筷,热气腾腾消逝了,没吃完的菜上面蒙上一层白霜,一只酒杯倒了,液体滴滴答答的顺着一次性桌布滴下来。大人们在房间里讨论着张小庆的工作,张小庆一个人把盘子往厨房里端。门并没有关严,一丝光线从门缝里泄露出来,似乎有争吵的声音。一会儿讨论过后,气氛就会重新热闹起来,男人们打起麻将,女人们打开电视。张小庆站在那里,等待着自己的命运。高中文理分科是这样,高考填报志愿是这样,大学学习编程也是这样。

门开了,说话的是大姑父,其他人站在他的后边,他的身材很魁梧,说话很洪亮,他说,小庆,我们几个姑父决定一人出一万块钱,二姑父有个关系,把你安排到县图书馆。

张小庆知道大姑父是为自己好,但这次突然就厌恶了这个声音,简直就是憎恶了,从小到大,一直都是这个声音,这个声音一直都是高高在上的,每次都是为自己好,每次都是,没有一次不是。他听到自己说,我不。

大姑父很吃惊,这个一向温顺和听话的孩子竟然第一次说了我不,他以为自己听错了,他说,你说什么?

张小庆说,我不。这次他的声音大了很多。我要自己找工作。张小庆知道,说完这话,有人的心里要松下一口气。

大姑父说,你知道你自己身体情况吗?你的身体就像一扇玻璃,轻轻一推就碎了。

张小庆说,我知道,但我想试着推一推。说这话的时候,他想起了队长的话,你真让我失望。

过完年,张小庆给周扬打了个电话,兄弟,过完年去你那儿。周扬说,来吧。周扬比张小庆小2岁,中专毕业没多久就来北京了,他在西钓鱼台的一家牙厂里当送货员,每天的工作就是吃早饭,取牙齿,做上公交车,穿行在北京的大街小巷,将牙齿交给散落在各处的各个男男女女的牙医们。真正到了北京,到了周扬住的地方,张小庆顺着楼梯往周扬住得地下室走,张小庆不禁说,真深啊。周扬笑了笑,说,这是全地下室,以前的防空洞改的。张小庆说,厕所在哪儿?周扬说,前面拐弯就是,注意,整个一层就这么一个厕所,早上起床要排队。张小庆说,洗脸在哪儿?周扬说,厕所旁边,有两个龙头,一个出水,一个不出水。张小庆往前走了一会,长长的过道里只有一盏半死不活的灯泡发出奄奄一息的光,借着微弱的灯光,他也看清楚了拐弯处的厕所,厕所的门上用油漆大大写着“禁止随地大小便违者罚款”几个大字,门四周,几坨或干枯或新鲜的大便们一脸无辜的蜷缩在一起,与这几个大字默默相对,彼此无言。到了周扬租的不到5平米的小屋,打开灯,张小庆说,你的灯这么暗?周扬说,房东规定了,超过15瓦的灯泡要罚款。

张小庆开始找工作,每天早上,他和周扬一起起床,一起到周扬的工厂里蹭早饭,然后分开,周扬去送货,他则去附近的报刊亭看最新出的北京人才市场报和前程无忧,北京人才市场报便宜一些,5角钱,前程无忧要1块,他于是总站在报亭门口翻看前程无忧,直到老板不耐烦了这才说来份北京人才。这次过来他带了一千块钱,其中四百是火车票钱,他对自己说,如果花完这六百块钱还没找到工作,那就回家。买完报纸,张小庆回到地下室,在那里,他掏出铅笔,把自己感兴趣的招聘会画上圈。

张小庆去了海淀体育馆人才市场,在那里,刚举办完北京国际斯诺克大奖赛,喧嚣散去,只剩下丁俊晖偌大的脸孤零零的飘荡在空中,他来到2楼,几家招工的企业零星散落,几个似乎是企业招聘人的人站在一起抽烟、跺着脚、咒骂着该死的天气还不热起来;张小庆去了海淀人才市场,在那里,他被收了5块钱入场费,入了场他才发现上了当,人员寥寥,剩下的人不是在收拾东西,就是在打个哈欠不停看表;终于,张小庆去了中关村人才市场,在那里,即将举办一年一度的高科技企业招聘专场,张小庆见识了什么叫大场面,人山人海,一波一波的向入口涌去,玻璃门瞬间被挤破了,玻璃散落一地,保安在破口大骂,人们根本站不住脚,有人的简历被挤掉了,有人早上带的豆浆被挤破了,有人在高声抱怨,有人在幸灾乐祸,有人在不顾一切的向前挤,一切都是那么的混乱、无序。张小庆被裹在人流中不由自主的向前涌去,根本没有时间看清楚企业到底要招些什么样的人,最开始他还使劲的踮起脚尖,到后来就只是机械的投简历了。大多数企业都是不冷不热的,扫一眼简历,非计算机专业,没有工作经验,说,回去等通知。也有苛刻的,大声敲着桌子,说,把期望工资写上、写上!张小庆犹豫了片刻,写上了1500元。也有特别热情的,那是培训机构,张小庆留意过,他甚至还去过,但是要七八千块钱,太贵了。

从招聘会出来,张小庆情绪低落,已经是春天,道路两旁的柳树发出绿芽,9点钟,正是上班的时间,路上的人们行色匆匆,没人会注意到这么个人,拿着装有简历的文件夹,穿着印有无数双新鲜脚印的黑皮鞋,一个人默默的在路上走,这个世界不是属于他的。张小庆看见一个穿着黑色西服的年轻人,一手拿着热腾腾的豆浆,一手拿着黄灿灿的鸡蛋灌饼,急匆匆的从他面前经过,他想,这个世界是属于他的;张小庆看见一对年轻的情侣,男人不知说了些什么女人突然大声笑动起来,热烈的从他面前经过,他想,这世界是属于他们的;张小庆看见路旁的肯德基里,一个女孩耳朵里塞着耳机手里拿着汉堡冲着太阳仰起她那张鲜艳的脸来,他想,这个世界是属于她的。

没有工作经验,这是个问题,培训机构太贵,根本不能考虑。没有招聘会的时候,张小庆就去公主坟电子工业出版社的地下书市,在那里,他一页一页的看书,然后将认为重要的内容一笔一画的抄到本子上。那里有个老奶奶,从来都不会催这个只看不买的年轻人,相反,她每次看到张小庆,总会点点头,说,来啦,时间长了,她还会告诉张小庆,这里有凳子,坐着看。

六百块钱花的很快,中午成都小吃西红柿鸡蛋盖饭四块,车费十块左右,招聘会门票一场十块。一次,张小庆错上了一辆空调车,售票员收了他四块,他伤心的眼泪就快流出来。

最后一次面试在亦庄,周扬给他指的路,他到亦庄医院送过货,张小庆面试的公司正好在亦庄医院对面,这是一家翻译公司,张小庆过来做他们的内部管理系统。去亦庄前,张小庆犹豫了很久,因为六百块钱用光了,再用就买不到一张回家的火车票了。我在担心什么?我在害怕什么?张小庆问了自己,担心买不了一张火车票?不是这样,想回家可以借钱的,那又在害怕什么?害怕没有面试通过?不是这样,自己面试通过过吗。他害怕的其实是真正买了火车票回去,那样,自己都会对自己说,你真让我失望。那为什么又不想动剩下的两百块钱呢,因为在他心里,那两百块钱等于了回家,用了那两百块钱就和回家无异了。我不要回家。他实在想象不出自己面对大姑父时的情景,那会是一种什么样的情景,对不起,我错了,我唯一自己做出的选择就错了,我以后会全部听您们的。这不是我想要的,张小庆恶狠狠的对自己说。

面试过了,一个月2500块,下周一开始上班。张小庆回忆这段时间所发生的事情,队长对自己说你真让我失望,这句话让张小庆一直自卑到现在;第一次违背了家庭会议的决议,决定到北京来找工作,尽管说这话的时候并没有太多的底气,但是,这句话却让自己前所未有的痛快;参加各种各样的招聘会,算计着每一块钱的花费,为的是找到工作。那么,为什么要找工作呢,为什么要工作呢?做自己喜欢的事情?不,不是这样。实现自己的理想?不,不是这样。挣很多很多的钱,将来出人头地?不,不是这样。那为什么呢?是为活着,工作首先是让自己能够活着,能够独立的活着,不再花父母的钱为自己买药,不再让父母为自己住院而四处奔波低声下气的借钱,不再听到我们每家出一万块钱帮小庆找份工作。是这样,是为活着,是为有点点尊严的活着。

上班第一天,办完入职手续,人力突然找了张小庆,说,张小庆,我们要你做一次体检。

张小庆一直担心的事终于还是发生了,他感觉自己像一只大大的肥皂泡泡,只需轻轻一扎,就会发出好听的嘭的一声,破掉。妈的,自己想得太简单了,任何公司都会做入职体检的啊,只要化验一下血常规,完了,一切都完了。

posted @ 2011-05-22 23:41 ronghao 阅读(3902) | 评论 (2)编辑 收藏

下午和周筠老师聊天,周筠老师一口气推荐了杨早的三篇文章:我最难忘的一位老师、等待起风的日子和没有空白的年代,言语之间能够看出周筠老师对杨早的特别喜欢。之前也看过杨早的一些文学评论,同样是周筠老师推荐,却没有那么深的感触,因为自己读书太少的缘故,杨早对王朔、王小波、王蒙的评价,因为自己阅读这些作者少或遗忘的缘故,没有太多的共鸣,惭愧。

 

读《我最难忘的一位老师》,我想起了我的高中语文老师,那是一个老头,上课的时候特别喜欢天马行空,他对古诗词是十分精通的,通常讲着讲着课文突然就能来那么一段,本来,长年累月的背诵早让我们对古诗词充满了畏惧,但是,他即兴发挥的一段却总能却中当时的情景,老头是自己背诵,带着感情,甚至有一次,看见泪水从他的眼眶中溢出来。我喜欢这个老头,我的语文其实并不好,一提起拼音就头疼,但是我的作文却总是能被他当做范文在班里阅读,评语同样是非常的简单,就是一个字:好!我是一个喜欢胡思乱想的家伙,一次是情景作文,要对断臂的维纳斯写一篇文章,所有人都写非常遗憾如果有胳膊就完美了,我却讨厌了千遍一律,我写的题目是“缺陷美”,自然,如我意料中的,这篇文章得到了最高的90分,评语还是那个字:好。老头喜欢我,我是知道的,因为想吸引班里一个女生注意的缘故,我经常往报纸杂志投一些很幼稚的稿件,也有能够发表的,每次发表,老头总能下课后找到我,指着杂志上的名字问我:这是你吗?我点点头,老头点点头笑笑,却不说话。

 

我们毕业后老头就退休了,他年龄大了。一直想着去看他,却犹豫再三,我是自卑的,高中前两年除了语文和数学其他科一直都不是很好,高三成绩好容易好起来高考却失败了,在父母的安排下读了军校,上了半年学却大病一场,几乎要了我的命,一直依靠药物维持着。这样,当同学约好一起去看老师时,我犹豫了,与他们相比,我是自卑的,他们上着这样和那样的学校、在这样和那样的公司,我却要为活着而活着,我是如此的卑微,我想,也许等我有一天出人头地了,能够再去看他,我是如此的固执,固执的自卑着,我想,如果不能做些成绩回去,真对不起他给的那些评语“好”。后来,我听同学说,他问了我,他是盼望我去的,但是,我让他失望了,其实,这个失望又何尝不是对自己呢。于是,一直都没去,直到现在。

 

我想,如果再回家,一定要去看看老头,那怕自己什么都不是,那又有什么呢。

 

读《等待起风的日子》,里面这段话自己非常欣赏:你真的想这样做吗?不是为了赢得别人的赞誉,不是为了博得进步的资本,真的想这样做?

 

 想。

 

 那就去做吧。

 

非常契合我现在的状态。因为张小庆的缘故,暂时离职了,在家里。周围的亲戚朋友说,不要太冲动,再好好想想。

 

高一分班,想读文科,考中文系。周围的亲戚朋友说,不要太冲动,再好好想想。于是,选择了理科。

 

高考失败,想着重考一年,周围的亲戚朋友说,不要太冲动,再好好想想。于是,读了他们安排好的学校。

 

大学的时候,读书,写字,想找份新闻出版相关的工作,周围的亲戚朋友说,不要太冲动,再好好想想。于是,自学了编程。

 

同样是在大学的时候,被高中一直喜欢着自己的女孩追到毕业,这次是自己对自己说,不要太冲动,再好好想想,自己现在什么都不是,身体还不好。于是,一再拒绝人家。

 

大学毕业,想去北京,周围的亲戚朋友说,不要太冲动,再好好想想。这次,没有听他们的意见进一个事业单位,于是,来了北京。

 

我总是很容易被周围的人所影响,不断的摇摆、不断的蹉跎,慢慢就迷失了自己,别人在追求着什么东西,哦,那也应该是自己追求的,于是,岁月就在不断的遗憾中逝去。我在追求什么?我是在追求自己的快乐,是自己的,不是别人认为的。每个人都在追求快乐,但是,快乐是需要自己定义的。

 

这次,请让我以杨早的话作为结尾:好吧,朋友,感谢你的关心。可是风已经起了,我要开始我的历程了。麻烦你把那个水瓶递给我。然后,请让我静一静,让我怀抱着我的梦想,开始我的翱翔。

 

为什么想表达,在《没有空白的年代》里,有这么一段话:粗分一下,这本集子里所讲的不过两个方面:一是一个身处世纪末的中国南方的青年如何与社会的各种影响,与自身的平庸抗争的心路历程。这在旁人看来自是平淡无奇,但在我的回眸里,却相当惊心动魄,我曾多少次在文字的迷阵里试图抓牢自己漂浮的灵魂,感谢上苍,在我的二十四岁,我终于取得了一点儿成功,但愿前面的路能好走一些。二是这个青年正在努力介入社会生活,对他感兴趣的东西发出自己的声音。他的执着在某些人看来是可笑的,特别是在南方,关心这些没什么油水的事的人不多。但是他相信保持对社会和文化的关念是这个社会的知识分子的职责,他愿意做这里面的一员。

 

这正是自己想表达的东西:一是一个生于八十年代的青年的心路变化,在之前大部分时候,他是懦弱的,他是摇摆的,他是被动的,现在,他在努力工作支付按揭贷款的同时,正在努力构建自己的精神世界,他原先的世界观在接触这个现实世界的一瞬间崩塌了,然后一直都没有重建。二是这个青年对这个社会是抱有希望的,最开始,他是相信一个人能改变世界的,现在,也许我们不能改变世界,但我们能改变自己,坚信只要做好自己应该做好的事情,就会改变。

 

再说说小说的提纲,小说现在分为两条线,一条是张小庆现在的生活,一条是张小庆对过去的回忆。回忆从小学开始一直到周扬离开,中间的主要事件在上面已经做了说明,表现张小庆的摇摆,为他人和社会所影响,世界观从最开始的感觉生活在巨大幸福之中到怀疑到沮丧到被动接受到希望改变,人生目标也从科学家到为共产主义奋斗到出国留学到先生存下来到做一些喜欢的觉得有意义的事情。现在的生活从早上的930开始,到晚上下班回家,描述一天的工作,侧重点会放在对两个项目的经历和思考上,和他周围的人一样,他追求卓越和代码质量,他认为这是他的本质工作,当你糊弄自己的工作时,那和掺三聚氰胺和染色馒头一样没有区别。

 

最后,谢谢周筠老师,谢谢杨早,谢谢他的文字,我会更多阅读他。

posted @ 2011-05-13 22:47 ronghao 阅读(1840) | 评论 (2)编辑 收藏

早上六点半的时候,张小庆起床了。天还没有亮,张小庆打了个大大的哈欠,其实闹钟定的是六点二十,每隔十分钟再响一次,张小庆特别享受六点二十到六点三十这段时光,这是他睡得最幸福的时刻。王晓丽和儿子还在熟睡,他亲了儿子一下,然后洗漱、穿衣服、将馒头在锅里蒸上、将稀饭煮开调成微火,走得时候,他拿了一本书,对王晓丽说,我走了,馒头蒸好了,稀饭还在锅里。王晓丽没有睁开眼睛,恩了一声。

走出单元门,天刚刚蒙蒙亮,和往常一样,这不是一个好天气,但是,按照某个标准,这又是一个好天气。张小庆这些天一直被一个问题困扰着,这个问题如此扰乱着他,让他时而激动不已,时而又头疼不已,这个问题已经困扰他很长一段时间了,却找不到答案。

有两种方式去北京,一种是拼车,一种是930。其实曾经有过第三种方式:小区班车。但第三种方式在发改委与国际接轨的过程中取消了,值得兴庆的是,是小区班车不是小区班机。倒数第二次乘坐班车的时候,一个结实的中年人发了传单:我们要抗议开发商违背承诺,取消班车。传单把全车老老少少男男女女们的情绪点燃了,气氛显得热烈而又群愤激昂,时间定在第二天的晚上,地点在开发商开发的大酒店。中年人甚至给司机说好了明天晚上到大酒店停一下,司机笑了下,说,支持。于是,第二天司机就拐了个弯把车停在了大酒店,却没有几个人挪动昨天群愤激昂的屁股,中年人说,兄弟们,争取我们自己利益的时刻到了。车厢里很安静,工作了一天,人们都太累了睡着了。中年人说,这是最后一天了,没有机会了。有人被吵醒了,把头扭向了车窗外。中年人和空气僵持在那里,终于,有人说话了,低低的,明天还要上班呢,太晚了。有人附和,是啊,明天还要上班呢。明天是星期六。

再过一会儿,空气不安起来。

有人说,没用的,开发商是黑社会。

有人说,930其实也不错,加车了。

于是,中年人只好下了车,张小庆默默的跟在他的屁股后面,也下了车,原来群愤激昂是靠不住的。在后面走得时候,张小庆望着中年人的背影,突然就想起了三哥哥,那个在童年里带着他走得三哥哥,他们的背影是如此的相像。

老板没有出现,出现的是市场总监,一个强悍的更年期女人,后面跟着两个帅气的保镖。女人说,没得谈。果然很像黑社会的女人。

中年人说,你们当初是怎么承诺我们的,那么大的广告牌写着社区班车30分钟直达国贸,为什么说取消就取消?

女人不耐烦的说,你没看完。

张小庆跑到门外,那个大大的广告牌还耸立在那里,四周的射灯照得社区班车四个字耀耀生辉。在右下角,一个不起眼的角落处,张小庆看到了这么一行字,这行字很小,几乎淹没在阴影里,不注意的话根本看不见,这行字是:最终解释权归开发商所有。真是个大杀器,制定好游戏规则,白字黑字的印出来,贴得到处都是,最后却是解释权归他们所有,也就是他们可以不遵守游戏规则,规则都是制定给别人遵守的。这一瞬间,张小庆想起了“威震天”,那个下午,威震天走进教室,脸上还淌着鲜血,说,别担心,现在是属于他们的,但未来是属于我们的。但事实是,现在还是属于他们的。

警察以前所未有的速度出了警,来了很多警察,开始对每个抗议的人一个接一个的录像。双方僵持在那里,女人说,两个方案,一个是取消,一个是每个月从200涨到400块钱,其他,没得谈。再一次上班的时候,张小庆在小区门口的电线杆上看到了班车恢复的通知,它就那样贴在那里,和治疗梅毒、阳痿的小广告们并排挤在一起,上面写着:为积极响应政府关于绿色出行的号召,鼓励大家乘坐公交车,经过和业主的积极协商,同时考虑到业主们的实际困难,经研究决定恢复班车,价格,399元一月。

拼车是不坏的选择,10块,从小区门口到国贸地铁站。最开始是QQ、富康和比亚迪,后来现代和雪福来也加入进来,再后来,张小庆甚至有一次做了回奥迪。也偶尔有不要钱的,说同一个小区顺道带了,但坐那车需要勇气,因为当得知不要钱的时候,怎么看那个司机怎么都不像个好人,如果心脏不好,很可能就就因为这10块钱挂在路上了,得不偿失。

拼车能碰到各种各样的人。有一次,一个女人开车,开得飞快,不停的变线,从国贸下车的时候,张小庆发现自己的后背汗湿了,想,再也不要坐女人开的车;有一次,QQ,塞下5个大男人,司机以你们都是80后吧,我也是作为开场白,聊起自己的大爱经历,说公司刚来的一个90后妹妹真开放,真嫩,说北京有哪些比较有档次的迪厅妞,说自己经常开车去二外,那里只要停车就有学生敲你的车窗,说这话的时候,屁股底下的QQ剧烈晃动不好意思起来,一超过70码,它就开始替它的主人不好意思;有一次,聊到最近最火的艺术家,前面的司机缓缓回了一句,我就是那个推手,这是我的名片,说以前只要说是艺术就会火,后来需要将学生和艺术这两个职业叠加起来才能火,现在则必须首先要是名牌大学的,越来越难啊,人民群众重口味了;还有一次,聊到了少先队,两个人争起来,究竟有没有五道杆,张小庆突然想起了黄晶晶,那个小学时代的三道杆大队长,当时她是那么的英姿飒爽,自己又是那么的迷恋。

几乎所有的司机都会在拥挤的道路上打开“一路畅通”,那感觉,就像一个坐在马桶上的便秘老男人憋红了脸旁边却在泉水叮咚,主持人说,今天路况还是不错的,西二环和北二环压力有些大,南二环和东二环参照西二环和北二环,三环参照二环、四环参照三环,五环参照四环,恩,六环还是相当不错的,但是大家也不要都往六环上挤,因为听我们广播的人很多,如果都去的话,很快就会挤起来。所有的司机听到这里都会着急上火的占用应急车道,张小庆很想提醒一下这是应急车道,但是他张不开嘴。他总是在犹豫,在担心,小学时是这样,中学时是这样,大学时是这样,现在,也是这样,他需要一个人推着他走,领着他走,杨玲曾经叹过气,说,你这个人啊,就是太担心,担心什么呢,都是自己担心出来的,我就和你不一样,起码我爱过你了。但是现在,必须他自己做出决定了。终于一次,一个司机没有占用应急车道,张小庆感到一阵欣慰,谁知司机摇下车窗,一口浓痰吐到路上,说,这帮傻比,刚过去的地方前两天刚装了摄像头都不知道,罚死这帮孙子,哈哈。说完这话,一把轮,拐回到应急车道上。

今天的930来得比较准时,等车的人不多,能上车,不用等下一辆。这是不常见的,常见的是又堵在北京了,半天才来一辆,人们潮水般的涌过去,这时候,张小庆是不去挤的,他不想去争些什么,从来都是这样,他站在一旁,等所有的人都上去,自己最后上,然后司机关门,把自己的脸贴在玻璃门上,挺好,在乎的不是目的地,而是沿途的风景。在车上站定,张小庆打开手中的书,路上四个小时,只有这一个半小时可以不被浪费,但是今天,他看不下去,他得做出决定。窗外,一地瓦砾,一栋危楼上一面五星红旗在迎风飘荡,巨大的标语:二次强拆,楼毁人伤,朗朗乾坤,公理何在。他们还在这里,张小庆想。自从去年春天,他们就屹立在这里,春天过去了,夏天过去了,秋天过去了,冬天过去了,一年过去了,这次春天是否会来呢?

车到开发区的时候,上来一个老太太。车上热烈的气氛顿时消逝了,车厢里再次变得安静,要开始一天的工作,人们都太累了睡着了,有人低声说了一句操,好像老太太会杀死他们。没有售票员,司机喊了一声,谁给老人让个座,没有人回答。张小庆的心脏突然剧烈跳动起来,要不要叫那个坐黄座的年轻眼镜让个座?他犹豫了,他担心别人讨厌他,担心给别人带来麻烦,所以他总是不去争什么,车上有人开了窗,吹着自己很冷,总是先忍着,想别人一定很热,等到实在忍无可忍时才小心翼翼的问别人是否可以将窗户关小一点,这就是他,一直都是这样。这次,憋了很久,他终于对着睡着的眼镜小心翼翼的说能否给老人让个座,果然,眼镜睡得很死,他不得不再小心翼翼了一次,这下全车人都听见了小心翼翼,有人幸灾乐祸的笑了一下,眼镜的眼镜后面泛过一道寒光,那一瞬间,像极了柯南,嘟嘟噜噜的站起来,从张小庆身边经过的时候就说了一句傻比她是你娘吗。张小庆的脸唰的红了,一种被侮辱的感觉传遍全身,这种感觉其实在他生命中经常存在,他一边被这种感觉侮辱着,一边一声不吭沉默不语。他想起了周扬,如果是他,他一定会淡然的笑一笑,脸上挂上一种满不在乎的神情。

车过酒厂,迎面驶过来一个绿色的大牌子,上书:北京界。不远处,一列火车迎面开来,和谐号,速度快,让人数不清有多少节。王晓丽嘱咐今天要在北京给孩子买鱼肝油,其实燕郊也有,比北京便宜很多,但是王晓丽从不放心:北京的东西才是真的,一定要去北京买。张小庆说,都有防伪标签的,北京还贵。王晓丽说,为了孩子,你这几个钱都舍不得花吗,你怎么当爹的?于是当爹的就闭了嘴,他想,这根本就是两码事嘛。王晓丽喜欢北京,她的梦想就是要在北京买套房子,她的这个梦想像个美丽的肥皂泡,经常变幻出美丽的颜色。其实不止是她,王碧薇、高晨晨、张雨和陈华,张小庆所认识的女人里面,都是喜欢北京的,她们中间有的有北京户口,有的没有,有的一个月能拿到七八千块,有的只能拿到一千块,有的用着几千块钱一套的雅诗兰黛,有的用着几块钱一瓶的大宝,但是她们对北京的热爱却是相通的,她们爱着此刻930正在奔跑着的地方。张小庆想起了王阳,想起了余鹏,想起了张川低低的哭泣,王阳说,不要给我提北京,我痛恨那个城市,余鹏从不说什么,但是一提起北京,他就转身离开。

太阳升起来了,照在张小庆的书上,有些晃眼睛,一页都没有看下去。他回到他的问题上来了,这个问题表面上是个选择题,选A还是选B,似乎都没有那么简单,那么该怎么办。在这个时候,张小庆想起三哥哥来了,如果是他,他会怎么选择。

posted @ 2011-05-08 23:29 ronghao 阅读(2409) | 评论 (5)编辑 收藏

第一卷还有两章就写完了,框架已经搭好,很快就会写完,但是,却写不下去了。第一卷是失败的,从写作技巧上,可以列出它很多失败的地方:没有一个一致的框架、线索,没有冲突、过于迷恋细节、太多无关的人物,主要人物经历片段化。写到第五章的时候,父亲说,你写得太平淡了,这是第一个向我提意见的人,接下来米高在开心上提到张力不够的事情。但当时的我还沉浸在生活就是如此平淡的迷梦中,终于到了第九章,在微博上,一个朋友猛烈的抨击了我:越来越无聊了,像流水账,毫无可读性可言。妹妹也批评了我:王碧薇会喜欢上张小庆,张小庆何德何能啊!把主角写成如此不堪,我是如此的不堪。于是,开始痛苦的思考和转型,第一件事是列剩下章节的提纲,以一条主线统一组织内容,制造矛盾和冲突,老舍说,以爱情来组织故事总是最容易的一件事,于是,我就偷了这个懒,另外,在妹妹的提议下,加入多角度的描述以突出人物。但是,我越来越感觉到不对劲,这(爱情)是我想表达的东西吗?不是啊!

我想表达什么,或者说我为什么想写张小庆?写张小庆是因为一件事触动了我,这件事是年前周扬告诉我要回家创业了,周扬是我的朋友,比我小两岁,85年的,中专毕业,在北京一家牙厂里当送货员,住的是地下室。一年过年回家的时候,还是送货员的他告诉我,他一定要自己开个牙厂,尽管是他的朋友,但其实我内心还是不太看得起他的,年龄小,学历不高,挣得不多,最开始还喜欢玩网游,处于社会的最底层。但接下来的事情却让我吃惊,做了两年送货员,第三年他开始和老板一起跑业务,第四年开始学做烤瓷,第五年学做牙冠,好几个过年都没有回家,现在,突然就要创业了。其实第五年他的工资就已经过了万元,我曾经问他,在燕郊买个房子吧,和我住一起。他笑笑,说,不,那不是我想要的。从一开始,他就有一个坚定的想法,就是在家做些事,和父母家人在一起,一直没变。在他的心中,北京只是个驿站,路途的一个临时停靠点而已,他的家一直在生他的地方。

看看他,再看看自己。在下东直门地铁站的时候,我问自己,我在追求什么,天天做地铁再换公交车,每天路上四个小时,天天忙忙碌碌,我到底在追求什么。问自己这个问题的时候,我愣住了,因为这真是他妈的一个好问题,因为我根本不知道该怎么回答。追求什么,追求当苹果新产品问世时,第一时间知道,第一时间拿到手吗;追求什么,追求当有好莱坞大片上映,第一时间知道,第一时间去观影吗;追求什么,追求能够用上最好最新的互联网产品,刷微薄,然后不断转帖吗;追求什么,追求,看,我们从不喝国产奶粉,都是从外代购吗;追求什么,追求过年回家时,面对那些在家的同学所具有的所谓的一点点优越感吗;追求什么,追求钱吗,那为什么总是爱说,等我有钱了,那就什么什么的呢。我在追求什么?!海贼王里说得很明白,金钱、地位和荣誉,是每个男人都追求的东西,真是这样吗,我怀疑,起码我怀疑自己。

这个社会不是一个理想的社会,这点我毫不怀疑。大学时,听到911事件的新闻,全班同学一起发出兴奋的狂呼,现在想想,冷汗直流,为什么会这样。李刚儿子遭到那么多人的咒骂,我相信,很多人其实是在咒骂为什么自己的爸爸不叫李刚。不管是好还是坏,总有人在咒骂,在他们眼里,所有支持这个政府的人都是五毛,所有批评这个政府的人都是愤青,非黑即白。还有人,听到油价上涨,一阵狂喜,涨死那帮买了车的,可知道,自己吃的东西用的东西都是用车运来的。拼车时,一边抱怨交通不好,一边占用应急车道;等车时,一边抱怨交通不好,一边不管别人死活的插队;在家时,一边抱怨城市关系落寞,一边根本不和对门刚出来的人打招呼;做馒头时,一边大骂双汇,一边给馒头染色。很遗憾,这里面都有自己的影子。都是别人的事情,一说都是社会的错,耸耸肩,无可奈何,自己一点点责任没有,移民吧,赶快的。所以,我想,张小庆应该不是这样,他应该是抱有希望的,尽管很渺小,但是尽力做好自己的事情,坚信只好做好自己应该做的事就一定能够慢慢改变。

周围的好多人和事。哥哥和嫂子总是打架,嫂子是大学毕业,哥哥是中专,嫂子是不甘心,为什么周围的女人们都这么热爱这座城市,想留在这里,想生活在这里,哪怕这个城市并不热爱她们。朋友因为分手和前男友打房子的官司,房子,如此畸形的影响生活。朋友的老婆身体有病,一直在休养,朋友就这样知道她身体不好的情况下和她结了婚、租房、生活,工资不高,一起在北京生活,他们又在追求着什么。朋友是北京的女孩,腿有残疾,家境很好,有车又有房,但一直在奋斗,为学日语,六个月天天学到凌晨2点,现在是一家外企的技术经理,一个女孩子,她又在追求什么,她曾经在路边跌倒过,很多很多的人经过,没有人去扶她一把,她只好自己挪到树边,自己挣扎的站起来,当时,她是一种什么样的心境。朋友一直在家做老师,教学水平却并不好,管不住学生,同学们谈起他总会带一种不是有意但是不知不觉的轻蔑,他是怎么想的。过年回到家,老人之间谈论最多的却是钱,谁谁在什么地方一个月多少钱,谁谁嫁给了如何如何有钱的老公,突然周围全是有钱人,此外,小县城突然也开始堵车了,这是以前从来没有过的事情,都是外地打工回来的人们,开车回来,他们像一群候鸟,几天后就飞走了。高中的同学,父母在当官的,很多重新回来,接了父亲的班。

至于软件开发,我也是有疑惑的。我们到底为什么在做软件?为什么我们软件用户只有政府和国企,中小企业都到哪里去了,他们在用什么软件?为什么很多时候,软件只是个花架子,我还记得,当客户单位更换领导取消我们项目(但付款)时,我们发出的欢呼。为什么我们一边把自己的软件加密再加密,一边却在网上跪求破解码?为什么产品开发最后会失败,明明只剩下最后一公里了,却迈不过去?为什么要重写软件,为什么不能打补丁,只是因为觉得代码丑陋?为什么看似是大公司接的项目,最后却是层层外包,还是我们小公司来做,资质只是做投标用的吗?为什么现在看东西越来越浮躁了,没有内容,只剩下标题,为什么只看了一篇报道就下了判断?

这就是我想写张小庆的原因,我想表达出周围人们的生存状态,表达出自己的疑惑,尽管还没有答案。所以,突然就想重写了,重写总是比重构来得容易。会抛掉所有无关的细节,以一条张小庆的视角串起来所有的故事,重新组织。所以,对不起了,所有关注张小庆的朋友,这个礼拜会开始重写,周末的时候会发出重写后的第一章,抱歉。

第一卷写得不好,但是张小庆说,今天比昨天好,明天比今天好,这就是希望。我是有希望的。

posted @ 2011-05-03 23:04 ronghao 阅读(2574) | 评论 (8)编辑 收藏

晚上的时候,张小庆突然收到了王碧薇的短信,问他有没有时间,能不能陪她散散步。这条短信让张小庆很意外,他说,什么时间,在什么地方。王碧薇说,就是现在,在活动中心的门口。张小庆说,好。


    汤姆在桌子上敲代码,看到张小庆出去,说,又去约会?


    张小庆说,没有,出去买点东西。


    汤姆笑了笑,说,别骗人了,买东西还需要照个镜子?


    张小庆说,真是买东西。说完这句话自己都觉得很无力,无奈的看看汤姆,两个男人相视一笑。


    张小庆和王碧薇在亦庄活动中心的门口见面了。一路上张小庆走得有点快,这让他的心脏跳得有些快,这样,当他远远看见王碧薇的时候,他停了下来,慢慢平息一下心情,然后才以正常的步子走过去。活动中心的门口有几级台阶,王碧薇站在最高的台阶上等张小庆,天气很热,她穿了件白色体恤,下面是一条碎花裙子,露出匀称的小腿,一双高跟凉拖,露出高高的脚倮,她低着头,在台阶上来回踱着步子。看到张小庆,她停下来,说,吃饭了吗,没有打扰到你吧。


    张小庆笑起来,他觉得王碧薇这身衣服穿得很漂亮,说,吃过了,反正我晚上也没什么事。


    王碧薇说,请你吃雪糕吧。


    张小庆说,好啊。


    于是,王碧薇给张小庆买了根他要求的北冰洋双棒,轮到自己时,来了瓶矿泉水。张小庆问,你不吃雪糕吗?王碧薇说,我怕长胖。张小庆说,你一点都不胖啊,甚至,有点瘦。张小庆的回答让王碧薇恢复了以前活泼的神气,说,哈哈,我最喜欢听的就是这句话了。


    尽管之前很多次的和王碧薇见面,也有独处的时候,但这次还是带给张小庆一些不一样的感觉,至于是什么感觉,却又说不出来。两个人开始绕着星岛嘉园慢慢的走,路上有很多行人,那是些老头和老太太,他们去活动中心旁边的广场上跳舞。王碧薇说了自己,她比张小庆早毕业3年,一毕业就来到北京,一直做得就是翻译工作,自己现在最大的愿望就是能在北京有个自己的家,不再租房,不再东搬西搬。她抬头看看小区里的点点灯火,一个窗口里,是一个男人和女人一起做饭的身影。说完自己,王碧薇问张小庆,你的理想是什么。


    张小庆说,写代码,写自己喜欢的代码。


    王碧薇说,就只是写代码吗,你不是一直说要去IBM和微软吗?


    张小庆说,那是因为我现在认为在IBM和微软能写自己喜欢的代码。


    王碧薇说,你会考虑一直在北京吗?


    张小庆说,当然,这里有我的希望。


    王碧薇说,你的希望?


    张小庆说,是啊,今天比昨天好,明天比今天好,这就是希望。


    王碧薇说,我想做好好做现在的工作,我想做项目经理。


    张小庆说,我不想做项目经理,我的职业规划是做个好程序员,一直写代码到老。


    两个人不知道绕着小区转了几圈,他们聊了很多,聊到公司里的同事,聊到自己上学甚至小时候的事情,张小庆说到了李静,说到高二时一次从未和他说过话的李静突然回过头来向他借一本高一的代数书,他急急忙忙的从高高的教学楼里跑下,奔向自己的寝室,翻箱倒柜的找,没有找到,于是到其他寝室里去借,好容易借到,然后再心急火燎的跑回教学楼,奔上高高的教室,临到门口,心跳的厉害,大口的深呼吸几次,这才进了教室,把书平静的递给李静。王碧薇哈哈大笑,说,当时她说什么了吗?


    张小庆说,她说我找了很久吧。

  

    王碧薇说,你怎么回答的呢?


    张小庆说,我说没有,就在床边,顺手拿来,easy。


    王碧薇笑得止不住,说,现在她在做什么呢?你没有联系吗?


    张小庆摇摇头,说,没有联系,她现在在西安交大念研究生,我们之间的差距太大了。张小庆说到了高三的时候,一次摸底考试成绩下来后,看到成绩,自己在本子上画了个高高的李静,旁边是蚂蚁般渺小的自己,无数的箭头从蚂蚁身上划过。王碧薇说,你想太多了,女孩子是很好追的。张小庆说,整个高中我都没和她说过几句话,她也不知道我喜欢她。王碧薇说,相信我,她一定知道,说不定那次向你借书就是想和你说说话呢。末了,王碧薇突然补了一句,你现在还喜欢她吗?对这个问题,张小庆不知道该如何回答,他只是说,5年没有见面了,不知道了。王碧薇哦了一声。张小庆说,你呢?


    天色已经很晚了,周围都安静下来,偌大的广场上早没了人影,小区也已经睡着了,只剩下橘红色的路灯孤寂的发出滋滋的声响,空气中开始弥漫着一股凉气,有点冷了。王碧薇在一个路边的靠椅上坐下,说起了她现在的男朋友,张小庆在她旁边站着,由于光线的缘故,他只能看到她长长眼睫毛投下的阴影,看不见她的眼睛。王碧薇说,他是她大学的同学,大学毕业后一起来到北京,工作了半年就考了研,现在还在读研。张小庆说,挺好的,读个研究生挺好的。这话说得酸酸的。王碧薇说,不是你想的那个样子,他其实是想逃避,他不喜欢北京,他想回成都,为此我和他吵过好几架,但是没有效果,他很坚决。张小庆说,成都也挺好的。王碧薇说,可是我喜欢北京,我在这里生活了3年,我喜欢这里,第一年工作给他的打击很大,他只想着离开,他为什么就不理解我呢。说到这里,王碧薇的声音有些哽咽,可以想到,她长长眼睫毛的阴影下,可怜的眼泪一定流下来了,这不是张小庆所认识的王碧薇,有一段时间,他站在那里,看着面前这个漂亮女孩的肩膀随着情绪一动一动,他却束手无策,不知道怎么办才好,他想递给她一个纸巾,却没有带,他想去搂住这个可怜的姑娘,但却喉咙发干,挪不动步子。时间就这样一分一秒的过去,慢慢的,王碧薇的情绪平静下来,两个人都没有说话,又待了一会,她站起来,擦擦眼睛,说,对不起,我没控制住情绪。张小庆说,我陪你回去吧。一路无言,走到王碧薇住的楼下的时候,张小庆说,没事的,明天一定比今天好,不要放弃希望。王碧薇说,恩,好,再见。


    回到家里,汤姆已经睡了。张小庆却无法平复自己此刻的心情,一躺到床上,就浮现出王碧薇肩膀随着情绪抽动的情景,真是个可怜的女人。心中便生出许多爱怜来。


    张小庆的夏天突然多彩起来,给他的夏天涂上色彩的是王碧薇。每个晚上,吃过晚饭,洗过澡,张小庆给王碧薇发过短信,老地方见!生活多了期盼,便多了生气。大多数的时候,他们沿着活动中心的广场慢慢的走,有些时候,他们也会走远一些,绕着贵园北里走。他们说到了博客,张小庆说到博客中国刚进行了第一轮融资,方东兴正是意气风发的时候,自己也刚刚开了一个博客。王碧薇访问了张小庆的博客,说,每天记录一些东西,真好,你都什么时间写博客。张小庆红了脸,小声的说,上班时间。王碧薇哈哈大笑,说,背着比尔吧。张小庆说,没有,是比尔鼓励我开的,因为我家里没有电脑。他允许我上班时间写博客,不过要是和技术相关的,他说他也在看。张小庆说到了自己给牛人发信问问题的事情,说都石沉大海没有回应。王碧薇说,牛人们不屑回答吗。张小庆说,开始我也不理解,但是想想,和我一样给他发信的菜鸟一定很多,他一定是处理不过来。他们说到了房价,王碧薇说政府正在对快速上涨的房价进行第一次调控。张小庆说我也听到了,那次从首图回来在公交车上听到的,广播里说最近一段时间办理二手房过户手续的人激增。他们说到了篮球,每个中午,比尔都会喊上张小庆去楼下的球场打球,王碧薇和张丽站在远远的树荫下看。王碧薇说,你知道我们公司最帅的男人是谁吗?张小庆说,不知道。王碧薇说,每次你都说不知道,你就不能猜一次吗?张小庆说,猜不到。王碧薇叹口气,说,是比尔!说完,和张小庆一起哈哈大笑。


    夏天移动的很快,因为王碧薇。8月一个星期天的傍晚,王碧薇给张小庆发了消息:去超市吗,我们一起去美廉美吧。早上的时候,汤姆出去了,张小庆在汤姆的机器上写代码。张小庆说,好几个星期了,你每个周末都出去,约会了吧。


    汤姆说,我哪像你啊,我是去我的同学那里。


    张小庆笑了笑,说,别骗人了,男人见面还需要照个镜子?


    汤姆说,真是见同学。说完这句话两个男人都想起来什么,这对话是那么的熟悉,只不过是换了个位置而已,于是两个人都笑起来。


    美廉美在天宝南街,两个人在小区门口见面,一起坐班车过去。张小庆不得不承认,美廉美和小白羊是两种风格完全不同的超市。两个人在美廉美吃了饭,王碧薇要了两份卤煮,张小庆没有吃过卤煮,王碧薇说,很好吃的,老北京特色,我特别喜欢吃。端上来,黏黏的,火烧、豆腐、小肠和肺头掺杂在一起,上面生长着些绿意葱葱的韭菜花。王碧薇说,这是补心肝的,快吃。张小庆却一下子适应不了这个味,皱着眉头,想咽咽不下,想吐对面坐着的却是王碧薇,于是就犯了难,含在嘴里,半天不动。最后还是王碧薇解了围,哈哈笑起来,说,我第一次也是吃不下,吐了吧,吐了吧。


    两个人在超市逛了很久,王碧薇买了很多东西,其中有瓶六神的沐浴露,张小庆想,原来她身上好闻的沐浴露味是六神的啊。从超市出来,已经8点半了,班车停了,于是两个人只好步行回家,天气有些阴,走到天宝中街的时候,突然下起雨来,两个人跑到一家临街的小商店里躲起来。雨越下越大,两个人站在小店的窗户前向外望,雨点打在窗户上,窗户渐渐模糊起来,映射出两个人的身影。王碧薇着急起来,过一会就让张小庆出去看看雨是不是停了,张小庆走到门外,把手伸到屋檐外,心里说,不要停,不要停,嘴里说,小了些,但是走不了。一会儿,王碧薇也走出来,两个人并排站着,把手都伸出去,两只手并排靠在一起,一只大一些,一只小一些,有个雨点刚好落在两个手心的中间,散开来,溅在两个人的手心里,凉滋滋的,背后,小店温暖的灯光从玻璃里挤出来,洒在两个人的身上,张小庆突然就想抓住旁边这只纤细的手,然后,慢慢的走。路上开始弥漫起水气味,不远处,体育中心的草坪刚刚剪过,水气味夹杂着青草的清香,真是个美好的夏天晚上。


    尽管张小庆特别想这个夏天过得更长一点,但是,夏天终究还是要过去了。

posted @ 2011-04-27 17:30 ronghao 阅读(1971) | 评论 (2)编辑 收藏

张小庆连忙往回走了几步,他怕王碧薇也看见自己了,在报刊亭旁,他站下来,拿起一本书,装着看书,然后看着王碧薇和那个男人从不远处走过去。他的心就那么慢慢落寞了,他应该是她的男朋友吧,他想,他想了小时候,周日的傍晚,和伙伴们在草地上玩耍,不知不觉中天就慢慢暗下来,再过一会儿,每个人都要回家了,明天还要上学,这样想着,心情就跟着天色一起伤感起来。直到王碧薇和那男人拐进小区,看不见了好久,张小庆这才放下手中始终停滞在同一页的杂志,慢慢的也往家走。也许不是男朋友呢,张小庆对自己说,也许是表哥老乡什么的。但他很快又否定了自己的想法,王语嫣和慕容复不也是表哥妹关系吗。突然间,他就觉得五一假太长了,还有好几天才上班,他恨不得明天就上班,这样,早上的时候就能问问王碧薇,问什么呢,问那是不是她的男朋友?这也太唐突了吧。


余鹏、王碧薇,这两种情感不停交织在一起,最后,张小庆打开了空洞毫无颜色的房门。迎接他的是热烈的汤姆的方便面味,这次是经典的红烧牛肉,刚煮开,康师傅在锅中沸腾,此刻,汤姆正盘腿坐在床上,腿上放着那本C++的编程书,旁边桌子上,他新买的笔记本里,伍思凯正在深情的歌唱特别的爱给特别的你,再旁边,是他无比熟悉的方便面缸子。看见张小庆回来,汤姆笑着说,可以啊,小子,天天往外跑,和小姑娘约会去了吧。


张小庆说,哪有,你给我介绍一个?


汤姆看出张小庆的情绪不高,他说,怎么,被抛弃了?


张小庆岔开话题,说,你这几天一直在家里?


汤姆说,是啊,看书,写代码。对了,隔壁住着两个姑娘,很漂亮。


张小庆说,你有兴趣?


汤姆说,你不叫我给你介绍一个吗?


张小庆说,那你给我介绍一下吧。


汤姆说,我不认识,我只是看见了。


张小庆说,你观察的倒很仔细啊,我根本就不知道。


汤姆说,我们楼上还住着一对年轻的情侣呢,每个周六的晚上11点多钟的时候都要折腾很长时间。说到这里,他神秘的笑起来,张小庆也感染的笑起来,他小声的问,声音大不大?


汤姆说,还行,女的声音不是很大,就是床老响。说完这句,两个人将刚才的笑放肆起来,笑声肆无忌惮的弹在墙上,甚至能够感觉到墙壁因此而微微的颤抖。张小庆说,所以一定要买个好床,走,我们去隔壁串串门。


汤姆说,你去,我不去,我们70后都很内敛的。张小庆想,内敛个屁,木子美不就是70后的杰出代表吗。80后故意刺激70后,说,这都不敢?


70后说,你敢?


80后看出了70后内心的渴望,于是,他和70后打了赌,如果他能和隔壁的姑娘搭上讪那么70后就请他吃晚饭,不行的话他就请70后吃晚饭。接下来,80后迅速跑下了楼,他去小卖店买了西瓜,不大不小,刚好四个人吃饱,再迅速跑回来,身上有点微微冒汗,啪一下切开,说准备好了吗,我们要过去了。


70后看得目瞪口呆,说,这就过去了?


80后说,是啊,还等什么?


70后说,等等,然后他回去卧室换了身衣服,照了镜子,将分头仔细梳整齐,脱下拖鞋,换了皮鞋,还擦了擦,这才出来。


80后说,好了吗,走。


70后说,好了。打开房门的一刹那,70后突然就意料中的后了悔,说,我还是不过去了。


80后说,走吧,有什么不敢的?


70后尴尬的笑了笑,说,不过去了。


80后说,走吧,说着还故意拽了拽70后的衣角,这就顺便把70后的脸给拽红了。80后提醒一下说,我的晚饭?70后说,你放心,我知道。


于是,张小庆一手托着半截西瓜,一只手敲响了隔壁的房门,他实际是紧张的,汤姆躲在门背后透过门缝偷窥他,他也是紧张的。


张小庆第一次给人送礼送的就是西瓜,那是在大学的时候,考完俄语,怎么想都会挂,于是,买了个西瓜,在晚上偷偷摸摸的去敲俄语老师的门,从学生宿舍到教师住宅区并没有多远,但张小庆却走了好久,他怕遇见同学和认识的老师,昏暗的路灯下,看到一个看起来似乎面熟的人,就躲到树后面,等人家走远后再出来走,有好几次,他都想回去了,甚至,当他已经站在老师门口的时候,他还是定不下最后的决心。最后,当他终于鼓足勇气去敲门时,门却突然自己开了,老师出来倒垃圾,这让他措手不及,卖西瓜时老板没有了塑料袋,张小庆是抱着西瓜过来的,敲门的时候,他另外一只手抱着西瓜,西瓜贴在肚子上,用肚皮微微使着劲,这一惊,就听到西瓜啪的一声跌倒地上,裂开个大口子,像张小庆惊愕的嘴。


尽管张小庆在汤姆面前是活跃的,是外向的,是滔滔不绝的,但这不是真实的张小庆,他只在他熟悉的人面前表现出外向,一旦遇到不很熟悉的人或者女孩子,他立刻变了一个人,张不开嘴,说完天气,问候过吃饭,就再不知道该说些什么,喉咙发紧,时间鸡冻了。现在,他再次有鸡冻的迹象,手心都渗出一些汗来,但背后汤姆的目光如芒刺在身,这芒刺刺着他前进。终于,他敲了门,门开了,是一个个子高高很白皙的女孩子,同时,他看见客厅的沙发上也坐着一个女孩子,正在惊奇的朝这边望。


缓存了好几遍的话,现在突然失效了,又没有持久化,好半天,张小庆才磕磕巴巴的从喉咙里挤出蚊子般的声音,说,我们买了个西瓜,吃不完,所以,所以,送过来。


女孩子笑起来,说,西瓜是吗?我们也买了。说着指了指茶几,上面确实躺着一只西瓜,两只西瓜四目相对,女孩西瓜此刻嘲笑了张小庆西瓜,傻了吧,傻了吧,没做需求分析就开始编码,傻了吧。张小庆的汗哗的一声淌下来。女孩子看出了张小庆的尴尬,那是西瓜进退两难的尴尬,于是,女孩子说,你们没冰箱吧,我们有冰箱,要不先放我们这里好了,明天你来拿。张小庆连忙说,好,好。关上门,张小庆听见隔壁的女孩子们不知道说了些什么笑起来,中间听到有西瓜两字,张小庆的脸涨红了。


不管怎么说,对汤姆,张小庆是胜利者,汤姆没有食言,他请张小庆吃了康师傅红烧牛肉面,打了两个鸡蛋外加一根双汇瘦肉精火腿肠。大嚼瘦肉精的时候,汤姆突然问了句为什么没看到王娓娓过来的话,张小庆没有在意,说她最近有些忙。


接下来的几天张小庆都是在首图度过的,带上半张大饼和一瓶矿泉水,一待就是一整天,最后一天,他借了《人月神话》这本书,在社区里,某大牛说,没看过人月还在编程,编个屁啊。大牛的话总是那么精辟,张小庆立刻借了这本书,从管理员手中接过书的时候,他是虔诚的、激动的和小心翼翼的。从图书馆出来到公交车上,张小庆保持了激动的心情,他时不时将书拿出来,把玩一会儿,对自己说,这次总算要会编程了。会编程的美好憧憬一直持续到一个电话的打来,是一个不认识的号码,张小庆犹豫了一下,接了。是宋晨晨,高中同学,在chinaren上看到了张小庆的手机号,通知在北京的同学下周末聚会,地点在玉渊潭公园,那里正在举办一年一度的樱花节。宋晨晨,张小庆想起来,那是个身体很好的女孩子,每年学校运动会,她都会参加,跳高,能得到第一名,第一名是5分,他每年都屁颠屁颠的为她写广播稿:在这个风和日丽的早上,我班宋晨晨同学勇得女子跳高比赛的第一名,我们2班的全体同学都谢谢她、祝贺她。


至于同学聚会,张小庆则没有好印象。最近的一次同学聚会是高中同学聚会,过年了,最后一个学生寒假,曾经的班长在chinaren发起了聚会,地点在学校。张小庆是冲着李静去的,那是他的单恋,在那些青青葱葱的岁月,单恋和荷尔蒙一起疯长。高中毕业,像洋娃娃般的李静考取了西安交大,张小庆却落榜了,南开没有考上,最后调剂到一个二类学校,曾经想到过复读,老师也做过他的工作,但最后还是放弃了,一同放弃的还有想象中的对李静的表白,根本不是一个层次,拿什么表白呢?于是,张小庆就特别想见见李静,四年没有联系,不知道曾经的洋娃娃现在怎么样了,不需要说话,看一眼就够了。但是,张小庆失望了,李静没有来,没有来的还有很多人,成绩最好的几个人都没有来。


因为没有李静,整个聚会对张小庆来说黯然失色。还好,他见到了李健,还有郑平,97年的时候,他们一同咒骂过戚务生,那是迄今为止最好的一届国家队,每个队员的名字都是那么牛逼闪闪,郝海东、彭伟国、范志毅、区楚良;98年的时候,他们翻*墙出去看过世界杯,他妈的,罗纳尔多性能力太弱了,最后一场竟然被苏珊娜搞阳痿了;99年的时候,他们熬夜看过法兰克福,法兰克福主场对慕尼黑1860,杨晨连入两球,我日,几个人一晚上都兴奋的没睡着,还有铿锵玫瑰,在玫瑰碗,三个男人眼睛哭得跟桃子一样。上课时,他们将体坛周报用小刀仔细的裁成一块一块的,压在课本低下,偷偷的看,这样即使老师发现,损失也不会太大。现在,张小庆再次见到他们,他们却共同表示了对足球的不感兴趣,李健说,还没找到工作呢,郑平说,找了一份工作,工资才2000块。这天的焦点属于王胖子,他口若悬河、他气势磅礴、他滔滔不竭,他说,我收了好几份Offer,最高的年薪8万。当张小庆们还在以月工资计算收入的时候,人家王胖子已经使用年薪为单位进行运算了。当张小庆们还在以汉语描述工作时,人家王胖子已经与时俱进使用Offer代替了。


王胖子说,在我同学中,这还算少的,我的很多同学都是年薪10万。虽然说得是同学,但张小庆听起来,这话和说自己年薪10万没有太大区别。张小庆迷失了,他想象中的同学聚会不是这个样子,同学们聚在一起,什么话题都没有,谈得是钱,是自己会挣到多少钱。其实这并不奇怪,当岁月流逝,曾经的人们渐去渐远,每个人都处在自己的社会层级上,把这些人们以同学的名义硬拽在一起,除了钱,还会有什么共同话题呢?于是谈到钱,自然会有人不免自得,自然会有人不免受伤,同学聚会就变成了炫耀会。张小庆沉默了,他坐在角落里,只看见王胖子厚厚的嘴唇在那里一开一合,无数的唾沫在他口腔里一浪接一浪的往上涌,然后退去,不时有些激动的唾沫星子喷溅到空气中。张小庆坐看潮起潮落。


王胖子说,但是。


王胖子说,我还是决定读研。


王胖子说,我的导师是这个行业最牛逼的专家之一,上周刚从美国回来。


张小庆再次听错了,他听成王胖子上周刚从美国回来。王胖子的工资,够高,王胖子的导师,够硬,那么王胖子的未来,一定是又高又硬。这样的聚会,再不要参加,张小庆对自己说。吃过晚饭,张小庆和李健去了网吧,在那里,他们星际了一个通宵,早晨分别时,李健说,早点找到工作,张小庆说,你也一样。


终于盼到五一长假结束,张小庆是如此的盼望上班,节前给一个牛人发了邮件请教使用Ireport绘制报表的问题,现在应该收到回信了吧,最重要的是又能见到王碧薇了。张小庆说,3号那天我看到你了。


王碧薇说,是吗,在哪里?


张小庆说,在小白羊,我看到你和一个男的在一起,那是你男朋友吗?


这个问题使王碧薇沉默了,脸上的笑容消逝了,长长的眼睫毛垂下了,好一会儿,她才下定决心似地说,恩,是。

posted @ 2011-04-24 14:35 ronghao 阅读(1817) | 评论 (2)编辑 收藏

五一来了,空气中开始弥漫一种淡淡的清香,那是好闻的沐浴露的味道。每天早上和王碧薇一起上班的时候,张小庆都能闻到这种味道。这个五一被张小庆安 排的满满的,一号二号要去周扬那儿,自从搬家后就一直没有去过了,答应请周扬吃饭的事情也一直拖着,刚好,他们五一就放一号二号两天假;三号农业展览馆有 个规模巨大的招聘会,要陪余鹏去看看,那天余鹏在公交车上说要回老家,公司因为迟迟没有业务散伙了,房子租到五月七号,张小庆立刻表达了剧烈的反对,怎么 能这么快就放弃?!余鹏一言不发,张小庆缓和了一下情绪,说,来一趟不容易,至少要试一试吧。张小庆这么说是因为他想起了那个阳光明媚的早上,小余老师懒 懒的躺在床上打了个长长的哈欠,眼睛都没有睁开,眼角上挂满了刚刚分泌出的新鲜眼屎。张小庆觉得在来北京这件事情上他是要对余鹏负责的。最后几天,他要去 首都图书馆办张借书卡。吃饭时,比尔问了张小庆五一的打算,张小庆说要去西单图书大厦看几天书,比尔说,为什么要去西单图书大厦?


张小庆说,那里书多。


比尔说,可以去首都图书馆,那里近。


张小庆感到一些稍微的吃惊,说,那里对外地人开放吗?


比尔说,开放。


张小庆还是有些担心,说,我还没办暂住证。


比尔说,身份证就可以。


这真是出乎张小庆的意料,在他印象中,办张公交月票都是要北京户口的。每个月的月初几天,周扬都会拿着他的月票卡去公交车站购买下个月的月票,他是得意的,因为只有北京户口才可以办理公交月票,尽管月票卡上是他的照片,但却不是他的名字。张小庆不无羡慕的说,牛啊。


周扬说,牛屁,所有业务员都有,老板找关系办的。


张小庆说,帮我也办一张吧。


周扬说,你还是先把暂住证办了吧,免得某一天去昌平挖了沙子。


距离周扬说这话突然间就过去了三个月,张小庆说,吃什么,我请客。


周扬说,真的吗,想吃什么就吃什么?


张小庆说,真的。


周扬说,那前面去?


张小庆说,没问题,走!


说完这话,两个人都笑起来。牙厂前面是一家三层的海鲜城,每天晚上夜幕降临的时候,巨大的霓虹灯亮起来,旁边,足足也有三层高的巨大基围虾紧紧贴在 海鲜城的墙壁上,闪闪发亮,下面,吃客们开始陆续到来,没有步行的,也没有骑车的,有的都是自驾和司机,院子里的停车场里很快堆满了各式各样的小汽车,还 有些由于没有位置就停在了路边上,有的标志张小庆认识,比如说奔驰,比如说宝马,有的标志张小庆不认识,周扬嘲笑了他,那是林肯;有的车牌张小庆认识,比 如说京B、比如说津C,有的车牌张小庆不认识,周扬嘲笑了他,那是军牌。他们都是衣着光鲜的,漂亮女人那是经常见到的,提着漂亮的手提包,跟在男人后边 走,橘红色的灯光打在他们的身上,别样的神彩。


最后,张小庆和周扬还是在牙厂解决了他们的吃饭问题,周扬说,吃什么不都是吃吗,请我上网好了。去网吧的路上,张小庆买了两份和路雪。张小庆注意到没有看见周燕,那个纤细的女孩子,他问了周扬,你表妹呢?


周扬说,回家了。


张小庆有点意外,说,回家了?不是刚来吗?


周扬说,你不知道吗?被她父母接回去了。


张小庆说,我不知道,怎么回事?


周扬叹口气,说,老板看上她了。


事情就是这样,老板看上周燕了,这个刚刚中专毕业从农村来到这座大城市的年轻女孩,在这里,她是羞涩的,见到谁都会微微低下头,脸微微的红;在这 里,她是好奇的,有太多太多和家乡不一样的地方,那么多的人们,那么多的车门,那么宽阔的马路们;在这里,她是朴素的,没有买衣服,没有买洗面奶,有时间 的时候,和几个姐妹一起压马路。于是,老板看上她了,他看上了她的羞涩、他看上了她的好奇、他看上了她的朴素,此外,最重要的,他看上了她的纤细,纤细的 眉毛,纤细的脸,一身的纤细。白白胖胖的牙厂老板、来自广东移民香港的牙厂老板、已经结婚儿子都上高中的牙厂老板,看上周燕了。他带她去了天安门、故宫和 长城,她带她去了前面吃了好几千块钱一顿的海鲜,她带她去了商城买了好几千块钱的衣服,这一切都让周燕紧张、心慌和不知所措,她告诉了周扬,周扬立刻给她 父母打了电话。当两位老实巴交的农民听到这一切时,他们震惊了,他们的第一反应是,丢人!他们几乎是刚接到电话就立刻买了火车票,他们的想法很简单,接回 女儿,不干了。甚至当老板想与他们见个面时,都被他们拒绝了。老板只好告诉周扬,不要结婚,只要答应当他在北京时陪他3年,那么,附近买一套三居室,老家 盖一座小洋楼,外加30万。


你和他们说了吗?张小庆问。


周扬说,说了,为什么不说。后来老板还跟着回去了,买了9999朵玫瑰,单腿下跪,送去周燕家,在家里都轰动了。


张小庆说,结果呢?


周扬说,闭门羹。我舅舅还找了几个人去打老板。


张小庆有点激动,说,就该这样,该打!


周扬摇摇头,说,人家老板也没做什么出格的事情,这事,太平常了。


张小庆说,那现在呢?


周扬说,现在老板找了个首师大的大二女生当情人,一个月一万。


张小庆突然就内心黑暗了一下,很想见见这个月薪一万元女生。


三号的早上,张小庆和余鹏在农展馆见面了,他们显然是迟到了,早上八点,他们下公交车的时候,车站全部是人,从车站到农展馆的路上,人们形成了长长 的队伍,像在行军,一对向北,那是进展览馆的,一对向南,那是已经投完简历出来的。走到门口,放眼望进去,全部是人,门口的售票窗口已经关闭了,直接改在 入口处交钱进人。


余鹏突然就说,我不去了。


张小庆说,不行!


余鹏说,我就没有准备简历。


张小庆很生气,语气有些生硬,说,那我们就去准备!


于是两个人没有进农展馆,继续向北走,张小庆在前面走得很快,碰到人就问哪里有打字复印,余鹏在后面不紧不慢的跟着。在一个小胡同里,一个五平方米 的小屋子里,他们找到了复印机,自然,全部是人,因为招聘会的原因,涨价了,复印一份一块。张小庆重新在计算机上给余鹏敲打了简历,很难写,在工作经历这 一段,他写下了,东湖中学,政治老师,一年,写完了,字数很少,于是,加上被评为优秀老师,想了想,没有加在北京工作的经历。写好了,老板问打印几份,余 鹏说五份,张小庆说十份,于是打印了十份,张小庆给了钱,两个人走出来。


展览馆里是人贴人,走完A馆和B馆,两个人都觉得累了,在从B馆到C馆的过道上停下来,张小庆去买了两瓶水,递给余鹏,两个人就坐在过道的石条凳子 上,偶尔喝口水,默默无语。张小庆很理解余鹏现在的处境,两个人逛了很长时间,那么多的展位,没有一个地方招政治老师的,好容易有几个教育培训机构,投过 去简历,不是摇头就是要求先交培训费培训再上岗。


我这种情况,只能干销售,余鹏说。


张小庆不知道该怎样回答,他想起了那个中关村人才市场的春天的早上,道路两旁的柳树发出绿芽,路上的人们行色匆匆,没人注意到自己,自己拿着装有简 历的文件夹,穿着印有无数双新鲜脚印的黑皮鞋,一个人默默的在路上走,这个世界不是属于自己的。现在,展览馆里各种树木都绿了,生气盎然,花坛里,花们全 部在怒放,旁边,卖矿泉水的老大妈正在数钱,喜笑颜开,同样,这是不属于余鹏的,在此时此地,也是不属于张小庆的。


张小庆说,我们去C馆吧。


余鹏摇摇头,说,你觉得有意义吗?


张小庆没有直接回答,只是说,走吧。


余鹏恢复了他那一贯满不在乎的神情,说,走吧,回去。


张小庆说,我觉得你也许可以先参加一些培训,没有地方住的话,先和我一起住。张小庆这么说完就后悔了,因为这么说表示他自己也没有信心了。


余鹏突然笑起来,张小庆的话让他放松下来,他说,走吧,又不是你找工作,怎么搞得像是我陪你找工作一样。


张小庆却认真起来,说,我可以教你编程,你再报个计算机班。


余鹏哈哈大笑,他已经放下了,他说,我可付不起家教费,这事只能说我和北京性格不合,在哪里不是一样的过,一定要在北京吗?北京确实挣得多,但我真的需要那么多钱吗?那真是我想要的吗?


张小庆却还是放不下,余鹏说,我们去买个电视吧。


张小庆一直想买个电视,这个愿望在麦迪4月26号113比111绝杀小牛后达到高峰,现在,火箭与小牛打成2比2平,第五场正在进行中。于是,两个 人出了展览馆,去了旁边的苏宁电器,一切颠倒过来,这次是余鹏手插在裤兜里走在前面,张小庆在后面慢慢的跟,余鹏的表情是轻松的,因为他已经做出决定,张 小庆的表情是黯淡的,因为他还想说服余鹏努力一把,但是却找不到甚至能够说服自己的理由,当已经不爱了,勉强在一起还有什么意义。


电视同样让张小庆失望了,最最便宜的21寸也需要一千多块,这超出他的期望太多,更让他失望的是,火箭103比106输给了小牛,比赛结束时,姚明 同样黯淡的走。如果火箭输了球,那么,买个电视同样失去了意义。两个人一起回了亦庄,在贵园南里下了车,余鹏说晚上要在张小庆这里过夜,但是这次,他也食 言了。有一辆665路经过,余鹏突然就在张小庆手里塞了个东西,然后跳上了665。


余鹏说,庆娃子,再见了,回家见。


张小庆展开手中的东西,那是一张一百元钱,他就追着那辆665,一边跑一边骂,你他妈什么意思,什么意思?!


余鹏双手插在口袋里,站在窗口,看着下边的张小庆,他本来是想请张小庆吃顿饭的,一直以来,都是张小庆请自己吃饭,这让他过意不去,但是最后他又改变主意了,他对自己说,再见,北京!


665越走越远,张小庆停下脚步,心里满是难过,不仅仅为余鹏,更是为自己,现在是余鹏,未来,会不会轮到自己。这么想着,一个人慢慢往回挪动的步 子就变得沉重起来。突然间,他看见王碧薇了,在小白羊超市的门口,王碧薇亭亭玉立的站在那里,像是在等待什么人。一会儿,一个男人走出来,手里提着一大袋 东西,男人走到王碧薇的身边,说了些什么,王碧薇突然笑起来,然后,两个人一起往这边走过来。


posted @ 2011-04-17 15:51 ronghao 阅读(1662) | 评论 (2)编辑 收藏

依旧是公司团体票,家住河北,早上5点就起床出发,这意味着我对这次QCon有着很高的期望。其实在公司时,团队一帮人就为第一天的票而争个不停,最后,我说,我住河北,于是,获胜,上午,同事南方,下午。


早上是三场专题演讲,分别是Facebook的《支撑Facebook消息处理的HBase存储系统》,Oracle的《Java EE 7平台:走进云计算》以及《在实践中实现企业的敏捷性》,最为期待的无疑是在中国并不存在的Facebook。


上台的Facebook是个年轻的小伙子,典型的闷骚性程序员,坐第三排,竟能看见他几次脸红的笑,没能引起共鸣,一个人自己笑。PPT做得不是很好,很多文字,费劲的去看,却因为近视而始终看不完。其实个人最为关心的是为什么选择HBase,它解决了什么问题,以及从Mysql切换到HBase后的相关对比数据,很显然,我失望了,除了最开始提到每月处理11TB聊天消息和14TB邮件以外,再没有看到任何数据,我现在越来越迷恋数据,当你说什么东西好的时候,对不起,请拿数据说话,不要用主观感受,不要拿什么大大提高了效率,客户很高兴之类糊弄人。介绍了Facebook的技术栈、HBase的优点以及Facebook对其的扩展,都是一带而过,没有深入;提到了对系统运行的有效监控,这也是我非常感兴趣的点,同样是一带而过;提到了Facebook采用新技术时最优先考虑的是快速上线与最小风险,有些感触,写程序一定要避免开发人员镀金与需求镀金;提到了与开源社区的有效互动。


总体感觉就是,太泛了,没有一个点能够深入,很不给力。团队内部要求做个分享,我想,我会说,很简单,打开浏览器,输入google,回车,再输入关键字Facebook加HBase,回车,好了,就是这些。


第二场是Oracle的《Java EE 7平台:走进云计算》,开始之前,周围的人都在说接下来是广告时间。还好,不是广告,但是,同样,打开浏览器,输入google,回车,再输入关键字Java EE 7,回车,好吧,也就是这些。关键词:容器、PAAS、资源与状态管理、更好的隔离性、对缓存的标准API、管理和监控、打包、版本、模块化以及各种规范。演讲的老先生让我想起了大学时的计算机老师,不管楼下风吹雨打,我自屹立岿然不动。


我想,是不是这种综合性大会决定了每个话题都不能深入?


第三场给了我惊喜,如果是在两年前。嘉宾显然比前两位有更多的演讲经验和技巧,PPT很干净、甚至还有视频,能够开些玩笑。但是,实质的东西太少,核心就是需要知道Why。我想,这个在TW已经是反复强调的原则,做任何事情都要想清楚为什么要这么做,这么做解决了什么问题,而不是直接拿一些所谓最佳实践去套,不存在最佳实践,只存在适合本项目的有效实践。我们的目标不是实施敏捷,而是对项目进行实实在在的改进,所谓敏捷,不过是个bizword,很多实践早已存在多年,现在突然全被称为了敏捷实践。说到这里,不得不再提自己对所谓Scrum认证的恶感来。


哎呀,真平淡啊,没有太多收获。但是明显感到INFOQ的组织水平提升很快,最突出的是大会茶歇时开始播放广告了,记得09的时候,还是无聊的播放维多利亚的秘密呢,当时就很诧异,这么好的广告时间竟然被浪费了。另外,大会人员也比前几届多了。但是这些人都是些什么来历呢,中午吃饭的时候,明显感到一些人是通过公司来打酱油的,不管怎样,有线的赞助商多了,交费参会的人多了,对QCon都是好事。


我在想,QCon是否能够在会前就将PPT开放出来呢,这样,不用去赌运气,南方应该就比我的运气好。

posted @ 2011-04-10 13:29 ronghao 阅读(2164) | 评论 (2)编辑 收藏

张小庆感到有点点意外,说,是吗?


王碧薇说,你猜猜是谁?


张小庆想了想,说,猜不到。


王碧薇说,再想想,你最近都碰到过哪些女孩子。


张小庆说,真不知道。


王碧薇放弃了,说,真没劲,是罗悦。


听到这个名字,张小庆想了好一阵,这才从眼前浮现出一个胖乎乎大大咧咧的女孩子来,她是和张丽合租房子的女孩,黄色的头发,烫过,上周末搬家时见过。


张小庆笑了笑,说,真的假的?


王碧薇说,当然是真的,我什么时候骗过你,是海蒂让我告诉你的。怎么样,是不是有点小兴奋?


张小庆说,没有,我不想去。


王碧薇说,哎呀,你这个人真是的,人家女孩子约你,你还不接受,哪有你这样的?


张小庆说,真不想去。


王碧薇说,可是我答应过海蒂的,你会去的。


王碧薇抓住了张小庆的软肋,张小庆犹豫了,说,这个。


王碧薇看出张小庆即将到来的妥协,她迅速换为了肯定句,说,那好,我告诉海蒂了!


于是,张小庆就没有选择了。星期五下班的时候,他给罗悦发了短信,约她周六一起去朝阳公园,他很快就收到了女孩的回信:好的!


星期六的早上,天气很好,太阳甚至好得有点晃眼睛,张小庆在贵园南里的公交车站旁等罗悦,他们约好9点见面。9点过5分,女孩子出现了,她显然打扮过了,脸蛋红扑扑的,眼睫毛黑黑的翘起,嘴唇打上了薄薄的一层唇彩,手上提着一只大大的包。


张小庆说,早。


女孩子说,早!


打过招呼,张小庆问女孩子吃没吃早饭,女孩回答说没有,于是,他们一起去了车站对面的小白羊超市,在那里,张小庆给女孩买了一袋牛奶和一袋苏打饼干,另外,还买了两瓶水。他们一起坐上了927路,车上的人不多,两个人在后排找个靠窗的座位坐下。张小庆显得有些拘束,他不知道该说点什么好,也不知道该如何开始,反倒是女孩显得很大方,问些张小庆之前的学校,哪里人,现在的工作。在小红门,他们下了车,要去倒625路,这次,等车的人很多,车来了,没有排队,一起往上挤,张小庆在前面,女孩在后面跟着,没有座位,两个人在拥挤的过道里站着,随着汽车的启动加速减速刹车而有规律的摇摆。05年的四环,还没有多少车,625行驶的很快,车窗外,吐出新芽的树木们像一帧帧的彩色电影胶片向后滑动,张小庆抓住头顶上的扶杆,女孩抓住车座背后的扶手,车厢里很嘈杂,这让张小庆舒出一口气,不用刻意找些话题,可以什么都不说,说了也听不清,静下来,专心看窗外的电影胶片。


下车的时候,张小庆又看见了他曾面试过的那家公司,那幢独立的三层小楼依旧漂亮的立在那里,没有说话。张小庆想,如果当初这家公司录用了自己,那会是一种怎样的情形。也许自己此刻正窝在地下室里看书,也许自己此刻正在公司加班,也许自己此刻写代码有些累了,正透过巨大的落地窗户向朝阳公园张望。谁知道呢,生活就是这样,充满未知,因为未知,再加上希望,生活就变得朝气蓬勃起来。


张小庆去售票处买了票,两个人就进入了公园。朝阳公园真大,因为春天,格外美丽,所有的小草都绿了,旁边,小草告诉你:正在成长,请勿打扰。两个人一起去坐了升降天梯,在最高处,张小庆看见了整座城市;两个人一起去坐了过山车,张小庆是第一次坐,有那么一瞬间,他感到自己的心脏像要跳出来;两个人一起经过了一块足球场,里面,一个男孩子刚进了一粒漂亮的远射,咆哮,和他的伙伴们热烈拥抱在一起,让张小庆想起了巴蒂斯图塔;最后,两个人沿着河岸慢慢的走。


依旧是女孩先问问题,接下去是张小庆回答,最后,女孩再介绍自己。一问一答中,张小庆知道女孩是东北人,也是去年毕业,现在在一家外贸公司里当总经理秘书,一个月2000块钱。女孩也问了张小庆的工作和将来的打算,张小庆回答说要好好做技术,将来去IBM和微软。女孩还问了张小庆是否喜欢看书和看电影,张小庆回答说很喜欢,最喜欢的是余华和活着。问答中,女孩没有提到房子。05年的时候,所有的年轻人都不认为房子会是个问题,如果在工作的城市连房子都买不起,那,还混个屁!这是张小庆当时的想法,他不会知道,仅仅一年过后,所有女孩子们约会的第一个问题就是房子,而房子,将成为未结婚的男人人生中一个最大的问题。最后,话题转移到了喝酒上。


女孩说,你们那边喝酒吗?


张小庆说,喝。


女孩说,我爸爸特别喜欢喝酒,每年他都会自己买些葡萄,自己酿葡萄酒,两大桶,酸酸的甜甜的,非常好喝。每次回家我都会喝好多。你们那边都喝什么酒?


张小庆说,我们那边喝米酒,自己买酒曲就能做,非常好喝。


女孩说,我爸爸也喜欢喝白酒,每天吃饭都要喝一点。我有时候也偷喝一点,哈哈。对了,你喝酒吗?


张小庆说,我不喝。


女孩说,噢。


从公园出来已经是下午一点,两个人去了公园对面的肯德基,人很多,排了好一会队,很奇怪,真正吃到嘴里,张小庆感到比想象中的差去很远,甚至比电视里都差去很远。当你向往一个东西的时候,你会把它想象的过于美好,而当你得到它的时候,你却习以为常,认为不过如此,直到你失去。


两个人最后在贵园南里公交车站分了手,女孩说,谢谢你今天陪我去朝阳公园,再见。张小庆说,再见。


真的就再见了。星期一的早上,张小庆感到了王碧薇眼神的异样。王碧薇说,迈克,周六的约会怎么样?张小庆说,挺好的。接着,张小庆就看到了王碧薇的异样,那是种想笑又竭力憋住的异样。中午吃饭的时候,王碧薇和张丽坐在一起,她们不知道在说些什么,一边说一边向张小庆这边看,看完了还神秘的笑,张小庆隐约感觉到了什么。下班的时候,他叫住了王碧薇,问是不是罗悦说了些什么,王碧薇说没有,但张小庆坚持问,最后,王碧薇告诉他,你是不是忘记帮女孩子拿包了?


张小庆想起来,那个早上,女孩提了一只硕大的包,他甚至觉得个子不高的女孩和那只包搭配在一起有一点点的比例失调。女孩提着那只硕大的包和他一起挤了公交车,在摇摆的公交车上,她一只手紧紧抓住车座背后的扶手,一只手紧紧抓住她的包,想到这里,张小庆的脸红了;女孩提着那只硕大的包和他一起坐了升降天梯,天梯上升的时候,她一只手紧紧抓住天梯的扶手,一只手紧紧抓住她的包,想到这里,张小庆的脖子也跟着红了;女孩提着那只硕大的包和他一起坐了过山车,过山车下落的时候,她一只手紧紧抓住过山车的扶手,一只手紧紧抓住她的包,想到这里,汗从张小庆的脸上渗出来;女孩提着那只硕大的包和他一起经过了足球场,进球的时候,她的高跟鞋正在路上绊了一下,她下意识紧紧的抓住了她的包,想到这里,张小庆大汗淋漓;甚至,在肯德基吃饭的时候,女孩也紧紧抓住她的包和她一起进了并不硕大的厕所,想到这里,张小庆啊了一声昏厥过去。


王碧薇说的非常委婉,但张小庆显然受到了巨大的打击,他想找点水喝,同时,他想告诉王碧薇,不是忘记,是根本都没有意识到,他也曾经犹豫了一下,但是,他想,包里可能有女孩非常私人的东西,也许并不方便。王碧薇感觉到了张小庆的创伤,她决定帮助他,于是,她说,其实也不是这个,主要还是你不会喝酒。这个理由让张小庆多少感觉好过了点。王碧薇还不放心,说,要不要我陪你走走?张小庆说,不用了,谢谢,我想回家。


受到打击的张小庆、忘记拿包的张小庆、大汗淋漓的张小庆,没有回家,他去了网吧,在那里,他在论坛发了贴,但是,很快,他受到了程序员们的打击,他被定了性,根本就不懂得关心人。这个定性让整个事情有了向人性挖掘的可喜变化,人类真伟大,人性真简单,因为定性是如此的容易,就像打个标签,绿色、环保、低碳、吐把口水就可以。


关键时候,是王娓娓和余鹏拯救了他。听完张小庆的叙述,王娓娓哈哈大笑,说,就这么大点事啊!


张小庆说,是不是件大事,但是,这说明我根本就不懂得关心人。


余鹏说,狗屁逻辑!


王娓娓说,刚开始都是这样的,没有谁天生就知道如何关心女孩子的。当然,这是最基本的,要帮女孩子拿包,要随身带着面巾纸。下次就知道啦。


余鹏说,我他妈最烦的就是为人定性的,作业没写完就是不爱学习的,批评就是反对的,买日本货就是不爱国的,上访就是精神病的。


张小庆说,可是,我觉得他们说的也有一些道理。


余鹏说,我靠,你自己也信了,真扯淡。你还真拿别人的话来解释自己?!


王娓娓说,你对那个女孩子有感觉吗?


张小庆说,没有。


王娓娓说,那为什么约会?


这个问题让张小庆很难回答,他总不能说是因为王碧薇答应别人而自己又不好拒绝王碧薇吧。张小庆说,这个很难回答。


王娓娓说,其实没有感觉就好,你也没有失去什么,就当是一次学习吧。


说这话的时候,三个人正坐在王娓娓租的屋子里吃饭。王娓娓自己做了饭,西红柿炒鸡蛋、可乐鸡翅、胡萝卜瘦肉和清炒绿豆芽,张小庆和余鹏打了下手,本来是胡萝卜炒肉丝的,张小庆将肉丝切得和心情一样厚重,结果就变成了胡萝卜炖肉块。


吃完饭,三个人一起去了安贞华联,旁边的,服装市场。整个市场被无数五六平方的小铺子分隔开来,在这里,你总是可以看到当下最流行的款式。王娓娓告诉张小庆至少要对半砍价,夏天要来了,张小庆看中了一套耐克的短袖短裤,老板要价50,张小庆说,25。


老板说,同样的衣服,你去华联看看,至少要200。


张小庆说,可人家是真的,而你是山寨的。


老板说,兄弟,其实质量差不太多,不要迷恋哥,40。


张小庆还想划价,他努力找出个线头,说,看,这有个线头。


张小庆话音还没来得及落下,老板已经转身抽出剪刀咔嚓将线头剪去了,速度之快让张小庆甚至没有看清他是如何出刀的。老板说,好了,没了,30。


于是30成交,王娓娓买了条裙子,余鹏打了酱油。张小庆和余鹏向东,王娓娓向西,张小庆和余鹏向东坐了957,王娓娓向西坐了957。其实张小庆他们还可以坐300,但是300太恐怖。汽车开到成寿寺,余鹏突然说,我要回家了。


张小庆没有听清,说,什么?


余鹏说,我要回老家了。说这话的时候,他一只手搭在公交车扶杆上,一只手插在裤兜里,非常平静,依旧是那副满不在乎的神情。

posted @ 2011-04-10 13:27 ronghao 阅读(1569) | 评论 (3)编辑 收藏
     摘要: 张小庆说,苏珊! 王碧薇看见张小庆了,她好看的毛毛眼笑起来,说,你搬家了吗? 张小庆说,是啊,刚搬过来,好巧啊。 王碧薇说,哈哈,太好了,有晚饭吃了! 于是,下班的时候,张小庆、王碧薇和张丽再次去了贵园北里旁边的那家餐馆里,张小庆请客。王碧薇点了地三鲜,张小庆点了鱼香肉丝,张丽点了清炒空心菜。吃饭的时候,王碧薇说,迈克,这个周六海蒂要搬家,你也过来帮忙吧。这话让张小庆没有办...  阅读全文
posted @ 2011-04-04 17:38 ronghao 阅读(1464) | 评论 (7)编辑 收藏

     星期六的早晨,张小庆一大早就乘车去了中关村海龙大厦,他是愉悦的,甚至带一点点骄傲。他骄傲的走在城市形形色色或匆忙或迷茫的人群中;他骄傲的对卖MP3的小姑娘说,我就要这个,那是一个399元的爱国者;他骄傲的在摇摆的公交车上给王娓娓和余鹏分别打了电话,他对王娓娓说,娓娓姐,来吃饭吧,他对余鹏说,快来给我搬家,早点过来;他骄傲的在邮局工作人员的注视下将MP3小心翼翼的装进包裹并添上自己的名字。做完这一切,他感到全身轻松,他给他妹妹打了电话,他能想到妹妹在同学当中使用这只MP3时同学们羡慕的表情,这个表情让他感到了满足,然后他返回了鹿圈,开始收拾明天搬家的东西。


    隔壁年轻的妈妈看见张小庆收拾东西了,她说,要搬家?


    张小庆骄傲的说,是啊。


    妈妈说,搬到哪儿?


    张小庆说,河那边。


    妈妈笑起来,说,不错,我要去菜市场,你要不要去买点东西?


    于是,张小庆,妈妈,妈妈的小女儿一起去了鹿圈的菜市场。妈妈帮张小庆仔细挑了做饭用的蒸锅、炒菜用的铁锅、锅铲以及洗菜的塑料盆,这些东西亮晶晶的,闪闪发亮。妈妈像给自己买东西一样为了几块钱与店主争的面红耳赤,这让张小庆有点不好意思起来。最后张小庆还红着脸问了妈妈,这蒸锅能做饭吗?妈妈说,可以,用碗淘小半碗米,一蒸就可以。


    余鹏周日来得果然就很早,张小庆说,这都8点了你才来,余鹏说,我靠,你也不看看我住哪儿,你以为我住你隔壁啊,说话间两个人一起把被褥打了包。张小庆以为没有多少东西,他想到了一个月前,自己一个编织袋就将这一切拖了来,但现在,多了两只手,才能勉强避免跑两趟。张小庆一只手扛着包,一只手提了水瓶,余鹏拿了盆,盆里乱七八糟的放了很多东西,两个人出了门,张小庆走在前面,走得很快,余鹏照旧是不紧不慢的跟在后边,有好几次,张小庆回过头,埋怨余鹏走得太慢,余鹏反驳说,我拉下了吗?


    到了贵园南里,汤姆还没起床,他睡眼婆娑的开了门,说好早啊。两个人进了门,开始归置东西,汤姆开始起床刷牙。说是归置东西,其实也没有什么东西可以归置,把褥子在床上铺好,编织袋塞到床底下,锅碗瓢盆放进厨房,背包挂在门后的钉子上,其余的往桌子上一摆,这就好了。汤姆给了张小庆钥匙,两个人又去了小白羊超市,买了油盐酱醋,最后,张小庆在雀巢咖啡的货架前停了下来,他想起了大学时对雀巢咖啡的迷恋,一块钱一小袋,一直舍不得买,这次,他想到犒劳自己的方法了,每次发完工资,就买一种大学时想吃又舍不得吃的东西,不仅要买,而且要买大盒的,要一次吃个够,把以前落下的补回来。于是,张小庆拿了一大盒的雀巢咖啡,但是在拿之前,他还是犹豫了一下,最后拿了一盒促销装。出了超市,王娓娓也就到了,三个人一起回了家。


    汤姆没有料到王娓娓的出现,其时他正在享受他的早饭,早饭是一袋康师傅方便面。主卧的房间门大开着,被子保留着主人刚起床时的凌乱样;书桌上,一本本的专业的非专业的书横七竖八了无生气的躺着,最内侧,是两大包康师傅的实惠装,都打开了;地上,很久没有打扫过了,厚厚的灰尘,还有两团蜷缩成一团的卫生纸;阳台上,一周的衣服侵染在一只红色的塑料桶里,微微向空气中弥漫些细微的味道。张小庆打开门的一瞬间,汤姆正在把缸子里的面条往口中送,因为有点烫,汗珠从他的脸上微微渗出来了,头发搭上汗珠凌乱的贴在前额上,见到张小庆身后的王娓娓,面条有那么一瞬间在他的嘴中僵硬住了,和他脸上的表情一样,措手不及。


    汤姆说,这是?


    张小庆说,这是我的师姐,王娓娓。


    汤姆说,你好,我是张小庆的同事,我叫张和。


    没等王娓娓回答,汤姆迅速转身和他的方便面一起回了他的房间,房间门被立刻合上了。五分钟过后,房间门打开,一切都发生了惊人的变化:被子被叠好了,床单被使劲扯过了,床铺变得平平整整;书桌上的书们恢复了次序,从上到下,从小到大,整整齐齐,大包的方便面不见了,被扔进了抽屉,关起来;地被打扫过了,虽然依旧算不上干净,但是两团抱团的卫生纸不见了;阳台上,红色的塑料桶被盖上了盖。张小庆看到,汤姆的头发也被打理过了,一边向左一边向右,不再无精打采的趴在前额,他还换了件衣服,将刚才的秋衣换成了衬衣。


    汤姆显然是从刚才紧张的情绪中释放出来了,他说话的语速不再那么快,他说,不好意思,房间有点乱,刚才还没来得及收拾。


    王娓娓说,张小庆是我的师弟,刚参加工作,还需要麻烦你多帮助他啊。


    汤姆说,没事,没事,那是肯定的。


    王娓娓说,张小庆,你都收拾好了吗,我帮你收拾收拾吧。


    张小庆说,都收拾好了,没什么可收拾的了。


    但张小庆很快发现了女人和男人观察世界视角的不同。王娓娓发现了房间顶角的两个蜘蛛网,她让张小庆用扫把把它们拭去了;她发现了窗户夹缝里众多的灰尘,她自己用抹布仔细的都擦去了;她发现了张小庆刚摆好的桌子是如此的凌乱,水杯、牙缸、热得快、饭缸、书籍,胡乱的混搭在一起,尽管摆的整整齐齐,但是用起来是那么的不顺手,她帮张小庆重新规整了桌子。最后,她来到了厨房,厨房显然是刚刚被汤姆打扫过了,灶台上不再有方便面沫和鸡蛋壳屑,锅被洗过了,挂在墙上正向下滴滴答答滴着水,连灶台角垃圾桶中的垃圾袋也被换过了。但王娓娓很快让张小庆有了新发现,她让张小庆拿来了清洁球和洗涤灵,使劲一擦,原本褐色的灶台露出了丝白色的脸,啊,灶台原来是白色的啊,张小庆吃惊的想,还有,抽油烟机,原本也是白色的,还有,汤姆的脸原本也是白色的,但此时,它再次因为紧张而染得通红。汤姆说,我来,我来。


    到了中午,三个人要去外边吃饭,他们热情的邀请了汤姆,但汤姆不肯去,他说,我有点事,就不去了。张小庆说,有什么事比吃饭还重要吗?汤姆瞅一眼王娓娓,说,真有事,真不去了。于是三个人来到了小区旁边的福华肥牛,饭馆是张小庆找的,他并不知道肥牛的意思,他只是知道,这是一座三层的饭馆,很大,很大就会很上档次。


    于是,在肥牛饭馆里,张小庆叫了肥牛服务员,张小庆对肥牛服务员说,来份羊蝎子!从周扬那里,张小庆知道,羊蝎子和水煮鱼是今年最火的两道菜。


    肥牛服务员面露了难色,小声提醒说,我们是肥牛。


    张小庆并不明白,王娓娓笑了起来,说,这里只有火锅。


    于是,大大的炭火火锅端上来,色彩鲜红的肥牛端上来,绿色油油的茼蒿端上来,三个人边吃边聊。与上次见面相比,东道主发生了变化,由王娓娓变成了张小庆,张小庆很享受这种变化,他不停的问,要不要再加点菜。余鹏依旧是懒懒的,他坐在那里,不提出话题,只是回答,他的工作没有进展,依然没有找到客户。张小庆说,这不是家真正的软件公司,我明年要去一家真正的软件公司,目标不变,微软和IBM。


    吃着吃着,热起来,张小庆脱去了外套,里面是他前不久在菜市场买的白色衬衣,扎在他黑色的裤子里,白领嘛,自然是要穿白衬衣的。接下来的事情就很戏剧性了,在张小庆去卫生间穿过长长过道的时候,有人叫住了他:服务员,点菜!最开始张小庆并未在意,但是当那个人再次叫了服务员并且周围并没有其他人时,张小庆意识到,完了,自己被当成服务员了。张小庆尴尬的说,我不是服务员!说完,加快了行走的步伐,同时,迅速的将衬衣从裤子里拉出来。


    晚上的时候,汤姆对张小庆说,你师姐来也不通知我一声。


    张小庆说,你也没有告诉我要通知你啊。


    汤姆说,你师姐是做什么的?


    张小庆说,她在这边做导游。


    汤姆说,她是几几年的?


    张小庆说,79年的,哎,你是不是对她有兴趣啊?


    汤姆说,没有,没有,别乱说,我只是问问。


    汤姆的午饭和晚饭都是方便面,在厨房刷牙的时候,张小庆发现垃圾桶里躺着四个方便面袋子和两个蛋壳,同时,张小庆看见,那两大包方便面重新出现在汤姆的书桌上。洗完澡,张小庆躺上床,偎依在床角,看书,他买了本犀牛书-Java  script  权威指南。汤姆也坐在他的桌子旁,看书,两个人都没有电视,也没有电脑,夜晚由此显得安静,只剩电棒发出孤独的滋滋声以及偶尔翻动的纸张的声音,像极了高中的晚自习。


    哎呀,张小庆突然想起来,走的时候,他还没有给房东电费。这个问题扰乱了他的心境,他决定下个周末再回鹿圈一趟。


    张小庆贵园南里的生活正式开始了。和鹿圈相比,他离公司更近了,步行只需不到十分钟,9点上班,每天早晨8点30准时起床,十分钟洗漱,下楼,在小区门口买个鸡蛋灌饼,边走边吃,8点50,他的机器已经开始工作。最开始的时候,他还叫汤姆一起起床,后来汤姆说,不用叫他了,他8点45起床。晚上6点下班,他会先去买份北京晚报,迅速翻到最后一页,姚明的球队在那一页正为季后赛努力,有时候,他会买点馒头回家煮点稀饭,有时候,他会买点面条,更懒的时候,就买张大饼,喝点水都能吃下去,汤姆的晚饭一成不变,变的是牌子和口味,康师傅、统一、今麦郎和白象,牛肉味、番茄味、炖鸡味和麻辣味。周末的时候,张小庆会去菜市场,他买了莲藕和排骨,莲藕炖排骨,家乡菜,多放点水,可以吃两天,有了这道菜,就可以试着焖点米饭了。


    这天早上,张小庆晚起了5分钟,不过不用着急,不会迟到,他像往常一样,去了小区门口买鸡蛋灌饼。搬过来唯一让他觉得不方便的是早餐,没有选择,鸡蛋灌饼、鸡蛋灌饼、还是鸡蛋灌饼,没有油条,没有豆浆,也没有咸菜。当然,也可以稍微跑远一点,那里有个北京早餐,张小庆去过一次,然后就决定再也不去了,一个包子竟然要8毛钱,而且巨难吃,是那种刚吃第一口就决定放弃的包子。在灌饼摊旁边,他看见王碧薇了!她也在那里等待买灌饼。

posted @ 2011-03-27 00:14 ronghao 阅读(1382) | 评论 (2)编辑 收藏

      如果一天是前一天的重复,一周是前一周的复制,那么日子将过得滞黏,没有惊喜,没有期待,也没有活气。对张小庆来说,第一个月的每一刻都是崭新的。他实现了菜单树,建立了第一张MySQL数据库表,尝试了一把Lucene,尽管没有找到合适的中文分词;他在Jdon围观了彭晨阳与范凯的巨幅吵架帖,牛人就是牛人,吵架都是那么牛逼闪闪,火星四溅;他还兼职了一周的网管,原来的网管离职了,人们理所当然的认为程序员什么都会,于是张小庆就经常满脸通红的在各个工位和各种莫名其妙的电脑问题间奔波,他满脸通红是因为他也不会,他会说,等等,我要查查,然后飞快的跑回座位进行Google,然后将结果抄在本子上,再疲于奔命的奔回来,好了,我来看看。

      周五的下午,临近下班,随着时钟的跳动,张小庆的心跳也逐渐加快,因为他要去赶368路,他要去周扬那儿,他要和他的兄弟一起吃饭、吹牛和打牌。他的黑色挎包里也第一次装上了东西,装了要换洗的一套干净衣服,他要去周扬那儿洗澡,周扬的工厂提供热水,两个水桶,一个水桶是漫漫的热水,一个水桶是小半桶冷水,提进厕所,关上门,脱掉衣服,对热水,用毛巾快速的擦。厕所是男女不分的,三个坑,经常是洗着洗着隔壁传来一阵满脸通红的挣扎声,一会儿,挣扎戛然而止,接下去就是什么东西落到水里的声音,有些时候,挣扎的时间过长,让张小庆都跟着着急,他真想拉开门,走过去,说,哥们,你应该多吃点蔬菜。

      这天,周扬给张小庆介绍了他的表妹,女孩子叫周燕,纤细的眉毛,纤细的脸,一身纤细的坐在那里,见到张小庆,眉毛低低的纤细的笑。周扬说,我表妹,刚来的,学做牙齿。

      吃完饭照例是打牌,斗地主,在周扬昏暗的地下室里,张小庆、周扬和周扬同样年轻的同事,一毛钱起抓,炸弹翻番,五毛封顶。张小庆买了袋瓜子,周扬把电视打开,那是老乡送给他的,屏幕巴掌大小,黑白,天线坏了,用根铁丝插着,只能收到中央一台,三个人开始抓牌、嗑瓜子和不时的拍拍没有图像的电视。这个晚上张小庆的运气很好,连续抓了三把大小猫,炸了三炸,他面前的纸币很快堆得像小山一样。凌晨两点的时候,他们结束了打牌,周扬同事回了自己的房间,张小庆和周扬走出地面透透气,张小庆点了点赢了多少钱,一共是十七张纸币和十个硬币,两块七毛钱,张小庆想,真是个愉快的晚上。两个人去小卖铺买包烟,月亮很大,从小店回来的时候,能清晰的看见自己贴在地上的身影,北京春天的晚上还是很凉的,张小庆吸了吸鼻子,看见周扬在冲自己笑。

      张小庆说,你笑什么?

     周扬从口袋里掏出部手机,说,你看这个。

     张小庆张大了嘴,我靠!诺基亚3250!

     其时正是诺基亚3250和诺基亚牛逼的时候,3250躺在电视里、躺在横幅里、躺在彩页里、躺在周扬的嘴里,充溢满这个城市。

     张小庆说,多少钱?

     周扬说,三千六百多。

     张小庆说,有钱人啊!

     周扬说,有钱个屁,我半年的工资!

     张小庆说,我日,那还买?

     周扬说,我就这点爱好,人活着总得满足自己一两个爱好吧,要不岂不白活。张小庆知道他的第二个爱好是他的分头。

     第二天,周扬去送货,张小庆去了公主坟电子工业出版社的地下书城,在那里,有几个柜台,专卖计算机书籍,张小庆会在那里呆上一整天,直到天黑。张小庆特别喜欢到一家店主是老奶奶的书店,老奶奶从来都不会催这个只看不买的年轻人,相反,她每次看到张小庆,总会点点头,说,来啦,时间长了,她还会告诉张小庆,这里有凳子,坐着看。这让张小庆感到了不好意思,他的脸红了,不自在,于是,在第二天再来的时候,他总会买上一本书。他买过《21天学通Java》,很遗憾,第21天的时候,他失败了;他买过《Java编程宝典》,国人原创,电力出版社,很遗憾,全书三分之二全是代码,越看越像葵花宝典;他买过《Java应用开发指南》,Jdon彭晨阳作品,神一样的男子,很遗憾,附属光盘里的代码全是片段,没有一个能够神奇的跑起来。

      周日下午,张小庆重新背上他的挎包,回家,回亦庄。在368上,他给一个老奶奶让了座,每次让完座,张小庆总会离让出的位置远远的,因为站在一个说谢谢你的人的旁边,让他感到不自在。回到大蒜屋,打开房门,打开灯,把衣服从挎包里取出来,扔进脸盆,到门外水龙头接上水,洗衣服。张小庆想,等这个月发了工资,就换到桥那边去,找个楼房。

      王碧薇找到房子了,在贵园北里,三居室,三个女孩合租。下班的时候,她叫了张小庆,走,请你吃饭。

      张小庆说,为什么啊?

      王碧薇说,我找到房子了。

      张小庆说,我也没帮上什么忙。

      王碧薇说,你就说,去不去吧。

      于是,张小庆、王碧薇和张丽一起去了贵园北里旁边的一家餐馆里,张丽和王碧薇同一天入职,是个安静的女孩子。餐馆不大,但是很整洁,服务员穿着统一的上衣,有人吃完饭,马上就会有人上去将餐具收拾干净,做好一道菜,厨师会拉响窗口的铃铛,服务员就会过去将热气腾腾的菜端出来。他们靠着窗户坐下,王碧薇将菜单递给张小庆,说,点菜。张小庆说,我不会点,还是你点吧。王碧薇将菜单拿回来,打开,说,哎呀,还担心你点贵了呢,这下放心了。王碧薇点了个地三鲜,她还是问了张小庆,你吃什么。张小庆说,随便。王碧薇问了站在身边的服务员,这里有随便吗?服务员说,没有。于是她对张小庆说,没有,换一个。这下张小庆的脸果然就如她期望的那般红了,说,那就来个青椒肉丝吧。张丽点了清炒油麦菜。等菜的空儿,三个人聊起来,王碧薇说,我最喜欢吃地三鲜了,一道菜吃到三种菜,合算。张小庆说,我什么都行。张丽说,我喜欢带叶的菜。王碧薇说,迈克,你是刚毕业的吗?张小庆说,是啊。王碧薇说,那你和张丽一样,她也是刚毕业的。张小庆说,你呢?王碧薇说,保密。

      一会儿,地三鲜先端上来,放了些西红柿,颜色顿时香起来,煞是生动。张小庆坐在那里,等着王碧薇和张丽先动筷子。王碧薇看看张小庆,笑了笑,说,还等谁呢,开动啦。

      吃完饭,王碧薇要去贵园北里,张丽要去贵园南里南区,张小庆要去鹿圈,三个人就此分手。王碧薇突然问了张小庆,迈克,你为什么总背个包啊,里面装什么东西了吗?张小庆措手不及,一时不知道该怎么回答才好,总不能说这样看起来像白领吧,他就那样愣在那里,支支吾吾不出来。王碧薇说,哈,我知道了,我刚上班时也这样。张丽在旁边眯起眼睛慢慢笑起来。

      张小庆没有马上回鹿圈,他去了富源里小区,昨天下班时他在电线杆上看到了出租信息,300块钱一个月。房子在顶层,张小庆爬上9楼敲开门,他大开了眼界,100多平米的房子里竟然住了有十来个人,全部是高低床,还有在地上打地铺的。从小区出来,张小庆反复对自己说,便宜没好货,便宜没好货,便宜没好货,靠,便宜没好货。

      是汤姆最后帮助了他,汤姆是个白白净净的年轻人,个子不高,带一副眼睛,是公司的翻译项目经理,研究生毕业。汤姆刚好在找人合租贵园南里的房子,他和张小庆聊起来,两个人发现,他们还是老乡,这拉近了两个人的关系。下班之后,张小庆跟着汤姆去了他租的房子,这是一间小两居,六层中的第五层,汤姆住大间,张小庆住小间,这小间确实足够小,因为原本是作餐厅的。汤姆说这房子是个老太太的,老太太是这里的农民,拆迁之后在这个小区分得了三套房子,自己住一套,往外租两套。这套房子的价格是每个月950块钱,如果没有意见的话,他出500,张小庆出450,包物业和取暖,水电单算平分,因为小间没有家具,张小庆的衣服可以放在大间的衣柜里。张小庆非常满意,他坐了坐小间里的单人床,非常好,非常稳,坐上去不会发出吱吱的声音,也没有让人突然陷下去的大窟窿,而且,房间里还有一张餐桌可以当书桌使用;他去看了厨房,管道煤气,尽管燃气灶上糊了厚厚的一层油渍,旁边还有残留下来的方便面沫和鸡蛋壳屑,但不影响使用;他去看了厕所,终于不用在大庭广众之下露出屁股了,有插电的热水器,也不用一边洗澡一边倾听隔壁的挣扎声和落水声了。张小庆几乎是不假思索的马上决定搬过来住,他和汤姆说好,这个月底发了工资就搬过来。汤姆也很高兴,他给他的老乡煮了一碗方便面,打了一个鸡蛋,吃完后又强烈挽留老乡在他那儿洗了个热水澡。从汤姆家出来,张小庆的整个身子都清爽了,不是洗澡的缘故,他走得很带劲,甚至都哼起不着调的歌来,走过凉水桥的时候,他想,再过几天,就再也不用到这边来了,这真令人高兴。

      第二天早晨上班的时候,走出门,张小庆突然想起点什么,他想起了王碧薇那双笑成一条缝的毛毛眼,于是,他返回去,将挎包放下,这才重新出了门。

      发工资了,张小庆这个月少上了2天班,发了1988块6毛,和他预想的差了一点点,不过这并不影响他的心情,他将工资卷起来装进自己的裤子口袋,厚厚的一叠,在大腿上鼓起一块,很有成就感。更让他感到高兴的是,比尔找了他,对他这个月的工作很满意,决定提前转正,工资从下个月开始涨到3000块。对程序员来说,能够得到工作的肯定,是一件幸福的事情,最怕的是,经理只会分配任务,从不知道有与手下私下沟通这回事。

      1988块6毛,能干些什么呢?首先是要付新房租,要搬家,恩,这是最重要的;其次是请王娓娓和余鹏吃饭,这是说好了的,让余鹏过来替自己搬东西;还有,要给妹妹买台MP3,自己答应过要给她一个惊喜的;最后,扣掉生活费,应该还能剩500块,去银行办张银行卡。事真多啊,张小庆想,但是每一件都是那么让人愉快,对了,还应该好好犒劳一下自己,怎么犒劳呢?还得想想。

posted @ 2011-03-19 22:27 ronghao 阅读(2455) | 评论 (4)编辑 收藏

张小庆的第一个任务是实现一棵菜单树,比尔没有限定时间,他给了张小庆一个地址,告诉他可以参考他们的实现。这是金蝶上次来公司做售前安装的ERP试用版本,同一时间只允许一个用户在线。从此以后,张小庆和比尔将无数次的访问该ERP以及辛苦的查找其对应的源文件,有很多次,比尔打开ERP的某个页面,然后告诉张小庆,我们需要这样的一个功能;又有很多次,比尔打开ERP的某个页面,然后告诉张小庆,看,这里有个类似实现,找找他们的源文件。当然,在张小庆每次访问ERP之前,他都得记得对比尔说,比尔,你在用吗,我要ERP。如果忘记的话,比尔的桌面上将华丽的弹出一个警告框同时强迫比尔下线,这应该是该ERP实现最酷的部分了,张小庆想。

找源文件是一件苦力活,同样是JSP加JavaBean,金蝶的工程师应该都是警察出身,上千个JSP,一个JSP嵌套一个JSP,一个JSP再引入另外一个JSP,JSP们也玩躲猫猫。终于,张小庆在一个几百行的JSP里艰难找到了一行Java  script  代码:XTree tree = new XTree()。在这个简短的JSP里爬满了注释:xxx增加于xxxx年xx月xx日,华丽的分割线;xxx修改于xxxx年xx月xx日,又一条华丽的分割线。不管怎么说,找到了,张小庆长长嘘出一口气,过段时间看自己写过的代码都是一种痛苦,更别说看别人的代码了,除了煎熬张小庆实在想不出更合适的词。

张小庆搜索了XTree并访问了其网站,很快,他就被作者强大的Java  script  编程能力所折服了,强大的页面JS组件,以前从未见过的超酷的动态效果,新鲜的好听的名字DHTML,最最重要的是:这些Java  script  代码张小庆能看懂的不超过10行。张小庆被彻底打倒了,真他妈酷,张小庆想,在他印象里, Java  script  只是实现一些辅助的页面效果和简单的校验使用,但现在,XTree给他打开了一扇新的窗户,他重新搜索了DHTML,这次,Google给他打开了一扇大门。

吃午饭的时候,张小庆问了比尔:为什么金蝶卖的这么贵?

比尔说,因为它叫ERP。

张小庆说,这么贵有人买吗?

比尔笑了笑,说,所以你我才有机会坐到这里。

吃完饭,张小庆接到了余鹏打来的电话,余鹏说,庆娃子,在哪儿呢?

张小庆说,鹏娃子,我上班呢,你哪儿呢?

余鹏说,在亦庄呢,没吃午饭,快饿死了,快请我吃饭!

张小庆和余鹏是死党,他们是同一所学校老师的孩子,余鹏之所以来北京,用他自己的话讲,全是张小庆忽悠的。正月初三的早上,张小庆去余鹏家找了他,其时余鹏刚刚奋战完一个通宵的麻将睡下没多久。

张小庆说,我靠,还睡呢!

余鹏说,这么好的天气不睡觉做什么?说这话的时候,他正懒懒的躺在床上打了个长长的哈欠,眼睛都没有睁开,眼角上挂满了刚刚分泌出的新鲜眼屎。

张小庆说,别睡了,去北京吧。

余鹏翻了个身,背对着了张小庆,说,去北京干鸟?

张小庆说,找北京的工作,买北京的房,谈北京的老婆,生北京的娃。

余鹏对张小庆的提议并不感兴趣,因为回答张小庆的是他忽高忽低的鼾声,这让张小庆很生气,于是他上前一把扯掉了余鹏的被子,他知道余鹏的习惯,这是个睡觉不穿衣服的家伙,再加上南方没有暖气,余鹏果然就如同他期望的那样从床上白花花的弹起来。

余鹏说,大哥,会感冒的。

张小庆说,我知道。

余鹏说,感冒会发烧的。

张小庆说,我知道。

余鹏说,发烧会很难过的。

张小庆说,这个我也知道。

余鹏说,知道还不把被子还给我?

张小庆说,又不是我感冒。

两个人终于在床边有些认真的扯淡起来。张小庆知道,他面前的余鹏已经是小余老师,余鹏读的是师专,比张小庆早毕业一年,毕业之后就回到他们的镇上学校教历史和政治,也算是子承父业。此时的张小庆,对未来充满足够的热情,尽管没有去过北京,但是他对这座城市时常荡漾着首都的朴素情感,他坚信,他的未来不是梦,而在这座遥远的城市里,有着他的梦。他竭力的鼓动余鹏,走吧,一起去北京。余鹏反复在问同一个问题,为什么要去北京。张小庆说,北京是首都,余鹏说,这不是理由;张小庆说,北京发展机会多,余鹏说,太抽象;张小庆说,北京挣得多,余鹏说,消费多;张小庆说,北京漂亮姑娘多,余鹏说,只有看的份;张小庆说,王娓娓在北京,余鹏说,这勉强算一个理由;张小庆说,你不去我会想你的,余鹏说,好吧,这听起来好像有点靠谱了。

过了两天,张小庆和余鹏一起去了王娓娓家,王娓娓和他们也是同一个学校院子的,大他们两届,一毕业就在北京工作,从余鹏家到王娓娓家不过十来步的距离。王娓娓热情的接待了他们,他们在沙发上坐下,王娓娓的父母王老师和黄老师端来了热水和点心,三个人一边嗑瓜子一边愉快的聊天,电视里,正在播放陈红主演的《纽约风暴》。张小庆详细问了王娓娓在北京的工作和生活情况,回答是让张小庆满意的,没有想象中的那么好,当然,也没有想象中的那么糟,只要找到工作,工作努力,踏踏实实,那么两年三年的过去,会越来越好,买个房,谈个朋友,结婚,生活似乎是一条坦途,闲暇的时候,可以去看看电影,看看演出,北京有着很多著名和不著名的景点,这些,都是北京所有魅力的一部分。王娓娓问了张小庆和余鹏的想法,张小庆说先要找到工作,余鹏说还没有想好。最后,告别时,张小庆突然问了王娓娓电视里那个漂亮女主角结婚没老公是谁,王娓娓说接了是陈凯歌,张小庆叹口气,说又是老牛吃嫩草。张小庆和王娓娓约定好了,说北京见,余鹏没有表态,他还是需要个理由。

余鹏的理由来得比张小庆的车票早,张小庆刚在周扬的地下室里蹲了一天,还没开始找工作,余鹏就来了电话,他在电话里得意的哈哈大笑:庆娃子,猜猜我在哪儿,我已经到北京上班了。张小庆急急的说,你在哪儿?什么时候来得?什么工作?余鹏故意调张小庆的胃口,说,见面说。张小庆说,靠。于是张小庆余鹏王娓娓再一次见面了,这次是在北京,一个星期天,王娓娓休息。张小庆和余鹏分别从各自住得地方坐公交车到了铁狮子坟,王娓娓在那里租的房子,三个人约定在北师大的校门口见面,余鹏先到了,张小庆跳下公交车,余鹏正在公交站台上眯着眼睛等他,他的手插在裤兜里,倾斜着身子,两个人见了面,第一句话是:狗日的!然后两个人相视哈哈大笑,在校门口用方言肆无忌惮的大声喧哗起来,张小庆问了余鹏工作的情况,余鹏告诉他他在草桥一家建筑工程公司上班,销售建筑材料,这家公司是他一个远房亲戚开的,也是刚成立,一个月800块钱底薪,包住不包吃。说到这里,余鹏从裤兜里掏出一张折叠过的因为遗忘而皱皱巴巴的宣传彩页,展开,递给张小庆,说,如果有机会帮他也宣传宣传。张小庆接过彩页,说,余销售这么快就进入角色了,搞个彩页也搞个完整的点吧。余鹏说,你就没这个必要了,有消息我会亲自上的,当然,少不了你的回扣。张小庆说,你可千万别发了,这样我会嫉妒你的。说话间,王娓娓到了,不用寻找,循着声音就闻出浓厚的家乡味。王娓娓皮肤白白的,刚烫过了头发,蓬松的扎着马尾巴辨。张小庆说,娓娓姐,你的头发真好看。王娓娓说,真的吗,刚烫的。张小庆说,假的。王娓娓说,讨厌。余鹏在旁边吃吃的笑。

三个人在王娓娓的带领下一起步行去了后海,一切在张小庆眼里都是全新的,宽阔的马路,亮晶晶的全玻璃的大楼,带辨儿的公交电车,狭小青砖的胡同以及步伐冲冲的人们。张小庆一路上都在贪婪的观看,他东张西望,他四处观望,他想理解眼前的这一切,他知道,他以后将生活在这里,他需要习惯这里;余鹏双手插在裤兜里,斜着身子,在后面慢慢的走,他是懒懒的,满不在乎的;王娓娓一路上不停的在纠结自己的头发,她显然是对张小庆的玩笑在乎上了,她终于再一次问了张小庆,这头发真的做的不好吗?张小庆有了一点点尴尬,说,我开玩笑的,做的真的很好。王娓娓仍不放心,说,你是说做的很好,还是做的很适合我。张小庆开始为他的玩笑后悔了,说,做的很好,非常适合你,很漂亮。王娓娓这才放下心来。

后海完全是另外一种风情,它是安静的、悠闲的、放松的和慢性子的。张小庆余鹏王娓娓三个人在路边买了三瓶矿泉水,一个人手里拿着一瓶,顺着河沿慢慢的漫无目的走,不时有人力三轮从他们的身边经过,手拉着车把上的绳儿,车上的铃儿就清脆的响,三个人就往路边闪一闪,花坛边上,有晨练还没回家的老人,在一棵树上,他们还看见了一串的鸟笼,不知名的鸟儿在里面唱着不知名的歌儿,一如旁边酒吧里的歌手。在经过一家酒吧时,张小庆微微骚动了,这家酒吧叫欲望都市,这是个充满欲望的名字,容易让人产生联想。王娓娓说,这里晚上有钢管舞。等我有钱了,一定要来一次这里,张小庆想。三个人最后在一个花坛边上坐下,王娓娓再次问了每个人的想法。张小庆说,我要去微软和IBM,一定会去。余鹏说,多挣钱。中午王娓娓请张小庆和余鹏吃了韩国烧烤,余鹏来了瓶啤酒,王娓娓和张小庆一人来了份果汁。很多年过去,很多细节已经释去,张小庆印象深刻的是最后端上来的金银小馒头,真的是好吃,沾上点果酱,甜甜的,正如北京给他的第一印象,此后有很多次,在很多家餐馆,张小庆一次次的点到金银小馒头,可惜,再也找不到当初这种心动的感觉了。如果找不到工作,我就回家做金银小馒头,张小庆说。王娓娓笑起来,我支持你,来,一起干一杯,加油!张小庆突然就觉得王娓娓的头发真的很好看。张小庆余鹏说,加油!

张小庆说,你怎么到亦庄来了?

余鹏说,哪里有塔吊哪里就有我。

张小庆说,你想吃什么?

余鹏说,一听这话就知道你的心不诚,算了,走了。下次等你发了工资再来宰你。记着啊,你欠我一顿。

发工资,是啊,张小庆从来没有如此热烈的盼望发工资,少上2天班,应该还有2000块,他想。

posted @ 2011-03-13 21:05 ronghao 阅读(1753) | 评论 (0)编辑 收藏

【荣浩作品,欢迎转帖】

张小庆和他的经理在礼拜一的早上再次见面了。经理是个子高高的年轻人,穿一条洗得发白的牛仔裤,上身是一件灰色的衬衣,方脸,浓眉毛,衬衣整整齐齐的扎在裤子里,看到张小庆,他们打了招呼。


经理说:早,小庆,来得真早,找到房子了吗?上班还顺利吗?


张小庆说:早,找到了,挺顺利的。


经理说:那就好,饮水机在那里,带杯子了吗?没有带的话下面有一次性的,带饭盒了吗?公司中午提供午饭。


经理的话让局促的张小庆暖和起来,他连忙说:带了。


经理的工位就在张小庆的对面,抬起头来就能互相看见,经理站在对面擦自己的桌子,说:对了,公司要求每个人都有个英文名,我叫Bill,你以后叫我比尔就好了。听到这话,张小庆的心忽然长长呼出一口气,他其实很担心如何称呼他未来的经理,叫方哥吧,会不会太亲切而不够正式?叫方经理吧,会不会太正式而不够亲切?叫方师傅吧,会不会让人想起方世玉?这下好了,什么问题都不复存在了,一切都云消雾散了。


王碧薇从旁边探出她的毛毛眼,说,啊,刚才忘了,我叫Susan,小庆,你叫什么?


张小庆说,我要想想。


第一天工作的内容总是相似:搭建开发环境和了解系统。比尔给张小庆共享了一个目录,张小庆从里面拷贝了JDK1.4、Jbuilder2005、Tomcat4.0、MySQL4.1以及项目源代码文件。张小庆打开电脑,操作系统是Windows2000,他安装了JDK1.4,接下来,他左键选中“我的电脑”,右键菜单点击“属性”,找到高级选项,点击了下面的环境变量按钮,有两栏,上面一栏是用户环境变量,下面一栏是系统变量,在系统变量一栏里,他点击了新建,在接下来出现的系统环境变量编辑框里,他输入了变量名“classpath”以及变量值“.” ;接着,他又新建了一个变量,变量名是“JAVA_HOME”,变量值是JDK的安装目录;最后,他找到系统变量里面有个path的变量双击了它,在变量值最前面加上了“%JAVA_HOME%\bin;”。做完这一切,张小庆打开命令行,输入“java”,屏幕上反馈出长长一串的帮助信息,好了,张小庆想。


系统还未上线,张小庆访问了比尔机器上正运行着的实例,这是公司内部的业务流程系统,公司主要的业务是翻译和本地化,系统主要对公司内部的工作进行协调。


中午的时候,张小庆和比尔一起去楼下的员工餐厅吃了饭,公司的午餐外包给了一对年长的夫妇。每天中午,张小庆透过窗户看见一辆白色的面包车晃晃悠悠的开进大门,他就知道,午饭时间到了。车在楼下停稳,女人从驾驶的位置下来,看看表,点上一支烟,抽烟,男人从副驾驶的位置下来,拉开滑动门,将盛有饭菜的不锈钢桶往餐厅搬,6个桶,四菜一汤两荤两素加上米饭和大饼。张小庆喜欢他们做的酸辣汤,豆腐、冬菇、海参、鱿鱼分别切成细丝,同熟肉丝、熟鸡丝稠稠的纠缠在一起,最上面漂浮着懒懒的鸡蛋花,喝一口,酸酸的,带点微辣,刚好使鼻尖有点微微渗出的汗意,一切都是恰到好处。如果是酸辣汤,张小庆吃饭的速度会稍稍快一点,他要抢两碗。等大家都吃完饭,一切都反过来,男人到门外,看看表,点上一支烟,抽烟,女人负责收拾,把6个桶两两落在一起,搬上车,关上滑动门,然后坐上副驾驶的位置,等男人抽烟,男人抽完烟,坐上驾驶的位置,点火,发动汽车,面包车就晃晃悠悠的开出大门。


吃完饭,张小庆陪王碧薇去找了房子。这个春日的中午,王碧薇像一只被放归大自然的小兽敏捷而灵活地跑来跑去,拖着张小庆前行。对一个女人来说,最重要的不是漂亮的外表,也不是各种名贵的衣服和首饰,更不是化妆品堆积起来的庸俗,而是光线,是走在千千万万女人群中一眼就能标新立异的那种光线。王碧薇的个子并不高,她长得严格意义上算不上漂亮,但她拥有光线,无限的光彩从她轻盈的身体和愉快的笑声里溢出来,光芒四射,流光溢彩,照得张小庆眼睛生生的疼。他们一起去了广德苑小区,一起去了贵园东里,一起去了星岛嘉园,一路上都是王碧薇在不断的问问题,张小庆不停的回答,他们一起去看了小区公告板上的出租广告,一起去社区服务站问了大妈们有没有出租信息,一起给广告上的电话号码打了电话,一路上都是王碧薇走在前面,张小庆就是个跟屁虫的份。


回公司的路上,王碧薇问了张小庆:小庆,你的英文名字想好了吗?


张小庆说:想好了。


王碧薇说:真可惜,我也帮你想了一个,叫什么?


张小庆说:米歇尔。


王碧薇说:怎么拼的?


张小庆说:M-i-c-h-a-e-l。


等张小庆拼完最后一个字母,王碧薇大声笑起来了,她笑得上气接不上下气,笑得她那双好看的毛毛眼又眯成了一条弯弯的线,她一边笑一边说:米歇尔,笑死我了,哈哈,米歇尔。


张小庆现在不仅仅是涨红脸那么简单了,他感到整个人都肿胀起来,他开始后悔中午喝了两碗酸辣汤,第二碗酸辣汤的后劲上来了,到处都在冒汗,根本堵不住,他甚至感到在他背后已经腾起了一股巨大的水雾,他不知道什么地方出错了,但一定是什么地方出错了。看到张小庆的囧样,王碧薇的眼泪都出来了,她说,小庆,这个单词不念米歇尔,念迈克尔。


张小庆的拼音很差,他从来分不清前鼻音和后鼻音,也分不清卷舌音和翘舌音,这让他的语文成绩一直很烂,一直到小学三年级作文题的出现,这才稍微扭转颓势,到初中,拼音题成功的在语文试卷中被压缩为一道选择题,张小庆窃喜的同时却悲哀的发现:英语出现了。张小庆想了很多方法来对付这个问题:在一次考试中,他将所有与发音有关的选择题全部选了A,结果对了一个;在另外一次考试中,他将所有与发音有关的选择题全部选了B,结果还是只对了一个;接下来,他又分别试了C和D,结果依然并不乐观;最后,他将自己的橡皮擦削成方方正正的正方体,在其中四面用铅笔标上了A、B、C和D,遇到不会的,将正方体抛向空中,接住,看,那面冲上,就填上那面的选项,这种方法至少从概率论的角度成功的将他的命中率提高到了百分之二十五,然而好景不长,到了高中,他惊恐的发现:不定项选择题出现了,这对张小庆来说几乎是一种秒杀级别的题型,选一个就已经非常困难,竟然还有可能选多个,而且他妈的数目还不能确定,这意味着小小的正方体已经容纳不下所有的可能答案,张小庆在心里诅咒了设计这种题型的专家,他想,这个家伙一定也是上学时为选择题而苦恼的家伙,人总是这样,自己吃过的苦一定要让后来人再吃一遍才觉得心理平衡,甚至更加变本加厉。


下午的时候,张小庆问了比尔咱们现在有几个人,比尔回答说目前就咱们两个,公司还在招。张小庆问如何提交代码,比尔回答说本地测试没有问题后将源文件拷贝到共享的项目源文件目录下覆盖即可。张小庆问如果我们俩修改的文件冲突怎么办,比尔说我会划分模块,我们不会同时修改同一个地方。张小庆问我们如何生成可运行的包,比尔说使用Jbuilder直接编译,然后直接拷贝文件夹。张小庆说,好,我知道了。这就是张小庆的第一个正式项目,Jsp加上JavaBean,足够简单,足够好玩,足够让张小庆兴奋一段时间。


公司六点下班,五点五十分的时候,张小庆瞅了比尔,比尔没有下班的意思;五点五十五分的时候,张小庆瞅了比尔,比尔还没有下班的意思;五点五十八分的时候,张小庆瞅了比尔,这次,他发现比尔也在瞅他,比尔说,迈克尔,可以走了。张小庆说,好,你什么时候走?比尔说,马上。这时让张小庆一直担心的事情发生了,旁边正在收拾包的王碧薇说,米歇尔,拜拜。王碧薇是故意的,这让张小庆的头放大了,他不知道回答什么好,回答吧,不是,不回答吧,也不是。正在苦恼的时候,王碧薇又说话了,拜拜,迈克尔。这次是正式的了,张小庆吐出一口气,说,拜拜。


下班后的张小庆先去楼下的联通营业厅办了电话卡,他的电话卡还是来北京前在老家买的,这让他一有电话就要到处寻找公共电话,一如尿急了到处寻找公共厕所。张小庆顺着马路慢慢向回走,在一个十字路口,他看到了小白羊超市,他想起周扬说过的话来:如果看见小白羊超市,那么证明你在郊区;如果看见京客隆,那么证明你在城乡接合部;如果看见美廉美和物美,那么,恭喜你,你已经进城了;如果看见家乐福和沃尔玛,好吧,欢迎来到北京。


顺着早上上班的路,张小庆知道,一旦跨过凉水桥,那将是另外一个世界。傍晚的凉水桥是吵杂的,附近工厂上班的人们下班了,三三两两的往回走,那多是年轻的女孩们,她们叽叽喳喳,说着白天碰到的人和事;菜市场的菜农们买完菜,骑着平板三轮车往回走,脚蹬在车链盘的脚蹬上,链条带动车轱辘咕噜噜的响,有一次,张小庆看见他的房东了,房东是个慢性子,每次并不使劲蹬车,直到车快停下来,这才慢悠悠蹬上一脚,车后的平板上,堆着张小庆熟悉的大蒜们,那里也许就有那么一个是在张小庆的床上睡过的;还有一个个的妇女们,她们或背着或牵着孩子,另外一只手里拎着晚上要做的菜,步伐匆匆的往家赶,为她们的男人做饭,张小庆碰到和他一起同住一个院子的另外一个租户了,那是个年轻的妈妈,牵着她女儿的手往回走,小女儿似乎刚刚被妈妈斥责过,脸上还挂着两行泪痕,很不情愿的被拖着走,走两步总要停一会,两眼汪汪的看着妈妈,想妈妈抱她走,可是妈妈腾不出手,于是母女俩在凉水桥上较着劲。张小庆走上前,打了招呼:回家啊,我帮你提着菜吧。妈妈感激的瞅张小庆一眼,说,孩子他爸回来了,要回去做饭,可这孩子真不听话,硬要抱。于是张小庆帮妈妈提了菜,妈妈抱了孩子,小女儿破涕为笑,三个人一起往回走。聊天中,张小庆知道孩子一家是山东人,孩子爸爸在这边一家工厂开货车,长途,经常十天半月的出车,孩子妈妈在这边带着小女儿在一家杂货店帮忙,小女儿四岁,还未上学,跟着她妈妈在店里玩。妈妈说,吃饭了没?一会儿过来一起吃。张小庆说,没事,吃过了。妈妈说,在哪儿吃的?张小庆说,外边。妈妈说,你可以自己做,这样不仅吃得好还很便宜。张小庆的脸红了,轻声说,不会。妈妈笑起来,说,谁一开始都不会,我教你,还有,房东是卖大蒜的,我们整个院子吃大蒜都不花钱。张小庆也跟着笑起来,他们稍微加快了步伐,桥下,河水依旧不紧不慢的流淌着。


晚饭对张小庆来说始终是个问题,有时候张小庆会到小白羊买个鸡排和两个馒头一共两块五回家吃,但大部分时候张小庆会在鹿圈徘徊,随便找个馆子进去。这边的馆子总是风格一致:一间不大的平房,门上顶着长方形名字各异的招牌:实惠酒家、好再来饭店、东北菜馆,让张小庆奇怪的是,这些招牌竟然都是惊人的一致:除去名字不同,招牌的背景都是同一个光鲜的苗族少女举着某酒露出白花花的细碎牙齿,这是编译好的模板,馆子名字是变量,张小庆想。很少有发光的招牌,于是,这些饭馆的老板们再次行为一致的在门口立上了小灯箱,夜幕降临,各色各样灯箱里橘红色的灯光就依次亮起来,于是,张小庆看到:酸辣土豆丝5元、地三鲜5元、鱼香肉丝8元、木须肉8元、水煮鱼15元。


走进店里,人很少,不到10张桌子,其中一张桌子上还残留着餐具未来得及收拾,前面一桌上有5个中年人正在热烈的喝酒,脸都喝得通红,棉衣都喝得解开了,其中两个人将左脚直接踏在自己的椅子角上,另外一个人正用牙签剔他那粗犷的牙缝,他们一边喝酒一边大声的说话,地上和桌子上都摆满了啤酒瓶,一个人冲站在旁边的服务员小姑娘大声说,再来5瓶啤酒,另外一个人拉住他,说,哥,不行,不能要了,喝完这个吃饭,吃饭,前面那人说,不行,到哥这里,不喝好怎么行,服务员,5瓶啤酒,快点!张小庆找个角落坐下,菜单压在桌子的玻璃下面,透过玻璃看菜单的时候,张小庆发现了桌子上的一团油渍,更远一点,他甚至发现了几粒饭粒,而桌子的里角,则糊满了已经发黑的灰尘,张小庆叫了服务员,可小姑娘根本没有时间理他,他于是自己拿了一次性纸巾将油渍和饭粒小心的拭去。他要了一份西红柿鸡蛋面,4元,等面的空儿,他看了挂在墙角的电视,电视里正在播放超级女声的广告,酸酸甜甜就是我,张小庆觉得这广告词形容他的面条很合适。吃完面,张小庆喊住了正皱着眉头收拾啤酒瓶的小姑娘,说,你们这儿的炒菜能做半份吗?小姑娘看着被踏的全是泥的椅子角,没好气的说,不能。张小庆说,你问一下老板吧,如果可以,我每天晚上都来。小姑娘瞅一眼张小庆,不耐烦的掀起布帘走到后厨去,一会儿,出来,说,不行。张小庆被打击了,走出饭馆,他想,再不来这里了。


饭馆对面是个电话超市,鹿圈的电话超市很多,一到晚上,都人满为患。张小庆等了好一会才轮到他,每个人的电话时间都很长,人们操着各式各样的方言,或热烈或有一搭没一搭的说着话,轮到张小庆,他先给王娓娓打了电话。


张小庆说,娓娓姐,我上班了。


王娓娓说,是吗?太好了,恭喜啊,有时间我去看你。


接下来,他又给余鹏打了电话。


张小庆说,余儿鹏,我上班了。


余鹏说,狗日的,可以啊,请我吃饭。


相比晚饭,张小庆的早饭就要容易的多。正对路口,有一家好吃酒家,每天早上,天还没亮,老板就将火炉旺旺的在门口生起来,先是蒸包子,等到有客人到来,就换上油锅开始炸油条。张小庆每天去的时候,正是人多的时候,没有位置,一地饱含鼻涕和口水的餐巾纸,很多人就站着吃,一有人离开,就有人快速的抢过去,一边坐下一边大声的拍桌子:老板,老板,把桌子收一下。老板这时候人手是不够用的,人们呼喊一声没有反应,就自己将前面吃剩下的碗们盘们推向一边,然后吃饭。这里没有服务员,服务员是你自己,你需要自己去油锅前排队,去抢那油条;你需要自己拿碗,去门外的粥桶前打豆浆和稀粥;你需要自己去取小碟子,咸菜就在刚进门的桌子上躺着;老板在油锅前汗流浃背,你甚至需要自己去算自己吃了多少钱,然后大声的对老板说,老板,钱放这里了,老板说,好的好的,走吧走吧。张小庆的习惯是一碗豆浆两根油条和两碟免费的咸菜,一共一块一毛钱。


吃完早饭,张小庆整理了自己,他看看自己的皮鞋,这双皮鞋昨天刚踩到一坨冻僵的人屎,在桥那面,路上多的是狗屎,在桥这边,路上多的是人屎。张小庆知道,新的一天又开始了,前面,凉水桥向他徐徐走来。

posted @ 2011-03-06 17:13 ronghao 阅读(1482) | 评论 (1)编辑 收藏

【荣浩作品,欢迎转帖】

    一切都有一个开始,连语文老师都说:任何文章都有开始、发展、高潮和结局。张小庆的程序员生活开始于一个星期一的早上,天气很好,初春的空气中还略带一些寒意,张小庆正快速走在从鹿圈到亦庄的凉水桥上,他穿着一件黑色的皮夹克,略微发白的牛仔裤,脚上是早上精心擦过的黑皮鞋,肩膀上挎着昨天刚刚从集贸市场买回的黑挎包。远处,红彤彤的太阳还未完全苏醒;脚下,臭烘烘的河水在缓缓流淌;脚后,低矮黯淡的房屋们渐去渐远;前面,一幢幢的高楼正徐徐向张小庆展开怀抱。很难形容张小庆此刻的心情,有一点点兴奋,这是他的第一天上班;有一点点期待,自己一定能够做出成绩;又有一点点担心,不知道未来的同事和领导好不好相处。


    张小庆找到这份工作并不容易,在此之前,他和周扬一起挤在西钓鱼台的地下室里。周扬是张小庆一个远房亲戚的远房亲戚,换句话说,就是他们一直并不认识,直到他们的父母坐到一起,一起聊起很久,这才一拍大腿,原来他们他妈的曾经是亲戚。周扬比张小庆小2岁,中专没毕业就来北京了,他在西钓鱼台的一家牙厂里当送货员,每天的工作就是吃早饭,取牙齿,做上公交车,穿行在北京的大街小巷,将牙齿交给散落在各处的各个男男女女的牙医们。


    过完年,张小庆给周扬打了个电话,兄弟,过完年投奔你去啊。周扬说,来吧。真正到了北京,到了周扬住的地方,张小庆顺着楼梯往周扬住得地下室走,张小庆不禁说,我靠,北京的地下室真他妈深。周扬笑了笑,说,这是全地下室,以前的防空洞改的,我今年的目标是往上升一级,住半地下室。张小庆说,厕所在哪儿?周扬说,前面拐弯就是,注意,整个一层就这么一个厕所,早上起床要排队。张小庆说,洗脸在哪儿?周扬说,厕所旁边,有两个龙头,一个出水,一个不出水。张小庆往前走了一会,长长的过道里只有一盏半死不活的灯泡发出奄奄一息的光,借着微弱的灯光,他也看清楚了拐弯处的厕所,厕所的门上用油漆大大写着“禁止随地大小便违者罚款”几个大字,门四周,几坨或干枯或新鲜的大便们一脸无辜的蜷缩在一起,与这几个大字默默相对,彼此无言。张小庆说,这环境可真够恶劣的。周扬说,便宜,价格公道,50元一月。到了周扬租的不到5平米的小屋,打开灯,张小庆说,你的灯也这么暗?周扬说,房东规定了,超过15瓦的灯泡要罚款。张小庆说,我靠,这都什么人啊,我们怎么睡啊?周扬拿起镜子照他的头发,小心翼翼的将他稍微翘起的几缕头发归置好,说,我俩一起睡,你睡我脚头,别担心,我对男人不感兴趣。张小庆说,可是我对男人感兴趣。周扬说,妈的,不是吧。两个男人就那么在狭小的地下室里疯了一会,直到周扬求饶这才停下来。周扬说,完了,头发又被你搞乱了,头可断,血可流,头发不能乱。周扬的头发留得长长的,分的开开的,墙上,八神庵的大幅战斗画像神情严肃。


    张小庆开始找工作,每天早上,他和周扬一起起床,一起到周扬的工厂里蹭早饭,然后分开,周扬去送货,他则去附近的报刊亭看最新出的北京人才市场报和前程无忧,北京人才市场报便宜一些,5角钱,前程无忧要1块,他于是总站在报亭门口翻看前程无忧,直到老板不耐烦了这才说来份北京人才。买完报纸,张小庆回到地下室,在那里,他掏出铅笔,把自己感兴趣的招聘会画上圈。然后,他开始看书,他从家带着3本书,分别是京京工作室翻译的《JAVA编程思想》、孙伟琴的《Struts实战》以及《程序员2004年合订本》。张小庆本科学的是机械,他大三下星期才开始自学JAVA,他学JAVA源自他大学计算机老师的一句话,在他们学校,计算机编程属于选修课,可以想象当一名年轻老师面对偌大空空如也教室心中的郁闷之情,年轻人说,他们什么都不懂,学好VB能拿3000元一个月,学好JAVA能拿5000元一个月。张小庆知道年轻人还未对逃课做好思想准备,他自己做了个简单的布尔计算,选择了5000元。


    张小庆去了海淀体育馆人才市场,在那里,刚举办完北京国际斯诺克大奖赛,喧嚣散去,只剩下丁俊晖偌大的脸孤零零的飘荡在空中,他来到2楼,几家招工的企业零星散落,几个似乎是企业招聘人的人站在一起抽烟、跺着脚、咒骂着该死的天气还不热起来;张小庆去了海淀人才市场,在那里,他被收了5块钱入场费,入了场他才发现上了当,人员寥寥,剩下的人不是在收拾东西,就是在打个哈欠不停看表;终于,张小庆去了中关村人才市场,在那里,即将举办一年一度的高科技企业招聘专场,张小庆见识了什么叫大场面,人山人海,一波一波的向入口涌去,玻璃门瞬间被挤破了,玻璃散落一地,保安在破口大骂,人们根本站不住脚,有人的简历被挤掉了,有人早上带的豆浆被挤破了,有人在高声抱怨,有人在幸灾乐祸,有人在不顾一切的向前挤,一切都是那么的混乱、无序。张小庆被裹在人流中不由自主的向前涌去,根本没有时间看清楚企业到底要招些什么样的人,最开始他还使劲的踮起脚尖,到后来就只是机械的投简历了。大多数企业都是不冷不热的,问两句,说,回去等通知。也有苛刻的,大声敲着桌子,说,把期望工资写上、写上!张小庆犹豫了片刻,写上了1500元。也有特别热情的,那是培训机构,拉着你的手不松开,说,高薪就业,IT高薪就业!


    从招聘会出来,张小庆情绪低落,已经是春天,道路两旁的柳树发出绿芽,9点钟,正是上班的时间,路上的人们行色匆匆,没人会注意到这么个人,拿着装有简历的文件夹,穿着印有无数双新鲜脚印的黑皮鞋,一个人默默的在路上走,这个世界不是属于他的。张小庆看见一个穿着黑色西服的年轻人,一手拿着热腾腾的豆浆,一手拿着黄灿灿的鸡蛋灌饼,急匆匆的从他面前经过,他想,这个世界是属于他的;张小庆看见一对年轻的情侣,男人不知说了些什么女人突然大声笑动起来,热烈的从他面前经过,他想,这世界是属于他们的;张小庆看见路旁的肯德基里,一个女孩耳朵里塞着耳机手里拿着汉堡冲着太阳仰起她那张鲜艳的脸来,他想,这个世界是属于她的。


    晚上,吃过饭,周扬看到张小庆心情不好,一拍他的肩,说,走,上网去。张小庆上51Job,周扬在旁边玩传奇。张小庆一脸郁闷,投了那么多简历,一个回复都没有,周扬不停的砍人,一会说,妈的,没血了,靠,靠,一会又哈哈大笑,捡个装备,真他妈爽。周扬说,哥们,生活不过就是场游戏,要抓紧时间。


    张小庆得到了3次面试的机会,第一次在方庄的一幢居民楼里,上电梯时,他问了开电梯的老大妈,大妈,12层是有家软件公司吗?大妈盯着张小庆看了很久,说,你是干什么的?张小庆说,我来面试。大妈稍稍放下心,回答说,我不知道。接待张小庆的是一个中年的男人,他把张小庆接进屋,倒上一杯水,自我介绍说是这家公司的总经理。张小庆仔细的打量这套3居室,除去总经理室和厕所,所有的隔断都被打开了,房间里密密麻麻的排满了电脑桌,还没配置上电脑,每个工位只有不到2个平方,地上全是凌乱的线头,因为朝向的原因,室内光线很是黯淡。总经理把张小庆带进经理室,递给张小庆一支烟,说,抽烟吗?张小庆说,谢谢,不抽。总经理说,那我就不客气了。说完把烟给自己点上。几乎没有问太多问题,他就开始介绍自己,我以前在方正干过,这次和一个朋友合作出来自己发展,我们在管理软件方面积累有丰富的经验,公司的前景应该说是非常好的。等总经理把话漫长的说完,张小庆小心翼翼的说,我看到公司现在只有您一个人,是这样吗?总经理顿了顿,说,目前是这样的,我们正在招人,要知道,小公司要比大公司学到多得多东西。张小庆说,好,我想一想然后给您消息好吗。于是谈话结束了,总经理把张小庆送到门口,说,我觉得你的技术很不错,你可以好好想一想。从潮湿的居民楼里出来,重新见到明亮的阳光,张小庆凉了半截的心重新鲜活起来,也许这也是一个不坏的选择,起码是一份工作。


    张小庆的第二次面试在朝阳公园对面一幢独立的三层小楼里,坐在宽大明亮的会议室里,张小庆的心情不错,隔着巨大的落地玻璃,他能够清晰看见朝阳公园巨大摩天轮的缓缓转动,此外,漂亮而声音甜美的前台姐姐送过来的茶水正温暖的躺在他的手心里,这一切都感觉好极了。第一步是套试卷,基本上全是基本概念,这对张小庆并不困难;第二步是技术经理面,技术经理看过简历,眉毛挑动了一下,说,你会SpringHibernate?张小庆说,用过一些。经理说,你用什么做映射?张小庆说,XDoclet。回答的时候张小庆有些紧张,他知道这是04年最热的两项新技术,对它们他并不熟悉,他的了解完全来自于那本程序员的合订本。后面的回答果然就不是那么顺利了,经理问到了一对多映射、多对多映射和一对一映射,张小庆凭印象都一一回答过去了。还好,经理最后问到了MVC,这个是张小庆擅长的,因为他用过Struts。最后,经理说,我觉得你还不错,你期望的薪水是多少?这对张小庆是个困难的问题,他想说3000,但是他想到了中关村人才市场的1500,于是,斗争了一下,他说,2000。经理显然对这个数字感到小小的吃惊,他又问一遍,什么,多少?张小庆说,2000。经理微微摇了摇头,说,好,我们会尽快给你消息。事情到这里已经非常明显了,但是张小庆没有看出来,从公司出来,他甚至对自己大吼了一声,搞定!接下来,他去周围地下室看了看有没有房间出租,抄了几个手机号码,然后去成都小吃来了一碗担担面,他甚至还破天荒的多要了一份紫菜汤。结果自然是失望的,在摇摆的从东四环到西四环的701上,张小庆接到了他期待中的电话,一如紫霞,他猜中了过程猜错了结果。


    张小庆的第三次的面试在亦庄,周扬给他指的路,他到亦庄医院送过货,张小庆面试的公司正好在亦庄医院对面,这是一家翻译公司,张小庆过来做他们的内部管理系统。他先是在西钓鱼台坐368到方庄,然后再换乘小公共过去。这次面试出人意料的顺利,张小庆未来的经理和张小庆聊了半小时很快就决定录用他,再次谈到工资,张小庆这次说要2500,经理没有砍价,很快就同意了,于是约定下周一来上班。这次从公司出来,张小庆的心情反而平静了很多,他平静地坐上368,平静地给周扬发了个消息,平静地给第一家面试的公司打了电话告诉不再过去,他知道,找到工作是一件如此平常的事情,仅仅只是合适不合适而已。


    星期六的时候,张小庆再次乘坐368来到亦庄,这次他带上了自己的全部家当:两条褥子、几件换洗的衣服以及他的三本编程书,它们被塞在一个蓝色的编织袋中,由张小庆拖着前进,他找到了最后一排靠窗的座位,将编织袋塞在座位底下,依偎在窗边,看着两旁的建筑物向后流动,脑子里和窗外建筑物一样闪过无数的片段。这件事过去很多年之后,张小庆常常感叹,一个包就能搬家的日子早已经一去不复返了,不仅仅是身材日益臃肿,生活也日益臃肿。他常常想起那么一个早上,那么一个青年,拖着包,偎依在车窗边,尽管不知道未来怎样,却充满希望,他想努力看清楚那个青年的面容,却什么都寻不见。


张小庆最先来到贵园南里,那里离公司最近,有公司人力姐姐推荐的房屋信息,可是几个电话过后,他就泄了气,最便宜的房子都要1200,而自己身上只有不到500,更可况要交三押一呢。正在发愁的时候,他看见了一个老太太,老太太问他是不是在找房子,如果是的话可以和她的孩子合租,一个月800。张小庆点点头又摇摇头,咬咬牙,问,这里哪有更便宜的房子?老太太想了想,手往南一指,说,去鹿圈吧。走在鹿圈的路上,张小庆顿时想起一首诗来: 借问酒家何处有? 牧童遥指杏花村。他觉得改为:借问房子何处有?老妪遥指鹿圈村,挺合适。这样想着,他不禁笑了出来。


    跨过凉水桥,果然就是两个不同的世界,一边是楼房林立,一边则全是低矮的平房,一边道路宽广整洁,一边则是污水横流,张小庆甚至看见一个中年男人在一个靠墙的角落里满不在乎的小便,那架势,就好像这里是他家的私人厕所,上完厕所,打个激灵,抖一抖,这才慢慢的将裤子提上,旁若无人的离开。张小庆找到了一家四合院里的一间,房东是买大蒜的,他要租的房子原先是堆大蒜的,没有床,只有一个木板,中间还有一个大窟窿,四个角用砖头码起来,一碰就吱嘎吱嘎作响,见到有租客过来,房东拿扫把把木板上厚厚的灰尘扫了扫,于是木板就不停的叫起来。张小庆站在一旁邪恶的想,这每天晚上北京又有多少块床板在欢乐的歌唱啊。张小庆和房东商量好价格,一个月100,包水不包电,此外每月需要交掏厕所的费用2元,厕所在院子外边,用半拉子的砖头胡乱切成,不仅透风还透光,张小庆觉得在里面上厕所跟在大马路大广场上亮出屁股没有太大区别。


    商量好房子,张小庆去超市买了日常用品:水瓶、热得快、喝水的杯子、两个脸盆、牙刷、牙膏、镜子、吃饭的陶瓷缸子还有一个黑色的挎包。不知道为什么,张小庆觉得自己一定要买一个黑色的挎包,他觉得自己的未来应该是穿梭在高级写字楼中的,头发柔顺没有头皮屑,目光坚韧盛满信心,黑色的西服上衣、白色的衬衣、黑色的西服裤子、黑色的皮鞋,最重要的是要挎一个黑色的挎包,与客人见面,握手、互递名片,然后从黑色挎包里取出黑色的笔记本,演示,回答问题,客人满意的点头。黑色挎包,真他妈酷!标价30,划价,划价,反复划价,最后20,成交。这样,在上班第一天,张小庆背上了他心仪已久的黑色挎包,包里,空空如也。


    张小庆到的很早,办公室里还没有什么人,他来到当初他面试时做过的工位,取下包,坐下,看看表,850分。几分钟后,人们陆续到达,整个办公室活络过来:有人把灯打开,房间里明亮起来;有人打开饮水机,饮水机开始发出嘟嘟的烧开水的声音;有人启动电脑,电脑风扇愉悦的转动起来;有人去洗杯子,杯子和龙头之间碰撞出清脆的声响;有人互相打招呼,早啊,早啊。出神间,旁边隔间里突然站起位年轻的女孩,女孩对张小庆说,早啊。张小庆忙站起来,说,早啊。女孩说,你是新来的吧。张小庆说,对,今天第一天上班。女孩笑起来,这让她的那双毛毛眼好看的眯成了一条弯弯的线,女孩说,啊哈,终于找到比我还新的新员工了,我是上周来的,哈哈。张小庆也被感染的笑起来,不过他的笑是羞涩的,他努力让笑容处于自己的控制中,这让他的脸慢慢的涨红了。


    女孩伸出手,那是只纤纤的细手,说,你好,我叫王碧薇,认识你很高兴!


    张小庆不知道该不该握住那只手,他犹豫了半响,最后还是抓住了那只手,说,你好,我叫张小庆,认识你也很高兴。


    女孩说,好,中午陪我一起找房子吧。

 

 

 

posted @ 2011-02-28 22:33 ronghao 阅读(1712) | 评论 (5)编辑 收藏

又是一年,坐在因为新年而显得空旷的办公室里,手边是图灵刚刚出版的一本访谈录《编程人生》,翻看了几页,我想说的是:那不是我们的生活,几岁就开始编程,十几岁就有了自己的软件公司,这种事情太过遥远。我们的生活是这样的:朝九晚五,经常加会班,会为了一个程序问题而抓狂,也会为了一个程序问题而狂喜,周末时候喜欢宅在家里,或者玩会游戏或者继续编程,日益感到生活的压力,曾经引以为豪的职业其实非常普通,为房子苦恼,依旧梦想通过技术创业。是的,这才是我们的生活,我们只是普通的程序员。


那么,我该做点什么呢?是的,写点什么吧。一直以来,我的梦想就是能够写自己的小说,高中时,在当地杂志和报纸上发表过几篇文章,那时的目的很单纯,就是想引起喜欢女生的注意;大学时,开始疯狂的看小说,最喜欢的是余华,他的文字能让我在合上全书时发出长长的一声的感叹;大三时,突然开始自己写了篇中篇小说,给北京文学投稿,没有回应,于是发表在了榕树下;工作了,身边充斥着的是厚厚薄薄的技术书籍,小说开始变得遥远,但我依旧能够时常记起那些温暖过我的小说人物们;现在,我越来越发现,我们生活在一个很小说的年代,而现在的小说们,却源自生活低于生活,在穿越在科幻,我想,这正是个写小说的好年代,你不需要加工,生活本来就是小说,荒诞而黑色。


于是,就有了这篇《张小庆,在路上》的小说,计划分为四卷,分别是:开始、奋斗、迷茫和平淡,它讲述了一个普通程序员的生活,一个处于社会底层的小人物,他的喜怒哀乐,他的生活。


为什么要写这篇小说,理由很多,但我想最重要的是:喜欢!我不想在我老去的时候,对我孙子说,知道吗,你爷爷年轻的时候文章写得很好,如果那时写小说去了,一定是个大作家。这话听起来就很虚伪,给人一种生不逢时的感觉,但是去他妈的生不逢时,与其那时后悔,不如现在开始,来吧,在路上的张小庆。

 

posted @ 2011-02-28 22:28 ronghao 阅读(1615) | 评论 (6)编辑 收藏
     摘要:   阅读全文
posted @ 2010-09-19 22:03 ronghao 阅读(2276) | 评论 (4)编辑 收藏

项目上线,有时间总结一下当前的项目,对自己而言,一直是一个学习的过程。本篇总结我们的测试实践。本文分5部分,分别是:项目背景、系统架构与模块划分、我们的测试实践、自动化测试在项目中的价值与对自动化测试的进一步思考。

一、项目背景
所有对项目的介绍一定是从客户开始。
客户:我们的客户是一家全球领先的时尚内容提供商,通过遍布全球的员工,客户每天获取大量关于时装发布、产品设计、街边流行、城市热点等信息,这些信息的绝大部分以图片的形式上传到公司服务器,然后由专职编辑对这些图片进行整理和归类(打标签),最后再由设计人员根据这些信息书写分析报表。
关键内容:分类细致的海量高清图片和具有前瞻性的分析报表。
商业模式:网站,行业内用户订阅-付费。
客户面临的问题:同质化竞争、客户流失。
新系统的关键词:CMS、更精确的内容分类、更好的全文检索、更好的用户体验(更有表现力的内容展现)、更快的内容发布。

二、系统架构与模块划分
1、REST的架构风格
系统采用了Sling作为WEB框架,JCR作为了底层内容存储框架。
系统的特点:
URI唯一标识资源
通过URI能够直接映射到JCR节点,例如http://localhost:80/content/section/news.html能够映射到JCR里的/content/section/news节点

GET/POST/DELETE标准方法对资源进行操作
支持标准方法对资源的直接操作

资源的多重表述
同一资源可以存在多种表述形式,例如http://localhost:80/content/section/news.html展现网页,

http://localhost:80/content/section/news.json展现资源信息的JSON描述,
http://localhost:80/content/section/news.pdf展现网页的PDF。

服务器端的无状态
通过JS获取当前用户信息并缓存在客户端。

2、系统分层
系统分为四层:JS、Servlet、Domain Model和JCR。
因为JCR提供了一套节点模型,所以Domain Model是在节点模型上的行为增强,例如所有对图片节点的操作我们封装在Asset领域模型里。
系统分层

3、程序划分
程序分为两个大的模块:Migration和Bundles。为什么叫Bundles?因为Sling使用了OSGI框架Felix。
Migration负责导入客户的遗留数据到新系统。之前客户的CMS运行已有10多年的时间,积累有大量数据。主要是各种类型的报表和图片。
Bundles实现系统功能。主要包括了定义报表模板、定义报表各种所见即所得的展现组件、实现对图片的管理、搜索(包括基于图片的可视化搜索)和其他七七八八。

三、测试实践
1、Migration的测试
  自动化测试

对Migration,我们采用了TDD的方式。
输入是客户实际提供的xml文件,输出是JCR里的节点。测试环境的搭建主要是在本地建立起Jackrabbit实例。我们的工作方式是这样:每天早上领到一张migration故事卡,然后先写一个xml到jcr节点的集成测试描述出该类型报表的功能需求,接下来就是让这个测试通过。经过开始阶段的熟悉过程,我们的速度保持在一对Pair一天一种报表类型的导入。

   手工测试
将xml文件内容正常解析并导入JCR只是第一步,第二步我们需要在Bundles里为该类型的报表编写模板使之正常展现。由于涉及到报表样式,这个测试我们采用手工测试,这个工作也是QA工作的重要一部分。
在最开始的开发中,我们没有导入所有报表数据进行测试。这带来了问题,由于客户遗留数据跨越10多年,各种数据形式都可能存在(特别是04年以前数据,给UI带来了很大挑战),而最开始的开发中,我们只是使用了部分数据(09、10年数据)进行测试,这个导致了建立UAT环境时程序的很多返工以及QA的测试压力。

2、Bundles的测试
自动化测试

对领域模型,我们采用了TDD的方式进行单元测试;在本地Jackrabbit实例里建立数据,领域模型封装数据测试行为。
对servlet,我们采用了TDD的方式进行集成测试(同时测试了servlet和领域模型),在测试中对request和response进行mock;
对JS,我们使用数据驱动的selenium功能测试进行覆盖。

测试覆盖

我们最后的自动化测试结构:
测试的分层

手工测试
手工测试内容主要是功能测试。
自动化测试价值低的部分我们采用手工测试,这部分内容包括报表模板,相对独立,内容不多,一次测试处处通过;
自动化测试成本高的部分我们采用手工测试,这部分内容包括报表展现组件的编辑功能,因为采用了Ext JS,所以自动化测试困难;
无法自动化的测试:报表在线生成PDF,报表样式,WEBDEV批量上传图片等;

我们与QA的约定:
每完成一个用户故事,我们会与QA、BA一起mini showcase;
QA验收完成后编写功能测试用例;
我们对QA编写的功能测试用例进行自动化,共同维护一个功能测试列表;
对于不能自动化或自动化价值不高的测试用例QA继续使用手工测试。

四、我们感受到的自动化测试价值
1、通过自动化功能测试,我们使得需求对客户可视化;
2、QA的回归测试成本降低,尽管目前频繁的向客户实际使用环境部署,但QA每次部署只需要做一些简单的冒烟测试;
3、测试即需求,这点在TDD的开发过程中感觉非常明显,今天开发的目的就是使这个测试通过,避免了频繁部署到应用中进行测试,最快的电梯不是速度最快的电梯,而是中间停的楼层最少的电梯;
4、与持续集成一起,及时反馈。这点在进行JS代码编写时,心理上都非常依赖于selenium测试,对于没有测试覆盖的地方,没有安全感;
5、足够的单元、集成测试保证了频繁重构,没有人愿意引入BUG,没有足够的测试,没人愿意重构;
6、测试即文档,良好的测试和命名,使得新加入的成员非常容易理解当前代码的功能。

五、思考和讨论
1、自动化功能测试做到多少才合适?
当然是越多越合适,问题在于自动化功能测试成本要大大高于单元测试和集成测试,这些成本反映在测试环境的搭建、数据的准备,需要准备其他很多关联数据例如用户信息和权限信息、自动化功能测试的运行时间长、稳定性(随机成功/失败)、编写等等,需要权衡成本与收益。
个人认为,自动化功能测试需要考虑的着重点包括:页面是否包含大量功能交互性JS(与展现性JS相对)?当前功能是否与其他功能共享一些代码?即独立性,独立性越低越需要着重覆盖(这里又涉及到另外一个问题,即从各个模块里重构出共用代码是否总是合适?)。QA每次冒烟测试时是否需要重复回归(重复回归次数越多,自动化越有价值)?经常失败的测试一定比不失败的测试价值更高?或者从未失败过的测试就没有价值?

2、单元测试?功能单元测试?
TDD的测试粒度多大才合适?从我个人而言,几乎天然的反对mock,为了满足测试覆盖率的追求,强制将两个联系很紧密的类分开,做出各式各样mock,这真让人气馁;stub也不是什么好东西,在一个曾经的spring项目里,在测试目录里,一堆一堆的测试xml配置文件让人有种呕吐的感觉。那就做集成测试吧,两个类关系很好,那么就整体测试它们的输入和输出,看起来很不错,功能实现了,测试也没多写,也不用准备太多其他东西,但是打开实现代码,你就发现那个丑陋,到处是复制和粘贴,是啊,不管黑猫白猫抓住老鼠就是好猫,只关注了当前结果,完全失去了TDD驱动简单设计的好处。

3、测试的重点是测试用例的设计,它反映出对需求的理解
那么结论就很明显,我们如果连需求就没有理解,如何进行实现,所以测试驱动是很自然的。

posted @ 2010-06-16 21:13 ronghao 阅读(2393) | 评论 (0)编辑 收藏


正如语言是人之间的沟通方式一样,数据是IT系统之间的沟通方式,语言之间的沟通总是最有效的,数据交互却未必,因为IT系统里的数据除了让计算机理解外重要的是还需要人理解。在这篇文章里,我们将讨论工作流系统里的数据,从数据角度分析工作流数据的分类以及不同的应用场景。

 

一、工作流系统的应用场景

在正式开始对工作流数据的讨论之前,首先对工作流系统的应用场景进行讨论是必要的,因为工作流数据脱离不开工作流系统这个大的上下文。目前,工作流系统的应用主要有两种方式:

1、    将工作流系统嵌入到业务系统中使用。此时工作流系统作为内部组件对业务系统进行流程逻辑的横切。试想一个需要多人处理的电力缴费流程,在引入工作流系统之前,我们需要为每个业务表单设置一个状态位,以此来进行业务处理状态的跟踪。如果流程固定,那么这样做并没有什么不好,例如财务软件、海关报关软件等,它们的流程虽然复杂但是不常改变,此时就没有必要引入工作流系统。但是对于另外一些情况,例如制造业的订单处理、库存管理、政府的协同办公等,流程经常需要定制修改,此时如果继续由业务系统自己处理流程逻辑那么成本将会很高。

2、    使用工作流系统进行业务系统的集成。在上规模的企业里,很多流程会涉及到不同的业务功能,例如报价、订单审核、资产核准、绩效评估等,这些流程经常会跨越不同的部门和业务系统。因为不同企业都有自己所采用的业务系统、组织机构以及最佳的业务协作方法,所以这些流程基本上也随企业而异。工作流系统此时扮演的就是集成角色,由其通过定制流程将这些业务系统撮合起来,实现企业内各部门、客户间的信息流动和协作。

在第一种应用场景下,工作流系统作为业务系统的内部横切组件出现,作为横切组件,工作流数据仅仅包括与流程逻辑相关的数据以及其他必需数据,这些数据包括工作流控制数据、工作流相关数据以及需要通过流程传递的业务数据。

在第二种应用场景下,由于不同业务系统之间的数据传递很大程度上依靠工作流系统,所以这些数据被封装为SDO在不同WEB服务间传递,需要注意的是,这些数据并不在工作流系统中存储。

        在下面的工作流数据分类中,我们将详细分类这些工作流数据。

 

二、工作流数据的分类

提到工作流数据,就不得不提业务数据。作为最直接的区分,我们将存储于业务系统中的数据称为业务数据,将存储于工作流系统中的数据称为工作流数据。根据WFMC定义,我们将工作流数据分为工作流控制数据和工作流相关数据。

1、  工作流控制数据。指被工作流系统管理的系统数据,这些数据包括了与流程实例和任务实例相关的执行数据,例如流程实例的状态、执行时间等信息、任务实例的执行者、执行时间、状态、紧急程度等。

2、  工作流相关数据。指与业务流程相关的数据。工作流相关数据又具体分为3种:

·         影响流程实例执行的业务数据。在WFMC中,这个数据被描述为:工作流系统通过该数据来确定流程实例的流转条件,并选择下一个将执行的任务,这些数据可以被业务系统访问并修改。例如报销流程中的“报销金额”,这个数据会决定该流程的审批路径;再例如为任务设置的超时时间,这个数据会触发任务的取消。实质上这些数据就是工作流系统需要依赖于进行流程流转的业务数据。

·         契合业务的关联数据。指工作流系统与业务系统进行关联的数据,例如特定于WEB系统,工作流系统会在每个流程实例里保持有导航至对应业务表单的URL

·         传递作用的业务数据。当流程跨越多个业务模块时,需要在模块间传递数据,此时会利用工作流系统进行传递,会在工作流系统里暂时存储这些业务数据。

那么,工作流数据有哪些应用场景呢?

 

三、工作流数据与业务上下文

工作流数据最重要的职责之一就是为业务系统的不同应用场景建立起与之对应的业务上下文。

什么是业务上下文?

我们知道,IT系统是对企业现实业务的映射。在一个翻译公司的典型业务场景中,校对人员对翻译人员提交的翻译文档进行审校,此时,校对人员持有翻译人员翻译后的文档,他需要对该文档进行检查,产生新的审校文档并反馈翻译人员的翻译质量。那么,映射到IT系统里,校对人员的任务通常对应于一张需要处理的业务表单,业务表单里会展现他进行当前工作所需要的数据:翻译文档、翻译人员信息、该校对工作的紧急程度等,另外,在这张表单里,他所能进行的操作也根据他此时的职责作出了行为限定:例如他可以上传新的校对后的文档,但是不能删除已有的翻译文档等。如下图1所示,业务表单实质上反映的是此刻我们能获取哪些数据以及能够如何处理这些数据,我们把它称之为业务上下文,可以看到,在IT系统里,业务上下文实质上等于数据加上行为。

企业业务由一系列相互关联的业务场景组成,这些业务场景对应于IT系统里的业务上下文,而业务上下文的本质则是数据加上行为。数据和行为的不同决定了业务上下文的差别。这与现实中的工作相符,人们根据获取/处理信息的不同,担负不同的职责。 


 1与应用场景对应的业务上下文

工作流数据如何建立业务上下文呢?看一个简单的例子。

在实际应用工作流系统进行开发时,我们经常会碰到这样的问题:同一流程中的不同任务对业务数据拥有不同的权限,如下图6-9所示。



 6‑9与流程相关的业务数据权限

上图中,在执行请假申请任务时,申请者可以编辑请假人、天数和原因3个字段;而到审批任务时,审批者增加了一个可编辑的审批意见字段,但其余3个字段变化为只读字段。我们将这类问题统称为与流程相关的业务数据权限控制。

产生这类问题的原因是什么呢?原因就在于在一个业务流程里,不同的任务具有不同的业务上下文。如下图6-10所示,不同的任务展现不同的数据,并具有不同的行为。


 6‑10任务与业务上下文

IT系统的设计实现中,数据的建模是通过领域模型实现的。在工作流系统的嵌入式应用中,流程实例即是通过与领域模型相关联实现与业务契合的。那么,图6-10可以进一步泛化为图6-11,流程中任务通过获取领域模型不同的部分实现业务上下文的界定。


 6‑11领域模型与业务上下文

在大部分的业务流程建模中,一个流程模型只与一个领域模型关联

那么,回到最初的问题上,如何处理此类权限问题呢?答案非常明了:由领域模型实现对业务上下文的切分,工作流系统通过契合业务的关联数据与业务上下文挂接,保持工作流系统的单一职责。


 6‑12流程相关的业务数据权限控制

如上图6-12所示,我们在业务系统里引入业务权限角色的概念,通过该角色隔离开工作流系统与业务数据权限,即我们认为业务数据权限的管理属于业务系统范围(由业务系统具体实现),更进一步,我们认为其属于领域模型的职责范围。在定义好业务系统的权限角色后,我们通过任务级别的工作流变量将流程中的具体任务与业务权限角色绑定,这样就实现了流程任务与业务数据权限的挂接。

在上面的例子中,我们看到的是利用关联业务的工作流数据界定任务级别的业务上下文,这是一种最简单的工作流数据应用场景。在不同应用中,我们可以看到,工作流数据一个重要的职责就是为业务流程里的任务/流程建立业务上下文,实现数据的聚合。在简单的应用场景里,对一个流程实例而言,这些数据可能只对应于一个领域模型;对流程实例里的一个任务实例而言,这些数据对应于领域模型的一个切面。复杂一些的情况,业务上下文需要跨越多个领域模型甚至多个业务系统,这时我们可以看到,通过工作流系统能够显著降低业务系统建模的复杂性,因为这些数据聚合工作可以有效分派给工作流系统承担。同时,我们需要保持工作流系统的单一职责,工作流系统只保存与业务数据进行关联的关联数据、必需的业务传递数据以及自身的执行数据。


 6‑29跨系统的业务上下文

 

 

四、工作流数据与数据分析

工作流数据的第2个应用场景是对业务流程执行进行数据分析,这部分的数据主要是工作流控制数据。这一部分正受到越来越多的重视,是未来工作流系统的发展方向。


 6‑48外部环境从流程实例拉数据进行分析

 

这里有两个典型的应用例子:

例子1在对报销流程进行分析时,我们发现大部分的报销金额都低于500元,然而这些报销流程却都要经过很多环节,在与客户确认后,我们将低于500元的报销限定于部门内部审批即可,这样对个人来说大大加快了报销过程,对公司来说则省下了很多的办公成本。

例子2,在制造流程里,很重要的一点是需要控制流程的节拍时间,即流程里各个任务的完成时间要一致,如果有一项任务的时间多于其他任务,那么很快就会形成瓶颈,造成在制品的大量积压,前续的任务完成很快,中间忙死,后续任务执行者却无事可做,更重要的是,不能对客户进行快速交付。

 

 

五、工作流数据与流程路由

最后一种应用场景非常常见,这部分数据即影响流程实例执行的业务数据,直接上图:


这部分影响路由的一定是业务数据,它们保存到工作流系统里对流程路由产生影响。这种影响不限于任务的选择,还包括的任务的执行条件、任务的完成条件、基于数据的任务触发等。

 

六、工作流数据小结

总结一下,作为区分,我们将存储于业务系统中的数据称为业务数据,将存储于工作流系统中的数据称为工作流数据。

工作流数据分为两种:工作流控制数据和工作流相关数据。其中工作流相关数据又分为3种:影响流程实例执行的业务数据、契合业务的关联数据和传递作用的业务数据。

工作流数据的应用场景:为流程/任务建立业务上下文,这部分数据主要是契合业务的关联数据和传递作用的业务数据,这是工作流数据最重要的职责之一;数据分析,这部分数据主要是工作流控制数据,这是未来工作流的发展方向;影响流程路由,这部分数据人如其名,是影响流程实例执行的业务数据。

实际应用中,我们一定要保持工作流系统的单一职责,例如划分任务权限这个需求,一定需要业务系统自行实现权限的界定,工作流数据仅仅进行挂接。

 

 


posted @ 2010-05-23 22:17 ronghao 阅读(2494) | 评论 (1)编辑 收藏

PDF噩梦

在之前的一段时间里,只要一提起PDF,我就会头晕,然后是头疼,最后是头大,总之是和头相关。需求很简单:为所有报表提供在线生成PDF版本的功能,这样网站用户在浏览报表时就可以下载离线浏览。对不住了,开源软件,我不得不说,慎用开源软件,慎用!痛苦的查找论坛、痛苦的翻看源码,最后,在支付了200欧后,痛苦消失了,我们购买了商业软件,200欧兼容了更多的网页结构,200欧具有更快的速度,200欧带有一年的技术支持,最最重要的是,200欧,客户出的。

这不是这里的关键,问题是,200欧后,我遇上了新麻烦:报表的PDF版本样式不正确,不正确的原因是图片下方的文字将图片的排列样式弄乱了(图片大小不规则,字数不规则)。在网页中,DOM渲染完毕后,我们使用JavaScript来进行图片与文字高度的重计算,但在PDF中,我们束手无策。

我问BA,可以容忍部分图片排列不整齐否?不出所料。

怀有侥幸,我继续问BA,可以容忍部分文字丢失否?BA说,不可以。意料之中。于是找到徐昊。

徐昊问BA,这些说明文字对客户如此重要吗?

BA说,是的。

徐昊说,为什么?它主要有哪些内容?

BA说,有标题,简单说明以及图片的版权信息,最重要的就是版权信息,一定不能丢失。

徐昊说,能不能这么说,其实对客户最关心的是版权信息。

BA说,是的。

于是问题解决。解决方案是:我们给文字定高,同时将文字缩小以容纳最可能多的字数,这样网站用户在PDF里看到的图片重新恢复了整齐,尽管看不太清图片说明文字,但是用户真正关心的是图片,谁关心哪些无处不在的版权信息呢?你可能会说了,看不清版权信息怎么行?幸好,你问的不是,版权信息有那么重要吗。回答是,这里是PDF,移动你的鼠标到Zoom,点击下拉框,点击150%以上的选项,然后,你会惊讶的发现,那些该死的版权信息到处都是。

BA的职责是帮客户发现的问题,开发人员的职责是解决问题,QA的职责是校验最终的实现是否能够解决客户的问题。具体到一个用户故事上,就是BA编写用户故事,DEV编码开发,QA验收用户故事,这是三个任务,很明显,这三个任务有一个非常重要的共享信息,这个信息就是用户故事所要实现的客户价值(即帮客户解决的问题)。围绕着客户价值,每次迭代开始前,团队都会进行迭代计划会议,所有成员会跟随BA逐一审核各个用户故事;围绕着客户价值,开发人员开发中随时可以和BA进行沟通,就设计问题进行讨论;围绕着客户价值,开发人员每开发完成一个故事,BA、开发人员和QA就会在一起进行一个微型 ShowCase,在期间讨论用户故事的实现是否实现了客户价值,大家对用户故事的理解是否一致。

那么,在相关的任务之间需要能够定义变量,这些变量数据能够在这些任务间共享。

 

描述

一定的任务范围能够定义变量,在一个流程实例里,该范围所包含的任务实例能够使用该变量。


6-4任务范围级别的数据可见性

如图6-4所示,我们划定了一个任务范围,该范围包含了任务A、任务B和任务C,同时,我们在该任务范围内定义了一个变量M,那么,在一个流程实例里,只有任务ABC的实例在运行期能够使用该变量,任务DE的实例都不能访问,不可见。

 

可以看到任务范围和块任务在概念上比较相似,都是包含一系列的子任务,它们之间的差别在于:块任务一般具有比较独立的执行上下文和业务语义,而任务范围则是对具有相同执行上下文的任务的一种分组。

在工作流系统里,对流程任务进行分组的好处在于:可以为特定的一组任务绑定数变量、异常处理器和补偿动作。例如在图6-4中,如果任务ABC中的任一实例执行失败,那么我们就认为整个任务区域执行失败,将统一执行一个业务补偿行为,同时,这些任务共享一个异常处理器。

实现

jBPM4里,流程定义模型相比jBPM3最大的变化即是引入了任务嵌套的概念,一个任务能够包含多个其他任务,这里的父任务即可充当任务范围的定义。jBPM4针对这种嵌套的任务建立了一套处理机制,总的来说就是建立任务运行期的嵌套关系,查找变量时首先会在任务级别进行查找,如果找不到,则会依次向上查找父任务实例,直至流程实例级别变量,同时,父任务可以统一绑定异常处理器和事件动作。在后续jBPM4的章节,我们将会详细分析该机制的实现细节。

posted @ 2010-03-22 22:26 ronghao 阅读(1609) | 评论 (0)编辑 收藏

什么代码才是好代码?这真是个老得能拔掉牙齿的话题。好吧,那让我们再在这刮沙尘暴的无聊时光里重复一次。好的代码要是易读的代码、要做到职责分离、要做到单一职责、要有高的执行效率....

等等,等等,这才抽象了,太书面化了。我只是一个菜鸟,刚写代码几年,也没念过什么书,能不能说得通俗易懂一些?

好吧,我停下来,想,这真是个难缠的家伙。我说,这样吧,我推荐几本书你去看吧,《重构》熊节最近再版了,建议你去买一本。恩,等等,有个省钱的招,去图灵俱乐部讨论组注册下,蹭赠书也很爽,哈哈。

可是,你还没告诉我什么代码才是好代码呢?知道你也没什么好答案,我自己来说好了。

省略掉此时我内心花花的汗水,下面是菜鸟的叙述:

1.一致
我发现自己有轻微的强迫症,当我碰到以下代码时,我就会冲动。
冲动前代码:
def imgName
if(XXX){
   imgName="meigui"
}else{
   imgName=""
}
冲动后代码:
def imgName=XXX?"meigui":""

尽管两段代码功能一致,但一旦我发现出现冲动前代码时,我就会感到不舒服,感到难受,就好像看到阅兵正步走不齐一样。方法名也是一样:
冲动前:
def testXXX(){}
冲动后:
def should_XXX_when_XXX(){}

变量亦是如此:
冲动前:
def imgNode=resouce.adoptTo(Node)
冲动后:
def node=resouce.adoptTo(Node)

总之,我不愿意看到同一个事情有两种实现方式,如果功能类似,那么不管是逻辑还是变量、方法名,我会强迫一致,整齐划一。

关于一致,从调试代码的角度看,零星的不一致比大量的不一致更加糟糕,因为这时大部分地方的一致性会令人麻痹大意。在实现查询分页功能时,我们有这样一行代码:
nodeIterator.size
这行代码的意思是获取查询结果的总数,大部分情况下它工作良好,但是在一种特殊情况下它返回了-1。这对我当时几乎是灾难性的,因为调试过程中我们始终相信这行代码的行为一致,结果是花费了一个下午才找到这个问题。

2.简洁
我喜欢短的代码,对我而言,短的程序总是比更长一些的代码容易理解,小学时学课文就已经这样了,一看到大段的段落我总是会晕过去(特别是文言文,首先我就 对自己理解这段文字失去了信心)。这里要提到注释,即是这些注释明确是为了提高代码的可读性,也会增加我阅读代码的困难,所以我不会在方法里的任何位置添 加注释,撑死在个别方法声明前添加,并且这种情况也尽量避免,如果这个类确实包含了重要的不易理解的算法,我也只会在类声明前添加注释。

关于自然语言,有一个基于经验的结论被称为Zipf定律,即:自然语言中最常用到的单词,其长度会趋于最短。

我写代码的时候,能够简写尽量简写,例如,变量名,imageNode,我一定会写成imgNode;方法名procedureXXX,我一定会写成 procXXX,和讨厌大段代码一样,我非常讨厌命名很长的方法名和变量名,尽管这些名称这么长是为了更好的增加可读性,但可读性不是这样增加的。

在我的第一份代码工作里,我们使用拼音来命名方法和变量(还好,没有包括类名),我讨厌这种命名方式的原因并不是因为我的语文老师不好以至于我前后鼻音不分,而是这种写法根本排除了简写的可能性,甚至,为了避免歧义,有时不得不变得更长。

3.联觉和顺序
关于记忆,人类有两种重要的记忆能力:联觉和顺序记忆。

关于联觉,一个例子是:你总可以一眼记住一个人的脸,比如范冰冰,尽管我到现在也不清楚她到底是单眼皮还是双眼皮,也不清楚她到底是厚嘴唇还是薄嘴唇。

那么,在代码里,这里的表现就是局部,即一个功能的所有相关代码都集中在一个地方。我最讨厌的代码是这样的:最开始我打开一个文件,在阅读的过程中,我发 现一个不清楚的方法,于是我按下ctrl并点击鼠标,于是我跳到另外一个文件;接下来,在阅读另外一个方法里,我再次发现了一个不清楚的方法,于是我再次 按下ctrl并点击鼠标,哇哈,新的文件打开了....如此反复,终于当我打开最后一个文件时,我发现IDE的文件条里已经密密麻麻的排满了好几排文件, 于是,我移动鼠标,右键,弹出一个关闭菜单,我选择了close others,瞬间,哦米拖佛,整个世界清静了,但是,等等,我最初是打算干嘛来着?

所以,请把所有相关联的代码都集中在一个地方,求您了。哦,对了,能不用接口请不要用接口,总会碰到这样的情况,打开好几排的文件,接口文件占了一半,我靠,少几个接口会死啊。对了,这可能是您的一致性心理在作怪,对不起,对不起。

关于局部,一个范例同样与调试有关,在很久之前的一次调试中,我们始终找不到一个变量错误的原因,因为在这段代码里,根本找不到任何错误,很久以后,终于 发现,这个变量竟然是个全局变量,嘿嘿,告诉你吧,这个变量在servlet里,04年的时候,网上很火的一篇文章,标题就是:不要在servlet里使 用全局变量!

关于顺序,最典型的例子出现在高中化学里,我总也不能瞬间说出第12、13个化学元素是什么,我通常会这样记忆:氢氦锂铍硼碳氮氧氟氖钠镁铝硅磷,啊哈,第12个元素是镁,第13个元素时铝,合起来就是--美女!

所以,在代码里,请将互相调用的方法按顺序摆放,方法1先调用了方法2,那么请将方法2紧放在方法1后边。我讨厌这样的配置:打开方法1,发现其调用了方 法2,点击方法2,编辑器里的滚动条瞬间从最上端滚到最下端,紧接着,滚动条又从最下端滚动到中间,再接着,又是最下端,接着,归零到最上端....人生 经不起这样的大起大落,真的,那得要多么大的心脏啊,麦蒂才有过那么一次,13秒....

还有,知道为什么goto为什么那么臭名昭著了吧。

4.自然
使得代码具有轻松的表达方式,同时把错误率降到最低,一种最重要的方法就是代码变得“自然”,即向自然语言靠拢。因为代码并不仅仅是与机器交流的,更重要的是,需要在人之间交流。

机器语言到高级语言,面向过程语言到面向对象语言,jdbc到hibernate,java到动态语言,这些都促使代码变得更加自然。

Ruby里有个不起眼的特性,就是方法调用不用再写括号,这一特性是如此的微不足道但是却被很多人津津乐道,原因就是它更加自然,更加贴近我们的自然语言。于是,我看到,我的同事晓娜,在groovy里,一遍遍的将她力所能及的括号去掉。

此外,程序语言和自然语言是有区别的,除了不能在代码里利用感情词抒发情感之外(我想,如果可以,一定会看到很多的冯特),程序语言没有口语。很少看到程 序员之间这样交流,来吧,我们来说段代码(当然也有,徐昊就可以,哈哈),他们更多的会使用白板和笔或者直接是编辑器。所以,结束招聘时是否需要笔试的争 论吧,我真为那些不经过笔试就直接招人的公司感到羞愧,因为他们根本就不懂程序语言。

此处省略华丽的分割线。

此文谢谢我们项目组WGSN的激烈讨论,谢谢讨论中徐昊的精彩点评。

posted @ 2010-03-21 22:27 ronghao 阅读(2127) | 评论 (2)编辑 收藏

唐僧与 QA MM

在一个典型的项目团队里,包括了以下几种角色(帽子): PM(项目经理)、 BA(业务分析师)、 DEV(程序开发者)和 QA(质量保证人员),整个团队的目标是向客户交付价值。


那么,有一天, QA MM来找我,我是开发人员。 MM说,一张图片没有正常显示,我想知道原因,同时想知道你能否修复。我的第一想法是,这不可能,一定是环境的原因。我说,好的,稍等。接下来,我张大嘴巴看到了 MM给我重现的 BUG:本该显示图片的位置一片空白,就像此时我合不上的嘴。这怎么可能呢?我想,这个功能完成的如此之得意,以至于测试用例里的数据都是以我的名字命名的。


几分钟后,或者更长,我叫来 MM,说,找到原因了。


我打开编辑器,光标在源程序的某一行闪烁,我说,最根本的原因在这里。我看到 MM的眼中闪过一丝迷茫。接下来,我却换到另外一个源文件,光标继续闪烁,我说,这里的程序因此受到影响。看得出, MM有点发晕。终于,当我打开第 N个源文件并试图继续讲解时, MM昏过去了。


当 MM苏醒过来时,我在她清澈的双眼中看到了一只清晰的唐僧。


MM肯定感到了不好意思,因为我的讲解中包含了比喻、类推、排比等我力所能及的各种语文知识,看得出,我很努力,我的语文老师也很努力,所以她委婉地说,能不能简单一点?


我想了想,说,测试驱动时测试数据不全导致程序少考虑一种情况。


MM说,能修复吗?


我说,可以。于是故事结束。


就 是这样,当我们执行一项任务时,围绕这项任务必然会产生许许多多的信息,这些信息对于该任务的执行者是必须的,但是对于其他人则不是,有效的沟通往往来自 于简练的表达即只表达对方需要和可以理解的内容,浩瀚的细节只会将真正想表达的内容淹没。其实这里还有这样一层意思:我之所以用这么多的细节信息来淹没 QA,实际上是不太情愿承认程序里有 BUG。 QA想要的结果很简单,是否是程序 BUG,能否修复。而开发人员则往往把自己的程序与自己关联在了一起,认为程序是自己的扩展,程序有 BUG则意味着自己有缺陷。这一关系明显是矛盾的,可是一些团队里开发人员和 QA能够和平相处,而有些团队却势如水火。


那么,对于单个任务而言,需要定义自己的变量,这些变量数据只与该任务相关,只在该任务里可见。典型的工作流应用于任务执行期间的中间数据存储。在文档处理中,一个重要的功能就是需要提供版本管理,在单个任务实例里,办理者能够管理自己处理过的文档版本。

 

描述

任务能够定义变量,在一个流程实例里,该变量只能被其任务实例所使用。


图 6-2任务级别的数据可见性

如图 6-2所示,我们在任务 B上定义了一个变量 M,此时,在一个流程实例里,只有任务 B的实例才能使用该变量。

 

实现

存在两种实现方式,一种是如图 6-1所示的,在任务节点定义中声明变量,运行期初始化任务实例的同时初始化该变量并使用; 另一种是在流程定义级别统一声明变量,但是各个任务实例都独立初始化并存储该变量。第二种实现方式在各个任务都需要使用同一语义的变量时很常见,例如各个任务实例都会有参与者,我们在流程定义时声明一个名为 userid的变量,在流程实际执行时,各个任务实例都会独自保存有自己的 userid数据。

posted @ 2010-03-16 22:05 ronghao 阅读(1624) | 评论 (0)编辑 收藏

和前面的章节一样,我们先从一个故事开始,这个故事和晚饭有关。在我家,周一至周五,老婆做饭,我洗碗。每天做完 饭,老婆会叫我到厨房,说,看,这个盘要洗一下,另外,灶台脏了,也要擦。如果放在以前,我会说,好,明白了。但是现在,程序员的生活让我意识到,沟通永 远不是一件简单的事情,我说,好,知道了。

等等,这个故事和本章的主题-数 据模式有一毛钱的关系?这只是一个关于沟通的故事。是的,让我们稍微映射一下:这里,晚饭这个流程包含了两个基本的任务,分别是做饭和洗碗,在做饭这个任 务完成时,任务的执行者(老婆)向下一个任务的执行者(我)传递了数据(要洗哪些东西),正如语言是人之间的沟通方式一样,数据是IT系统之间的沟通方式,语言之间的沟通总是最有效的,数据交互却未必,因为IT系统里的数据交互除了让计算机理解外重要的是还需要人理解,IT系统是对现实生活的映射,也正因为如此,现在数据之间的沟通也在向语言靠拢即语义化(REST/语义网)。

好,言归正传。

在 前两章里,我们分别讨论了工作流的控制模式和资源模式,控制模式关注于如何合理调配业务流程里的任务,从而获得理想的执行效率和收益;资源模式则关注于如 何合理调配可用的资源来执行业务流程。本章将介绍工作流系统里的数据模式,从数据的角度分析工作流系统对数据的处理。数据模式共计39种,在下面的介绍中,我们将这些模式分为了四部分,分别是数据可见性、数据交互、数据传输和基于数据的路由。

本章先会概要重复一下与数据模式相关的一些基本概念,例如流程定义、流程实例、原子任务、块任务等。接下来会对具体的39种数据模式进行讨论,讨论的模式按照应用、描述和实现展开,分别对应着实际场景对模式的映射、模式的介绍和工作流系统对该模式的实现支持。最后是小结。

一、基本概念

1、工作流系统里的流程结构

在正式介绍数据模式之前,让我们先简单回顾一下工作流系统里流程的基本结构。


6-1 工作流系统里的流程结构

流程定义:对业务流程的建模和描述,其具有足够的细节信息,能够直接被工作流系统所执行。典型的,流程定义由一系列的任务组成,这些任务以图形的形式展现并被连接起来。

流程实例:流程定义的一个执行实例被称为流程实例。一个流程定义可以存在多个同时执行的流程实例。这些流程实例互相独立执行。

任务:一个任务对应着流程定义里的一个单一工作单元。存在四种不同类型的任务:原子任务、块任务、多实例任务和多实例块任务。

原子任务包含简单且独立的任务定义,当其初始化时只会产生一个可执行的任务实例。

块任务是一系列任务的组合,典型的,工作流系统里存在的子流程任务(节点)即是块任务,当一个块任务开始执行时,其将流程控制权传递给与之对应子流程的第一个任务,当子流程完成执行后则将控制权返回给块任务。如图6-1所示,块任务C对应着一个由任务X、任务Y和任务Z组成的子流程,实际执行时,任务C会触发任务X的执行,任务Z执行完毕即子流程执行完成后则会触发任务C执行完成。

多实例任务在实际执行时会产生多个并行执行的任务实例,这些任务实例一般互相独立执行。当一定数量的实例执行完毕后即会触发后续任务的执行。

多实例块任务结合了块任务和多实例任务的定义,其在实际执行时产生多个任务实例,每个任务实例对应着一个子流程实例。

任务实例:任务的一个执行实例。

2、数据相关约定

我们使用def var ${变量名}定义数据元素,同时def var ${变量名}的声明位置决定了变量的作用范围。如图6-1所示,我们在任务C上定义了一个名为M的数据变量,其的作用范围为任务C,任务级别。

我们使用use(${变量名})表明对变量的使用;使用pass(${变量名})表明数据变量的传递。在图6-1里,变量M从任务C传递至任务E

对于变量的数据类型,典型的有Stringintegerfloatbooleandatetime等,很多工作流系统使用序列化和反序列化支持存储任意类型的数据类型,如数组、集合、对象。

3、类比的约定

在后续对各个模式的介绍里,我会围绕一个项目团队的故事进行映射,我们如此约定:

流程定义:我们认为所有成熟的软件公司都会建立起其完整并适用的一套软件开发流程,我们将这套流程看作是这里的流程定义。

流程实例:围绕着软件开发流程,我们会使用这套流程来开始我们实际的软件开发项目,我们将所有的软件开发项目都看作是开发流程的执行实例,即流程实例。

任务:项目开发过程中的各项任务。

数据:我们将团队成员之间的信息交流看作是数据交互。

posted @ 2010-03-14 21:14 ronghao 阅读(2265) | 评论 (0)编辑 收藏
我们从一个最简单的登录例子开始。

最开始我们需要验证在用户名和密码都正确的情况下,能够正常登录系统,我们这样编写测试代码(以下都是伪代码,使用TestNG和Selenium):

@Test
def should_login_success_with_exist_username_and_correct_password(){
    LoginPage page 
= user.open(LoginPage,"/login.html")
    user.perform(
"login",['user1','1234'],on(page))
    
assert page.successLogin
}


恩,很不错,运行一下,出现红条。为什么呢?原来测试数据库里没有用户名为user1的用户,好吧,写个数据库数据初始化脚本。再运行,OK,绿条!

那么,接下来我们再增加一个测试,需要覆盖密码错误时不能登录系统的情况,很快测试就完成了:

@Test
def should_login_success_with_exist_username_and_incorrect_password(){
    LoginPage page 
= user.open(LoginPage,"/login.html")
    user.perform(
"login",['user1','4321'],on(page))
    
assert page.successLogin,false
}


再运行一下测试,绿条。好啦,现在可以看下这段代码,恩,有些重复,重构一下:

@Test
def should_login_success_with_exist_username_and_correct_password(){
    
assert login('user1','1234')
}

@Test
def should_login_success_with_exist_username_and_incorrect_password(){
    
assert login('user1','4321'),false
}

def login(username,password){
    LoginPage page 
= user.open(LoginPage,"/login.html")
    user.perform(
"login",[username,password],on(page))
    
return page.successLogin
}


重构完成,可以看到,我们的测试方法里现在没有了任何行为,仅仅是数据!这样让我感觉有点怪,不管了,先用TestNG提供的@dataProvider整理一下:

@Test(dataProvider="testdata")
def testLogin(username,password,expected){
    LoginPage page 
= user.open(LoginPage,"/login.html")
    user.perform(
"login",[username,password],on(page))
    
assert page.successLogin,expected
}

@DataProvider(name
="testdata")
def Object[][] dataForLogin(){
    def data
=new Object[2][]
    data[
0]=['user1','1234',true] as Object[]
    data[
1]=['user1','4321',false] as Object[]
}


测试方法只剩下了一个!如果要测试不存在的用户不能登录系统呢?很简单,增加数据即可!

@DataProvider(name="testdata")
def Object[][] dataForLogin(){
    def data
=new Object[2][]
    data[
0]=['user1','1234',true] as Object[]
    data[
1]=['user1','4321',false] as Object[]
    data[
1]=['inexistuser','1234',false] as Object[]
}


在我们的测试方法里,测试数据和测试的行为进行了完全的分离。从系统的功能来说,功能一旦实现,那么就是一个黑盒,我们只要提供数据即可进行测试,这个数据包括两部分:输入和期待的输出。我开始暗自嘀咕:难道我以前那么多的洋洋得意测试方法很多都是不需要的吗?这些方式为什么会存在呢?恩,想起来了,这些方法是为了覆盖功能的各个路径的,是提高测试覆盖率的。那么为什么会产生这么多的测试方法呢?哦,在这些测试方法里,测试数据和测试行为是耦合在一起的!

我伸了个懒腰,突然想,这下好了,我已经封装好了功能行为,QA想增加测试用例只需要自己增加数据就可以了,嘿嘿,爽啊。说曹操,QA到。QA mm说,你的某个功能实现有问题。我瞅了一眼,说,不可能啊(这是dev面对bug的第一反应),俺有测试的,持续集成一直是绿条的。QA mm说,在你的开发环境下测试是没有问题的,但是在QA环境,因为数据库变了,数据变了,应用服务器变了,所以会有些问题。我极不情愿的登录到QA环境,一测试,还真是,郁闷。

怎么办?修复完BUG,第一反应就是自动化测试能不能跑在QA环境呢?一般情况下,这些测试需要干净的测试环境,我们会制造很多的测试数据,可是在QA环境下,QA有她自己的测试数据,这些数据都不存在了哈。恩,看看刚才的测试代码,哈,就用QA的数据也可以啊,心情愉悦的改下:

@DataProvider(name="testdata")
def Object[][] dataForLogin(){
    def data
=new Object[2][]
    data[
0]=['hrong','1234',true] as Object[]
    data[
1]=['hrong','4321',false] as Object[]
    data[
1]=['rhao','1234',false] as Object[]
}


OK,完成!为了该测试既能在开发测试环境运行又能在QA环境下运行,我们可以引入一个环境变量,将测试数据扔到文件里,通过环境变量来加载不同的测试数据(测试文件)。

好吧,喝点东西(甲流很厉害,喝板蓝根好了),总结一下:

数据驱动测试:测试数据与测试行为分离,通过数据来驱动测试。

好处:在对测试行为封装好的情况下,QA mm能够自己通过数据修改自动化测试;
      自动化测试能够运行在多个环境下(开发环境、QA环境、产品环境)
      测试的可读性
      测试方法大量压缩


适用范围:功能测试(selenium测试)
          通过环境准备测试数据(非测试用例自己准备数据)


可能存在的问题:比一般的测试编写困难,特别是在静态语言里

最后:该文章的思考来自于徐昊在团队内部的相应Session.




posted @ 2010-01-17 12:08 ronghao 阅读(2565) | 评论 (0)编辑 收藏
今天下午1时20分,百度首席产品设计师孙云丰在自己的博客中撰文关于谷歌退出中国,直指Google退出中国的姿态证明自己是市侩分子,对此感到恶心。
他的博客全文如下:
google宣称要退出中国,所证明的,恰恰不是市面上的那些g粉所宣称的那样,google是个人权斗士,而刚好反了过来,正好证明google是个市侩分子。
google 的首席法律顾问的调调让我感到恶心。因经济利益退出,就直白白的说好了,把自己涂脂抹粉一番,还煞有介事的提到google被中国人攻击,中国异议分子的 Gmail信箱被攻击,把这些事情作为退出中国的铺垫,这种论调是侮辱中国普通老百姓的智商,但还真有可能迎合那帮目空一切,但从未到过中国、对中国没有 丝毫了解,却又喜欢对中国说三道四的西方人的假想。

只提一个假设,如果谷歌占据了中国80%的搜索市场份额,google的高管,还会这么高调的宣称要do no evil,从中国退出吗?

整个事情给我的唯一感受,就是恶心。
————–
以上是作为一个曾经的忠实google用户而说的,和百度无关。市面上沾沾自喜于了解一点google的产品技术细节将google奉为道德楷模而自封G粉的兄弟,请勿跟帖瞎喷,你们根本不懂什么叫搜索引擎,什么叫自由人权。


立此存照的原因在于,原帖 http://news.csdn.net/a/20100113/216459.html 被百度的人要求删掉,所以本着对历史负责的态度,保存与此,欢迎转帖。
posted @ 2010-01-14 13:29 ronghao 阅读(414) | 评论 (0)编辑 收藏

本书关注于IT里的流程产品。面对市场上品种繁多的流程产品,很多人的困惑是:这些流程产品究竟能够帮助企业做出哪方面的改进,这些产品背后的理论基础又是什么?同时,很多人对IT产品的宣传也存在着困惑,最多的就是:工作流技术和BPM(业务流程管理)技术究竟存在着什么区别?为什么很多原先的工作流产品现在都改称为BPM产品?本书将对这些问题都进行一定的讨论,一个事实是IT流程系统将在企业的改进方面发挥越来越重要的作用,但是不可否认的是,就目前而言,这些系统还存在着很多的局限,如果一个流程产品的思想是流程自动化,那么很大程度上这个产品是不符合企业发展需要的。


提到流程,第一个问题就是流程的历史。18世纪英国经济家学亚当·斯密在《国民财富的性质和原因的研究》中提出劳动分工原理,提出分工有利于提高效率、增加产量,其理由有三:第一,劳动者的技巧因业专而日进;第二,分工可以免除由一种工作转到另一种工作的时间损失;第三,简化劳动和机械的发明使一个人能做许多人的工作。亚当·斯密的分工论蕴涵了最朴素的流程理念。流程产生于一系列的分工。


维基百科里 对业务流程进行了如下定义:业务流程是为特定的对象(客户)创造价值的过程,这一过程由一系列相关联、有组织的活动或任务组成。企业和组织中,业务流程一 般被划分为三种基本类型:管理流程,对企业运行进行管理、协调的流程;运行流程,构成核心业务和创造基本价值的流程,如采购、制造、市场销售等;支持流 程,支撑管理流程和运行流程的流程,如会计、招聘、技术支持等。


接下来我们关注工作流技术的历史。工作流技术发端于1970年代中期办公自动化领域的研究工作,但工作流思想的出现还应该更早,1968Fritz Nordsieck就已经清楚地表达了利用信息技术实现工作流程自动化的想法。1970年代与工作流有关的研究工作包括:宾夕法尼亚大学沃顿学院的Michael D. Zisman开发的原型系统SCOOP施乐帕洛阿尔托研究中心Clarence A. EllisGary J. Nutt等人开发的OfficeTalk系列试验系统,还有Anatol HoltPaul Cashman开发的ARPANET上的监控软件故障报告程序。SCOOP, OfficetalkAnatol Holt开发的系统都采用Petri的某种变体进行流程建模。其中SCOOPOfficetalk系统,不但标志着工作流技术的开始,而且也是最早的办公自动化系统。


 可以看到,工作流最初出现的思想和要解决的问题即是实现工作流程的自动化。但是这带来了工作流技术应用的局限:第一是在企业里存在着很多关键的业务流程,这 些流程自动化的成本太高,无法自动化;第二是很多流程并不需要自动化,自动化反而会降低这些流程的执行效率,典型的在一个企业里,请假往往需要一定的审 批,在这种情况下,直接面对面的交流往往比通过工作流提交表单更有效率;第三是自动化流程往往意味着流程的柔性降低,比如制造企业都有设备维修业务过程,基本步骤如下:故障维修申请 > 审批 > 派工 > 领料 > 维修 > 验收 > 维修数据记录。这样的一个维修过程如果用工作流实现,工作流引擎会严格按照这样一个顺序执行,但是车间随手换了一个备件,可能只需要5分钟,而从提交申请到维修结束,走这样一个繁琐的过程,恐怕不是信息系统服务于人了,而是人服从信息系统了,再比如,紧急情况下进行的维修,可能直接进行维修、验收、记录维修数据三个步骤,可能连派工都来不及了。此时,自动化流程就会严重影响执行效率。

1970年 代人们对工作流技术充满着强烈乐观情绪,研究者普遍相信新技术可以带来办公效率的巨大改善,这种期望不可避免的落空了。人们观察到这样一种现象,一个成功 的组织往往会在适当的时候创造性的打破标准的办公流程;而工作流技术的引入使得人们只能死板的遵守固定的流程,最终导致办公效率低和人们对技术的反感。1970年代工作流技术失败的技术原因则包括:在办公室使用个人计算机尚未被社会接受,网络技术还不普遍,开发者还不了解群件技术的需求与缺陷。总结一下,工作流应用失败的原因有两点:第一点是自动化流程的柔性低;第二点则是限于当时的技术原因。


进入1990年代后,随着IT技术的发展、个人计算机的普及,工作流技术开始重新进入一个新的热潮,这个热潮完全是技术驱动的,这时候出现了大量的工作流技术应用。需要注意的是,工作流技术不仅仅是指专门的工作流管理系统,同时也指拥有工作流特征的各种应用系统,例如各类企业管理软件(ERP)和协作软件里有具有的相应流程组件。工作流技术的应用使得很多应用软件的开发得到一定程度的简化(同时,可以观察到工作流产品的采购客户往往会是系统集成商)。需要注意的是,此时工作流要解决的问题域依旧是实现工作流程的自动化,由此带来的应用局限并没有发生变化。


与此同时,新的管理革命正在发生。


1990年迈克尔·哈默在《哈佛商业评论》上发表了题为《再造:不是自动化改造而是推倒重来》(Renglneenllg workdon"t automateobliterate)的文章,文中提出的再造思想开创了一场新的管理革命。1993年迈克尔·哈默和詹姆斯·钱皮在其著作《企业再 造:企业革命的宣言)(Reengineering the Corporationa Manifesto for Business Revoiution) 一书中,首次提出了业务流程再造(BPRBusiness Process Reengineering)概念,并将其定义为:对企业业务流程进行根本性的再思考和彻底性的再设计,以取得企业在成本、质量、服务和速度等衡量企业绩 效的关键指标上取得显著性的进展。该定义包含了四个关键词,即:流程根本性彻底性显著性

以此为标 志,形成了新的业务流程理念,并伴随着对传统企业金字塔式组织理念和管理模式的反思,新的理念强调企业以业务流程为中心进行运作、打破传统的部门隔阂、增 加客户价值和企业效益(降低成本)。以业务流程为中心取代职能分工,成为管理的首要原则,围绕流程建立起来的组织具有更高的敏捷性、效率和效益,呈现出扁 平化、网络化的特征。


新的管理理念催生新的IT产品,BPM产品孕育而生。可以说一个好的IT产 品总是对应有相应的理论基础,那种简单的对现有工作方式的复制化是没有生命力的(一个小的而典型例子是电子印章软件,从布局到排版都很逼真。可是现实中印 章的设计是为进行文件的状态确认,非常直接,但是在电脑上摹仿这种印章,不但用着别扭,看着也十分难过,更重要的是,明明通过工作流的控制已经能够确认文 件的状态,却一定要通过电子印章来生硬模拟。)。很多技术人员以XPDLBPEL来区分流程产品是工作流还是BPM,认为BPM更为强调软件的系统集成能力。实际上,工作流软件与BPM软件最大的区别不在于技术实现,而是它们解决的问题域发生了变化。


工作流软件解决的问题域是流程的自动化,而BPM软件解决的问题则是业务流程的优化


因为解决的问题域发生变化,那么BPM软件相比工作流软件在技术上的变化就很清晰了:强调对流程运行的监控、强调对流程运行数据的分析、强调对各种企业应用软件的集成能力、强调快速的开发能力。实际上很多BPM软件的前身即是工作流产品,从技术角度上理解,工作流软件和BPM软件是没有区别的,BPM软件是工作流软件发展的结果,只是开发商出于市场的考虑换上一个不同的标签而已(非常类似于当前的药品市场,同一种成分换个名称就变成新药)。然而从处理问题的角度考虑,区别两者则又是必要的。


但是BPM软件面临的问题依旧存在,因为很多BPM软件解决问题的思路并不正确,很多BPM软件依旧是通过自动化流程来实现业务流程的优化,这再次回到工作流软件所面临的问题:企业很多业务流程很难自动化、自动化流程的柔性很低。对于这些问题,BPM软件试图通过简化编程(快速开发、SOA思想)和系统集成来尽可能自动化多的流程,通过增强流程定量分析能力来尽可能的增加流程柔性。这实际上是在用正确的方式做错误的事,因为解决问题的思路从一开始就决定了这并不是一条正确的路。


相比而言,NimbusControl-ES软件则选择了另外一条道路,它并不强调流程的自动化,它是从咨询软件发展而来的,这决定了其解决问题的另外一种方式:强调对现有流程的评估和重构而非自动化。在Control-ES里,流程是作为企业财产保存的,仅仅文档化。这几乎立刻扩大了其对业务流程的描述能力,但是其的咨询背景也决定了它的局限性:无法实时获取业务流程执行的数据(完全依靠咨询人员的工作),于是Control-ES更多是作为咨询人员的工具而存在的。从某种意义上说,流程改进本来就是一项咨询工作,很多IT厂商甚至没有任何业务领域经验,拿出其BPM软件就宣传能够实现客户流程的优化是一件很搞的事情,很多所谓的流程梳理实际仅仅是对现有流程的复制再现,没有任何改进可言。


          一种更好的方式是文档化所有业务流程,然后通过系统集成能力实时获得关键的数据信息,实现以流程为中心的数据撮合,关键的流程执行和改进则交由人去灵活执行。对这种实现思路我们将在本书的最后部分进行讨论。可以看见的是,流程优化从来也不应该是IT系统能够完成的事情,IT系统所要做的是为流程优化撮合必需的数据,做为支撑系统而存在。


          说完BPM软件,最后我们需要关注的一个方向是云计算。越来越多的企业将其工作放置到了网上,典型的如Google提供的各种在线服务,文档、邮件、Excel等,这种趋势触发了新的业务模式,云中的工作流即是其中一种,通过提供在线的工作流程自动化,将各种在线服务通过流程粘合起来。在这方面,Cordys走在了最前面。


posted @ 2009-11-29 20:39 ronghao 阅读(1883) | 评论 (0)编辑 收藏

我们知道,一个商业目标的实现必定由一系列 的活动组成,这些活动的编排即构成了以目标为导向的业务流程。管理的目标即通过合理有效的编排这些活动以期以最少的成本达到最大的收益。这个编排的过程亦 即进行业务流程建模的过程。在进行业务流程建模时反复出现的活动结构构造即产生了模式。在本章中,我们将讨论工作流的控制模式。控制模式关注业务流程中活 动的编排,一方面强调与实际业务的契合,另一更为重要的方面则是如何合理调配这些活动。

 

本章讨论的控制模式共计43种。需要注意的是,这些模式的出发点是基于对实际业务进行描述的,与具体的工作流系统没有太大的关联。而一个工作流系统对工作流模式的支持程度则直接决定了该系统对业务的建模能力。所以在某种程度上,衡量一个合适的工作流系统时,往往会考虑其对工作流模式的支持程度。

 

本章讨论的控制模式按照描述、应用和实现展开,分别对应着模式的介绍、模式对实际业务的映射和工作流产品对该模式的实现支持。最后是小结。作为约定,我们将业务流程里的活动映射为任务,将对活动的建模描述映射为任务节点。

 

一、       基本控制模式

基本控制模式包括5个模式,是其他控制模式的基础。

 

1、顺序(WCP_01: Sequence

描述

在同一个流程实例里,任务会在前续任务完成后顺序触发。

图 4-1

如图4-1所示,任务A执行完毕后会顺序触发任务B的执行,任务B执行完毕后会顺序触发任务C的执行。

 

同义词

顺序执行、串行路由。

 

应用

顺序模式是工作流建模的基础,是流程定义里最基本的构建块,用以描述连续串行的一系列任务,这些任务之间的触发是无条件的。

顺序模式也是实际的业务中应用最多的模式, 当实现一个业务价值需要执行多个任务时,最自然的方式就是排序并顺序完成这些任务,典型的如流水线作业。当企业人数不多,业务模式简单(不需要过多的任务 或任务之间存在很强的线性依赖关系),管理成本很低时,顺序模式是最自然的选择。

 

2、并发分裂(WCP_02: Parallel Split

描述

分支分裂为两个或多个后续分支,当分支执行完毕后触发后续并发分支的同时执行。并发的分支有可能在后续合并为一个分支,也可能不合并。


4-2

如图4-2所示,任务A完成后将同时触发任务B和任务C的执行,任务B和任务C的执行不存在前后关系。

 

同义词

AND-splitFork、并行路由、并行分裂。

 

应用

在传统的软件开发里,开发过程被典型的分为了5个阶段,如下图所示:


4-3

5个 阶段是顺序执行关系,典型的当需求分析完毕后会有一个需求冻结状态,在这种状态下才开始正式的软件设计和实现。该模式最大的弊端在于在需求分析阶段不可能 捕获用户所有可能的需求,而且客户的需求是变化的,开发阶段由于需求冻结对于客户完全黑盒,导致最后的交付无法实现客户期望的业务价值。

在敏捷开发里,开发过程由多个迭代组成,在每个迭代里,需求分析、架构设计、编码开发、测试和交付都是同时进行的,客户参与到这个过程中,客户能够从不断的交付中提出新的需求,这样软件才能够更好的响应变化,不至于在最终交付时出现业务价值的偏差。


4-4

其实从某种角度上看,不同企业的组织管理结构也决定了它所采用的业务流程模式。在图4-3所 示的开发流程里,每个阶段都对应于不同的部门,需求分析有专门的业务部门,开发部门内部分为了架构部门、开发部门和测试部门,交付则又有专门的实施团队, 在这种情况下,从管理的成本考虑,顺序执行无疑是最自然和最便宜的选择(这也是为什么在传统企业里实施敏捷困难的原因之一)。

而在敏捷开发团队里,整个团队则是围绕开发流程建立,减少了内部不必要的协调沟通成本,能够达到相对较高的执行效率。

 

实现

由于后续分支的触发是无条件的,所以在很多工作流产品的实现里省去了AND-split节点,直接由任务节点进行分支分裂,如下图4-5所示:


4-5

jBPM使用token记录当前流程实例执行的位置并触发流转,建立起token的父子关系。如下图所示,在AND-split节点每个并发的分支都会产生一个新的子token,当子token到达AND-join节点后即可通过其访问到它的父token,再通过父token遍历其子token即可获得当前并发分支的执行情况并实现合并。


4-6

作为约定,我们在后续的说明中,将会采用token来指代当前流程实例所执行的位置。

 

3、同步(WCP_03: Synchronization

描述

两个或多个分支合并为一个后续分支,当被合并的分支都执行完毕后,后续分支才被触发。


4-7

如上图所示,当任务A和任务B都完成后,才会触发任务C的执行。

 

同义词

AND-join、汇聚、同步。

 

应用

在实际的应用中,AND-split节点与AND-join节点一般成对出现。

在任何时候,总结总是有必要的,每次迭代开发结束后,我们都会进行迭代小结,写出应该改进的部分、应该保持的部分,以保持整个团队的迭代前进。

实际上,很多情况下AND-split节点和AND-join节点往往隐含着管理意义,上一级的管理者在AND-split节点进行任务的管理和下发,在AND-join节点对任务执行结果进行负责。

 

实现

AND-split节点一样,在部分工作流产品里,直接采用任务节点进行分支合并,如下图所示:


4-7

jBPM里,分支的合并实际是token的合并,子token生命周期终止,父token重新激活。

 

4、排他选择(WCP_04: Exclusive Choice

描述

分支分裂为两个或多个后续分支,当分支执行完毕后只能选择触发一个后续分支执行,即多选一。


4-9

如上图所示,任务A完成后将会选择触发任务B或任务C的执行,任务B和任务C之间只能选择一个执行。

 

同义词

XOR-split、排他OR-split、条件路由。

 

应用

流程里的决策任务。会存在两种决策方式:人为决策和系统决策。由人或一组系统设定条件根据流程执行情况作出后续执行路径的选择。

 

实现

两种实现方式,一种是在XOR-split节点定义路由选择条件(图4-10)、一种是在后续转移线上定义触发条件(图4-11)。


4-10


4-11

条件的计算有多种方式:工作流变量与相应条件定义值简单匹配、提供接口由具体实现类返回计算结果、规则引擎进行规则匹配计算。同时,当存在多个后续分支和条件判断时,一般会定义一个默认执行分支。

 

5、简单合并(WCP_05: Simple Merge

描述

两个或多个分支合并为一个后续分支,任何一个分支执行完毕后就会触发后续分支的执行,不需要同步,遵循先进先出的原则。需要注意的是:该模式有个前提条件,即前续分支有且只有一个会执行。


4-12

如上图所示,任务A或任务B只要有一个完成都会触发任务C的执行,但是任务A和任务B有且只有一个可以执行。如果任务A和任务B都有可能执行,则变为另外一个模式:多合并模式(WCP_08)。

 

同义词

XOR-join、排他OR-joinmerge

 

应用

在实际的应用中,为保证该模式的前提条件,一般XOR-join节点与XOR-split节点成对使用。

 

实现

AND-join节点一样,因为不需要任何条件判断,所以在部分工作流产品里,直接采用任务节点进行分支简单合并,但是需要和AND-join做出区别,如下图所示:


4-13

6、基本控制模式小结

基本控制模式非常简单,实现起来也没有太大的难度。需要注意的是,对于一种模式往往会存在多种实现方式,笔者的建议是:将条件判断的节点独立出来,由其负责条件计算和路径选择,而任务节点则只关注于实际业务的执行,做到职责分离。例如,AND-splitAND-joinXOR-splitXOR-join节点都会单独存在。

可以看到:除去AND-splitAND-join节 点,顺序、排他选择、简单合并模式组合的流程和我们编写程序的逻辑流程图非常的相似,也就是这三种模式能够对程序的逻辑流程图进行建模。于是一件有意思的 事情出现了:有快速开发平台产品使用流程引擎来编排程序逻辑。他们的做法是将细粒度的代码逻辑封装为运算构件,然后再通过流程的可视化编辑器将这些运算构 件粘合起来。这样,传统方式下采用代码实现业务逻辑的过程变成了画流程图的过程。笔者认为这样的实现存在相当大的弊端,相当不合理。首先,编写代码变得复 杂了,明明几十行代码能够实现的逻辑却需要经过编写构件、绘制程序流程图、部署、运行好多步才能实现,编程效率可以想象;其次是代码的执行效率低,程序的 运行需要经过一次流程定义的解释才能执行;然后是这种实现完全牺牲了语言自身的特性,面向过程,很难提供代码级别的单元测试环境,只能提供有限的调试。该 实现实际上是定义了一种简单的流程语言,通过该流程语言来进行功能函数(运算构件)调用的编排。任务编排没有问题,服务编排也没有问题,但是如果编排细粒 度到功能函数,那么就超出了流程引擎的作用域。提升编程效率的最好途径总是语言而不是工具。

posted @ 2009-11-22 22:39 ronghao 阅读(1417) | 评论 (9)编辑 收藏

六、自动开始模式

在前面的资源模式里,我们讨论了创建模式、推模式和拉模式,它们实际对应着工作项的一个正常生命周期:创建、提供/指派、资源选取开始执行。在前面的讨论里,工作项的执行都是由资源驱动的(从工作项待办列表里选取执行),而自动开始模式则提供了一种系统驱动工作项执行的方式,系统直接驱动工作项执行往往表明了该工作项的最高优先级,需要马上开始执行。


5-42

如图5-42所示,自动开始模式对应着红线标识着的工作项的状态变迁,共有4种模式:创建即执行、指派即执行、成堆执行和链式执行。

 

1、创建即开始执行(WRP_36: Commencement on Creation

描述

资源能够在工作项一创建完毕就开始执行。


5-43

如图5-43所示,任务A工作项一创建就插入员工甲的办理列表,需要员工甲马上开始执行。

 

应用

该模式应用在关键的优先级高的任务里,通过系统推送,强制资源优先执行该任务,省去任务的等待时间。

 

实现

该模式的实现实际是系统同时完成了工作项的创建和推送,系统需要确定具体的执行人,工作项不会分配给角色、岗位等资源组以提供给相应资源进行选择。

 

2、指派即开始执行(WRP_37: Commencement on Allocation

描述

资源能够在工作项一指派完毕就开始执行。


5-44

如图5-44所示,任务A工作项一旦被员工甲从可拾取列表拾取就马上插入员工甲的办理列表,需要员工甲马上开始执行。

 

应用

该模式跳过了工作项的指派状态,实际是对创 建即开始执行模式的扩展,在创建即开始执行模式里,工作项必须预先确定明确的执行人,不能分配给角色、岗位等资源组,而在该模式里除了支持创建即开始执行 模式里的情况,同时也提供了对这种情况的支持,工作项可以提供给多个资源拾取,一旦一个资源拾取则必须马上开始执行(从这个角度看,该模式与资源驱动执行-提供工作项模式是相同的)。

 

 

3、成堆执行(WRP_38: Piled Execution

描述

资源能够成堆执行相同任务的不同工作项。


5-45

如图5-45所示,员工甲有多个任务A的工作项需要执行,这些注意的是,这些工作项并不是由一个任务实例所产生的,它们属于不同的流程实例,是由多个流程实例里的任务A生成的。一旦员工甲开始任务A工作项的执行,那么他将执行所有任务A的工作项,即将任务A相关的工作项全部打包执行。这一功能由系统驱动,系统将与任务A相关的工作项依次推送至资源的办理列表。

 

应用

开发人员甲熟悉持续集成工具,此时同时有多个软件开发项目需要搭建持续集成环境。一旦他为某个项目组搭建了持续集成环境,那么处于执行效率的考虑,最好的方式无疑是他一鼓作气将所有的持续集成环境都搭建完毕。

相同/相似的工作交由同一资源一并执行,这些工作具有完全或大部分相似的执行上下文(相同的知识、能力要求),从这个角度能够达到最高的工作效率。

 

实现

系统需要在进行工作项状态变迁操作时提供相应的钩子,以进行相应的回调操作。

 

4、链式执行(WRP_39: Chained Execution

描述

在一个流程实例里,当前一个任务的工作项执行完毕后,能够自动开始执行下一个任务的工作项。


5-46

如图5-46所示,任务A和任务B是两个连贯的任务,它们都分配给员工甲执行,当员工甲执行完毕任务A的工作项后,任务B生成的工作项将马上被系统发送至员工甲的办理列表,员工甲需要马上办理。

 

应用

该模式实际是将资源胶黏在一个流程实例上,同样是出于执行效率的考虑(两个任务位于同一流程实例里,具有相同的执行上下文)。该模式的应用具有前提条件:流程定义时,连续的任务由相同的资源进行处理。

 

七、可见性模式

可见性模式讨论各种不同资源对工作项的可见 性,不同的资源由于角色、权限的不同,对工作项拥有不同的可见范围。由于涉及到权限,那么根据不同的组织机构设置,必然会出现不同的工作项权限分配,这里 不讨论具体的工作项权限分配,仅从工作项的状态来讨论这些工作项区分可见性的必要性。

可见性模式包括2种:未指派状态工作项的可见性和指派状态工作项的可见性。实际上,工作项处于执行状态或完成状态也存在不同的可见性。

 

1、可配置的未指派工作项的可见性(WRP_40: Configurable Unallocated Work Item Visibility

描述

能够配置未指派工作项的可见性。


5-47

如图5-47所示,可拾取列表里存在3个工作项:任务A工作项、任务B工作项和任务C工作项。员工甲可拾取的工作项包括:任务A和任务B工作项;员工乙可拾取的工作项包括:任务B和任务C工作项,那么由此产生的可见性是:员工甲只能看到任务A和任务B工作项,而员工乙则只能看到任务B和任务C工作项。而作为员工甲和员工乙的部门经理,他需要了解每个属下的工作情况,所以他可以看见所有甲乙可见的工作项。

 

应用

随着企业规模的发展,几乎所有企业的组织模 型都会形成金字塔型的结构,一方面是出于分工的需要,另一方面则是出于管理的需要,每一层级的人员都需要对上一级负责,同时管理下一层级的人员。处于管理 的需要,管理者必然需要了解下属的工作情况,这样权限就自然产生了,具体到工作流的任务里,管理者需要对其所管理下属的工作具有可见性。

其实不仅仅是对于工作项,对于流程实例本身 也具有可见性的分配。对流程负责的人必然具备最大的可见性和权限,流程根据任务分解,如果仅仅只对某一任务负责,那么则只对该任务具有可见性,而如果需要 对多个任务负责,那么就需要对多个任务具有可见性,最直接的负责人就是具体执行该任务的人员,但是引入管理的层级后,职责的承担也会形成层级的关系,从上 至下层层承担,此时担负最大职责的人员往往不再是具体的工作执行人员,而是相应的管理人员。

 

实现

在前面所描述的情况里,支持员工甲乙的可见性是比较简单的,因为每条工作项记录都携带有参与者信息,但是部门经理显然不在这些参与者信息里,所以需要引入与组织权限模型相匹配的工作项查询机制,即不同于工作项列表的查询列表。

 

2、可配置的指派工作项的可见性(WRP_41: Configurable Allocated Work Item Visibility

描述

能够配置已指派工作项的可见性。


5-48

如图5-48所示,待办列表里存在3个工作项:任务A工作项、任务B工作项和任务C工作项。指派给员工甲的工作项包括:任务A和任务B工作项;指派给员工乙的工作项包括:任务C工作项,那么由此产生的可见性是:员工甲只能看到任务A和任务B工作项,而员工乙则只能看到任务C工作项。而作为员工甲和员工乙的部门经理,他需要了解每个属下的工作情况,所以他可以看见所有甲乙可见的工作项。

 

八、多资源模式

到目前为止,我们讨论的工作项都是与某一特定资源一一对应的,即一个工作项只能由一个单一资源执行,或者严格来说,一个工作项在任何时间段都只能由一个单一资源执行(考虑到工作移交的情况);同时,一个资源在任何一个时间段都只能处理一个工作项。

多资源模式将会讨论两种不同的情况:一个资源同时执行多个工作项、多个资源执行同一个工作项。

 

1、同时执行(WRP_42: Simultaneous Execution

描述

资源能够同时执行多个工作项。


5-49

如图5-49所示,员工甲的办理列表里有三个工作项,他能够同时执行这三个工作项。

 

应用

和计算机一样,虽然在任何时刻都只能处理一项工作,但是通过将多项工作切分成多个线程交替执行,从某个时间段看,人能够同时处理多项工作。

人能够选取相关联的多个工作,同时开始执行,在执行的过程中,合理安排这些工作的执行时机和顺序。

 

实现

几乎所有的工作流系统都不会约束人员往自己的办理列表里增加多个工作项。

 

2、增加资源执行(WRP_43: Additional Resources

描述

资源能够要求增加资源来处理他正在执行的工作项。


5-50

如图5-50所示,员工甲和员工乙同时处理一个工作项。

 

应用

在一些复杂的场景里,一项工作往往需要多个资源共同协作完成。

典型的在一个会签任务里,一个发文需要多人签字通过,同时在会签过程中,经常出现动态加签的情况:需要新的人员加入进行签字。

在敏捷开发里,所有的开发工作都是由两个开发人员共同结对完成。

 

实现

工作项作为工作流系统里最小的工作单元,如果将其分配给多个资源,无疑会增加编程模型的复杂度。最常见的实现方式是增加工作项,一个任务节点对应多个工作项,对于需要增加资源的情况,增加工作项。

 

九、小结

在本章里,我们讨论了工作流的43种资源模式,这些模式分为7类,分别是创建模式、推模式、拉模式、折回模式、自动开始模式、可见性模式和多资源模式。

创建模式在系统创建工作项时生效,其位于工作项生命周期的创建阶段,创建模式作为流程模型的构成部分在流程设计期指定,通常在任务节点的定义里进行定义,与一个任务关联,其用来限定可执行该任务的资源范围。系统根据创建模式限定的资源范围生成工作项。

接下来,系统需要将工作项推送给相关的资源进行执行,这个推送的过程即是推模式所包含的内容。工作流系统通过工作项管理器即不同类型的工作项列表与用户进行交互,这里的推送可以理解为系统将生成的工作项推送至相应资源的工作项列表里。

推模式的主语是系统,由系统将工作项推送至资源的工作项列表,那么,接下来的主动权交由单个资源本身,由其拉动工作项的执行,这是拉模式所包含的内容。

实际工作中,工作的执行状态不可能总是与预想相符的,总会出现各种各样的情况,例如重新分配、重做、挂起等等。折回模式对应着这些情况,折回代表着工作项状态的反复、回退。

自动开始模式提供了一种系统驱动工作项执行的方式,系统直接驱动工作项执行往往表明了该工作项的高优先级,需要马上开始执行。

可见性模式讨论各种不同资源对工作项的可见性,工作项自身作为资源与权限相关。

多资源模式讨论一个资源执行多个工作项和多个资源执行同一个工作项的情况。

从这些模式的讨论可以看出,这些模式更多关 注的是对实际业务执行的场景描述,关注通过合理分配任务和调配工作的执行为组织带来最大的执行效率。从另一个角度看,由于这些模式都以业务作为出发点,这 给工作流系统的实现带来了复杂性,很多模式当前的工作流系统都无法完全支持或直接支持。在很多情况下,模式的支持需要很多的约束,而这种约束往往需要在工 作流实施阶段结合客户具体情况进行限定,这实际强调了工作流实施的重要性,工作流系统的应用是由工作流产品加实施两部分组成,很多时候,实施占据了更大的 比重,这就对工作流产品的可扩展性提出了要求。应用工作流不仅仅是选择工作流产品,更重要的还包括选择合适的实施团队。

在下一章里,我们将讨论另外一种工作流模式-数据模式。

posted @ 2009-11-16 09:26 ronghao 阅读(1275) | 评论 (0)编辑 收藏

五、折回模式

实际工作中,工作的执行状态不可能总是与预想相符的,总会出现各种各样的情况,例如原本分配给员工甲的任务由于甲要请假不得不重新分配,由于新的紧急任务员工乙当前的工作需要挂起一段时间等等。折回模式则刚好对应着这些情况,折回代表着工作项状态的反复、回退。


5-33

如图5-33所示,折回模式对应着红线标识着的工作项的状态变迁。这些状态变迁对应着以下情况:

委派:资源将先前指派给他的工作项委派给他人执行。

系统重新分配:系统将没有完成的工作项重新提供或指派给其他资源执行。

退回指派:资源撤销指派给他的工作项,工作项可以重新指派给其他资源。

工作移交:资源将其已经开始执行的工作项移交给他人执行。

挂起/恢复执行:资源临时挂起当前执行的工作项,并在某一个时候重新恢复执行该工作项。

跳过:资源选择跳过指派给他的工作项的执行,不执行该工作项。

重做:资源重新执行先前已经完成的工作项。

提前执行:资源在流程实际触发该工作前已经开始执行该工作。

折回模式共有9种。

 

1、委派(WRP_27: Delegation

描述

资源能够将先前指派给他的工作项指派给另外的资源执行。


5-34

如图5-34所示,委派对应于红线标识着的工作项状态变迁。

 

应用

委派在平常工作中非常常见,例如员工甲将要请假,他将他要完成的工作委派给同事乙执行;在协同办公里,领导将相关工作委派给下属执行等等。

 

实现

实际应用中,委派按照粒度分为了两种:一种 是工作项的委派,这是一种细粒度的委派,指单一任务实例的委派,与某一特定的任务实例关联;另一种是业务的委派,这是一种粗粒度的委派,例如,员工甲将其 负责的某类业务的工作全部委派给员工乙,这意味着属于这类业务的所有工作都将由员工乙执行。业务的委派通常与权限紧密关联,实际实现时为避免用户权限的混 淆,一般采取分开登录系统的方式进行实现,即员工乙如果想处理员工甲委派给其的工作,则需要用员工甲的账号和其给定的密码重新登录系统,并注销掉自己账号 的使用。

需要注意的一点是:委派意味着原先指派的资源还必须对该工作负责,例如员工甲将某项工作委派给员工乙执行,尽管员工乙实际执行了该工作,但该工作仍然必须由员工甲负责。所以在实现中,员工甲必须能够保持对委派工作项的追踪和控制。

 

2、系统重新分配(WRP_28: Escalation

描述

系统能够重新分配已经分配的工作项,以加快工作项的执行。


5-35

如图5-35所示,工作项原先提供或指派给了一个或多个资源执行,现在由于各种原因,需要优化该工作项的执行,所以将该工作项收回重新分配,提供或指派给其他的资源。

 

应用

工作分配的优化。很多时候,计划跟不上变 化,工作也是这样,分配工作前有许多的考虑因素,例如个人能力、工作经验、技能要求等等,但在实际工作中往往会发现原先的人力分配并不合理,或者有些人承 担了太多的职责,或者有人能力超出其目前担承的职责等等(在敏捷软件开发里,我们通过频繁的交换结对编程以达到部分的平衡),在这种时候就需要对工作进行 灵活的重新分配以到达最高的执行效率。

 

实现

实际实现中非常受限,对流程的优化始终是一 个对人的命题,而不是对机器对工具的命题,工具所能做到的只是尽可能多的提供可供参考的数据模型,例如各种报表、数据分析等等,最后做出决策的还是人。所 以该模式的实现也以提供给流程管理员重新分配工作项的能力为主,同时提供工作项超时的提示为辅。

 

3、退回指派(WRP_29: Deallocation

描述

资源撤销指派给他的工作项,工作项可以重新分配给其他资源。


5-36

如图5-36所示,资源能够退回原先指派给他的工作项,该工作项可以交由系统重新分配给其他资源。

 

应用

同样是工作分配的优化,只不过该模式由资源驱动。

实际工作中,由于各种原因,例如经验不足、当前承担职责过多等等,发现自己并不能很好完成当前的任务时,可以提出不执行该任务。

 

实现

实现的难点在于资源退回工作项后的重新分配,即如何重新分配该工作项。

与提供给多个资源模式对应,该模式的最简单支持是退回重新提供给多个资源。例如工作项分配给开发人员这一角色执行,开发人员甲拾取了该工作项(故事卡),经过一番痛苦和彷徨,最终将该工作项退回,这样所有开发人员又都能拾取该工作项。

 

4、有状态工作移交(WRP_30: Stateful Reallocation

描述

资源将正在执行的工作项移交给其他资源执行,该工作的状态将得到保存。


5-37

如图5-37所示,资源将已开始执行的工作移交给其他资源执行。该模式与委派模式很相似,差别就在于委派模式是将未开始执行的工作进行重新指派执行,而该模式则是将已开始执行的工作进行重新指派执行。委派模式中的委派者仍需要为委派出去的工作负责,而移交则同时意味着责任的移交。

 

应用

工作移交非常常见,最常见的原因包括位置调动、离职、休假等等。

 

实现

在大多数的工作流系统实现里,业务数据与流程数据是相互隔离的,仅仅通过某一字段进行关联(例如业务主键),流程的目标是携带业务数据进行流转。在这种情况下,该模式的实现变成了默认实现,系统不需要做任何回退或清理操作,直接做工作项的转发即可。

 

5、无状态工作移交(WRP_31: Stateless Reallocation

描述

资源将正在执行的工作项移交给其他资源执行,该工作的状态不会得到保存。

与有状态工作移交对应。

 

应用

工作的无状态移交通常意味着工作的重新执行,并且原有的工作对重启的工作而言没有太大的价值。

 

实现

与有状态工作移交相比,该模式需要处理相应业务数据的回退或清理。直接支持该模式比较困难,需要在实施时根据情况对该任务节点操作业务数据的权限进行限定,并在限定的基础上进行记录和回退。

 

6、挂起/恢复执行(WRP_32: Suspension/Resumption

描述

资源能够挂起当前执行的工作项,并在某一个时候重新恢复执行该工作项。


5-38

应用

资源对分配给其的工作进行优化执行,能够根据自己和当前的实际情况合理的安排工作执行,挂起正在执行的工作,执行当前最重要或效率最高的工作,然后再返回执行该工作。

 

实现

基本的状态变迁。

 

7、跳过(WRP_33: Skip

描述

资源能够选择跳过指派给他的工作项的执行,不执行该工作项,并将该工作项标识为完成。


5-39

 

应用

实际工作中,对于非关键工作,可以选择跳过,以便进行后续更为紧急或重要的工作。

 

实现

对于实现工作项本身的状态变迁来说,支持该模式非常简单,问题在于业务数据的依赖关系,如果后续工作的处理依赖于该节点处理后的业务数据,那么简单的选择跳过该工作项的执行就会产生问题。所以一个好的做法是给一些关键任务节点加上约束,不允许执行跳过操作。

 

8、重做(WRP_34: Redo

描述

资源能够对先前完成的工作项重新处理,同时,该工作的后续工作项(后续任务所对应的工作项)也将被重新处理。


5-40

 

应用

对已完成的工作进行重新处理并不少见,但该 模式最为重要的部分还是在于要求所有后续工作的重新处理。所以该模式一般应用在极其重要的关键任务节点,例如非常重要的决策节点,因为后续的任务严重依赖 于该节点所作出的决策(所产生的数据),一旦决策发生变化,那么相应的后续工作必须都要做出变化。这也是业务敏捷性的一种反映。同时需要注意,该模式也是 一种代价高昂的应用,因为这往往意味着该业务流程实例中的很多工作都需要重新处理,所以如何在业务处理中尽早发现可能的环境变化并及时作出决策的调整并避 免成本高昂的返工才是最为重要的一点。

现在的软件开发越来越强调于拥抱变化,强调 代码的重构和演进,尽管如此,如果软件的基础架构需要根据当前业务变化发生重大变更,那么这依旧是一件成本高昂的事情,因为一旦基础架构发生变化,那么很 多模块实现将不得不重新编写代码。所以尽管越来越多的开发团队开始引入敏捷的开发方法,但是经验丰富的开发人员才是整个敏捷团队的基石(敏捷开发非常重视 人)。

 

实现

从该模式里能够看到资源模式与控制模式的结合,工作项的重做需要后续任务的重新执行,这需要取消当前的执行任务,并将流程实例的控制点重新返回至该工作项所对应的任务。这涉及到两种控制模式,分别是:取消任务模式和任意循环模式(具体可以参考第四章的说明)。

 

9、提前执行(WRP_35: Pre-Do

描述

在工作项实际提供或指派给资源执行之前,资源已经可以执行该工作项。


5-41

如图5-41所示,任务A还在执行,任务B还未激活,但此时员工甲已经可以开始执行任务B的工作项。该模式的实现需要一个前提条件:任务B不能依赖于任务A的执行结果,即不能依赖于前续任务的处理输出。

可以看出,该模式与前面推模式里的提前分配模式非常相似,所不同的是:提前分配强调一种通知机制,强调预先准备;而提前执行则已经可以开始实际的执行工作。

 

应用

和提前分配模式不同,该模式实际提供了一种 流程任务执行的灵活机制,在预先定义的业务流程里,任务的执行是具有一定顺序的,可能在大多数情况下,这种顺序是合理的,但是在某些具体的流程实例里,某 些串行执行的任务节点可以并行的执行以达到最好的执行效率和负载均衡,在这种情况下,就可以应用该模式并行执行部分任务。

需要注意的是,该模式仅仅引入了一种实际执行任务的灵活性,是对业务流程定义固化的补偿,如果在一个业务流程中频繁应用到该模式,则往往意味着流程定义本身需要作出调整。

posted @ 2009-11-08 21:08 ronghao 阅读(1668) | 评论 (1)编辑 收藏

本书全文连载地址
四、拉模式

与推模式相比,拉模式的区别在于动作的主语发生了变化:推模式的主语是系统,由系统将工作项推送至资源的工作项列表,那么,接下来的主动权交由单个资源本身,由其拉动工作项的执行。


5-28

如图5-17所示,拉模式对应着工作项的五种状态变迁:

由提供给一个资源拾取到指派给一个资源负责执行,这意味着该资源拾取了该工作项,其将负责该工作项的执行,并将在未来的某个时候执行该工作项;

由提供给多个资源拾取到指派给一个资源负责执行,这意味着多个资源中的一个资源拾取了该工作项,其将负责该工作项的执行,并将在未来的某个时候执行该工作项,余下的资源将不再有机会执行该工作项;

由提供给一个资源拾取到开始执行,这意味着该资源拾取了该工作项,其将负责该工作项的执行,并立即开始执行该工作项;

由指派给一个资源负责执行到开始执行,这意味着该资源开始执行该工作项;

由提供给多个资源拾取到开始执行,这意味着多个资源中的一个资源拾取了该工作项,其将负责该工作项的执行,并立即开始执行该工作项,余下的资源将不再有机会执行该工作项;

拉模式共有6种,分为两组:前三种模式关注工作项的状态变迁;后三种模式关注工作项显示在资源工作项列表里的顺序以及选择执行的方式。

 

1资源驱动指派(WRP_21: Resource-Initiated Allocation

描述

资源能够将工作项指派给自己,负责该工作项的执行,但是不必马上开始执行该工作项。


5-29

如图5-29所示,员工甲拾取了可拾取列表里的任务A工作项,该工作项由可拾取列表移至待办列表。可拾取列表通常是一个共享的列表,而待办列表则是某一资源的专属列表。资源拾取工作项,意味着工作项从共享状态进入到专属状态。

该模式实际对应着工作项的两种状态变迁:由提供给一个资源拾取到指派给一个资源负责执行;由提供给多个资源拾取到指派给一个资源负责执行。

 

应用

该模式符合大多数的工作场景,我选择负责执行该工作,但我并不马上开始,我可能还有其他的工作需要处理,等到处理完毕后才处理该工作。

 

实现

分配给角色、部门等资源组的工作项通常都以共享的形式分配给所有的组内成员,一旦有人拾取即进入他的专属待办列表,其他人不再可见。

 

2资源驱动执行-指派工作项(WRP_22: Resource-Initiated Execution – Allocated Work Item

描述

资源能够开始执行指派给其的工作项。


5-30

如图5-30所示,员工甲开始执行任务A工作项,该工作项由待办列表移至办理列表。

该模式对应着工作项的一种状态变迁:由指派给一个资源负责执行到开始执行。

 

实现

最基本的工作项状态变迁,所有的工作流系统都提供支持。

 

3、资源驱动执行-提供工作项(WRP_23: Resource-Initiated Execution – Offered Work Item

描述

资源能够选取提供给其的一个工作项,并马上开始执行该工作项。


5-31

如图5-29所示,员工甲拾取了可拾取列表里的任务A工作项并立刻开始执行,该工作项由可拾取列表移至办理列表。

该模式对应着工作项的两种状态变迁:由提供给一个资源拾取到开始执行;由提供给多个资源拾取到开始执行。

 

应用

与描述略有不同,实际应用该模式是强制要求资源一旦拾取了共享的工作项就必须马上开始执行,基于两点的考虑:一是工作项能够尽快执行;二是工作项能够指派给当前最为空闲的资源,不会出现该工作项被一繁忙资源卡住,造成等待和阻塞。

在敏捷开发里,我们使用故事卡管理项目的开 发,故事卡足够小(如果大的故事卡则分解为多个任务),每天早上由开发人员挑选移动该卡,一旦该卡由可开发状态移动至开发状态,则必须进行该卡的开发工 作,这样项目的真实进展随时得到显示,同时不允许一个开发人员同时进行多张卡的开发。

 

实现

通过这三个模式我们可以发现,工作流系统实现这些模式只是在不同的工作项列表里移动这些工作项,以反映工作项不同的状态和变迁策略,这对于IT系 统而言这不是很困难,困难在于如何能保证人确实是这么做的,例如说一旦拾取就必须开始执行,工作项的跳转很简单,但无法保证的是拾取该工作项的人一定会按 照要求马上开始执行该工作项,也就是说业务流程项目的实施不仅仅包含技术实施,也包含了一套与之相应的管理实施。那种期望上一套流程系统就能马上提高生产 效率和管理水平显然是不现实的,其中一定包含管理方式的变化和组织机构的变化。

敏捷开发中,早上的站立会议是重要的部分,每个团队成员都会汇报昨天的进展和今天将要进行的工作,这样就保证了工作执行的有效性。

 

4、系统决定工作队列内容(WRP_24: System-Determined Work Queue Content

描述

工作流系统能够排定资源工作项列表里的工作项顺序和内容。


5-32

如图5-32所示,员工甲共享的可拾取列表默认按时间排序工作项。

 

应用

实际应用中工作项的排序条件非常多,其目的就是将最重要或优先级最高的工作项排在最前面,引起资源的注意或优先执行。

 

实现

实际实现时有多种排序策略,通常会有时间排序,例如先进先出、先进后出等,同时也有很多其他的排序元素,例如工作项的预定完成时间、执行该工作项的成本预算、工作项的优先级或重要程度等,系统查询工作项时根据这些影响因素进行默认排序。

 

5、资源决定工作队列内容(WRP_24: Resource-Determined Work Queue Content

描述

资源能够排定其工作项列表里的工作项顺序和内容。

 

应用

为资源提供一定程度上排定工作项的灵活性。每个人关注的视角和侧重点不同,就会产生不同的排序和内容过滤。

例如,作为老板,我可能更为关注各个工作的成本预算,我需要按成本排定各项工作;而作为秘书,我更为关注老板下发各项工作的重要程度,我需要按老板指定的重要程度排定工作。

 

实现

提供工作项列表的客户端排序,一般情况下列表显示系统给定的顺序,用户可以在客户端进行二次排序,典型的Web系统中,工作流系统提供JavaScript的表格控件,利用Ajax异步请求重新排序或进行工作项的过滤。

 

6、自主选择(WRP_26: Selection Autonomy

描述

资源能够根据自己个人的情况选择执行工作项。


5-32

如图5-32所示,员工甲能够根据自己的情况选择执行任务ABC中任意一个工作项。

 

应用

尽管老板要求先实现功能最后再重构,但是我认为当前代码如果不进行一定重构会严重影响后续的开发效率,所以我决定先进行部分重构。

 

实现

几乎所有工作流系统都不会对用户实际选择执行工作项的方式进行限制,也没有办法限制。但是系统一般会把重要的工作项加以高亮显示,让用户优先选择。

posted @ 2009-11-01 20:48 ronghao 阅读(1428) | 评论 (0)编辑 收藏

三、推模式

在创建阶段,系统根据不同的创建模式为任务 节点产生了一个或多个工作项,每个工作项或分配给单个资源或分配给角色、部门等。那么接下来,系统就需要将这些工作项推送给相关的资源进行执行,这个推送 的过程即是推模式所包含的内容。需要注意的是,推模式讨论的是对单个工作项的推送。

在前面我们已经了解到,工作流系统通过工作项管理器即不同类型的工作项列表与用户进行交互,这里的推送也可以理解为系统将生成的工作项推送至相应资源的工作项列表里。


5-17

如图5-17所示,推模式对应着工作项到三种状态的变迁:提供给一个资源拾取执行;提供给多个资源拾取执行(这些资源中只会有一个会实际执行,属于竞争关系);指派给一个资源负责执行。

推模式共有9种,分为3组, 第一组包括提供给单个资源、提供给多个资源和指派给单个资源,讨论工作项推送的最终分配状态;第二组包括随机指派、循环指派和最短队列指派,关注当工作项 分配给角色、部门等包含多个资源的资源组时,如何从中确定最终的一个资源并进行指派;第三组包括提前分配、即时分配和推后分配,关注将工作项推送给用户的 时间。

1、提供给单个资源(WRP_12: Distribution by Offer - Single Resource

描述

能够在非绑定的基础上将工作项推送给单个资源。


5-18

如图5-18所示,任务A工 作项被系统推送至员工甲的可拾取列表。这意味着员工甲不必为该工作负责,他可以选择执行该工作也可选择忽略或拒绝。如果他选择拒绝或忽略且工作项超时,那 么会导致系统对该工作项的重新分配。如果他选择执行该工作,那么他首先需要拾取该工作项,这会使该工作项进入他的代办列表,意味着其必须对该工作负责。

应用

该模式类似于现实工作中的征求意见,先将工作分配给你,然后找你谈话,征求你对该工作的看法,如果合适那么就由你执行,否则再找他人执行。

实现

参与者对工作项的拒绝会导致系统对工作项的 重新分配,这是实现该模式的难点。如何重新分配该工作项,采取何种重新分配策略,这些都具有很大的复杂性。实际上这些工作流模式单个看起来可能比较清晰明 了,但一旦组合起来,例如该模式与创建模式结合起来,那么就有了多种情况变得复杂起来。对于复杂的问题,最好的解决办法就是留给实施阶段,由用户情况作出 使用限定。这也再次强调了工作流实施在工作流应用中的重要性。

2、提供给多个资源(WRP_13: Distribution by Offer – Multiple Resource

描述

能够在非绑定的基础上将工作项推送给多个资源。


5-19

如图5-19所示,任务A所 生成的工作项被推送给多个员工的可拾取列表。这些员工不必为该工作负责,他们可以选择执行该工作也可选择忽略或拒绝。如果他们都选择拒绝或忽略且工作项超 时,那么会导致系统对该工作项的重新分配。如果有一名员工选择执行该工作,那么该工作项进入他的代办列表,其他员工将不再具有拾取该工作项的机会。

应用

该模式是典型的竞争参与,即多人可以完成该工作,先执行者先得。类似于寻找志愿者。

实现

该模式的实现一般是创建阶段将工作项分配给角色、部门等包含多个资源的分组,在推送阶段,将该工作项送至这些组下所有资源共享的可拾取列表里,工作项的实例只有一个,但是多资源可见。

3、指派给单个资源(WRP_14: Distribution by Allocation – Single Resource

描述

能够在绑定的基础上将工作项推送给单个资源。


5-20

如图5-20所示,任务A工作项被系统推送至员工甲的待办列表。这意味着员工甲必须为该工作负责。

应用

该模式是应用最多的模式,直接指定任务的负责人。

在采用军事化管理的企业里,上级的命令一定要执行,下属没有商量和拒绝的权利。

实现

相比提供,指派实现非常容易,直接将工作项推送至选定资源的待办列表。

4、随机指派(WRP_15: Random Allocation

描述

当存在多个资源可供选择时,从中随机选择一个资源进行工作项的指派。


5-21

如图5-21所示,任务A所生成的工作项在创建阶段分配给了开发人员这一角色,在推送阶段,系统会随机选取一名开发人员负责该工作项的执行。

应用

该模式提供了一种指派资源的非确定性机制。

5、循环指派(WRP_16: Round Robin Allocation

描述

当存在多个资源可供选择时,循环选择其中一个资源进行工作项的指派。


5-22

如图5-22所示,任务A所生成的工作项在创建阶段分配给了开发人员这一角色,在推送阶段,系统会循环轮流选取一名开发人员负责该工作项的执行。

应用

不患贫而患不均,平等的分配工作。

6、最短队列指派(WRP_17: Shortest Queue

描述

当存在多个资源可供选择时,选择其中一个具有最少待办工作即最短工作队列的资源进行工作项的指派。


5-23

如图5-23所示,任务A所生成的工作项在创建阶段分配给了开发人员这一角色,在推送阶段,系统发现员工甲的待办列表里有两条待办工作(任务B和任务C),员工乙的待办列表里没有待办工作,所以系统将任务A工作项指派给员工乙负责该工作项的执行。

应用

该模式的目的在于能够最快开始工作的执行,找出相比而言最为空闲的资源迅速开始工作。但是实际应用中,仅仅依靠工作的数量来判断资源是否空闲是不可靠的,因为工作和工作之间还存在着难易之分。

7、提前分配(WRP_18: Early Distribution

描述

在工作项实际可以执行之前即将该工作项通知或潜在的分配给资源。


5-24

如图5-24所示,任务A还在执行,任务B还未激活,但此时任务B的工作项已经提前分配给员工甲,该工作项的主要职责是通知员工甲将由其来完成任务B并能开始一部分准备工作,而实际的工作则要等到任务B被激活后才能进行。

应用

该模式强调的是预先计划,即管理的计划性。

在我们实际的项目开始之前,项目经理已经通知我们将要进行的开发工作,让我们提前熟悉相关的技术。这样当项目开始时就能提高最初迭代的开发效率。

从某种意义上说,稍微复杂一点的工作都应该做到提前通知、提前准备,即计划的必要性。

实现

让工作流系统直接支持该模式比较困难,因为该模式嵌套在控制模式和不同的工作项创建模式里,找不出一种通用的模式,无法预判工作项的生成和实际的参与者。在一定范围内,可以采用下面的方式变通:


5-25

如图5-25所示,在自动节点执行时能确定任务B的参与者的情况下,可以通过自动节点给员工甲发送邮件或消息进行通知,工作流系统并不生成工作项。

8、即时分配(WRP_19: Distribution on Enablement

描述

在工作项实际可以执行时将该工作项分配给资源。

应用

机器执行的工作,重复单一的审批工作,无计划性的工作,如各种突发情况的处理。

实现

大多数工作流系统的标准实现,满足任务执行条件时先激活任务节点,然后创建工作项、分配工作项。

9、推后分配(WRP_20: Late Distribution

描述

在工作项实际可以执行后的某个时间才将该工作项分配给资源。


5-26

如图5-26所示,任务B已经激活且已生成可以执行工作项,但是系统并没有将其分配至员工甲的工作项列表里。这是因为员工甲正在执行任务A的工作项,直到其执行任务A完毕,系统才会把任务B工作项推送至工作项列表。

应用

保证流程和资源对工作的负载处于一种良好的状态,避免出现下图的情况:


5-27

在敏捷开发里,我们强调客户合作,整个的开发过程对用户透明,用户知道当前正在进行的开发工作,也清楚开发团队的开发速度,在这种情况下,一旦有新的需求加入,用户会推迟该需求的实现,或者推迟当前其他需求的实现,从而保证整个团队的开发效率。

实现

该模式的实现依赖于推后的策略,即在什么情况下推后分配,满足什么条件下进行分配。具体实现同样采取推后模式,推后到实施阶段实现。

posted @ 2009-10-25 21:46 ronghao 阅读(1807) | 评论 (1)编辑 收藏

二、创建模式

创建模式在系统创建工作项时生效,如下图所示,其位于工作项生命周期的创建阶段。


5-2

正如上面提到的,工作流系统在执行任务节点时会为其创建相应的工作项,根据情况工作项可以是一个也可以是多个。

创建模式作为流程模型的构成部分在流程设计期指定,通常在任务节点的定义里进行定义,与一个任务关联,其用来限定可执行该任务的资源范围。系统根据创建模式限定的资源范围生成工作项,工作项可以直接分配给人,也可以分配给角色、部门、岗位等。

 

1、直接分配(WRP_01: Direct Distribution

描述

在设计期直接为任务指定特定的资源,该任务的工作项能够在运行期分配给它。注意,指定的资源可以为多个,如果是多个的话就会生成多个工作项,为每个资源生成一个单独的工作项。该模式实际限制执行该任务的资源范围。


5-3

如图5-3所示,任务A在定义时直接指定给员工甲,任务B在定义时直接指定给员工乙,当实际执行任务A和任务B时,将由员工甲和员工乙分别执行。

 

应用

该模式一般应用于流程里的关键路径。同时, 在中小企业里,该模式是应用最多的分配模式,因为人员少,管理扁平,所以每个人的职责都非常清晰。该模式也是执行效率较高的资源模式,因为人和任务直接绑 定,所以不会产生推诿等情况,便于管理也便于追究责任,因为运行情况完全在设计期确定。而随着企业规模的扩大,管理层次的复杂,一个任务往往需要交由特定 的部门、岗位或角色来执行,这样无形中会影响任务执行的效率。

该模式的缺点在于一旦关键人物因为各种原因不能及时处理任务,那么将造成整个流程的挂起等待。

 

实现

最简单的资源模式,设计期确定资源,运行期工作流引擎不需要做额外的工作。

 

2、基于角色的分配(WRP_02: Role-Based Distribution

描述

在设计期为任务指定一个或多个角色,该任务的工作项能够在运行期分配给这些角色。实际执行该任务时,资源将从属于这些角色的资源中产生。该模式实际限制执行该任务的资源范围。


5-4

如图5-4所示,任务A在定义时指定给“开发人员”这一角色,该角色包括了两名员工甲和乙。实际执行任务A时,将由员工甲或乙来执行。

 

应用

企业达到一定规模,就会产生人员的分组,角色是典型的分组方式,将具有相似属性的人员定义为一个特定的角色,这些属性通常与工作的内容相关,例如开发人员、项目经理、总经理等,而角色通常又会与权限产生关联。

将任务分配给角色意味着将会有多个员工可以执行该任务,执行效率相比直接分配会有下降,这也是企业扩大后管理成本增大的一种表现形式。

 

实现

工作流系统内置的组织机构模型需要对角色进行支持。引擎运行期通过角色访问属于该角色的人员。

 

3、延迟分配(WRP_03: Deferred Distribution

描述

在设计期为任务指定资源的标识,典型的,在工作流系统里,该标识对应于一个数据字段变量,例如userid。即在设计期并不确定资源,资源的确定被延迟至运行期,系统运行期从该数据字段里获取该任务分配的资源。


5-5

如图5-5所示,任务B在定义时资源的标识指定为userid,实际执行时,任务A由员工甲执行,其指定了下一任务的执行者为员工乙即设置了userid这一变量为员工乙的id,在任务B实例创建时,它访问userid,发现该变量里是员工乙的id,于是将该任务分配给员工乙。

需要注意的是,根据具体的分配策略,运行期该数据字段不仅可以指定人员,也可以指定角色、部门等。

 

应用

该模式给资源的分配引入了灵活性。资源的确定延迟至运行期,即任务可以根据具体的实际情况再确定执行人。具体实施时,这个指定的数据字段可以通过一系列的规则运算得出。

在国内应用中,该模式被大量应用在人工干预流程中,例如下一任务的执行由上一任务的办理者指定。

 

实现

一般通过工作流变量来包含资源的id。为了区别该id是人员id还是角色id,一般内置特殊变量,例如useridroleidgroupid等。

 

4、按权限分配(WRP_04: Authorization

描述

能够指定资源的范围,只有该范围内的资源才具有执行该任务的权限。


5-6

如图5-6所示,只有项目经理才有权限对开发人员申请软件的要求进行批准。

 

应用

随着分工的细化,每个人工作内容的不同,必然会产生权限的概念。权限实际代表着对同一件事情,每个人执行工作内容的差别。权限越大能执行的操作越多意味着需要负责的方面越多。

该模式强调通过权限对执行任务的资源加以限定。

 

实现

在大多数的软件系统中,角色不仅仅代表具有相似属性的一组人员,同时其也是最重要的权限概念,人员往往不与权限直接关联,角色将人员与权限两者进行关联。所以,对任务设定权限可以通过指定角色来完成。

 

5、职责分离(WRP_05: Separation of Duties

描述

在一个流程实例里,能够指定两个任务必须由不同的资源执行。


5-7

如图5-7所示,任务A和任务B在设计期被指定不能由相同的资源执行,同时它们都指定分配给经理这个角色。那么在运行期,在一个流程实例里,任务A被分配给了员工甲执行,那么在进行任务B的分配时,尽管员工甲也属于经理,但是将不能由其执行。

 

应用

职责分离,不能既当运动员又当裁判员,不能又当跳水队领队又当全运会裁判长。

典型的报销流程里,一般员工的差旅报销由财务人员核实,但如果某名财务人员需要报销差旅费显然不能由自己审批,需要另外一名具有同样权限的财务人员审核。此时就可以对申请任务和审核任务两个任务节点应用该模式。如下图所示:


5-8

 

实现

后续节点进行任务分配时,需要获取与之关联前续节点的分配执行信息。流程定义期,在定义任务节点属性时建立两者的关系。

 

6、流程实例整个处理(WRP_06: Case Handing

描述

在一个流程实例里,所有的任务都能够分配给同一个资源执行。


5-9

如图5-9所示,流程实例中的所有任务都由同一个人执行:任务ABC都由员工甲执行。

 

应用

在应用该模式的流程里,通常流程里的任务都 是紧密关联的。流程里的任务往往执行在一个紧密相关的上下文里,如果所有的工作都由一个人执行,那么在其了解该上下文的情况下连贯执行这些任务会具有更高 的效率;而如果执行任务的过程中换人,那么新换的人无疑还需要时间对该流程实例相关的上下文进行熟悉,这样无疑会多付出执行成本。

同一个软件的开发最好由同一拨开发人员完成,如果中途更换开发人员,那么新来的开发人员需要一段时间熟悉该软件,会对开发进度产生影响。同样的道理,在软件上线前增加开发人员不一定会提高开发效率,很多时候甚至作用相反。

 

实现

可以在实现延迟分配模式的情况下实现该模式,即在第一个任务节点初始化设置参与者变量,后续任务全部使用该变量。如下图所示:


5-10

 

7、经验获取(WRP_07: Retain Familiar

描述

在同一个流程实例里,当存在多个资源都能执行某一工作项时,能够将该工作项分配给执行了前某一工作项的同一资源。


5-11

如图5-11所示,任务A和任务B在设计期被指定由相同的资源执行,同时它们都指定分配给经理这个角色。那么在运行期,在一个流程实例里,任务A被分配给了员工甲执行,那么在进行任务B的分配时,任务B依旧由员工甲执行。

 

应用

该模式刚好与职责分离模式相反,正如它的名字所暗示的,这些任务之间存在着紧密关联,如果执行了其中一个,那么就会熟悉这些相关联任务的背景上下文,积累一定经验,那么后续任务的执行相对其他人来说会变得容易。提高任务执行的效率。


5-12

如图5-12所示,在软件销售的过程中,售前和编写标书是重要的两步,这两个任务最好由同一个人进行处理,因为参与软件售前的人更熟悉客户的实际情况和需求,如果分开由不同的人员执行,那么必然会存在前者与后者的交流成本。

 

实现

后续节点进行任务分配时,需要获取与之关联前续节点的分配执行信息。流程定义期,在定义任务节点属性时建立两者的关系。运行期可以通过参与者变量(工作流变量)建立起运行期关系。

 

8、基于能力的分配(WRP_08: Capability-Based Distribution

描述

能够基于资源的能力进行工作项的分配,能力作为组织模型的一部分建模为资源的属性。


5-13

如图5-13所示,任务A需要交与开发人员执行,同时其对办理该任务的人员能力提出了要求,要求只有熟悉JAVA且工作经验大于2年的开发人员才能执行该任务。这样就缩小了选取资源的范围。

 

应用

人力资源管理中很重要的一点就是要做到人尽 其用,每个人的能力都能得到最大的发挥,其实也就是根据能力将人放置到最合适的工作中去。从这一点看,该模式是对人尽其能的实现。但是如何定义各个人员的 能力,这本身就比较的主观,执行各项工作需要具备的能力也具有主观因素,更何况很多时候能力并不能与表现划上等号,模型是固定的但人是变化的受环境影响 的,所以工具本身就是工具,工具的应用最后还是依赖于人。

 

实现

实现需要两部分:一部分是组织机构对人进行建模时需要包括能力模型;另一部分是流程建模时需要给任务定义执行该任务所需的能力约束,这些约束是一系列的领域特定条件,需要有特定的解析器进行条件的解析。

很多工作流引擎提供有参与者接口,接口返回人员ID列表供给任务分配,从而将这部分工作委派到工作流实施时实现。

 

9、基于历史的分配(WRP_09: History-Based Distribution

描述

能够基于先前的工作历史决定当前任务的分配。


5-14

如图5-14所示,当需要在员工甲和员工乙之间做出选择时,会选择此前执行类似任务时完成最好的员工。需要注意的是,这里的历史不再是限定在同一流程实例的任务执行里,可能是同一流程模型的执行历史,也可能是不同流程模型的执行历史。

考虑历史时,有很多的考虑因素,例如完成类似任务最好的员工、完成类似任务最快的员工、完成类似任务出差错最少的员工等等。这些考虑因素依赖于具体的任务内容和背景。

 

应用

国家篮球队比赛前,主教练会考察各个位置里各个球员的联赛比赛历史情况,其中不仅会考虑出场时间也会考虑各项数据,从而做出选择。

 

实现

同基于能力的分配模式一样,该模式的实现依赖于工作流具体应用的场景,也就是需要到实施阶段结合实际实现。从某一种角度也说明,应用工作流产品时,产品实施阶段也是最重要的步骤,选择工作流产品不仅仅是选择产品也需要考察其背后的实施团队。同时也可见,这些模式在诸如政府OA这些应用中是根本应用不上的。

实现需要两部分:一部分是对用户任务执行历史进行统计分析找出关键的评定因素;另一部分是流程建模时需要给任务定义执行该任务所需的历史因素约束,这些约束同样是一系列的领域特定条件,需要特定的解析器进行解析。

 

10、按组织分配(WRP_10: Organizational Distribution

描述

能够基于人员在组织机构中的位置以及与其他人员的关系分配工作项。


5-15

如图5-15所示,审批任务需要由申请人的部门经理执行,即需要员工甲的部门经理执行。这种情况即基于人员之间的关系分配工作。

基于人员在组织机构中的位置分配即需要在工作流模型里建立起与用户相匹配的组织机构模型,可以将任务分配给部门、角色、临时组、岗位等。

 

应用

应用最为广泛的模式,实际工作中几乎所有的工作都必然和组织机构具有联系。其他模式,如基于历史的分配、基于能力的分配等都是基于该模式之上的扩展,直接分配、基于角色的分配则直接是该模式的简单情况。

 

实现

对该模式的支持实际上是对用户组织机构模型的支持。正如前面所提到的,很难存在一种工作流产品能够支持所有的组织机构模型,但是大部分的产品都会提供一套元模型或扩展机制,需要在实施过程中最大限度的契合客户业务。

 

11、自动执行(WRP_11: Automatic Execution

描述

任务的执行能够自动完成,不需要人员参与。


5-16

如图5-16所示,请假审批后需要将情况通知给申请人,该任务由计算机完成,不需要人的参与,也不会产生工作项。

 

应用

随着自动化水平的提高,越来越多的工作可以交由计算机或相应的机器设备完成。流程中的自动执行节点也会逐渐增加。

 

实现

实际上工作流产品对该模式的支持即是支持各种的企业集成方式,不管是通过Web服务还是消息,工作流需要驱动相应的设备机器或应用系统进行工作并传递给必须的数据。所以也可以看出,随着信息化程度的提高,目前工作流应用越来越趋向于企业集成领域。这也是为什么基于Web服务编排的BPEL会逐渐取代XPDL成为工作流流行标准的原因之一。

posted @ 2009-10-20 13:18 ronghao 阅读(1489) | 评论 (2)编辑 收藏
在上一章里,我们谈到了工作流的控制模式,控制模式强调的是对业务流程进行建模,业务流程的目标是实现一个商业目标或者管理目标,业务流程的执行往往由一系列的任务所构成,控制模式建模的实质在于合理调配这些任务,以期以最少的成本达到最大的收益。

本章将介绍工作流的资源模式,如果说控制模式更为宏观,强调的是业务流程里各个任务的合理调配的话,那么资源模式则深入细节,将要讨论单个具体任务的执行情况。提到任务的执行,那么谁能执行这些任务呢。答案很直接,是人。不管是在公司企业还是政府里,人都是最重要的资源,除去人之外,还有其他的非人力资源,例如机器、设备、计算机等。探讨这些资源如何执行业务流程中的具体任务,如何调配这些资源即构成了本章的内容,即资源模式。

本章介绍工作流的资源模式,共计43种。提到模式,很多人会想到四人帮,想到他们的设计模式,但是需要与编程里的设计模式区别的是:程序里的设计模式关注的是代码,通过应用设计模式做到代码的职责清晰、不重复、开发人员友好等等;而工作流里的模式关注的是业务价值,通过合理调配任务和资源为组织带来最大的业务价值,工作流模式是对实际业务的直接描述,与具体的工作流产品实现没有直接的关系(后面我们可以看到,很多模式当前的工作流产品很难实现),两者的出发点完全不同。

本章先会讨论与资源模式相关的一些基本概念,例如资源、工作项、组织机构建模等。接下来会对具体的43种资源模式进行讨论,讨论的模式按照描述、应用和实现展开,分别对应着模式的介绍、模式对实际业务的映射和工作流产品对该模式的实现支持。最后是小结。

一、基本概念

1、资源
既然是资源模式,那么什么是资源。在本章的前言里,我们已经提到人是业务流程执行里最为重要的资源,除去人之外,随着自动化水平的提高,还有其他的非人力资源,例如机器、设备、计算机等。资源指的是能够进行工作的实体,通俗一点,就是能够执行业务流程里任务的实体。对于需要盈利的公司而言,就是找到一种可以盈利的模式,然后找寻能够执行这些盈利工作的资源,通过资源的工作达到盈利的目的,通过合理调配这些资源达到利益最大化。

因为人是最为重要的资源,所以在后续对资源模式的讨论中,没有特殊说明,资源指的都是人力资源,大多数的模式也将以人来说明。

典型的,人是某个组织机构里的成员。组织机构对人员进行分组,执行相关的工作以达到共同的目标。例如,企业的目标是盈利,政府的目标是为人民提供更好的公共服务。组织机构对人员的分组具有多种形式,最常见的就是部门、角色和岗位(实际上与角色相比,岗位更多体现的是一种业务职能,而角色更多体现的是管理职能,与权限相关)。对于大的跨地域的组织而言,还有分支机构的划分,此外,还有临时组(典型的如以交付为核心的软件开发公司里的项目组)。在很多情况下,人可能具有多个角色、属于多个部门,这些,增加了管理的复杂性。

对工作流产品而言,要对资源模式进行支持,则必然涉及到对资源分组的支持,在大多情况下,资源分组即组织机构模型。只有支持目标客户的组织机构模型,才能在实施工作流产品时最大限度的契合客户业务。当然,如果产品是某个行业的标准,让客户模型向产品靠拢也是另外一种方式。

2、工作流产品里的组织机构建模
所有的工作流产品都有自己的组织机构模型,其是工作流产品里一个重要的模块。但是一套模型往往很难契合多种业务场景。在大多数的产品实现里,都会提供一套元模型,例如人(Person)和组(Group),然后建立多套与业务相关的模型向元模型适配,例如,角色、部门都是组的一种形式,它们只是拥有不同的业务语义而已。

在工作流产品实施时,很重要的一步就是进行组织机构建模,然后将建立完成的模型与工作流产品内置的模型进行适配,在适配的过程中,妥协是经常出现的。

3、工作项
一个业务流程由一系列相关的任务组成。在工作流产品里,使用图形化的节点代表这些任务,而实际的任务被映射为工作项(work item),任务的调用被映射为工作项的执行。一般情况下,一个任务对应着一个工作项,但是存在一个任务需要多人完成的情况,这个时候一个任务就会对应着多个工作项。工作项可以看作是工作流中最小的工作单元,其代表着一个单一资源对某一任务的执行。

既然在工作流系统里任务的执行被映射为工作项的执行,那么就一定存在着人与工作项这个计算机概念的交互,在工作流系统里,这一交互通过工作项管理器来进行管理。即我们通常所见的工作项列表(任务列表),我们通过这一列表拾取任务、处理任务以及管理任务的状态。

4、工作项的生命周期
工作项有其自己的生命周期。
 
图 5-1
如图5-1所示,当工作流系统执行某一任务节点时就会创建工作项,工作项可以是一个也可以是多个,正如上面已经提到的,工作项代表着一个单一资源对某一任务的执行即一个工作项只能由一个资源来执行,现在我们讨论的是一个工作项的生命周期。

工作项被系统创建完毕后即处于创建状态,接下来系统会选取资源来执行该工作项。有两种状态:一种是提供状态,一种是指派状态,这两者的区别在于一个是可选的一个是必须的。如果系统提供一个工作项给你执行,这意味着你符合执行该工作的条件,但你不必为该工作负责,即你可以选择执行该工作也可以选择拒绝,你只是该工作的合适候选者;而如果系统指派一个工作项给你执行,则意味着你必须为该工作负责,该工作必须由你来执行。因为一个工作项只能由一个资源来执行,所以如果是指派的话,那么只能指定一个资源;而提供,则可以提供给一个资源也可以提供给多个资源来候选。通常工作项管理器会提供两种列表来区分这两种状态,分别是待拾取列表和待办列表,一旦资源对待拾取列表里的工作项进行拾取,工作项即进入到资源的待办列表,状态成为指派状态。

工作项进入指派状态即意味着执行该工作的资源已确定,那么接下来就可以由资源来开始执行该工作,执行的过程中可以将工作暂时挂起中断处理,后续可以再恢复对该工作的执行。如果工作成功完成,则工作项成为完成状态;如果工作因为各种原因没有成功完成,则工作项置为失败状态。
posted @ 2009-10-18 09:45 ronghao 阅读(1586) | 评论 (1)编辑 收藏
似乎一到年末,就会忙起来。

05年的时候忙着和现在的老婆谈那从来没谈过而导致过分饥渴的恋爱;06年的时候新配置了机器,忙着通关使命召唤和生化危机;07年的时候和张祖良一起翻译AJAX企业级开发,第一次翻译,忙得像黄牛,慢得像蜗牛,在心里祈祷,翻译出来的东西不被骂就好;08年的时候和丁雪峰、总司令又一起翻译Spring攻略,第二次翻译,熟练了一些,但是每一个句子还是要花上很多时间,很多时候还得一个词一个词的确认,翻译对我来说是个苦力活,第一次翻译完我就告诉自己不要再翻译了,但是Spring攻略确实是一本好书,完全是书本身吸引了我,同样在心里祈祷,不要糟蹋了这本书。09年了,和辛鹏一起完成这本《Head First Process-深入浅出流程》,还是祈祷,千万不要写出垃圾,有时候,我常想,有必要这么辛苦吗?我是一个喜欢意淫的人,经常就把思绪抛到了多年之后,在未来里洋洋自得,于是回来时就有了动力。

我负责该书的第一部分,包括了工作流的控制模式、资源模式、数据控制模式与异常处理模式,包括了对三种流程规范的介绍、对开源工作流的介绍、对jBPM4的分析。辛鹏负责该书的后半部分,他对流程应用有着非常丰富的经验,目前他正在杭州实施一个BPM的大项目,其中包括了完整的IBM产品套件,包括了企业集成和ESB。很多人认为工作流只是OA,这其实是一个误区,工作流确实在OA里应用的非常多,但这仅仅只是一个方面。说实话,我对本书也非常的期待,非常期待辛鹏在书中分享他众多的实施经验,他是一个非常勤奋的人,这点让我钦佩不止,常常想,等我到了30多岁,还会不会有一颗勤奋的心。

家住燕郊,上班在东直门,每天在路上四个小时,挤那传说中的930,时常安慰自己:哥挤的不是930,哥挤的是寂寞。谢谢老婆,尽管还还着房贷存在着心理障碍,但是还是为我在东直门租了个房子,下周起就可以走路上班了,这样也会有了更多的时间来完成这本书。

还是那句话:战战兢兢。

十一过完,公司的新项目也开始了,时尚网站。技术栈包括了:OSGI、JCR、REST、渐进式增强。该项目有个巨大的亮点,不是徐昊是我们的技术leader,而是开发人员中一半是女生,这样每天pair的时候就生活在祖国的花园中了。

希望能写些有价值的东西,征得刘江的同意,将会在博客连载部分章节。第一部分连载的将是工作流的资源模式,内容包括前言、基本概念、创建模式、推模式、拉模式、迂回模式、自动开始模式、可见性模式、多资源模式以及最后的小结。限于篇幅,将会分节进行连载。考虑到复制麻烦,将会在开放流程社区连载所有内容,博客会连载概要并提供链接。


posted @ 2009-10-17 23:03 ronghao 阅读(2604) | 评论 (2)编辑 收藏

一、    代码主要结构
所谓流程设计器者,无怪乎读取xml文件,图形展现,操作图形元素,改变xml文件,回写,如此而已。

既然如此,设计器的流程结构就非常清晰:首先是xml框架解析xml文件为Model模型组件,然后Model模型组件被展现为Component视图组件;用户对Component视图组件进行操作,这些操作被同步的修改到Model模型组件;最后用户保存时,Model模型组件经过xml框架解析回xml文件,该文件被上传到服务器或本地覆盖原有的xml文件。

那么代码结构就很清晰了:xml框架、Model模型组件和Component视图组件。但是等等,Model与Component如何交互呢?这里就需要GEF框架嫁接起两者的联系。同时,一个流程设计器往往要同时编辑多个流程定义,相比具体的流程定义而言,设计器拥有一些全局的对象,这些全局对象包括系统菜单栏、工具条、整个设计器布局框架(ProcessDesigner)、设计器入口(ProcessEditor),还有就是负责保存全局属性和发布/订阅定制事件的TheModel对象。

二、    Component视图组件
很直接,Component视图组件指的是与用户打交道的、与流程定义相关的视图元素。注意这里的一个定语:与流程定义相关的,即不包括系统菜单、工具条这些东东。这些视图元素很简单,包括画图板、各种节点元素和连接线元素。

代码位于org.jbpmside.view.component和org.jbpmside.view.component.node下。主要类SurfaceComponent、NodeComponent和ConnectionComponent。看类名就很清晰这些类分别代表着什么组件:
•    SurfaceComponent代表画图板;
•    NodeComponent代表节点;
•    ConnectionComponent代表连接线;

org.jbpmside.view.component.node下的类就是NodeComponent类的子类,代表具体的单个节点类型了,包括开始节点、结束节点、Fork节点、Join节点等等。

Component视图组件使用了degrafa来渲染表现形式。

目前缺少一个属性弹出框组件,职责展现和修改节点/连接线属性。

三、    Model模型组件
Xml流程定义文件解析为本地Model模型组件,本地建模和jBPM4的PVM建模一致,代码位于org.jbpmside.model下,重要的类:
•    ProcessModel代表流程定义;
•    NodeModel代表节点定义;
•    ConnectionModel代表连接线定义;
剩下的就是具体节点类型的模型类,例如StartNode/EndNode/TaskNode等。

目前模型类还非常简单,因为前段时间主要关注Component视图组件部分,接下来很快会与jPDL规范完全同步,同时ProcessModel/NodeModel/ConnectionModel会进行重构,目标是与jBPM4模型完全一致。

 最新的模型位于org.jbpmside.model.common下,对jpdl4的支持位于org.jbpmside.model.jpdl4下,未来需要将Component与Model的关联迁移至common包下。

四、    GEF框架
GEF框架嫁接Model与Component。

1、    IGraphicalEditor与IEditPart
IGraphicalEditor与IEditPart是GEF框架里最重要的两个接口:
•    IGraphicalEditor代表整个图形编辑器,IGraphicalEditor里最重要的方法:

function get graphicViewer():GraphicViewer;


返回当前的图形视图。在当前的设计里,设计器支持多个TabPane,每个流程定义会拥有一个单独的图形视图(即一个TabPane),这里的图形视图即指当前处于激活(编辑)状态的画图板;很显然IGraphicalEditor是一个全局类。

•    IEditPart代表单个的图形编辑元素,很显然,这些元素是和Component组件一致的,IEditPart里最为重要的方法:
function get model():Object;

function set model(_model:Object):
void;

Component组件继承于IEditPart,这样就瞬间将Component组件与Model关联起来。IEditPart重要的实现类包括GraphicViewer与GraphicEditPart。
GraphicViewer被SurfaceComponent继承;
GraphicEditPart被NodeComponent和ConnectionComponent继承。
 
2、    Tool
Flex应用程序是基于事件驱动的,用户对界面的操作即反映到各种鼠标和键盘事件上。在原先的设计里,由Component组件自己来处理各种原生事件,当需要其他组件协作时,通过TheModel发出应用定制事件。在GEF的设计里,Component组件的原生事件处理被委派到Tool类进行处理。Component组件只管理自身的图形渲染和变化。
例如SurfaceComponent处理鼠标点击事件代码:

public function mouseClickHandler(event:MouseEvent):void
        {
            … …
            
this.tool.mouseClick(event, compX, compY);
        }

     
注意this.tool方法,这个方法同样是由GraphicViewer和GraphicEditPart分别  引入的。注意有些时候组件的Tool是需要切换的,例如鼠标点击面板,通常会导致被选中的节点或连接线选中状态消失,但是当工具条选中一个节点时,这个鼠标事件会导致向面板增加相应的节点。这时需要ToolManager来进行Tool却换的管理,针对SurfaceComponent/NodeComponent/ConnectionComponent分别有SurfaceToolsManager/NodeToolsManager/ConnectionToolsManager来管理不同的Tool切换策略。需要注意的是ToolManager和Tool都是无状态的,全局唯一,所有视图组件共用。

3、    Command
视图组件的变化会导致Model组件的变化。Tool处理视图原生事件、调用CommandService执行各个Command具体操作视图组件和Model对象实现视图组件和Model组件的变化。

CommandService与SurfaceComponent进行一对一绑定。CommandService持有CommandStack,实现单个Tab编辑界面内Command的redo和undo。

具体操作视图组件和Model对象必须通过Command。

五、    TheModel全局类的用途
      TheModel全局唯一,职责如下:
•    负责应用所有定制事件的订阅/分发;
•    负责持有工具条和系统菜单属性;
•    负责持有剪贴板,实现各个画板之间的节点拷贝/剪切。

六、    ProcessDesigner与ProcessEditor
ProcessDesigner负责整个应用的布局,目前由三部分组成,系统菜单、工具条和TabNavigator(TabBar管理器),TabBar管理器负责添加和删除Tab,由Tab加载画板,这样实现对多流程定义同时编辑的支持(即多Tab)。

ProcessEditor是应用的入口,它持有ProcessDesigner,实现了IGraphicalEditor接口。目前其对graphicViewer()方法的实现是返回当前激活状态Tab的画板。

同时,ProcessEditor负责统一监听工具条/键盘事件,并将这些事件处理委派给当前处于激活状态的Tab画板处理。

七、    Xml框架
位于org.jbpmside.xml下,使用E4X,使用binding对各种类型的节点进行解析,不集中在一个文件完成解析和转换,一个节点类型对应一个binding。使用代码如下:
   public function parse(xml:String):ProcessDefinition{
    var parser:Parser
=new Parser();
    
return parser.createParse().setString(xml).
          execute().getProcessDefinition() as ProcessDefinition;
}


测试代码位于test目录下,是目前唯一可以进行单元测试的部分。

八、    还需要完成的工作
1、    xml框架还需要大量的解析工作完成(以支持jpdl4)
2、    第一个版本为本地应用,需要增加对本地文件操作的支持
3、    模型迁移至org.jbpmside.model.common
4、    工具条使用flexlib重写,新的16位图标,节点属性弹出框
5、    Command目前只实现了对undo的支持,需要实现对redo的支持


posted @ 2009-09-20 21:17 ronghao 阅读(2215) | 评论 (4)编辑 收藏
     摘要: 使用Selenium测试showModalDialog模态对话框一直是一件困难的事情,本文提出一种hack解决的方法。  阅读全文
posted @ 2009-07-27 21:17 ronghao 阅读(3585) | 评论 (0)编辑 收藏
       现在流行抱大腿,不过对眼光的要求也高。要不就如高也,即使四眼,一样无用。对Java企业开发而言,Spring的腿则是一定要抱的。而所谓抱Spring的腿,无外乎三点:

一是通过Spring暴露出服务,将服务配置到Spring的IOC容器里;
二是在自己的运行环境里访问到Spring的IOC容器,能够轻松使用Spring容器里所配置的服务;
三是对于具有事务管理特性的项目来说,将事务管理与Spring的事务管理进行合并。

        下面分别讨论:

一、    通过Spring暴露服务
还记得在jBPM4的运行期环境里提到的JbpmConfiguration吗?它是整个jBPM4的入口,并且是整个应用独此一份的。通过它可以获取processEngine,并藉此获得工作流引擎所提供的各种服务:

ProcessEngine processEngine 
= new Configuration()
      .buildProcessEngine();


RepositoryService repositoryService 
= processEngine.getRepositoryService();
ExecutionService executionService 
= processEngine.getExecutionService();
TaskService taskService 
= processEngine.getTaskService();
HistoryService historyService 
= processEngine.getHistoryService();
ManagementService managementService 
= processEngine.getManagementService();

通过Spring暴露这些服务,配置如下:
<bean id="jbpmConfiguration" class="org.jbpm.pvm.internal.cfg.SpringConfiguration">
        
<constructor-arg value="be/inze/spring/demo/jbpm.cfg.xml" />
    
</bean>
   
    
<bean id="processEngine" factory-bean="jbpmConfiguration" factory-method="buildProcessEngine" />
    
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
    
<bean id="executionService" factory-bean="processEngine" factory-method="getExecutionService" />


细心的你会发现,配置时使用了JbpmConfiguration 的子类SpringConfiguration。SpringConfiguration相比JbpmConfiguration有哪些增强呢,下面再讲。总之,现在,就可以使用Spring来获取或注入这些Jbpm4所提供的服务了。

二、在environment里加入SpringContext
jBPM4的environment(运行期环境)提供Engine IOC(process-engine-context)和Transaction IOC(transaction-context)。要想在运行期方便地访问到Spring里所配置的服务,最直接的方法就是在environment里加入Spring IOC(applicationContext)的引用。
SpringConfiguration即是对JbpmConfiguration增强了对Spring IOC的一个引用。
 
SpringConfiguration是如何做到的呢?简单,实现Spring的ApplicationContextAware接口,自动持有applicationContext,然后openEnvironment时将其加入environment。

environment.setContext(new SpringContext(applicationContext));


SpringContext是对applicationContext的简单封装。

那么什么从Engine IOC移民到Spring IOC了呢?是的,最重要的就是Hibernate Session Factory。

在jbpm.cfg.xml的process-engine-context里干掉:

    <hibernate-configuration>
      
<cfg resource="jbpm.hibernate.cfg.xml" />    
    
</hibernate-configuration>

    
<hibernate-session-factory />

 
相关配置挪动至Spring配置文件。

三、    事务
哪里有数据库操作,哪里就有事务。对于嵌入式工作流而言,最重要的集成就是事务的集成。这里先分析jBPM4的事务实现,然后再介绍集成入Spring的事务实现。

1、    Command模式
jBPM4的逻辑实现采用了Command模式。
 
采用Command模式后,jBPM4对CommandService构造拦截器(Interceptor)链,配置在jbpm.cfg.xml的process-engine-context里:
<command-service>
      
<retry-interceptor />
      
<environment-interceptor />
      
<standard-transaction-interceptor />
    
</command-service>


2、    原有的事务实现
jBPM4原有的事务通过StandardTransactionInterceptor实现,在CommandService执行Command之前打开事务(实际委派Hibernate的事务管理),完成后提交/回滚。
 
jBPM4的事务是基于Command的。

3、    集成入Spring的事务实现
Spring的事务是基于服务调用的。

使jBPM4使用Spring提供的事务:
<command-service>
      
<retry-interceptor />
      
<environment-interceptor />
      
<spring-transaction-interceptor current="true" />
</command-service>


拦截器换用SpringTransactionInterceptor,SpringTransactionInterceptor从environment 提供的Spring IOC获取PlatformTransactionManager,使用事务模板回调Command,事务传播模式强制加入当前事务。

同时,对hibernate session的配置(jbpm.cfg.xml的transaction-context)强制从当前线程中获取:
<hibernate-session current="true"/>

并干掉原有的事务实现:
<transaction />

参考文档:
http://www.slideshare.net/guest8d4bce/spring-integration-with-jbpm4


posted @ 2009-06-22 16:38 ronghao 阅读(7361) | 评论 (7)编辑 收藏
        万物生长靠太阳,儿童的生长离不开土壤、空气和水,当然,也离不开绿坝娘的调教。应用程序也是如此,离不开数据库连接、事务、日志、消息等,这些,共同构成了应用程序的运行期环境。
        理想中的环境是什么样子的哩。好吧,一句话,召之即来,挥之即去,当需要某个服务时,ok,打个响指,该服务就准备好被调用了,调用完毕后也不用费心费力地擦屁股,不必老是提心吊胆有好事者追问:你擦了吗,确定擦了?真的确定擦了?直接丢弃给环境降解处理,自然又环保,还有个好名声叫专注领域逻辑。

一、    运行期环境就是一个餐馆
1、    提供必要的服务
作为一个餐馆,必须有厨师做饭我吃,必须有桌子和椅子。作为运行期环境同样如此,我要发消息,你得提供我发消息的Service,我要获取节点任务,你得扔给我TaskService。

2、    提供获取这些服务的统一方式
好吧,我不会亲自到厨房告诉厨师我想吃什么(因为我担心这样一来我会吃不下去),我也不会亲自到收银台给钱。这些服务有一个统一的获取方式:服务员。我想吃什么和结账,告诉服务员即可。关键是这一方式要统一,要足够简单。Spring最懒,把服务给你全部注入了,当然你也可以握住BeanFactory的纤纤细手,一个一个的get。

3、    提供特定于我线程不安全的服务
我点了一盘鱼香肉丝,隔壁也点了一盘鱼香肉丝,结果服务员让我们吃同一盘鱼香肉丝。我立刻跳起来:靠,你们的服务不是线程安全的吗?!Hibernate的Session正是属于这么一种情况,需要环境进行隔离,我的唯一职责就是吃饭!我的领域逻辑是如何优美的进餐!为此还要不断重构我吃饭的姿势哩。
好不容易吃完饭,付完款,正准备离场。服务员风度翩翩地走到我的身旁,我以为还有打折券供应,结果是:服务员小姐轻启朱唇:先生,麻烦您把吃剩的盘子清洗完毕。
崩溃!
像数据库连接的打开,关闭、事务的打开、提交等都属于运行期环境应该做的事情。

4、    其他的七七八八
杂事不少,例如统一的事件机制、权限拦截等等。

二、    jBPM4的运行期环境
好吧,先来看看如何建立jBPM4的运行期环境:
EnvironmentFactory environmentFactory = new DefaultEnvironmentFactory();
 
  
 
  Environment environment 
= environmentFactory.openEnvironment();
  
try {
 
     everything available in 
this block 
 
  } 
finally {
    environment.close();
  }


两个关键的类:EnvironmentFactory和Environment。

EnvironmentFactory是全局的,在整个应用程序中保持一个实例即可。

Environment则是每次方法调用则要new一个。

看看Environment的主要方法:
public abstract Object get(String name);
public abstract <T> T get(Class<T> type);


是的,environment为我们的代码提供所需要的服务类实例。

那么,如何获得environment?
继续看:
public static Environment getCurrent();

static,我喜欢也。方便、快捷,不管是在地上、车上还是房顶上,随处都可调用。

那么,为什么Environment每次调用要new呢?
好吧,当你需要获取数据库Session的时候,是不是每次都要new呢。Environment提供的服务里包括了非线程安全的数据库操作服务。

三、    jBPM4运行期环境的实现

1、JbpmConfiguration
JbpmConfiguration是jBPM4里最重要的类,它是整个应用程序的入口。它实现了EnvironmentFactory接口。

      JbpmConfiguration加载jBPM总的配置文件,还是大概扫一下这个配置文件:
      <jbpm-configuration xmlns="http://jbpm.org/xsd/cfg">

  
<process-engine-context>
 
    
<repository-service />
    
<repository-cache />
    
<execution-service />
    
<history-service />
    
<management-service />
    
<identity-service />
    
<task-service />

    
<hibernate-configuration>
      
<cfg resource="jbpm.hibernate.cfg.xml" />    
    
</hibernate-configuration>

    
<hibernate-session-factory />
 
  
</process-engine-context>

  
<transaction-context>
    
<repository-session />
    
<pvm-db-session />
    
<job-db-session />
    
<task-db-session />
    
<message-session />
    
<timer-session />
    
<history-session />
  
</transaction-context>

</jbpm-configuration>


配置文件被分为了两部分,分别是:process-engine-context和transaction-context。
对应于两个IOC容器(WireContext)的配置文件。

作为EnvironmentFactory,JbpmConfiguration持有成品process-engine-context对应的IOC容器(全局的)实例,持有半成品transaction-context的WireDefinition。当调用openEnvironment方法时,JbpmConfiguration会new Environment,然后将process-engine-context IOC填充入environment,同时初始化transaction-context IOC,并将其也填充入environment。这样通过environment就可以获得所有所需要的服务,包括全局的和非线程安全的服务实例。也就是environment透过IOC容器提供了查找各种服务的能力。


 

2、与线程绑定的environment
environment初始化之后,避免参数传递得一塌糊涂的方式就是将environment与线程绑定。看Environment的代码:
  static ThreadLocal<Environment> currentEnvironment = new ThreadLocal<Environment>();

  
static ThreadLocal<Stack<Environment>> currentEnvironmentStack = new ThreadLocal<Stack<Environment>>();


是的,在openEnvironment时,有这么一行代码:
Environment.pushEnvironment(environment);


这样environment就与线程绑定了,可以通过Environment.getCurrent()任意调用了。

哪里有压迫,哪里就有放抗。
在environment.close()方法里:

Environment.popEnvironment();


OK,结束。


posted @ 2009-06-17 18:15 ronghao 阅读(2802) | 评论 (5)编辑 收藏
一、目前的情况
目前我们要进行持续集成的对象是一个有着100人左右的开发团队,他们开发着一套很庞大的系统。整个开发团队划分为多个开发小组进行协同开发,每个开发小组负责2-3个模块的开发,实际这里的模块已经相当于一个中小型系统。各模块所有的类都通过eclipse整体编译在一起,直接放置在WEB-INF/classes下。本地是无法启动整个系统的,需要耗费大量的资源。

二、碰到的问题
在了解具体情况之前,我们最初的想法是为整个产品做一个持续集成,但是很快就发现这一想法存在很多的问题:
1、整个产品每次构建的时间会很长,这个时间包括代码的编译、启动Weblogic,完成自动化测试,同时对服务器的硬件要求非常高
2、因为构建时间长,所以如果本地构建通过后再提交会严重影响开发效率,况且本地的硬件条件很可能启动不了
3、如果本地不构建提交,则由于开发人数众多,构建会非常不稳定,会经常处于失败状态。而构建失败会导致后续提交的阻塞。
4、作为一个100人的开发团队,代码提交会引发频繁的服务器构建,服务器无法负担。

同时作为客户,他们有这样一种想法:敏捷开发是好的,但是不适合于大的项目和大的团队。

最重要的问题集中在两个方面
1、启动整个产品过于重量级(不包括自动化测试的情况下已经如此)
2、如何不影响开发人员的频繁提交

三、我们的想法
我们现在的想法是做多阶段的持续集成(multi-stage CI)

可以参考这里http://www.ddj.com/development-tools/212201506

具体而言:
1、各个开发小组内做小组内的持续集成
2、开发小组间集成做整个产品的持续集成


大概:
1、每个开发小组一个分支,整个产品一条主线
2、在小组分支上搭建持续集成环境,小组内的开发向该分支上提交,各个小组可以并发开发,互不影响
3、小组完成一个完整的功能后,从主线更新合并代码,本地构建通过,提交,触发整个产品的持续集成

为使小组内持续集成构建加快,小组内尽量划分清楚对其他模块的依赖,不必要的模块(这里的模块包括基础模块,例如工作流模块)不必加载。
同时推荐轻量级的web服务器例如Tomcat来完成小组内的测试环境。需要启动weblogic的情况或功能依赖过多的情况下,建议在产品持续集成时进行测试。
同时保留原有的启动单独测试服务器进行手工测试的习惯。
posted @ 2009-05-26 23:13 ronghao 阅读(1518) | 评论 (0)编辑 收藏
       和Jbpm3一样,Jbpm4实现了自己的IOC容器。以现在的眼光看来,应用程序里一个IOC容器几乎是居家必备的,否则,又要平白多出一坨一坨的工厂类和单态类来。

一、    Jbpm4 IOC容器介绍
IOC容器的目的是管理组件和实现组件之间的解耦。和Spring里的BeanFactory对应,Jbpm4里的接口是Context,具体实现则是WireContext。Context实际在Jbpm4里有更多的含义,它与Environment一起,共同构成了代码运行的运行期环境。在这个环境里可以获取系统的组件,更为重要的是提供了数据库连接(session)和事务(这个稍后会讲)。

先来看看Context接口的核心方法:
      Object get(String key);
  
<T> T get(Class<T> type);


很明显,提供两种从容器里获取组件的方法,一种是通过name,一种是通过type。

对于IOC容器来说,一般情况下都会提供一种加载的方式,比如从xml文件进行加载、从资源文件进行加载。Jbpm4透过WireParser具备从xml加载的能力。

此外,WireContext通过一个Map缓存初始化后的组件。

二、    Jbpm4 IOC容器实现
容器的实现有五个关键类和接口,分别是:WireParser、Binding、Descriptor、WireDefinition和WireContext。
 

WireParser读取xml文件,同时WireParser会加载一系列的Binding(默认从jbpm.wire.bindins.xml文件读取加载)。

Binding负责根据xml里元素的tag将xml元素转换为对应的Descriptor。

Descriptor负责初始化对象。它们被添加到WireDefinition。

WireDefinition被WireParser返回给WireContext。WireContext创建对象时会访问WireDefinition里的Descriptor,同时将初始化对象的任务委托给Descriptor自身。

需要注意的是:Jbpm4在初始化对象时有着四种策略,分别是:延迟创建和初始化、延迟创建和立刻初始化、立刻创建和延迟初始化、立刻创建和立刻初始化。

立刻创建:在WireContext创建完毕后对象就已经创建。
延迟创建:调用WireContext的get方法获取该对象时才创建该对象。
初始化:一般完成对象属性的注入等操作。

三、    Jbpm4 IOC容器在Jbpm4里的应用
IOC容器在Jbpm4里最重要的作用就是加载Jbpm的总的配置文件(默认是jbpm.cfg.xml),这也是整个Jbpm应用的起点。大概扫一下这个配置文件:

<?xml version="1.0" encoding="UTF-8"?>

<jbpm-configuration xmlns="http://jbpm.org/xsd/cfg">

  
<process-engine-context>
 
    
<repository-service />
    
<repository-cache />
    
<execution-service />
    
<history-service />
    
<management-service />
    
<identity-service />
    
<task-service />

    
<hibernate-configuration>
      
<cfg resource="jbpm.hibernate.cfg.xml" />    
    
</hibernate-configuration>

    
<hibernate-session-factory />
 
  
</process-engine-context>

  
<transaction-context>
    
<repository-session />
    
<pvm-db-session />
    
<job-db-session />
    
<task-db-session />
    
<message-session />
    
<timer-session />
    
<history-session />
  
</transaction-context>

</jbpm-configuration>


可以看到配置文件被分为了两部分,分别是:process-engine-context和transaction-context。在实际应用中,它们分别对应着两个不同的WireContext:ProcessEngineContext和TransactionConext。ProcessEngineContext覆盖了jbpm4里最重要的服务类,这些类是全局唯一的,当然,ProcessEngineContext也是独此一份。本是同根生,命运各不同。TransactionConext则是在每次openEnvironment时重新创建,因为其包含了数据库连接和事务。

贯穿于整个Jbpm4中,这两个Context被压到Environment里(Environment和线程绑定),在任何需要的地方都能提供一条龙的服务。于是,在很多领域类里,利用这些服务实现充血模型就是很顺理成章的一件事了。

总结: ProcessEngineContext给引擎领域模型提供全局的组件查找;TransactionConext提供数据库相关服务。


posted @ 2009-05-07 18:43 ronghao 阅读(3687) | 评论 (2)编辑 收藏
公司团体票,参加了QCon北京大会第二天的内容。感觉大多数技术网站都有水化和后继乏力的感觉,原因很简单,简单地依靠用户产生内容是有问题的,专业作者和专职编辑团体才是高质量内容的保障。在这方面,个人觉得INFOQ非常不错。

说说自己的一些大会感想。

关于话题

第二天的话题是关于网站架构,虽说一直做企业开发,但对网站一直有很高的兴趣。网站的访问量、用户量是企业应用所无法比拟的。早上赶到已经是九点半,Rod的演讲已经开始,非常多的听众,走道上全是人。第一次见到真人,但session确实没给人留下太多的影响,主要还是对springsource当前产品的介绍,其中也提到了ruby企业应用的局限性,明显是在推grovy。

第二个session是来自eBay的教训,提到了好几个原则:水平扩展、垂直扩展、异步、记录所有错误日志、实时性与可用性的取舍等等。非常不错,都是实战的总结,但是觉得现场的同声翻译有些问题,将可用性翻译为了可及性。来的晚了,坐最后一排,PPT的字有些多看不清楚,希望INFOQ能尽快提供下载。听力不好,另外有些概念没太理解,旁边的Tin给我讲解,其实听过Tin关于网站结构的一个session,非常好。同时,我在想,关于大会session,我不知道INFOQ是如何确定的,能否和投稿一样运作呢。

上午最后一个是Agile+CMMi,口音听起来比较吃力,刚好李默同学招呼吃饭,为下午的session占座,于是一帮人先去吃饭。

下午的话题可以说是非常精彩。先是支付宝程立带来的SOA治理。我对SOA这个话题一直是不感冒的,我认为仅仅把SOA认为是一种技术架构或实现是很BT的。程立的话题亦由它们产品采用SOA架构展开,这让我很瞌睡,我认为这个就是程序良好的功能划分、交互的约定、统一的查找/注册。不过很快就有了惊喜,话题接着讲到了公司架构、组织架构、业务架构、应用架构,以及如何协调这些架构已达到企业的运营目标。这和我的理解比较一致:SOA治理其实并不是采购所谓SOA产品来达到的,它之于公司运营过程类似于敏捷之于软件开发过程,是一套方法或者说是一套实践,而BPM管理企业流程则类似于Mingle管理开发流程。话题非常好,也很有作者自己的理解,可惜的是到后面由于时间原因都没有展开。另外,我觉得前面和后面的内容有些冲突让人犯晕,如果分开成两个话题就更好了。程立拿上帝创造世界的7天做比较,后来我提问时也提到:你们支付宝真累啊,一周要工作6天,我们都是双休日:)

豆瓣和优酷的话题我认为是全天最精彩的话题了。豆瓣按发展历程依次介绍了豆瓣架构的整个变化过程,优酷则着重强调了架构的简单性原则,邱丹在演讲时不停地强调这一原则,他们的架构是典型的LAMP,感觉是实用性至上。豆瓣则更技术性一些,开发了自己的DoubanDB和DoubanFS,有意思的是它们给每台服务器都起有一个指环王里的人物名字,这其实也是公司文化的体现。

有道的话题是关于日志分析,介绍的比较简单,主要是一些对系统概念的介绍,没有展开。后面提到了他们分析的一些有意思的结果,但是也是很快带过了,感觉可以再多一些,我们有时候挺需要八卦。

个人觉得对一个好的技术会议来说,话题应该是最最重要的,今天的话题就让人觉得很值。下一次的规模是否可以更大一些呢,涉及更多的话题,各种同一领域的实现技术都可以在一起PK,这样更加有意思。

其他

广告
我认为这个其实还有很多地方可以发掘,最明显的就是中午吃完饭回来,大厅在放维多利亚的秘密,这是一个多么好的广告时段啊,却维多利亚了。演讲的空隙都可以播放广告。

同声翻译的不太好。可能不是技术人员出身吧。

食品
主会场只有一个饮水机,拥堵。小面包不错,一连吃了四个。

晚上的沙龙本来是非常好的交流机会,由于住得太远,只有眼馋的份了。
posted @ 2009-04-11 22:31 ronghao 阅读(1744) | 评论 (1)编辑 收藏
     摘要: 在周末的openparty上,分享了关于JbpmSide介绍的session。主要介绍了这个项目的目标、功能规划、开发计划以及当前的进度。下面是可供下载的ppt。

JbpmSide仍然定位于一个完整的工作流解决方案。  阅读全文
posted @ 2009-03-30 10:35 ronghao 阅读(2443) | 评论 (1)编辑 收藏
汇报一下设计器当前进度以及下一阶段主要的开发目标。
当前进度主要集中在图像处理方面:
    1、采用Riawave,完成了整个设计器图形处理的架构
    2、工作流节点的图形建模
    3、节点的拖拽以及连线
    4、节点、连线的剪贴、复制、删除操作,支持快捷键
    5、画板的缩放和Grid显示与否
采用的图形类库是Degrafa。
下一阶段(2-3周)会实现以下的功能:
    1、目前的连线是通过点击两个节点出现的,将支持单独的连线增加(两种形式:直线、曲线)
    2、增加不同节点对连线的约束,即开始节点只能出、任务节点单进单出等。
    3、采用flexlib实现新的设计器菜单(Tabpane形式),同时将图形模型工具条移到左侧垂直放置
    4、do/undo功能,将所有对画板的操作重构为Command模式
余下的功能包括:导出图片、打印等。
近阶段的主要任务是实现设计器的图形功能,不考虑XML的转换和建模。其中包括根据大家的反馈改变部分的现实细节。源代码将尽快放出。请关注JbpmSide。

posted @ 2009-03-26 22:21 ronghao 阅读(3876) | 评论 (4)编辑 收藏


目标: jBPM-side ProcessDesigner是一个独立的设计器,基于 Flex技术。其目的在于既使得程序开发人员能够基于其进行业务流程的建模,同时业务人员也能够基于其进行简单的建模和修改,例如修改节点的顺序、参与者等操作。此外, jBPM-side ProcessDesigner具有很高的可配置性,在流程定义 scheme变化的情况下,能够很快的做出适配。即其与具体的流程定义语言无关,可以对 jPDL XPDL BPEL都进行建模。 jBPM-side ProcessDesigner的数据核心是 xml


功能规划:

一、 图形建模

支持流程模型与图形元素的一一对应,支持通过图形元素来进行流程的建模。流程模型与图形元素之间通过 xml进行互相转换。图形建模方面,支持图形元素的拖拽、定位、复制、粘贴,支持快捷键操作,例如 do/undo delete


二、 流程参与者的适配导入

支持在进行流程建模时,适配导入流程参与者。在进行人工参与节点定义时,选择参与者。进行人员 /部门 /角色的本地建模,提供最简单的必需属性。


三、 图形展现与 xml编辑的互相切换

支持流程图形与 xml编辑的切换,支持 xml定义的本地导入和导出。


四、 流程的分包与版本管理

支持流程定义的分包和版本管理,需要服务器端的支持。


五、 与服务器端的远程调用

在支持服务器端的远程调用之前, jBPM-side ProcessDesigner仅仅是本地 xml的导入导出建模。采用 restful-ws,基于 xml在服务器与设计器之间传递数据。


六、 与业务适配的 DSL


七、 流程的权限管理

支持对流程定义的分类权限管理,不同业务部门对属于自己的流程定义有各自的管理权限。此功能独立与 jBPM-side ProcessDesigner,但是需要通过契约使得 jBPM-side ProcessDesigner对流程定义的展现进行过滤。

posted @ 2009-03-08 21:59 ronghao 阅读(2767) | 评论 (0)编辑 收藏

      Jbpmside要使用Flex开发流程设计器以及管理器,刚好公司的一个内部项目也应用到Flex,大概花了一个礼拜来熟悉和了解Flex。其中发现Flex开发和AJAX开发的很多相通之处,这里把自己对Flex的一些学习心得总结一下。分为三部分,分别是对Riawave框架的介绍、参照Riawave,对自己先前采用的AJAX开发框架的介绍、以及对AJAX前景的一些自己看法。

一、Flex框架Riawave的定制应用

Flex有很多开发框架,最有名的是Cairngorm。但是Cairngorm太复杂了,职责的细粒度分离带来的是过多的类和代码,不太适合中小项目。这里我们采用了Riawave,准确的说Riawave并不是一个框架,它只是一个通讯录管理的简单代码示例,它包含了很多的最佳实践。这里想说一点,就是很多时候采用某种技术起决定因素的往往不是技术本身是否先进,而是看该技术的使用者是否众多。


 

Riawave将应用程序拆分成三部分,分别是ViewsModelLocatorDAO

 

Views指的是应用里的页面组件,例如表格、表单、菜单等。它的职责:1、组成整个应用的界面;2、对组件内部的原生事件进行处理,这些事件指发生在该组件里的鼠标事件和键盘事件;3、当需要其他组件进行协作时,发布定制事件;4、对其所关联的数据进行操作;5、监听其他组件发布的与之相关的定制事件,并做出展现形式上的改变。

 

ModelLocator是整个应用的核心。它的职责:1、集中存储应用的所有数据;2、给页面组件提供统一的定制事件监听器接口,即所有的页面组件要对其他组件的定制事件监听则必须通过ModelLocator来进行订阅;3、唯一的定制事件发布接口,即任何页面组件要发布自己的定制事件都必须通过ModelLocator发布;4、集中维护所有定制事件的类型。ModelLocator是单态的,通过ModelLocator,应用程序所有的定制事件都被统一管理。

 

DAO负责操作应用的数据。它的职责:1、改变应用的数据,提供应用数据修改的统一接口,即用户操作页面组件改变数据时,实际上是由页面组件转发给DAO处理;2、远程访问服务端,Flex通常作为应用程序的前端展现出现,和AJAX一样,数据的操作要和服务器端进行交互;3、服务器端返回正确的操作结果后,改变ModelLocator里的数据,发布定制事件,触发相应页面组件改变。

 

可以看到,整个Flex应用完全是由事件驱动的。其他需要了解的部分:1FlexData Binding机制,这个机制实际也是由事件发布/订阅机制实现的,它可以省去自己编写重复的定制事件发布/订阅代码,实现组件展现数据与ModelLocator的数据同步;2VO,本地的数据建模、用途:发布定制事件时,通过VO携带组件数据,VO被包含在事件里;传递数据到DAO

 

二、自己采用的自制AJAX框架介绍

Flex一样,AJAX也是事件驱动的。在自己以前的AJAX开发中,也自制过与Riawave类似的开发框架,应用的效果也不错。在这个框架中,采用了Mootools

 

使用过一些js库,包括prototypeMootoolsJqueryExt。说说自己的看法:prototype更像是底层库,封装了DOM查找和操作、XMLHttpRequest异步访问以及简单的js类继承体系,方便小巧;Mootools的特点是非常面向对象,另外对事件进行了统一的管理,这两个特点决定了Mootools非常适合用以编写自己的AJAX组件;Jquery在项目中使用过一部分,丰富的插件让人印象非常深刻;Ext则是一个完整的解决方案,代码非常有参考价值,提供了众多的组件,但是只是适合企业应用,另外,效果有些让人审美疲劳。


 

应用程序分为三部分,分别是ViewModelLocatorDataHandler

 

View同样是应用里的页面组件,例如表格、表单、菜单。它的职责:1、组成整个应用的界面;2、对组件内部的原生事件进行处理,这些事件指发生在该组件里的鼠标事件和键盘事件;3、当需要其他组件进行协作时,发布定制事件;4、调用DataHandler对其所关联的数据进行操作;5、如果存在子组件,监听子组件发布的定制事件,调用其他子组件协作,或者继续向上发布定制事件。

 

ModelLocator负责维护整个应用的数据。ModelLocator是单态的。

 

DataHandler负责操作应用的数据。它的职责:1、改变应用的数据,提供应用数据修改的统一接口2、远程访问服务端;3、服务器端返回正确的操作结果后,改变ModelLocator里的数据,回调调用View里的方法,View会改变DOM元素、发布定制事件,触发其他页面组件重新渲染;4、应用程序的入口,由一个服务器访问开始,使用返回数据构造View Container

 

Flex相比,事件的订阅/发布机制发生了变化,改为由View组件向父View组件依次冒泡传递,直到找到可完全处理该事件的组件为止。


AJAX开发里,由于没有统一的View组件,所以组件之间的关系通常是一种容器-子组件的关系,是一种层级关系。第二层的子组件可能充当第三层组件的容器。

 

容器的职责:1.保存对所有子组件的引用;2.订阅子组件的定制事件;3.调用其所管理的子组件协作;4.渲染时调用子组件依次渲染;5.销毁时,依次调用子组件的销毁方法;6.当对子组件发布的定制事件无法完全处理时,继续向自己的上一层的容器发布事件。

三、AJAX的看法

很多人将AJAXFlex相比,认为AJAX只是一种过渡的技术。我不这么认为,我认为Flex适合企业应用,而网站还是会广泛应用AJAX。另外,个人认为AJAX需要进一步提高的地方:1、统一标准的组件库,现在的组件库没有标准,嵌入到网页里应用没有问题,但是当需要这些组件进行协作时会比较困难,需要大量的编码;2、更快的JS解析引擎,这个没什么好说的;3、更强大的运行环境。

posted @ 2009-03-01 21:56 ronghao 阅读(1788) | 评论 (0)编辑 收藏

在拥挤的公交车上读完《工作流管理(模型、方法和系统)》,自从搬完家,上班的路途突然变得遥远。


这本书确实是按照它的副标题组织的,分别介绍工作流的建模模型、应用工作流开发的方法以及部分商业的工作流产品。
对petri网的介绍是这本书的重点,如果想对petri网有个大概的了解而又不愿意接触深奥的数学,那么可以一读。本书随后分析了如何对流程模型进行分 析,包括对建模正确性与否的定量分析以及对资源运行效率的定性分析。至于介绍的工作流产品,因为年代久远,可读性不高。应用工作流开发的方法就更是理论 了,不过作为一本2000年的书,里面提到的一些原则还是很有敏捷的意思,例如和客户在一起、迭代开发、交流的重要性等等。


读完这本书,加上先前的范玉顺的书,突然就明白为什么BPEL会如此之流行,原因在于它们都非常强调BPR的概念,即业务流程重组。也就是从一开始,工作 流系统就是瞄准BPR这个目标来的,想利用工作流系统将整个企业的业务流程都管理起来。目标如此远大,整合自然是不能避免,整合包括了对人员的整合,也包 括了对IT系统的集成。如此以来,恍然大悟:BPEL这种强调服务集成的执行语言无怪乎会大红大紫了。至于说国内最普遍的工作流应用:将工作流引擎嵌入应 用系统中,分离流程逻辑与业务逻辑,则自然登不了大雅之堂了。一句话说,就是国内应用工作流的层次太低。或者反过来也可以理解:现在的所谓BPM软件都眼 高手低,不太适合国内的应用。


可是问题依旧存在,即BPEL根本上说是一种执行语言,要业务人员理解简直是强人所难,所以BPMN应运而生。好吧,BPMN有了,自然BPMN到 BPEL的映射就出现了,可惜这终究是一厢情愿,一种是业务建模语言,一种是计算机执行语言,中间的代沟比70、80还宽。就像科比,篮筐在他眼里比大海 还广阔。


此外,BPEL的应用还存在一个天然的障碍,即应用集成从来都不是一件轻松的事情。将接口用web service包装一下就SOA了?就面向服务了?这鬼话你也信,那可真是你服务,你全家才服务呢。应用集成不轻松,所谓的企业敏捷性:能够根据外部环境 的变化迅速调整服务编排流程那自然是镜中月,水中花了。君不见,无数程序员们在开口大骂:靠,流程又要调整,早吃屎了?!


所以结论有三:
1、国内的嵌入式工作流应用还是什么适用就用什么吧,和XPDL\BPEL都无关;
2、一心要SOA、要BPEL。那别指望它能减少工作量,也别指望流程能够迅速修改;
3、要对企业流程进行敏捷管理,那就考虑文档化,别考虑执行。

posted @ 2009-02-19 14:20 ronghao 阅读(1769) | 评论 (7)编辑 收藏

                                                   

       大概花了三天的时间读完这本书,书本身也不厚,读起来很快。这本书出版于2001年,所以对它也没有抱有很大的期望,但是还不错,特别是前三章,很有些惊喜。后面关于工作流仿真的描述也很到位。但是关于技术实现,则大都略过了。

        总结一下里面个人觉得不错的部分。

        第一章很不错,强调为什么需要工作流管理系统。

        企业经营环境的变化:过去企业市场竞争主要围绕着如何提高生产率进行,现在则是围绕新产品的竞争而展开。新产品的价格总是高于其价值,通过竞争,价格才逐渐接近价值,产品失去独占期,同时也意味着产品生命周期的结束。与产品生命周期缩短所对应的,是客户定制产品数量的增加。

        在这种情况下,传统串行的产品研制会延长产品的上市时间,同时串行过程也是在企业以功能为核心划分组织机构下的必然产物。

        敏捷制造提出的背景:用户需求多样化、个性化,所有企业都将处于一种连续改变、不可预见的市场环境中,此时问题的核心在于是否抓住机遇、快速响应市场、开发新产品。敏捷制造的基本思想是,企业能够对持续变化、不可预测的市场需求做出快速反应,强调面向市场的敏捷性。实现敏捷制造的关键是对企业进行敏捷化改造和重组。其中企业组织结构发生重大的变化,传统的企业组织结构是功能部门制,即按照不同的功能和职能设立不同的部门,上下级之间形成一个树型的结构。这种结构的缺点在于:每个单元都由上一级的功能单元进行管理,出现问题时,每一级都会把责任推到上一级,这样会造成部门职责不清。柔性底,一个生产流程往往跨越多个部门,部门之间的协调成本很高,扯皮。需要建立起面向流程的组织机构,按照企业要实现的主要业务流程来配置组织机构,以项目来组织人员,减少内部不必要的沟通协调成本,提高对市场的响应速度。

        由此,需要工作流系统来对企业的流程进行分析和梳理。然后围绕这些流程来进行企业的业务重组和改造。

        第二章的亮点在于如何实施工作流系统。

        工作流的实施不同于普通的业务处理系统,它首先需要在战略层次上对企业的业务目标进行分析,确定企业的战略目标和组织要求,然后再进入到具体的实施阶段,分为三个阶段:模型建立阶段、模型实例化阶段和模型执行阶段。实施工作流的目的在于提高企业的柔性,能够根据市场的变化不断改进其业务流程。其中作者强调了工作流的两个重要职责:集成和仿真。工作流系统本身是一个完成流程建模和流程管理的软件系统,但是为了在企业的实际业务中得到有效的应用,它必须和企业已有的或购买的其他业务系统实现集成,通过集成来提高整个企业 的应用水平和应用效率。

        第三章分析工作流系统的组成以及WFMC定义的五个接口。很清晰。

        第四章到第八章描述具体商业产品的大概技术实现、XPDL规范和分布式的工作流,由于现在已经是B/S软件的天下,所以里面的分布式在这里显得理所当然。这部分可以跳过。

        第九章讲述作者实现的一个工作流系统CIMFlow。亮点在于分布式工作流机的设计方案。

        核心思想是:多个工作流机分配给多个部门,与这个部门相关的流程或流程节点就由这个部门专属的工作流机执行,部门可以各自独立修改这些流程或流程节点。另外为了集中管理,再设置一个主控工作流机,集中管理这些部门工作流机。这样可以提高流程的柔性。很赞的思想,但是实现无疑复杂了。

        第十章讲如何在企业流程重组中应用工作流。偶觉得,这本书一旦上升到企业运营的层次讲解工作流,马上就很赞了,O(_)O~。其中关于流程仿真部分很是好看,颠覆了自己对流程仿真的观点。以前认为是流程仿真是确保流程建模的逻辑正确,属于软件测试的范畴。这里的仿真却是为企业决策提供数据。需要注意的是对资源的定义。资源包括了人、业务系统、运营成本等等,很广义的概念。

        最后一章再次强调工作流集成能力的重要意义。不禁让我想起了BPEL

        合上书,我想,这是在讲工作流吗,( o )?,咋和我印象中的工作流不一样哩。我想,作者更强调的应该是一种高端的业务流程管理,它既不是现有的工作流、也不是BPM软件,然而又不是BPG,因为它管理的流程是可以马上执行的。只能这么想,作为7年前作者对工作流的理解,期望太多。

        如果有电子版,值得一读,如果买纸版,就没有必要了。

posted @ 2009-02-09 18:07 ronghao 阅读(2382) | 评论 (3)编辑 收藏
在温暖的办公室里写下这些字的时候,外边的天气很好,目光从明亮的窗户扔出去刚好能够触到西直门,所以这应该算是北京的好天气。回想起去年的这个时候,也是坐在办公室里,在上地,不远处的信息环岛,运通105在缓缓挪动。我很喜欢运通105,尽管有很多车可以选择,但是运通的司机总是很生猛,他能够骂骂咧咧地迅速变线超车,也能够抢在绿灯的最后一秒秒穿过路口,上他的车你需要确实坐稳扶好。下雨的时候会去坐车,平时则是骑车,那辆自行车几乎每个月都要修理一次,最近一次是刹车时用力过大结果闸应声而断了。11月换了份工作,坐城铁上班,自行车开始生锈,每天上班时经过车棚,看见布满灰尘的自行车,突然就有一种极不真实的感觉,我认识它吗,为什么它会显得如此之陌生,生活,似乎在一瞬间就完成了转换。

我是一个怀旧的人,经常会沉浸在过去的某种情绪里不能自拔。今年又似乎过得太快,我还没来得及回忆,已悄然流逝。过完年就考虑着要不要换一份工作,公司的效益不好,产品的BUG太多,似乎也看不见前景。然而三月份岳母的生病打断了这一想法,请假回家了一个礼拜,先是手术,然后化验,结果出来,是癌症,然后就是化疗。每月发完工资,我和老婆都会去邮局,给她家里汇钱,然后打电话安慰她的母亲,在电话那头,岳母总是带有愧疚,觉得她拖累了我们。我喜欢她的母亲,和我妈妈一样,都是很朴实的农村人。过年去她家,还没起床,岳母已经把早饭端到床头,糖水煮鸡蛋,一个压着一个,八个,尽管尽了最大的努力,但终究还是没有吃完。

四月份公司策略发生变化,停止业务平台的开发,以维护为主,同时重新开发工作流,我一直认为单独的工作流产品是有前途的,平台则不然,市场上这类产品已经非常的多,并且都没有什么亮点,如果说解决某一业务类型的问题尚还合适,那么夸大到通用的地步就只能是市场忽悠罢了。至于技术性的平台,我想,在国内,只能是叫好不叫座。再说说工作流,作为很多系统的基础设施,我觉得是越来越普及,同时,也会越来越廉价。

那是快乐的一段时光,功能调研、TDD、制定开发计划,每天都很充实。但是第一个版本迟迟不能发布,终于等到八月,开发被迫终止。原因是人员的流失非常严重,到最后只剩我一人在支撑。作为小公司,如何留住开发人员,这是个很大的问题。其实,我想,有时候不仅仅只是薪水的问题,还有很多因素,例如代码的封闭、人员之间的交流等等。给我印象很深,一次讨论实现时,我坚持了hibernate xml映射的形式,同事想引入注解,我反对,理由是会给团队带来新的技术学习成本,而我们的重点应该是快速实现引擎本身。后来,同事离职,老板找我谈话时提到了这件事情。这让我汗了很长时间。还有很多沟通的方式需要学习。

开始招人,始终找不到合适的人。有人给我的博客留言想面试,是一个四川的小伙子,汶川地震当了一个月志愿者回来没有多久,身上似乎还有沙子、混凝土的味道。一下子就喜欢上他,很勤奋、积极,虽然工作经验不多、也只是大专,但是给人的感觉很向上。有时,真的,觉得工作经验并不是很重要(行业经验除外),但是一定要有精神。离开公司时,我想,这也算是我给公司做出的贡献。

汶川地震时,坐在办公室里,3楼,没有任何感觉,就看到楼下突然聚集了越来越多的人们。QQ同学群发来消息:地震了!哪儿地震了?通州?四川?不,是心里!接下来几天里,看着电视里的不间断报道,眼泪就下来了。我是坚持要对政府批评的那部分人,我认为虽然有进步,但是还不够,一直到今天,政府失职的地方实在是太多。其实,政府一直缺少的就不是赞美,而是批评。我厌恶专家,他们在08年集体华丽地转身,变成砖家。一直在关注冉云飞的博客,每周,他都会有四川掮客周刊,然而,最近,他也被和谐了。

8月份在北京受孕,工作流开发停止,刚好看看书,也从公司的角度思考一下发展。小公司,生存是第一位的,工作流开发需要投入,暂时没有产出,中断可以理解。但是,作为一个技术性公司,如果没有技术那就失去了竞争力,长远的发展是个问题。真是矛盾。中小公司,99%的成败取决于老板。大公司靠财务,小公司靠销售。我觉得公司的销售和市场有些问题:好吧,就算是产品功能不强大,那售前那么少,怎么解释。是客户在没了解你的产品前,就把你的产品给cut了?或者,他们根本就不知道有这么一个产品?你是仅仅提供一个产品,还是提供一整套的解决方案?解决方案仅仅基于自己的产品?有那么多的工作流应用经验,这个能不能以服务的方式提供收益?盈利模式是什么?技术、编码,这些此时都不重要了。

去年的这个时候为自己写下了08年的展望。第一是结婚,10月份的时候实现了。第二个是开发一个基于js的工作流设计器,没有完成,也不打算去做了。第三个是工作上是否有新的挑战,是的,11月终于换了工作,去了自己一直想去的公司,但是要是原公司能够有足够的成本支持工作流的开发,也许现在还在那里,毕竟是自己负责的第一个产品,有很多的想法在里面,并且已经完成了第一个版本目标的80%,有这么个说法,很多产品倒在最后一里路上,我的体会很深。第四是多看看非技术书、善待家人,这点做得还不够,年底和朋友一起翻译了《Spring Recipes》,每天晚上都翻到12点,经常忘记给家里打电话。有次父亲给我打过来,说,吃饭了没。我看看表,11点了,说,都11点了怎么会还没吃饭?父亲用很惊讶的语气说,哦,我还以为你忙得连饭都没空吃呢,原来还是不忙啊。父亲是在怪我没给他打电话呢。

有过很多矛盾,年底前最终还是决定开发一个工作流的开源系统,想法是前公司的老大提出的,基于jbpm,实际我并不是很喜欢jbpm的一些实现,我想自己实现引擎,但是考虑到用户,这无疑是最好的选择。明年的愿望就是:把这个开源的工作流系统做下去,发布出来。

再就是,要锻炼身体,又胖了。


posted @ 2008-12-29 17:03 ronghao 阅读(487) | 评论 (0)编辑 收藏

当面对一个完整的工作流系统时,你可能会被它众多的功能所困惑:流程流转模式、时间服务、组织适配、表单权限等等。但是如果我们转换一种思路,首先从用户使用的角度来进行分析,工作流系统的组成就会变得异常清晰。实际在现实开发中,整个系统也是由用户的业务需求一步步迭代而来。

 

一、        从用户的角度分析工作流系统的组成

这里的用户分为两类:一类是应用系统开发人员(以后简称开发人员),一类是应用系统的最终用户(以后简称最终用户)。对于最终用户而言,工作流系统往往是不能直接使用的,它需要由IT部门的开发人员嵌入到应用系统中。开发人员才是工作流系统的直接使用者,这造成了问题:工作流系统更多关注于开发人员的需求,例如如何快速开发、如何更好的嵌入业务逻辑等等,而最终用户的需求被或多或少的忽略了。


这里从最终用户的角度进行分析。

 

1、                    面向开发人员的流程设计器

最终用户通过流程设计器对业务流程进行描述,实际是一个流程建模的过程。理论上,业务分析师完成这个业务流程建模的过程,并且业务分析师往往被假定为非技术人员。对于业务分析而言,流程建模通常是抽象的,一定程度上是模糊的,建模的目的在于通过图形的形式向其他人解释一个业务的过程,图形只是一种方式,采用它只是它更易于理解和易于沟通,实际类似于DSL。实际上企业的规章制度、文字描述的执行流程都是对业务流程具体的描述方式。

 

对于工作流而言,这个建模所产生的流程是需要被引擎执行的。这就要求流程中每一个节点的定义都是要有明确含义的,它需要被计算机明确而准确的解释。同时,出于集成业务系统的需要,流程模型定义往往带有很多额外的属性。

 

所以现实中的流程设计器往往属性配置繁多。导致最终用户在打开设计器后根本无从修改和建模,他需要关注很多与业务无关的配置,无意中的修改往往产生流程无法运行的后果。

 

2、                    工作项列表

即任务列表。工作流系统通过工作项列表进行人工任务的分配。最终用户通过该列表签收、处理每天的工作,工作以工作项的形式展现。对于工作项,用户有着多种业务操作:签收、完成提交、收回、回退等等。对于分配给他人的工作项,也存在着多种业务操作:催办、提醒、时间限定等等。

 

3、                    流程追踪

用户在处理工作项时,对该工作在流程中所处的位置进行查看,了解当前流程的状态和执行情况。一般情况下,流程追踪以图形化的方式展现。用户通过不同的图标和标示来区分流程中各个节点的状态和参与者信息。

 

4、                    流程实例管理

包括流程实例、节点实例、工作项实例的管理。改变状态,包括了挂起、重新启动、终止、跳转等等。主要目的在于对流程人为执行错误进行人工干预以及对流程信息的监控。

 

二、        系统架构

从用户的角度分析完工作流系统的组成,这里从开发人员的角度分析工作流系统的架构。系统架构里的每一部分是如何与用户使用的部分进行对应,以及每一部分在实现时需要注意的事项。

 

1、                    整体构成

如图,各模块分层组织,位于上层的模块依赖于底层的模块。正如你所看到的,流程定义模型位于整个工作流系统的最低层,因为它是整个工作流系统的基础。


 

流程定义模型:定义对流程进行描述的所有对象。因为对流程进行描述的本质就是利用这些模型进行建模,所以这些模型对象的实现直接决定着工作流系统对流程的描述能力。

 

组织结构适配器:工作流系统在与业务系统进行集成时,需要进行组织适配,通过这一过程将业务系统里的组织机构导入到工作流系统里。具体实现时,工作流系统需要建立起自己的组织机构模型(包含在流程定义模型里),要适应多种业务系统,往往需要建立多套模型,根据具体情况进行切换。有多种方式完成这个适配,最简单的方式是利用SQL配置读取数据进行语义转换。

 

流程设计器:供用户使用的可视化图形工具。每种图形都对应着一种流程定义模型。具体的实现有SwingSWT,但是基于AJAXWEB设计器无疑会提供更好的可用性。

 

流程执行引擎:将流程定义模型解释为流程实例模型。利用这些流程实例模型完成流程的调度和执行。在工作流系统里,执行引擎是整个系统的核心。实现时不仅需要考虑各种流程调度的实现,还要考虑执行的效率、缓存、日志等等。

 

工作项引擎:解析参与者定义模型和工作项定义模型,生成相应的工作项。对用户对工作项的操作作出响应。

 

WEB应用:工作流系统的WEB展现。包括了工作项列表、流程追踪以及流程实例管理的操作和显示。

 

流程仿真:对建立好的流程模型进行运行仿真,模拟流程模型的执行过程。目的在于发现流程建模过程中的疏漏,发现由此导致的流程不能运行。

 

时间服务:提供对整个流程实例执行时间和任务执行时间的控制,根据规则触发相应的时间事件,例如任务超时、任务预警等等。根据规则自动触发启动新的流程实例。

 

业务集成:提供工作流系统与业务系统的契合方式。典型的实现包括通过注册事件监听器和提供接口抽象类调用业务系统代码、提供API给业务系统调用、工作项驱动业务表单和脚本引擎执行业务逻辑脚本等等。特定于工作项驱动业务表单,为方便开发,绝大多数的工作流厂商都提供了电子表单的实现。

 

2、                    基于事件的流程执行引擎

流程执行引擎的主要职责就是负责流程的调度和执行。

 

首先需要将流程定义模型解释为流程实例模型,在定义模型和实例模型之间建立起对应关系。一个简单的对应关系如下图所示:


 

执行引擎将流程定义模型的属性读取到相应的实例模型里,由实例模型完成流程的调度和执行。当然,上图只是一个简单的描述,实际情况要复杂的多,特别是节点定义(ActivityDefinition),根据实际应用,往往存在着多种类型,典型的有开始节点(StartDefinition)、任务节点(TaskDefinition)、自动节点(AutoDefinition)、分裂节点(SplitDefinition)、汇聚节点(JoinDefinition)、结束节点(EndDefinition)等等。这些节点的实例根据类型的不同执行不同的逻辑。其中,分裂节点实例和汇聚节点实例负责流程的调度,它们决定流程的流向,通常情况下,它们会调用一个脚本引擎执行一段脚本来决定流程的流向,同时,也会提供对外暴露的接口,由业务系统实现,接口返回的结果决定流程的流向。任务节点实例和自动节点实例则负责流程的执行,为保证流程执行引擎职责的清晰以及对外围设施的松耦合,它们只是发布相关的事件,通过事件发布/订阅机制来触发具体的逻辑执行。


 

典型的事件有流程启动事件、流程结束事件、进入节点事件、离开节点事件、时间事件等。例如,任务节点实例的进入节点事件将会触发工作项引擎生成工作项(Workitem),并触发时间服务器开始计时。

 

3、                    基于充血模型的工作项引擎

对最终用户而言,大部分的业务操作都集中在对工作项的操作上。常见的包括工作项的提交、收回、委派、追加和退回。这些操作从系统设计的角度不仅涉及到工作项(Workitem)对象内部状态的变化,而且影响到流程执行引擎的调度以及相关的其他工作项对象状态。

 

工作项引擎的职责包括两部分。第一,监听任务节点实例的进入事件,生成工作项实例。第二,处理上面提到的各种工作项操作。


 

实现时,工作项生成器根据任务参与者的执行模式典型的分为四种情况:

 

竞争参与,当有多个参与者参与该任务时,产生竞争,谁先开始这项工作,就由谁负责完成该工作。此时,工作项生成器生成多个工作项实例,在某个工作项完成时会终止其余工作项。

顺序参与,多个参与者按照指定的顺序完成该工作。A完成之后由B完成,B完成之后再交给C完成。此时,工作项生成器生成多个工作项实例,根据顺序依次激活各个工作项。

共同参与,多个参与者同时对工作进行处理。此时,工作项生成器生成多个工作项实例并全部激活。

智能决策存在多个参与者的情况下,工作项生成器能够根据一定的指标(由数据分析,例如人员的处理效率,工作负载等等)和规则来决定该节点的参与者并为其生成相应工作项。这里涉及到算法。

 

对于工作流系统而言,各种流程实例对象都是充血模型。特定于各种工作项操作的处理,此时的工作项对象亦设计为充血模型,将业务逻辑封装到领域模型里,简化领域模型之间的交互,省去频繁的get/set。由领域模型再委派到具体的处理类里。

Client->(Business Facade)->Domain Model->service->Data Access(DAO)

 

4、                    工作项驱动业务表单的业务集成方式

最终用户对任务的处理,必然由工作项对应着某一业务表单。用户在工作项列表里选择自己需要办理的工作项,由工作项导航到业务表单。

 

特定于WEB系统,业务表单的导航由url完成。在流程定义模型设计时,将url设置入节点属性,生成工作项时将此url保存在工作项对象属性里。点击工作项详细信息时即打开该url,完成到业务表单的导航。业务表单页面通常需要引入处理工作项逻辑的父页面或者导入定制的js库,这些父页面或js库由工作流产品提供。这样,对于业务表单编写,工作流逻辑是透明的。

posted @ 2008-11-07 11:20 ronghao 阅读(2188) | 评论 (0)编辑 收藏

系统要集群,使用SNA方案。
一、 缓存的处理
缓存要使用统一的缓存服务器,集中式缓存。
原先的实现采用ehcache。
在spring里的配置,以资源缓存为例:

<!-- EhCache Manager -->
    
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        
<property name="configLocation">
            
<value>classpath:ehcache.xml</value>
        
</property>
</bean>

<bean id="resourceCacheBackend"
          class
="org.springframework.cache.ehcache.EhCacheFactoryBean">
        
<property name="cacheManager" ref="cacheManager"/>
        
<property name="cacheName" value="resourceCache"/>
    
</bean>

    
<bean id="resourceCache"
          class
="com.framework.extcomponent.security.authentication.services.acegi.cache.EhCacheBasedResourceCache"
          autowire
="byName">
        
<property name="cache" ref="resourceCacheBackend"/>
    
</bean>


cacheManager负责对ehcache进行管理,初始化、启动、停止。
resourceCacheBackend负责实际执行缓存操作,put 、get、remove。
resourceCache实现具有业务语义的业务应用层面的缓存操作,内部调用resourceCacheBackend操作。

现在采用memcached。
关于客户端,采用文初封装的客户端,地址在http://code.google.com/p/memcache-client-forjava/
使用spring的FactoryBean进行二次封装。同理:
memcachedManager负责对memcached进行管理,初始化、启动、停止。
代码:

/**
* User: ronghao
* Date: 2008-10-14
* Time: 10:36:30
* 管理Memcached 的CacheManager
*/
public class MemcachedCacheManagerFactoryBean implements FactoryBean, InitializingBean, DisposableBean {

    
protected final Log logger = LogFactory.getLog(getClass());

    
private ICacheManager<IMemcachedCache> cacheManager;

    
public Object getObject() throws Exception {
        
return cacheManager;
    }

    
public Class getObjectType() {
        
return this.cacheManager.getClass();
    }

    
public boolean isSingleton() {
        
return true;
    }

    
public void afterPropertiesSet() throws Exception {
        logger.info(
"Initializing Memcached CacheManager");
        cacheManager 
= CacheUtil.getCacheManager(IMemcachedCache.class,
                MemcachedCacheManager.
class.getName());
        cacheManager.start();
    }

    
public void destroy() throws Exception {
        logger.info(
"Shutting down Memcached CacheManager");
        cacheManager.stop();
    }
}
 

配置:


<bean id="memcachedManager"
          class
="com.framework.extcomponent.cache.MemcachedCacheManagerFactoryBean"/>

 

resourceCacheBackend负责实际执行缓存操作,put 、get、remove。
代码:

/**
* User: ronghao
* Date: 2008-10-14
* Time: 10:37:16
* 返回  MemcachedCache
*/
public class MemcachedCacheFactoryBean implements FactoryBean, BeanNameAware, InitializingBean {

    
protected final Log logger = LogFactory.getLog(getClass());

    
private ICacheManager<IMemcachedCache> cacheManager;
    
private String cacheName;
    
private String beanName;
    
private IMemcachedCache cache;

    
public void setCacheManager(ICacheManager<IMemcachedCache> cacheManager) {
        
this.cacheManager = cacheManager;
    }

    
public void setCacheName(String cacheName) {
        
this.cacheName = cacheName;
    }

    
public Object getObject() throws Exception {
        
return cache;
    }

    
public Class getObjectType() {
        
return this.cache.getClass();
    }

    
public boolean isSingleton() {
        
return true
    }

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

    
public void afterPropertiesSet() throws Exception {
        
// If no cache name given, use bean name as cache name.
       if (this.cacheName == null) {
        
this.cacheName = this.beanName;
    }
        cache 
= cacheManager.getCache(cacheName);
    }
}

配置:

<bean id="resourceCacheBackend"
          class
="com.framework.extcomponent.cache.MemcachedCacheFactoryBean">
        
<property name="cacheManager" ref="memcachedManager"/>
        
<property name="cacheName" value="memcache"/>
    
</bean>
  resourceCache同上,替换新的实现类MemcachedBasedResourceCache即可。

二、 Session失效的处理
采用memcached作为httpsession的存储,并不直接保存httpsession对象,自定义SessionMap,SessionMap直接继承HashMap,保存SessionMap。

会话胶粘:未失败转发的情况下没必要在memcached保存的SessionMap和httpsession之间复制来复制去,眉来眼去。

利用memcached计数器保存在线人数。

系统权限采用了acegi,在acegi的拦截器链里配置snaFilter

<bean id="filterChainProxy"
          class
="org.acegisecurity.util.FilterChainProxy">
        
<property name="filterInvocationDefinitionSource">
            
<value>
                CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                PATTERN_TYPE_APACHE_ANT
                /**=snaFilter,httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor
            
</value>
        
</property>
</bean>


注意需要配置在第一个。
snaFilter的职责:
1、 没有HttpSession时,创建HttpSession;
2、 创建Cookie保存HttpSession id;
3、 如果Cookie保存的HttpSession id与当前HttpSession id一致,说明是正常请求;
4、 如果Cookie保存的HttpSession id与当前HttpSession id不一致,说明是失败转发;失败转发的处理:
     4.1、根据Cookie保存的HttpSession id从memcached获取SessionMap;
     4.2、SessionMap属性复制到当前HttpSession;
     4.3、memcached删除SessionMap。
5、 判断当前请求url是否是登出url,是则删除SessionMap,在线人数减1.

代码:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) 
throws IOException, ServletException {
        
final HttpServletRequest hrequest = (HttpServletRequest) servletRequest;
        
final HttpServletResponse hresponse = (HttpServletResponse) servletResponse;
        String uri 
= hrequest.getRequestURI();
        logger.debug(
"开始SNA拦截-----------------" + uri);
        HttpSession httpSession 
= hrequest.getSession();
        String sessionId 
= httpSession.getId();
        
//如果是登出,则直接干掉sessionMap
        if (uri.equals(logoutUrl)) {
            logger.debug(
"remove sessionmap:" + sessionId);
            
//在线人数减1
            getCache().addOrDecr("userCount",1);
            getCache().remove(sessionId);
        } 
else {
            String cookiesessionid 
= getSessionIdFromCookie(hrequest, hresponse);
            
if (!sessionId.equals(cookiesessionid)) {
                createCookie(sessionId, hresponse);
                SessionMap sessionMap 
= getSessionMap(cookiesessionid);
                
if (sessionMap != null) {
                    logger.debug(
"fail over--------sessionid:" + sessionId + "cookiesessionid:" + cookiesessionid);
                    initialHttpSession(sessionMap, httpSession);
                    cache.remove(cookiesessionid);
                }
            }
        }
        filterChain.doFilter(hrequest, hresponse);
}

利用HttpSessionAttributeListener监听httpsession的属性变化,同步到memecached中的sessionmap。
public void attributeAdded(HttpSessionBindingEvent event) {
        HttpSession httpSession 
= event.getSession();
        String attrName 
= event.getName();
        Object attrValue 
= event.getValue();
        String sessionId 
= httpSession.getId();
        logger.debug(
"attributeAdded sessionId:" + sessionId + "name:" + attrName + ",value:" + attrValue);
        SessionMap sessionMap 
= getSessionMap(sessionId);
        
if (sessionMap == null){
            
//在线人数加1
            getCache().addOrIncr("userCount",1);
            sessionMap 
= new SessionMap();
        }
        logger.debug(
"name:" + attrName + ",value:" + attrValue);
        sessionMap.put(attrName, attrValue);
        getCache().put(sessionId, sessionMap);
    }

    
public void attributeRemoved(HttpSessionBindingEvent event) {
        HttpSession httpSession 
= event.getSession();

        String attrName 
= event.getName();
        String sessionId 
= httpSession.getId();
        logger.debug(
"attributeRemoved sessionId:" + sessionId + "name:" + attrName);
        SessionMap sessionMap 
= getSessionMap(sessionId);
        
if (sessionMap != null) {
            logger.debug(
"remove:" + attrName);
            sessionMap.remove(attrName);
            getCache().put(sessionId, sessionMap);
        }
    }

    
public void attributeReplaced(HttpSessionBindingEvent event) {
        attributeAdded(event);
    }

 
利用HttpSessionListener,sessionDestroyed事件时根据sessionid删除memcached里的sessionMap(如果存在)。不再担心httpsession的过期问题。
public void sessionDestroyed(HttpSessionEvent event) {
        HttpSession httpSession 
= event.getSession();
        String sessionId 
= httpSession.getId();
        logger.debug(
"session Removed sessionId:" + sessionId);
        SessionMap sessionMap 
= getSessionMap(sessionId);
        
if (sessionMap != null) {
            logger.debug(
"remove sessionmap:" + sessionId);
            
//在线人数减1
            getCache().addOrDecr("userCount",1);
            getCache().remove(sessionId);
        }
    }

  三、 文件保存的处理

和缓存类似,采用集中式的文件服务。对于linux,采用nfs。参考文档http://linux.vbird.org/linux_server/0330nfs.php#What_NFS_perm。关键在于对权限的分配。
应用程序本身不用修改。


posted @ 2008-10-28 20:41 ronghao 阅读(2499) | 评论 (3)编辑 收藏

在集群部署的情况下,应用程序需要做出调整,主要集中在四个方面:对httpsession的处理、对缓存的处理、共享的文件系统、synchronized关键字的失效。

httpsession的处理

httpsession的处理最为重要,因为对WEB程序而言,httpsession无疑是最重要的全局资源,它需要被多个web服务器所共享。

无共享的集群架构(SNA),在这样的集群中,每个节点具备完全相同的功能,并且不需要知道其他节点存在与否。每个节点JVM进程不保持全局状态,才能够保证nJVM节点的幂等性,那些所有涉及到全局状态的,必须放在JVM进程之外,例如用户ID可以使用cookiesession可以放入数据库(这并不是一个好的选择),文件可以放在共享存储系统中。

也就是说httpsession的信息需要被保存在JVM进程之外,例如分布式缓存、数据库。


这里是方案:

1、使用会话cookie保存web服务器产生的sessionid
   为什么是sessionid而不是userid,原因在于谁也不知道除去登录外其他人会在httpsession里干些什么

2、自定义SessionMap<String,Serializable>同步保存httpsession内的信息
   自定义SessionMap同步httpsession,在操作httpsession时不用改变调用接口,不用东张西望

3、使用分布式缓存memcached保存自定义SessionMap<String,Serializable>

4、会话胶粘
   未失败转发的情况下没必要在memcached和httpsession之间复制来复制去,眉来眼去

5、使用SnaFilter处理失败转发

6、使用HttpSessionListener实现SessionMap<String,Serializable>的过期
   利用容器session 机制的好处,httpsession过期的时候干掉memecached里的SessionMap


下面根据web请求的过程分情况讨论该方案:

A、登录


根据请求的url判断是否是登录请求

在线人数保存在memcached

B、 正常请求


C、 失败转发


D、登出


根据请求的url判断是否是登出请求

E、HttpSession过期

hack memcached,使用HttpSessionListenersessionDestroyed事件时根据sessionid删除memcached里的sessionMap(如果存在)

关于在线人数的统计:在线人数存储在memcached里,将在线人数与sessionMap绑定,往memcached里增加sessionMap时在线人数+1,删除时-1.

posted @ 2008-09-04 14:31 ronghao 阅读(2257) | 评论 (0)编辑 收藏
项目情况:是一个大型公司的内部办公系统,该系统有两个和一般企业应用不太一样的特点:一是用户量非常多,人员数达到2W左右,另一个是采用分级管理的形式,各个分公司数据分开管理。

我们的定位:我们是作为业务平台的提供商参与这个项目的,我们提供底层的开发平台,系统集成商在此基础上进行二次开发。

在项目从开发到部署的过程中遇到了很多的问题,也反映出很多问题。

一、怎么回事,跑得比猫还慢
项目开发完毕后部署在Ibm aix 小型机上,32G内存,16个cpu。应用服务器采用的是weblogic9.2,数据库是oracle10.0.2。上线后发现系统运行的非常缓慢,甚 至比开发环境下的tomcat还要慢。于是开始排查原因,最开始是对SQL进行监控,优先考虑是数据库访问性能产生瓶颈。通过监控,发现很多业务需要执行 大量的SQL语句,查看客户编写的相关代码,发现在查询数据时循环执行了大量SQL。主要原因在于他们在代码中循环调用了我们相关API,一个最典型的例 子是通过用户ID查找用户NAME,他们在业务表格里没有保存用户name,而是在查询的时候通过用户ID查找用户name填充到页面,几乎每一个查询都 是n+1。
 
另外由于平台使用了hibernate,使得oo编程得非常爽快,导致开发人员完全忽略了相应的数据库操作所带来的压力。很多业务逻辑直接通过PO叠加完成,把一些可以通过很少SQL完成的逻辑全部分散放置到PO里,导致了大量PO的交互和SQL语句。

开始优化SQL,优化的同时增加大量业务缓存。但优化完毕后运行缓慢的现象依旧存在,性能有了一定的提升但是不是非常明显。继续优化,其中考虑过 多频繁访问的数据使用内存数据库的方式。但是优化过后在tomcat上效果明显,部署到生产环境就问题依旧。于是考虑weblogic的配置问题,作为开 发平台提供商,我们只是提供系统开发相关方面的支持,对于应用服务器和数据库服务器只是做基本的配置系统可运行即可。但是在这个问题上系统集成商咬定是我 们平台的问题不放,并且存在一个很严重的问题:他们使用的是盗版的weblogic,这样根本就没有相应的技术支持。

问题的解决:最后是找了一个BEA曾经的开发人员,问题实际非常的简单,现场部署的weblogic默认是运行在32位机器上,与64位机器存 在一定的不兼容。通过替换相应的jar包,问题得到了解决,主要是IO方面。替换完毕后,速度提升了进30% 。该开发人员说,如果没有lisence,根本就不会得到这些替换的jar包。

二、内存耗尽了
访问速度的问题解决了,系统的使用量很快上来,马上遇到新的问题:内存耗尽了。严重到几乎每天都要out of memory一次。这种问题在客户现场频繁出现。

本地测试,tomcat,sun jdk 通过Jprofiler监测内存使用情况。在并发访问门户的情况下,内存确实存在暴涨的情况,100并发,内存使用立刻上升了150m左右,继续并发 100,再增长150m。但是很快在抵达高峰时会有一次gc发生,内存使用稳定在200m,内存里大量char[]数组对象。疲劳测试,内存使用曲线并没 有出现逐渐上升泄露的情况。换weblogic和jrocket测试,gc发生的更加频繁,内存使用稳定。
 
但是现场依旧频繁当机,内存根本释放不了,一直逐渐增长,典型的内存泄露。对系统缓存、单态对象包括spring管理的对象、IO流进行了统一 排查,依旧没有找到内存泄露的原因。使用IBM 工具分析heapdump文件,结果还是大量的char[]数组对象占据内存,查找应用,找不到相关业务对象引用。
 
问题解决:问题解决是一篇偶尔搜到的oracle论坛的帖子,这里http://forums.oracle.com/forums/message.jspa?messageID=1040570 。原因在于oracle10的数据库驱动对statement最后执行的结果集有着引用,并且不会释放,目的在于通过内存而换取更好的性能。数据库连接采 用的是weblogic的连接池,关于connection有个相关的statement cache设定,设定一个connection能够被缓存的statement个数,最大是1024,而现场就被设定为了1024!connection pool的connection个数被设置为了500 。真是个恐怖的设置。在将1024改为10后,内存使用量轰然倒地,稳定在1g左右。这个设置是在前面系统访问速度存在问题时由系统集成商的开发人员设置 上去的,他们将所有和优化相关的参数全部开到了最大。这个问题要是用户购买的是正版的weblogic和oracle的话,相信也会很快得到解决。

三、线程阻塞
内存泄露的问题解决后,线程阻塞的问题浮出水面。系统集成商报告是线程死锁,通过分析工具其实是线程阻塞,主要问题在于系统用到了 synchronized关键字,对工作流相关API全部使用了synchronized,原因在这里:http: //ronghao.javaeye.com/blog/205731 。分析发现一个工作项提交的操作在连接数据库时被挂起了20分钟!造成了大量线程的排队阻塞。被挂起的原因有很多种。我们采用的方法是将接口拆分和设置事 务timeout时间。但是这显然不是一个好方法。最后是去掉所有的synchronized关键字,将同步的问题交由数据库解决,问题解决。

四、反思
1、系统集成商为什么不购买正版?
2、开发平台提供商究竟在项目开发中处于一种什么样的位置?开发平台是否对所有软件开发问题都要负责?
3、开发平台是越封装越快乐吗?还是越封装越丑陋?

更具体的细节在这里:

 AIX+weblogic性能诊断记录

posted @ 2008-09-01 13:49 ronghao 阅读(4104) | 评论 (10)编辑 收藏


从事工作流以及相关开发已经三年。提到工作流,很多人都会想到
BPM,想到业务流程。对于业务流程,我的理解经过了一个过程,从最开始对工作流抱有的不切实际的期望,到对BPM的一些看法,再到目前的趋于实际。有一些感触,也有一些理解。对于业务流程管理而言,我想说的是:BPM向左,工作流向右,都不靠谱,或者说它们实际所能描述的流程和这里的业务流程根本就风牛马不相及,不是一个概念,唯一的相同点是只不过都叫流程而已。

一、什么是业务流程

业务流程是一个技术术语,它具有准确的定义:有组织的活动,相互联系,为客户创造价值。

这句话很好理解。甚至可以说任何企业的活动都是以业务为主线,以流程为线索串联起来的。企业的规章制度、操作手册等都与业务流程有着契合点。

二、业务流程对于企业的意义

业务流程对于企业的意义不仅仅在于对企业关键业务的一种描述,更在于对企业的业务运营有着指导意义,这种意义体现在对资源的优化、对企业组织机构的优化以及对管理制度的一系列改变。这种优化的目的实际也是企业所追求的目标:降低企业的运营成本,提高对市场需求的响应速度,争取企业利润的最大化。

三、业务流程也是一个体系

业务流程通常的表现形式是流程图(不是唯一形式),毕竟图形是最易于理解的一种形式,但似乎我们太关注于流程图本身而忽略了其他。除了流程图之外,业务流程还应该包括目标和指导方针,这才是一个完整的业务流程。在梳理业务使用业务流程描述时首先要想到的是该流程所要达到的目标,能为客户创造什么价值,脱离开业务目标或者说纯粹为描述业务而描述业务是没有意义的。同时在制定业务流程时也要考虑到该业务流程的指导方针,同一个业务可能有很多种业务流程的描述形式,具体哪一种是最合适的,这里就必须有一个指导方针来进行约束。

四、业务流程的特征

1、层次性、逐层抽象

业务流程是有层次性的,这种层次体现在由上至下、由整体到部分、由宏观到微观、由抽象到具体的逻辑关系。


这样一个层次关系符合人们的思维习惯,有利于企业业务模型的建立。一般来说,我们可以先建立主要业务流程的总体运行过程(其中包括了整个企业的大的战略),然后对其中的每项活动进行细化,落实到各个部门的业务过程,建立相对独立的子业务流程以及为其服务的辅助业务流程。

业务流程之间的层次关系一定程度上也反映了企业部门之间的层次关系。不同层级的部门有着对业务流程不同的分级管理权限。决策层、管理者、使用者可以清晰的查看到下属和下属部门的业务流程。


为使得所建立的业务流程能够更顺畅的运行,业务流程的改进与企业组织结构的优化是一个相互制约、相互促进的过程。

2、以人为本

组织中最重要的部分是人员的工作方式以及构成他们每日操作的工作流程。

人是业务流程的驱动者,组织中的每一个人都会在业务流程中充当一个角色。通过良好的业务流程,每一个人都会有自己清晰的职责,要求具有良好的沟通协作意识和团队意识,明确自己在一个个业务流程中所担当的角色。

同时对于参与其中的业务流程,每个人员都要有自己的反馈。

首先,每个人员都能查看到这些业务流程,他们需要充分理解这些业务流程、流程的业务意义和目的,这些业务流程通过切合他们理解能力的方式(切合业务的图形、说明文字、相应的制度、规范、标准等等)得以展现。

其次,对于流程运行中存在的问题或瓶颈,每个人员都要积极反馈(提出修改的建议,或者在权限范围内直接修改)以促进流程的持续改进,业务流程的管理和变动不仅仅是业务分析人员或管理人员的职责,每一个员工都要参与其中,否则只有失败。管理人员和决策层更重要的职责是制定出业务流程的规则和约束,在这个规则和约束范围内,员工可以根据变化的商业环境对业务流程做出迅速修改,这样不必等到领导了解情况后再做出决策从而失去机会。

3、对流程运行效益的分析

从企业投资者的角度来讲,好的业务流程设计必然是能够为企业带来最高利润的设计。因此,对业务流程的效益分析是评价业务流程的一个重要方面。财务数据是最关键的数据,但这种分析不一定完全是由数据支撑的,有些是不能量化的,比如人员效率等等。

五、业务流程管理

良好的业务流程管理是保证企业灵活运营的关键(业务流程管理又何尝不是一种业务流程?)。

1、业务分析

实际这也是业务流程管理最重要的部分。它需要对企业业务有着强大的分析能力,因为业务分析对企业的运营有着重大的指导意义,只有具备了这样的业务分析能力,才能把握住企业运转的真实流程,而且这种分析能力往往带有对整个行业的深刻理解和前瞻性。没有异议,业务分析在于人,与IT无关。

2、业务流程的持续改进

不仅仅是流程管理人员(管理决策层)根据运行效益的分析和商业环境的分析对流程进行重整。还包括每个员工对其参与的流程的持续反馈和持续改进。柔性的业务流程。

3、IT系统与业务流程的关系

IT系统与业务流程并没有直接的关系。正如06zSOA帖子里表达的:soa95%以上的工作是在做业务流程的分析解构和重整,技术层面的支持只占5%不到。在落实到技术层面,你觉得一个soa产品究竟应该包括些什么内容呢?这些内容又能有多少是能够辅助大家对业务流程进行分析和测试,对业务元素进行重整和再分配?如果你们真的有这个能力,你们觉得是在这里继续开发软件过苦日子,还是去开拓商业咨询呢?我的观点是:SOA很美好,但是一落地就变成了小丑。所谓的业务流程管理软件同理。

可以这样理解:业务流程管理是一个很大的命题,IT系统通过信息化对它的子集进行支撑,这里的IT系统包括的范围很广泛,包括了所有的企业应用软件(所有的企业应用软件都可以看作是对企业某部分的业务流程进行的描述)。业务流程管理的核心在于业务流程的分析解构和重整,这点是所有软件都不可企及的,关键在于人。至于BPM还是工作流,它们本来就有它们自己的适用范围,硬要把它提升到业务流程管理的高度来宣传,那就真的和小丑一样,滑稽而可笑。

关注下篇:BPM是干什么的

posted @ 2008-08-26 17:33 ronghao 阅读(6208) | 评论 (2)编辑 收藏
用js编写自己的组件,测试一直是个头疼的问题。最开始大量使用alert,firebug出现后天突然蓝了。但人的欲望总是没有止境的,在面对越来越多的后台数据交互以及特定于不同业务数据不同的展现形式时,仿佛一夜回到解放前。

说说我现在的困境:

目前要做的是工作流的提交页面,也就是对当前办理工作的用户展现后续任务,根据不同的情况由用户选择或是引擎自动计算。这是最简单的情况,后续包括参与者的选择计算、时间服务设定以及Comment等等。
现在根据业务逻辑分为了四种情况:
1、串行
2、分支选择
3、M选N选择
4、复杂的分支组合
四种情况需要准备不同的业务测试数据,同时页面展现也是不同的。我采用的方式如下图:


针对每种情况都建立相应的测试文件夹,在各自文件夹下准备各自的业务测试数据以及测试页面。并且一个testcase往往需要很多的业务测试数据(和通用组件还是不太一样)。清晰还是清晰,但是问题在于这种测试还是人肉,做不到自动化测试,同时为了业务数据能够顺利插入不得不hack一些代码。当增加或改动部分代码后就要人肉返测一次,预计代码还会大量膨胀,相应的测试文件还会增加。真是苦海无边,无心睡眠。想想cc和junit真是幸福的像花一样。

我佛慈悲,不知道大家有什么好的方法没有?

posted @ 2008-08-11 19:05 ronghao 阅读(1662) | 评论 (3)编辑 收藏

收回

收回是工作流参与者对自己“已办任务”(对已完成的工作项)的一种操作,即参与者主动对已办理过的工作项进行重新办理。

为什么要收回?

参与者完成任务后,发现自己办理有错误等情况后,需要将此任务收回重新办理。

工作项的参与方式

目前有四种方式:共同参与、竞争参与、顺序参与、基于角色的共同参与。

下面会针对这四种方式进行讨论。

工作项收回模式

1、未触发下一节点的工作项的收回

即当前任务节点并未完成,依旧处于执行状态


1.1共同参与


如图:在节点A未结束之前,workitem1、workitem2和workitem3正常完成后可以任意收回。在只产生一个workitem的情况下,不存在未触发下一节点的收回情况。


1.2顺序参与


如图:workitem1workitem2workitem3顺序完成,workitem1workitem2签收(包括挂起和手工终止)前可以收回,同样,workitem2workitem3签收(包括挂起和手工终止)前也可以收回。在只产生一个workitem的情况下,不存在未触发下一节点的收回情况。


1.3竞争参与

因为只会产生一个workitem,该workitem完成后会立刻触发下一节点,所以不存在未触发下一节点的收回情况。


1.4基于角色的共同参与

1.1相同。

2、已触发下一节点的工作项的收回

 

2.1共同参与


问题1:多个工作项时谁可以执行收回操作?

workitem1workitem2workitem3都可以执行收回操作。第一个工作项的收回将会导致节点B实例的删除,同时节点A重新恢复执行状态。


问题2:节点B处于什么状态节点A的工作项可以执行收回操作?

A触发的节点B处于正在执行的状态,节点B所产生的工作项:

a共同参与   工作项均未签收、挂起或手工终止

b顺序参与    第一个工作项未签收、挂起或手工终止

c 竞争参与   工作项均未签收、挂起或手工终止

d角色        同共同参与


问题3:工作项收回产生的影响?

节点A重新执行,收回的工作项重新执行。节点B重新恢复未触发状态,B所产生的工作项全部删除。


2.2顺序参与


问题1:多个工作项时谁可以执行收回操作?

workitem1workitem2workitem3根据顺序可以依次执行收回操作。


2.3竞争参与

情况简单,只有一个工作项,所以可以直接收回。


2.4基于角色的共同参与

2.1


工作流收回模式

后续触发节点只能是人工节点(可以是多个,至少一个),否则不支持收回。目前不支持父子流程之间的收回。

一个典型的同步汇聚情况:


节点1首先执行完毕,但是因为是同步汇聚,所以它不会触发实际的流转;而节点2的完成则会触发节点3的执行。在这种情况下,节点2的工作项可以执行收回操作,而节点1的工作项因为后续没有触发节点而不能收回。

posted @ 2008-07-15 18:28 ronghao 阅读(1411) | 评论 (3)编辑 收藏
关于Domain Model的讨论已经非常多了,炒炒冷饭,这里是自己的一些做法。
以Workitem(工作流里的工作项)作为例子

最开始的做法:
一个实体类叫做Workitem,指的是一个工作项或者称为任务项
一个DAO类叫做WorkitemDao
一个业务逻辑类叫做WorkitemManager(或者叫做WorkitemService)

主要看看WorkitemManager,因为主要逻辑集中在这里

public class WorkitemManager {

        
private WorkItemDAO workItemDAO;

    
public void setWorkItemDAO(WorkItemDAO workItemDAO) {
        
this.workItemDAO = workItemDAO;
    }
    
    
/**
     * 提交工作项
     * 
@param workitemId 工作项ID
     
*/
    
public void commitWorkitem(String workitemId){
            WorkItem workitem 
= workItemDAO.getWorkItem(workitemId);
            
//当前工作项结束
        workitem.complete();
        
int sID = workitem.getSequenceId();
        
//找到所对应的节点
        InstActivity instActivity=workitem.getInstActivity();
        
//查找是否存在下一工作项
        WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
        
//如果不存在则触发节点流转
        if (sequenceWorkitem == null) {
            instActivity.signal();
        }
        
//否则把下一工作项激活
        else {
            sequenceWorkitem.setExecutive();
        }
    }
    
}


Workitem类里有一些状态转换的逻辑,这样避免直接调用get/set属性方法

public class Workitem{

        
private int state = WorkitemInfo.PREPARE;

        
/**
     * 委派工作项
     
*/
    
public void commission() {
        
if (state != WorkitemInfo.EXECUTE && state != WorkitemInfo.SIGNINED
                
&& state != WorkitemInfo.TOREAD&& state != WorkitemInfo.SUSPEND)
            
throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);
        setState(WorkitemInfo.COMMISSIONED);
        setCommitted(
new Timestamp(System.currentTimeMillis()));
    }

    
/**
     * 完成工作项
     
*/
    
public void complete() {
        
if (state != WorkitemInfo.SIGNINED)
            
throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);
        setState(WorkitemInfo.COMPLETE);
        setCompleted(
new Timestamp(System.currentTimeMillis()));
    }
}


接下来的做法:
三个类不变,将WorkitemManager打平,将逻辑移动到Workitem

public class WorkitemManager {

        
private WorkItemDAO workItemDAO;

    
public void setWorkItemDAO(WorkItemDAO workItemDAO) {
        
this.workItemDAO = workItemDAO;
    }
    
    
/**
     * 提交工作项
     * 
@param workitemId 工作项ID
     
*/
    
public void commitWorkitem(String workitemId){
            WorkItem workitem 
= workItemDAO.getWorkItem(workitemId);
            
//当前工作项提交
        workitem.commit();
    }
    
}

实际上此时WorkitemManager的功能非常有限,仅仅是事务边界和获取workitem对象,甚至在一些情况下可以省略。

通过一个Container类将spring的applicationContext进行封装,然后通过getBean()的静态方法即可访问被spring所管理的bean。实际是将workItemDAO隐式注入了Workitem。

public class Workitem{

        
/**
     * 提交工作项
     
*/
    
public void commit() {
        
if (state != WorkitemInfo.EXECUTE && state != WorkitemInfo.SIGNINED
                
&& state != WorkitemInfo.TOREAD&& state != WorkitemInfo.SUSPEND)
            
throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);
        setState(WorkitemInfo.COMMISSIONED);
        setCommitted(
new Timestamp(System.currentTimeMillis()));
        
int sID = workitem.getSequenceId();
        WorkItemDAO workItemDAO
=(WorkItemDAO)Container.getBean("workItemDAO");
        
//查找是否存在下一工作项
        WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
        
//如果不存在则触发节点流转
        if (sequenceWorkitem == null) {
            instActivity.signal();
        }
        
//否则把下一工作项激活
        else {
            sequenceWorkitem.setExecutive();
        }
    }

}


这样带来的好处是业务逻辑全部被封装到Domain Model,Domain Model之间的交互变得非常的简单,没有频繁的set/get,直接调用有业务语义的Domain Model的方法即可。问题在于单元测试时脱离不了spring的容器,workItemDAO需要stub。我觉得这个问题不大,问题是Domain Model开始变得臃肿,在业务逻辑复杂时代码行急剧膨胀。

现在的做法
以上三个类保持不变,增加一个类WorkitemExecutor,将业务逻辑移步。

public class Workitem{

        
/**
     * 提交工作项
     
*/
    
public void commit() {
        
if (state != WorkitemInfo.EXECUTE && state != WorkitemInfo.SIGNINED
                
&& state != WorkitemInfo.TOREAD&& state != WorkitemInfo.SUSPEND)
            
throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);
        setState(WorkitemInfo.COMMISSIONED);
        setCommitted(
new Timestamp(System.currentTimeMillis()));
        WorkitemExecutor workitemExecutor
=(WorkitemExecutor)Container.getBean("workitemExecutor");
        workitemExecutor.commitWorkitem(
this);
    }

}

public class WorkitemExecutor {

        
private WorkItemDAO workItemDAO;

    
public void setWorkItemDAO(WorkItemDAO workItemDAO) {
        
this.workItemDAO = workItemDAO;
    }
    
    
/**
     * 提交工作项
     * 
@param workitemId 工作项ID
     
*/
    
public void commitWorkitem(Workitem workitem){
        
int sID = workitem.getSequenceId();
        
//找到所对应的节点
        InstActivity instActivity=workitem.getInstActivity();
        
//查找是否存在下一工作项
        WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
        
//如果不存在则触发节点流转
        if (sequenceWorkitem == null) {
            instActivity.signal();
        }
        
//否则把下一工作项激活
        else {
            sequenceWorkitem.setExecutive();
        }
    }
    
}


将业务逻辑拆分成两部分,一部分在Workitem,另一部分委托给WorkitemExecutor。实际上是Domain Model将复杂逻辑的情况重新外包出去。调用的时候,面向的接口还是Domain Model的方法。注意到WorkitemExecutor和WorkitemManager的API是非常相似的。实际可以这样认为,传统的方式
Client->(Business Facade)->service(Business Logic 部分依赖Domain Model)->Data Access(DAO)。
现在的方式
Client->(Business Facade)->Domain Model->service->Data Access(DAO)。

另外,在返回client端的查询的时候还是倾向于直接调用DAO,而不是通过Domain Model。

改进:
注意到代码中有这么一行
WorkItemDAO workItemDAO=(WorkItemDAO)Container.getBean("workItemDAO");

确实是一个bad smell.当代码中大量出现后,这种造型是很恐怖的。所以采取了一种处理方式:给所有Domain Model继承一个父类,在父类里集中管理所有Domain Model所依赖的services,在父类里进行造型。


posted @ 2008-07-03 18:23 ronghao 阅读(2610) | 评论 (2)编辑 收藏

回退(Rollback WorkItem)

回退是工作流参与者对自己“待办任务”(实际是对工作项)的一种操作,即参与者主动回退待办任务列表中的任务到已经执行过的人工节点。

为什么要回退?

参与者接受任务后,发现不应由自己办理此任务或以前的执行者办理有错误等情况后,需要将此接受的任务回退给以前某个节点的执行者重新办理。

回退模式

回退的情况实际上是非常复杂的,其中包括了参与者的重新选择以及回退的条件判断等等。这里先列出常见的回退模式(其实也是我们支持的模式)。

串行

   

这种情况最为简单,后续节点可以回退到前续任意人工节点。回退后,节点重走。

分支

   

这种情况也相对简单,实际执行的分支上的节点可以回退到前续任意人工节点(不区分主支和分支)。同样,主支上的节点也可以回退到任意实际执行的分支上的节点。

可能的问题:多次回退后的回退节点选择。例如:第一次流程经过节点2、节点3到达节点5,节点5可以回退到节点1、节点2和节点3的任意一个,此时节点5回退到节点1,节点1重走,这一次流程改为经过节点4到达节点5,节点5回退时如何选择回退节点?此时的策略是以最近实际执行的分支为准,即节点5只允许回退到节点4和节点1,不允许回退到节点2和节点3。(抹去记忆)

并发

   

对于并发的情况,分支节点只允许在分支的节点间回退。


同理,主支节点也只允许在主支的节点间回退。

多实例汇聚

   

在这种情况下,节点5会产生2个实例,实际相当于继续并发。节点5根据具体哪个节点触发的它而产生回退节点。同时不允许回退到节点1以及前续的节点去。

子流程

   

支持子流程到父流程的回退,也支持父流程到子流程节点的回退。需要注意的是子流程节点有可能产生多个子流程实例,在这种情况下不支持父子流程之间的相互回退。

回退节点的参与者选择

默认策略是由原先节点的实际参与者重新处理,比如节点2回退到节点1,则节点1的实际参与者重新处理该节点任务。这也符合大多数实际的业务场景。

在节点任务竞争参与的情况下,提供另一种策略,即让人员重新竞争。

回退的条件判断

对于多人(或者多部门,用户)参与的工作项,提供不同的回退策略

任意人回退即回退,剩余工作项手工终止

最后提交人回退才回退

   流程定义期定义该策略。

   另外流程定义时提供节点可回退列表,由用户在定义期对可回退的节点进行限制。

关于业务补偿

业务补偿是一个很重要的概念,在回退的情况下需要相应的回退部分业务操作。这里由引擎提供统一的接口,返回回退路径,由客户自定义代码进行匹配处理。

 

关于实现

很多工作流引擎通过流程定义时绘出回退线来显式的支持回退,这种实现在业务复杂的情况下会造成流程图的异常烦琐,但是比较清晰,实现比较容易。隐式实现相比而言优点更多。

posted @ 2008-06-24 09:12 ronghao 阅读(2364) | 评论 (3)编辑 收藏
收到这本书已经好久,甚至读完这本书都已经好久,一直想着写个书评,却一直被这事那事拖着,直到今天。我只想说,这是一本好书。

关于Hibernate似乎不必说太多。和朋友聊天,朋友说,你对Hibernate熟吗?我说,还好,用了两年了。朋友说,如果10分是满 分,你给自己打几分?我认真想了想,6分吧。说实话还真没有底气,会用而已。在此之前,我就看过一本Hibernate的书籍,《深入浅出 Hibernate》,然后就是满江红翻译的Hibernate中文手册。我想,也许没有必要再买一本Hibernate的书了,有问题查查 Hibernate中文手册就好了。

问题在于,我收到了这本书。

花了两个礼拜大概看过这本书。看的快是因为一些内容本来就很熟悉,还因为把jpa这部分全部略过了。一口气读完的。原谅自己曾经的浅薄吧,看 完这本书才发现自己对Hibernate的了解其实少的可怜,知道如何映射实体对象,知道对象的3种状态,知道save和 saveorupdate,get和load的区别,知道级联和关系控制反转。。。。知道这些就够了吗,实际差得还很远呢。这本书把以前许多零散的知识点 全部系统的串联起来,很多时候,看得我暗自流汗,哎,原来是这样的啊。

实际这本书不仅仅局限于Hibernate,我更认为是对java持久化的一个完整介绍和总结。我想,像without ejb一样,是应该人手一本的。

关于翻译,这本书被人诟病的厉害,原因是认为翻译的很差。我的感觉是,确实存在问题,但是也不至于差到网上说得那种地步。至少我读过一遍,基 本上没有碰到读不过去的地方,相反,还是比较流畅的。但是不是说没有问题,一些句子是根据上下文很快得出意思的。我想,对于刚使用Hibernate不久 的人来说,这种理解很可能就显得比较困难,会显得无所适从。另外,单纯从英文版来说,这本书也不适合作为Hibernate的入门书。另外,这本书的定价 太高,我想,这也是读者反应很激烈的一个原因。很高的期望,很贵的价格,结果不是很满意,自然谩骂的厉害。我想,出版社的读者定位、市场策略包括定价都是 存在问题的。

最后说说翻译,翻译确实不是一件轻松的事情,这点我有很深的体会。去年翻译《Enterprise AJAX》时,每天的进度只有两三页,有时一个句子要反复揣摩好长时间,真是痛苦的一个过程(但是却是收获良多)。

最后,一本好书。
posted @ 2008-06-22 15:22 ronghao 阅读(2027) | 评论 (3)编辑 收藏
测试在sqlserver2000上进行,对工作流操作的相关方法在单元测试里进行多线程并发。测试发现sqlserver出现死锁的情况相当多,一些典型的情况:

1、对同一张表先insert再update是很快会引起死锁的,不管操作的是否是同一记录
解决方法:对于同一记录,需要调整hibernate的映射策略,使得一次insert完成操作。对于不同的记录需要在代码中手动flush,使得update先于insert。

2、对两张表进行多次update操作时,两张表交替update也会很快引起死锁
解决方法:在代码中手动flush,保证对两张表的update不会出现交替的情况。

3、部分大范围扫描的select和update混合也会导致死锁
解决方法:优化sql,尽量减少sql语句,通过给po增加持久化字段的方式减少关联查询

经过优化,大部分情况下数据库死锁的情况得以避免,另外奇怪的是通过事件探查器在死锁时并未发现锁升级的事件。但是在一些特殊情况下(例如多个并发汇聚的直接联合),死锁依旧发生。最后不得不对方法进行synchronized关键字同步,这个通过synchronized flush完成。业务方法不必同步,最后批量操作数据库时进行同步。

换oracle进行测试,在未synchronized的情况下,未发生死锁情况。由此可见sqlserver与oracle锁实现机制存在很大的差别。对sqlserver鄙视之。另,同事说,sqlserver2005后性能和机制发生了很大的变化,未测试。

补充一下我的一个最简单情况下的测试用例:
PO:
public class TestPO {
    String id;
    String name;
    
int num;
    
    .
}

映射文件 hibernate3:
<hibernate-mapping default-access="field">
  
<class table="WFMS_TESTPO" name="com.eway.workflow.test.po.TestPO">

    
<id name="id" column="ID"><generator class="uuid" /></id>

    
<property name="name" column="NAME" type="string"/>

    
<property name="num" column="NUM" type="integer"/>

  
</class>
</hibernate-mapping>

被测试方法(都配置有事务):
    public void testSave(int num) {
        TestPO po 
= new TestPO();
        po.setName(
"ronghao");
        po.setNum(num);
        theadTestDao.save(po);
        po.setName(
"haorong");
    }

    
public void testSaveByJdbc(int num) {
        String sql 
= "insert into WFMS_TESTPO (ID,NAME,NUM) values (?,'RONGHAO',?)";
        Object[] params 
= new Object[]{num,num};
        jdbcTemplate.update(sql, params);
        sql
="update WFMS_TESTPO set name='haorong' where id=?"  ;
        params 
= new Object[]{num};
        jdbcTemplate.update(sql, params);
    }

测试用例:
     public void testSave() throws Exception {
        TheadtestTemplate template 
= new TheadtestTemplate();
        template.execute(
new TheadtestCallback() {
            
public void doInThead(int suquence) {
//               theadTestManager.testSave(suquence);
                theadTestManager.testSaveByJdbc(suquence);
            }
        }, 
10);
    }

测试结果:不论是hibernate还是jdbc,并发情况下都很快就会引起sqlserver2000的死锁,换用两种数据库驱动jtds和jturbo死锁的情况没有变化。

结论:sqlserver2000数据库的lock配置策略,不支持,或者数据库本身,就不支持对不同的行做同时操作(或者支持不完善),所谓的行锁支持很不完善,死锁情况非常容易发生。

补充:我对数据库的一些实现机制也并不是很了解,所以这里也只能列出现象而不能解释死锁的根本原因。另外感谢Alex的讨论。
posted @ 2008-06-19 13:34 ronghao 阅读(6265) | 评论 (22)编辑 收藏
今天用hsqldb做单元测试时碰到这么个异常
failed batch; nested exception is java.sql.BatchUpdateException: failed batch
经过检查发现是HSQLDB的问题
The bug is in HSQLDB - a well known one (any Google search for HSQLDB and that "failed batch"
message would have told you it).
https://sourceforge.net/tracker/?func=detail&atid=378131&aid=1407528&group_id=23316
解决方法 : turn off batching with HSQLDB it doesnt work.
设置<prop key="hibernate.jdbc.batch_size">0</prop>即可
posted @ 2008-05-30 18:40 ronghao 阅读(671) | 评论 (1)编辑 收藏

这是我们(东方易维)工作流产品设计过程中采取的设计:

一、流程实例的状态

   状态分为5种:实例化、执行中、挂起、手工结束、正常结束。

   状态的变迁如下图:


二、节点实例的状态

状态分为5种:实例化、执行中、挂起、手工结束、正常结束。

状态的变迁如下图:


三、具体节点的状态

   细分:

A、人工节点、等待节点

这两个节点被触发后存在一个执行等待的过程,所以可以被用户直接挂起和手工结束。人工节点的挂起意味着所有未完成工作项的挂起,同时相应时间服务的时间计算的挂起。手工结束会使流程跳过该节点(所有工作项手工结束),继续往后流转。

B、开始节点、结束节点、分支节点、自动节点

这些节点的特点在于被触发后立刻执行和流转,所以不会存在挂起和手工结束的状态。

C、并发节点、汇聚节点

并发节点和汇聚节点不存在挂起的状态,同时不能被用户直接手工结束,它们的状态受流程实例状态和相关节点实例状态的影响。

并发节点和汇聚节点的情况复杂一些,分模式讨论


1

1、同步汇聚(图1

根据情况触发节点0、节点1、节点2中的一个或多个,汇聚节点等待所有实际触发的节点完成后再执行流转。中间汇聚节点只会产生一个实例。

1.1、正常流转时的处理策略

当汇聚节点未被触发时(即节点0、节点1、节点2都未执行结束),并发节点处于执行状态,一旦汇聚节点被触发(即节点0、节点1、节点2有一个执行结束),并发节点正常结束并且汇聚节点处于执行状态,所有并发出的节点实例执行结束后,汇聚节点正常结束,流程继续流转。

1.2、用户挂起、手工结束相关节点的处理策略

1.2.1、汇聚节点未激活时

节点0、节点1、节点2的挂起和恢复执行不会影响并发节点的状态(依旧处于执行状态);节点0、节点1、节点2的任一手工结束都会触发汇聚节点,使并发节点正常结束,如果所有并发的节点实例都结束(包括手工结束和正常结束),汇聚节点正常结束,触发流程流转。

1.2.2、汇聚节点已激活时

节点0、节点1、节点2的挂起和恢复执行不会影响汇聚节点的状态(依旧处于执行状态);节点0、节点1、节点2的手工结束会影响汇聚节点的状态,每个节点实例的手工结束会引起汇聚节点的判断,如果所有并发的节点实例(包括正常结束和手工结束)都结束,汇聚节点正常结束,触发流程流转。

1.3、用户挂起、手工结束流程的处理策略

1.3.1、汇聚节点未激活时

流程的挂起和恢复执行不会影响并发节点的状态(依旧处于执行状态),节点0、节点1、节点2会被全部挂起或恢复;流程的手工结束会引起所有节点的手工结束。

1.3.2、汇聚节点已激活时

流程的挂起和恢复执行不会影响汇聚节点的状态(依旧处于执行状态),节点0、节点1、节点2未执行结束的实例会被全部挂起或恢复;流程的手工结束会引起所有节点的手工结束。

2、nOutOfM汇聚(图1

根据情况触发节点0、节点1、节点2中的一个或多个,汇聚节点等待N个实际触发的节点完成后即执行流转(N>0N<MM为实际触发的节点个数),在N<=0N>=M的情况下即为同步汇聚。中间汇聚节点只会产生一个实例。

2.1、正常流转时的处理策略

当汇聚节点未被触发时(即节点0、节点1、节点2都未执行结束),并发节点处于执行状态,一旦汇聚节点被触发(即节点0、节点1、节点2有一个执行结束),并发节点正常结束并且汇聚节点处于执行状态,N个并发出的节点实例执行结束后,汇聚节点正常结束,流程继续流转,M-N的节点实例被手工结束。

2.2、用户挂起、手工结束相关节点的处理策略

2.2.1、汇聚节点未激活时

节点0、节点1、节点2的挂起和恢复执行不会影响并发节点的状态(依旧处于执行状态);节点0、节点1、节点2的任一手工结束都会触发汇聚节点,使并发节点正常结束,如果N个并发的节点实例都手工结束,并发节点正常结束,触发汇聚节点,汇聚节点正常结束,触发流程流转,M-N的节点实例被手工结束。

2.2.2、汇聚节点已激活时

节点0、节点1、节点2的挂起和恢复执行不会影响汇聚节点的状态(依旧处于执行状态);节点0、节点1、节点2的手工结束会影响汇聚节点的状态,每个节点实例的手工结束会引起汇聚节点的判断,如果N个并发的节点实例(包括正常结束和手工结束)都结束,汇聚节点正常结束,触发流程流转,M-N的节点实例被手工结束。

2.3、用户挂起、手工结束流程的处理策略

2.3.1、汇聚节点未激活时

流程的挂起和恢复执行不会影响并发节点的状态(依旧处于执行状态),节点0、节点1、节点2会被全部挂起或恢复;流程的手工结束会引起所有节点的手工结束。

2.3.2、汇聚节点已激活时

流程的挂起和恢复执行不会影响汇聚节点的状态(依旧处于执行状态),节点0、节点1、节点2未执行结束的实例会被全部挂起或恢复;流程的手工结束会引起所有节点的手工结束。

3、辨别汇聚(图1

nOutOfM汇聚的特例,N=1

4、多实例汇聚(图2


2

根据情况触发节点0、节点1中的一个或多个,节点0和节点1任意一个执行结束后都会触发汇聚节点产生一个新的实例,汇聚节点实例紧接着触发节点2,节点2也会产生多个实例。

4.1、正常流转时的处理策略

当汇聚节点未被触发时(即节点0、节点1都未执行结束),并发节点处于执行状态,一旦汇聚节点被触发(即节点0、节点1有一个执行结束),汇聚节点会紧接着触发节点2,汇聚节点正常结束。所有并发出的节点实例执行结束后,并发节点正常结束。

4.2、用户挂起、手工结束相关节点的处理策略

节点0、节点1的挂起和恢复执行不会影响并发节点的状态(依旧处于执行状态);节点0、节点1的手工结束会影响并发节点和汇聚节点的状态,每个节点实例的手工结束会引起汇聚节点产生新的实例并触发节点2,同时会引起并发节点的判断,如果所有并发的节点实例都手工结束,并发节点正常结束。

4.3、用户挂起、手工结束流程的处理策略

流程的挂起和恢复执行不会影响并发节点的状态(依旧处于执行状态),节点0、节点1、节点2会被全部挂起或恢复;流程的手工结束会引起所有未结束节点的手工结束。

5、隐式结束,没有汇聚节点与并发节点对应(图3


3

所有节点结束时(正常结束或手工结束),并发节点正常结束。流程的手工结束会引起所有未结束节点的手工结束。

四、流程实例状态变化对节点实例状态造成的影响

1、流程实例的挂起

   A类节点挂起,BC类节点不受影响。同时在流程实例恢复执行之前,A类节点不允许用户直接恢复执行。

2、流程实例的手工结束

   所有节点全部手工结束。

五、节点实例状态变化对流程实例状态造成的影响

隐式结束的情况下,节点的手工结束或正常结束都会触发流程的判断,如果所有的节点都已结束则流程结束。

posted @ 2008-05-26 19:36 ronghao 阅读(1594) | 评论 (2)编辑 收藏
用户的需求大概分为两部分:一部分是整个项目完全基于工作流来搭建开发,这也是很多工作流厂商患有“平台压迫症”的原因;另一部分是将工作流作为业务组件加入已有的项目中,推动业务的“审批”流转。

前者的要求显然更高,但也意味着有更多的利润。其实这一部分的用户又可以进一步的细分:一是技术能力比较差的公司,他们通过层层外包接到项目,而又没有实力自己开发,于是想通过采购工作流加上几个刚入门的程序员来完成整个项目的开发(这类用户往往也是业务平台最大的客户群),他们想着是一整套的开发解决方案,甚至包括业务分析;二是对业务编程的需求,他们需要流程引擎能够侵入业务编程的内部,对业务的状态和生命周期进行灵活的管理,从而最大程度的简化开发或者说满足一些复杂业务编程的需要。

后者的需求则比较简单,多是某一行业的项目公司,突然碰到有审批的需求了,采用工作流多是满足人工“审批”的需要,以及部分的统计分析。

需要承认,工作流其实与最终用户还差得很远,也就是说在众多厂商的网页上,那副著名的业务流程生命周期其实是一句空话。一句话说,就是那个什么流程设计器是给程序员用的,至于用户,哪凉快哪去。也就是说现在的工作流还不能给最终用户提供价值。OK,既然工作流的价值是提供给集成商的,集成商就会考虑成本,于是工作流能否提供一个完整的开发解决方案就成了最重要的考量。

最后说说市场。工作流其实有着很大的市场,只不过这个市场被开源工作流和平台瓜分掉了。因为目前的工作流不能给最终用户提供价值,所以集成商在遇到审批的需求时,首先想到的会是开源的工作流引擎,从jbpm、osworkflow的流行也可以看出这一点,并且知识的积累确实比购买工作流来的划算,同时很多公司通过积累也会有自己的流程组件,这并没有太大的难度。难度留给技术能力一般的公司,他们首先想到的会是一整套解决方案而不仅仅止于流程服务,于是平台出现了,平台说:“灰壳显灵,银弹来了。”

关于平台,有一个很时髦的流行词汇,叫“业务应用基础平台”,稍候待续。
posted @ 2008-05-08 17:49 ronghao 阅读(2787) | 评论 (3)编辑 收藏
py工作流是国内比较好的工作流之一。大概看过它的一些文档,分析一下。
1、路由模型
 py支持的工作流模式其实并不多,只是支持1到7七种模式而已,其中比较重要的是模式6和模式7,即M选N分支和M选N聚合,看过它的实现,利用转移线条件来触发转移线,从而触发后续的节点。这样做比较简单,但是同时也存在很多问题,例如在路由非常复杂的情况下,例如多个分支节点的串联,以及并发路由存在多个节点时,这种做法实现起来就非常困难。另外,并发路由的工作流变量会存在相互冲突的情况,也包括业务数据的冲突。可以说py的路由模型还是很简单的,支持简单的业务可能没有问题,对于复杂的业务可能需要很多其他额外的办法。当然,很多国内的工作流甚至连模式6和模式7都支持不了,同时工作流的应用目前还具有很浓的“审批”的影子(貌似有人很讨厌审批这个说法),所以目前的路由模型应该满足需求了。
2、任意路由和回退
 没有看到任意路由和回退的复杂示例。关于任意路由,产品说明中说到可以在整个流程范围内任意自由路由,我觉得这个说法本来就是有问题的,并发路由的情况下,并发支线往主线上跳转,这种情况会有很多问题存在,其他并行的支线如何处理?或者说根本就没有考虑到这些复杂的情况?回退也是一样的道理,至于业务补偿的提出还是不错的,不过推给了用户自己设置回退动作。
3、关于WFMC和BPEL规范
 看看流程定义文件就知道了,它不支持任何规范。敢说国内工作流的流程定义就没有遵循规范的。
4、参与者的指定
 提供了组织机构、角色、个人这三种常见的参与者设置模式,还提供了流程启动者、活动执行者、从相关数据或从规则逻辑中获取参与者的模式。
5、工作的代理和代办
6、时间服务
 提供了四种时限。活动提醒、活动执行、流程提醒、流程执行。
7、业务开发
 感觉这是非常出彩的地方,在一个简单的示例中几乎不需要任何编码,比如一个简单的请假管理。看看它的流程定义文件,它几乎将整个业务表单都嵌入到流程定义里去了。这样做是否合适?我个人倾向于引擎与业务完全分开,通过反射或者某种映射将两者关联到一起。如果是用户自己开发已有的复杂业务,如何将工作流嵌入?至于studio也是非常出色的,具有开发调试的功能。调用接口非常的清晰。
总结一下:py工作流还是一个不错的工作流引擎,抛开它的宣传,感觉引擎的实现还是有些简单,或者说只是满足了目前的一些常见需求,至于所说的SOA和服务编排,我觉得目前还不现实。它的优势在于与其平台的完全融合,能够利用很多既有设施,可是这又何尝不是把双刃剑?另外,强大的市场宣传和良好的服务团队也是选择工作流时的重要考虑。
posted @ 2008-05-06 17:21 ronghao 阅读(2042) | 评论 (3)编辑 收藏
委办是什么?即分发给A的工作项可以委派给B代为进行处理。委办只针对个人。委派给组织或岗位似乎没有意义。
一、委办的分类
 1、用户单一工作项的委办以及收回委办
 2、用户所有工作项的委办,全权委办
 3、用户按流程划分工作项的委办,基于模板的全权委办,也可以理解为基于业务的委办
二、委办的触发与终止
 1、对于单一工作项的委办,在待签收和待办工作项列表需要出现委办的功能按钮,由用户选择其他用户代为办理。工作项委办后进入委办工作项列表,用户可以收回委办,同时用户和被委办人都可以对该工作项进行办理,用户自己处理则工作项自动被收回委办。
 2、对于全权委办以及基于模板的全权委办,需要委办申请单。用户通过填写委办申请单,将某段时期内工作项列表的处理工作委派给他人。消息通知:当用户将工作委派给指定的被委办人时,被委办人可以收到提醒消息。
 3、委办的自动终止以及手动结束:当委托的时间到期时,委办功能自动终止,委办申请记录将只读。用户也可以手动结束委托,逻辑删除或对委办申请记录进行修改。维护委办申请列表。
三、委办工作项列表
 1、用户可以在委办列表里对委办的工作项状态进行跟踪,对于还未被被委办人签收的工作项可以收回或直接办理
 2、被委办的工作项进入委办人的委办列表
 3、被委办的工作项按状态进入被委办人的待签、待办和办结列表,注明是被委办即可
 4、委办工作项的再委办,工作项增加委办的说明字段,委办工作项的依次状态影响
四、其他
 1、提交工作项页面,选择用户出现委办人时,名字红现,括弧注明其将被委办的被委办人
 2、提交工作项页面,选择部门或角色包含委办人时,不做处理。引擎生成工作项时做出处理,对工作项做出委托说明
 3、流程跟踪列表,增加委办说明字段
posted @ 2008-04-07 18:28 ronghao 阅读(1558) | 评论 (1)编辑 收藏
既然是与用户相关的权限,那么权限的表现则一定与UI紧密相连。工作流管理系统里,用户与工作流的交互界面有四种:
1、流程设计器
    流程设计器的功能比较单一:定义或更新流程定义。里面涉及到包、模板和版本的概念。资源即流程模板(例如发文模板、收文模板),权限可以细分为:维护、只读以及不可见。
2、流程管理控制台
  对流程实例(包括活动实例和工作项实例)进行管理。这里对资源的划分有两种方式:操作和数据。从操作来分比较琐碎,例如:流程实例的挂起、终止、恢复、跳转,活动实例的挂起、终止、恢复等等,当然可以做一种集合,例如:对流程实例的管理、对活动实例的管理、对工作项实例的管理、时间服务的管理等等。从数据划分则很好理解,例如:发文的流程实例、收文的活动实例等等。两种方式的组合构成最终的权限。
3、工作项列表
    这个似乎没什么好说的,工作项直接分配到用户、部门和岗位。
4、与流程相关的业务数据
    用户对业务自身的权限以及不同流程节点对业务的权限。看问题的两种方式。业务数据处于流程中时由流程决定权限,例如拟稿时可以操作哪些字段,审批时是否可以上传附件等等。流程结束后,业务数据归档,此时的权限由业务权限+流程权限组合。简单的一个例子:普通用户A可以在发文模块里看到自己参与过的所有发文文件,发文管理员B则可以看到发文模块里所有的发文文件。
    
结合具体的业务需求:
1、主控岗位的提出。例如发文管理,存在主控岗位,可以对所有的发文流程进行管理,催办、督办等等。
2、大集中模式下对数据的再划分。还是以发文管理为例,北京公司的发文管理员对北京的发文数据进行管理,上海公司的发文管理员则只能对上海的发文数据进行管理。

最终的权限分类:
1、流程设计器里流程模板的可见与不可见。可见即可维护。
2、流程管理按操作来分显得没有实际的意义,用户关注的是业务数据即操作的范围。流程实例(活动实例)的可见与不可见。可见即可操作。更进一步说,用户甚至根本都不会登录到流程管理控制台,他会倾向于在业务菜单里有自己相应的流程管理功能,例如在发文管理里增加发文催办、督办等等。
3、不用
4、往业务权限表里增加流程参与者的权限信息。

总结
:总是感觉工作流管理部分的权限不是那么的必要,流程定义的复杂度让最终用户很难直接使用,流程实例的管理更多的是契合到业务中去,而这种契合表现则是流程数据按业务进行划分后的管理。
posted @ 2008-03-08 16:31 ronghao 阅读(1437) | 评论 (0)编辑 收藏

DOM事件标准定义了两种事件流,这两种事件流有着显著的不同并且可能对你的应用有着相当大的影响。这两种事件流分别是捕获和冒泡。和许多Web技术一样,在它们成为标准之前,Netscape和微软各自不同地实现了它们。Netscape选择实现了捕获事件流,微软则实现了冒泡事件流。幸运的是,W3C决定组合使用这两种方法,并且大多数新浏览器都遵循这两种事件流方式。

默认情况下,事件使用冒泡事件流,不使用捕获事件流。然而,在Firefox和Safari里,你可以显式的指定使用捕获事件流,方法是在注册事件时传入useCapture参数,将这个参数设为true。下面用个例子分别来测试这两种事件流。

1、冒泡事件流
当事件在某一DOM元素被触发时,例如用户在客户名字节点上点击鼠标,事件将跟随着该节点继承自的各个父节点冒泡穿过整个的DOM节点层次,直到它遇到依附有该事件类型处理器的节点,此时,该事件是onclick事件。在冒泡过程中的任何时候都可以终止事件的冒泡,在遵从W3C标准的浏览器里可以通过调用事件对象上的stopPropagation()方法,在Internet Explorer里可以通过设置事件对象的cancelBubble属性为true。如果不停止事件的传播,事件将一直通过DOM冒泡直至到达文档根。

测试的HTML文件,其中用到了mootools-release-1.11.js,对mootools的代码进行了改动:
addListener: function(type, fn,setCapture){
            
if (this.addEventListener) this.addEventListener(type, fn, setCapture);
            
else {
                
this.attachEvent('on' + type, fn);
                
if (setCapture) this.setCapture(true);
            }
            
return this;
        }
     
给addListener方法里增加了setCapture参数,用于测试捕获事件流。
<body>
<div  id="dd1-ct" style="width:400px;height:400px;border:1px solid #999;padding:2px">Container
    
<div id="dd1-item1" style="width:200px;height:200px;border:1px solid #999;padding:2px">Item1
        
<div  id="dd1-item2" style="width:100px;height:100px;border:1px solid #999;padding:2px">Item2</div>
    
</div>  
</div>
<div id='rh'></div>
</body>

效果:

js:
fn1=function(e){
//    e.stopPropagation();
    $('rh').innerHTML+='Item1 clicked!******';
};

fn2
=function(e){
//    e.stopPropagation();
    $('rh').innerHTML+='Item2 clicked!-------';
};

fn
=function(e){
//    e.stopPropagation();
    $('rh').innerHTML+='Container clicked!&&&&&&&&';
};
   
$('dd1
-item2').addListener('click', fn2.bindWithEvent(),false);       
$('dd1
-item1').addListener('click', fn1.bindWithEvent(),false);
$('dd1
-ct').addListener('click', fn.bindWithEvent(),false);


测试结果ie和ff下效果一致:单击item2,会依次触发fn2、fn1、fn;单击item1,会依次触发fn1、fn;单击Container,只会触发fn;当在任何一个事件处理器里调用e.stopPropagation();都会阻止事件的冒泡。

2、捕获事件流
事件的处理将从DOM层次的根开始,而不是从触发事件的目标元素开始,事件被从目标元素的所有祖先元素依次往下传递。在这个过程中,事件会被从文档根到事件目标元素之间各个继承派生的元素所捕获,如果事件监听器在被注册时设置了useCapture属性为true,那么它们可以被分派给这期间的任何元素以对事件做出处理;否则,事件会被接着传递给派生元素路径上的下一元素,直至目标元素。事件到达目标元素后,它会接着通过DOM节点再进行冒泡。

这里ie与ff存在着很大的差异,甚至ie6与ie7的表现也各不相同,所以分开测试。

a、ff
事件从从DOM层次的根开始往下传递时,会被useCapture属性为true的事件监听器所捕获,而到达目标元素再从目标元素冒泡时,则会被useCapture属性为false的事件监听器所捕获。当在任何一个事件处理器里调用e.stopPropagation();都会阻止事件的传播。

b、ie6
用事实说话:

第一种情况:
$('dd1-item2').addListener('click', fn2.bindWithEvent(),true);       
$('dd1
-item1').addListener('click', fn1.bindWithEvent(),true);
$('dd1
-ct').addListener('click', fn.bindWithEvent(),true);

单击浏览器的任何位置,都只是触发fn;

第二种情况:
$('dd1-item2').addListener('click', fn2.bindWithEvent(),true);       
$('dd1
-item1').addListener('click', fn1.bindWithEvent(),true);
$('dd1
-ct').addListener('click', fn.bindWithEvent(),false);

单击浏览器的任何位置,会依次触发fn1、fn;

第三种情况:
$('dd1-item2').addListener('click', fn2.bindWithEvent(),true);       
$('dd1
-item1').addListener('click', fn1.bindWithEvent(),false);
$('dd1
-ct').addListener('click', fn.bindWithEvent(),false);

单击浏览器的任何位置,会依次触发fn2、fn1、fn;

结论:如果HTML元素捕获了通过该元素的setCapture()方法对这个元素的设置,依附于该元素的处理器将会被事件触发,即使setCapture()方法
被调用的这个元素不在目标元素的祖先路径中。事实上你甚至单击浏览器的非页面部分都会触发事件处理器。并且事件一旦被捕获就不会继续再
往下传播(即使该元素在目标元素的祖先路径中),而是立刻冒泡。e.stopPropagation();会阻止事件的冒泡。
 
c、ie7
测试效果与冒泡事件流一致。将对捕获事件流的支持干掉了?
 
结论:正如mootools所做的,避免捕获事件流。


posted @ 2008-03-02 14:54 ronghao 阅读(1996) | 评论 (0)编辑 收藏
工作流现在已经应用的非常广泛了,审批OA等等自然不必多说,许多业务系统里也有大量的应用。前两天的
一个项目就是使用工作流将整个项目管理的过程进行整合,包括了前期预算、项目进度管理、合同管理等等。
可供选择的工作流也很多,商业的、开源的。那么你是如何评价一个工作流产品的好坏的呢?你的标准是什么?
当然,用户也经常会问我这个问题,我的回答是:根据你实际的业务。是的,不管是什么样的工作流,都是
为了满足业务的需要,你把你的需求提出来,我们看看是否满足,不能直接满足,最合适的间接方式又是什么。你说,我要有催办。是的,我们有。你说,我要有任意回退和任意流。是的,我们有。你说,我想对流程实例进行分级管理。oh,没有也,重要吗?让我们想想其他办法。你说,你们符合BPEL标准吗?这个。。。你说,你们采用了petri网模型吗?汗。。。你说,你们是SOA架构吗?。。。
我的衡量标准是这样的:
1、流转功能
  包括了基本的工作流模式实现,串行、并发、分支、汇聚、循环等等。这个是最基本的。其实打开流程设计器拖拖拽拽很快就能知道这个产品到底实现了哪些流转模型。实际这个的实现也是引擎的核心。有多种模型可以选择。petri 模型应该是最灵活的了,也有很大的实现难度。但是流程模型做这么灵活,到底实际能用上多少……就我个人的经验来说,大部分的复杂性都是由流程的分支并发(m/n)引起的,最坏的办法是强制要求客户将这些并发的任务改成 step by step 的执行。这样牺牲一点效率,还是可以把项目做成的。
2、业务的内在支持
  比如说催办、时间服务、收回等等。我觉得这个与实际业务挂钩,反而是最为主要的考虑。因为采用间接的方式必然会产生编程,而很显然会耗费成本。
3、与业务的契合方式
  流程维护流转。业务还是自己实现。如何将这两者很好的衔接起来。同时这个过程还存在权限的限定,每个运行节点对业务的权限肯定存在差别,是否有一套完整的解决方案?当然这其中也包括了组织机构的适配,对各种组织模型的支持。
4、定义良好的API
  通常会存在工作流无法直接满足的业务场景,那么肯定需要程序直接调用工作流的API,清晰且简洁的API。
5、流程的仿真
  这种仿真比较简单,目的在于检验所定义的流程是否正确。出错要有明确的提示信息。普元的单点调试?
6、电子表单
  我始终觉得电子表单目前实际应用并不理想,它仅仅只能处理简单的业务。但是销售的经验告诉我,这是一个巨大的闪光点。用户喜欢自己动手。流程定义实际最终用户很难实际操作。我在想:简化版本的流程设计器+电子表单也许会有很好的售前效果。
7、良好的售后
8、良好的最终用户体验
9、性能
10、最好能够和标准扯上关系,可是谁知道我是否真的有关系呢?
posted @ 2008-02-22 15:02 ronghao 阅读(2876) | 评论 (5)编辑 收藏
这是一个完全基于js的应用程序,区别于一般的web应用,它是oaop。大概需要一些什么样的工作呢?

大概的概念:

1、容器
 是的,需要容器。容器的两个目的:布局和管理组件。组件之间的相互通信以及影响都需要容器来协调。管理组件之间的状态,组件需要向容器进行注册。对组件传播过来的事件,容器需要做出处理,调用相关其他组件的方法或者忽略或者继续向上一级容器传播。

2、组件
 组件的目的是完全屏蔽对dom编程的依赖,同时屏蔽底层的浏览器事件,例如鼠标单击、双击等等,对这些事件进行完全的封装。组件有着自己的生命周期:初始化、渲染、重画、销毁等等。由组件完成页面的渲染工作,例如节点、画板、连线等等。

3、模型
 在页面进行建模是必要的,例如活动节点、流程等等,这些模型与组件衔接,它们之间的状态互相影响,比如节点组件名称的改变实际影响的是所对于节点模型的属性。画板节点的增加实际也会给响应的流程定义模型新增一个活动节点。

4、与服务器交互
 与服务器的交互完全基于xml。流程定义模型有着自己的xml方法,由xml解析为模型,由模型解析为xml,双向的过程。本地存储。很自然的选择。
 
可能的难点:
最大的难点就是组件的实现,事件的处理以及传播机制。

开发的过程:

1、底层库的选择
 面向对象的开发方式,底层库需要完成的工作:继承、接口实现、事件的统一处理接口、element DOM编程的封装。

2、基本组件的开发
 画板、图形组件、连线组件、弹出面板、简单表格组件、树等等。封装基本的事件。可以定制事件。

3、容器的开发,管理组件
 组件实际也是容器的实现,比如画板的概念。画板中节点之间的互相影响。

4、加入模型的支持

5、xml与模型之间的js解析

参考:
ext是一个不错的参考,但是太笨重,功能越多越缓慢,轻量实现。主要参考其中容器以及组件的概念。
draw2d 实现太简单,基本就是一个图形库,考虑其中对图形组件的实现。
posted @ 2008-02-13 22:08 ronghao 阅读(2972) | 评论 (4)编辑 收藏

内容



致谢
关于作者

第1章 AJAX和富互联网应用

    转变中的Web
       传统Web应用的痛处
       AJAX止痛药
    企业中的AJAX
    采用AJAX的驱动因素
       可用性
       网络利用率
       以数据为中心
       递增的技巧、工具和技术升级
       服务器不可知论
    关于应用
       AJAX技术
       编程模式
    AJAX的替换技术
       XUL
       XAML
       Java Applets 和Web Start
       Adobe Flash,Flex和Apollo
       OpenLaszlo
    小结
    资源

第2章 AJAX组成技术(AJAX Building block)

    JavaScript
       JavaScript类型
       闭包
       面向对象的JavaScript
       Prototype属性
       面向对象编程(OOP)和继承(Inheritance)
       易变性(Mutability)
       线程(Threading)
       错误处理(Error Handling)
       命名空间(Namespacing)
    文档对象模型(Document Object Model)
       基本原理
       操作DOM
    层叠样式表
       继承和层叠(Inheritance and the Cascade)
       内联样式
       样式表
       动态样式
    事件
       事件流
       事件绑定
       跨浏览器事件
       事件对象
    客户端通信(Client-Side Messageing)
       XMLHttpRequest基础
       处理数据
    小结
    资源

第3章 Web浏览器中的AJAX

        增量的AJAX
        对服务器影响
    HTML标准
       文档类型定义(Document Type Definitions)
       盒子模型
    启动加载AJAX组件
       onload事件
       浏览器编码技巧
    模型-视图-控制器
       视图
       控制器
       模型
    AJAX MVC
       AJAX模型
       AJAX视图
       AJAX控制器
       面向方面的JavaScript
    小结
    资源

第4章 AJAX组件

    命令式组件
    声明式组件
       服务器端声明式编程
       声明式Google地图
       替代方法

    自定义声明式组件
       行为式组件
       声明式组件
       关于声明
    构建组件
       基本功能
       连接到服务器
       最终版本
    小结
    资源

第5章 从设计到部署

    设计
        为AJAX建模
        应用模型-视图-控制器模式
        优先考虑性能问题
    设计原型
       线框绘制
       验证设计决议
    测试
       测试驱动开发
       调试
    部署
       JavaScript压缩
       图片合并
       保护知识产权
       文档
    小结
    资源

第6章 AJAX架构

    多层架构:从单层到多层
    异步消息
    轮询
    服务器推送
       Comet
    跟踪请求
    缓存:处理数据
    基本缓存
    在组件中缓存
    在浏览器中缓存
    在服务器中缓存
    在数据库中缓存
       MySQL
       MS SQL Server
       Oracle
    更新服务器端模型:并发
       悲观锁定
       只读锁定
       乐观锁定
       冲突鉴定
       冲突解决
       自动化的冲突解决
    流量控制(Throttling)
       客户端
       服务端
    可伸缩性
       负载平衡和群集
       AJAX可伸缩性问题
    离线AJAX
    Firefox离线存储
    Internet Explorer userData离线存储
    使用Flash客户端存储
    离线AJAX和并发
    小结
    资源

第7章 Web服务和安全性

    Web服务
    Web服务协议
       表象状态传输
       XML远程过程调用
       Web服务
       选择合适的工具
    客户端的SOAP
       IBM Web服务JavaScript库
       Firefox
       Internet Explorer
    跨域Web服务
       服务器代理
       URL片段标识符
       Flash跨域XML
       脚本注入
    安全性
    AJAX的安全性考虑
    跨域漏洞
       跨站脚本
       跨站伪造请求
       JavaScipt劫持
   SQL注入
       预处理语句
       存储过程
       XPath注入
    数据加密和隐私
    防火墙
    小结
    资源

第8章 AJAX可用性


    常见问题
       后退按钮和书签
       页面大小
       自动提交
    可访问性
       识别用户的可访问性需求
       JavaScript和Web可访问性
       屏幕阅读器和可访问性
       兼容JAWS的AJAX交互
       键盘可访问性
    可用性测试
    迅速而又随性的测试
       招募参与者
       设计和运行测试
    软件断言测试
       用于测试可用性的工具
       对软件辅助测试的一般忠告
    小结
    资源

第9章 用户界面模式

    显示模式
       动画模式
    交互模式
       基本交互模式
    小结
    资源
       拖拽资源
       进度栏资源
       活动指示器资源
       颜色淡出资源
       即时编辑资源
       向下钻取资源
       即时搜索资源
       即时表单资源

第10章 风险和最佳实践

      
       风险来源
          技术风险
          文化和政治风险
          市场风险
       技术风险
          范围
          浏览器能力
          可维护性
          向前兼容
          第三工具支持和代码过时
       文化和政治风险
          终端用户的期待
          可培训性
          合法性
       市场风险
          搜索引擎的可访问性
          范围
          货币化
       风险评估和最佳实践
          采用特定的AJAX框架或者组件
          渐进增强和不唐突的JavaScript
          Google 网站地图
          可视化的提示
          避免镀金式设计
          采用一种收益模型
          把培训作为应用的一部分
       小结
       资源
          搜索引擎优化
          统计
          网站地图
          屏幕截取工具


第11章 案例研究

    基于Web2.0 重新武装美国国防部
       背景
       挑战
       解决方案
       采用技术
       成果
    Agrium公司整合AJAX技术到业务
       背景
       挑战
       解决方案
       采用技术
       成果
    AJAX助力国际运输和物流公司
       背景
       挑战
       解决方案
       采用技术
       成果
    小结
    资源

附录A OpenAjax Hub

    主要特性:发布/注册管理器
       范例
       未来对OpernAjax Hub支持的工具包

索引

posted @ 2008-01-24 18:21 ronghao 阅读(648) | 评论 (0)编辑 收藏
时间真快,一晃08年都过了半个月。一直很忙,也不知道在忙些什么东西。领导催着要份年终总结,于是就有了这个东西。还是分成两个部分:工作和生活。

工作:
1、年初的时候继续业务平台的开发,主要是修改BUG和书写使用文档,公司开始增加测试的岗位,平台也开始有订单,于是BUG像雨后春笋般的茁壮成长出来。实际一直到现在,那份长长的BUG单依旧存在,改不完的BUG。现在有很深的体会:一个好的产品,并不在于采用了多么先进的技术,重要的是要成熟,要贴近业务,而这需要的是时间和大量的项目经验。所以,要是想节约成本采购一个业务平台,首先需要考虑的是这个平台存在几年了?都被哪些实际项目采用过?它面对的业务是什么,是否符合我项目大部分的需求?对于号称采用很多新技术和通用性非常强且刚开发出来的所谓平台一定要慎用,很可能你就成了实验品。

2、业务平台的价值。其实业务平台是有很大的存在价值的。这个价值就是业务。我们公司产品的口号是:释放你的技术,专注你的业务。我认为这个口号相当不妥,正确的应该是:发挥你的技术,我们替你思考业务。业务平台还有生存的意义,至于意义单一的所谓开发平台,我看还是洗洗睡吧。

3、5月份开始进入工作流的开发,一直到现在。对工作流有了很多新的认识。工作流应该算是公司最核心的产品了。代码很臃肿,没有测试,但是分层还是非常的清晰。期间开始增加测试并增加功能,对引擎部分做了部分的重构。中间也陆续做过工作流的售前和售后工作。工作流,感觉是越来越普及了。但是感觉市场却没有想象的那么好,单独的工作流引擎有没有前景?其实可以这样分析:很多行业都有他们自己针对的行业软件公司,这些公司都有自己的业务平台和流程组件,虽然不是独立对外卖的;另外就是中小型企业和客户采用开源的工作流引擎了,这样积累的使用经验可以成为公司的财富,只要把核心人员留住,就不用每个项目都去购买一个商业工作流了,节约成本;可不可以这么认为:商业工作流的市场本来就很小呢?

4、12月到新疆出了趟差,然后就是全力开发新疆的项目了。这是一个大集中的项目,也就是一套系统,所有分公司一起使用,最 紧迫的问题就是组织结构、权限以及数据(包括流程)的分级管理了。现在是业务需求基本都满足,但是可以想象未来性能的压力,工作还会有很多。

个人技术:
1、上半年看过一些敏捷和UML的书籍。敏捷感觉已经是一个被用滥的词语,人人头上都顶着一个敏捷的帽子,就像言必称 spring"hibernate一样,有些恶心了。看过这些书,其实体会最深的还是如何开发出一个大家都能读懂的代码,如何与他人交流,能让别人看懂的代码就是好代码。至于UML,好像也能画上和看懂几个了。

2、最后一个季度和朋友一起翻译了《Enterprise AJAX》这本书,重新对ajax有了兴趣,翻译确实是一件辛苦的事情,但是现在回忆起来却很是觉得有意义。最直接的就是翻译水平有了很大长进,然后就是ajax有了一些新的认识。现在看Ext的代码,想着能否有扩展为类swing框架的可能性。期间参与了满江红组织的seam翻译工作,自己做的工作很少,纯属充数,算是了解了一把seam。

3、我和老婆说,怎么今年就感觉进步没有去年那么明显呢?老婆说,因为你去年起点太低!于是释然。

生活:
1、年初买了房子了,注明一下:是在老婆强烈要求下买的。地点在燕郊。
2、关注了厦门PX、上海磁悬浮,除去工作,开始关注很多东西,也开始看历史。

08的展望:

1、结婚
2、开发一个基于js的工作流设计器,能否完成?
3、做了3年的开发工作了,工作上能否有新的挑战?
4、多看点非技术书,多出去转转,善待自己和亲人。
posted @ 2008-01-18 16:47 ronghao 阅读(780) | 评论 (0)编辑 收藏
事件无疑是AJAX应用中最重要的部分,EXT将事件分为了两种:自定义事件与浏览器事件。

自定义事件
按字面意思就是用户自己定义的事件,这个事件通常与组件相关。并且需要用户根据组件的状态自己触发。相关的类Ext.util.Observable 、Ext.util.Event(Observable.js)。Ext.util.Observable是所有组件(component)的父类,它使得所有的组件都可以任意的添加自定义事件,它的events属性对事件进行维护,这些事件实际都是Ext.util.Event对象,Ext.util.Event对象里的listeners属性对与该事件相关的处理器进行维护。Observable给所有的子类提供了一个统一的接口来发布事件以及管理事件,这一特性对于组件来说是至关重要的。

浏览器事件
即传统意义上的鼠标单击、移动等等事件,这些事件是由浏览器根据用户的动作自己触发的,与页面元素紧密关联。相关的类Ext.Element 、Ext.EventManager、Ext.EventObject、Ext.lib.Event 。Element包含了常见的DOM方法和属性,提供一个快捷的、统一的、跨浏览器的接口,内置了常用的DOM节点的动作,并且是跨浏览器的定位的位置、大小、动画、拖放等等。对事件的处理,Element实际将这一处理委托给了EventManager,由EventManager对页面所有的浏览器事件进行管理,例如增加事件处理器、移除事件处理器等等,另外EventManager还定义了几个很重要的方法:onDocumentReady、onWindowResize、onTextResize。其中onDocumentReady尤为重要,通常在页面需要通过它来启动我们的AJAX程序,它会在页面document渲染完毕而图片等还未下载时调用我们的启动函数。至于EventObject,它则是对原始的浏览器事件进行了封装,提供给事件处理器一个统一一致的事件接口。Ext.lib.Event呢?EventManager的很多功能其实是调用它完成的,它的listeners、unloadListeners维护着所有的事件处理器。
posted @ 2008-01-17 15:17 ronghao 阅读(3346) | 评论 (0)编辑 收藏
正在翻译《Enterprise AJAX》,附录里介绍到OPENAJAX HUB,google相应的中文资料很少,所以就把译文贴出来:)

OpenAjax Hub(“Hub”)主要用来处理Web应用开发者需要在同一个应用中同时使用多个AJAX运行库的情况。它提供标准的JavaScript,当被包含在AJAX驱动的Web应用里时,它使得多个AJAX工具包能够在同一个页面里一起协同工作。

AJAX应用开发者在开发中的需求往往存在着巨大的差异,这导致了如今市场上存在超过200个各种各样的AJAX产品,同时这些产品的架构和特性也存在巨大的差异。对一些开发者来说,他们认为开发中最重要的因素是找到一个能提供与后端服务器强大集成能力的AJAX工具包。

而对其他一些开发者来说,最重要的因素则是能否应用特殊的客户端组件(例如,富数据网格组件或交互式的图表组件)。结果,AJAX生态系统发展到现在,开发者在大部分时间里都能找到满足他们每个特殊需求的AJAX工具包,但是也存在问题,他们往往必须在同一个Web应用里混合和匹配使用多个AJAX工具包才能满足所有的需求。 

Hub应用的一个重要场合是门户和内容糅合,这里,应用开发者创建一个页面,页面里松散组装预先包装好的应用组件。Hub实际上是保证这些AJAX驱动的应用组件能够使用多个不同的AJAX工具包创建。

主要特性:Hub 的发布/订阅管理器

Hub的主要特性是它的发布/订阅管理器(“pub/sub管理器”)。 pub/sub管理器允许内容糅合的一个部分能够传播其他应用组件所订阅的事件。例如,假设存在一个日历组件,该组件允许用户能够选取一个特定的日期。内容糅合里可能存在多个UI组件,这些组件都需要根据新选择的日历日期而更新它们的视觉外观。在这种情况下,日历组件将发布一个“新日历日期”的事件,而其他可视化组件将订阅这个事件。因此,pub/sub管理器的通用消息的优点是给由不同AJAX工具包所构建的组件之间提供了一个关键的集成机制。

Hubpub/sub管理器提供各种各样的先进特性,例如对事件名称通配符的强大支持,在下面的例子里并没有展示这个特性。

范例

让我们假设现在有这样一个商务智能应用,该应用使用下面的AJAX运行库:

UTILS.js, 对浏览器的JavaScript环境提供非常有用的扩展,例如XMLHttpRequestAPIs

CALENDAR.js,提供一个日历组件

CHARTS.js, 提供一个图表组件

     DATAGRID.js,提供一个交互式的数据网格组件

该应用有一个唯一的日历组件,用户可以以图表组件的形式(例如,每日情形、每周情形、每月情形和每年情形的柱状图)和数据网格组件的形式(例如,地方数据与全国数据,两种数据都以用户选择的感兴趣的列展示)选择其中的一些数据视图当一个新的日期在日历组件里被选择时,各个用户指定的可视化组件(例如,图表和/或数据网格组件)都需要被更新。

实现该应用的一个方法是在加载其他AJAX库之前加载OpenAjax HubJavaScript。例如:

<html>

<head>



<script type=”text/javascript” src=”OpenAjax.js”/>

<script type=”text/javascript” src=”UTILS.js”/>

<script type=”text/javascript” src=”CALENDAR.js”/>

<script type=”text/javascript” src=”CHARTS.js”/>

<script type=”text/javascript” src=”DATAGRID.js”/>



</head>


 

一些AJAX运行库包含OpenAjax Hub,将Hub作为它们标准发布的一部分,在这种情况下,只要特定的AJAX运行期的JavaScript(译注:这里特定的JavaScript指的就是包含OpenAjax HubAJAX运行库的代码)在其他兼容OpenAjax的运行库之前被加载,那么则没有必要为OpenAjax.js使用一个单独的<script>元素。

要使应用工作,开发者需要注册一个回调函数,当用户在日历组件里选择一个新的日期时调用该函数。这个回调函数接着使用OpenAjax Hubpublish()函数传播这个新日期事件:

<script type=”text/javascript”>



function MyCalendarCallback() {

OpenAjax.hub.publish(“myapp.newdate”, newdate);

}



</script>
 

接着开发者需要开发这样一些代码:所有的图表组件和数据网格组件都要订阅这个新日期事件,并要提供一个回调函数。各个回调函数将相应地更新特定的可视化组件:


<script type=”text/javascript”>



function NewDateCallback(eventname, publisherData,

subscriberData) {

更新特定的可视化组件

}

OpenAjax.hub.subscribe(“myapp.newdate”, NewDateCallback);



</script>

未来支持OpenAjax Hub的工具包

OpenAjax联盟正与工业界一起合作,达到对OpenAjax Hub的广泛支持。一个特殊的AJAX工具包可以像下面这样支持OpenAjax Hub

AJAX工具包可以包含Hub(最好的方式)。Hub可以被小于3KJavaScript实现,所以一些AJAX工具包简单地捆绑Hub,将它作为它们工具包的一个标准组件。

如果Hub在运行环境里可用则使用它。其他一些AJAX工具包可能决定在它们的发布中并不包含Hub,它们会检查Hub是否早先已经被加载了,如果已经加载,它们则直接使用Hub的服务。 

第三方的开发者可以开发适配器。对大多数工具包来说,它们可能允许第三方的开发者编写少量的JavaScript使得自己能够支持Hub

AJAX工具包包含内置对Hub的支持时,应用开发者的工作将更加容易,但是通过查找或编写适配器的方式,Hub依然可以被那些并未实现支持Hub的工具包所使用。



posted @ 2008-01-01 12:57 ronghao 阅读(2457) | 评论 (4)编辑 收藏


其实本没打算看这部电影来着,其实是等着德甲来着,然而,德甲还没有开始,百无聊赖地那么一按,就看到了《火柴人》,其实如果不是尼古拉斯·凯奇我也早继续换台来着。

“一二三,开门”,习惯性随着短促咳嗽而来的眨动的左眼,洁癖的让人抓狂的种种,呵,真是个烦躁的男人。

故事开始,诈骗亦开始,中间向体育频道换过好几次频道,温吞的故事,毫无新意的诈骗过程,让人没有欲望。哪有什么所谓的大片好看,几次爆炸,追车、美女,眼球已经被牢牢抓住。情况在罗伊第一次去见他的女儿时发生变化,烦躁的罗伊不停地在车里吸着烟,颤抖的夹着烟卷的手,布满烟雾的车厢,一根接着一根的烟卷,飘忽不定找不到落脚点的眼神,看到女儿要走下定决心的追赶,立刻没有犹豫地喜欢上这个犹豫的男人。心想,这肯定是一部充满温情的电影了,一定就是了。因为女儿,他的生活即将打乱,充满生气?抑或是杂乱?不管那种,都得改变。

生活中存在着这样或那样的不安,所以我总是以最大的善意来揣度电影。剧情也在向我希望的方向发展。小女孩让父亲教她去行骗,第一次行骗成功。女孩拿着骗来的300美元开心地关上车门:我们成功了!父亲和她庆祝后却让她把钱还回去:如果不这么做,我算得上是一个父亲吗?呵,温暖。

随后一次更大的诈骗开始,父亲也决定完成这最后一次诈骗重新开始。情节发展再次让我大跌眼镜:这也太easy了吧,虽然被骗的胖男人没有像他们想象的那样在飞机中才发现钱被调了包,而是一路追赶出来,收费站找零钱一节也确实让心里小小激灵了一把,但整个过程非常顺利,顺利得让我觉得索然无味。这应该是一个多么充满惊险和刺激的过程啊,枪战、追逐、飞车、心里悬疑,可惜,没有,没有,还是没有,什么都没有。应该是这样一个激烈的过程,然后是美妙的结局:父亲和女儿美满的在一起,成功隐退,快乐的生活,然后是终场的字幕打起。应该是这样的呵!

女儿枪响的一刹那,我的心咯噔一下:完了,导演该如何结尾呢?我再也想像不到一种合乎逻辑而又符合我心理的结局。悲剧?感人的悲剧?父女情深的悲剧?无论是哪一种,我都不愿意看到。切到体育频道:斯图加特30领先拜仁。切回来,被捕、审讯、医生、密码、接头地点这一切都意味着父亲在劫难逃。作为父亲,能做到就这么多了;然而,作为电影,它显然可以做到更好-一切在打开那扇门的一刹那霍然开朗,谜底像沙子那样刺入人的双眼,让人心疼,原来一切都是假的,一切都是骗局,他们欺骗了罗伊,导演欺骗了我。心里空荡荡的再无着落,像无人的秋千,空在那里荡漾,像突然的人去楼空,只剩满心的空旷。半天回味回来:哦,这是电影,并且是好莱坞的电影呢。

一年后再见,女儿离开,转身说:“I'll see you, dad”。是的,到此为止,over

却还是温暖。我更愿相信最初故事的情节是这样的:父亲诈骗,女儿到来,最初的混乱,亲情,融合,改变,生活一起。可是,可是,如何处理这位父亲的过去,抹不去的过去,而这个过去最直接的代表莫过于他诈骗所得来的钱财了。捐掉?这不很搞吗?自首?你以为是中国电影?于是导演以一场更大的骗局将这些钱将父亲的过去轻松的抹去了。原来是这样。原来是这样呵!女儿还是女儿,父亲还是父亲。“I'll see you, dad”。是的,没有复仇,没有怨恨。

好久没有看过如此温暖的电影了。再补充一下:很喜欢罗伊的眼睛,非常喜欢,焦虑、关爱、犹豫、彷徨、绝望、温情。

posted @ 2007-11-11 22:58 ronghao 阅读(1159) | 评论 (0)编辑 收藏

Solr 是一个可供企业使用的、基于 Lucene 的开箱即用的搜索服务器。对Lucene不熟?那么建议先看看下面两篇文档:

实战Lucene,第 1 部分: 初识 Lucene:http://www.ibm.com/developerworks/cn/java/j-lo-lucene1/

Lucene加速Web搜索应用程序的开发:http://www.ibm.com/developerworks/cn/web/wa-lucene2/

一、 solr介绍

solr是基于Lucene Java搜索库的企业级全文搜索引擎,目前是apache的一个项目。它的官方网址在http://lucene.apache.org/solr/ 。solr需要运行在一个servlet 容器里,例如tomcat5.5solrlucene的上层提供了一个基于HTTP/XMLWeb Services,我们的应用需要通过这个服务与solr进行交互。

二、 solr安装和配置

关于solr的安装和配置,这里也有两篇非常好的文档,作者同时也 Lucene Java 项目的提交人和发言人:

使用Apache Solr实现更加灵巧的搜索:http://www.ibm.com/developerworks/cn/java/j-solr1/index.html

http://www.ibm.com/developerworks/cn/java/j-solr2/index.html

下面主要说说需要注意的地方。

Solr的安装非常简单,下载solrzip包后解压缩将dist目录下的war文件改名为solr.war直接复制到tomcat5.5webapps目录即可。注意一定要设置solr的主位置。有三种方法。我采用的是在tomcat里配置java:comp/env/solr/home的一个JNDI指向solr的主目录(example目录下),建立/tomcat55/conf/Catalina/localhost/solr.xml文件。


<Context docBase="D:/solr.war" debug="0" crossContext="true" >

   
<Environment name="solr/home" type="java.lang.String" value="D:/solr/solr" override="true" />

</Context>

     观察这个指定的solr主位置,里面存在两个文件夹:conf和data。其中conf里存放了对solr而言最为重要的两个配置文件schema.xml和solrconfig.xml。data则用于存放索引文件。

     schema.xml主要包括typesfields和其他的一些缺省设置。

solrconfig.xml用来配置Solr的一些系统属性,例如与索引和查询处理有关的一些常见的配置选项,以及缓存、扩展等等。

上面的文档对这两个文件有比较详细的说明,非常容易上手。注意到schema.xml里有一个

<uniqueKey>url</uniqueKey>

的配置,这里将url字段作为索引文档的唯一标识符,非常重要。

三、 加入中文分词

对全文检索而言,中文分词非常的重要,这里采用了qieqie庖丁分词(非常不错:))。集成非常的容易,我下载的是2.0.4-alpha2版本,其中它支持最多切分和按最大切分。创建自己的一个中文TokenizerFactory继承自solr的BaseTokenizerFactory。


/**

 * Created by IntelliJ IDEA.

 * User: ronghao

 * Date: 2007-11-3

 * Time: 14:40:59

 * 中文切词 对庖丁切词的封装

 
*/

public class ChineseTokenizerFactory extends BaseTokenizerFactory {

    
/**

     * 最多切分   默认模式

     
*/

    
public static final String MOST_WORDS_MODE = "most-words";

    
/**

     * 按最大切分

     
*/

    
public static final String MAX_WORD_LENGTH_MODE = "max-word-length";

    
private String mode = null;

    
public void setMode(String mode) {

             
if (mode==null||MOST_WORDS_MODE.equalsIgnoreCase(mode)

                      
|| "default".equalsIgnoreCase(mode)) {

                  
this.mode=MOST_WORDS_MODE;

             } 
else if (MAX_WORD_LENGTH_MODE.equalsIgnoreCase(mode)) {

                  
this.mode=MAX_WORD_LENGTH_MODE;

             }

             
else {

                  
throw new IllegalArgumentException("不合法的分析器Mode参数设置:" + mode);

             }

        }

    @Override

    
public void init(Map<String, String> args) {

        
super.init(args);

        setMode(args.get(
"mode"));

    }

    
public TokenStream create(Reader input) {

        
return new PaodingTokenizer(input, PaodingMaker.make(),

                  createTokenCollector());

    }

    
private TokenCollector createTokenCollector() {

        
if( MOST_WORDS_MODE.equals(mode))

             
return new MostWordsTokenCollector();

        
if( MAX_WORD_LENGTH_MODE.equals(mode))

             
return new MaxWordLengthTokenCollector();

        
throw new Error("never happened");

    }

}

在schema.xml的字段text配置里加入该分词器。


<fieldtype name="text" class="solr.TextField" positionIncrementGap="100">

            
<analyzer type="index">

                
<tokenizer class="com.ronghao.fulltextsearch.analyzer.ChineseTokenizerFactory" mode="most-words"/>


                
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>

                
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0"/>

                
<filter class="solr.LowerCaseFilterFactory"/>


                
<filter class="solr.RemoveDuplicatesTokenFilterFactory"/>

            
</analyzer>

            
<analyzer type="query">

                
<tokenizer class="com.ronghao.fulltextsearch.analyzer.ChineseTokenizerFactory" mode="most-words"/>               

                
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>

                
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>

                
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0"/>

                
<filter class="solr.LowerCaseFilterFactory"/>

                
<filter class="solr.RemoveDuplicatesTokenFilterFactory"/>

            
</analyzer>

        
</fieldtype>

</types>

    完成后重启tomcat,即可在http://localhost:8080/solr/admin/analysis.jsp

体验到庖丁的中文分词。注意要将paoding-analysis.jar复制到solr的lib下,注意修改jar包里字典的home。

四、 与自己应用进行集成

Solr安装完毕,现在可以将自己的应用与solr集成。其实过程非常的简单,应用增加数据-->根据配置的字段构建add的xml文档-->post至solr/update。

应用删除数据à根据配置的索引文档唯一标识符构建delete的xml文档-->post至solr/update。

检索数据à构建查询xml—>get至/solr/select/-->对solr返回的xml进行处理-->页面展现。

具体的xml格式可以在solr网站找到。另外就是solr支持高亮显示,非常方便。

关于中文,solr内核支持UTF-8编码,所以在tomcat里的server.xml需要进行配置

<Connector port="8080" maxHttpHeaderSize="8192" URIEncoding="UTF-8" …/>

另外,向solr Post请求的时候需要转为utf-8编码。对solr 返回的查询结果也需要进行一次utf-8的转码。检索数据时对查询的关键字也需要转码,然后用“+”连接。


String[] array = StringUtils.split(query, null0);

        
for (String str : array) {

            result 
= result + URLEncoder.encode(str, "UTF-8"+ "+";

        }
posted @ 2007-11-06 18:03 ronghao 阅读(67309) | 评论 (14)编辑 收藏

也许是流程做多了的缘故,所以看起程序开发来一切都是流程或者说都包含流程。个人认为大多数的企业应用(不包括特殊应用,例如文档库、信息资源库、BBS等等)不过是对数据以一定的样式展现(表单),以一定的逻辑对数据进行操作(业务规则),以及把这些处理数据的过程以一定的流程进行管理(流程)。上面三个方面分别对应着表单、业务规则和流程。程序开发中则对应于表单引擎、规则引擎和工作流引擎。而这些方面又可以统一到一个更大范畴的流程上来,所以这里有对流程驱动开发的设想。

先来看看具体的应用场景。

单表增删改查

这是最简单的情形,也没有流程,对这个情形不加讨论。但是这里会提到表单引擎,VB里的数据控件非常的易用,没有PO,没有DAO,也没有Service,直接与数据库字段进行绑定。我们的表单引擎也可以采用这种方式。

支持表单控件(输入框、文本框、下拉框等)的拖拽,将整个表单与数据库表绑定。


表单控件与数据库字段的绑定。


单表业务+流程

比上面的情况稍微复杂一点点,也就是要在业务里引入流程,其实这也是现在工作流引擎应用最多的地方,比如说政府OA里的收文、发文。

这里只需要将表单与流程进行绑定,表单引擎的处理方式不变,依然是直接与数据库表进行绑定。表单负责对数据库里的业务数据进行展现,工作流则负责推动这些数据在业务意义上状态的转换,互不影响,并在需要的时候在自动节点上对这些数据进行相应的业务处理。


关于表单权限。这个也是表单与工作流进行绑定时所必须考虑到的问题。其实只是需要在表单引擎里引入权限角色的概念,每个角色对应于一种权限,这种权限具体说来就是表单里每个字段的可见、可编辑等等。然后在人工节点定义时指定表单权限角色即可。这样也实现了流程与表单权限一定程度上的解耦。


其实还有一种更方便的方式,将表单直接与人工节点进行绑定,每个人工节点对应于不同的表单。

复杂一点,多表关联的情况

复杂一点的情况是业务往往是多表的关联。这需要对表单引擎做出扩展,让它可以根据关联字段对关联表做出查询,得到关联表的设计结构,继续映射。这让我想起了ORM,这里很有FRM的意思在里面。其实对于常用的关联查询往往有通用的组件可用,例如根据userid渲染出用户名,根据数据字典的id关联渲染出相应的值,oa里的正文、附件、印章等等。

流程跨越多个业务

流程需要跨越多个业务,一个典型的流程如下:


会议审批会涉及到两张业务表:会议室使用表,会议记录表。在会议申请和领导审批节点,最终用户打开的都是会议申请的表单,对应于会议记录表,对该表进行操作。但是流程运行到会议室管理员安排会议室的节点,该节点最终用户不仅需要看到会议申请的表单同时还要看到会议室使用情况的表单,如果有空闲的会议室,用户登记操作会议室使用表,然后通知申请者;如果没有空闲的会议室,则不用登记直接通知申请者。这个过程中跨越了两个业务,分别是会议室管理和会议管理。表单在各个节点也是不同的。

这其实对工作流引擎提出了比较高的要求。例如如果流程已经结束,会议得到批准,但申请者突然有事要改变会议时间怎么办?回退。这里的回退无疑就需要有业务的补偿,例如要删除会议室的相关记录。

应用集成

一个流程不仅会跨越多个业务,也会跨越多个系统。这里的应用场景很多,重要的是要去其他系统抓取数据和操作数据,仅仅靠数据库表对表单的映射满足不了需求。


对工作流引擎做出改进,与前面相比,需要由引擎来完成对其他系统服务的调用。这里一个很重要的载体就是XML。首先要定义交换数据所用的XML scheme,然后将这个XML scheme再与表单引擎做出映射。实际执行时,工作流的自动节点会在人工节点前调用其他系统的服务,按照XML scheme将数据转换为符合定义的XML,在紧接着的人工节点送给表单引擎,表单引擎渲染。修改数据也是同样的过程,表单引擎将处理后的数据以XML返回,工作流再次做出转换,调用服务的修改功能。

上面五种都是比较常见的应用场景,理想的情况下,开发方式应该是这样的:画出应用流程à定义流程表单à表单与数据库进行映射à对流程进行业务仿真à完成开发。问题是这样的:你的表单引擎是否足够强大?表单与后台是直接用SQL进行交互的,也就是Transaction Script模式,没有业务对象,对于复杂业务逻辑如何处理?如何使用规则引擎来解决业务逻辑的问题?权限如何以一种AOP的方式对数据操作进行横切?

呵呵,纯属个人YY

 

posted @ 2007-11-02 10:07 ronghao 阅读(1624) | 评论 (5)编辑 收藏

原文地址:http://www.blogjava.net/RongHao/archive/2007/10/30/157009.html

对于当前的工作流应用来说,人工节点无疑扮演着一个非常重要的角色。因为无论是传统的协同办公系统还是越来越多的企业应用,都需要有人参与到具体的流程中来。这篇文章着重分析工作流应用中人工节点所涉及到的参与者模式以及与此关联的工作项模式,最后会提供部分的解决方案作为参考。

先简单说说什么是人工节点。

人工节点的概念是与自动节点相对的。顾名思义,人工节点就是需要有人参与的节点,在实际流程中,它体现在产生由人完成的工作项以及由人决定一些决策变量,这些决策变量会对流程的运行产生影响(例如分支的选择等等)。自动节点则是由工作流引擎自己调用完成,不需要人的参与,通常是执行定制的业务操作。相比较而言,人工节点更多的应用在管理流程里,而自动节点更多的则是应用在企业业务流程里。

人工节点的职责有三个:第一是决定该节点的参与者;第二是根据参与者生成需要人来处理的工作项;最后是当工作项被参与者处理完毕时,它将继续触发流程的流转。参与者处理工作项时可以处理相应的业务和设置流程决策变量。

下面我们就按照这三个职责分别对人工节点所涉及到的参与者模式和工作项模式进行分析。

1、  决定参与者模式

换句话说就是决定该节点的参与者,这里有两种模式:引擎自动获取和最终用户指定。
 

11引擎自动获取

所谓引擎自动获取就是由引擎在运行期计算实际的节点参与者,不需要最终用户的参与。这个计算基于流程定义时对该节点参与者的定义。

(1)    直接指定人员、部门或角色

这种情况最简单,也最直接,用户定义节点时直接在组织用户树里选定人员、部门或角色,然后在运行期根据定义执行与或者是或的运算。大多数的工作流引擎都支持这种模式。但很明显它也存在着很大的局限性,它是静态的,一旦流程定义完毕参与者也就跟着固定下来,运行期的任何变化都不会对参与者造成影响,一个很简单的需求,请假流程,节点的参与者需要是当前申请者的部门领导,因为申请者在定义期是不确定的,所以根本无法指定节点的参与者,所以这种模式远远满足不了用户稍微复杂一点的需求。

(2)    调用用户定制的计算参与者代码

这种情况通常是由引擎提供一个接口或是父类,用户需要实现或是继承这个接口或父类,然后实现相应的方法。这个方法通常会传递进一个执行上下文的参数,用户代码通过这个上下文可以访问到当前流程实例的信息,例如当前节点状态,工作流变量等等,然后用户可以根据实际业务和当前流程实例信息进行逻辑计算,最后返回一个参与者的ID集合。对于上一个模式里提到的计算当前申请者部门领导的例子,这个模式实现起来非常简单,首先获得当前申请者ID,然后在根据这个ID找出该申请者部门再找出该部门领导即可。

实际流程运行到该节点就会调用用户自己定制的计算参与者的代码,方法返回的参与者ID即作为该节点的实际参与者。

这种模式对于工作流引擎的实现而言最为简单,因为它把最大的复杂性都抛给了用户,由用户代码来计算实际的参与者。实际上很多开源的工作流引擎采用的都是这种方式,例如JBPM。但是这种方式给用户带来最大灵活性的同时也带来了复杂和烦琐。特别是当面对一个数量巨大的流程需求时,为每一个流程的每一个人工节点都定义一个参与者计算类是让人头疼的。再加上现在强调业务的敏捷,业务里的改变要迅速反馈到流程的定义里,让最终用户来编写或修改这个参与者计算类不现实也不可能。补充一下,这也是用户在考虑采用开源的工作流引擎还是商业工作流引擎时需要着重考虑的一个方面。

(3)    指定前续节点的参与者

实际上是用户在节点定义时指定参与者为前续某个节点的参与者,当流程运行到该节点,引擎会自动获取所指定的前续节点的参与者作为该节点的实际参与者。

这个模式实现起来并不困难,大多数商业工作流引擎都对该模式进行了支持。它能够满足用户的部分特定需求。

(4)    更为复杂的情况

用户的需求永远是复杂的,引擎所要做得就是尽量降低这种复杂性,流程的变化要能够迅速跟上业务的变化。考虑下面两种稍微复杂一点但是又很常见的需求。需求一:参与者为当前申请者的部门领导且职位为副总;需求二:参与者需要是测试部的所有女同事。这两种需求模式13都不能满足,2可以,但是正如提到的那样,模式2可能会非常的烦琐,不能适应业务的敏捷。其实这里的复杂性主要体现在:1、这里的参与者可能是运行期决定的;2、参与者的限制条件可能非常多,而这些条件不是简单的部门、角色或职位所能描述的。

对于一般的工作流引擎而言,它们都会选择模式2的实现,让用户自己实现逻辑。实际在后面的部分解决方案里,我们会看到更为好一点的实现方式。

 

1.2最终用户指定

   运行期由最终用户来决定节点的参与者。这也是中国国情所独有的特色。这种模式最为常见的就是用户提交工作项时的提交页面,用户在该页面上选定下一节点(多数分支用户选定时)和下一节点的参与者。这种模式本身并不困难,问题在于在提交页面需要给用户提供一个参与者的选择范围,让用户进行选择。而关于这个选择范围,则又回到前面所提到的引擎自动获取的模式,这个范围同样是需要引擎计算的。于是又回到了刚刚讨论过的四种模式。

 

2、参与者执行模式

   现在,已经获得了节点的参与者。引擎下一步将会根据这个参与者生成工作项,注意,这里的参与者可能是一个人,也可能会是一个人员范围(即多个人)。于是就产生了参与者的执行模式,也可以理解为工作项的生成模式。

2.1竞争参与

当有多个参与者参与这个节点时就会产生竞争参与这个模式。同样一个工作,A可以完成,B也可以完成,于是就产生竞争,谁先开始这项工作,就由谁负责完成该工作。

2.2顺序参与

   多个参与者按照指定的顺序完成该工作项。A完成之后由B完成,B完成之后再交给C完成。

2.3同时参与

   多个参与者同时对工作进行处理,所有参与者均完成后,流程继续向后流转。这个模式其实比较复杂,因为这里同时涉及到一个完成规则:是所有参与者均完成工作项后流程流转,还是有其他规则?例如完成2个工作项即可流转,完成80%的工作项即可流转。稍候会讨论到。

2.4负载均衡

   这也是一个常见的需求。这项工作AB都可以完成,但是A目前有10个待办工作项,B只有2个待办工作项。于是用户期望该工作交由B来完成。这里需要实现一个简单的负载均衡。其实这种情况只是智能决策的一种最简单的情况,所谓智能决策是指系统能够根据一定的指标(由数据分析,例如人员的处理效率,工作负载等等)和规则来决定该节点的参与者。

3、工作项完成模式

这个模式在参与者执行模式为同时参与时有效。在说到这个模式之前,先简单说说工作项可能存在的几种特殊状态,这些状态包括挂起、人工终止和委派。挂起就是工作项暂时停止执行,挂起会影响到流程的流转,会导致流程的挂起。人工终止则是人工手动改变该工作项的状态,使该工作项终止执行,这个人通常会是管理员。人工终止也会对流程流转产生影响,当除去该工作项之外的所有工作项都完成时,人工终止该工作项会触发流程的流转。委派就是将该工作项委派给他人完成,同时该工作项也就结束了。人工终止和委派是工作项结束的特殊状态。
 

3.1全部完成

当所有工作项都结束时触发节点的结束和流程的流转。

3.2完成规定的个数

节点定义时指定工作项必须完成的个数,当完成的工作项达到这个指定的个数时触发节点的结束和流程的流转。 

3.3完成规定的百分比

节点定义时指定工作项必须完成的百分比,当完成的工作项占所有工作项的比例达到这个指定的百分比时触发节点的结束和流程的流转。
 

其实这里很明显的可以看出不管是所谓的参与者执行模式还是工作项完成模式不过都是一定的规则,既然是一定的规则那必然就限定了应用的灵活性,用户能否自定义规则?根据业务灵活地修改规则?规则引擎+DSL应该是一个不错的选择。

posted @ 2007-10-30 18:06 ronghao 阅读(3655) | 评论 (3)编辑 收藏
周末参加了清华大学信息系统与工程研究所组织的一次工作流研讨会。
会上清华大学的研究人员介绍了他们对工作流研究的一些成果。其中主要关注了数据挖掘和分析。
关于数据挖掘,有两个方面。第一个方面是当企业没有应用工作流时,对企业日常数据进行挖掘,分析数据
之间的关联和逻辑,得出一套基本的企业应用业务流程,为将来应用工作流做出准备;第二个方面是当企业
已有工作流应用时,对相关流程数据进行挖掘,根据数据描绘出正在运行的流程,很显然,这个方面与流程
仿真有关。
关于数据分析,则主要是对流程数据进行分析,这个涉及的内容就比较多,包括流程的运行效率,人员效率
分析,统计等等。其中有一个非常有趣的例子:贪官A试图逃跑,逃跑前是有一定前兆的,比如向国外转移
账款,给小蜜频繁打电话,工作效率降低等等。于是数据分析就可以起作用,对A最近的所有数据进行分析,
得出他将要逃跑的结论。而来自有生博大的赵斌,谈论到他们已经实施的项目中,涉及到对一些电子公文
流程中的办文统计,并依据“指标”进行工作量和效率的分析。
这其实也反映出我们工作流软件中缺失的部分:数据分析-数据统计-优化流程。而这也正是BPM的重要功能之一。也是未来发展的趋势。功能缺失最重要的原因在于实际上我们的软件没有研发只有开发。具体他们略微谈到了他们一些实现的方式,非常的理论,涉及到算法,数据结构等等。我们软件开发最主要的是满足用户的各种业务需求,比如说回退、收回、会签等等,这也是现实情况所决定的,开发团队的人员通常都不是很多,并且这种分析挖掘需要的人员无疑需要很高的理论水准,这个本科往往是达不到的(不得不承认研究生和博士就是比本科要优秀),博士硕士的研发团队的开发成本会非常的高,并且实际产生的利润很难评价,对于不大的公司来讲这个是很难接受的。于是就造成了这种状况:软件研发缺失掉了。
依托高校是个不错的想法,但是依托高校不等于说你把办公地点搬到清华大学门口就是依托高校了,也有这种依托,那是买煎饼的。
posted @ 2007-10-29 11:57 ronghao 阅读(1397) | 评论 (6)编辑 收藏
问题背景:我们是一家工作流公司,客户采购我们的产品后,将其嵌入其项目中。我们的工作流采用的是   spring+hibernate的方式,客户项目则是jdbc直接进行数据库操作。
问题:客户在其数据库操作过程中需要调用我们的工作流接口,这样就需要将我们的工作流操作与他们的业  务操作置于同一个事务中。我们的服务采用的都是spring的声明式事务,而客户采用的是对         connection进行事务处理。如何保证事务的一致性?
想到的解决方案一:使用jta事务,用tomcat+jotm提供事务管理器。为什么一开始就想到要使用jta事务??实际上我们和客户都是使用的同一个数据库,为了方便,各自使用了不同的数据库连接方式,使用jta的话确实有bt的意思在里面。但是事实上是我们的第一反应都是jta。最后没有采用该方法的原因也很简单:我没有将jotm配置成功!汗一个。
想到的解决方案二:将客户的这些特定代码用spring管理起来。因为要修改客户部分代码,这个方案遭到了客户的强烈反对。于是放弃。
想到的解决方案三:客户数据库操作与我们的服务使用同一个数据库连接。然后编程处理事务。存在两种方式:一种是把客户的连接传给我们,另一种则是把我们的连接传给客户。第一种方式对我们的影响太大,所以最后决定采用后一种方式:从hibernate session中获取connection然后传递给客户。接下来查看一下HibernateTemplate的execute()方法,思路就很简单了:获取定义的sessionFactory-->创建一个新的session并打开-->将session与当前线程绑定-->给客户代码返回connection-->打开事务-->客户使用我们传递的connection进行数据库操作-->我们不带声明事务的服务操作-->提交事务-->解除绑定。
实际要注意的地方是:1、将session与当前线程绑定使用的TransactionSynchronizationManager.bindResource()方法,这样在HibernateTemplate里才能找到session;
                    2、我们的服务一定要把声明式事务彻底干掉,否则会有commit;
                    3、我们服务调用完毕后一定要flush session,否则客户代码不会感知数据库里的数据变化。
最终解决:使用了spring里常用的模板和回调。代码如下:
public class TransactionTemplate {

    
protected final Log logger = LogFactory.getLog(TransactionTemplate.class);

    
private FlushMode flushMode = FlushMode.ALWAYS;

    
public Object execute(TransactionCallback callback) {
        
//首先获取sessionFactory
        SessionFactory sessionFactory = (SessionFactory) Framework.getEngine()
                .getContainer().getComponent(
"sessionFactory");
        
//创建一个新的session并打开
        logger.debug("Opening single Hibernate Session in TransactionTemplate");
        Session session 
= getSession(sessionFactory);
        
//将session与当前线程绑定
        TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
        
//获取数据库连接
        Connection conn = session.connection();
        Object result 
= null;
        Transaction transaction 
= null;
        
try {
            
//开始处理事务
            transaction = session.beginTransaction();
            
try {
                result 
= callback.doInTransaction(conn);
            }
            
catch (RuntimeException ex) {
                doRollback(session, transaction);
                
throw ex;
            }
            
catch (Error err) {
                doRollback(session, transaction);
                
throw err;
            }
            
//如果数据库操作过程中没有发生异常则提交事务
            transaction.commit();
        } 
catch (WorkflowException e) {
            logger.error(
"数据库操作失败,事务回滚也失败!");
            
throw e;
        } 
catch (RuntimeException ex) {
            logger.error(
"数据库操作失败,事务被回滚!");
            
throw ex;
        } 
catch (Error err) {
            logger.error(
"数据库操作失败,事务被回滚!");
            
throw err;
        } 
finally {
            
// 将session与当前线程解除绑定
            TransactionSynchronizationManager.unbindResource(sessionFactory);
            doClose(session);
        }
        
return result;
    }

    
protected Session getSession(SessionFactory sessionFactory) {
        Session session 
= SessionFactoryUtils.getSession(sessionFactory, true);
        FlushMode flushMode 
= getFlushMode();
        
if (flushMode != null) {
            session.setFlushMode(flushMode);
        }
        
return session;
    }

    
private void doRollback(Session session, Transaction transaction) {
        logger.debug(
"数据库操作异常,开始回滚事务");
        
try {
            transaction.rollback();
            logger.debug(
"回滚事务成功!");
        }
        
catch (Exception e) {
            logger.error(
"回滚事务失败!");
            
throw new WorkflowException("回滚事务失败!");
        } 
finally {
            session.clear();
        }
    }

    
private void doClose(Session session) {
        logger.debug(
"开始关闭连接");
        
try {
            session.close();
        }
        
catch (Exception e) {
            logger.error(
"关闭连接失败!");
            
throw new WorkflowException("关闭连接失败!");
        }
    }

    
public FlushMode getFlushMode() {
        
return flushMode;
    }

    
public void setFlushMode(FlushMode flushMode) {
        
this.flushMode = flushMode;
    }
}

public interface TransactionCallback {

    Object doInTransaction(Connection conn);
}
调用伪代码:
    public void methodA(){
        TransactionTemplate transactionTemplate
=new TransactionTemplate();
        transactionTemplate.execute(
new TransactionCallback(){
            
public Object doInTransaction(Connection conn) {
                
//客户代码
                client.method1("1");
                
//我们代码 直接使用
                our.method2();
                
//客户代码
                client.method3("l");
                
return null;  
            }
        });
    }


posted @ 2007-10-09 15:11 ronghao 阅读(7736) | 评论 (5)编辑 收藏
基于职责设计对象
最关键的软件开发工具是受过良好设计原则训练的思维,而不是UML或其他任何技术。

RDD是思考OO软件设计的一般性隐喻。把软件对象想象成具有某种职责的人,他要与其他人协作以完成工作。RDD使我们把OO设计看作是有职责对象进行协作的共同体。

分配职责的基本模式是GRASP。

创建者模式
  问题:谁创建类A的实例?
  建议:如果以下条件为真时(越多越好),将创建类A实例的职责分配给类B:
        1、B“包含”或组成聚集了A。
        2、B记录A。
        3、B紧密的使用A。
        4、B具有A的初始化数据。
        首选聚集或包含A的类B。
  注意:A和B指的都是软件对象而不是领域模型对象。
  禁忌:对象的创建常常具有相当的复杂性。在一些情况下,更适合使用工厂。

信息专家模式
  问题:给对象分配职责的基本原则是什么?
  建议:把职责分配给具有完成该职责所需信息的那个类。把职责和数据置于一处,把服务与其属性置于一处。
  禁忌:相比较而言系统关注更为重要,设计要分离主要的系统关注。例如:领域对象加入持久化逻辑成为充血模型,这就把应用逻辑与数据库逻辑混合起来了(不良设计)。

低耦合模式
  问题:如何减少因变化产生的影响?(高耦合并不是问题所在,问题是与某些方面不稳定的元素之间的高耦合)
  建议:分配职责以使(不必要的)耦合保持在较低的水平。(信息专家模式支持低耦合)

控制器模式
  问题:在UI层之上首先接受和协调系统操作的对象是什么?
  建议:把职责分配给能代表下列选择之一的对象:
        1、代表全部“系统”、“根对象”、运行软件的设备或主要的子系统(外观控制器的变体)。
        2、代表发送系统操作的用例场景(用例或会话控制器)。

高内聚模式
  问题:怎样使对象保持有内聚、可理解和可管理,同时具有支持低耦合的附加作用?
  建议:职责分配应保持高内聚。委派职责。内聚性低的类通常表示大粒度的抽象,或承担了本该委托给其他对象的职责。

多态模式
  问题:如何处理基于类型的选择?如何创建可插拔的软件构件?
  建议:当相关选择或行为随类型(类)有所不同时,使用多态为变化的行为类型分配职责。不要测试对象的类型,也不要使用条件逻辑来执行基于类型的不同选择。
  经验:如果有一个具有抽象超类C1的类层次结构,可以考虑对应于C1中的公共方法特征标记来定义接口I1,然后声明C1来实现接口I1。

纯虚构模式
  问题:当并不想违背高内聚和低耦合或其他目标,但基于专家模式的方案又不合适时,哪些对象应承担这一职责?
  建议:人为制造类,由该类来承担一组高内聚的职责。该类并不代表问题领域的概念-是虚构的事物。例如DAO
  纯虚构通常基于相关的功能性进行划分,因此这是一种以功能为中心的对象。

间接性模式
  问题:为了避免两个或多个事物之间的直接耦合,应该如何分配职责?
  建议:将职责分配给中介对象。例如,Adapter
  间接性的动机通常是为了低耦合,并且大多数间接性中介都是纯虚构的。

防止变异模式
  问题:如何设计对象、子系统和系统,使其内部的变化或不稳定性不会对其他元素产生不良影响?
  建议:创建稳定的接口。
  不要和陌生人讲话:约束了应该在方法里给哪些对象发送消息。它要求在方法里,只应给以下对象发送消息:
  1、this对象(自身)
  2、方法的参数
  3、this的属性
  4、作为this属性的集合中的元素
  5、在方法中创建的对象
  典型违反该原则的例子: F f=foo.getA().getB().getC().getD().getE().getF();

命令-查询分离原则:
  执行动作(更新、调整,等等)的命令方法,这种方法通常具有改变对象状态等副作用,并且是void的(没有返回值)。
  向调用者返回数据的查询,这种方法没有副作用,不会永久性地改变任何对象的状态。
关键在于:一个方法不应该同时属于以上两种类型。
 
在绘制交互图时考虑和决定职责分配。然后在类图的方法分栏里概括职责分配结果,方法是职责的具体实现。
posted @ 2007-09-04 17:38 ronghao 阅读(1283) | 评论 (0)编辑 收藏
在迭代开发中,我们并非一次就实现所有需求。

在多个迭代里对同一用例进行增量式开发。

细化阶段:构建核心架构,解决高风险元素,定义大部分需求,以及预计总体进度和资源。(包括了三到四次的细化迭代)
关键思想和最佳实践:
 实行短时间定量、风险驱动的迭代
 及早开始编程
 对架构的核心和风险部分进行适应性设计、实现和测试
 尽早、频繁、实际的测试
 基于来自测试、用户、开发者的反馈进行调整
 通过一系列讨论会,详细编写大部分用例和其他需求,每个细化迭代进行一次
制品:领域模型、设计模型、软件架构文档、数据模型、用例、用户界面原型。

领域模型
领域模型是对领域内的概念类或现实世界中对象的可视化表示。
应用UML表示法,领域模型被描述为一组没有定义操作的类图,反映属性和关联。(一定注意,它不是软件对象!是概念对象!)
准则:
  使用领域术语
  认真汲取领域专家所使用的核心词汇和概念
  如果某概念类不是现实世界中的数字或文本,那么她可能是概念类而不是属性
  需要描述类:1、需要有关商品或服务的描述,独立于任何商品或服务的现有实例;2、减少冗余或重复信                 息
  避免加入大量关联,添加关联是为了突出对重要关系的大致理解,而非记录对象或数据的结构
  通过关联而不是属性来表示概念类之间的关系,领域模型中属性的类型应该是数据类型(基本类型)
  对数量和单位建模
每次迭代的领域建模不超过几个小时。

系统顺序图
系统顺序图(SSD)是为阐述与所讨论系统相关的输入和输出事件而快速、简单地创建的制品。是操作契约和对象设计的输入。
SSD展示了直接与系统交互的外部参与者、系统(作为黑盒)以及由参与者发起的系统事件。这些事件是通过用例文本总结出来的。
应为每一个用例的主成功场景,以及频繁发生的或者复杂的替代场景绘制SSD。
基本上软件系统要对三种事件进行响应:来自于参与者的外部事件,时间事件,错误和异常(通常源于外部)。
系统事件应该在意图的抽象级别而非物理的输入设备级别来表达。

操作契约
操作契约使用前置和后置条件的形式,描述领域模型里对象的详细变化。其中的关键元素是后置条件。
为系统操作定义操作契约。系统操作在绘制SSD草图时确定。
后置条件描述了领域模型内对象状态的变化。可分为三种类型:创建或删除实例,属性值的变化,形成和消除关联。注意,它描述的是所需的变化,而不是这些变化是如何完成的。以说明性的、被动式的过去时态编写后置条件。(例如,创建了xx,而不是创建xx)

逻辑架构和UML包图
逻辑架构是软件类的宏观组织结构,它将软件类组织为包(或命名空间)、子系统和层等。
UML包图常用于描述系统的逻辑架构--层、子系统、包等。
将系统的大型逻辑结构组织为独立的、职责相关的离散层,具有清晰、内聚的关注分离。
协作和耦合是从较高层到较低层进行的,要避免从较低层到较高层的耦合。较低层包含可复用功能。
可以将应用逻辑层更准确地称为架构的领域层,即包含领域对象(注意领域模型与领域对象是不同的两个概念),处理应用逻辑的层。
领域层是软件的一部分,领域模型是概念角度分析的一部分。
从UI层发送到领域层的消息将是SSD中所描述的消息。

轻量级绘图然后编码。
posted @ 2007-09-03 17:50 ronghao 阅读(2118) | 评论 (1)编辑 收藏
在OO开发中,至关重要的能力是熟练地为软件对象分配职责。

在面向对象分析中(OOA),强调的是在问题领域内发现和描述对象。OOA关注从对象的角度创建领域描述,OOA的结果可以表示为领域模型。需要注意的是,领域模型并不是对软件对象的描述(区别于软件中的对象类),它是真实世界领域中的概念和想象可视化。

在面向对象设计(OOD)中,强调的是定义软件对象及它们如何协作以实现需求。(类图与顺序图)

迭代开发是OOA/D成为最佳实践的核心。建议迭代的时间在2-6周之间,小步骤、快速反馈和调整。迭代的一个关键思想是时间定量,或时长固定。
1、在第一次迭代之前,召开第一个时间定量的需求工作会议(两天)。业务和开发人员需要出席。
   a、高阶需求分析。仅仅确定用例和特性的名称,以及关键的非功能性需求(性能,可伸缩性等等)。
   b、通过架构师和业务人员,从高阶列表选择10%列表项(例如,30个用例名中的10%,3个)。这3个用例      需要具备:具有重要的架构意义;具有高业务价值;具有高风险(例如,可处理500个并发交易)。标      识这3个用例:UC2,UC11和UC14。
   c、剩下的一天半对这3个用例的功能和非功能性需求进行详细的分析。
2、在第一次迭代之前,召开迭代计划会议,选择UC2,UC11和UC14的子集,在特定的时间内(例如,四周)    进行设计、构造和测试。要注意,并不是在第一次迭代中就要构造全部的3个用例,可以将其分解为一系   列更为详细的迭代任务。
3、在三到四周内完成第一次迭代(严格遵守时间)
   a、在开始的两天内,开发者在架构师指导下分组进行建模和设计工作。白板,UML草图,界面、讨论。
   b、编程。注意UML草图只是参考
   c、测试
   d、结束前一周,检查是否能够完成初始迭代目标,如果不能,缩小迭代范围。
   e、最后一周的周二,冻结代码,集成和测试所有代码,建立迭代的基线。
   f、周三上午,演示此局部系统,展示早期可视进展,要求反馈。
4、在第一次迭代即将结束时(最后一周的周三周四),召开第二次需求会议,对上次会议的所有资料进行复   查和精化。然后选择具有重要架构意义和高业务价值的另外10%到15%的用例,一到两天详细分析。这项工   作完成后,大约20-25%的用例得到了详细记录。
5、周五上午,举行下一迭代的迭代计划会议。
6、相同步骤2次迭代。
7、反复四次迭代和五次需求会议,这样在第四次迭代结束后,可能已经详细记录了80-90%的需求,但系统只   实现了10%。
8、大概推进了整个项目过程的20%。up里,属于细化阶段。此时,可以估计整个项目的工作量和时间。
9、一般不再需要需求会议,需求已经稳定了。接下来是一系列为期三周的迭代,在最后一个周五召开的迭代   计划会上选择适宜的下一步工作。
  
早期的迭代要致力于核心架构的构造,应该首先处理困难的和具有风险的事物。

建模(构建UML草图)的主要目的是为了理解,而非文档。只需对设计中不常见、困难和棘手的一小部分问题建模和应用UML。不要单独建模,而是结对(或三个人)在白板上建模。并行的创建模型(例如,类图和顺序图交替开始)。UML细节是否精准不重要,重要的是人员能够互相理解。坚持用最简单、常用的UML元素。开发者应该为自己进行OO设计建模,而不是创建模型图后交给其他人实现。

UP的核心思想是:短时间定量迭代、进化和可适应性开发。

初始阶段是建立项目共同设想和基本范围的比较简短的起始步骤。包括确定大部分用例名称,对10%的用例进行分析,关键的非功能需求的分析,业务案例创建和开发环境的准备。包含第一次需求研讨会,并为第一次迭代制定计划。要解决的主要问题:涉众是否就项目设想基本达成一致,项目是否值得继续进行认真研究。

用例是文本形式的情节描述,特别注意,用例是文本不是图形!用例建模主要是编写文本的活动,而非制图。用例名称应使用动词开头。
参与者的三种类型:主要参与者,协助参与者,幕后参与者。
用例的三种常用形式:摘要,非正式,详述(在第一次需求会中,详细的编写其中少量(例如10%)的具有重要架构意义和高价值的用例)。用例应该包含所有涉众关注点的事物。
准则:
以无用户界面的本质风格编写用例;排除用户界面而关注参与者的意图。
编写简洁的用例。
编写黑盒用例,不对系统内部工作、构件或设计进行描述。而是通过职责来描述系统。
采用参与者和参与者目标的视点。
posted @ 2007-08-30 17:59 ronghao 阅读(1438) | 评论 (1)编辑 收藏
这些天主要在做一些翻译的事情。周日的时候抽了半天时间给一本书做了一章的试译。似乎并不是太意外,编辑今天告诉我试译没有通过,与他们的要求还有一定的差距。然后他把他批注过的试译发给了我。看得出编辑是一个非常负责任的人,几乎所有的句子都被他详细的review过。总结了一下原因:
1.没有经验。这是我第一次翻译书,犯了很多常识性错误。例如少用被字句,章号用阿拉伯数字,图题和图中需  要翻译的英文应该对应,我、你、你的这些代词,能不翻译尽量不要翻译等等。
2.对专业知识不熟悉。这本书是关于swing,java2d\3d的,而自己在这方面没有什么经验,只是使用过Swing,这  样就使一些专业术语翻译很不到位。对英文的理解会存在问题。
3.自己翻译的理解问题。一些句子不通顺,不好理解,措辞的不当。其实这个问题我认为主要是匆忙不负责任引  起的。
尽管被cut了,但我认为自己还是学到了很多东西的:一些常用的翻译技巧和注意事项,对相关知识不熟悉时不要随便翻译,遇到难度的句子要做批注,翻译完毕后一定要review等等。感谢华章图书的编辑,他让我懂得许多。也让我长长出了口气:不用再去网上找java2d/3d方面的文档和书籍,不用狂补相关的知识。另外要感谢的是阿敏总司令,是他介绍给我这个机会,但是我没有把握住。
翻译,并不是一件简单的事情。
posted @ 2007-08-28 15:28 ronghao 阅读(652) | 评论 (0)编辑 收藏
UML有三种使用方式:用作草图绘制,用于蓝图绘制,用于程序编制。
倾向于将UML用于草图绘制,绘制草图的实质是选择,重点是进行交流,常用的介质是白板。
草图是故意不完备的,要突出重要的信息。草图是探究性的,蓝图是定义性的。草图用于正向工程(设计阶段),蓝图用于逆向工程(根据已有的代码导出)。详细文档应该根据代码生成。

UML最重要的是类图和顺序图。

瀑布风格和迭代风格
瀑布风格是基于活动来分解项目的,迭代风格根据功能子集来分解项目。
迭代的一种常用技术是时间框定法,迫使各次迭代的时间长度固定。通过定时搁置功能,使人们能够在搁置日期和搁置功能之间进行明智的选择。

敏捷过程是强适应性的过程。敏捷方法强调项目成功最重要的因素是人的素质以及人之间的良好协同,敏捷方法倾向使用时间框定的短小迭代。每一次迭代结束时要进行一次迭代回顾。

RUP本质上是一个迭代过程,分为四个阶段:初始,细化,构造,移交。
需求分析最重要的是与用户及客户的交流。

类图
类图表述系统中各个对象的类型以及其间存在的各种静态关系。
对不重要的事(如日期或布尔值,一般说,值类型)使用属性,对较为重要的类使用关联。
非常反感那些除了一组域及其get/set方法没有行为的类。如果你在利用get方法重复调用数据,这预示着某一行为应该移往具有数据的对象。
依赖应该单向,依赖越少越好,特别谨慎循环依赖,尤其反对包间的循环依赖。对类使用依赖最常见的情形是阐明瞬间关系,比如,把一个对象作为参数传递到另一个对象时。
不要试图使用对你可用的所有图示法,保持图示简单,集中考虑关键方面。绘制类图时总以使用某种形式的行为技术为宜。

顺序图
尽量省去回送。
单一职责,提倡分布式控制(把处理分散到多个对象里去)。
减少过程式编程,如if/else,改用多态解决类似问题。
把顺序图看作各个对象如何交互的形象化表示而不是一种对控制基理的建模方法。顺序图擅长示明对象间的协作,不擅长于示明行为的精确定义。

CRC卡
CRC的一个重要部分是认识职责。任意一个类都可以用少量职责对其概括。对具有三项以上职责的卡片提出质问,是否应该把类分解,或把职责合并成一个更高层次
的概述。
posted @ 2007-08-27 22:27 ronghao 阅读(2506) | 评论 (5)编辑 收藏
工作流开发已经有一段时间了,这里把自己的一些想法小结一下。仅仅就工作流引擎来说,不包括一些外围的实现,例如流程定义器,管理控制,工作项列表等。
工作流引擎其实就是一个状态机,只不过在状态变化的过程中加入了其他一些工作。我把工作流引擎的职责理解为以下四个方面:
1、对工作流模式的支持。
   这无疑是最重要的部分,状态的变迁往往取决于不同模式的选择。支持的模式越多则客户的开发代码会越少。衡量一个工作流引擎的技术水准很大程度取决于引擎支持模式的多少。
2、工作流变量的传递和转换。
   工作流引擎通过工作流变量与外部应用交互,工作流变量在各个活动节点以及父流程与子流程之间传递。变量除基本类型(String,int等)以外,也需要支持一些复杂的数据类型(例如对象,以一种配置映射的方式)。这里还涉及到一个上下文的问题。
3、任务项的分配。
   任务项的分配往往和工作流组织权限联系起来,其实工作流组织权限存在的目的就是决定任务项分配,决定由谁来完成这个工作项。工作项涉及到的内容也比较多,比如工作项的回退,撤回等等。
4、调用外部应用。
   单纯的表单推动已经不再适用,活动节点本身需要支持许多的业务操作,而这些操作与引擎本身是无关的,与外部的应用有关,所以就需要引擎提供一种调用外部应用的机制。外部应用可以是javabean,webservice,rcp等等形式。
除去上述四方面还有一些外围的工作:例如时间服务,节点的事件机制等等。
对客户而言,他们需要的仅仅只有两个接口:任务项管理接口(比如提交任务项,委派任务项等等)和流程状态管理接口(比如启动一个新的流程实例,推动流程流转等等)。在理想的情况下,给用户提供一个封装完全的提交页面和父类Action也是很好的一种方法。
posted @ 2007-08-27 17:21 ronghao 阅读(10122) | 评论 (5)编辑 收藏
今天终于有空看看了Fielding的rest论文,没有看完,很多文字确实难懂,但有些还是很有感触的,做个记号。
一个软件架构是一个软件系统在其操作的某个阶段的运行时元素的抽象。
架构元素:组件,连接器,数据,配置。
架构风格:一组协作的架构约束。
一种特定的架构可以由多种架构风格组成。
关键关注点的架构属性
性能
最佳的应用性能是通过不使用网络而获得的。这意味着对于一个基于网络的应用,最高效的架构风格是在可能的情况下能够将对于网络使用减少到最少的架构风格。
可伸缩性
表示在一个主动的配置中,架构支持大量的组件或大量的组件之间交互的能力。
简单性
对组件之间的功能分配应用分离关注点原则。使得单个的组件足够简单,更容易被理解和实现。
可修改性
基于网络的系统的一个特殊的关注点是动态的可修改性,它要求在对一个已部署的应用做出修改时,无需停止和重启整个系统。包括:可进化性,可扩展性,可定制性,可配置性,可重用性。
可见性
能够通过限制必须使用通用性的接口,或者提供访问监视功能的方法,来影响基于网络的应用中交互的可见性。在这种情况下,可见性是指一个组件对于其他两个组件之间的交互进行监视或仲裁的能力。
可移植性
能够在不同的环境下运行。
可靠性
当在组件、连接器或数据之中出现部分故障时,一个架构容易受到系统层面故障影响的程度。
posted @ 2007-07-11 17:37 ronghao 阅读(754) | 评论 (0)编辑 收藏
  整体来说实现的非常清晰:
  1、引擎解析流程定义xml时,给相应的事件挂接上create-timer 和 cancel-timer动作
  2、流程实例实际运转时,create-timer动作在相应事件触发时执行
  3、create-timer在job表里插入相应时间job记录,给该job记录附上计算完毕的执行时间
  4、JobExecutorServlet在后台启动一到多个JobExecutorThread线程
  5、JobExecutorThread线程不停的每隔一段时间对job表扫描一次,找出需要执行的job记录,执行之
  6、只执行一次的job记录,执行完毕后删除之;重复执行的job记录,写入新的执行时间,更新之
  7、相应事件触发cancel-timer动作,将对应job记录从job表里删除
  下面具体用代码来说话(挂接到node节点):
  1、引擎解析流程定义xml 
  JpdlXmlReader.java 
  
protected void readNodeTimer(Element timerElement, Node node) {
    String name 
= timerElement.attributeValue("name", node.getName());
   
    CreateTimerAction createTimerAction 
= new CreateTimerAction();
    createTimerAction.read(timerElement, 
this);
    createTimerAction.setTimerName(name);
    createTimerAction.setTimerAction(readSingleAction(timerElement));
    addAction(node, Event.EVENTTYPE_NODE_ENTER, createTimerAction);
   
    CancelTimerAction cancelTimerAction 
= new CancelTimerAction();
    cancelTimerAction.setTimerName(name);
    addAction(node, Event.EVENTTYPE_NODE_LEAVE, cancelTimerAction);
  }

  可以看到,引擎把xml中timer节点解析成了两个ACTION:CreateTimerAction和CancelTimerAction
  CreateTimerAction会在进入该节点时触发,而CancelTimerAction会在令牌离开该节点时触发。
  2、看看CreateTimerAction和CancelTimerAction究竟在做些什么
  CreateTimerAction.java
   
public void execute(ExecutionContext executionContext) throws Exception {
    Timer timer 
= createTimer(executionContext);
    SchedulerService schedulerService 
= (SchedulerService) Services.getCurrentService(Services.SERVICENAME_SCHEDULER);
    schedulerService.createTimer(timer);
  }

  很明显,是通过一个职责集中的schedulerService向job表中插入了一条job记录,注意到这个方法:
  protected Timer createTimer(ExecutionContext executionContext) {
    Timer timer 
= new Timer(executionContext.getToken());
    .
    
if (dueDate!=null) {
      Duration duration 
= new Duration(dueDate);
      Date dueDateDate 
= businessCalendar.add( new Date(), duration );
      timer.setDueDate(dueDateDate);
    }
    .
   
    
return timer;
  }

  这里利用JBPM提供的工作时间计算组件计算了job的执行时间。
  CancelTimerAction就很简单了,删除相应的job记录。
  CancelTimerAction.java
  
public void execute(ExecutionContext executionContext) throws Exception {
    SchedulerService schedulerService 
= (SchedulerService) Services.getCurrentService(Services.SERVICENAME_SCHEDULER);
    schedulerService.deleteTimersByName(timerName, executionContext.getToken());
  }

  3、JobExecutorServlet是干什么的
  启动线程
  public void init() throws ServletException {
    .
    jbpmConfiguration.startJobExecutor();
  }

  4、线程是如何工作
  public void run() {
    
try {
      currentIdleInterval 
= idleInterval;
      
while (isActive) {
        
try {
          Collection acquiredJobs 
= acquireJobs();   //从job表里获得将要执行的job记录

          
if (! acquiredJobs.isEmpty()) {    //如果记录不为空,则开始执行
            Iterator iter = acquiredJobs.iterator();
            
while (iter.hasNext() && isActive) {
              Job job 
= (Job) iter.next();
              executeJob(job);             
//执行
            }

          } 
else { // no jobs acquired     //如果没有可执行的job,则等待一段时间
            if (isActive) {
              
long waitPeriod = getWaitPeriod();  //等待的时间是找出即将执行的job离现在最近的时间间隔
              if (waitPeriod>0) {
                
synchronized(jobExecutor) {
                  jobExecutor.wait(waitPeriod);
                }
              }
            }
          }
         
         .
    } 
catch (Throwable t) {
      t.printStackTrace();
    } 
finally {
      log.info(getName()
+" leaves cyberspace");
    }
  }

  看看实际执行的方法
  protected void executeJob(Job job) {
    JbpmContext jbpmContext 
= jbpmConfiguration.createJbpmContext();
    
try {
      JobSession jobSession 
= jbpmContext.getJobSession();
      job 
= jobSession.loadJob(job.getId());

      
try {
        log.debug(
"executing job "+job);
        
if (job.execute(jbpmContext)) {    //交由Job对象本身去完成执行的逻辑,并决定是否删除job记录
          jobSession.deleteJob(job);
        }
            .
  }

  5、着重关注Time对象
  在上面我们看到实际执行的代码是job.execute(jbpmContext);
  Time 是Job的子类,看看它的实现:
  public boolean execute(JbpmContext jbpmContext) throws Exception {
    
boolean deleteThisJob = true;       //执行完毕后是否删除job记录

    ExecutionContext executionContext 
= new ExecutionContext(token);
    executionContext.setTimer(
this);

    
if (taskInstance!=null) {
      executionContext.setTaskInstance(taskInstance);
    }

    
// 触发timer事件
    if (graphElement!=null) {
      graphElement.fireAndPropagateEvent(Event.EVENTTYPE_TIMER, executionContext);
    }

    
// 如果timer节点上挂有action则执行之
    if (action!=null) {
      
try {
        log.debug(
"executing timer '"+this+"'");
        action.execute(executionContext);
      } 
catch (Exception actionException) {
      .
        }

    
// 如果定义了transition属性,则流程顺着定义的路径流转
    if ( (transitionName!=null)
         
&& (exception==null// and if no unhandled exception occurred during the action 
       ) {
      
if (token.getNode().hasLeavingTransition(transitionName)) {
        token.signal(transitionName);
      }
    }
   
    
// 如果定义了repeat属性则job记录不容许删除,同时计算新的执行时间
    if (repeat!=null) {
      deleteThisJob 
= false;

      
while (dueDate.getTime()<=System.currentTimeMillis()) {
        dueDate 
= businessCalendar
              .add(dueDate,
                
new Duration(repeat));
      }
      log.debug(
"updated timer for repetition '"+this+"' in '"+(dueDate.getTime()-System.currentTimeMillis())+"' millis");
    }
   
    
return deleteThisJob;
  }


posted @ 2007-06-22 17:13 ronghao 阅读(1940) | 评论 (0)编辑 收藏
JBPM时间服务的使用主要体现在对timer节点的使用。timer节点有两种使用方式:一种是挂接到node节点下,在进入node节点时触发,在离开node节点时终止;另外一种是挂接到task节点下,在任务创建时触发,默认在任务完成后终止。下面举例说明:
  一、挂接到node节点  
  <state name='catch crooks'>
      
<timer name='reminder'
         
duedate='3 business hours'
         repeat
='10 business minutes'
         transition
='time-out-transition' >
        
<action class='the-remainder-action-class-name' />
        
<transition name='time-out-transition' to='next' />
      
</timer>
    
</state>

  解释:timer将会在流程令牌进入节点catch crooks时触发,延迟3 business hours开始执行动作,每10 business minutes重复执行一次,直到令牌离开catch crooks节点。
  对time节点来说 name、repeat、transition都是可选属性。对一个流程定义来说,每一个time节点的name必须唯一,如果你不定义name属性,引擎会默认把node节点的name赋给timer。在上面这个例子里,如果你不定义timer节点的name,则它的name就会是catch crooks。说说repeat属性,如果你不定义它,则timer就会只执行一次动作不会重复执行。transition属性,如果定义了这个属性,流程令牌会在timer执行动作完毕后,顺着这个路径离开node节点。所以在上面这个例子里,尽管定义了repeat属性,action还是会只执行一次。
  action节点,可选,即timer节点在时间到时执行的动作,可以是任意action类型,包括script。注意与时间有关的两种action类型:create-timer 和 cancel-timer。其实一个timer节点在被引擎解释时就是被分解为create-timer 和 cancel-timer两个action,create-timer挂接到node-enter事件中,cancel-timer挂接到node-leave事件中。action节点最多只可以挂一个。
  说说整个过程:
  1、令牌进入节点catch crooks
  2、timer被触发(实际这时是在执行create-timer动作)
  3、3 business hours后 timer 事件触发
  4、定义的action被执行
  5、令牌顺着time-out-transition路径离开catch crooks节点
  6、cancel-timer动作被执行即timer终止(没有给repeat的机会)
  二、挂接到task节点
  <task-node name="Evaluate web order">
    
<task swimlane="salesman">
      
<timer duedate="20 seconds" repeat="10 seconds" cancel-event='task-start'>
        
<action class="org.jbpm.websale.RemindActor">
          
<swimlaneName>salesman</swimlaneName>
        
</action>
      
</timer>
    
</task>
    
<transition name="OK" to="salefork" />
    
<transition name="More info needed" to="Fix web order data" />
  
</task-node>

  与挂接到node 的区别是:这里可以定义一个属性cancel-event,可以指定那些事件可以终止timer的执行,默认是task-end。可以指定多个事件,以','分割,任一事件触发timer即终止。
  可以看到jbpm对任务实例和节点执行时的时间服务还是支持的很好,可以做出很多的扩展,但是它没有对整个流程实例本身提供更多的服务,比如说定时的流程启动和整个流程的时间控制等等。以及对精确时间点的支持还不够。
posted @ 2007-06-21 12:00 ronghao 阅读(1713) | 评论 (2)编辑 收藏
jbpm BusinessCalendar是一个很好用的计算工作日设定的时间服务组件,美中不足的是它的工作日设定是写死在配置文件中,不能灵活的由用户修改。另外hongsoft在他的博客中提到jbpm BusinessCalendar可能存在的一个bug:
http://blog.csdn.net/hongbo781202/archive/2006/02/28/612541.aspx
这个bug在我的测试中没有重现,我的jbpm版本是3.2,可以认为jbpm已经修复这个bug。另外在
jbpm BusinessCalendar的配置文件中有这么一行
weekday.thuesday=  9:00-12:00 & 12:30-17:00

可以理解为是一个疏忽,应该是tuesday,礼拜二,呵呵。
posted @ 2007-06-15 17:58 ronghao 阅读(659) | 评论 (0)编辑 收藏
工作流时间管理按功能分类
  1. 时间事件启动工作流流程实例(指定时间点、时间间隔、周期时间)
  2. 任务挂起恢复(指定时间点、时间间隔)
  3. 任务预警、报警、超时通知
  4. 工作流流程实例超时通知
  5. 非工作日、节假日设定
  6. 流程、任务的处理时间统计
具体说明
  1、工作流流程实例在设置的时间自动启动,设置时间包括下面两种方式:
     a、指定一个固定的时间点,然后设置周期时间,例如每天、每周的周一、每月的第一天;
     b、指定一个固定的时间点,然后设置时间间隔,例如20分钟后,2小时后,一天后,一个月后。
  2、任务在上一个任务节点完成后多长时间启动。并发任务之间的时间启动关系。任务在指定时间点启动。
     举例:财务每周五下午2点开始集中处理报销事务,所有流程实例流转到财务报销节点处于等待状态,直到周五下午2点任务才启动,财务才在任务列表里看到待处理的报销事务并集中处理。
  3、举例说明:经理审批这个任务节点设置完成时间1小时
     预警:时间过去预定完成时间一定百分比比如50%还未完成,则在任务发出半小时后系统发出预警信息,按一定时间间隔循环发出。直到任务报警或任务超时或任务完成。
     报警:时间过去预定完成时间一定百分比比如90%还未完成,则在任务发出54分钟后系统发出报警信息,预警自动终止。报警信息只发送一次。
     超时通知:任务在规定时间内未完成,系统发出超时通知。同时任务超时存在业务或流程处理,任务
超时应当可以挂上javabean处理一定业务逻辑,同时流程可以选择继续等待或是跳转。
  4、和任务超时类似,系统发送超时通知,同时应该存在业务和流程的处理。比如说流程自动终止。
  5、主要提供时间计算时对非工作日、非工作时间和节假日的考虑。这里的时间计算仅仅针对于输入一个时间计算一定时间间隔后输出一个时间,比如说现在是周五2点,输入,两天时间间隔,输入,周日2点,输出。考虑非工作日,则输出应该为下周二2点。用途主要体现在对任务和流程的时间完成期限限定计算上。
  6、统计,报表。
大家提提自己的意见。

posted @ 2007-06-14 11:42 ronghao 阅读(1804) | 评论 (3)编辑 收藏
这是我前年AXIS学习的笔记,最早写在了JR上,是分散的五篇(其中一篇是转载)。按内容分别是:
1、介绍AXIS
2、使用Handler来增强Web服务的功能(这是一篇转载)
3、建立安全的AXIS服务(上)
4、建立安全的AXIS服务(下)
5、AXIS服务间传递JavaBean及其安全解决
其实很多内容现在自己看来写的都很浅显,包括输出大量使用了System.out以及没有测试。这里也不打算
修改了,简单整理了一下并提供源代码的下载。希望能给需要的人一些帮助。
代码下载
posted @ 2007-06-12 16:36 ronghao 阅读(2242) | 评论 (0)编辑 收藏
     摘要: 这是AXIS学习笔记的最后一篇。在前面我们讨论了最简单的HelloWorld服务,客户端并没有向服务器端传递参数,现在我们来传JavaBean。当然,也可以传递你自己定义的JAVA类,但那样你必须自己创建专门的XML序列化器和反序列化器;而对JavaBean,AXIS提供了现成的序列化器。(有人说:懒惰是程序员最大的美德)    一、服务器端   1、...  阅读全文
posted @ 2007-06-12 16:25 ronghao 阅读(5323) | 评论 (3)编辑 收藏
     摘要: 四、使用WS-Security规范对信息进行加密与身份认证     我们打算用Handler结合WSSecurity实现Web服务安全     设想流程:用WSClientRequestHandler.java位于客户端对客户端发出的XML文档进行加密  WSServerRequestHandler.ja...  阅读全文
posted @ 2007-06-12 16:20 ronghao 阅读(2240) | 评论 (4)编辑 收藏
     摘要: 在前面的文章中,我们实现了最简单的AXIS服务。现在我们一起来讨论一下Web服务的安全问题。 根据应用的对安全要求的级别不同,可以采用不同的方式来实现安全性,以下是目前最常用的一些实现方式(从低到高排列):     1、J2EE Web应用默认的访问控制(数据是明文的);      2、使用a...  阅读全文
posted @ 2007-06-12 16:10 ronghao 阅读(3927) | 评论 (4)编辑 收藏
     摘要: 原文作者: 陈亚强 原文链接:http://www.ibm.com/developerworks/cn/webservices/ws-handler/index.html 高级软件工程师, 北京华园天一科技有限公司 2003 年 8 月  一、Handler的基本概念 J2EE Web 服务中的H...  阅读全文
posted @ 2007-06-12 16:01 ronghao 阅读(3127) | 评论 (1)编辑 收藏

一、软件环境

 1
axis-1_2  (apache网站下载最新axis-bin-1_2.zip解压即可)

 2
Tomcat5.0 

 3
JDK5.0

二、相关配置

 1
、在你的%TOMCAT_HOME%\common\lib下需要加入三个包 activation.jarmail.jartools.jar

 2
、环境变量设置

     AXIS_HOME 
axis-bin-1_2.zip解压的目录(我的是在F:\soap\axis-1_2

     AXIS_LIB    
 %AXIS_HOME%\lib

    AXISCLASSPATH 
 %AXIS_LIB%\axis.jar;%AXIS_LIB%\commons-discovery-0.2.jar;%AXIS_LIB%\commons-logging-1.0.4.jar;

%AXIS_LIB%\jaxrpc.jar;%AXIS_LIB%\saaj.jar;%AXIS_LIB%\log4j-1.2.8.jar;也就是把%AXIS_LIB%下所用JAR文件都导入

三、实验一下

   
%AXIS_HOME%\webapps下找到axis文件夹,将其整个拷贝到%TOMCAT_HOME%\webapps下,启动

  Tomcat,
打开浏览器访问http://localhost:8080/axis/,出现以下页面说明你配置成功了。很简单吧:)


四、发布我们的第一个程序

   
第一个程序简单的返回HELLO WORLD

 HelloWorld.java

public class HelloWorld {
  
public String sayHello()
  {
    
return "HELLO WORLD!"
  } 
}


我们的第一种发布方式:

HelloWorld.java拷贝到%TOMCAT_HOME%\webapps\axis下,然后将其改名为HelloWorld.jws,这样AXIS就自然将其发布了。现在写个客户端程序访问一下:
TestClient.java

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;

import javax.xml.rpc.ParameterMode;

public class TestClient
{
   
public static void main(String [] args) throws Exception {
       
       String endpoint 
= "http://localhost:" +"8080"+ "/axis/HelloWorld.jws";//指明服务所在位置

       Service  service 
= new Service();  //创建一个Service实例,注意是必须的!
       Call     call    = (Call) service.createCall();//创建Call实例,也是必须的!

     call.setTargetEndpointAddress( 
new java.net.URL(endpoint) );//为Call设置服务的位置

        call.setOperationName( 
"sayHello" );//注意方法名与HelloWorld.java中一样!!

         String res 
= (String) call.invoke( new Object[] {} );//返回String,没有传入参数

        System.out.println( res );
   }
}


我的测试是在jbuilder2005中,注意项目中要导入其自带的AXIS包(当然应该把其中JAR文件替换一下),可以看到程序返回了 "HELLO WORLD!"

可以看到在AXIS里发布服务其实是一件很容易的事,这是因为这个服务很简单的原因:)下面我们介绍第二种发布方式,这是常用的。

我们的第二种发布方式:

1
、将HelloWorld.java编译成HelloWorld.class,放到%TOMCAT_HOME%\webapps\axis\WEB-INF\classes

2
、在%TOMCAT_HOME%\webapps\axis\WEB-INF下新建deploy.wsdd文件,即SOAP服务发布描述文件

     deploy.wsdd

<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
    
<service name="HelloWorld" provider="java:RPC">
        
<parameter name="className" value="HelloWorld"/>
        
<parameter name="allowedMethods" value="sayHello"/>
    
</service>
</deployment>


DOS下转换目录到%TOMCAT_HOME%\webapps\axis\WEB-INF,命令:

java -cp %AXISCLASSPATH% org.apache.axis.client.AdminClient deploy.wsdd

你会发现目录下多了一个server-config.wsdd文件,这就是AXIS的配置文件,以后所有的服务发布描述都会在里面找到。(当然,你可以直接修改它,不用再写deploy.wsdd)然后打开浏览器http://localhost:8080/axis/servlet/AxisServlet,你就会看到你的服务已发布

同样用客户端程序访问一下:(注意和上边的差别!!)
HelloClient.java

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;

public class HelloClient
{
   
public static void main(String [] args) throws Exception {

       String endpoint 
= "http://localhost:" +"8080"+ "/axis/services/HelloWorld";//注意!差别仅仅在这里!!

       Service  service 
= new Service();
       Call     call    
= (Call) service.createCall();
       call.setTargetEndpointAddress( 
new java.net.URL(endpoint) );
        call.setOperationName(
"sayHello"  );

         String res 
= (String) call.invoke( new Object[] {} );

                         System.out.println( res );
   }
}

posted @ 2007-06-12 15:48 ronghao 阅读(2659) | 评论 (1)编辑 收藏
    这段时间突然多了好多客户演示,与单纯的OA不同,这次客户的项目涉及比较广泛,有税务有银行甚至还有erp,都是业务系统。与OA相比,更多的业务操作加入到了流程当中。这是个很好的现象,workflow的市场在持续增长。用户本身以前从来没有用过工作流,都是从去年才开始逐渐关注。有流程的地方就应该有工作流,这是用户的一个认知过程。工作流的使用在大众化,并不再局限于OA和一些特定的领域,由此带来的必然是工作流的竞争加剧和价格的白菜化。
    对工作流厂商来说,我觉得现在是一个很好的机会。就工作流系统本身,各个厂家产品的功能应该相差并不多,关键还在于产品的成熟度,如果听到用户抱怨他们成了你的测试部时,那你基本上就完了。在大的功能相差不大的情况下,剩下的就是用户体验,如何尽可能的让用户开发方便,良好的可扩展的接口固然不错,提供几个典型的实现就更好了。甚至在对方也采用webwork、struts的情况下可以提供已封装好的action,或者页面组件。最后不得不提的是价格,对一个成熟的产品来说,实际上它的成本是接近于0的,但实际情况切是往往需要很多的技术支持,越是不成熟的产品它的成本必然越高。就功能上来说,门户搭配工作项列表比单纯的工作项列表要好很多。
    在workflow价格低廉的情况下,BPM无疑是一个新的利润点。workflow在大多情况下是一种嵌入式,BPM则是独立部署。在用户实际业务集成的需求下,BPM更多的是一种高端的产品出现。目前国内真正意义上的BPM还是处于空白的状态,有的也只是在workflow中加入一些统计和报表的功能,一种概念上的炒作。所以说现在BPM正当时,对工作流厂家来说,BPM应该是努力的方向。
    但是BPM对于很多研发团队只有几个人的厂家来说是否又过于遥远呢?
posted @ 2007-06-08 17:36 ronghao 阅读(1531) | 评论 (1)编辑 收藏
一个朋友留言,提了一些关心的问题,这里说说自己的想法(不一定对)。
问题:
数据范围的控制:“事实上不是每个用户都可以看到所有记录的。以财务管理为例,部门经理只能查看金额小于1W的数据;而总经理则没有限制”。象这样要根据数据的某个字段对数据范围进行控制,应该实现呢,是通过AOP动态改变执行的SQL吗?这样似乎不太可行,如果要执行的SQL特别复杂,那动态改变SQL 就更难了。现在的应用中一般使用hibernate,这样的话就变成了动态改变HQL,也是难的。
回答:目前在我的代码里就是通过AOP动态改变执行的SQL,改变HQL确实很困难,但是改变criteria就比较简单了。对于特别复杂的sql,我的建议是把这些SQL直接写到你的业务程序里去,或者单独配置出来,和ibatis比较类似。

问题:单条数据ACL权限:由于数据是不断增加的,所以要对单条数据的ACL权限,不应该是数据已存在,然后再对存在的数据授权,应该是对某种规则进行授权。以你举的个人通讯录为例,各人自己维护自己的通讯记录,数据只对自己可见,要想实现对自己的数据的可再授权,应该是对符合某个规则的数据进行授权,也就是说 “要执行授权操作的人就是数据的拥有者”这是个规则,那是不是应该对这个规则授权呢。
回答:我理解的和你不一样。我的理解是这样的,实际中我把单条数据的权限划分为拥有、浏览、修改、删除四种权限,用户拥有哪种权限就可以对数据进行相应哪种操作。“要执行授权操作的人就是数据的拥有者”也可以理解为“要执行授权操作的人就必须有该数据的拥有权限”,这可以理解为一种规则,但我更愿意把它理解为一种习惯,很显然对习惯授权是没有意义的。当然这里是存在规则的,这种规则简单的说是这样:当我新增一条数据时,哪些人对该条数据拥有拥有的权限,哪些人对该条数据拥有浏览的权限,哪些人对该条数据拥有修改的权限,哪些人对该条数据拥有删除的权限。权限相关记录会在新增这条数据时根据该规则生成。在上面的例子里,这一规则体现在:各人自己维护自己的通讯记录,数据只对自己可见。也就是说在用户新增自己的通讯记录时,系统同时往权限表里插入了该用户对该记录的一个拥有权限记录。

问题:数据字段权限:要实现对某个字段的权限控制,那是不是应该所有字段应该有个默认的操作呢,或者默认就是只可以查看,要进行修改的话就必须授权。但是通常整个应用中的字段非常多,这样是乎不太合理。那是不是可以做成所有字段默认就是可以CRUD的,只有要控制的字段才进行权限判断。同样,由于使用 Hibernate来进行持久化,那对字段的控制是不是就变成了对类中属性的控制。
回答:数据字段权限一直是一个很难办的事情,实际上我很倾向于把这个问题推到页面来解决。其实所有的权限控制最后都是要通过页面来表现的。其实对字段来说,所需要的权限也很简单:可见/不可见,只读/可修改。这样的话,通过标签的形式来控制字段的显示、只读就显得很自然。对字段的权限记录可以放入到数据库,或者xml中,与具体的pojo类没有关系,当渲染页面的时候,由标签来读取相关权限记录并控制显示。

问题:角色权限的继承:角色权限的继承通过规则来做,这个规则应该怎么设计呢。
回答:这个问题其实是一个分离关注点的问题。你可以抽象出一个规则接口,这个接口定义了对部门、角色下的用户而言,哪些权限是可以从部门角色继承的,继承几级,哪些是不可以的。然后再具体实现。更灵活的方式是定义出一个配置文件,运行时可以灵活修改。


posted @ 2007-06-05 17:36 ronghao 阅读(4053) | 评论 (3)编辑 收藏
平台现在无疑已经是一个用滥的概念。先来看看业务平台的定义:是为企业提供的一个可快速开发的、稳定的、成熟的业务基础开发平台。实际上在一个技术人员的眼里,平台=类springside的开发框架+一套组织机构+和组织机构相适应的一套权限系统+门户+信息资源库+工作流引擎+一些通用的业务模块。因为一直做关于所谓业务平台的开发,有过困惑也有思考,这里说说自己对于平台的一些想法,也希望大家多多拍砖。我认为一个好的业务平台应该做到下面的几点:
1、业务性
   这个问题我曾经和胡长城有过很长时间的争论。最后我得承认胡兄的正确性:业务确实是一个平台的根本。一个业务平台如果不满足业务的需求,那么它存在的意义就值得怀疑。一个很简单的例子,协同办公中不可缺少的发文管理,正文需要用到电子签章,业务中有这个需求,平台不支持,那么这个平台就不是个好的业务平台。
2、易用性
   业务平台不是面对最终用户的,它需要做二次开发。这样平台对程序员的友好性就相当重要,毕竟平台的使用者还是程序员。
   基础代码生成是必要的,不管你采用何种方式,是根据已有表格来生成模型和相应代码还是先画出模型再来生成表格和相应代码(所谓的MDA)。平台的代码结构最好能够和IDE有个良好的结合,因为项目的开发往往是几个程序员协作的过程,平台对代码开发的支持不可能做到IDE的水平。最好的方式是生成的代码自动在IDE里有着清晰的结构,在IDE里编辑、测试自己的代码,然后自动发布到平台相应目录中,启动平台,代码运行成功。eclipse插件是个很好的选择。
   良好的API的支持,开发中不可避免的会与平台已有的组织机构模型和权限系统交互,这样良好的、封装清晰的API支持就很重要。
   页面组件的支持。往往有时候凌乱的WEB页面最让人头疼。对常用的一些页面元素可以用标签或是AJAX做进一步的封装。比如树组件、弹出层组件、分页组件等等。与这些相比,一些业务页面组件的封装也很重要。比如需要在页面选择用户,直接封装出一个用户选择组件,而不用开发人员在页面用弹出层+用户树自己再去组装。再比如说发文中的签批意见,可以用AJAX直接封装到页面里,不用用户自己调用相应API。意思就是让用户开发尽量简单,把工作从代码里尽量推到页面上去。
3、模块化。
   尽管这个或类似概念(比如说组件、构件)一再被平台厂家们提及,我对实际的实现一直报怀疑的态度。实际的实现往往是对模块的一个模拟。这里的模块实际上是对代码做出的一种人为上的区分,所谓的模块配置,更直接一点就是页面URL的配置。与代码行为无关,与运行期管理更没关。
4、可扩展性
   这个暂且不说,很大的一个话题。
5、前瞻性
   一个优秀的平台凭什么和别人不同,一个很重要的一点就是平台的前瞻性。一句话,就是要有别人没有的东西。个人非常看好未来对业务整合的需求,遗留的业务系统+国外大厂商的概念轰炸让客户开口就是业务整合、BPM。是时候不再忽悠而是做点实际的东西了。
可以说,业务性+易用性在现阶段就是一个很好的平台了。如果再有些前瞻性的东西(注意:不是口头上!)这个平台应该说很优秀了。至于模块化、可扩展性,想想,现在只能说忽悠了。
posted @ 2007-05-22 16:22 ronghao 阅读(1283) | 评论 (1)编辑 收藏
随着近两年来各企业/单位的基础系统的建设,应用集成的需求已经越来越急切。个人认为现在的应用集成可以分为以下三种情况:
  一、门户+单点登录。这种情况最简单,说白了就是简单的页面集成。各个系统通过门户统一登录,登录完毕后在门户上显示各自的业务页面,当需要具体处理各项业务时跳转到各自的业务系统里。当然这里也有问题,仅仅B/S系统能做这种集成。这种情况也是实际项目中碰到最多的情况。
  二、数据集成。这个和第一种情况相比就复杂了很多。拿一个简单的情况来说,系统A和系统B里都有各自的一套组织机构,现在我想做集成,只保留一套组织机构然后做统一管理。需求合情合理,处理起来就麻烦了。模型设计相似还好一点,再简单一点可以做数据库的同步。但这往往是开发人员的一厢情愿。写适配器几乎是必须的。模型的不同带来的问题是最大的,系统A里有岗位这个对象,系统B里没有,怎么办?这种情况在实际项目中越来越多了,然后每一次都让人特别的难受。
  三、业务集成。提到业务集成,不得不说说SOA。SCA让业务集成看起来那么的顺理成章,SDO又搞定了数据交换这个头痛的问题。一切都是那么的美好(有点不太真实,嚯嚯)。因为最近对BPM关注比较多也准备往这方面做一些尝试,所以这里拿BPM举例,好比一个公司录人的流程,一开始我会调用原有的HR系统的业务服务录入人员信息,然后我又会在下一个流程节点调用财务系统相应的业务服务来计算新员工工资。可以这样认为,BPM是业务集成的最好的例子。这种情况在实际中用户也越来越多的提出来,或者说提到了这个概念(和各大公司的宣传有很大的关系)。个人也认为这一块应该有很大的发展空间。麻烦的地方也在于数据或者说服务的调用交互。
除了第一种情况,剩下两种情况都是很麻烦的。很多人都在说业务集成,但是数据集成是在任何有不止一套遗留系统时必须面对的问题。甚至我可以这么认为,数据集成是最困难的,因为它还没有一个标准,也不会有标准了。
posted @ 2007-05-18 18:08 ronghao 阅读(1033) | 评论 (0)编辑 收藏
最新的1.2*版本开始支持jndi数据源,版本与1.*完全兼容。注意的是以前的jackrabbit-core-1.x.jar现在
需要jackrabbit-core.jar,jackrabbit-api.jar, jackrabbit-jcr-commons.jar三个包来替代;另外,其要求Lucene 的版本要2.0,下了个2.1不行。
然后就是改配置文件。
原先的配置
        <PersistenceManager class="org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager">
        
<param name="driver" value="com.newatlanta.jturbo.driver.Driver"/>
        
<param name="url" value="jdbc:JTurbo://192.168.0.2:1433/bizfocus50"/>
        
<param name="schema" value="mssql"/>
        
<param name="user" value="sa"/>
        
<param name="password" value="sa"/>
        
<param name="schemaObjectPrefix" value="${wsp.name}_"/>
        
<param name="externalBLOBs" value="false"/>
    
</PersistenceManager>

现在的配置:
    <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.JNDIDatabasePersistenceManager">
      
<param name="dataSourceLocation" value="java:comp/env/jdbc/wfmsDataSource" />
      
<param name="schemaObjectPrefix" value="DEFAULT_" />
      
<param name="externalBLOBs" value="false" />
  
</PersistenceManager>

还有就是:不要仅仅修改你总的那个配置文件,每个工作区间下的配置文件都要同时修改,却记却记啊!
posted @ 2007-04-05 18:44 ronghao 阅读(1166) | 评论 (2)编辑 收藏
周末参加了两次活动,一次是BJUG,一次是BEA UG。
BJUG的活动相对轻松些,没有固定的topic,每人都要写一个topic然后大家投票选择。每个人都可以发言,在topic演讲者演讲的时候可以随时举手打断提出自己的疑问和看法。
第一个topic是关于tw的Pre-Sales,很有意思的Sale。其实BUG和项目延期一直是开发中让人头痛的两个问题。现在我就饱受这样的苦恼。解决的方法其实也很简单:测试和快速迭代。冰云从经济学的基础上分析了快速版本迭代的收益。问题在于:对一个接近完成而又测试覆盖率很低的项目还有敏捷的必要吗?如何在现有工作中的团队里引入敏捷?这其实也是我想提出的问题。个人觉得如果从项目一开始就敏捷会容易的多。接着这个话题,讲了他所在团队如何进行敏捷。每天的日构建,每周固定一次的版本迭代,和客户频繁的交互需求,良好的项目进度和反馈。这个团队敏捷的让人嫉妒。我觉得敏捷的推行除去个人的部分关键在于领导的决心,开始的不适应是肯定的,领导能不能坚持才最重要。
第二个topic是郑晔介绍他的开源项目xruby。我对ruby并不了解。理解xruby是把ruby转化为java的class文件,同时提供了运行期的一些扩展。因为ruby是动态语言,所以这些扩展也是必须的。很有意义的项目,用ruby快速开发,然后通过xruby编译为java,最后部署到java服务器里。只是我后来想想,与其给ruby做辛苦的转换,不如通过这些扩展给java本身提供更多的动态特性。也许出现一个框架性的东西也有可能。xruby的主页
最后一个topic也就比较随意了,因为时间不够了。讨论了个人的职业发展。确实要和自己的性格结合起来。
遗憾就是自己一直都没有一个很好的topic,工作中的总结太少,或者说自己知道的东西还很少。下次一定努力。
BEA UG。其实很早就和霍泰稳说好要去帮忙的,路上堵车,到的时候都快一点了,也是很不好意思的。认识了胡长城,认识了李文章,碰见了莫映,都是工作流方面的专家。第一个演讲嘉宾是李文章,当时我也提出了自己的问题,有些问题上还需要继续请教,应该还会有机会。莫映,聊了一会,他们做的东西和我们竟然非常相似,寒一个。其实现在谁都在向一个平台产品发展。胡长城,约好了要到我们公司来看看。呵呵,最后抽奖时还幸运的被抽到一个背包,真有意思。
posted @ 2007-03-26 18:17 ronghao 阅读(615) | 评论 (0)编辑 收藏
昨天参加了BEA UG的活动。其中第二场是BEA罗振东先生的BPM讲解。因为公司一直就是做工作流的,所以对BPM这个概念一直是非常的关注,但是一直也是搞不太清楚Workflow与BPM的关系,总是以为BPM是对WorkFlow的一层包装而已,是新瓶装老酒。在听罗振东先生演讲的过程中,我开始有了一些自己的认识。
一句话说:BPM是建立在EAI基础上的工作流。
和工作流不同,BPM关注的是一个很完整概念上的业务流程,这个业务流程可能需要横跨多个IT系统,这些系统通过某种方式暴露出流程中所需要的服务(webservice是一种选择),BPM推动这个流程的流转。同时,相对于以往的工作流单纯的流程流转,BPM提供了更多,包括流程仿真,过程分析、过程优化等等。意思就是,在某个流程运行一段时间以后,BPM会基于数据提供对该流程的分析(数据挖掘?),从而能够基于这些分析提供对上层管理决策的支持。有点像运筹学。
那么,一个工作流厂商是否可以很容易的研发出BPM的产品?答案是不行。看看哪些BPM的厂商,无一不是在EAI方面有很多经验的大公司。所以,BPM实施的关键还是要建立在EAI实施的基础上。至于BPM和SOA,如果以前的系统是基于SOA架构的,那自然EAI起来是会更加容易,BPM理所当然是拥抱SOA的。(BEA的产品没用adapter)
那么当前工作流的发展方向呢?个人认为可以从BPM的功能里找到一些线索,那就是流程仿真,过程分析、过程优化。比如一个请假申请流程,统计一下,在哪个节点的办理效率最低,哪些节点在实际中不是必须等等,当然这些都是工作流本身基于流程的数据进行的独立的分析,有点决策的意思在里面。
呵呵,个人的一些浅见。希望多批评。
posted @ 2007-03-26 14:27 ronghao 阅读(2281) | 评论 (1)编辑 收藏
一开始我把控制数据权限写在业务里,以订单管理为例,先讨论一个最简单的情况。管理员可以看所有的订单,而用户只能看自己的订单。这里的管理员是一个角色。我会这么写(一些次要代码都省略了):

   List getOrders(String userId){
             String sql;
             Role role
=orgService.getRoleForUser(userId);
             
if(("admin").equals(role.getName()))
                   sql
="select * from order";
             
else
                   sql
="select * from order where author="+userId;
             Object o
=excuteSql(sql);
             
return excute(o);
        }

恩,不错,很好的完成了权限控制。过了没多久,公司发展了,老板增加了人手,老板发话了,我要设置区域管理员分管不同区域的订单。管理员分为北京地区管理员,上海地区管理员,其他地区管理员和总管理员。怎么办?修改代码吧
    List getOrders(String userId){
             String sql;
             Role role
=orgService.getRoleForUser(userId);
             
if(("admin").equals(role.getName()))
                  sql
="select * from order";
             
else if("bjadmin").equals(role.getName()))
                         sql
="select * from order where area='beijing'";
             
else if("shadmin").equals(role.getName()))
                         sql
="select * from order where area='shanghai'";
             
else if("qtadmin").equals(role.getName()))
                         sql
="select * from order where area='qita'";
             
else
                   sql
="select * from order where author="+userId;
             Object o
=excuteSql(sql);
             
return excute(o);
        }

恩恩,这就搞定了,可怎么也感觉不爽,也许该做点什么。一堆if/else权限判断让人心烦,再写个类把这些sql管理起来好了,那就动手吧
  public class SqlManager{
          String getSql(String userId){
                  String sql;
          Role role
=orgService.getRoleForUser(userId);
          
if(("admin").equals(role.getName()))
              sql
="select * from order";
          
else if("bjadmin").equals(role.getName()))
                   sql
="select * from order where area='beijing'";
          
else if("shadmin").equals(role.getName()))
                   sql
="select * from order where area='shanghai'";
          
else if("qtadmin").equals(role.getName()))
                   sql
="select * from order where area='qita'";
          
else
            sql
="select * from order where author="+userId;
          
return sql;
          }
  }

这样把权限判断移到SqlManager里,业务代码就清爽了很多,再增加管理员就修改SqlManager好了
    List getOrders(String userId){
             String sql
=sqlManager.getSql(userId);
             Object o
=excuteSql(sql);
             
return excute(o);
        }

呵呵,看起来还不错。但是等等,我们的业务方法为什么需要userId这个参数呢,是啊是啊,权限判断用到了它,但是那和我业务又有什么关系呢,不爽。现在AOP不是很流行吗,你不用AOP怎么能说明你技术高呢?快用吧快用吧,用不着也要想着方法用。
业务方法简化为
    List getOrders(){
             String sql
="";
             Object o
=excuteSql(sql);
             
return excute(o);
        }

对excuteSql方法我们来AOP一下,注入权限判断过的sql.嘿嘿,技术水平又一次得到了显现。业务方法是简单了,可我的SqlManager倒是复杂了,还是很不幸福。咋办?用个配置文件吧,hibernate不是老鼓励我们把sql写在配置文件里吗?
    <xml>
        <sql role
="admin">select * from order</sql>
        <sql role
="bjadmin">select * from order where area='beijing'</sql>
        <sql role
="shadmin">select * from order where area='shanghai'</sql>
        <sql role
="qtadmin">select * from order where area='qita'</sql>
        <sql role
="none">select * from order where author=?</sql>
    </xml>

这样SqlManager就可以把行数缩小了,就可以敏捷一点了。
    public class SqlManager{
          String getSql(String userId){
                  String sql;
          Role role
=orgService.getRoleForUser(userId);
          sql
=getSqlfromXml(role.getName());
          
return sql;
          }
          
          String getSqlfromXml(String rolename){
                  .
          }
  }

以后再增加权限连类都不用修改了,改xml好了。等等,你是不是把问题太简单化了。现在不仅仅是订单,货物也要这么分区域管理。
不错,我们应该想着通用一下了。这样,把SqlManager抽象一下
        String abstract getSqlfromXml(String rolename);

然后做几个子类好了 OrderSqlManager, GoodsSqlManager .
可是,哥们,书上说,要面向接口编程,你这样不太好吧。没事,再接口一下:
   public interface SqlManagerInterface{
           String getSql(String userId);
   }

还是没法用啊。也许现在可以看看acegi的provider机制了,把这一大堆SqlManager全部作为provider,根据不同的模块选择不同的provider,统一拦截excuteSql方法,生成不同的sql到数据库执行。xml不爽?db也可以。然后,再然后呢?改你的类名,重构,和acegi整合一下。
呵呵,完全是个人的一些想法,希望多批评提提意见。我想表达的意思是:也许把数据权限再抽象一些,以组件的形式来减少侵入是可以做到的。
posted @ 2007-03-23 18:35 ronghao 阅读(6705) | 评论 (6)编辑 收藏
什么是权限系统,权限系统究竟在整个系统中起到什么作用,或者说权限系统必须提供哪些功能?
谈谈个人的看法,我认为授权和验证是一个权限系统最基本的功能,而一个更完善的权限系统会增加上如何验证和验证后如何处理这两种功能。
对于一个权限系统的基本元素,我觉得只有两个:Principal(权限主体),权限。用户,用户组,角色等都可以抽象为权限主体这个概念;而权限则是资源+操作的抽象。权限可分为两种,一种纯粹就是资源,没有操作,也可以认为是默认操作;另外一种就是带操作的资源。授权本身没有什么说的,赋予权限主体权限;验证简单的说就是传入权限主体和当前操作需要的权限,然后验证返回说可以操作或是不可以操作yes/no。同时要注意的是验证的时候需要一个类(接口)来返回用户当前所拥有的权限,然后与当前操作需要的权限进行比对,最后返回比对结果。
以acegi为例,下面分开来说明:
首先,acegi是没有授权功能的,它简单把权限主体与资源配置在了xml里,当然这也带来了可扩展性,可以看一个配置:
                /index.jsp=ROLE_ANONYMOUS,ROLE_USER
                /hello.htm=ROLE_ANONYMOUS,ROLE_USER
                /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER
                /switchuser.jsp=ROLE_SUPERVISOR
                /j_acegi_switch_user=ROLE_SUPERVISOR
                /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER
                /**=ROLE_USER

/index.jsp,/hello.htm这些URL都是资源,因为它默认的操作就是访问,所以访问特定URL就成了一个权限,ROLE_ANONYMOUS,ROLE_USER无疑就是权限主体。
Acegi对方法访问权限的配置也是和上面一样的。
然后,看看acegi的验证,其实它的验证就是一个个的Voter,当你访问一个方法时,由它获得当前用户和需要的权限(就是该方法),然后返回yes/no。
OK,这就是一个完整的权限系统了。当然acegi如果仅仅提供上面两个基本功能,它是不会向现在这样成功的,它最大的亮点就是提供了如何验证和验证后如何处理这两种功能。对url它提供了filter,方法也一样提供了Interceptor,这就是如何验证,和我们在action里验证没有任何区别,只是换了一种方式而已。然后它验证后的处理方式:yes就继续往下执行,no就抛出异常最后再处理这个异常。我们在执行每个action前同样也可以做到这一点。
这里着重要提到的是数据权限。比如说A只能对部门A下的数据有查看权限,B可以对部门A的数据有修改权限和对部门B的数据的查看权限。
看起来这个需求是比较麻烦的,但是我们可以按上面说的拆分以下:
资源1:部门A下的数据  ;资源2:部门B下的数据
操作1:查看;    操作2:修改
产生四种权限:
权限1:资源1+操作1 ;权限2:资源1+操作2;权限3:资源2+操作1;权限4:资源2+操作2
权限主体A拥有权限:1 ; 权限主体B拥有权限:2,3
这样就非常清晰了。
总结:我始终认为拥有授权和验证功能就完成了一个完整的权限系统。至于如何去校验权限,不管你是filter还是aop那是具体实现时的细节,采取的不同策略。而验证后的处理则是和不同策略相对应的。在你考虑设计一个权限系统时首先考虑的是如何实现授权和验证,而不是一开始就考虑我是采取filter,还是aop,还是动态改变sql。
谢谢wolfsquare,它的blog相关
posted @ 2007-03-21 19:04 ronghao 阅读(4144) | 评论 (1)编辑 收藏
本文假设你对Acegi其他部分已经比较熟悉。Acegi 的ACL控制是建立在对相应业务方法拦截的基础上的。这里以Acegi自带的contacts例子来说明。
先看看总的配置

<bean id="contactManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
      
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
      
<property name="accessDecisionManager"><ref local="businessAccessDecisionManager"/></property>
      
<property name="afterInvocationManager"><ref local="afterInvocationManager"/></property>
      
<property name="objectDefinitionSource">
         
<value>
            sample.contact.ContactManager.getAll=AFTER_ACL_COLLECTION_READ
            sample.contact.ContactManager.getById=AFTER_ACL_READ
            sample.contact.ContactManager.delete=ACL_CONTACT_DELETE
            sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN
            sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN
         
</value>
      
</property>
   
</bean>
该拦截器实现了org.aopalliance.intercept.MethodInterceptor接口。在方法被调用之前,拦截器会先调用 AuthenticationManager判断用户身份是否已验证,然后从objectDefinitionSource中获取方法所对应的权限,再调用AccessDecisionManager来匹配用户权限和方法对应的权限。如果用户没有足够权限调用当前方法,则抛出 AccessDeniedException使方法不能被调用。方法调用后会调用AfterInvocationManager对返回的结果进行再次处理。下面依次说明。
AccessDecisionManager的配置
<bean id="businessAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
      
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
      
<property name="decisionVoters">
         
<list>
            
<ref local="roleVoter"/>
            
<ref local="aclContactReadVoter"/>
            
<ref local="aclContactDeleteVoter"/>
            
<ref local="aclContactAdminVoter"/>
         
</list>
      
</property>
   
</bean>
AccessDecisionManager接口有decide()和support()方法。decide()方法决策是否批准通过即方法是否容许调用,如果没抛出AccessDeniedException则为允许访问资源,否则拒绝访问。support()方法是根据配置属性和受保护资源的类来判断是否需要对该资源作出决策判断。
AccessDecisionManager的 decisionVoters属性需要一个或多个Voter(投票者),Voter必须实现AccessDecisionVoter 接口。Voter的工作是去匹配用户已拥有的权限和受保护的资源要求的权限,在该资源有相应权限的情况下,如果匹配则投允许票,否则投反对票。
allowIfAllAbstainDecisions属性表示是否允许所有都弃权时就通过。Voter的实现类RoleVoter在当受保护资源的名字由ROLE_开始时才参与投票。
AccessDecisionManager有三个实现类,功能各不相同:
AffirmativeBased: 当至少有一个Voter投允许票时才通过
UnanimousBased: 没有Voter投反对票时才通过
ConsensusBased: 当所有Voter都投允许票时才通过
下面列出一个Voter的配置
<bean id="aclContactDeleteVoter" class="org.acegisecurity.vote.BasicAclEntryVoter">
      
<property name="processConfigAttribute"><value>ACL_CONTACT_DELETE</value></property>
      
<property name="processDomainObjectClass"><value>sample.contact.Contact</value></property>
      
<property name="aclManager"><ref local="aclManager"/></property>
      
<property name="requirePermission">
        
<list>
          
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
          
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.DELETE"/>
        
</list>
      
</property>
   
</bean>
上面第一个配置里有这么一行:sample.contact.ContactManager.delete=ACL_CONTACT_DELETE
所以在调用sample.contact.ContactManager.delete这个方法时aclContactDeleteVoter会参与投票,它会获得sample.contact.Contact这个对象(这个对象从delete方法的参数中获得),然后通过aclManager去获得当前用户对该Contact实例的ACL权限,最后拿这个权限与我们需要的权限比对,我们配置需要的权限是org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION和org.acegisecurity.acl.basic.SimpleAclEntry.DELETE。如果我们通过aclManager获得的权限包括这两个配置的权限之一,Voter就投容许票,方法容许调用。如果不包括,那对不起,反对票,AccessDecisionManager就会抛出AccessDeniedException。方法拒绝调用。
AclManager的配置
<bean id="aclManager" class="org.acegisecurity.acl.AclProviderManager">
      
<property name="providers">
         
<list>
            
<ref local="basicAclProvider"/>
         
</list>
      
</property>
   
</bean>

   
<bean id="basicAclProvider" class="org.acegisecurity.acl.basic.BasicAclProvider">
      
<property name="basicAclDao"><ref local="basicAclExtendedDao"/></property>
   
</bean>

   
<bean id="basicAclExtendedDao" class="org.acegisecurity.acl.basic.jdbc.JdbcExtendedDaoImpl">
      
<property name="dataSource"><ref bean="dataSource"/></property>
   
</bean>
AclManager是整个ACL中一个很核心的概念,它包含了两个方法AclEntry[] getAcls(Object domainInstance)和
AclEntry[] getAcls(Object domainInstance, Authentication authentication)。在了解这两个方法前,我们先了解AclEntry这个对象。AclEntry只是一个接口,系统中一般都是造型为BasicAclEntry。它包括了这个Entry所保护的domainObject instance(这里是Contact),实际它实现上是以AclObjectIdentity来替代这个domainObject的(domainClass+domainObjectId);它包括了谁(Recipient)拥有这个domainObject instance以及他所对这个domainObject instance的操作权限(mask)。
一个domainObject instance对应了多个AclEntry,比如一条通讯录张三可以查看,而李四可以管理,一个Contact instance就对应了两个AclEntry,第一个AclEntry包含信息:所保护的domainObject(Contact),谁(张三),权限(查看);第二个AclEntry包含信息:所保护的domainObject(Contact),谁(李四),权限(管理)。
这样AclManager的两个方法就很好理解了getAcls(Object domainInstance)返回所有这个domainInstance所对应的权限信息,
getAcls(Object domainInstance, Authentication authentication)在第一个方法返回结果的基础上做了过滤,过滤出和authentication(当前用户)相关的权限信息。如果当前用户是张三,则返回与张三对应的记录。

这样Acegi就会拦截业务方法发挥相应的作用,但是在业务方法返回一个List或是单个domainObject instance的时候,同样也是需要把用户没有权限查看的domainObject instance过滤掉的,这时就要用afterInvocationManager了,看配置
<bean id="afterInvocationManager" class="org.acegisecurity.afterinvocation.AfterInvocationProviderManager">
      
<property name="providers">
         
<list>
            
<ref local="afterAclRead"/>
            
<ref local="afterAclCollectionRead"/>
         
</list>
      
</property>
   
</bean>
   
   
<!-- Processes AFTER_ACL_COLLECTION_READ configuration settings -->
   
<bean id="afterAclCollectionRead" class="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider">
      
<property name="aclManager"><ref local="aclManager"/></property>
      
<property name="requirePermission">
        
<list>
          
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
          
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.READ"/>
        
</list>
      
</property>
   
</bean>
   
   
<!-- Processes AFTER_ACL_READ configuration settings -->
   
<bean id="afterAclRead" class="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationProvider">
      
<property name="aclManager"><ref local="aclManager"/></property>
      
<property name="requirePermission">
        
<list>
          
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
          
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.READ"/>
        
</list>
      
</property>
   
</bean>
afterAclCollectionRead会对配置AFTER_ACL_COLLECTION_READ的方法进行拦截,这里是sample.contact.ContactManager.getAll方法,它会遍历方法返回的domainObject,然后挨个通过aclManager判断当前用户对domainObject的权限,如果和需要的权限不和,则过滤掉。呵呵,传说中的虎牙子就在此时产生了!
afterAclRead则依次类推。
参考了ss wiki里相关的文档,特别是差沙和cac的文档,写的相当好。另外acegi的代码也是相当的易读。
posted @ 2007-03-20 19:07 ronghao 阅读(4532) | 评论 (0)编辑 收藏
昨天对现有系统已实现的功能大概的描述了一下,其实里面我觉得最重要的是对客户可能存在的权限需求的分类。这两天也在写关于权限的文档,这里想到了对权限的第六种分类,其实之所以这样分类也是由于具体实现的差异所造成的。

六是数据范围操作权限,其实这个是可以和数据范围权限合为一个的。具体的区别在与对已经划分范围的数据再增加操作的权限控制。还是以财务管理为例,部门经理只能查看金额小于1W的数据;而总经理则没有限制,可以查看所有数据。但是请注意:他们只能对这些数据拥有查看的权限,不能修改或是删除,而财务则拥有修改的权限。在一些情况下可以用模块操作权限和数据范围权限的叠加来满足对该权限的需求,但是在权限复杂的情况下,这个权限独立出来是必须的。

事实上现有系统就是模块操作权限和数据范围权限的叠加来满足这部分需求的。

posted @ 2007-03-20 11:04 ronghao 阅读(2225) | 评论 (3)编辑 收藏
很早就完成了权限系统的编码,这里把一些功能说明贴出来,也希望提提意见。

权限系统基于acegi框架实现了从前端页面到后台数据全面的控制。在权限控制中,它将权限分成五类:  
 
一是系统权限,主要是对模块为单位的权限划分,具体就是用户对该模块可见不可见,能不能对该模块进行再授权的操作。表现在用户界面就是用户登录系统主页面后,可以看到的顶部菜单和左侧outlookbar菜单的内容控制。作为粒度最大的权限控制,系统实现了web url的防盗链功能。举例来说,用户新开发的一个叫车辆管理的模块,配置在http://localhost/business/carmanage.actionurl下,当对这个模块的权限加以控制后,直接在浏览器中键入http://localhost/business/carmanage.action同样是无法访问的,而不仅仅是界面内容的屏蔽。
 
二是模块操作权限,在对整个模块的权限做出控制后,这里继续对模块的浏览、增加,修改,删除的操作权限做出控制,也可以理解为对象权限 。还是以车辆管理为例,不同的人员对这个模块的操作是不同的,有些用户可以新增,删除车辆;而有些用户则只是可以对车辆的情况查看不能修改。通过系统提供的一套web 标签,页面可以根据用户不同的操作权限屏蔽相应的功能按键。例如删除,新增按键。用户绕过页面直接操作相应业务方法同样也做到了严格的控制,没有权限的访问会被拒绝同时记入日志。 
 
三是数据范围权限,又可以叫做对象实例级权限。事实上不是每个用户都可以看到所有记录的。以财务管理为例,部门经理只能查看金额小于1W的数据;而总经理则没有限制。权限系统对这部分权限也做出了全面的控制,可以根据数据类型,相应字段数值范围做出控制。
 
四是单条数据ACL权限,为了满足更严格的数据权限要求,权限系统对数据实现了单条数据的ACL权限,具体说就是对每条数据都实现了权限控制,每条数据都有一到多条权限数据与其对应。以个人通讯录为例,每个用户都维护自己的一个通讯录,这些数据都只是对本人可见,其他人不可见。但用户可以对这些数据做出授权,将某条联系方式以授权的方式共享给其他人,并赋予不同的权限,包括拥有,修改,删除,浏览四种权限。

五是数据字段权限,通过xml配置,系统保证了用户的最小粒度的权限控制。每条业务数据权限可以精确控制到每一个字段。包括单个字段的可否浏览以及可否修改。保证了敏感信息的安全性。


授权,作为权限系统的重要部分,系统提供了相当方便的操作体验。以树状的方式展现权限主体(用户,角色,部门)以及资源,方便,直接,一目了然。区别与传统的RBAC模型,权限不仅仅可以分配给角色,也可以分配给部门和用户。实际的权限是这三者权限的叠加,最大限度的方便用户操作。考虑到用户的扩展,系统提供两个权限继承规则接口,用户可以自定义权限继承的规则。例如,部门A下有部门A1,部门A的权限是否由部门A1继承。

整个权限系统的数据都建立在系统统一的缓存管理之上,用户登录后,其权限信息即被缓存,保证系统的效率
posted @ 2007-03-18 22:46 ronghao 阅读(4930) | 评论 (5)编辑 收藏
用hibernate做持久层工具,建立User对象,映射到sqlserver的USER表,但在做查询的时候不断的报sql错误,打印hibernate产生的sql语句到sqlserver查询工具执行,报同样的错误。给表名加上[]后sql可以正常执行。例如:select * from USER错误。select * from [USER]正确。开始以为是什么地方配置的问题。因为以前用过达梦数据库,它默认也是必须给表名加[]的,可以通过设置数据库属性解决这个问题。但也没有发现什么配置。后来在SecuritySite群提问,illusion提示是与系统表重名,更改表名后果然正常。于是感到很奇怪:USER这么常用的表名怎么会与系统表重名呢?
posted @ 2007-03-11 11:29 ronghao 阅读(1750) | 评论 (4)编辑 收藏
1.昨天开始在程序里增加和服务器端交互的部分。看看代码
var url = 'http://localhost:8080/app/get_data';
        var pars 
= '';
        
        var myAjax 
= new Ajax.Request(
            url, 
            {
                method: 
'get'
                parameters: pars, 
                onComplete: showResponse
            });
很简单的一段程序,在firefox里始终不能工作,但在ie下一切正常。打开debugger,跟踪调试到这一行
this.transport.open(this.options.method, this.url, 
        
this.options.asynchronous);

在这一行执行出现问题,干掉prototype,自己写XMLHttpRequest,问题同样出现在同一个地方
req.open("GET", url, true);
ie下正常。于是开始怀疑是firefox自己的问题,firefox可能更多的从安全方面做了考虑,拒绝http://localhost:8080这种带域名的访问方式。把js程序移到应用程序里,url改为
var url = '/app/get_data';
问题解决,访问正常。
2.点击日历单元格需要弹出一个窗口用于新增工作安排,在firfox里为了方便这样拼写
var TR=document.createElement("tr");
TR.innnerHTML
="<td><input ></td>";
firfox下正常,ie显示不出,改为下面方式则问题解决
var TR=document.createElement("tr");
var TD
=document.createElement("td");
TD.innnerHTML
="<input >";
TR.appendChild(TD);

posted @ 2007-02-28 21:53 ronghao 阅读(778) | 评论 (0)编辑 收藏
晚上9点的火车,回家!
posted @ 2007-02-15 15:30 ronghao 阅读(499) | 评论 (0)编辑 收藏
把firefox下js程序向ie兼容,一直用firefox调试,今天在ie下跑跑遇到了很多问题。
1.程序中用到prototype.js,对于事件监听,我一直这样处理
Event.observe(job.jobDiv.timeDiv, "mousedown", eway.TimeTableDiv.selectMoveJobDiv);
在firefox下完全正常,但在ie下就找不到视图所附加的对象了,改回来
job.jobDiv.timeDiv.onmousedown= eway.TimeTableDiv.selectMoveJobDiv
这样两个浏览器都可以跑。附一个简单的测试
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>    
<title></title>
<script src="lib/prototype/prototype.js" type="text/javascript"></script>
<script>
window.onload
=function(){
  
var b=new Button("wokao",$('simple'));
  }  
function Button(value,domEl){
  
this.domEl=domEl;
  
this.value=value;
  
this.domEl.buttonObj=this;
//  this.domEl.onclick=this.clickHandler;
  Event.observe(this.domEl, "mousedown"this.clickHandler);
}

Button.prototype.clickHandler
=function(){
  
var buttonObj=this.buttonObj;
  
var value=(buttonObj && buttonObj.value) ?
    buttonObj.value : 
"unknown value";
  alert(value);
}
</script>
<div class="resizeMe" id="simple">
    
<p>This is just a clean DIV</p>
</div>
</body>
</html>
2.处理td的跨行和跨列,都必须这样写
td.setAttribute("rowSpan",2);
td.setAttribute(
"colSpan",2);
注意rowSpancolSpan中间字母都是大写的,而写成rowspan,colspan在firefox下是没有任何问题的。
3.在拖拽层的时候会出现鼠标经过的文字被选中的现象,解决这个问题非常简单,当你开始拖拽前即你用鼠标点中你想要拖拽的层,这时候对事件进行处理
e=e||window.event;
if(e.stopProgation){
e.stopPropagation();
}
else{
e.cancelBubble 
= true;
}
if(e.preventDefault){
e.preventDefault();
}
else{
e.returnValue 
= false;

如果使用
prototype.js,可以简单为一行代码
Event.stop(e);

posted @ 2007-02-13 19:07 ronghao 阅读(1441) | 评论 (0)编辑 收藏
昨天发了个很什么的随笔,今天把与集群有关的东西搜了搜。整理一下。
什么是集群,集群的概念。下面这个BLOG讲的非常清楚:
http://blog.csdn.net/ESoftWind/archive/2006/10/19/1341089.aspx
web层次的集群方案讨论,看完javaeye相关的讨论,你会大概了解:
http://www.javaeye.com/topic/20298
注意里面robbin的无共享架构(Share Nothing Architecture)SNA。
web层次的集群主要技术就是:负载均衡和http session的失败转移。
负载均衡不再多说,焦点在于http session的失败转移。各个节点的http session复制会极大的影响性能。如何避免,robbin提出保持每个节点的无状态性,不再使用Session来保持全局状态。用户标示从cookie取得,假设不使用分布式Cache,session直接放在数据库中。他推荐了memcached作为分布式Cache,这样在从数据库读取session时中间又隔了一层Cache来提高性能。
大致的方法是这样:用户登陆的时候给他一个cookie,存放userId,同时给这个用户分配一个Session,存放user对象,然后 把这个session保存到数据库和分布式 Cache里。黏性会话。写一个filter或者 webwork拦截器对用户请求进行拦截,如果他有cookie,但是session里面没有user对象,说明前一个节点down掉了,就根据 cookie里面的userId查数据库或者是分布式 Cache获得先前保存的session,把原先的session复制到他的新session里面。这样各个节点间的 session就不用复制,因为 session是没有状态的。我们的程序对使用session不受影响,只是session里的对象要可序列化,当改变session里的对象时需要同步 到cache和数据库。当然,效率的原因,session里面东西越少越好,越稳定越好。
谁有这方面的经验?
posted @ 2007-02-12 23:18 ronghao 阅读(1294) | 评论 (0)编辑 收藏
最近要开发一个与拍卖有关的大访问量交易网站。一直做电子政务,对这方面没有任何经验。一开始考虑用php,mysql开发,后来由于觉得和交易相关,数据的一致性和安全一定很重要,最后考虑用java开发。
我不清楚在做这个开发时和平时相比有哪些需要注意的地方,我想到的有:
1.webwork+spring+hibernate这种组合方式是否可行。据说tobao用了ejb,虽说个人并未觉得ejb哪点好,但别人 既然用了就肯定有它的一定道理;
2.数据的缓存肯定是必须的,但哪些是最需要被缓存的数据?
3.dba肯定需要,在没有dba的情况下,涉及数据库时应该注意什么;
4.这样的一个系统,它的性能肯定非常主要。它最有可能的瓶颈会发生在什么地方?
5.我们的理想工期会有多长,3到5个开发人员。
6.jms远程异步调用的支持。
目前想到的大概这些,希望有经验的朋友给些建议。我对这个项目目前还没有很大的把握。
posted @ 2007-02-10 11:41 ronghao 阅读(2607) | 评论 (9)编辑 收藏
这个礼拜一直在写日历。把以前的JS全部推翻了,重新再写。开发工具使用了aptana,调试用firefox的javascript debugger.aptana开始使用的时候还是挺爽,随着代码量的增长,然后经常是代码高亮显示不了了,更不要说提示了。这点IDEA6.0显然要更加完善,直接可以方便的查找调用的函数。但是IDEA太笨重,不清爽。aptana写CSS还是相当方便的。
开始使用stripes,一切看起来都不错。
在家里安装了subvision ,可是怎么也跑不起来,难道人品有问题?去年在公司配过一次,一点问题都没有的。
火车票还没着落,着急!
明天公司开年会,要集体K歌,嗓子不好,准备合唱。
posted @ 2007-02-08 23:39 ronghao 阅读(621) | 评论 (0)编辑 收藏
获得列表
在上一步中我们已经把数据保存到了内容仓库中,那我们如何确定数据确实保存进去了呢?getBlogList() 这个方法将返回根节点下所有名为blogEntry.的子节点。
public ArrayList getBlogList() throws BlogApplicationException {
    Session session 
= JackrabbitPlugin.getSession();
    ArrayList blogEntryList 
= new ArrayList();
    Node rootNode 
= session.getRootNode();
    NodeIterator blogEntryNodeIterator 
= rootNode.getNodes();

    
while (blogEntryNodeIterator.hasNext()) {
        Node blogEntry 
= blogEntryNodeIterator.nextNode();
        
if (blogEntry.getName().equals("blogEntry"== false)
            
continue;
        String title 
= blogEntry.getProperty("title").getString();
        String blogContent 
= blogEntry.getProperty("blogContent").getString();
        Value creationTimeValue 
= (Value) blogEntry.getProperty(
                
"creationTime").getValue();
        String userName 
= blogEntry.getProperty("userName").getString();
        BlogEntryDTO blogEntryDTO 
= new BlogEntryDTO(userName, title,
                blogContent, creationTimeValue.getDate());
        blogEntryList.add(blogEntryDTO);
    }
    
return blogEntryList;
}

一旦你获得了根节点这个对象,你就可以通过调用getNodes()这个方法来获取它所有的子节点。如果这个节点没有子节点,将返回一个空的NodeIterator 对象。我们可以遍历这个NodeIterator 对象来获得名为blogEntry 的节点集合,然后通过getProperty()方法来获得节点上的属性,即我们保存的真实数据。getProperty()方法返回Value对象的一个实例。因为存储数据类型的不同,所以返回的Value对象实例是不同的。根据不同的数据类型,你应该调用特定的方法来获取数据,比如getString()来获取字符串,而getDate()获得一个日期。

查找内容(用XPath的方式)
JSR-170定义了两种方式来查找内容(也可以理解为查找节点)。一种使用XPath语法,另一种使用SQL语法。JSR-170要求Level 1必须实现XPath的方式,而SQL的方式则作为一个可选的功能。

XPath原本是一种设计用来查找XML元素的语言。因为我们的workspace是树状的结构,很像XML。所以XPath语法非常适合于在这里查找内容。下面的代码演示了通过作者名来查找节点。
Session session = JackrabbitPlugin.getSession();
    Workspace workSpace 
= session.getWorkspace();
    QueryManager queryManager 
= workSpace.getQueryManager();

    StringBuffer queryStr 
= new StringBuffer(
            
"//blogEntry[@"+PROP_BLOGAUTHOR +"= '");
    queryStr.append(userName);
    queryStr.append(
"']");
    Query query 
= queryManager.createQuery(queryStr.toString(),
            Query.XPATH);

    QueryResult queryResult 
= query.execute();

    NodeIterator queryResultNodeIterator 
= queryResult.getNodes();
    
while (queryResultNodeIterator.hasNext()) {

        Node blogEntry 
= queryResultNodeIterator.nextNode();
        String title 
= blogEntry.getProperty(PROP_TITLE).getString();
        String blogContent 
= blogEntry.getProperty(PROP_BLOGCONTENT).getString();
        Value creationTimeValue 
= (Value) blogEntry.getProperty(
                PROP_CREATIONTIME).getValue();
        BlogEntryDTO blogEntryDTO 
= new BlogEntryDTO(userName, title,
                blogContent, creationTimeValue.getDate());
        blogEntryList.add(blogEntryDTO);
    }

首先获得session 对象,通过它获得它连接的workspace,然后就可以通过workspace获得这个workspace的QueryManager 。QueryManager 接口定义了很多用来查询的方法。接下来我们要做的是创建一条查询语句。我们这里这样写"//blogEntry[@blogAuthor='<bloggerName>'"。这句话的意思是查找所有名为blogEntry ,含有blogAuthor 属性且属性值为<bloggerName>的节点。具体可以看JSR-170规范。

通过queryManager's createQuery()方法创建一个查询对象,这个方法需要两个参数,一个是我们的查询语句,另一个是查询的方式,这里使用XPath。获得这个Query 查询对象后,调用它的execute() 方法开始执行查询,返回一个QueryResult 对象。注意,查询的结果受到当前session的限制,换句话说,就是如果这个session没有权限查看一个特定的节点,哪怕这个节点满足我们查询的条件,在我们的查询结果里也是看不到这个节点的。所有的查询数据来自于该workspace已经持久化的数据,哪些已经改变但还没有通过session.save()(item.save())持久化到workspace的数据不在查询之列。获得QueryResult 对象后,我们就可以通过调用getNodes()方法来获得符合查询条件的节点的一个遍历。

剩下的两个未实现的方法是updateBlogEntry() 和 removeBlogEntry(),它们实现起来都很简单。我们把BOLG 标题作为主键,通过标题来获得相关的节点。在updateBlogEntry()方法里,我们直接设定需要改变的属性;在 removeBlogEntry()方法里,我们获得目标节点后直接在节点上调用remove()方法。最后别忘了一定要调用session.save()方法把我们改变的数据持久化。

处理二进制内容
对内容仓库来说,处理二进制内容是个很基本的要求,比如说图片。现在我们的示例程序容许给每个BLOG附加一张图片。下面分别是附加图片和获取图片的方法。
public void attachFileToBlogEntry(String blogTitle,
  InputStream uploadInputStream) 
throws BlogApplicationException {
    Session session 
= JackrabbitPlugin.getSession();
    Node blogEntryNode 
= getBlogEntryNode(blogTitle, session);
    blogEntryNode.setProperty(PROP_ATTACHMENT, uploadInputStream);
    session.save();

}
public InputStream getAttachedFile(String blogTitle) throws BlogApplicationException {
    InputStream attachFileIS 
= null;
    Node blogEntryNode 
= getBlogEntryNode(blogTitle);
    Value attachFileValue 
= (Value) blogEntryNode.getProperty(PROP_ATTACHMENT).getValue();
    attachFileIS 
= attachFileValue.getStream();
  
return attachFileIS;
}

正如你看到的那样,我们的代码在处理二进制内容和一般内容间并没有什么太大的区别。仅仅一点不同的是你要通过InputStream 对象来保存和获取二进制数据。在我们的配置文件里关于persistent manager会有一个externalBLOBs 属性。把这个属性设为true, 图片将会保存在文件里,相反则会保存在数据库的blob字段里。

总结
到这里,我们对 JSR-170, Jackrabbit以及如何使用 JSR-170 API开发一个简单的应用程序都有了大概的了解。我们的讨论更多的在于基础。相信大家一定会对内容仓库有个初步的认识。
posted @ 2007-01-28 23:55 ronghao 阅读(1841) | 评论 (3)编辑 收藏
开发我们的例子程序
jackrabbit已经配置好了,现在让我们来创建我们的示例程序。这个例子程序将调用JCR-170 API。很显然,我们需要做两件事情:一个是作为后台的对数据进行增删改查(持久层),另一个是开发相对应的UI界面(WEB 层)。首先,让我们定义一个DAO接口。这个接口BlogEntryDAO.java 如下:
public interface BlogEntryDAO {
    
public void insertBlogEntry(BlogEntryDTO blogEntryDTO)
        
throws BlogApplicationException;
    
public void updateBlogEntry(BlogEntryDTO blogEntryDTO)
        
throws BlogApplicationException;
    
public ArrayList getBlogList()
        
throws BlogApplicationException;
    
public BlogEntryDTO getBlogEntry(String blogTitle)
        
throws BlogApplicationException;
    
public void removeBlogEntry(String blogTitle)
        
throws BlogApplicationException;
    
public ArrayList searchBlogList(String userName)
        
throws BlogApplicationException;
    
public void attachFileToBlogEntry(String blogTitle, InputStream uploadInputStream)
        
throws BlogApplicationException;
    
public InputStream getAttachedFile(String blogTitle)
        
throws BlogApplicationException;
}

正如你看到的,这个接口提供了增删改查的方法,同时还提供了两个方法来处理附件。接下来,我们需要一个DTO对象用来在两个层之间传递数据。
public class BlogEntryDTO {

    
private String userName;
    
private String title;
    
private String blogContent;
    
private Calendar creationTime;

    
//Getter and setter methods for each of these properties        
}

这里我们将仅仅讨论持久层。

连接jackrabbit
现在,第一件事情是开发一个组件,获得一个到jackrabbit内容仓库的连接。为了简单,我们将在程序启动的时候获得这个连接,然后在程序停止的时候释放这个连接。这里我们使用了Struts ,所以我们需要开发一个PlugIn 类。如下:
public class JackrabbitPlugin implements PlugIn{
    
public static Session session;
    
public void destroy() {
        session.logout();
    }
    
public void init(ActionServlet actionServlet, ModuleConfig moduleConfig) 
    
throws ServletException {
        
try {
            System.setProperty(
"org.apache.jackrabbit.repository.home",
                
"c:/temp/Blogging");
            Repository repository 
= new TransientRepository();
            session 
= repository.login(new SimpleCredentials("username",
                    
"password".toCharArray()));
        } 
catch (LoginException e) {
            
throw new ServletException(e);
        } 
catch (IOException e) {
            
throw new ServletException(e);
        } 
catch (RepositoryException e) {
            
throw new ServletException(e);            
        }
    }
    
public static Session getSession() {
        
return session;
    }
}

init()方法将会在程序启动的时候调用,destroy()将会在程序停止的时候调用。我们在init()方法里获得了到jackrabbit内容仓库的连接。看看代码,我们做的第一件事是设定了org.apache.jackrabbit.repository.home这个系统属性,在上篇文章里提到,这个属性是用来指向我们的内容仓库主目录。这里我们设定它为c:/temp/blogging。接下来,我们创建了TransientRepository的一个实例。这是jackrabbit提供的类,它提供了一个到内容仓库的代理。它在第一个session 打开的时候自动启动内容仓库,在最后一个session 关闭的时候自动关闭内容仓库。
一旦我们获得了一个内容仓库对象,我们就可以调用它的login() 方法来打开一个连接。login() 方法需要一个Credential 对象作为参数。如果Credential 对象是NULL,jackrabbit会认为其他的机制做了这个验证(比如JAAS)。login() 方法还可以传入一个workspace名字作为参数,如果不传入这个参数,jackrabbit会返回一个session对象指向默认的workspace。注意workspace和session是一对一的,即一个session仅对应一个workspace。(注:如果不传入Credential对象,返回的session对workspace是只读的)

增加内容
连接已经建立起来了,下面让我们实现BlogEntryDAO这个接口。第一个我们想实现的方法是插入数据 insertBlogEntry()
public void insertBlogEntry(BlogEntryDTO blogEntryDTO)
            
throws BlogApplicationException {
        Session session 
= JackrabbitPlugin.getSession();
        Node rootNode 
= session.getRootNode();
        Node blogEntry 
= rootNode.addNode("blogEntry");
        blogEntry.setProperty(
"title", blogEntryDTO.getTitle());
        blogEntry.setProperty(
"blogContent", blogEntryDTO.getBlogContent());
        blogEntry.setProperty(
"creationTime", blogEntryDTO.getCreationTime());
        blogEntry.setProperty(
"userName", blogEntryDTO.getUserName());            
        session.save();
}

首先获得session 对象,即到内容仓库特定workspace的连接。然后,我们在这个session 对象上调用getRootNode() 方法,获得这个workspace的根节点,这个根节点的路径是("/").一旦我们获得这个根节点,我们就可以通过addNode()方法在这个根节点下增加新的子节点。新节点的名字是blogEntry. 通过setProperty() 方法我们把数据存储到节点的property里。正如我们先前说明的,真实的数据是存储在property元素里,property元素是叶子。
注意session.save() 这行代码。这个方法是必须调用的,这个方法调用之前,任何 Node,Property的改变都被保存在这个session的一个临时区域里,其他的和该session连接到相同workspace的session都看不到这些改变。当这个方法被调用并被成功执行后,这些Node,Property的改变才会被持久化到这个session关联的workspace里,同时所有与这个workspace关联的session才可见这些变化。相对应的,Session.refresh(false)将会丢弃所有这些改变。item.save()和Item.refresh(false)作用相似,只是影响范围限定在单个Item上(注意,包括它的子节点)
posted @ 2007-01-25 23:12 ronghao 阅读(3823) | 评论 (0)编辑 收藏
内容仓库模型
JSR-170 是这样定义内容仓库的,内容仓库由一组 workspace(工作空间)组成,这些workspace通常应该包含相似的内容。一个内容仓库有一个到多个 workspace。每个workspace都是一个树状结构,都有一个唯一的树根节点(root node)。树上的item(元素)或者是个node(节点)或者是个property(属性)。每个node都可以有零个到多个子节点和零个到多个子属性。只有根节点没有父节点,其余所有的节点都有一个父节点。property 也必须有一个父节点,但它没有子节点或是子属性,property 是叶子元素。property是真正存储数据的元素。

下图描述了一个blog应用程序的内容仓库模型。每个root node(根节点)的子节点都代表了一个blog实体。与这个blog实体有关的数据都存储在 bolgEntry 节点的属性里,其中一个 blogAttachment property 存储了一个二进制图片文件。
repositorymodel3.gif
根据内容仓库实现的功能,JSR-170定义了三种级别:
Level 1:定义了一个只读的内容仓库。功能包括读取内容,将内容导出为XML和查找内容。
Level 2:定义了可写的内容仓库。Level 2是Level 1的扩展,新增的功能包括往内容仓库里写入内容,和从XML导入数据到仓库。
Advanced options:定义实现五种附加功能,版本控制、JTA、SQL查询、清晰的内容锁定和监视。

什么是Apache JackRabbit?
Apache JackRabbit是一个开放源码的JSR-170 实现,实现了Level 2,但它还有许多扩展的功能。详细可以去它的官方网站。

下面我们决定用Apache JackRabbit来作为我们示例程序的内容仓库。

如何配置Apache JackRabbit
JackRabbit需要两个参数来配置一个内容仓库实例。
1.内容仓库主目录:这个文件目录下通常包含了所有的内容,搜索索引,内部配置文件和其他持久化信息。它的结构看起来会像下面这个样子:
   c:/temp
        
|
        
|--Blogging
                
|
                
|-repository
                
|       |
                
|       |-index
                
|       |-meta
                
|       |-namespaces
                
|       |-nodetypes             
                
|
                
|-version
                
|
                
|-workspace
                        
|
                        
|--default

  在上面的情况下,内容仓库主目录是c:/temp/Blogging.
2.内容仓库配置文件:一个典型的配置文件如下:
<Repository>
 
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
  
<param name="path" value="${rep.home}/repository"/>
 
</FileSystem>
 
<Security appName="Jackrabbit">
  
<AccessManager class="org.apache.jackrabbit.core.security.SimpleAccessManager"/>
  
<LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule">
    
<param name="anonymousId" value="anonymous"/>
  
</LoginModule>
 
</Security>
 
<Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/>
 
<Workspace name="${wsp.name}">
  
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
   
<param name="path" value="${wsp.home}"/>
  
</FileSystem>
  
<PersistenceManager 
        
class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
   
<param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
   
<param name="schemaObjectPrefix" value="${wsp.name}_"/>
  
</PersistenceManager>
  
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
   
<param name="path" value="${wsp.home}/index"/>
  
</SearchIndex>
 
</Workspace>
 
<Versioning rootPath="${rep.home}/version">
  
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
   
<param name="path" value="${rep.home}/version" />
  
</FileSystem>
  
<PersistenceManager 
        
class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
   
<param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>
   
<param name="schemaObjectPrefix" value="version_"/>
  
</PersistenceManager>
  
</Versioning>
  
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
   
<param name="path" value="${rep.home}/repository/index"/>
  
</SearchIndex>
</Repository>

 
  在这个配置文件里,<Repository>元素是根元素,它包含了下面这些元素:
  a,<FileSystem>: 该元素配置了内容仓库的全局数据存储位置,这些全局数据包括已注册的命名空间,定制的节点类型等等。        JackRabbit 提供了几种选择,一种是像上面例子里配置的存储在本地文件里,LocalFileSystem. 如果你想把它们存储在数据库里,你可以使用 DbFileSystem.
  b,<Security>:内容仓库的安全配置,它有两个子元素:<AccessManager>和<LoginModule>。<AccessManager>配置的类用来判断用户有没有权限来对特定数据执行特定的操作。
  c,<Workspaces>:这个元素的配置对所有的workspace都通用。它的rootPath 属性是所有workspace文件夹的根目录,在我们的例子里它是c:/temp/Blogging/Workspace;defaultWorkspace 属性则包含了workspace的默认名。
  d,<Workspace>:这个元素是所有workspace的默认配置模板。去每个workspace文件夹下你都会发现一个workspace.xml文件,这个文件和这个元素的配置一模一样。三个子元素:<FileSystem>,和这个workspace相关数据的存储位置;<PersistenceManager> ,这个workspace内容节点存储策略;<SearchIndex>,可选,全文检索。
  e,<Versioning>:配置一个版本相关的对象。其实JackRabbit也是把它作为节点来处理的。

这两个参数可以通过两种方式设置,一种是在仓库实例创建时直接传到Jackrabbit里去,一种是间接的通过设置JNDI object factory。
你可以设置org.apache.jackrabbit.repository.home 这个系统属性的值来指定你的内容仓库主目录;也可以设置
org.apache.jackrabbit.repository.conf 这个系统属性的值来指定你的内容仓库配置文件repository.xml。如果你不设定这两个
参数,Jackrabbit会把当前目录作为内容仓库主目录,同时,它有一个默认的内容仓库配置文件。
posted @ 2007-01-23 23:57 ronghao 阅读(6805) | 评论 (25)编辑 收藏
原文地址:http://www.onjava.com/pub/a/onjava/2006/10/04/what-is-java-content-repository.html?page=4
JSR-170把自己定义为一个能与内容仓库互相访问的,独立的,标准的方式。同时它也对内容仓库做出了自己的定义,它认为内容仓库是一个高级的信息管理系统,该系统是是传统的数据仓库的扩展,它提供了诸如版本控制、全文检索,访问控制,内容分类、访问控制、内容事件监视等内容服务。

Java Content Repository  API(JSR-170)试图建立一套标准的API去访问内容仓库。如果你对内容管理系统(CMS)不熟悉的话,你一定会对内容仓库是什么感到疑惑。你可以这样去理解,把内容仓库理解为一个用来存储文本和二进制数据(图片,word文档,PDF等等)的数据存储应用程序。一个显著的特点是你不用关心你真正的数据到底存储在什么地方,是关系数据库?是文件系统?还是XML?不仅仅是数据的存储和读取,大多数的内容仓库还提供了更加高级的功能,例如访问控制,查找,版本控制,锁定内容等等。

一段时间以来市场上出现了各个厂家开发的不同的CMS系统,这些系统都建立在他们各自的内容仓库之上。
问题出现了,每个CMS开发商都提供了他们自己的API来访问内容仓库。这对应用程序的开发者带来了困扰,因为他们要学习不同的开发商提供的API,同时,他们的代码也与这些特定的API产生了绑定。

JSR-170正是为解决这一问题而出现的,它提供了一套标准的API来访问任何数据仓库。通过JSR-170,你开发代码只需要引用 javax.jcr.* 这些类和接口。它适用于任何兼容JSR-170规范的内容仓库。

我们将通过一个例子来逐步了解JSR-170。

为什么需要 Java Content Repository API

随着各个厂家各自的内容仓库实现数量的增长,人们越来越需要一组通用的编程接口来使用这些内容仓库,这就是JSR-170所要做的东西。它提供一组通用的编程接口来连接内容仓库。你可以把JSR-170理解为和JDBC类似的API,这样你可以不依赖任何具体的内容仓库实现来开发你的程序。你可以直接使用支持JSR-170的内容仓库;或者如果一些厂家的内容仓库不支持JSR-170则可以通过这些厂家提供的JSR-170驱动来完成从JSR-170与厂家特定的内容仓库的转换。

下面这张图描述了使用JSR-170开发的应用系统的结构。在该系统运行的时候,它可以操作内容仓库1,2,3中的任意一个。在这些内容仓库当中,只有2是直接支持JSR-170的,剩下的两个都需要JSR-170驱动来和应用系统交互。注意:你的应用系统完全不用关心你的数据是如何存储的。1可能使用了关系数据库来存储,而2使用了文件系统,至于上,它甚至更前卫的使用了XML。

repositorymodel1.gif

JSR-170 API对不同的人员提供了不同的好处。

●对于开发者无需了解厂家的仓库特定的API,只要兼容JSR-170就可以通过JSR-170访问其仓库。
●对于使用CMS的公司则无需花费资金用于在不同种类CMS的内容仓库之间进行转换。
●对于CMS厂家,无需自己开发内容仓库,而专注于开发CMS应用。
posted @ 2007-01-23 15:13 ronghao 阅读(5103) | 评论 (3)编辑 收藏
2006年马上就要过去,在06年的最后一天总结一下自己一年来的生活工作,自己觉得还是挺有意义的,尽管上学时最烦的就是各种各样的总结了。
工作技术上:
1、可以比较熟练的使用webwork、spring和hibernate.这是和05年做的对比,05年做的东西完全是jsp+servlet+jdbc.尽管自己那时也用过,但那还限于demo。
2、对一个不算复杂的WEB项目有比较清晰的开发思路/架构。三层、封装、抽象、领域对象。
3、有过一次和最终用户打交道的经历,有了一些经验。
4、负责开发了公司产品的权限系统。对ACL、RBAC都有了谈不上深入的理解。最后的实现是基于 acegi的扩展。包括数据范围权限、单条数据权限都实现了控制。只是对所谓的大集中模式下的权限没有一个很好的解决方法,也是一个遗憾。
5、初步的学习了ajax。05年自己对javascript还是很不屑的,也仅仅把它用于表单的验证。今年买了本权威指南,也算是浏览过一遍。在开发中用到了prototype.js,dwr,观念发生了很大的变化。写了个outlookbar.js以及把xloadtree进行了很多的扩展。最后还模仿了google的日程表也做了个日程表,比较丑,css还得加强。
6、基于jackrabbit实现了产品的CMS。对jsr170有了理解。一度想数据仓库可否替代数据库在一定应用中。
7、有了一些工作流理论和实践。
生活中:
1、世界观发生了一点变化。什么是民主,什么是人权,什么叫自由,你真的经过你自己的思考了吗?
2、关注了一些以前没有关注的声音。知道了一句话叫用脚投票。
3、关注了高房价、人民币升值、股票、基金。了解了一些财政金融方面知识,对自己每月把工资存入 银行产生了怀疑。
4、给自己配了台台式机,然后又给老婆买了台笔记本。
展望:
1、年底开始注意敏捷开发,明年能不能敏捷到我们团队?
2、很显然,ajax刚刚会用的阶段,上升到模式?我们的页面能不能xhtml规范?
3、OSGI应用到我们的产品中?
4、具有架构的能力?
5、明年我能买的起房吗?
呵呵,不管怎样,希望明年会更好。
posted @ 2006-12-31 16:22 ronghao 阅读(928) | 评论 (1)编辑 收藏
似乎到了年末,心情也跟着莫名的烦躁起来了。CMS基本算是开发完毕,现在主要是改改产品的BUG。BUG单总是那么长,让人打不起精神。产品的发布已经几次延期,看不到海的对岸。其实还有好多的问题要去解决。想着对acegi 的ACL进行进一步的扩展,CMS的页面模板要实现可视化的编辑,对页面的javascript进行重新整理,把页面由table换成div...确实还有很多的工作。没有觉得充实而是杂乱没有条理。
也许也和生活有关。
浮躁的年末的一个傍晚,我坐在那里,看着外边的天慢慢黑透...
posted @ 2006-12-29 18:30 ronghao 阅读(432) | 评论 (0)编辑 收藏

周六参加了agileChina + BJUG 的活动,把我参加这次活动的一点体会记下来:
1)那天的风特大,走到理想大厦的时候那风差点把我给刮跑了。于是我就在想理想大厦里上班的人肯定是都穿羽绒服的,后来一
   看果不其然。结果是周日马上给自己买了件羽绒服。

2)自我介绍:
  冰云让每个人用三个词来概括自己,我说了真诚、爱读书和运气好。真诚是因为我当过兵;爱读书吗,其实是现在读书的时间越来越少了,杂文倒是喜欢。运气好,上次参加BEA UG时莫映就给我抽了个一等奖,所以....

3)topic 1 CMM VS AGILE, 讲演者:杨崑(PHD在读)
  好多专业术语我都没听过,大部分要靠想象去理解。还好知道单回路和双回路是什么:)理论总是让人难以理解的。

4)topic 2 DSL In Practice 讲演者:徐昊(TW)
 第一次见到徐昊是在去年的BEA UG,当时是一个老外介绍Selenium,他做同声翻译,留下了深刻的印象。语言有趣、易懂。
  对我来说效果很直观:知道DSL是什么,能做什么了。然后附加知道了徐昊机器的屏保时间很短,他总是飞跑着去关掉屏保。

5)关于topic 3 Selenium 讲演者:黄亮(TW)
  其实去年就试用过Selenium,悲惨的很,去年做的项目在firefox里跑不起来,于是作罢。怎么办?黄亮让我的手又痒痒了。
 
6)冰云(limo): 主持的相当不错,休息的时候问了他工作角色的问题。知道了冰云就是李莫,李莫就是冰云。

  yulimin:第一次休息的时候本打算去找他去的,没想到他先过来了。其实最早认识他是在jdon,那时我还在军校里,每天在
  jdon里狂翻帖子,下载jive的源码。然后就看到了他的头像,于是就想:怎么也是个当兵的??好感那时就生长起来了。这次
  见到了本人,真的是非常随和,很有亲切感。谈到很多东西,然后是晚上一路回家。时间太短,心里想什么时候能够再见面。
 
  总结:下次还会参加。

posted @ 2006-12-20 12:03 ronghao 阅读(567) | 评论 (0)编辑 收藏

数据权限分为两种,一种是数据范围权限,一种是具体到每一条数据的权限。前一种可以通过动态构建SQL解决;后一种
似乎必须通过ACL不可。于是就想对Acegi ACL做一个通用的扩展。以通讯录为例
先看一个总的配置
  <bean id="contactManagerSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
      <property name="authenticationManager"><ref bean="authenticationManager"/></property>
      <property name="accessDecisionManager"><ref local="aclDecisionManager"/></property>
      <property name="afterInvocationManager"><ref local="afterInvocationManager"/></property>
      <property name="objectDefinitionSource">
         <value>
            com.ronghao.acltest.services.ContactService.saveContact=AFTER_ACL_CREAT
            com.ronghao.acltest.services.ContactService.getAllContacts=AFTER_ACL_COLLECTION_READ
            com.ronghao.acltest.services.ContactService.getContact=AFTER_ACL_READ
         </value>
      </property>
   </bean>
扩展一、当你增加一条记录的同时向ACL表里插入权限信息 这个SS已经做到了这一点
  ss有一个接口标示哪些doamin需要acl保护 AclDomainAware
  这里再扩展一个基类 ,目的很简单当读出数据时附加上权限信息
  public  class BasicAclDomain implements AclDomainAware {

    private int mask; //权限

    public int getMask() {
        return mask;
    }

    public void setMask(int mask) {
        this.mask = mask;
    }
  }
  
  public class Contact extends BasicAclDomain
扩展二、读取列表时进行数据的过滤,原来的acegi在ACL的集合后处理会造成分页产生虎牙子
     这里采用前拦截,在acl表里增加一个className字段,里面放上PO的类名,这样可以缩小
     数据查询范围.实际在读取集合时,是先到acl表里完成分页,然后获得对Contact的real id list
     然后拦截实际DAO方法,动态改变SQL成select * from real_data where id in ( {real id list} )的形式,这样就OK了,
     分页实际是对acl表里相应记录的'分页'.比如说取第10条到20条,实际是取acl表里相应记录的第10条到20条来动态改变SQL
     这个可以写一个专门被用来拦截的类 SecurityDAO 方法findByPageACL(query, page),ContactServiceImpl中getAllContacts
     方法强制调用该方法
扩展三、后拦截。这里AFTER_ACL_COLLECTION_READ和AFTER_ACL_READ的目的就很简单了,因为他们不再进行数据过 滤, 他们只是把用户对每条记录的mask取最大权限就OK,然后往PO里setMask。这样PO带了权限信息到页面上就非常好处理了。比如button的显示等等
扩展四、封装AclService,对单条记录的ACL权限管理。比如增加权限、修改权限、删除权限。这个acegi的最新1.0.3已经开始加入。
具体在实现中感觉acl的vote完全是鸡肋,全部不用。另外在扩展二中如果用户数据要实现数据库排序就比较困难。所以就有了还未实现的构想:
一、PO创建向ACL表里插入权限信息时可以配置不同的策略:比如通讯录创建一条新信息只能创建者可以看并管理,而你往请假表里插一条新信息后,不仅你了,你的上司也可以同时看到。(这个还是比较easy)
二、用户数据要实现数据库排序。需要在ACL_OBJECT_IDENTITY里增加几个额外的字段,把po相应的排序字段同步更新到ACL表中。什么?不好做?写配置文件啊!再对PO的update进行后拦截。
想法就这样。实现??

posted @ 2006-12-14 10:20 ronghao 阅读(4264) | 评论 (6)编辑 收藏
作为CMS内容发布来说,将要显示的内容html化无疑是一个很基本的要求,这样可以提高整个系统的效率。
考虑一个内容节点Content,其中节点有个pagecontent的属性,这个属性用来存储用户在后台输入的内容,
内容+模板=显示,这里是:
内容+模板-->显示静态html
内容用FCK来编辑,模板也同样用FCK编辑,这里用freemarker无疑是一个好的选择。这里是个最简单的例子。
首先定义一个最简单的模板content.ftl
<html>
$
{content}
</html>
然后就是把内容填充进去:
 Configuration cfg = new Configuration();
 
//模板存储的目录
 cfg.setDirectoryForTemplateLoading(Path.getTemplateDirectory());
 
//默认
 cfg.setObjectWrapper(new DefaultObjectWrapper());
//填充数据        
Map root = new HashMap();
root.put(
"content", c.getPageContent());
     Template temp 
= cfg.getTemplate("content.ftl");
//发布日期
String date=c.getActivedate();
String filename 
= c.getId()+".html";
//创建目录
Path.createFold(Path.getWebCmsHtmlDirPath(),date);
Writer out 
= new OutputStreamWriter(new FileOutputStream(Path.getWebCmsHtmlDirPath()+"/"+date+"/"+filename));
temp.process(root, out);
out.flush();
其实就是这么简单!对html的管理就很容易了,基本的FILE操作
posted @ 2006-11-27 18:51 ronghao 阅读(5995) | 评论 (8)编辑 收藏
CMS包括了网页的发布,在编辑网页内容的时候用到了FCK.网上的教程是很多的.一切刚开始都很顺利.问题出在对图片\FLASH的上传和浏览支持上.对JAVA来说是需要编码的.这里用到了FCK-java.2.2这个包.其实里面除了两个servlet其他的都可以干掉的,似乎用不上它提供的标签.
两个servlet里用到了common-fileupload.jar.一切看起来都很不错,可是就是上传不了文件.debug发现common-fileupload得不到fileItem.很是郁闷,因为提供的sample是可以正常跑的.上了common-fileupload的官方网站,才发现是有别的进程拦截request的缘故.于是开始调试.我靠,系统里的过滤器真TM的多,最后是把webwork\webwork-cleanup拦截/*变为*.action这才正常.
 问题:我们真的需要这么多的filter吗?这些filter过滤的范围认真考虑过吗,可以缩小吗?
posted @ 2006-11-23 22:02 ronghao 阅读(1453) | 评论 (1)编辑 收藏
平台新版本将要发布,目前还缺CMS.原计划是将原先的CMS移植过来即可,原先的版本是基于Slide开发的,后来经过讨论还是决定重新架构.新版本基于jackrabbit.大概考虑了一下需求:
1.很多CMS都包括了发布模板、显示模板,这里不用考虑,CMS仅仅是内容管理,负责内容存储,至于展现,那是门户事情.内容和展现分开.CMS作为门户数据源的一个选择.
2.一个完整的CMS并不是作为一个所谓的栏目信息,它还必须包括对各种文件的保存.对整个平台的文件和附件进行统一管理
3.版本管理
4.全文检索
5.权限管理,具体到每一个节点的配置.不仅仅是在CMS内部,用户通过门户看到的信息也是要经过权限过滤(内网门户)
6.一套可扩展的组织用户接口(目前是直接用平台内部的组织用户,考虑到CMS将来的独立性,这个也是必要的)
7.文件的webdav支持
大概就是这些了,已初步完成了对jcr的封装和架构.感到spring有劲使不上,呵呵.
CMS信息发布需求
信息分两种:未发布的和发布的
1、未发布的信息可以进行增删改查、版本管理、上传和下载附件
2、将未发布的信息发布,信息的状态将置为发布状态同时结合模板生成静态HTML
3、发布的信息本身会增加一个对静态HTNL联接的属性,在后台即可以对静态HTML进行浏览
4、发布的信息依旧可以进行内容修改和版本恢复,这样会将生成的静态HTML删除然后再重新生成
5、发布的信息可以删除,信息进入回收站,静态HTML同时被删除
6、从回收站恢复信息,信息重新发布,生成静态HTML
7、通过栏目的设置来决定信息的发布是直接发布还是通过工作流发布
posted @ 2006-11-16 22:55 ronghao 阅读(5000) | 评论 (12)编辑 收藏
今天对acegi的页面标签做扩展,突然发现找不到tld文件了
Jsp中非常简单:
<%@ taglib prefix="authz" uri="http://acegisecurity.org/authz" %>
但记得自己没有在web.xml中声明这个标签,咋就跑起来了呢?google一下,原来authz.tld被打入jar包的META-INF下
呵呵,记录一下
posted @ 2006-10-12 15:50 ronghao 阅读(615) | 评论 (0)编辑 收藏
acegi内置了对CAS的支持。这里的CAS是3.0。建立CAS server是一个比较简单的事情。CAS server就是一个标准的war文件,把它发布就可以运行。需要做的仅仅是调整登陆和其他一些页面。先了解一下CAS如何实现SSO。
例子:原有系统A和系统B,现在在它们之间做SSO。
很显然,系统A和B都是CAS client。首先是访问系统A,干掉A的登陆页面,在A的入口判断有没有Ticket(票据),如果没有则重定向到CAS server,在CAS server提供Credential(大多数情况就是用户名和密码)。CAS server的作用非常简单:就是来验证用户密码。正确,则发送Ticket。CAS有5种Ticket,分别是TGC(通过cookie发送的ticket),ST(Service Ticket),PGT,PGTIOU,PT。其中PGT,PGTIOU,PT属于代理ticket,这里不作讨论。具体可以参考
http://www.blogjava.net/openssl/archive/2006/04/26/SSO_CASProxy.html
TGC和ST的关系可以打个比方:
我去中央电视塔去玩,结果发现地下还有个海底世界。SSO前我是这么玩的:先去电视塔买张门票,玩完了;再去海底世界买张门票,玩完了。发现真累,两个景点这么近还要买两次门票,就不能搞个通票吗?于是就SSO。于是这样:我先去电视塔,门卫告诉我你不能进去要买票,于是把我送到通票售票处(CAS server)买票(登录),买吧,于是给了我两张票,注意,是两张,一张发到我手里,上面写着仅限电视塔使用(ST);靠,不是通票吗,咋仅限电视塔使用?别急,还有一张票(TGC)通过cookie发送你看不见。人家说了保证没问题,我咋办,这是人家的规矩,那就先去玩吧。出了电视塔我直扑海底世界,
门卫说要海底世界票,不会吧,我买的通票啊,门卫说不着急,又把我送回通票售票处(CAS server),通票售票处(CAS server)一看,发现我有TGC,嘿嘿,这家伙买过票了不用再买(不用再登录),于是换我一张票(ST)上面写着仅限海底世界使用,于是我就拿着这张票又去海底世界了。于是我明白了啥是SSO了,不就是把买票改成换票了吗?
比方完了,最开始的例子也就不往下继续了。需要注意的是系统A和B整合SSO需要把A、B的用户密码集中管理,你说A中我的用户名是张三,B中是李四,SSO能不能帮我自动识别,回答是不行的。
继续Acegi的整合。
CAS server是做用户密码验证,具体的权限授权的工作还是在各个单个系统里,也不应该交给它管。做用户密码验证需要AuthenticationHandler。这个具体就是根据Credential返回一个boolean值来判断你输入的用户密码正不正确。acegi提供了一个实现。
以一个典型的web访问来说明整个过程
1、用户访问一个受acegi安全保护的页面或业务方法;
2、用户没有登陆的话显然会抛出AuthenticationException
3、配置exceptionTranslationFilter捕获这个异常重定向到CAS server登陆页面
       <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
            
<property name="authenticationEntryPoint"><ref local="casProcessingFilterEntryPoint"/></property>
        
</bean>
        
        
<bean id="casProcessingFilterEntryPoint" class="org.acegisecurity.ui.cas.CasProcessingFilterEntryPoint">
            
<property name="loginUrl"><value>https://my.company.com/cas/login</value></property>
            
<property name="serviceProperties"><ref local="serviceProperties"/></property>
        
</bean>
        
        
<bean id="serviceProperties" class="org.acegisecurity.ui.cas.ServiceProperties">
            
<property name="service"><value>https://server.company.com/myapp/j_acegi_cas_security_check</value></property>
            
<property name="sendRenew"><value>false</value></property>
        
</bean>
serviceProperties里的service属性即在CAS server登陆完毕后由CAS server重定向回来的页面
  https://my.company.com/cas/login?service=https%3A%2F%2Fserver.company.com%2Fmyapp%2Fj_acegi_cas_security_check
4、CAS server检查是否有TGC ,没有则登陆,登陆后返回。这里屁股后面跟着的即ST,TGC通过cookie一并发送到客户端。
   https://server.company.com/myapp/j_acegi_cas_security_check?ticket=ST-0-jhsdfguwgeds
5、配置casProcessingFilter来处理返回ST(和以前的authenticationProcessingFilter比较类似)
   <bean id="casProcessingFilter" class="org.acegisecurity.ui.cas.CasProcessingFilter">
        
<property name="authenticationManager"><ref local="authenticationManager"/></property>
        
<property name="authenticationFailureUrl"><value>/casfailed.jsp</value></property>
        
<property name="defaultTargetUrl"><value>/</value></property>
        
<property name="filterProcessesUrl"><value>/j_acegi_cas_security_check</value></property>
    
</bean>
6、配置authenticationManager
   <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
      
<property name="providers">
         
<list>
            
<ref local="casAuthenticationProvider"/>
         
</list>
      
</property>
   
</bean>
   
  
<bean id="casAuthenticationProvider" class="org.acegisecurity.providers.cas.CasAuthenticationProvider">
        
<property name="casAuthoritiesPopulator"><ref local="casAuthoritiesPopulator"/></property>
        
<property name="casProxyDecider"><ref local="casProxyDecider"/></property>
        
<property name="ticketValidator"><ref local="casProxyTicketValidator"/></property>
        
<property name="statelessTicketCache"><ref local="statelessTicketCache"/></property>
        
<property name="key"><value>my_password_for_this_auth_provider_only</value></property>
    
</bean> 
具体作用的是casAuthenticationProvider,casAuthenticationProvider通过 casProxyTicketValidator来校验ST
    <bean id="casProxyTicketValidator" class="org.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator">
        
<property name="casValidate"><value>https://my.company.com/cas/proxyValidate</value></property>
        
<property name="serviceProperties"><ref local="serviceProperties"/></property>
    
</bean> 
casProxyTicketValidator又具体实现调用了CAS Client library里的ProxyTicketValidator校验ST,ProxyTicketValidator 就比较有意思了,它做了个HTTPS请求CAS server,结果还是CAS server来校验ST(绕了一大圈)
 https://my.company.com/cas/proxyValidate?service=https%3A%2F%2Fserver.company.com%2Fmyapp%2Fj_acegi_cas_security_check
 重新回到CAS server,它接受到这个HTTPS请求,检查ST是否与对这个service发行的ST吻合,吻合的话CAS server就会发回一个肯定的XML回复,里面包含了用户名(username)。剩下的就EASY了,casProxyTicketValidator解析XML,casProxyDecider处理代理,casAuthoritiesPopulator根据解析后的XML获得user,最后就是casAuthenticationProvider构造Authentication(这里是CasAuthenticationToken)
7、重新回到casProcessingFilter,它将Authentication放入HttpSession
这样就完成了整个过程
posted @ 2006-08-29 12:31 ronghao 阅读(6253) | 评论 (5)编辑 收藏
在web.xml中一般我们这样配置:
    <filter-mapping>
        
<filter-name>Acegi Filter Chain Proxy</filter-name>
        
<url-pattern>/*</url-pattern>
    </filter-mapping>
这样agegi就对所有的url进行了过滤检查,以一个显示树状菜单为例,它甚至对页面上的每一个图片连接都进行了检查,实际上这是完全没有必要,可以这样:
    <filter-mapping>
        
<filter-name>Acegi Filter Chain Proxy</filter-name>
        
<url-pattern>*.action</url-pattern>
    
</filter-mapping>

    
<filter-mapping>
        
<filter-name>Acegi Filter Chain Proxy</filter-name>
        
<url-pattern>*.ftl</url-pattern>
    
</filter-mapping>
呵呵,检查范围缩小了,可是登陆时系统报404错误,找不到/j_acegi_security_check,因为/j_acegi_security_check这个路径是agegi自己的,所以再增加一行拦截过滤就OK
    <filter-mapping>
        
<filter-name>Acegi Filter Chain Proxy</filter-name>
        
<url-pattern>/j_acegi_security_check</url-pattern>
    
</filter-mapping>
posted @ 2006-08-03 10:45 ronghao 阅读(1721) | 评论 (1)编辑 收藏
周末公司组织去北戴河旅游。闷热的天气、潮湿的空气,让人对海边充满向往。上次去海边已是十六年前了,没有记忆,只有发黄的照片,年幼的我,傻呵呵地笑。十六年前是父亲带我去的,一晃十六年,上个礼拜父亲学校放暑假带奶奶来北京却没有时间陪他们,请了一天假带妹妹去了石景山游乐园,然后是让女朋友带着去了趟八达岭,仅此而已,仅此而已。很是愧疚。
目的地是河北昌黎的黄金海岸。下午五点到达,到达后的第一件事:下海。海浪很大,不会游泳,和女朋友一起站在腰深的地方,泡海。一切都很商业化:厕所,一元;更衣室,一元;热水淋浴,五元;游泳圈,二十元。。。女朋友属于很怕水的那种,一看见海浪就往海边跑,我好点,只要不没过胸就好。很羡慕那些会游泳的人,远远的只能看见他们的一个头,一个黑点,在海里起伏。其实离海岸越远海水越平静,海浪都是靠近海岸的时候才突然变得猛烈起来的。生活,是不是也象这样呢?
晚上在海边的大排挡解决了晚饭问题。和女朋友一起去买点东西,价格自然是高。她想吃袋饼干,一问价格,八元!于是划价,六元。朋友表示不可接受,店老板表示不可再让。于是向外走,走到门口的时候,店老板突然说,五元!于是又回去四点五元成交。生意都是不断妥协做成的。
出发前公司租了帐篷,晚上在海边扎帐篷过夜。真是很奇妙的感觉:外边海浪打在沙滩上发出巨大的声响,帐篷里却是异常的安静。静静地躺在那里,想到了很多的东西。想到了自己的童年,想到了自己的父母,想到了自己的工作,想到了自己的未来。想的最多的还是自己的未来,未来会是什么样子呢?想起刚到北京找工作那会,经过一家麦当劳门口,巨大而明亮的落地窗户后,一个年轻的女孩子,吃着炸薯条,仰起她那张无忧无虑的脸。
早上退潮了,朋友兴奋地去海滩上捡贝壳。有渔民过来买螃蟹,刚早上六点,他们已经出海回来了。是个晴天。
生活本来是很简单的,是不是自己把它搞复杂了?
posted @ 2006-07-16 16:40 ronghao 阅读(402) | 评论 (0)编辑 收藏

一、             考虑实现

1、  系统权限考虑继续采用原先实现方式,在构造功能树和树状菜单时作出权限判断;

2、  数据库操作权限考虑应用 Acegi 扩展,在业务层对相应的浏览 / 增加 / 修改 / 删除的业务方法进行拦截;

3、  行级数据权限考虑采用 AOP 的方式在用户访问相关资源时根据用户权限动态构造 SQL 注入到业务方法里,再由业务方法传递到 DAO 里;

4、  列级数据权限考虑做入页面,这里不再讨论。

5、  大集中模式下的权限管理,本质也是行级数据权限,即在每次数据访问时都需要强制判断用户所属部门 Group

一、 权限管理详细解决方案

用户、用户组、角色设计如下:
Image00000.bmp

Principal 即为权限主体

1、  系统权限授权

web 页面:

页面上显示两棵树:左侧显示用户、用户组和角色树,右侧显示功能模块树,功能模块树的节点上跟两个复选框,分别是可见 / 可再授权。点击用户、用户组和角色树上的节点对相应权限主体进行授权。

很显然可再授权权限包含可见权限。

数据库实现:

ACL 实现,表设计如下:

资源 ID ,权限主体 ID ,权限主体 TYPE ,资源 TYPE ,资源操作权限,条件查询语句 queryStr

说明:

权限主体 TYPE  三种: user/group/role

资源 TYPE         两种: function/method  此时对系统权限来说是 function

条件查询语句 queryStr    数据范围控制   此时对系统权限该字段无效

对象:
Image00002.bmp

用户保存授权信息时,先删除该权限主体 ID 的授权记录,再更新。

2、  数据库操作权限授权

这里引入一个新的对象:数据库操作资源对象   DataResource

表设计如下:

ID NAME parentId resStr resType desc

说明如下:

ID

NAME 资源名称 例如:新增用户

parentId

resStr  业务方法地址 例如: com.way.sevice.UserService.addUser

resType 资源类型,分两种: abstract/detail  抽象 / 具体

desc  描述说明

例子:

对用户管理进行数据库操作权限授权

新建“用户管理”做父节点,选择资源类型为“抽象”

在“用户管理”下再依次新增“新增用户”、“浏览用户”、“修改用户”、“删除用户”四个子节点,选择资源类型为“具体”

如果系统自己来判断存储就是叶子节点是“具体”,其他为“抽象”

web 页面:

页面上显示两棵树:左侧显示用户、用户组和角色树,右侧显示数据库操作资源对象树,数据库操作资源对象树的叶子节点的一级父节点上跟一个数据范围限定 button ,点击后弹出窗口输入限定条件;叶子节点上跟一个复选框。点击用户、用户组和角色树上的节点对相应权限主体进行授权

授权信息存入 ACL 表中

资源 TYPE method

3、  行级数据权限授权

已在上面做了说明即数据范围限定

4、  大集中模式下的权限授权

其实所谓大集中模式控制的也不过是数据范围,考虑是所有相关表全部增加 groupId 字段,新增时添入该字段,读出时进行判断

web 页面:

“组织和用户管理”模块中,每个组织中都允许设定一个管理员,一旦设定管理员,该组织就进入大集中模式,即所有该组织的数据与其他同级组织互相隔离,仅上级组织可见。同时说明一点的是:该管理员与系统总的管理员权限一样,不同的是数据范围仅限制与该组织内部。

综述:

实际的授权部分采用了 ACL ,所以授权比较简单和直观

系统资源和数据库操作资源分开两个表存储,这样可能会给用户带来不便,也就是授权时还需要切换,但这种不便似乎不好解决,因为一个是粗粒度的一个是细粒度的。

Image00001.bmp

posted @ 2006-07-06 17:47 ronghao 阅读(3009) | 评论 (0)编辑 收藏
问题的提出:我们的需求是什么?
首先是系统中需要哪些权限。
权限应该分类四类:  
  一是系统方面,主要是对模块的权限划分 ,具体就是可见不可见,用户能不能对该模块进行再授权 
  二是数据库操作权限,主要是浏览、增加,修改,删除记录,也可以理解为对象权限  
  三是行级数据权限,又可以叫做对象实例级权限,主要是考虑到不是每个用户都可以看到所有记录的,比如在工资管理中一个部门用户就只能看到本部门人员的工资,甚至只能看到本部门什么职位以下的人员的工资。
  四是列级数据权限,即数据字段的权限,不是每个用户都可以看到或是修改所有的字段的。
具体四类可能的权限划分如下:
  一系统权限,可见/可再授权  
  二数据库操作权限,浏览/增加/修改/删除  
  三行级数据权限,数据范围
  四列级数据权限,可见/修改
然后是可能存在的权限模式,现在考虑的是大集中模式下的权限管理
  理解:什么是大集中模式下的权限管理。举个例子总公司下面有分公司A和分公司B,总公司系统管理员分别在分公司A和分公司B中设定分公司系统管理员,则分公司A和分公司B数据是互不可见的,分公司系统管理员只对各自分公司负责,他们可以同时建立一个相同名字的角色,但这两个角色是不同的。即下级只可以看到自己,上级可以看到所有。这个管理是可以嵌套的,即分公司A下面可能还会存在分公司,分公司下面还会有新的分部门。。。。
posted @ 2006-07-03 10:50 ronghao 阅读(3959) | 评论 (5)编辑 收藏
最近在设计权限方面的内容,有些想法,乱弹一下。个人觉得实现权限系统主要是三个方面:
1、授权。主要是授权模型的维护,如资源、角色、用户、部门的对应关系等。
2、认证。主要是用户身份的认证,以及取出用户的权限。
3、校验权限。当用户对某一资源进行操作时,将用户的权限与操作该资源所应有的权限进行比对校验。
在对这三个方面进行说明前,也想说说对数据权限的看法。什么是数据权限,很简单,
考虑一种场景 (javaeye里的例子)
看看销售数据
A销售员可以看到自己的销售情况和每一笔具体销售业务,但是看不到B的
B销售员可以看到自己的销售情况和每一笔具体销售业务,但是看不到A的
分公司销售经理则可以看到本部门的A和B的销售情况,但是看不到其他分公司的销售业务
集团销售Boss可以看到集团内所有分公司的销售业务数据
共同点:他们都可以看到“销售数据”这一模块
不同点:他们读取数据的范围是不一样的
这个也可以叫做实例级权限控制。有人认为这种权限一般是放在业务里做的, 如果非要用一个固定的模型实现,可以参考 ACL, 不过也是要在业务里写 ACL 相关代码的。确实,以前自己也都是放在业务里做的,但一直认为这也应该是权限系统的一部分,通过用户的权限来构造不同的sql语句。具体通过AOP实现,好象已经有个开源的东西还没看(lllyq的http://bba96.dev.java.net)。ACL对实例级权限控制感觉效率会有问题,个人看法。
具体来说:
1、授权
   具体开发里简便的授权方式已经成了用户必提的要求,很明显,仅仅基于role和权限交互是远远不够的。现实中你必须把角色、用户、部门三者全部与权限挂钩。而这三者毫无疑问地就会存在权限继承的问题。考虑一下,在部门A增加一个用户,很显然该用户会继承部门A的权限;同时如果在部门A下增加一个部门角色,该角色应不应该也继承部门A的权限呢?也许需要一个规则接口,具体规则实现看客户需求。
   再说说资源,这里仅讨论系统资源不考虑数据资源。考虑一个“业务管理”的模块,该模块下面还有“项目管理”,“物品管理”,“采购管理”三个模块,当对用户赋予了“业务管理”模块的查看权限,用户是否同时对“项目管理”,“物品管理”,“采购管理”具有查看权限呢?这里同样存在资源权限继承的问题。客户的需求是不同的,所以同样需要一个资源权限继承的规则接口。
   最后说说授权信息的保存。考虑ACL。表设计:资源ID,权限主体ID,权限主体TYPE,资源操作权限。一开始考虑权限主体ID就是UserID,这样会在认证的时候效率很高,但考虑到部门A下有100个用户,当对部门A增加一个权限时,实际上会往ACL表里插入100条记录,这就让人不能接受了。
2、认证
   其实就两个方面,一是检查是否存在这个用户,二是取出用户的权限。呵呵,这里有些绝对了,取出用户的权限完全可以放到校验权限里来做,不必这里一次性全读出来。用户的权限放在一个List里,对象可以构造一个,两个属性:资源ID和资源操作权限。取出用户的权限时候要用到上面定义的规则接口来组装用户实际的所有权限。
2、校验权限
   这个就比较简单了,个人倾向于在Action里完成这个工作,需要进行权限检查的Action实现一个接口,接口里有一个 public boolean hasPermission() ,写个拦截器拦截即可。这里的关键还是通过资源权限继承的规则接口来校验用户操作该资源的权限。
完全是个人乱弹,欢迎拍砖:)
posted @ 2006-06-29 16:29 ronghao 阅读(3623) | 评论 (2)编辑 收藏

开发中遇到这么一个要求,用户USER这个PO里需要保存用户的照片。一般情况下有两种处理方法:一是直接保存图片到数据库;二是保存图片到服务器端,PO字段保存一个联接。考虑到用户照片一般较小,采用第一种方法。

 1 public   class  User  extends  Principal  {
 2      private   byte [] photo;    // 考虑与各种数据库兼容问题
 3      public   byte [] getPhoto()  {
 4          return  photo;
 5     }

 6
 7      public   void  setPhoto( byte [] photo)  {
 8          this .photo  =  photo;
 9     }

10     ..
11 }

12


页面里面,直接用webwork的FileUploadInterceptor拦截

< @ww.form  enctype = " multipart/form-data "  action = " doUpdateUser "   method = " post " >
< image name = " img1 "  src = " /getUserPhoto.action?userId=${user.id} "  width = 90  height = 120  border = " 0 " ></ image >
< @ww.file label = " %{getText('user.photo')} "  name = " photo "   />
</ @ww.form >


xwork.xml里的配置

        < action name = " doUpdateUser "   class = " com.ronghao.organization.action.OrganizationAction "  method = " updateUser " >
            
< interceptor - ref name = " defaultStack " >
                
< param name = " fileUpload.allowedTypes " >
                     image
/ x - png,image / gif,image / pjpeg
                
</ param >
            
</ interceptor - ref >
            
< result name = " success " >/ organization / user.ftl </ result >
        
</ action >

        
< action name = " getUserPhoto "   class = " com.ronghao.organization.action.OrganizationAction "  method = " getUserPhoto " >
            
< result >/ organization / user.ftl </ result >
        
</ action >


引用默认的拦截器栈,栈里已经包含FileUploadInterceptor拦截,这里配置FileUploadInterceptor拦截的参数,设定上传的文件为图片
格式
Action类

 1 public   class  OrganizationAction  extends  BaseOrganizationAction {
 2      private  File photo;   // 与页面里的上传文件字段名对应
 3      private  OutputStream outPhoto
 4
 5       /**
 6      * 更新用户信息
 7      *
 8      *  @return
 9      *  @throws  Exception
10       */

11      public  String updateUser()  throws  Exception  {
12          // 以下为保存图片,视情况修改
13          if  ( null   !=  photo)  {
14             FileInputStream file  =   new  FileInputStream(photo);
15              int  length  =  file.available();
16              byte [] buffer  =   new   byte [length];
17             file.read(buffer);
18             file.close();
19             user.setPhoto(buffer);    // 将上传的图片转换为字节数组存储到PO中
20         }

21         organizationService.updateUser(user);
22          return  SUCCESS;
23     }

24
25      public  String getUserPhoto()  throws  Exception  {
26         user  =  organizationService.getUserById(userId);
27          if  (user.getPhoto()  !=   null {
28             outPhoto  =   this .getResponse().getOutputStream();  // 将PO中字节数组转换为输出流
29             outPhoto.write(user.getPhoto());
30             outPhoto.flush();
31             outPhoto.close();
32         }

33          return  SUCCESS;
34     }

35 }

36


这样就OK了,考虑一个问题,就是action 必须要指定一个result ,实际这里的图片显示仅仅是要一个输出流

< image name = " img1 "  src = " /getUserPhoto.action?userId=${user.id} "  width = 90  height = 120  border = " 0 " ></ image >


而我的配置里面是

< result >/ organization / user.ftl </ result >


不知道这会不会有什么影响。或者这样?

< result >/ organization / blank.ftl </ result >
posted @ 2006-06-15 17:14 ronghao 阅读(660) | 评论 (0)编辑 收藏
好久都没有更新过BLOG了,别的原因没有,就是变懒了。工作还算可以,加班不是太多。买了辆二手
自行车,修车就花了20,天天蹬着车往返于公司和家之间。又有新的楼盘开盘了,价格还是高得吓人,
女朋友天天想着什么时候住上自己的房子,真是一件苦恼的事情。油价涨了我还有腿,可房价涨了呢?
在对自己重新补课,现在确实感到一些东西你不记下来忘得还是飞快的。节绳记之还是必须的。呵呵,
重新开始写BLOG吧。
posted @ 2006-06-11 14:22 ronghao 阅读(381) | 评论 (0)编辑 收藏

    来博罗调研已经是第七天,后天的机票返回北京。短短的几天给我的印象却非常的深。
    这是我第一次出差调研和客户打交道,习惯了面对机器,习惯了每天的编码,或许自己从来没有料到自己会出来面对人来交流。出公司的时候,领导说这是一次难得的锻炼机会,现在来看确实是这样。
印象一:工厂
    项目是广东惠州市博罗县的电子政务。从深圳机场出来到惠州,沿路两旁全部是厂房和宿舍,一片接着一片,多得让人吃惊,车子在高速路上飞驰,看不见田野,看到的是山是楼房。司机开玩笑说,我敢说你穿的衣服一定有一件产自广东。人说中国是世界的工厂,那广东一定是中国的工厂。
印象二:喝汤
    第一顿饭是在县机关饭堂吃得。吃饭前先给打来了一盆汤,喝汤。习惯了吃完饭喝汤,这里却是先喝汤再吃饭。以后在哪里吃饭都是这样,先上汤,喝得差不多了再上菜。再就是早上出去吃早饭,没有豆浆,没有油条,更没有包子,只有米线。第二天早上跑了大半个街道,不好意思,只有米线。
印象三:乱
    这个乱不是指这里的治安,指得是调研。其实在我来之前,公司已经有过两次前期的调研,大致的需求已经清楚,我们所做的事把需求进一步完善,五一后就开始正式开发。情况出乎我的意料,非常的不顺利。在对工作流的演示完毕后,各种各样的情况就来了:我们的领导不会打字,你们要想想办法;流程已经到下一个流程,上一个流程节点的人员可不可以强行把流程回退。种种情况让我感到迷惑。很多概念都要解释很长一段时间。总的感觉就是:人为干扰流程的因素太多,流程迟迟定不下来。然后就是发言的人特多,几乎都是同时。其实这里也有很大一部分责任在于自己:没有对客户做好引导,他们的
思维还是停留在他们自己的日常办公上。OA的实施不仅仅是我们要适应他们,他们也要适应一定的规范。对客户如何引导,我想,这对我来说还是一个要好好思考的问题。
印象四:累
    其实来广东之前已经连续加了一个多礼拜班了。想着是这次调研会轻松一些,甚至还带了XML的书,打算把这方面补一补,不都web2.0了吗:)结果却是比编程感觉更累。白天和客户交流,中午和晚上一有空就要写文档,每天晚上都到12点,中午就没有休息过。写文档真是一件枯燥而又繁重的工作。每一个button都要作出详尽的描述,让人气馁。而流程的描述更是繁琐几乎要面面俱到,中间还有权限的分配。情况却往往是这样:客户提出的流程很快发生了变化。还有就是,文字的描述要符合官方的习惯。例如,县委就一定要在县政府的前面。这点自己也是一直并未在意,但客户就提意见了。
    最后,就是,广东话我一句都听不懂,呵呵。

posted @ 2006-04-26 19:40 ronghao 阅读(459) | 评论 (0)编辑 收藏
刚刚换了工作,下周去新的公司上班。复杂的心情,很难说的出是一种什么的感觉。中午经理请我吃饭,本该我请他的,他一定要付钱。老实说,经理对我非常不错,但是,很多东西是很难说的。上个月向经理提出辞职,他表示了理解。新公司在上地,这就意味着要重新找房子。感觉北边的房子比南边的房子要贵出一头,找了个两居,明天打算搬过去。呵呵,也正在找人合租。
posted @ 2006-03-03 14:04 ronghao 阅读(844) | 评论 (4)编辑 收藏
Tomcat是接触最久的应用服务器,同时也被它的classloading愚弄过好多次。印象中比较深的一次是建立了一个web应用,使用oracle数据库,我把oracle的jdbc driver放到了WEB-INF/lib目录下面,然后给Tomcat配置了数据源,在这个应用里面连接这个数据源,来访问数据库。看起来一切正常,可是一启动Tomcat,就报出一个错误,说是找不到driver类。后来知道这是由于Tomcat的classloading机制造成的。总的来说,Tomcat开源、简单,文档清楚,又学习过一阵它的源码,是我了解最多的应用服务器了,所以就从它开始。
 
      运行Tomcat就是运行org.apache.catalina.startup.Bootstrap类的main方法,和运行普通的应用程序并无二致,所以Java 2的classloading机制适用于这个过程。但是Bootstrap运行起来以后,会加载common、server下面的类,加载webapps下面的web应用。这些类的加载是由不同的classloader来完成的。Tomcat的classloader结构如下:

BootStrap classloader(加载JRE/lib下的rt.jar和其他重要jar文件)

                                                     é

ExtClassLoader (加载JRE/lib/ext下的jar文件,Java扩展框架使用)

                                                     é

AppClassLoader ($CATALINA_HOME/bin/bootstrap.jar commons-logging -api.jarcommons-daemon.jar$JAVA_HOME/lib/tools.jarjmx.jar)

                                   é

common (加载$CATALINA_HOME/common/Tomcat本身和Web App共享类)

                  é                                                                        é

Catalina (加载$CATALINA_HOME /server/Tomcat本身使用的类)

Shared (加载$CATALINA_BASE /shared/,所有Web App共享类)

                                                        é

WebappClassLoader (加载各个Web Appclass,不同的Web App被隔离开)


Tomcat在启动的时候,完全忽略了class path的设置,而重新设置了class path,所以AppClassLoader 载入的类将不是class path设置的类。
 
Tomcat没有完全使用Java 2的parent delegation模型。这一点体现在WebappClassLoader上。在一个web app中,载入类的过程是这样的:
 
首先检查本地的WebappClassLoader,如果没有,
则请求它的父ClassLoader,即shared。
从shared开始,采用parent delegation,即shared请求它的父classloader common来载入类,这个过程一直延续到BootStrap classloader。
 

正是因为这种机制,使我们在两个Web app中有相同的class的时候,不会相互干扰。比如说,两个app中都使用了log4j,在WEB-INF/lib下面分别有一份log4j.jar,配置输出到不同的文件。因为WebappClassLoader仅对本app可见,所以log4j可以独立工作,而不相互影响。但是,如果我们把这两个app下面的log4j.jar移动到shared目录或者common目录,那他们就会把日志输出到同样的文件了,因为这时候是共享的。
 
记得当时看到WebappClassLoader的这个特性,心下暗喜,盘算着自己能不能写一个java.lang.String类,放到WEB-INF/lib下面,而得到优先加载的机会呢?马上兴冲冲地进行试验,但是结果让我失望,翻出tomcat的源码一看,发现以java.,javax.,sun.,开头的class,WebappClassLoader一概不予理会,直接把烫山芋扔给它的父loader了。另外,Tomcat文档交待,遇到加载org.xml.sax.* ,org.w3c.dom.* ,org.apache.xerces.* ,org.apache.xalan.* 这些包的class的请求,WebappClassLoader也不会受理。

引用地址:
http://spaces.msn.com/myj1024/blog/ 
                                
posted @ 2006-03-01 16:55 ronghao 阅读(2705) | 评论 (0)编辑 收藏
个人比较懒一点,对异常处理也懒的可以。程序中异常分为Exception和RuntimeException。每个层定义一个RuntimeException,例如DAO层,就一个DaoRuntimeException;service层,就一个ServiceRuntimeException.所有该层中程序无法恢复的异常通通用各层的RuntimeException封装扔出,最后统一捕捉有一个专门的异常处理类处理(这个类也就是读出异常类中所包含的信息,最后告诉用户:不好意思,系统问题,请通知那帮程序员!)
而Exception定义的比较多一点,其实仅仅是类的签名不同而已。它们表达了不期望的各种事件流,可以通过它们来部分的控制事件逻辑。比如很简单的一个UnauthorizedException,告诉客户没有权限等等,调用捕捉到这个异常就会改变事件流到相应处理页面提示用户。
posted @ 2006-02-20 15:36 ronghao 阅读(683) | 评论 (2)编辑 收藏
好久没有更新BLOG了,过完年回来就一直思考着要不要换个工作。老实说,自己目前的工作还是很不错的,老板和经理都很好,但是问题就是研发这块人数太少,自己的工作从表现层到数据库、用例分析包括CSS都有涉及,都懂点但都不精;并且所有的开发相关全部用的是开源,自己的提高有限,很多东西仅仅靠自己自学是远远不够的,要有人来带一带。工作的时间越长,越感到自己学的东西太少太少。所以就想换一个大一点的公司更好的提高自己。
网上更新了简历,最近也有面试,很多都让你去做外包。好象很多人对外包有看法,我也是。不做外包。
posted @ 2006-02-15 10:52 ronghao 阅读(552) | 评论 (2)编辑 收藏
周六报名参加了BEA UG活动。去的时候工作人员已经在不断的加凳子了。奇怪的是第一排竟然还有空位却没人去坐,于是很幸运的来得晚却坐到了第一排。
BEA的张健的演讲更倾向于BEA本身的一些东西,除了推介BEA贡献的开源项目Beehive,他还用XMLBean和BEA的WorkShop做了演示,第一次见BEA的WorkShop还以为是Eclipse,两者长得太象,后来张键说WorkShop是基于Eclipse的。总体感觉有点象给BEA做市场推广的意思。呵呵,个人意见。
Perryn Folwer,使用Selenium为Web App进行自动功能测试。自己Selenium并不了解,Perryn Folwer说Selenium是用javascript写的,整个演示给人的感觉是很舒服。看了用JAVA写的Selenium测试脚本,代码量还是不少的(仅仅是一个查询),这样如果覆盖整个应用程序会有多少代码量呢?但是考虑到一旦测试脚本确定下来以后的工作将是非常轻松的,可以更快的得到用户的反馈,这点代价也是值得的。
其实这次参加BEA UG最主要的还是想听听Michael Chen对AJAX的一些发展看法。试用过json,Bufflo,Demo都做的非常好,但自己关心的是如何把它运用到具体的项目中去,有没有一些最佳实践。Michael Chen强调了一个适用范围:中小型应用,但什么是大应用与中小应用的分界点还是比较模糊,Michael Chen说自己对Bufflo的应用还是结合webwork一起用,取代webwork
的校验部分,同时他还说到了一个什么TREE??(没听清)有意思的是Michael Chen在提到webwork的时候又顺便把STRUTS鄙视了一通:)
总的感想是:真正感兴趣的话题没有深入地谈,遗憾。对我们这些开发人员来说,参加几个人或者十几个人的小型聚会互相交流一下经验和想法也许会更好一点。
posted @ 2006-01-23 11:04 ronghao 阅读(528) | 评论 (0)编辑 收藏
     摘要: ibatis DAO 框架提供了事务管理模块。而这个事务管理可以应用到很多场合,包括JDBC、Hibernate、JTA、SQLMAP等。下面以最简单的JDBC来分析一下其如何实现事务管理。首先来看一段代码: public class OrderService {  private DaoManager daoManager;...  阅读全文
posted @ 2006-01-20 17:50 ronghao 阅读(7209) | 评论 (6)编辑 收藏
一、执行期根据方法的名称来执行方法
下面的示例演示了这一操作:
import java.lang.reflect.*;
public class method2 {
   
public int add(int a, int b) {
       
return a + b;
   }

   
public static void main(String args[]) {
       
try {
           Class cls 
= Class.forName("method2");
           Class partypes[] 
= new Class[2];
           partypes[
0= Integer.TYPE;
           partypes[
1= Integer.TYPE;
           Method meth 
= cls.getMethod("add", partypes);
           method2 methobj 
= new method2();
           Object arglist[] 
= new Object[2];
           arglist[
0= new Integer(37);
           arglist[
1= new Integer(47);
           Object retobj 
= meth.invoke(methobj, arglist);
           Integer retval 
= (Integer) retobj;
           System.out.println(retval.intvalue());
       }
 catch (Throwable e) {
           System.err.println(e);
       }

   }

}

注:上面划线的粗体字最好用Object methobj =  cls.newInstance();来代替,原因很明显如果这个类及方法事先都是清楚的也不需要用reflection了
    假如一个程序在执行的某处的时候才知道需要执行某个方法,这个方法的名称是在程序的运行过程中指定的 (例如,JavaBean 开发环境中就会做这样的事),那么上面的程序演示了如何做到。上例中,getMethod 用于查找一个具有两个整型参数且名为 add 的方法。找到该方法并创建了相应的 Method 对象之后,在正确的对象实例中执行它。执行该方法的时候,需要提供一个参数列表,这在上例中是分别包装了整数 37 和 47 的两个 Integer 对象。执行方法的返回的同样是一个 Integer 对象,它封装了返回值 84。

二、执行期创建新的对象

对于构造器,则不能像执行方法那样进行,因为执行一个构造器就意味着创建了一个新的对象 (准确的说,创建一个对象的过程包括分配内存和构造对象)。所以,与上例最相似的例子如下:

import java.lang.reflect.*;
public class constructor2 {
   
public constructor2() {
   }

   
public constructor2(int a, int b) {
       System.out.println(
"a = " + a + " b = " + b);
   }

   
public static void main(String args[]) {
       
try {
           Class cls 
= Class.forName("constructor2");
           Class partypes[] 
= new Class[2];
           partypes[
0= Integer.TYPE;
           partypes[
1= Integer.TYPE;
           Constructor ct 
= cls.getConstructor(partypes);
           Object arglist[] 
= new Object[2];
           arglist[
0= new Integer(37);
           arglist[
1= new Integer(47);
           Object retobj 
= ct.newInstance(arglist);
       }
 catch (Throwable e) {
           System.err.println(e);
       }

   }

}

三、改变字段(域)的值

reflection 的还有一个用处就是改变对象数据字段的值。reflection 可以从正在运行的程序中根据名称找到对象的字段并改变它,下面的例子可以说明这一点:

import java.lang.reflect.*;
public class field2 {
   
public double d;
   
public static void main(String args[]) {
       
try {
           Class cls 
= Class.forName("field2");
           Field fld 
= cls.getField("d");
           field2 f2obj 
= new field2();
           System.out.println(
"d = " + f2obj.d);
           fld.setDouble(f2obj, 
12.34);
           System.out.println(
"d = " + f2obj.d);
       }
 catch (Throwable e) {
           System.err.println(e);
       }

   }

}
这个例子中,字段 d 的值被变为了 12.34。
实际开发时用Common BeanUtils
posted @ 2006-01-17 17:41 ronghao 阅读(575) | 评论 (0)编辑 收藏
 其实很早就有这个愿望了,只是一直没有实施。在买笔记本还是自己配台式机的问题上一直拿不定主意。去年十一的时候就打算买了,朋友说,你一天上班要接受电脑9、10个小时的辐射,下班回来还要接着辐射啊。去年一年的工作,给我印象最深的恐怕就是:学到很多东西,但大部分都未能实际实施。想一想,报表、搜索引擎、工作流、邮件、lunix、css、webservice自己都有做过。工作中交给你一个未实现过的功能实现,google-->开源项目-->读它的英文文档-->跑出第一个demo-->应用到自己的模块中-->研究它的源码,这个过程是一个很惬意的事情。但问题是不能深入。懂点皮毛根本没什么用!呵呵,用公司的电脑学自己想学的东西根本就没那个时间。
 买台电脑,多写代码,多钻研点东西,这就是今年的愿望。
posted @ 2006-01-05 17:39 ronghao 阅读(408) | 评论 (1)编辑 收藏

 项目终于上线运行了,但是还是问题多多,但还好都不是涉及到逻辑的大问题。这两天把数据备份这块重做了一下,原先是自己写的一个线程池,功能其实就是每天对mysql进行备份(当初设计为什么会把这个功能放到程序里实现??)。这完全是重复的造轮子!开始是用Jrontab重构了下,后来讨论后决定在程序里把这项功能删除,改在Linux里写个备份脚本。每天的晚上12点进行备份,然后本机一份,ftp到另一台服务器一份。有段时间没操作Linux了,结果好多命令都忘了,寒!为什么会重复的造轮子??

posted @ 2005-12-28 18:19 ronghao 阅读(816) | 评论 (4)编辑 收藏

在数据库里新建两个表
A、用户表
create table users (user_name varchar(20) not null,
                               user_pass varchar(20) not null,
                               PRIMARY KEY (user_name)) ;
B、用户与角色关联表
create table user_roles (user_name varchar(20) not null,
                                        role_name varchar(20) not null,
                                        PRIMARY KEY (user_name)) ;
C、插入数据
insert into users values('user1', 'password');
insert into user_roles values('user1', 'manager');

在tomcat的server.xml里加入描述
      <Realm  className="org.apache.catalina.realm.JDBCRealm"
              driverName="org.gjt.mm.mysql.Driver"
              connectionURL="jdbc:mysql://localhost/databaseName"
              connectionName=yourname connectionPassword=yourpassword
              userTable="users" userNameCol="user_name" userCredCol="user_pass"
              userRoleTable="user_roles" roleNameCol="role_name" />

在自己应用程序的web.xml里加入描述(基于表单)
<security-constraint>
  <web-resource-collection>
   <web-resource-name>My Test</web-resource-name>
   <url-pattern>/get.jsp</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>manager</role-name>    //能够访问的角色,可以多个
  </auth-constraint>
</security-constraint>

<login-config>
  <auth-method>FORM</auth-method>
    <realm-name>My Test</realm-name>
  <form-login-config>
  <form-login-page>/login.jsp</form-login-page>
  <form-error-page>/fail_login.html</form-error-page>  //认证失败后跳转的页面
  </form-login-config>
</login-config>

登录表单必须包含输入用户姓名和口令的字段,它们必须被分别命名为j_username和j_password,表单将这二个值发送给j_security_check逻辑名字。
  下面是一个该表单如何在HTML网页中实现的例子:
<form method="POST" action="j_security_check">
  <input  type="text" name="j_username">
  <input  type="password" name="j_password">
    <input  type="submit" value="确定"/>
</form>

这样当对get.jsp进行访问时,tomcat就会自动转到login.jsp页面实现认证。对于简单的认证,小型系统,采用Tomcat实现容器内认证是方便的。

posted @ 2005-12-28 14:00 ronghao 阅读(1408) | 评论 (3)编辑 收藏

commons.fileupload实现文件的上传,代码如下:
<%!
 //服务器端保存上传文件的路径
    String saveDirectory = "g:\\upload\\";
    // 临时路径 一旦文件大小超过getSizeThreshold()的值时数据存放在硬盘的目录
    String tmpDirectory = "g:\\upload\\tmp\\";
    // 最多只允许在内存中存储的数据大小,单位:字节
    int maxPostSize = 1024 * 1024;
%>
<%
    // 文件内容 
    String FileDescription = null;
    // 文件名(包括路径)
    String FileName = null;
    // 文件大小
    long FileSize = 0;
    // 文件类型
    String ContentType = null;

%>

<%
   DiskFileUpload fu = new DiskFileUpload();
    // 设置允许用户上传文件大小,单位:字节
   fu.setSizeMax(200*1024*1024);
    // 设置最多只允许在内存中存储的数据,单位:字节
   fu.setSizeThreshold(maxPostSize);
    // 设置一旦文件大小超过getSizeThreshold()的值时数据存放在硬盘的目录
   fu.setRepositoryPath("g:\\upload\\tmp\\");
    //开始读取上传信息 得到所有文件
   try{
      List fileItems = fu.parseRequest(request);
     }catch(FileUploadException e){
         //这里异常产生的原因可能是用户上传文件超过限制、不明类型的文件等
         //自己处理的代码
     }
%>
<%
   // 依次处理每个上传的文件
   Iterator iter = fileItems.iterator();
   while (iter.hasNext()) {
     FileItem item = (FileItem) iter.next();
       //忽略其他不是文件域的所有表单信息
     if (!item.isFormField()) {
       String name = item.getName();
       long size = item.getSize();
       String  contentType = item.getContentType();
     if((name==null||name.equals("")) && size==0)
       continue;
%>
<%
   //保存上传的文件到指定的目录
  String[] names=StringUtils.split(name,"\\");  //对原来带路径的文件名进行分割
   name = names[names.length-1];
   item.write(new File(saveDirectory+ name));
  }
}
%>
 下面是其简单的使用场景:
 A、上传项目只要足够小,就应该保留在内存里。
 B、较大的项目应该被写在硬盘的临时文件上。
 C、非常大的上传请求应该避免。
 D、限制项目在内存中所占的空间,限制最大的上传请求,并且设定临时文件的位置。
 
 可以根据具体使用用servlet来重写,具体参数配置可以统一放置到一配置文件
 



 文件的下载用servlet实现
      public void doGet(HttpServletRequest request,
                       HttpServletResponse response)
     {
         String aFilePath = null;    //要下载的文件路径
         String aFileName = null;    //要下载的文件名
         FileInputStream in = null;  //输入流
         ServletOutputStream out = null;  //输出流

         try
   {
          
             aFilePath = getFilePath(request);
             aFileName = getFileName(request);

             response.setContentType(getContentType(aFileName) + "; charset=UTF-8");
             response.setHeader("Content-disposition", "attachment; filename=" + aFileName);

             in = new  FileInputStream(aFilePath + aFileName); //读入文件
            out = response.getOutputStream();
            out.flush();
            int aRead = 0;
           while((aRead = in.read()) != -1 & in != null)
        {
             out.write(aRead);
         }
           out.flush();
      }
       catch(Throwable e)
     {
     log.error("FileDownload doGet() IO error!",e);
      }
         finally
         {
             try
             {
                 in.close();
                 out.close();
             }
             catch(Throwable e)
             {
              log.error("FileDownload doGet() IO close error!",e);
             }
         }
     }

posted @ 2005-12-16 10:46 ronghao 阅读(6232) | 评论 (1)编辑 收藏

  检查字符串是否为空或null或仅仅包含空格
  String test = "";
  String test1=" ";
  String test2 = "\n\n\t";
  String test3 = null;
  System.out.println( "test blank? " + StringUtils.isBlank( test ) );
  System.out.println( "test1 blank? " + StringUtils.isBlank( test1 ) );
  System.out.println( "test2 blank? " + StringUtils.isBlank( test2 ) );
  System.out.println( "test3 blank? " + StringUtils.isBlank( test3 ) );
  运行结果:
  test blank? true
  test1 blank? true
  test2 blank? true
  test3 blank? true
  相对应的还有一个StringUtils.isNotBlank(String str)
  StringUtils.isEmpty(String str)则检查字符串是否为空或null(不检查是否仅仅包含空格)
 
  分解字符串
  StringUtils.split(null, *, *)            = null
  StringUtils.split("", *, *)              = []
  StringUtils.split("ab de fg", null, 0)   = ["ab", "cd", "ef"]
  StringUtils.split("ab   de fg", null, 0) = ["ab", "cd", "ef"]
  StringUtils.split("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
  StringUtils.split("ab:cd:ef", ":", 1)    = ["ab:cd:ef"]
  StringUtils.split("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
  StringUtils.split(String str,String separatorChars,int max) str为null时返回null
  separatorChars为null时默认为按空格分解,max为0或负数时分解没有限制,max为1时返回整个字符串,max为分解成的个数(大于实际则无效)
 
  去除字符串前后指定的字符
  StringUtils.strip(null, *)          = null
  StringUtils.strip("", *)            = ""
  StringUtils.strip("abc", null)      = "abc"
  StringUtils.strip(" abc ", null)    = "abc"
  StringUtils.strip("  abcyx", "xyz") = "  abc"
  StringUtils.strip(String str,String stripChars) str为null时返回null,stripChars为null时默认为空格

  创建醒目的Header(调试时用)
  public String createHeader( String title ) {
    int width = 30;
    String stars = StringUtils.repeat( "*", width);
    String centered = StringUtils.center( title, width, "*" );
    String heading = StringUtils.join(new Object[]{stars, centered, stars}, "\n");
    return heading;
  }
  调用createHeader("TEST")的输出结果:
  ******************************
  ************ TEST ************
  ******************************

  字符的全部反转及以单个词为单位的反转
  String original = "In time, I grew tired of his babbling nonsense.";
  StringUtils.reverse( original )   = ".esnesnon gnilbbab sih fo derit werg I ,emit nI"
  以单个词为单位的反转
  public Sentence reverseSentence(String sentence) {
    String reversed = StringUtils.chomp( sentence, "." );
    reversed = StringUtils.reverseDelimited( reversed, ' ' );
    reversed = reversed + ".";
    return reversed;
  }
  String sentence = "I am Susan."
  reverseSentence( sentence ) )   = "Susan am I."

  检查字符串是否仅仅包含数字、字母或数字和字母的混合
  String test1 = "ORANGE";
  String test2 = "ICE9";
  String test3 = "ICE CREAM";
  String test4 = "820B Judson Avenue";
  String test5 = "1976";
  结果:
  boolean t1val = StringUtils.isAlpha( test1 ); // returns true
  boolean t2val = StringUtils.isAlphanumeric( test2 ); // returns true
  boolean t3val = StringUtils.isAlphaSpace( test3 ); // returns true
  boolean t4val = StringUtils.isAlphanumericSpace( test4 ); // returns true
  boolean t5val = StringUtils.isNumeric( test5 ); // returns true

posted @ 2005-12-14 12:47 ronghao 阅读(2584) | 评论 (1)编辑 收藏

  primitive 数组克隆及反转排序
  long[] array = { 1, 3, 2, 3, 5, 6 };
  long[] reversed = ArrayUtils.clone( array );
  ArrayUtils.reverse( reversed );
  System.out.println( "Original: " + ArrayUtils.toString( array ) );   //打印
  System.out.println( "Reversed: " + ArrayUtils.toString( reversed ) );
 
  对象数组克隆及反转排序
  Long[] array = new Long[] { new Long(3), new Long(56), new Long(233) };
  Long[] reversed = ArrayUtils.clone( array );
  ArrayUtils.reverse( reversed );
 
  primitive 数组与对象数组之间的转换
  long[] primitiveArray = new long[] { 12, 100, 2929, 3323 };
  Long[] objectArray = ArrayUtils.toObject( primitiveArray );
  Double[] doubleObjects = new Double[] { new Double( 3.22, 5.222, 3.221 ) };
  double[] doublePrimitives = ArrayUtils.toPrimitive( doubleObject );
  注意:对象数组可以含有null元素,primitive 数组则不容许含有null元素,所以对象数组转换为primitive 数组时,可以添入第二个参数,当碰到为null的元素时用其代替(如下,Double.NaN)。如果不添入第二个参数,当碰到为null的元素时,则会抛出NullPointerException 。
  double[] result = ArrayUtils.toPrimitive( resultObjArray, Double.NaN  );
 
  查找一个数组中是否含有特定的元素(查找对象数组时,比较的是对象的equals()方法),及特定元素的第一次出现位置和最后一次出现位置
  String[] stringArray = { "Red", "Orange", "Blue", "Brown", "Red" };
  boolean containsBlue = ArrayUtils.contains( stringArray, "Blue" );
  int indexOfRed = ArrayUtils.indexOf( stringArray, "Red");
  int lastIndexOfRed = ArrayUtils.lastIndexOf( string, "Red" ); 
 
  由二维对象数组创建一个 Map
  Object[] weightArray =
    new Object[][] { {"H" , new Double( 1.007)},
                     {"He", new Double( 4.002)},
                     {"Li", new Double( 6.941)},
                     {"Be", new Double( 9.012)},
                     {"B",  new Double(10.811)},
                     {"C",  new Double(12.010)},
                     {"N",  new Double(14.007)},
                     {"O",  new Double(15.999)},
                     {"F",  new Double(18.998)},
                     {"Ne", new Double(20.180)} };

  Map weights = ArrayUtils.toMap( weightArray );
  Double hydrogenWeight = (Double)weights.get( "H" );
  注意:当二维对象数组"key"值重复时,创建的Map,后面的键-值对会把前面的覆盖掉

posted @ 2005-12-13 18:48 ronghao 阅读(1357) | 评论 (4)编辑 收藏

  DisplayTag是一个非常好用的表格显示标签,适合MVC模式,其主页在http://displaytag.sourceforge.net 
一、最简单的情况,未使用<display:column/>标签
  <%request.setAttribute( "test", new ReportList(6) );%>
  <display:table name="test" />
  标签遍历List里的每一个对象,并将对象里的所有属性显示出来。一般用于开发的时候检查对象数据的完整性。
 
二、使用<display:column/>标签的情况
<display:table name="test">
  <display:column property="id" title="ID" />
  <display:column property="name" />
  <display:column property="email" />
  <display:column property="status" />
  <display:column property="description" title="Comments"/>
</display:table>
   property对应List里对象的属性(用getXXX()方法取得),title则对应表格表头里的列名。定义列有两种方式:
   A、<display:column property="email" />
      使用<display:column/>标签里的property属性来定义
   B、<display:column title="email">email@it.com</display:column>
      在<display:column/>标签体里增加内容,可以是常量,也可以用其他标签等等
   两种方式比较,用property属性来定义更加快速和利于排序。
  
三、表格显示样式的定义
  A、在<display:table/>和<display:column/>标签里指定标准的html属性,烦琐
  B、修改样式表
<display:table name="test" class="mars">
  <display:column property="id" title="ID" class="idcol"/>
  <display:column property="name" />
  <display:column property="email" />
  <display:column property="status" class="tableCellError" />
  <display:column property="description" title="Comments"/>
</display:table>
   通过class属性来指定所要应用的样式。可以在其默认样式表里(./css/screen.css)直接修改
  
四、标签取得数据的数据源
  有四种范围
   pageScope
   requestScope (默认)  <display:table name="test2" >
   sessionScope  <display:table name="sessionScope.holder.list" > 注意,这里要指定范围,非默认
   applicationScope
  
五、通过增加id属性创建隐含的对象
<display:table name="test" id="testit">
    <display:column property="id" title="ID" />
    <display:column property="name" />
    <display:column title="static value">static</display:column>
    <display:column title="row number (testit_rowNum)"><%=pageContext.getAttribute("testit_rowNum")%></display:column>
    <display:column title="((ListObject)testit).getMoney()"><%=((ListObject)pageContext.getAttribute("testit")).getMoney()%></display:column>
</display:table>
   注意到在<display:table/>里增加了id属性,这时就在page context里创建了一个隐含对象,指向List里的当前对象,
   可以通过(ListObject)pageContext.getAttribute("id")来捕获这个对象。同时还创建了一个id_rowNum对象,同样,可
   通过pageContext.getAttribute("testit_rowNum")来捕获,它仅仅代表当前行的行数。
   有了这两个隐含对象,就可以通过其他标签来访问,例如Jstl:
  <display:table id="row" name="mylist">
    <display:column title="row number" >
      <c:out value="${row_rowNum}"/>
    </display:column>
    <display:column title="name" >
      <c:out value="${row.first_name}"/>
      <c:out value="${row.last_name}"/>
    </display:column>
  </display:table>
 
六、显示部分数据
   显示开始五条数据:通过设定length属性
<display:table name="test" length="5">
  <display:column property="id" title="ID" />
  <display:column property="email" />
  <display:column property="status" />
</display:table>
   显示第三到第八条数据:通过设定offset和length属性
<display:table name="test" offset="3" length="5">
  <display:column property="id" title="ID" />
  <display:column property="email" />
  <display:column property="status" />
</display:table> 

七、对email和url地址的直接连接
 <display:table name="test" >
  <display:column property="id" title="ID" />
  <display:column property="email" autolink="true" />
  <display:column property="url" autolink="true" />
 </display:table>
 如果要显示的对象里包含email和url地址,则可以在display:column里直接设定autolink="true"来直接连接
 
八、使用装饰模式转换数据显示(写自己的 decorator )
  A、对整个表格应用decorator
  <display:table name="test" decorator="org.displaytag.sample.Wrapper" >
      <display:column property="id" title="ID" />
      <display:column property="email" />
      <display:column property="status" />
      <display:column property="date" />
      <display:column property="money" />
  </display:table>
    org.displaytag.sample.Wrapper即自己写的decorator,它要继承TableDecorator类,看看它的一个方法:
        public String getMoney()
    {
        return this.moneyFormat.format(((ListObject) this.getCurrentRowObject()).getMoney());
    }
    很明显,它通过父类的getCurrentRowObject()方法获得当前对象,然后对其getMoney()方法进行‘油漆’
  B、对单独的column应用decorator
  <display:table name="test">
     <display:column property="id" title="ID" />
     <display:column property="email" />
     <display:column property="status" />
     <display:column property="date" decorator="org.displaytag.sample.LongDateWrapper" />
  </display:table>
    org.displaytag.sample.LongDateWrapper要实现ColumnDecorator接口,它的方法:
        public final String decorate(Object columnValue)
    {
        Date date = (Date) columnValue;
        return this.dateFormat.format(date);
    }
    显然,它获得不了当前对象(因为它实现的是接口),仅仅是获得该对象的columnValue,然后‘油漆’
   
九、创建动态连接
   有两种方法创建动态连接:
   A、在<display:column/>里通过增加href、paramId、paramName、paramScope、paramProperty属性
      href             基本的URL 地址
      paramId          加在URL 地址后的参数名称
      paramName        数据bean的名称,一般为null(即使用当前List里的对象)
      paramScope       数据bean的范围,一般为null
      paramProperty    数据bean的属性名称,用来填充URL 地址后的参数值
<display:table name="sessionScope.details">
  <display:column property="id" title="ID" href="details.jsp" paramId="id" />
  <display:column property="email" href="details.jsp" paramId="action" paramName="testparam" paramScope="request" />
  <display:column property="status" href="details.jsp" paramId="id" paramProperty="id" />
</display:table> 
    这种方法简便直接,但缺点是无法产生类似details.jsp?id=xx&action=xx的复合URL
   B、应用decorator 创建动态连接:
<display:table name="sessionScope.details" decorator="org.displaytag.sample.Wrapper" >
  <display:column property="link1" title="ID" />
  <display:column property="email" />
  <display:column property="link2" title="Actions" />
</display:table>
   org.displaytag.sample.Wrapper里的方法:
 public String getLink1()
 {
  ListObject lObject= (ListObject)getCurrentRowObject();
  int lIndex= getListIndex();
  return "<a href=\"details.jsp?index=" + lIndex + "\">" + lObject.getId() + "</a>";
 }


 public String getLink2()
 {
  ListObject lObject= (ListObject)getCurrentRowObject();
  int lId= lObject.getId();

  return "<a href=\"details.jsp?id=" + lId
   + "&action=view\">View</a> | "
   + "<a href=\"details.jsp?id=" + lId
   + "&action=edit\">Edit</a> | "
   + "<a href=\"details.jsp?id=" + lId
   + "&action=delete\">Delete</a>";
 }

十、分页
   实现分页非常的简单,增加一个pagesize属性指定一次想显示的行数即可
<display:table name="sessionScope.test" pagesize="10">
 <display:column property="id" title="ID" />
 <display:column property="name" />
 <display:column property="email" />
 <display:column property="status" />
</display:table>

十一、排序
   排序实现也是很简单,在需要排序的column里增加sortable="true"属性,headerClass="sortable"仅仅是
   指定显示的样式。column里的属性对象要实现Comparable接口,如果没有的话可以应用decorator
   defaultsort="1"              默认第一个column排序
   defaultorder="descending"    默认递减排序
<display:table name="sessionScope.stest" defaultsort="1" defaultorder="descending">
  <display:column property="id" title="ID" sortable="true" headerClass="sortable" />
  <display:column property="name" sortable="true" headerClass="sortable"/>
  <display:column property="email" />
  <display:column property="status" sortable="true" headerClass="sortable"/>
</display:table>
  注意的是,当同时存在分页时排序仅仅针对的是当前页面,而不是整个List都进行排序
 
十二、column 分组
   分组只是需要在column里增加group属性
<display:table name="test" class="simple">
  <display:column property="city" title="CITY" group="1"/>
  <display:column property="project" title="PROJECT" group="2"/>
  <display:column property="amount" title="HOURS"/>
  <display:column property="task" title="TASK"/>
</display:table>

十三、导出数据到其他格式(页面溢出filter??)
   在<display:table/>里设定export="true"
   在<display:column/>里设定media="csv excel xml pdf" 决定该字段在导出到其他格式时被包不包含,不设定则都包含
   <display:setProperty name="export.csv" value="false" />
   决定该种格式能不能在页面中导出
<display:table name="test" export="true" id="currentRowObject">
  <display:column property="id" title="ID"/>
  <display:column property="email" />
  <display:column property="status" />
  <display:column property="longDescription" media="csv excel xml pdf" title="Not On HTML"/>
  <display:column media="csv excel" title="URL" property="url"/>
  <display:setProperty name="export.pdf" value="true" />
  <display:setProperty name="export.csv" value="false" />
</display:table>

十四、配置属性,覆盖默认
  两种方法:
  A、在程序classpath下新建displaytag.properties文件
  B、对于单个表格,应用<display:setProperty>标签
  具体可配置的属性:http://displaytag.sourceforge.net/configuration.html
 
十五、一个完整的例子
<display:table name="test" export="true" sort="list" pagesize="8">
  <display:column property="city" title="CITY" group="1" sortable="true"    headerClass="sortable"/>
  <display:column property="project" title="PROJECT" group="2" sortable="true" headerClass="sortable"/>
  <display:column property="amount" title="HOURS"/>
  <display:column property="task" title="TASK"/>
</display:table>
   sort="list" 对整个list进行排序
   导出数据到其他格式时,group无效

posted @ 2005-12-08 16:10 ronghao 阅读(6605) | 评论 (25)编辑 收藏
        看到你的博客,感触很多.我也有群程序员朋友,特别是做了几年后也在考虑自己以后的职业发展方向与规划问题.理论大家都知道,可是实际操作中,却不是那么单纯与容易的.
        我想到了下面的一个故事及一篇对程序员职业生涯规划的一文章,与你分享:
     偶然在网上看到这样一个故事:John和Bill一起到山中探险,忽然他们发现一只老虎正深情的望着他们,John撒腿就要跑,Bill却迅速的从背包里拿出一双跑鞋穿在脚上,John看到后气急败坏的对说Bill“你穿什么鞋也跑不过老虎的”Bill同情的看了他一眼,回答说“我干吗要和老虎比,我只要跑过你就够了。”

  大笑以后不免想起John的境遇是不是有点象中国的程序员呢?我个人习惯把软件从业人员分为初级程序员、高级程序员、系统分析员和项目经理四大类的方法,我把优秀程序员的标准分为职业习惯和个人能力两方面,职业习惯包括文档编写习惯,规范化、标准化的编码习惯、软件测试习惯、模块化开发习惯等,个人能力包括团队协作能力、需求理解能力、学习和创新能力等。我接触过的几百个程序员后的感觉,除非那种天生适合编程的人才能成为行业的顶尖高手,按照以上标准绝大多数程序员只能归入“平庸”之列,所以我们这里的讨论主要是基于大多数“平庸”的程序员的。

  故事中的John能不能活命跟三个问题有关:老虎、Bill、自己,中国程序员的困境也来自三个方面。

  困境之一:老虎的威胁。程序员要面对的饿老虎实在不少,比如说老板,好象老板就是程序员的天敌(当然自己当老板的程序员除外,呵呵),什么“不懂技术却指手画脚”、什么“得到与付出不相当”似乎是程序员最常见的牢骚,这个问题不可能得到真正的解决,在这里就不详细讨论了。

  困境之二:Bill的竞争。一般说来中国的程序员大都是吃“青春饭”的,大部分程序员的黄金时代是24~28岁。到了30岁左右,一批又一批年轻程序员会给你带来巨大的竞争压力。首先由于软件行业的飞速发展,很多自己以前学的东西逐渐升级换代,而许多程序员由于长期于工作,学习新知识的效率必然下降。其次自己干了几年,薪水要求自然就高了,而年轻程序员工资又低、干活又快,当然会成为老板的首选;第三,30岁基本都已经成家了,要支撑家庭的生活负担,你几乎连从头在来的勇气都不会有了。中国的老话说“长江后浪催前浪、一代新人换旧人”,这个历史的规律在软件开发行业体现的尤其明显和残酷,很多程序员必然要面对的结果就是降薪乃至失业。

  困境之三:自我的实力。我们都知道人最难战胜的是自己,所以自我也就是程序员需要超越的最大障碍。大多程序员都把系统分析员和项目经理作为自己的职业目标,但这些目标的达成,需要个人素质、市场机遇等多个方面的条件,太多的程序员就是在高不成、低不就的状态中蹉跎了岁月。对于系统分析员,特别需要以下几方面的素质:客户需求分析能力、系统架构与设计能力、模块分解设计能力、项目流程控制能力、项目风险评估能力等,而对于项目经理则更注重项目管理方面的能力如团队组织能力、沟通协调能力、分析问题解决问题的能力以及良好的职业道德等,而这些素质和能力往往只能依靠程序员个人的学习和努力。看到越来越多的程序员开始学习项目管理的课程,真的有点为他们担心,因为现在的项目管理培训只能停留在理论和考证的程度,既没有素质方面的训练,又缺少实际软件开发项目的案例,学习的结果远远不能达到预期的效果。
posted @ 2005-12-06 18:37 ronghao 阅读(503) | 评论 (1)编辑 收藏

        一位优秀的商人杰克,有一天告诉他的儿子
  杰克:我已经决定好了一个女孩子,我要你娶她
  儿子:我自己要娶的新娘我自己会决定
  杰克:但我说的这女孩可是比尔·盖茨的女儿喔
  儿子:哇!那这样的话……


  在一个聚会中,杰克走向比尔·盖茨
  杰克:我来帮你女儿介绍个好丈夫
  比尔:我女儿还没想嫁人呢
  杰克:但我说的这年轻人可是世界银行的副总裁喔
  比尔:哇!那这样的话……


  接着,杰克去见世界银行总裁
  杰克:我想介绍一位年轻人来当贵行的副总裁
  总裁:我们已经有很多位副总裁,够多了
  杰克:但我说的这年轻人可是比尔·盖茨的女婿喔
  总裁:哇!那这样的话……


  最后,杰克的儿子娶了比尔-盖茨的女儿,又当上世界银行的副总裁


  知道吗,生意通常都是这样谈成的

posted @ 2005-11-29 13:22 ronghao 阅读(525) | 评论 (0)编辑 收藏
jbpm在用户角色管理上共设计了四个类:Entity、 Membership、 Group、 User
Entity类是其他三个类的父类,它包含了两个属性:name(String)、 permissions(Set)
User类继承Entity类,包含三个属性:password(String)、 email(String)、 memberships(Set)
Group类继承Entity类,包含四个属性: type(String) 、parent(Group)、 children(Set)、 memberships(Set)
Membership类继承Entity类,包含三个属性:role(String)、 user(User)、 group(Group)
很明显,一个user对应一个用户,一个group对应一个用户组,它们之间通过membership关联,并且一个user可以属于多个不同类型(type)的group,user和 group之间是多对多的关系。
Membership类的role属性个人感觉用途不大,反倒是name属性代表了user在group里的role(角色)!
posted @ 2005-11-16 18:00 ronghao 阅读(1209) | 评论 (0)编辑 收藏
<2005年11月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

关注工作流和企业业务流程改进。现就职于ThoughtWorks。新浪微博:http://weibo.com/ronghao100

常用链接

留言簿(38)

随笔分类

随笔档案

文章分类

文章档案

常去的网站

搜索

  •  

最新评论

阅读排行榜

评论排行榜