2007年9月6日星期四

mutable的使用方法

在C++中,很多关键字是我们平常不注意的。今天要提到的一个就是mutable。

mutable是个变量修饰符。它的作用是让这个变量在const成员函数中可以被修改。那const成员函数的语义本来就是防止成员变量被修改,为什么还要允许mutable来破坏这样的语义呢?

事实上,很多情况下面const是作为一个getter函数在使用。对于一个getter函数来说,他只要简单的返回要求的值就可以了。但是当这个值在已经被计算过,被赋过值的情况下,这个说法是成立的。但是在很多程序中,为了加快速度,对于不用的成员变量都采用了推迟计算。如果所有的值都放在类型初始化的时候,会大大减缓速度。这就是为什么很多设计者都会为一个类提供init()方法而不是把所有的初始化工作都放在构造函数里面的原因。

言归正传,有时候需要在getter函数里面在返回值之前先进行计算,这就要改变const函数内的内容了。这种时候,有些人就会这样写:

int get_value() const{
if( !isInited ){
Type* const pThis = const_cast(this);
pThis->value_ = 123;
}
return value_
}
千万不要这样用,这是“犯罪”。这种做法完全破坏了const成员函数的语义。
正确的做法就是使用motable。

class Type{
public:
//...
int get_value() const{
if( !isInited ){
Type* const pThis = const_cast(this);
pThis->value_ = 123;
}
return value_
}

private:
//...
mutable int value_;
};

这样的设计符合“把逻辑上,设计上来说应该是const的函数声明为const函数”

2007年8月2日星期四

马尼拉项目回忆录(一)

马尼拉项目终于结束了,一个花费了我们小组整整一年半时间的项目终于圆满了。不带一丝遗憾的结束,不带一个残留FT的结束。

有位同事第一时间给我道贺说:“听到你们项目结束比听到北京申奥成功还感动!”,这让我着实深深愧疚了一把,因为我自己都没有这么感动呢。不过想想也对,我身在其中一年半多,这个项目都成了我生活的一部分了(嗯,应该还是挺大的一部分),最后结束也象是水到渠成,非常自然,反而就没这么激动了。

回想这一年半来,我恐怕是这个项目中感触最多的人了。从这个项目中,我学到了很多东西,除了技术本身,还有项目管理,成本核算等等。再加上同时在交大读工程硕士,像很多武侠小说中印证功夫一样,我也把书上的东西和现实中的运用一一印证,的确受益颇多啊。

想写篇小小的回忆录,却一时间都不知道从那里说起。

还是从最开始说起吧,这恐怕是我第一次把一个项目这么完整的写下来。

项目开始

大约在2005年11月份,部门经理Jay来找我,“Hey,凌浩,有没有兴趣做manila项目啊,这是个很不错的项目哦~”。“好啊!”。这么一句简短的话,我就开始进入这个项目。同时公司的一纸任命书也下来了。WPM-Working Package Manager,一个我至今还搞不清楚做什么的职务。由于当时我们的RIS(相当于系统架构师)不在,因此我也代理了他的工作。PM也是一个刚刚来公司的新人,对AFC(自动售检票)系统没有任何了解。而我也就多看了几个月的代码,稍微在其他项目为某个package改改FT(我们对bug的称呼),也是相当缺乏经验。同时加入的还有一位同济大学的实习生。于是三个新丁就开始了一个真正的AFC项目。

公司这么做的原因恐怕首先是认为这个项目相比其他项目是小项目,而且又有所有的源代码,风险已经降到最低了。所以才放心大胆的放到我们手里。其实恐怕也是没的选,当时公司人太少。

但是事实证明,这个项目比其他很多大项目都难缠。有一段时间弄得我精疲力尽。这个在后面细说。

平台迁移

我们首先遇到了一个难题,就是源代码的升级工作。原来的系统是dos6.22+win3.1+win nt4.0 + access 2.0 + sql server 6.5 的系统。我们现在要全面升级到windows 2000 , xp,2003 server + sql server 2005的平台。我们有两个选择:一是看懂源代码,然后抛弃掉,自己重写一份;二保持原来代码,一点一点在上面改。基于公司一贯的保守政策,我们采取了后者。事实证明,这样做真的把我们害苦了。

如果我们当时做些成本估算,绝对是前者成本低很多。特别是到维护期间。一开始的选择错误,会在项目后期慢慢显现出其巨大的危害。到那个时候,维护的成本呈几何级上升。

我们首先花了将近两个月时间学习原来的业务逻辑和研究代码难点。我们发现进程间通讯协议是DDE,这个极其古老的进程间数据交换方式。我提出了使用COM,被无情的拒绝.......

最复杂的通讯协议

最复杂的还是和设备的通讯,使用的是一种20年前写的协议--ADLC。这是项目中最难的一个难点。因为原先有一块硬件电路板,用来负责和设备的通讯。但是现在这种板子已经完全停产了。没办法,只能用软件代替。幸运的是我们已经有最底层通讯代码了。但是这些代码是用法文写的,就连变量名都是法文,几千行的代码,我过了一遍,愣是一点没看懂。

这时候RIS从曼谷回来了,他开始进入这个项目,他首先负责设计了ADLC协议应用层的程序框架,由那位同济的实习生来负责编写具体代码。在整个项目中,这块代码是改的最多的,前前后后基本上改了一年多才终于稳定下来。

我和这位RIS的家挺近的,我们都买了一辆自行车,每天一起下班,锻炼身体+聊天,真是很开心的一段日子。

经过一段时间,我们开始有点上手了,代码也改的快多了,但是我们慢慢发现,这些代码似乎是未完成品,很多地方有明显的缺陷。代码错误随处可见。那时候可真把我们郁闷坏了,不仅要看逻辑,还要看很多莫名其妙的代码错误。

在闷头苦干中,我们终于迎来了2006年新年。我那时候对这个项目充满了干劲,而且觉得这个项目绝对能完美的成功。因为升级工作出奇的顺利,很多问题基本上不过夜。

集成的教训

新年后回来上班,我们终于迎来了第一个集成。设备模拟器和车站系统的集成。这个集成是个大失败,几乎什么功能都跑不起来。首次失败让我的心情有点沮丧,不过后来仔细考虑了一下。当时做集成显然时机未到,unittest都没有做好,就开始做连调,实际上所暴露出来的问题都是应该在unittest的时候解决的。因此PM开会,分发任务,各人又回去完成自己未完成的事情。我认为,即使release时间来不及,也要做好unittest后再integrated test。UnitTest在软件开发过程中绝对重要。那时候,我也开始了解到CppUnit的威力。但当时想在公司推广被拒,一气之下再也没有提过。连自己都不用了,整整过了一年才重新拾起。

极尽重用之能事

这时候又一位同济的实习生加入了我们,负责线路级系统的开发,主要负责各个机器之间的通讯。原先的系统使用的是NamedPipe和拨号联机。现在显然不可能再用拨号方式,于是又一次展开讨论--在车站和线路之间用什么通讯方式比较好。由于公司一直相当抵触Socket(公司宁愿用shared folder+ copy file这种一点也不可靠的低级方式也不愿意用Socket让我始终觉得相当奇怪),最后还是采用了NamedPipe,因为有现成的代码可以重用。省钱啊~~PM和RIS的算盘打的就是比我好!果然技术人员和管理人员看到的就是不一样!想想在新技术层出不穷的年代,我们还使用如此古老的技术,真让人汗颜啊~。最终的代码就是NamedPipe上面包一层DDE通讯,呼,这种代码我看了就想吐。多线程资源共享和互斥,数据传输机制过于繁琐等等问题至今还存在其中,但我也懒得管了。这些问题就如跗骨之蛀,除非整个机制重写,否则再改也没有用。

我们的系统见证了windows的变迁

2006年3月28号,系统Internal Release,我至今还记得这一天。我记得那天终于我们有了真正的测试环。一台windows server 2003, 两台windows xp pro,一台windows 2000 pro,一台dos7.22 + windows 3.11,我们的软件真是见证了几个windows 系列几个时代的变迁啊。

不过测试刚开始就遇到许多设置问题,让新来的测试工程师大为恼火。事实上我作为开发人员也希望能够提供简单,方便的软件,不需要过多复杂的设置就可以运作 良好。可是这毕竟不是通用软件,专门软件就只有一份,在这点资金下面,我们没有更多的余力(也不想做)来完成那些额外的功能了。

在回家路上感叹manila项目设置困难,结果同事笑着告诉我,bangkok项目的安装配置才叫地狱呢。仅仅安装手册(不包括使用手册)就有400多 页,那里的测试系统有我们的人在的时候还能听使唤,我们的人一走,那里没几天就完蛋了。超级复杂的安装,配置系统曾经差点逼疯一个工程师。我听得瞠目结 舌。

提供傻瓜式的安装程序

我一直感叹我们公司项目的安装实在过于复杂,我终于下定决心要做一个安装文件。我选用的辅助工具是Inno Setup。这是个得过多次大奖的安装工具。制作安装程序相当简单,还提供一定的脚本编程能力。最后我们的程序基本上只要next, next就可以顺利安装上目标机器,并自动完成大多数设置。这个工具在后来的北京项目中也开始使用。我相信,大多数客户还是会喜欢这样的方式。至少,我们的客户看到这个安装程序时大为满意。

第一次正式Release是马拉松的开始

3月的最后一天,也是个值得纪念的里程碑。一个振奋人心的好消息从法国RIS那里传来,我们发给法国的第一个版本就能和真实设备连通了。几个多月的努力没有白费。这个项目终于出现了一曙光。

线路系统也已经external release出去。接下来就是新的一轮开发了。第一次迭代开发也快接近尾声。主要剩下的就是联调,微调。让我们的程序更健壮。这个阶段往往会发现很多原先注重功能开发阶段不注意的程序错误。而一些深层次的程序问题也暴露出来。这个阶段持续了很长一段时间。不停的向外面发Release,不停的测试,不停的修改。

签证风波

5月份,我要到法国去和真正的设备集成测试了。我在4月份办签证整整用了一个月。公司一向让我们办Training,但是没有人告诉我,于是我傻乎乎地对签证官说我去工作。因为我知道对签证官要诚实。于是签证官要求我叫法国那边警署出一个劳动证明,同时法国Thales还要出我地用工证明。

结果这两个东西法国那边都办不出来,护照又被扣在了大使馆,这里公司找了好多人,托了好多关系,甚至让法国地大老板打电话过来,还是不行。没办法,公司不如Alcatel有名啊,听说他们申请签证可以走特殊通道,很容易申请到。

最后我没辙了,准备好了另外一封资料,在去领退回来地签证时候,同时递交另外一份申请。因为我的飞机就在几天后。时间已经不允许我再预约一次了。但可恶的法国签证官一眼看出了我的企图,坚决的回绝了我。在我万般无奈的时候,里面的中国签证官出面让这个法国签证官放过了我,似乎她官大一级,法国签证官也无话可说。其实从这里就可以发现,法国人的等级观念很严重的。上级说的话,下级几乎没有反驳的余地。可惜我在最近才真正体会到这一点,可以看《对不起,我逾越了》。

终于,我还是拿到了签证,和另外一个同事一起,坐上了开往巴黎的飞机。说来惭愧,这还是我这个乡巴佬第一次做飞机。样样透着新奇,就像刘姥姥进了大观园一样。接下来,我就要开始一个人在巴黎度过一个月时间了。

2007年8月1日星期三

删不掉的目录

今天学到了一招,觉得非常有用。那就是--“Windows上删不掉的目录”。

首先看看我这里的一个目录:c:\tomcat.6.0.14. 嘿嘿,看起来很正常是不是?我们来打开看看。当我双击它,想打开看个究竟时立刻出现了一个错误“c:\tomcat6.0.14.引用了一个不可用的位置......”


当你想删除它时,windows提示无法删除文件!
够布尔比的目录了。看又不能看,删又不能删。那这个文件夹到底怎么产生的呢?真相只有一个,那就是--这个文件夹不叫这个名字。

开始->运行里面打开cmd,在c:\根目录下打
c:\rmdir tomcat.6.0.14..\

你就会发现这个目录被轻易的删除了。原来这个目录真正的名字是tomcat.6.0.14..\ 注意最后的.\ 这个在windows里面被隐藏起来了。做GUI的程序员没有把这部分显示出来,因此在资源管理器里面看出来的就是tomcat.6.0.14. 因为前面有这么多点了,最后一个点大家也不会特别注意,因此就起到了掩人耳目的作用。

创建这类目录也很容易,在命令行里面打
c:\mkdir tomcat.6.0.14..\
那么看到这里,聪明人就知道怎么利用这个漏洞了,在里面放点不想让别人看到的图片啊之类的东西(嘿嘿,某人很猥琐的笑笑),或者放点木马,病毒之类的,统统没法被杀毒软件查出来。这个目录还无法显示大小,一般人根本不会注意到自己的硬盘上被划分出去这样一块空间。其实最先找到这个bug的还是黑客们,他们通过这个在目标机器上放程序或者在肉鸡上面创建这样的目录来存放中间文件。

不过奉劝大家一声,看过了就算了,不要做坏事情啊,要遭雷劈的!(假如做坏事,也不要放在C盘根目录下面这么明显的地方)

2007年7月26日星期四

ATL8的COM服务器实现(一)

在现阶段,ATL仍然是实现COM组件的一个重要手段。在几年的发展中,ATL出现了两个重要的版本-ATL3和ATL8。虽然ATL8已经出来好几年,但是由于我一直重用着以前用ATL3开发的老代码,因此对ATL8没有什么太多的研究。这次借着新项目的机会,能够正式使用到Visual Studio 8进行开发,我也因此得以好好的研究了一下ATL8的实现。虽然现在是.net大行其道的时代,但我这个守旧的人还是希望能记录下这个快要被遗忘的技术的研究心得。


COM服务器的种类

Win32版本的COM有两种服务器

  • inporc server --进程中服务器(动态链接库,DLL等形式)
  • out-of-proc server --进程外服务器(exe程序,进程外是对客户进程而言)
其中out-of-proc server有几个特例,一个是DCOM,可以叫做Remote Server,一种是系统服务,也就是service。

对于InProc-Server,我没有过多的研究其在ATL8中的实现,毕竟相当简单。这里主要对out-of-proc Server的实现进行研究。研究对象也是一般的EXE程序,而不是service。

COM服务器的职责

COM服务器有三个基本职责
  • 注册和反注册服务器
  • 暴露类厂(实现IClassFactory),使SCM能够访问
  • 服务器生命周期管理
进程外服务器简述

进程外服务器就是一个exe程序。如果在命令行上面指定 regserver或unregserver就能要求该服务器进行注册和反注册。注册成功就会在注册表中出现该服务器所包含的所有接口的ProgID和ClSID。ATL实现了这个简单的功能。

ATL同样是调用标准COM辅助函数CoRegisterClassObject把类厂和每个类的IUnknown暴露给SCM,这样客户就可以通过SCM来使用组件提供的接口。在Server结束生命周期之前,调用
CoRevokeClassObject把组件从SCM中注销。

对于生命周期管理,由于是exe程序,所以它可以自己管理自己的生命周期。当服务器检测到已经没有任何客户在引用它内部对象时,它可以选择是否退出运行(ATL中默认是exe退出运行)。


服务器与COM对象

一个服务器在程序中是什么呢?事实上没有具体的对象可以代表它。ATL使用OO编程,因此为服务器定义了一个类,其最高层父类是CAtlModule。它是个全局变量,因此它有着和exe程序本身相同的生命周期,因此由他管理COM对象再合理不过。

一个COM对象可以通过CoCreateInstance创建。它有自己的类厂,该类厂实现IClassFactory接口。
注:有些对象是不能被CoCreateInstance创建的,只能通过其他对象间接的创建。

那么服务器如何与COM对象打交道呢?
ATL从一开始的版本就提供了一个叫Object Map的结构来存放服务器内COM对象的信息。CAtlModule(简单起见,就把它当作服务器吧,这样容易理解一点)利用Object Map来做要求每个类自己注册和反注册,创建类厂,提供一些辅助函数来进行初始化和结束前的工作。

下一章 详细介绍Object Map




2007年7月22日星期日

BENQ海湾键盘,让我对你说爱还是恨

这已经是我第二个海湾键盘了。黑色的键盘,非常酷,不宽,占地小,有质感。最重要的是这个键盘的手感很好,很像ThinkPad T系列键盘的手感。打上去戚戚嚓嚓,有点像以前机械键盘的感觉,但没有那么坚硬,反而很有弹性。让我在两年前第一次看到这款键盘的时候就毫不犹豫地在新蛋上买了下来。

有了这个键盘,我在打字的时候常常停下来感叹一番,国产的键盘也不错阿,谁说国产的东西不行的。

但是没想到这款键盘还是个扶不起的阿斗,一年也没有到,这个键盘边缘的键位上的字已经磨光了,然后开始成皮成批的键位失灵。那时候我有点气愤,想想以前的无名键盘用了3年都没有什么问题。这个100多块的键盘竟然才用了1年就报废了。

我今年年初拿着键盘去太平洋三期准备保修的,结果找不到发票了,本来想去磨磨看的,结果鬼使神差得又买了一个一模一样的海湾键盘回来。当戚戚嚓嚓的声音再次想起,熟悉的感觉又回来了。我心说,算了,希望这个好些。上次那个估计没有保养好。

可是恼人的事情又来了,这次的键盘质量更差,才6个月不到回车键就失灵了。这样的质量,让我太失望了。

我曾经一度希望支持BENQ这样的国产品牌,我买BENQ的光驱,买BENQ的鼠标(公司里面),买BENQ的键盘,换来的结果就是没有一样东西能用超过1年的。一个还算知名的品牌,就连一个键盘,一个鼠标都做不好,还谈什么振兴国货!!

本来想直接去买罗技的键盘的,不过BENQ海湾键盘两年包换,我昨天带了键盘去换,没有发票,没有保修卡,人家也收下了,说检修完之后等到键盘来了就通知我。恩,售后还算不错,但我希望给我的不是返修货。。。。。。

我决定,这次拿到键盘就再用一阵子,如果再坏了,我不换了,直接去买罗技的。

2007年7月19日星期四

manila项目终于结束了

昨天,收到了马尼拉项目的项目经理发来的祝贺信,说客户终于签字接收了。由此,项目结束,开始进入一年的维护期。

这是我在Thales第一个完整的项目,历时一年半,经历了数不清的困难,现在收到一封简简单单的祝贺信,却让我由衷的高兴。

回想这一年多来的经历,也不是那么平淡的。自己眼界在开阔,经验在增加。

简单的写下自己一路走来的历程:

  • 一开始拿到手像狗屎一样的代码。
  • 开始在原来代码上改造,不过狗屎就是狗屎,永远不会变成美味的狗腿。
  • 到法国总部做第一次集成测试。天天品尝法国美食。
  • 第一次看到上位机程序和设备(下位机)通讯。
  • 第一次对整个AFC系统有了整体,清晰的理解
  • 步行游巴黎,里昂看老友。
  • 去签证署办延期,整整站了一天,深刻体会到法国公务员工作效率。
  • 在上海用模拟器痛苦的调试程序,体会和真实设备的天壤之别
  • 第一次去马尼拉部署,发现菲律宾是个贫富差距极其严重的国家。
  • 连续工作两个通宵49小时,没有空调,没有凳子,只有白花花跳动的Trace和那颗兴奋的心。
  • 看着自己的成果让几十万人用,激动的心情不言而喻。
  • 每天早上在5星级宾馆狂吃美味的早餐,省掉午餐,晚上和法国人在Makati一家一家餐馆吃过去
  • 回到家里发现自己重了整整5斤。
  • 第二次去马尼拉部署,开始学会坐轻轨,喝gulaman,尝试了解菲律宾普通人的生活。
  • 第三次去马尼拉,硬啃用法文写的代码,花了两个礼拜终于解决了困扰我们半年的通讯协议问题。
  • 在小年夜匆匆赶回上海。
  • 第四次去马尼拉,不停得到处救火,发现法国人的软件水平比较差,菲律宾人实在是懒。
  • 法国人卖给菲律宾人的烂键盘一年不到已经按下去键弹不起来了,鼠标被真正的老鼠咬坏。
  • 感叹没有知识,没有技术就要被别人欺负。
  • 第五次去马尼拉,被领事馆的签证官警告,如果再办旅游签证,将把我和我们公司放进黑名单。
  • PM换成了拽的没边法国小伙子。
  • 发现自己不过是个小巴拉子,法国的PM宁愿相信那个新来的PM也不愿意相信我这个全程跟进的工程师。法国公司和国企一样,等级森严啊。
  • 按照不合理的安排干了两个通宵。
  • 回来改掉最后几个Bug,发现法国人的设备全是毛病。
  • 然后---结束!
一个项目结束了,但别的项目在开始,在继续。生活也在继续。过了浪尖,又是平淡。

2007年6月19日星期二

对不起,我逾越了

今天早上法国的实习PM气冲冲地跑过来,指着我鼻子就问“你有什么权利发昨天晚上的那封信?”。

事实上在他来之前我就知道要糟。事情是这样的,我们在马尼拉(菲律宾首都)有个地铁项目,明天我就要去Onsite了,这是最后一次Onsite,任务是升级新版本软件,并且解决一切问题,如果顺利的话,这个项目就可以结束了。

这个项目上到法国的项目经理,RIS(类似于系统架构师的职位),下到我们这边的项目经理,开发人员,测试人员,只有我有过5次Onsite经验。也就是说我是最熟悉这个项目的。因此以前的项目经理给了我一些小小的权利,可以直接和马尼拉那边的工程师联络,讨论一些任务安排之类的事情,也可以通过他先非正式的和客户谈谈,看看客户的想法。我承认这个做法比较随意,但是带来的效果却很好,就像XP中提倡的开发人员和用户直接面对面一样。因此我一直这样做,并没有受到什么指责。

而昨天,这位实习PM发给我一个Schedule,告诉我明天到菲律宾后每天干什么,非常简单的一个计划,一共就7行字。但是他并没有通知其他人,因此我就“自作主张”地把这个计划发给了马尼拉的工程师,由于他是直接和我工作的人,因此我需要和他讨论详细的计划,比如哪个小时做什么事情,遇到情况怎么恢复等等。因此我在原Schedule上面增加了非常详细的步骤,甚至详细到一个report有多少大,需要多少U盘才能放得下的地步。其中最重要的一点,也就是我和这位实习PM计划中有所不同的一点是,在升级过程中,我会帮助客户每天都拿到报表;而他的计划中是把报表一直存放在车站计算机上面直到3天后才能让客户看到。但这一点我没有理解清楚,他的计划中也没有提到这一点。我一直以为我上次告诉他,我们的客户要求每天都能看到报表之后他已经同意我的方案。现在看来他根本没有考虑我的提议,也没有把这件事情告诉法国那边的PM和RIS。而最糟糕的是,直到今天,我们的客户对我什么时候去,我们怎么升级系统还根本一无所知。基于这几点,我提议马尼拉的工程师将此计划非正式的给客户看一下,看看他们的意见。

结果这份信不知道怎么就转到了法国PM手里,又转到了RIS的手里,他们很明显认为我挑战了他们的权威,因此回信把我的计划驳回,同时CC给了这位PM和我们的CTO(两个都是法国人)。

于是就发生了早上的那一幕。这位实习PM怒气冲冲地对我吼:“我才是这个项目的负责人,你不是!” 这句话把我气得不行,至今为止,和我合作过得PM各个都是和和气气的有事大家一起商量。从来没有指着对方鼻子吼的。就算以前在法国,那里的PM再怎么喜欢阴人,表面上还是很和气的。

然后我木然地看着他,他还不过瘾,又加了几句,“你只是一个工程师,你没有权力直接和客户联络,你只要照着我说的做就可以了,在计划的安排上,你没有任何自由!联络客户是我的指责,不用你来代劳!”。

我没有反驳他,但我还是问了一下他是否已经把这个计划交给客户评审,同时如果他们要每天都看到Report怎么办。他随口回答了我一句:“我会和法国的PM讨论。”

我知道整件事我的确错了,我的确逾越了我的权限。因为他没有给我这个权限。另外一个错误就是和这位实习PM沟通太少,毕竟刚刚开始合作,双方的摩擦是少不了的。同时也显示出我太年轻,对这种事情的处理还不够老练。

但我觉得我是下意识地不想和这个人沟通,因为他除了有事找我,平时走过路过连正眼都不会看我一眼。我有几次主动和他打招呼,他睬都不睬我。这位实习PM显然是一个对级别,职位看得很重的人。我显然和他的职位相差太多。对付这种人,我想老祖宗的“韬光养晦”是一个不错的办法。

在写这篇文章之前,我心里还有气,因此我觉得到了Site上,绝对不主动做任何不在我范围内的事情。如果客户不知道计划,我就不开工。

但现在我的想法又变了,我没必要因为这件小小的事情改变自己,我坚信我自己没有做错,我会积极的和这位PM沟通,把这最后一次Onsite做好。这才是双赢的局面。这也是我在这个公司第一个能够Close的项目,我不希望在我手里搞砸。

2007年5月25日星期五

Java RMI 极速攻略

回家作业上要用到RMI,这两天抽了点时间学习了一下,感觉上手还算简单,但有些设置之类的时间长了怕记不住,因此花点时间写了这个入门攻略。

---------------------------------

RMI简介

RMI就是Remote Method Invocation,类似与RPC。 它的原理同CORBA, DCOM,.net remoting相似,但是比他们要简单很多。详细的介绍可以看参考资料部分的[SUN公司的RMI教程]http://java.sun.com/developer/onlineTraining/rmi/RMI.html

实做

下面分几步来完成一个简单例子----login(这个功能用的快烂了)

想法是,server端程序提供接口方法login,接收两个参数,用户名和密码,为了简单起见,我们直接返回true。在client端调用server端的方法,看看是否能够顺利返回true。

该程序没有任何意义,仅仅用于演示RMI的开发过程。

1. 定义远程接口

远程接口如下:(ICustomer.java)

import java.rmi.Remote;
import java.rmi.RemoteException;
//
// This is remote interface for Customer
public interface ICustomer extends Remote{

public boolean login(String username, String password)
throws RemoteException;
}

2. 实现远程接口

实现文件是真正的业务逻辑所在。和RMI基本上没有任何关系。

要注意的是

  • 类要继承java.rmi.UnicastRemoteObject 并且实现刚才定义的接口。
  • 每个方法都要抛RemoteException
  • 要有显示的构造函数

实现文件如下: (ICustomerImpl.java)

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;


public class ICustomerImpl extends UnicastRemoteObject
implements ICustomer{

public ICustomerImpl() throws RemoteException{
}
public boolean login(String username, String password )
throws RemoteException{
return true;

}
}

3. 服务器端启动程序

服务器端启动程序做两件事

  • 加载服务器端对象到JVM中
  • 将该对象注册到RMI Registry

在这个例子里面,我使用的是一般的控制台程序,据说可以用Tomcat代替,我想这个应该是可行的。毕竟,只要有个JVM能够加载对象,就能把对象注册到RMI Registry里面去。

代码如下(Server.java):

import java.rmi.Naming;

public class Server.java{
public static void main(String[] arg){
try{
ICustomerImpl cu = new ICustomerImpl();
//创建服务器端对象
Naming.rebind("cust", cu);
//注册
}catch(Exception e){
System.err.println("launcher error");
e.printStackTrace();
}
}
}

4. 编译服务器端程序

先假设所有的代码都放在d:\rmitest\下面

先到该目录下,编译所有的代码

javac Server.java

接下来,肉戏来了,需要用rmic来编译实现文件产生存根(stub),如果对这个词不是很了解,请参考一些跨进程访问的资料,一般的解决方案都是用存根/代理实现。

rmic -keep ICustomerImpl

在 运行rmic之前一定要用javac编过,rmic是找编译好的类的,不是找源程序的。rmic的工作原理是,首先反编译 ICustomerImpl.class,加入rmi架构需要的代码,然后把这个中间过程产生的Java文件编译成 ICustomerImpl_stub.class

-keep 就是让它保留中间产生的java文件。

这样基本上服务器端的事情都做完了。

客户端做这几件事情:

  1. 查找服务器端接口,要指定ip地址,端口号,接口注册名
  2. 然后可以是用接口的方法
import java.rmi.Naming;
import java.rmi.RemoteException;

public class Client{
public static void main(String[] args){
try{
ICustomer p =
(ICustomer)Naming.lookup("rmi://localhost/cust");
System.out.println(p.login("linghao","1234"));
}catch(RemoteException re){
System.out.println(re);
}catch(Exception e){
System.out.println(e);
}
}
}

客户端实际上是通过stub类来和服务器端通讯的,因此,运行的时候需要把接口文件,stub文件和客户文件放一块儿。

6. 安全策略

跨进程,跨机器访问一个不能避免的问题就是权限问题。

RMI中,是用java的访问权限管理 有两种方法来设置权限

1. 建立一个新文件test.policy,放在和server端代码一起,文件内容为

grant codeBase "file:/d:/rmitest/"
{
permission java.security.AllPermission;
};

或者在命令行中运行policytool,选"添加规则项目",在后面出来的那个对话框中,codebase填 file:/d:/rmitest/ ,然后选择"添加权限",许可中设置"All Permission".然后一路ok返回。然后选文件->另存为,取名字为test.policy放到server端程序的同一目录。

7. server端启动

首先要启动RMI注册器,这时候会出现一个空白的dos窗口,不要关掉它,否则注册服务器就没有了。

start rmiregistry

其次启动服务器端程序

java -Djava.rmi.server.codebase=file:/d:/rmitest/
-Djava.security.policy=test.policy Server

如果出现access permission exception的话,说明可能权限文件没设好,打开来看看。

注意,服务器端的两个程序都是不会返回的,每次你都要开个新窗口

8. Client端启动

注意ICustomer,ICustomerImpl_Stub.class 和 Client.class要放在一起

java Client运行后,如果一步一步照着做的话,应该能看到返回值为true;

9. 小结

本文是对RMI开发的一个极其简单,极其粗浅的介绍。很多系统的知识都没有写出来,目的就为了大家刚接触的时候上手快一些。

很多介绍也是某一种可选的方案(为了快速学习而选择的方案)罢了,专业的代码需要考虑更多的内容,比如下面列出的一些就需要考虑:

  • 服务器端程序不一定要继承java.rmi.UnicastRemoteObject, 实现更灵活
  • 端口号如果不写就是用默认的1099,但是也可以自己指定
  • 服务器端的安全设置
  • 远程调用的开销很大,是否要考虑一些模式如Gateway, DTO之类的。
  • 服务器端的宿主容器选择

更深层次的介绍还是请查看参考资料里面列的链接。

参考资料

事实上,大多数的书都很厚,如果像快速的学习RMI的用法就不要看那些书。看完了,项目也结束了。

一些有用的Link

[sun公司的RMI教程]http://java.sun.com/developer/onlineTraining/rmi/RMI.html

[sun公司的RMI培训资料]http://java.sun.com/docs/books/tutorial/rmi/running.html

2007年5月15日星期二

windows server 2003 中DCOM 权限设置

Windows2003加强了DCOM的权限管理,其设置和window2000中稍有不同。

首先打开 注册表,在HKLM\Software\Microsoft\Ole下面加上两个DWORD类型的键
ActivationFailureLoggingLevel (1)
CallFailureLoggingLevel(1)
这两个键可以把失败的登录尝试记录下来。比如你用xxx尝试登录,结果得到800706ba的错误

然后运行DCOMCNFG,在My Computer上面右键,然后选择Default Property标签,Default Authentication Level 选择None,Default Impersonation Level选择Impersonate.
这里很奇怪,就算选择别的选项,关掉再打开,又会回到这两个选项。

然后选择 COM Security标签,在Access Permission , Launch and Run Permission 里面把刚才失败的用户名加上,并赋予 Remote Access, Remote Launch 的权限。

然后最重要的,在防火墙里面把RPC服务的端口135打开。打开端口似乎需要重启计算机,否则用netstat -a -b 查看一下是否真的打开。

当然,这个方法只能运用于信任度高的局域网内,否则就危险了。

2007年5月14日星期一

NB的签名

刚才逛POPGO,看到一个签名,绝了!

看动画,要求别太高
没有新作,遗作也可
没有佳作,臭作也可
没有神作,鬼作也可

2007年5月4日星期五

《大剑》Claymore


这次的四月新番中最吸引我的莫过于《Claymore》(译为大剑或猎魔战记)这部动画了。

它的背景设定和《Berserk》(剑风传奇)有些类似,都是在一个妖魔当道的世界,为了与妖怪对抗,人类制作出了有着妖魔血统的半人半妖的女性战士。因为她们的金眼银瞳,她们也被人类所惧怕;因为她们总是背着一把双刃大剑,她们被人类称为Claymore。

她们在斩杀妖魔的过程中不断得释放自己的妖气,直到临界点。超过临界点,她们就会妖魔化,这时候就成为了比一般妖魔厉害得多的“觉醒者”。在她们还有一些理智的时候,她们会给自己最好的伙伴发“黑函”,让自己最好的伙伴把自己杀了,因为她们都希望做为人类而生,也做为人类而死。毕竟,她们原先是人类。

她们按照实力排名从No.1 到No.47。我们的主人公克莱雅就是实力最差的No.47。但她不是通过和妖魔合体被制造出来的,她是一个非常特殊的个体。她是和之前的No.1 微笑的特蕾莎的血肉结合而被制造出来的只拥有妖魔1/4血统的Claymore。
这部作品显然比其他的四月新番有更多的东西要表达。它并不是一部主人公的复仇记那么简单。Claymore们其实是最痛苦的一群人,组织认为她们只是被造出来对付妖魔的工具,而工具使用到一定次数就该扔掉,否则工具就会有了自己的思想,那是很危险的。因此组织一旦发现Claymore们的异常,就会把她们派到极其危险的任务中去,借刀杀人。Claymore们也不被人类所理解,人类其实就和惧怕妖魔一样惧怕她们。但是她们还是要挥舞大剑帮助人类。最后还要带着对这个世界的眷恋,流着泪,让自己的同伴杀死自己。她们是在这个世界的夹缝中生存着。

Claymore都是冷酷的战士,但是当她们和人类有了纽带之后,她们的命运将会完全被改变--或者是失去战士的资格而被杀,亦或有了更强大的动力,激发自己的潜能。前者的代表是特蕾莎,做为No.1的她可以不用释放妖力就轻易斩杀妖魔和对付从No.2到No.5的围剿,但是由于她和克蕾雅的接触,使得她那完美的做为战士的冷酷的心出现了裂缝,一念之仁导致被觉醒的No.2菲斯娜斩去了头颅。这也是克蕾雅加入组织追讨菲斯娜的原因。但历史总是相似的,克蕾雅遇到了少年拉基(垃圾还是Lucky?),勾起了自己以前的回忆,因此带着拉基一起旅行。

值得一提的就是这部动画的画风,这也是我一看就有了兴趣的原因。八木教广对我来说是个十分陌生的名字,但是他的画风却让人映象深刻。他笔下的少女都有过分纤细的身材,但看上去又十分的和谐。线条干净利落,将少女战士的柔美和坚强一齐表现了出来。尤其是人物的眼镜,嘴唇和头发,非常的有特点。

2007年5月2日星期三

heroes实在是太精彩了

这一集(1E20)中,HIRO来到了未来,见到了未来的HIRO。尽管上一集的时候我已经预料到这是一个黑暗的未来,但是没想到竟然会这么黑暗。

塞拉杀了内森和那个会变身的女人,结果变成了美国总统,愚弄民众长达5年。一个骗子能做到这个份上真是神乎其技了。超级白痴的马特助纣为虐,终于帮着塞拉获得了克莱尔的再生能力,又杀了未来的HIRO。克莱尔的fans们估计要哭了,编剧们为了将邪恶进行到底终于那她开刀了(还真是开刀呢)。妮其失去了自己的儿子迈伽和D.L.,成为了彼特的女友。伯纳特好像成了地下党,专门帮有特异功能的人逃脱塞拉的掌控。Mohinder变成了塞拉(总统)的御用教授,看来读书人就是好骗。

变化最大的还是要数彼特,似乎他已经完全掌握了自己靠近别人所学到的能力。连HIRO的时间静止都学会了。人也变得非常稳重成熟起来。(没办法,不变的酷一点,怎么能把大美女妮其骗到手呢?)除了塞拉他是唯一一个身怀数种技能的人了,不愧是塞拉的宿敌。不过塞拉最强的那招念力不知道他学会了没有?那简直是和JEDI武士一样的王道能力啊。如果他也会念力,再学习一下日本剑,穿个灰袍子,绝对是一个JEDI。

真是佩服编剧的天马行空的能力啊,不过本来这种科幻片玩的就是想象力。没有点special ability 怎么行。

片尾最后就是塞拉终于熬不住显出真面目和彼特对决。两个人都有超强的实力,一个是靠杀人吸收了无数的能力,一个是通过修炼,慢慢掌握自己靠近有能力的人所暂时学到的能力。

两人的对决谁会赢呢?不知道,也没有必要知道,无论如何,那个黑暗的时代是不会改变了。重要的是现在,不是吗?现在将会更难了!

2007年4月21日星期六

大勇和小花的欧洲游记

大勇和小花的欧洲游记 www.dayong.name

在网上逛的时候无意中遇到了这个网站,美涣美仑的Flash,悠扬美妙的音乐,再加上熟悉的画面引起了我极大的兴趣。

我很久没有这么仔细,这么悠闲的在网上阅读文章了。进入他们2005年的游记慢慢品读,发现细腻的文字出自小花之手,照片则出自大勇之手,两者配合的天衣无缝。耳边飘着法文音乐,思绪已经跟着他们的步伐一路畅游欧洲了,真是奇妙的感觉。

手机小知识

觉得有用,因此转贴过来:

  1. 大家都知道用手机拨长途,前面加拨17951可以获得优惠价。但是,我们都不知道,必须手动拨号才能享受,重拨键系统不识别!
  2. 要想查询手机中的信息预定,请拨“0000”到186201,系统就会回复一个短信,列明所有的定制信息。如果想退定,就拨“00000”到186201即可。
  3. 锂电池的手机请不要等电量彻底耗尽再充电,因为锂电池和原来的镍电池不同,没有记忆功能,而且完全耗尽反而会影响电池中成份的反应。
  4. 打电话时,如果信号很弱,只有一格,请尽量停止通话,因为这时候手机的辐射最强。

2007年4月9日星期一

Father and Daughter

小L正在打游戏,老L笑眯眯的走到小L旁边,“最近UML学的怎么样了?”
“嗯,还可以”小L应付了一声。
老L仍旧笑眯眯的说,“那就出道题目考考你吧”
于是老L随手在纸上画了一个简单的类图

“你看看这个关系对不对呢?”
小L抬头一看,哑然失笑,“那还用问,当然是正确的!”
“错!”老L眼神开始严厉起来。
“为什么?”小L关了游戏机,不服气的问道,“父亲和女儿不就是继承关系吗?”
老L没有回答小L的问题,指着图说道:“泛化关系就是is-a关系,那女儿是父亲吗?”
小L心里咯噔一下,他已经知道自己错在哪里了。
老L继续说下去,“这女儿啊,是永远不可能成为父亲的,就算以后结婚了,也是成为母亲。除了一种情况,她是个同性恋,她和另外一个女人结婚了,然后她让她孩子叫她‘爸爸’!”
“好吧,我承认我错了,但是他们到底是什么关系呢?”小L反问道。
“你再来看看这个图”,老L又拿出一张白纸,画了另一个图。“那这个图对不对呢?”老L又是笑眯眯的问。
“嗯……这个是拥有关系,父亲拥有女儿,应该是对的吧……”小L觉的有点头大了,自己在看书的时候觉得都看懂了,怎么遇到个这么简单的问题就开始犯迷糊了呢?
“我要首先纠正你的用语,用语规范才能学好东西。这个是聚合,表达的是has-a的关系……”,老L看了看小L,继续说道,“这个关系也是错的。”
“为什么?父亲拥有女儿,说起来虽然有点别扭,但应该还是对的吧。”
“是错的,因为聚合表达的是整体-部分关系,女儿是父亲的一部分吗?如果父亲被车撞死了,女儿要怎么办?女儿也得自杀。因为聚合有个同生共死的概念在里面。而且聚合常常这样画……”说着老L在画上加上了重数(multiplicity)小L一看也哑然失笑,的确这是错误的。
“事实上,聚合是不推荐使用得东西,在你初学得时候,尽量不要去用它。”
“好吧,再看看这个”老L又画了一个图。

“这是组合”小L有点想起来了,“我总是把实心的菱形和空心的菱形搞混,不过现在我记住了,这里是一个father拥有1到多个女儿,这个肯定是对的了。”
“让你失望了,这个还是错的!”老L说道,“虽然组合的逆重数常常是1,但是他归根结底和聚合是差不多的,都是表明has-a这个概念的。比如代码可以这样写……”

class Father {
Daughter d = new Daughter();
……


“从这个关系里面可以看到,女儿是做为父亲的一个属性,也就是特性存在的。而且最重要的,父亲知道女儿,但女儿知道父亲吗?不知道!所以这个是错的”

“你是对的”小L垂头丧气的说道,“那,到底他们是什么关系?”

“呵呵”,老L笑着说到,“看看这个图”

“这不就是一般的关联吗?”小L疑惑的问道。
“对,父女的关系就是一般的关联,另外还有两个role: be father of 和 be daughter of。他们的关系只能用关联来表示”。
“原来是这样,看来我还要自己得仔细得去思考UML的细节问题啊……”小L拿起几张图一一对照过去,若有所悟得说道。

2007年1月30日星期二

开张了

很久以前就从msn space搬到blogger了,但是一直没有时间装修这里,今天稍微弄了一下,准备开张营业了。