Perfil de 风静雨轩FotosBlogListasMás Herramientas Ayuda

风 田

静雨轩

~晓@风@清@月~or~晓@月@清@风~_____我心独处飞扬
26 septiembre

一个越战中国特种兵的故事

老蔫儿是个普通的中国男人,人过中年,头发花白。谈起老蔫儿认识他的人都语含同情,老蔫儿的日子过得很辛酸。为什么呢?老蔫儿这人太老实,中国人的温良恭谦让在他身上发挥到了极致,所以他也就有了这个绰号。老蔫儿蔫在何处?比如说单位分房子,他多少年前就该分到了,可他从不争取,名额也就让别人给抢了去。被抢了,他也不生气,笑说道:“人家比我更需要 吧!”


于是老蔫一家三口到现在还挤在鸽子窝似的单位宿舍里,每每想到此处,他的那些同事只能哀其不幸怒其不争,叹道:“老实人呐!”但老实人也曾有过一段被人羡慕的日子——他娶了个漂亮老婆,让所有认识他的男人都郁闷了把,“咋被老蔫给娶到了呢?”但令人扼腕而叹的是:倒霉鬼常是老实人,老蔫的老婆给老蔫生下个漂亮女儿没几年就出了车祸,唉,一向乐观的老蔫从此也就没了笑容。看到这,诸位可能就有了疑问:他家怎么是一家三口啊,难道老蔫又娶了?现在这社 会哪个女的愿意嫁给这么蔫的老实人呐!再说了,老蔫这孩子厚道着呢,再娶他压根没想过。


那第三口是谁啊?老蔫的姐夫。对于这个姐夫,老蔫也摸不着头脑,他就没听自己老婆提过有什么姐姐,可这姐夫来的那天,自己老婆就是叫他姐夫的,随后躲到房里哭得涕泗滂沱。老蔫的悲惨日子也就从那天开始的,他老婆在上街给姐夫买酒的路上出了车祸,本来 该是老蔫去买的,可两人抢着去的时候,老蔫老婆的眼泪禁不住又流了下来,老蔫心一软…


老蔫是个老实人,他总觉得自己配不上老婆,自己亏欠老婆太多,他也就时常在心中 懊悔:“那天我去该多好。”每每此时老蔫常蹲在自家门口前望着远方,在某一刻他的意 识或许回到了那天,潜意识里他应该渴求着奇迹的发生,但冰冷无情的事实花白了老蔫的头发。老蔫老婆临出门前交待的那句话:“照看好菲菲和姐夫。”便时常回响在老蔫耳边了。 老蔫也就不苟的执行着老婆的最后一句交待,以作为自己对老婆的补偿,希望在心中 寻得份安心和慰籍.


※※※※※一个中国真正特种兵的真实故事※※※※


老蔫的姐夫也就住了下来,照中国传统的说法,老蔫的姐夫对老蔫来说就是颗灾星, 要不是这所谓姐夫的来到,要不是这所谓的姐夫爱喝酒,要不是…老蔫的老婆也不会就这么去了。


但老蔫并没怨恨他姐夫,因为他是个老实人,而且他并不笨,他看得出来姐夫比自己还要伤心,再看自己妻子看见姐夫的反应,老蔫便猜出姐夫和妻子肯定有什么伤心事瞒着自己。伤心事加伤心事,换谁也不好受啊,所以老蔫一直怀疑姐夫精神上有问题,否则怎么会一天到晚没事就傻坐在那呢,一坐就是个十几年,害得家里一切开销都得*老蔫那点工资,日子过得倍苦。


日子就这样熬了过来,菲菲也快高中毕业了,出落得比她妈还漂亮,打小想对她动歪主意的坏胚就不少,但让老蔫欣慰的是精神失常的姐夫派上了用场,菲菲上学一直就是姐夫接送的,只要菲菲尖叫一声,壮实的姐夫就跟影子一样刷得声出现,学校的老师看菲菲都是一脸的敬畏,都以为是哪位大老板的女儿。24小时有专人保护。


有时老蔫常常疑惑:姐夫精神未失常前到底是干什么的?咋就跟中南海保镖一样呢!


但最近老蔫也郁闷了起来,厂里决定下岗一批工人,第一个就是好处从不想苦活就他 干的老蔫,这样一来,菲菲的大学是肯定上不起了,再过几星期估计家里锅都揭不开了,唉…老蔫蹲在家门口叹气着。


快吃晚饭的时候,姐夫回来了,但与往常不同的是菲菲并没一起回来,“菲菲同学聚会去KTV 唱歌,叫我先回来。”说完,姐夫便进屋呆坐着去了。


老蔫摇了摇头,姐夫被菲菲使唤得言听计从指哪打哪,就跟一佣人似的,唉,天底下竟有这样的姨父和侄女!老蔫在心中叹道。


太阳渐渐告别地平线以上,老蔫有点着急了,“管不住的儿子看不住的女儿,唉,又玩疯了不是!”老蔫蹲着自语道。


“…叔叔,菲…菲出事了。”菲菲的闺中密友上气不接下气的跑了过来。吓得两个男人立马慌了,老蔫都哆嗦了起来,“怎么了,到底怎么了?”


“我们在KTV 唱歌,有一帮小痞子过来调戏菲菲,我们班的几个男生上去阻拦,结果被痞子打伤了,菲菲气坏了便打了为首的一耳光。”


姐夫的脖子都粗了起来,一把抓住那女生摇晃着问道:“菲菲现在怎样了?”


吓得那小女孩欲哭道:“被他们堵在包厢里了,为首的那个好像是市委书记的儿子,据说还是个警察。”


听到市委书记这四个字,老蔫的腿就开始发软,似乎都站不稳了,口中念叨着:“这可怎么办,怎么办…”在老蔫没了主意的时候,老蔫的姐夫已冲了出去,等二人跟着出去,却眼见着姐夫的身影消失在远处。


※※※※※KTV房


学生们早已不在,估摸着找人的找人去医院的去医院了,老蔫的姐夫迅速的一层层的找将开来,很快,他便看到七八个黄毛绿毛嬉笑着站在一个包厢外。走近,便听到一个熟悉的声音在喝骂:“放开我…”“给我让开。”老蔫的姐夫喝道。黄绿毛们讥笑的看了他一眼随后放肆的笑骂了起来,“老东西,瞎狗眼了。”接着又是一阵哄笑。


看到老蔫的姐夫不吊他们,说话的那位又开骂了,“老…”但他没说完就发现自己半边脸开始麻木了起来,口腔中也好像少了一半什么,当他倒地的时候,才发现自己的一帮哥们全在地上躺着呢。


老蔫的姐夫也没干什么,只是用脚一人赏了一耳光。


嘭的声,包厢门被踹开。一股烟酒之气扑面而来,老蔫的姐夫皱了皱眉头。本来撕打着一对男女停了下来,女孩看见来人不禁哭了出来,“姨父。”


此时的老蔫的姐夫被这声姨父弄红了眼,他这姨侄女从小就没受过半分委屈,更何况像今天这样子被人欺负,一天天看着菲菲长大,她出落得很像她的姨娘,一想到菲菲的姨娘,他感到自己又回到了那个战火纷飞的年代。


在老蔫的姐夫出神的那刻,市委书记的儿子一把扼住了菲菲,菲菲的尖叫把老蔫的姐夫拉回了现实。


当看到菲菲被人扼住几近无法呼吸,他的血液加速了也加热了,他已经有点无法控制 自己的愤怒了。他似乎看到是菲菲的姨娘在被人扼住。


“放开她。”老蔫的姐夫有点歇斯底里的喝道。那人紧张之下竟拔出了把枪。当看到 枪指着菲菲的时候,愤怒彻底的燃烧了他。


可能是由于一直以来的张狂,市委书记的儿子并未觉察到危险的逼近,一时事情的突发,他根本没来得急注意到门外躺着的那一堆混仔。


“操,哪来的老东西,坏你大爷的好事。”说着扬着手中的枪,按他往常的经验,正常人看到枪都得吓趴下,他以为已镇住了那个闯进来坏了自己好事的中年人。唉,只可惜包厢里太黑了,再加上浑浊的空气使他晕乎乎的,事实上他才是应该被镇住的。如果他能看清老蔫姐夫脸上的神色,他的这辈子就不会这么毁了。但历史从不用如果这两个字。


就在他扬来扬去的那刻,他感到自己的手好像被什么打断了,手枪也掉在地上了,当他醒悟过来的时候,两腿膝盖传来剧痛,接着他便看到那中年人的脚如蝴蝶翻飞般的在他身上踢着,踢得煞是好看,可惜伴奏的是自己骨节碎裂声。


此时的市委书记公子才看清对方的神态——霸气横溢,骇得他连忙后悔自己刚才眼瞎,可已与事无补…


菲菲没敢回头看那色狼,她爹不知道姨父的脚力,她可是见识过,碗口粗的树那都是 被一脚扫断,踢人身上不废也得残。


老蔫的姐夫拉着菲菲回了家,他似乎什么事也没发生过,没有焦虑没有担忧。


“姨父,他不会死吧,他万一真是市委书记的儿子怎么办?”看到姨父并没理她,那位被称作菲菲的女孩无奈下撅起了小嘴,担心了起来。


※※※※※市第一人民医院高干病房


市委书记铁青着脸望着他半昏迷的儿子,手上青筋暴起,此时的他已经被怒火燃烧, 他无法想象到底是谁那么有种敢在自己治下废了自己儿子。 看着市委书记凶冷的眼神,医院院长焦急的小声对着一个医生命令道:“快看看去,董老怎么还没来?”


就在此时,门被推开,一个白头老者在别人搀扶下蹒跚进来,院长看见老者迎了上去,市委书记脸色也缓和下来。


老者没搭理二人,径直走向病人,当看见病人的伤口时,老者发现了什么的珍宝似的,眼中露出惊喜的光芒。


老者慢慢流下了眼泪,喃喃自语道:“五十多年了,没想到临死前,我还能看到柳派谭腿踢出来的骨伤。”


老者抚弄着伤口,欣慰着念道:“没失传啊,这脚力…”老者嘴里发出啧啧的惊叹声, 全然不顾旁边人的焦急。


医院院长看着脸又阴沉下来的市委书记,心中暗叫不好,凑到老者身边,“董老,您看这伤…”“这伤,没三四十年的功底踢不出来,想来中国现在能踢出来的不会超过这个数。”老者竖起五个指头。
“是个什么样的人踢伤的?”老者问道。医院院长望着市委书记支支吾吾说不出来,市委书记却是等不及,生怕再拖沿下去,他宝贝儿子就废在那了,回道:“是个中年人。”


听到这话,老者一直昏暗着的眼睛亮了一下随即暗淡下来,“我知道是谁了。”接着就向门外走去。


院长一看就急了,“董老,你这是?”老者语气中实起来,“这伤是我一位故交踢的, 我知道他的为人,不为点事,他不会踢这么重的。所以这伤我不能治,你们另外请人吧。”院长一听更急了,直接拉着老者袖子,“董老,董老…”老者不客气的拂开院长的手,回到病人的面前。


院长和市委书记提起来的心又松了下来。“这年轻人,身体早被酒色掏空了,就是做了手术,也恢复不了,就是恢复得了他还得过以前的酒色生活,这样一来也挺不了几年,就这样躺着反能活到60开外。”说完,老者又要开走。


这次市委书记也急了,“董老…”可怜兮兮地望着老者,老者回望了他一眼,长叹了 一口气,“我老了,像这种手术已经没精力做了也没胆量做了。”接着推开门,走掉。


再接着,高干病房里一阵东西摔砸声。


那个董老是刚从北京回来养老的,以前可是只给首长看病的,中南海的路比谁都熟,强横如一省省委书记也奈他不何。


这位市委书记也只有含血吞了断齿,但他绝不会咽下这口气的。他把碰壁的怨气全发 在那个踢伤自己儿子的中年人身上。


※※※※※市委办公室里


“给我把李四找来。”市委书记对着电话另一头命令道。一旁倒茶的秘书听了愣住了,“那可是市里出了名的杀神,一人追着十几个人砍的狠角,那人没事惹市委书记儿子干嘛…”秘书在心中叹道。


※※※※※深夜


老蔫厂子家属区门前停下辆面包车,七八个彪型大汉跳将下来,提着砍 刀便向家属区里面冲,门卫探出头看了眼,看到这架势连忙缩了回去。“哎呀我的妈呀,


不知谁家要倒霉了。”赶忙拨了110 和120.


此时,老蔫和菲菲正在家中发愁呢,万一真是市委书记儿子,那可怎么办,就是不是,那医药费也赔不起啊。可把老蔫愁得一佛出世二佛生天。


老蔫瞅了眼他姐夫,他姐夫没事人样的呆坐在那,“精神出问题就这好,出再大的事, 他也不会觉得有啥可担心的。”


就在三人干坐着的时候,门被踢开了,光看到为首的那张脸,老蔫的魂魄就飞得差不多了,那位刀疤从眉角一直拉到下巴,一看就知道绝非善类。


就在老蔫绝望的感到末日来临的时候,他发现为首的那位从一脸杀气腾腾转为一脸诚 惶诚恐,“师叔,是您老呐!”这话可把所有人吓得不轻。


直到那帮流氓走了许久,老蔫神还是没回得过来,他第一次感到自己得重新审视自己姐夫了,菲菲则是用敬仰的眼神看着自己的姨父,她可认出为首的那位是传说中的徐四了,看到全市最出名的流氓头子见自己姨父都毕恭毕敬的叫师叔,她唯有用敬仰的眼神看着自 己的姨父了。


当徐四一帮人走到家属区门口时,110也到了,领头的警察拉开车门笑说道:“请吧,徐四什么事犯得着你亲自动手啊。”


徐四一脸的讪笑,“今天可没砍人,到一朋友家坐坐而已,不信,您自己进去看。”


看到120车空手而回,警察们知道没出事,“去哪儿啊,要不我顺道载你一程。”


徐四陪笑道,“我带车来了,您那车坐多了,我晦气。”


为首的警察也笑了,“嘿,你小子还挺讲究的啊。”警车和匪车绝尘而去,扔下门卫在那郁闷着,“咋就没砍成呢?”


市委书记在办公室里来回踱着,焦急的等待回音。


铃声响了下,是短信,秘书知趣的拿起手机,“是徐四的,他说要砍的是他的师叔,他不能砍也不敢砍,劝您就这么算了,否则玩到底吃亏的只能是你。”秘书把手机递给了市委书记,市委书记接了过来,一把扔了出去,“放他XX的屁

 

※※※※※面包车内


“四哥,那人没多大啊,真是你师叔?”徐四颤抖抖的点起根烟,“你们说我能打吗?”


旁边人回道:“谁不知道你四哥是我们市最能打的啊,一人砍十几个的主。”


“你们见过我怕过什么人没?”


“没,道上没听说过谁敢跟你叫板的,就是公安局长见你还得客气点打招呼啊,您怕过谁啊?”另一人回道。


徐四长吐了口烟气,“刚才我被吓得腿都发软了,差点跪下。”车厢里人都乐了,“四哥,你就别忽悠我们了。”


“忽悠你们做啥,当年我见到他的时候,都尿裤子上了。”徐四说得一本正经的,其他人来兴趣了,“四哥,给讲讲。”


“知道胡司令吗?XX市里最吊的那个黑社会头头,号称铁手铁胳膊,碗口粗的石柱子一撸就断了,那才叫真功夫。”众人听得一脸神往。


“知道胡司令这功夫哪学来的吗?”众人听得更起劲了。“你们出道晚,砍砍人就算混开了,我出道那会得拜师,别以为这师没拜头,那时候道上的人的功夫都是跟自己师傅学的,所以那时候尊师啊。”


“所以道上也就讲究辈分,见了前辈得行礼,否则,就别再想在道上混,我们这城里当时道上的都是清帮一脉,清静道德、文成佛法、能仁智慧、本来自性、圆名行理、大通俗学,那都是按辈来的。”


“四哥,那你什么辈分啊?”


“俗字辈,胡司令也是,嘿,我们俩一个师傅带出来的, 我入的迟,没学到什么师傅就翘了,我只好跟着胡师兄混。”


“我师兄那时候道上名头响,绰号胡铁臂,附近几个市老青帮的都知道我们市出了个胡铁臂,我师兄那时候好不威风,可他也有怕的人。”众人露出不可置信的神色.


“那时候,城里有一老头,清帮的,辈份可真高,大字辈比我师傅还高一辈,我师傅在的时候我师兄那功夫已经练出来了,我师傅一再告诫我师兄不要去惹老头,我师兄哪听得进去,我师傅没办法就把老头的事情讲给我们听了。”


那几位听得张大了嘴巴全神贯注的听着生怕漏了一个字。


“那老头真是吊得不得了,年轻的时候号称清帮第一打手,知道清帮第一打手这几个字份量吗?那可是用上千颗人头堆出来的,上千颗人头呐!”徐四说得自己长虚短叹,唉,谁听了不感叹呢?


“你们是不是疑问老头怎么来我们这了?”众人点头。


“我师傅跟老头关系还不错,老头也就谈了些过去,当年这个词不让说,没办法啦一位高层落到了日本黑龙会手里,落到黑龙会手里可比什么地方都难弄出来了,这个词不让说,没办法啦的高手一时调不过来,便找到了老头。”


“老头一出手,嘿,日本那些黑龙会高手只配舔鞋底,人是肯定救出来了,可老头在救人的时候撞见黑龙会的人在做些禽兽都做不出来的事,可把老头火的。杀了个回马枪,杀光了,整整黑龙会一个分部啊,七八百号人,一晚上杀的半个也没留。”


“听说日本猪们赶来的时候鬼嚎了半天,黑龙会的头们自己切自己切了一大半, 嘿,被杀的里面据说有一个日本大官,嘿,他们也有今天,哈哈…”


“然后就是疯狂追杀啊,还好老头的儿子和儿媳被那个高层带走了,老头也就带着孙子隐居在我们这了。”


“听完后,我师兄再也不敢提去会那老头了,不敢也没了那心了,那老头可是民族英雄级别的,再流氓的痞子他也是中国人啊,他也懂爱国。我师兄也就没再混了,安心当了个工人。”


“四哥,后来呢?”旁人追问道。


“后来就文革了,我师傅和老头他XX的前就相继翘了,我师兄也就熬出头,那时候乱,派别林立,我便跟着我师兄混出了些名堂,成了市里最出名的一帮造反派,能打嘛!”徐四说到这的时候,脸上露出了些自豪。


“可我师兄真不是个好胚,得势便张狂,那时候乱,他又色,我师兄也就犯下了些伤天害理的事,照例说没人管,可老头的孙子听到了些传闻,便捎话给我师兄了,叫我师兄收手否则他出来清理门户。他是通字辈,按理说是我们长辈是我们师叔。”徐四叹了口气,又说道:“我师兄哪听得进去,一毛孩子,能有多大能耐
啊。我当时也这么想。”


“唉,又过了些时日,一天我师兄瞄上一女孩子,那女孩子可真漂亮,我这辈子再也见过有这么漂亮的女人了,钱市长那中戏毕业的儿媳漂亮吧?”


“漂亮、漂亮。”那几个连忙点头称是,并在脸上露出不怀好意的笑容。


“嘿,跟那女孩比,那就是一乡下柴禾妞,那就是一泡牛大便。”徐四陷入回忆的迷茫。


“当把那女孩抢到总部的时候,那女孩倒也没惊慌,她很镇定的告诉我师兄她是那个老者孙子的女朋友,在帮里这可是大忌,可他XX的谁还管帮里的规矩啊,但我师兄一开始也没敢动手,关键那女孩的气质,让人看了不忍侵犯,就跟一仙女似的。”徐四的嘴角抽搐了两下,接着长叹口气。


“现在想来,我那师兄真浑啊,那么好的女孩他都忍心去侵犯,我也是一混,我怎么就上去搭手了呢。”徐四一脸的羞愧,说得那几个孩子也一脸羞愧,都没干什么好事过。


“就在我们扒那女孩衣服的时候,那少年来了,一个人,还没带家伙,当时那地方我师兄的人足足有两百多,每人手里都操着家伙。”


“当时我们在二楼,我师兄叫我去窗口望着,他自己动手,我就站到了窗口一边望着我师兄一边望着楼下,当那少年动起手来我就没再回望我师兄,因为我不敢相信发生在我眼前的那是事实。”


“两百多人,唉,就跟沙包样一个个被踢飞,都是一脚,踢哪地方的都有反正结果一样,没见有起来的,太快了,根本都没来得及有反应,当时感觉就眨了下眼,人全趴下了。到最后就剩下个两米多高的东北汉子守在楼梯口,那汉子近四百斤重提着根碗口粗的铁棒,平常几个人推都推不动,唉,就一脚,铁棒和人一起弯了、飞了,撞了进来,当时我就尿裤子上了,瘫倒在地。当时我还望了我师兄一眼,嘿,才扒了一件衣服下来。“


“唉,什么铁手铁胳膊,当我们抬着我师兄去医院的时候,医生直接问是不是被压路 机碾过了,全碎了,粉碎粉碎的那种。”众人听得一脸骇然,有两位口水都出来了。


“当时听说市委书记儿子是被踢伤的,我心里就犯难了,生怕再碰到他,唉,可惜碰到就是他,你们说
我能怎么办,我出来的时候冷汗都把内衣湿透了,还好进去的时候看了眼,否则我们再去百十号人也得全折在那。”

“不信,摸摸我内衣,还湿着呢。”徐四长吸了口烟,扔掉烟蒂。

“给道上放话吧, 别去惹那一家子,特别是那帮被打的小子,谁再动那女孩一下子,我就挑了他手筋!”

不到两天,老蔫姐夫的故事传遍了整个城市,在这缺乏英雄的年代,这种传闻比什么都热销,给老蔫带来的唯一好处是:他被通知不需要下岗了。厂长还专门找了老蔫一趟,一改以前的盛气临人,客气多了的说道“老蔫啊,啊,不,蔫哥,家里有困难,怎么不向厂里反映呢?要相信组织嘛,你是厂里的元老了,说什么也得照顾,就这么说定了,下次分房第一家就你,你可不能再推了,这不伤我们领导的心嘛。”老蔫只有唯唯诺诺的点头。

菲菲处境的变化是学校里什么人都对她特别客客气气的,特别是那帮学人家混着的体育特长生,走路都远远见着了躲着走,也不敢在学校里生事了,其他以前饱受其苦的普通学生都暗地喊菲菲叫雅典娜女神,驱走黑暗带来光明。

当然这些都传入市委书记的耳朵中了,可把他气得牙痒,徐四也就被通缉了,当然市委书记也知道没用,只能出出气,这种流氓头子都有有钱的大老板罩着,奈他不何。但市委书记哪能咽下这怨气,他堂堂一个市委书记的儿子被人废了,岂能就这么算了。

 

※※※※※书记怎么办

他毕竟是市委书记啊,他就是雄霸一方的土皇帝,他掌握着国家 专政机关的领导权。但他不能为所欲为,毕竟这还是这个词不让说,没办法啦的天下,而不是国民党的。作为一个能爬到市委书记岗位的人,我们绝不能小瞧他的智商和能力,一个一直作恶的笨蛋对社会的危害和一个偶尔作恶的聪明人对社会的危害,那就如拿着AK-47的伊拉克抵抗者和操着战斧导弹的美军的战斗力比对。不是一个档次滴!

也是一个夜晚,市委书记自己开车光临了市公安局长的家,在这净秘的夜,他们开了共和国的先河:上级向下级行贿30万美元。我们只能这样评点:盛怒之下的聪明人是失去理智的,他们用自己绝佳的才智论证着自己罕见的愚蠢。

“老赵,我知道你是特种兵出身,身手好得很,这次抓捕,你亲自带队一定要保证成功,还市里一个安定团结的大好局面。”公安局长只有点头。

※※※※※老蔫家

唉,老蔫辛辛苦苦修起来的门又坏了不,一群特警从各个方向跃了进来,在老蔫的惊恐和绝望中特警们等待着公安局长的命令,在那么多渴求的眼睛的注视下,局长稍息、立正,然后有力的行了个军礼,“首长好!”

当他做完这些后,他才意识到呆坐在沙发上的是他的老上级,看了看老上级住的像鸽子窝般房子,再看了看神情呆滞的老上级,他真的不敢相信,这就是他的老上级,共和国最精锐的特种部队“××”的第一任大队长。

“队长。”换来的是迷茫的眼神,公安局长的眼睛红了,他无法把眼前这个人联系到当年的偶像全军的楷模身上…

当市委书记在办公室里苦等消息无果后,他派出了自己的秘书亲临现场,他秘书看到的是本来去抓捕的特警正在那修门窗,本该进拘留所的老蔫姐夫还呆坐在那,公安局长却不知所踪。

市委书记的肺气炸了,死打公安局长的手机回音是已不在服务区。随后几个小时后,省军区。

“还活着!”省军区的司令员坐不住了,他站了起来来回的走动着,紧张兴奋的搓着双手,话也说得结巴起来了,“你,你,你,给我带一个连先回去,死也要把人给我看好了。”

“回去后立马整理个材料出来,如果没出问题人再过来一趟,这30万美元我替你先交到省纪委去。” 公安局长敬了个军礼,就要出去,司令员还是不放心,“把人给我看周全了,千万别出差错。”公安局长又敬了个军礼,“指导员,我保证完成任务。”两人都有点伤感和激 动。“去吧,路上小心。”当老蔫看到公安局长带着一队士兵回来的时候,他自己也不知道自己该作何想法了,等到那些士兵开始架构火力点的时候,他开始掐自己大腿了,在心中自语道:“要打仗了吗?”

市委书记的消息也算灵通,当30万美金到了省纪委的时候,他明白了一切,公安局长的那一句“首长好。”也早已传遍了大街小巷。

那个家属区站岗的军人小半天功夫在市里已不再是新闻,市委书记瘫坐在沙发上,他 在脑中想象着盘算着,公安局长的车离省城还有多远,在盘算着省纪委的人什么时候双规自己。

他开始在大脑中幻想起来,要是自己儿子不去调戏那女孩,要是…但不该发生的一切 都的发生了。他便开始幻想起尚未发生的了,他幻想到了公安局长的车翻出了高速公路,燃烧了起来…

就在市委书记恍恍惚惚的时候电话响了,电话的那头急促的说着什么。市委书记痴痴呆呆地听着,突然他蹦了起来,恢复了往常的干练,“什么,你再大声的说遍。”

电话中断,接着办公室响起市委书记的狂笑,“哈,车祸,哈,死了,哈哈…”笑声 中无尽的是重获新生的喜悦,市委书记的大脑重新运转起来,他开始细细分析起来。

新上任的市公安局代局长是市委书记的亲信,因为市委书记把他从一个派出所所长直接提拔成市公安局代局长,只要能让市委书记满意,那正式的局长也就是迟早的事。

老蔫居住的那个家属区来了一批警察,领头的正是刚上任的市公安局代局长。中国人总是喜欢感恩图报的,枉不枉法那就放一边去了。

面对警察的询问,两位哨兵哗得声拉响了枪栓,当警察再次询问时,哨兵便把枪口对向了他们。警察们无奈下离去。(中国军人最实在,俺佩服。)

对此,两位要人一个书记一个局长郁闷了半天,当然同时省军区的司令员也在郁闷着,他反映的情况未被省纪委受理,因为他举报市委书记行贿下级的那个夜晚,一位省主要领导同志出来证明那个市委书记当晚在他家,在加上公安局长出了车祸,死无对证,只能不了了之。

在省军区司令员束手无策的时候,公安局代局长想出了对策,一番耳语让市委书记感 到自己好运不断也庆幸起自己没看错人。

当天夜里,家属区发生火灾,所幸的是那连军人发挥了作用,让家属区的居民再次感受到了解放军的温暖。但与居民的喜笑开颜相比对的是官兵们的愁眉苦脸,老蔫一家失踪了,随后该连连长在向军区汇报的时候,他在电话这头切实的感受到了司令员的震怒,“你个榆木脑袋,我叫你干什么去的?救个火,你就不知道留两个人,你个2.5基数急速射也轰不开的死榆木脑袋,12个小时内给我把人找回来,找不回来唯你是问…”

人生地不熟,到哪找啊,可把官兵们愁的,但老蔫一家已成为这个城市的名人,一个看上去很蔫的中年男人,一个看上去痴呆的中年男人,再加上个异常漂亮的女孩,嘿,老蔫一家呗。不知道消息怎么传出来的,但当省军区的援军赶到时,那一个连的官兵已把某公安分局围个水泄不通。

当特种兵们冲进审讯室的时候,老蔫的姐夫呆呆地坐在审讯桌上,地上几个警察被击昏在那。特种兵们看着这位传奇人物,敬仰了半天。

当市委书记赶到的时候,军队的人已带着老蔫一家开赴省城了,市委书记得到仅是百姓的嘘声和鸡蛋。

接下来,事情就闹到了省里,市委书记这边坚持说是刑事犯,当然他得到省里面强力人士的支持,有点有恃无恐。军队这边说是失踪已久的战斗英雄,军区司令员好像从没怕过什么,因为军队里支持着呢,特别是特种部队那帮孩子,都准备动手了,24小时整装待发。

在省委办公室里,两帮人发生了争吵,司令员讥笑道:“不就是废了你儿子嘛,调戏妇女妇女未遂的不该打?那叫见义勇为。”

市委书记倒是语气平常,“且不提他打伤人,据我所知,他在军队的时候不听令擅自行动,最后直接失踪,这样的人应该视作逃兵,送上军事法庭。”

他这话可激怒了司令员,作为曾经一起出生入死的战友,最忌讳就是自己的战友声名被侮辱。逃兵?这可把那火爆脾气的司令员气坏了。

他哗的声把枪掏了出来,“你再给老子说遍逃兵试试,信不信老子毙了你个XXXX的。”

这玩笑可开大了,可把旁边的政委吓坏了,连忙夺下枪,安慰道“老李,消消气,别跟那王八蛋计较。”听得某中间派的省领导直摇头,唉,这批军队领导都是越战下来的, 那脾气那嘴没话说他们了。

省里解决不了,只有到中央了,两帮人搞足材料,开赴北京。

※※※※※北京

军队的人快了一步,毕竟他们去老首长们那边方便的很,军队里上下级感情很深的,特别中国的军队,那就是父子兄弟。当老蔫和菲菲随着姐夫被带到一个个军队宿老家中的时候,老蔫第一次见识了自己姐夫真实面目,基本上每一个老将军都有和姐夫的合影,照 片上的姐夫真是英拔挺立。当来到杨得志杨老家时,一直呆呆的姐夫看到杨老——他的老上级竟回忆起什么,行了个军礼,并喊了“首长好。”(杨曾任对越反击战最前线指挥官)把杨老感动的老泪纵横,拿出了与老蔫姐夫的合影,令众人意外的那是一张三人照,另一人就是菲菲的姨娘。

老蔫第一次看到了自己老婆姐姐的样子,很像,她们姐妹俩和菲菲三个人彼此都很像,但菲菲姨娘的气质最好,仅仅是张照片就让人的眼睛不忍离去。那样的女子真是不忍心有丝毫冒犯和侵犯,要不是亲眼所见,都无法想象世间竟有这样的女子存在。

众人都被照片吸引了,谁都没注意到老蔫姐夫的神态,他呆立了半天,渐渐回忆起什么又抗拒着不想回忆,身体抖动起来,最终无法忍受,一把把照片夺了过来,眼睛贴着照片看了许久,嘴中发出呜呜的声音,渐渐变大,最终嚎啕大哭起来。

让老蔫无法相信,因为他无法想象到一个大男人会这么大声的哭出来,哭得这么肆无忌弹哭得这么伤心,因为就他目前了解的情况来说,姐夫应该是个铁汉,钢铁铸就的特种兵战士,从血肉到心。

但接着老蔫看到司令员、政委、杨老也跟着哭了出来,同样的大声同样的肆无忌弹同样的伤心,“他们该为一件事哭得吧。”菲菲在心中自语道。

支持市委书记的省领导和市委书记灰溜溜回到了省城,因为他们发现他们面对的是整个中国军方,他们虽然得知了老蔫姐夫的一些情况:特种部队的大队长,立下些军功,擅自行动,最后失踪,但老蔫姐夫对他们来说还是一个迷,因为就是老蔫和姐夫一起生活了
十几年,姐夫对他来说也还是个谜,姐夫的历史好像就是无法揭开锅盖的锅,除了他的战友,没人知道里面是什么。

老蔫一家就暂时住在杨老的家里,杨老对菲菲很是疼爱,因为他以前一直把菲菲的姨娘当自己女儿看待的,菲菲也就能自由出入杨老的书房,那是一个有着温暖春风的午后,菲菲准备找些书看看,她一进书房就被桌上一本发黄薄册吸引。

那是一本内参,有些年头了,没有标题,一开始就是一个人的简历:××,父:×× ×;母:×××。我党历史上著名的夫妻烈士……曾任国家领导人×××贴身卫士,面对越共特工部队渗透袭击,军委决定组建精锐特种部队“××”以打击越共猖狂气焰,×× 被抽调任命为该部队第一任大队长。

看到这,菲菲便知道讲的是她姨父了,她便接着往下看了下去。由于出身在武林世家,××擅长传统武术格斗,迅速培养出一批身手矫捷的特种战士。

与越共特工相比,我们特种战士强在徒手格斗能力上,特种部队“××”的战士往往能在伏击战中一瞬间徒手格毙越方三名特工,该部队大队长××更被越方称为铁腿死神,以至后期越方不可一世的特种部队往往需要躲着我军特种部队行动,在被我军特种部队连续无情打击后,越共迁怒与我军其他部队,便发生了“野战医院惨案”。

××××年××月×日,越军特种部队袭击了我×××号野战医院,残忍杀死我数百名伤兵和医生,并掳走所有护士。其中一名护士正是特种部队大队长××的妻子,越共得知其身份后,针对性采取了报复行动。值得一提的是越共对待我军女战虏往往是一种号称“海豹人”极为残忍的手段。越共对我军女战虏往往采取有计划的反复强*,当中国女兵们怀孕后,便被锯掉四肢,让她们仅能像海豹一样蠕动,越军籍此取乐。 当我军女战俘绝食求死的时候,越军就强行注射葡萄糖,以便交换战俘的时候交还给我方。

在××的妻子被俘后,我方提出用越方高级战俘交换,越方并未理睬,而是施以他们一贯的禽兽行为,并且更加狠毒残忍,每天锯下她的肢体的一小截,通过前方哨所送与我特种部队“××”,企图动摇我军士气,却不知激起我全体特种战士的怒火。

在随后数天内,我特种部队在大队长××领导下对越共进行了疯狂打击,越共便采取另一种无耻手段,以××妻子性命作要挟,让××单人赴死局。虽然××仅仅一人赴约,但越共设下的伏击徒增越特工的伤亡罢了,以至越特工产生恐惧压力,提到××人人变色,再不复初开战时极端民族主义思想下的不畏生死了,当时我军特种部队“××”战士全部陷入狂怒,每天早上看着越军送来的惨肢和地址,每天晚上看并等着大队长××的归来.

至此前线总指挥部失去对该部队控制,该部队疯狂出击,过处不留活口,常渗透到越军后方数百里定点清除越高级指挥所,往往在一分钟之类用无声武器击毙所有对手,收缴文件,随后实施爆破,据统计越军5个师级以上高级指挥所被清除。

另有传闻越共两高干在视察前线途中遭遇该部队,被击毙,越共高层震动。以至越军高级将领提到前线视察,都畏如赴死。都怕遭遇我军“死神兵团”,越共高层便产生一批反对黎笋集团的高级干部。

一系列打击后,越共停止对××的要挟,并在前线通过广播要求双方停止特工战,但直到一个月后,特种部队“××”攻占距河内仅35公里的一个据点后,我军才停止特工战。

“××”大队长××就在斯役宣告失踪,据后来调查结果,那个据点正是敌人虐待我 军女战俘的主要据点,当“××”部队赶到时,××妻子已遭毒手,死状惨不忍睹。

据越军俘虏交待,一切兽行都是在黎笋次子亲自命令下干的,在报复和要挟无望后, 越共把愤恨全出在中国女战俘身上了,越共运来一桶桶蚂蟥,将中国的“海豹人”一个个放入桶中,直至蚂蟥全部钻进中国女战俘身体。不敢想象,她们死得时候是何等的痛苦。

那一声声刻意压抑的悲鸣和哭声,那些娇柔的女子那些本该窃窃私语欢笑着的女子…

唉,泪流下。

当我军特种部队攻占该据点的时候,看到的仅是一具具失去四肢肿胀不堪失去人形的 尸体,当时特种战士们都傻了,他们没有想到自己善待俘虏换来竟是战友被这样对待,都抱着尸体哭了一夜,据说这个部队从此就有了个严格执行的传统,绝不称越南人作人的。

据说在那哭了一天一夜,抱着自己残缺不全的妻子,让所有是配称人的动物都悲愤流泪的是他妻子腹腔是割开的…菲菲再也看不下去,在她扔下书时,她的衣服早已哭湿透。她感到自己好像掉入了漆黑冰窖里,不见灯火也感觉不到丝毫温暖。

关于越共对菲菲姨娘的暴行,我不忍心再叙述,当天晚上老蔫也知道了,坐在那呆立半天,杨老怕他也出问题拿了另一份内参给他。

这一份是越南特工叛逃过来后交待的材料。越南籍男子×××,原黎笋卫士,黎笋集团下台后叛逃我国。下面是他交待材料的一部分:“××”大队长××事在我们特工间一直流传,初开始我们都不服中国的“××”部队,都想上去和他拼一把,毕竟美国的特种 部队遇到我们也只有败的份。

但到后来,越来越多特工部队被“××”打掉了,高层就害怕了,不敢把特工部队拉上去硬拼,关键中国人“手太重”,伏击战打起来吃亏大都是我们。 ××的名字我们也就听多了,对他又恨又怕,但我们卫士中身手好的还是不服气,但也没办法单挑啊。袭击医院那是违反国际公约的事,但当时我们特工队伍被中国的“××” 部队压制着打,上头为出气硬下命令。

但没想到那次竟抓到了××的老婆,黎笋也惊动了,还派了他小儿子亲自负责,我们都以为这次能够整垮“××”部队,先是吓阻却迎来中国“××”部队更大规模的攻击后来就抽调我们卫士组的人,准备去伏击××,连续几批人,都没回来,也就没人再敢去了,事情不了了之了,黎笋的儿子便将那女的“放了风筝(剖开腹腔,取出肠头拴在高处将人扔下,人在下落的时候,内脏被扯出而死。

再后来听说,中国“××”部队疯狂渗透,好几个高级指挥部被端了,谈到“××”和××基本上人人变色 ,接着就有了那一晚,当时我们知道中国“××”部队渗透的利害,加强了防备,但我们没预计到他们会渗透到河内来,而且还是一个人。

那晚,正好我当正值,贴身护卫黎笋,黎笋和他一家子在吃饭,无声无息的门就开了,当我们都往门口看的时候,一个中国男的出现在黎笋的旁边,并挟住了黎笋勒令我们把枪扔出门外,当时,我们屋里共有十几个卫士,但都没发觉注意到他是怎么进来的,当时我心里就闪了一下,“不会是××吧。”真不敢相信,不谈河内的防卫,就是在屋外我们也还有三百多卫士,那是越南最精锐的特工部队。

当枪扔完后,他就用越南语问我们谁是黎笋的次子,我们都没敢说。就在这个时候,他被蛇缠住了,那是两条专门训练的蟒蛇,专门训练出来护卫黎笋的,我曾看过这两条蛇 生生勒死过一头水牛,都以为制住他了,黎笋的次子就站了出来问他是谁。

他说他是××要为自己的老婆报仇,我们都笑了,蟒蛇越缩越紧,他一会就得被勒死,都在看着他死,黎笋的次子笑得最大声,毕竟一直杀而不死的敌人自己送上门来了。就在我们以为他死定的时候,蟒蛇被他用手撕裂了,我们以前得到资料,××都是用腿的,没想到他的手比腿还厉害,那可是两条巨蟒啊,没有枪我们十几个卫士就上去硬打了,关键我们当时都以为他是潜进来的而没想到他是杀光外面的人才进来,谁会这么想啊,三百多最精锐的特工,一个人杀!还以为十几个人能制服他,毕竟我们是越南身手最好的一批人。

都是一下子,现在回忆起来都后怕,都是被踢被打在脑壳,踢的还好直接死掉,被手打到的一下子脑壳就碎了,上去几个就碎几个,我们一看就知道不妙了,那哪还是人啊! 赶忙护着黎笋从暗门走了。

黎笋的次子没走得掉,暗门关上的那刻,大厅里就剩下他俩了,××的眼神我瞥见了,那就是死神呐!

第二天,等我们回去的时候才发现,外面的三百多卫士全死光了,黎笋的次子尸体则是直接找不到了,最后算尸体的时候,才发现多了块肉泥,团在某个墙角,这时我们才注意到那面墙上全是肉沫和血迹。看了没有不吐的,那天的当值的卫士后来大都不干了,黎笋听说他儿子死的惨状也就被吓出毛病来了,直接到后来不敢出地下室半步,越共中央一批人便夺了权。

老蔫看完默然无语,杨老缓缓的说道:“当时,我们看到内参的时候,政治局的和军委的同志很多都哭了,你姐夫父母是我们党一对著名的烈士,再加上你姐夫俩口子,唉,伤心呐,你姐夫后来没了音讯不知生死,我们部队在边界等了近10年啊,没办法,最后中央秘密的开了追悼会。”

“97年邓老临走前,曾捶床长叹他平生的遗憾,其中就有没能见到你姐夫的遗体,不知他的生死,邓老这样说的,‘他父母为党牺牲,他夫妻俩为国捐躯,他爷爷还曾救过我们的×××同志,这个家庭我们党亏欠的太多!’‘我们国家不妄自开战,不逼不得已不要打,但是打了就要狠狠的打,让它痛上个三十年,要不怎能对得起像××这样的烈士家庭!’”

※※※※※结局
……菲菲大学里谈了个男友,带回来见家长,中午,饭桌上
菲菲说道:“夏陨,给姨父敬酒啊。”菲菲男朋友斟满酒,敬上,“怎么是水啊?”菲菲含着淡淡忧愁与伤心的回道:“这不是水,是爱情的眼泪!”

05 septiembre

Struts应用的国际化

Struts应用的国际化

发布:512d 时间:2005-06-10 点击: 83
Struts应用的国际化
万维网(World Wide Web)的迅猛发展推动了跨国业务的发展,它成为一种在全世界范围内发布产品信息、吸引客户的有效手段。为了使企业Web应用能支持全球客户,软件开发者应该开发出支持多国语言、国际化的Web应用。
 
1 本地化与国际化的概念
国际化(简称为I18N)指的是软件设计阶段,就应该使软件具有支持多种语言和地区的功能。这样,当需要在应用中添加对一种新的语言和国家的支持时,不需要对已有的软件返工,无需修改应用的程序代码。
本地化意味着针对不同语言的客户,开发出不同的软件版本;国际化意味着同一个软件可以面向使用各种不同语言的客户。
如果一个应用支持国际化,它应该具备以下特征:
· 当应用需要支持一种新的语言时,无需修改应用程序代码。
· 文本、消息和图片从源程序代码中抽取出来,存储在外部。
· 应该根据用户的语言和地理位置,对与特定文化相关的数据,如日期、时间和货币,进行正确的格式化。
· 支持非标准的字符集。
· 可以方便快捷地对应用作出调整,使它适应新的语言和地区。
 
在对一个Web应用进行国际化时,除了应该对网站上的文本、图片和按钮进行国际化外,还应该对数字和货币等根据不同国家的标准进行相关的格式化,这样才能保证各个国家的用户都能顺利地读懂这些数据。
Locale(本地)指的是一个具有相同风俗、文化和语言的区域。如果一个应用没有事先把I18N作为那前的功能,那么当这个应用需要支持新的Locale时,开发人员必需对嵌入在源代码中的文本、图片和消息进行修改,然后重新编译源代码。每当这个应用需要支持新的Locale时,就必需重复这些繁琐的步骤,这种做法显然大大降低了软件开发效率。
 
2 Web应用的中文本地化
无论时对Web应用的本地化还是国际化,都会涉及字符编码转换问题。当数据流的源与目的地使用不同的字符编码时,就需要对字符编码进行正确的转换。
 
2.1 处理HTTP请求数据编码
    默认情况下,IE浏览器发送请求时采用“ISO-8859-1”字符编码,如果Web应用程序要正确地读取用户发送的中文数据,则需要进行编码转换。
    一种方法是在处理请求前,先设置HttpServletRequest对象的字符编码:
              request.setCharacterEncoding(“gb2312”);
    还有一种办法是对用户输入的请求数据进行编码转换:
              String clientData = request.getParameter(“clientData”);
              if(clientData != null)
                     clientData = new String(clientData.getBytes(“ISO-8859-1”), “GB2312”);
 
2.2 处理数据库数据编码
    如果数据库系统的字符编码为“GB2312”,那么可以直接读取数据库中的中文数据,而无需进行编码转换。如果数据库字符编码为“ISO-8859-1”,那么必需先对来自数据库的数据进行编码转换,然后才能使用。
 
2.3 处理XML配置文件编码
    如果在XML文件中包含中文,可以将XML文件的字符编码为“GB2312”。这样,当Java程序加载和解析XML文件时无需再进行编码转换。
              <?xml version=’1.0’ encoding=”GB2312”?>
 
2.4 处理响应结果的编码
    可以通过以下方式来设置响应结果的编码:
       · 在Servlet中
              response.setContentType(“text/html;charset=GB2312”);
       · 在JSP中
              <%@ page contentType=”text/html;charset=GB2312” %>
       · 在HTML中
              <head>
       <META HTTP-EQUIV=”Content-Type” CONTENT=”text/html; charset=GB2312”>
              </head>
 
3 Java对I18N的支持
Java在其核心库中提供了支持I18N的类和接口。Struts框架依赖于这些Java I18N组件来实现对I18N的支持,因此,掌握Java I18N组件的使用方法有助于理解Struts应用的国际化机制。
3.1 Locale类
    java.util.Locale类时最重要的Java I18N类,在Java语言中,几乎所有对国际化和本地化的支持都依赖于这个类。
Locale类的实例代表一种特定的语言和地区。如果Java类库中的某个类在运行时需要根据Locale对象来调整其功能,那么就称这个类是本地敏感的(Locale-Sensitive)。例如,java.text.DateFormat类就是本地敏感的,因为它需要依照特定的Locale对象来对日期进行相关的格式化。
Locale对象本身病不执行和I18N相关的格式化或解析工作。Locale对象仅仅负责向本地敏感的类提供本地化信息。例如,DateFormat类依据Locale对象来确定日期的格式,然后对日期进行语法分析和格式化。
创建Locale对象时,需要明确地指定其语言和国家代码。如:
       Locale usLocale = new Locale(“en”, “US”);
       Locale chLocale = new Locale(“ch”, “CH”);
构造方法的第一个参数是语言代码。语言代码由两个小写字母组成,遵从ISO-639规范。可以从http://www.unicode.org/unicode/onlinedat/languages.html中获得完整的语言代码列表。
构造方法的第二个参数是国家代码,它由两个大写字母组成,遵从ISO-3166规范。可以从http://www.unicode.org/unicode/onlinedat/countries.html中获得完整的国家代码列表。
Locale类提供了几个静态常量,他们代表一些常用的Locale实例。例如,如果要获得Japanese Locale实例,可以使用如下两种方法之一:
       Locale locale1 = Locale.JAPAN;
       Locale locale2 = new Locale(“ja”, “JP”);
 
3.1.1 Web容器中Locale对象的来源
    Java虚拟机在启动时会查询操作系统,为运行环境设置默认的Locale。Java程序可以调用java.util.Locale类的静态方法getLocale()来获得默认的Locale:
              Locale defaultLocale = Locale.getDefault();
    Web容器在其本地环境中通常会使用以上默认的Locale;而对于特定的中断用户,Web容器会从HTTP请求中获取Locale信息。
 
3.1.2 在Web应用中访问Locale对象
    在创建Locale对象时应该把语言和国家代码两个参数传递给构造方法。对于Web应用程序,通常不必创建自己的Locale实例,因为Web容器会负责创建所需的Locale实例。在应用程序中,可以调用HttpServletRequest对象的以下两个方法,来取得包含Web客户的Locale信息的Locale实例:
              public java.util.Locale getLocale();
              public java.util.Enumeration getLocales();
    着两个方法都会访问HTTP请求中的Accept-Language头信息。getLocale()方法返回客户优先使用的Locale,而getLocales()方法返回一个Enumeration集合对象,它包含了按优先级降序排列的所有Locale对象。如果客户没有配置任何Locale,getLocale()方法将会返回默认的Locale。
 
3.1.3 在Struts应用中访问Locale对象
    有序Web服务器并不和客户浏览器保持长期的连接,因此每个发送到Web容器的HTTP请求中都包含了Locale信息。Struts配置文件的<controller>元素的locale属性指定是否把Locale对象保存在session范围中,默认值为true,表示会把Locale对象保存在session范围中。在处理每一个用户请求时,RequestProcessor类都会调用它的processLocale()方法。
    尽管每次发送的HTTP请求都包含Locale信息,processLocale()方法把Locale对象存储在session范围中必需满足以下条件:
    · Struts配置文件的<controller>元素的locale属性为true。
    · 在session范围内Locale对象还不存在。
    processLocale()方法把Locale对象存储在session范围中时,属性key为Globals.LOCALE_KEY,这个常量的字符串值为“org.apache.struts.action.LOCALE”。
    如果应用程序允许用户在同一个会话中改变Locale,那么应该对每一个新的HttpServletRequest调用其getLocale()方法,来判断用户是否改变了Locale,如果Locale发生改变,就把新的Locale对象保存在session范围内。
    在Struts应用程序中可以很方便地获取Locale信息。例如,如果在Action类中访问Locale信息,可以调用在Struts Action基类中定义的getLocale()方法。
    Action类的getLocale()方法调用RequestUtils.getUserLocale()方法。
    getUserLocale()方法先通过HttpServletRequest参数获得HttpSession对象,然后再通过HttpSession对象来读取Locale对象。如果存在HttpSession对象并且HttpSession中存储了Locale对象,就返回该Locale对象,否则就直接调用HttpServletRequest的getLocale()方法取得Locale对象,并降它返回。
    在Web应用程序的其他地方也可以直接调用RequestUtils类的getUserLocale()方法来获取Locale对象。
 
3.2 ResourceBundle类
    java.util.ResourceBundle类提供存放和管理与Locale相关的资源的功能。这些资源包括文本域或按钮的Label、状态信息、图片名、错误信息和网页标题等。
    Struts框架并没有直接使用Java语言提供的ResourceBundle类。在Struts框架中提供了两个类:
    · org.apache.struts.util.MessageResources
    · org.apache.struts.util.PropertyMesasgeResources
    这两个类具有和ResourceBundle相似的功能,其中PropertyMessageResources是MessageResources类的子类。
 
3.3 MessageFormat类和符合消息。
    Java的ResourceBundle和Struts的MessageResources类都允许使用静态和动态的文本。静态文本指定的是实现就已经具有明确内容的文本。动态文本指的是只有在运行时才能确定内容的文本。
    通常把包含可变数据的消息成为符合消息。符合消息允许在程序运行时把动态数据加入到消息文本中。着能够减少Resource Bundle中的静态消息数量,从而减少把静态消息文本翻译成其他Locale版本所花费的时间。
    当然,在Resource Bundle中使用符合信息会使文本的翻译变得更加困难。因为文本包含了直到运行时才知道的替代值,而把包含替代值的消息文本翻译成不同的语言时,往往要对语言做适当调整。
    Struts框架封装了MessageFormat类的功能,支持复合消息文本,该功能的实现对于Struts的其他组件是透明的。
 
4 Struts框架对国际化的支持
Struts框架对国际化的支持体现在能够输出何用户Locale相符合的文本何图片上。当Struts配置文件的<controller>元素的locale属性为true时,Struts框架把用户的Locale实例保存在session范围内,这样,Struts框架能自动根据这一Lcoale实例来从Resource Bundle中选择合适的资源文件。当用户的Locale为英文时,Struts框架就会向用户返回来自于application_en.properties文件的文本内容:当用户的Locale为中文时,Struts框架就会向用户返回来自于appcation_ch.properties文件的文本内容。
4.1 创建Struts的Resource Bundle
    对于多应用模块的Struts应用,可以为每个子应用配置一个或多个Resource Bundle,应用模块中的Action、ActionForm Bean、JSP页和客户化标签都可以访问这些Bundle。Struts配置文件中的每个<message-resources>元素定义了一个Resource Bundle。当应用中包含多个Resource Bundle时,它们通过<message-resources>元素的key属性来区别。
Resource Bundle的持久化消息文本存储在资源文件中,其扩展名为“.properties”,这一文件中消息的格式为:key=value。
在创建Resource Bundle的资源文件时,可以先提供一个默认的资源文件,默认资源文件应该取名为application.properties。如果应用程序需要支持中文用户,可以再创建一个包含中文消息的资源文件,文件名为:application_ch_CH.properties或application_ch.properties。
当Struts框架处理Locale为中文的用户请求时,Struts框架首先在WEB-INF/classes/目录下寻找application_ch_CH.properties文件,如果存在该文件,就从该文件中获取文本消息,否则再一次寻找application_ch.properties和application.properties文件。
应该总是为Resource Bundle提供默认的资源文件,这样,当不存在和某个Locale对应的资源文件时,就可以使用默认的资源文件。
应该把Resource Bundle的资源文件放在能被定位并加载的位置。对于Web应用程序,资源文件的存放目录为WEB-INF/classes目录。如果在配置Resource Bundle时还给定了包名,那么包名应该和资源文件所在的子目录对应。
4.2 访问 Resource Bundle
    Struts应用的每个Resource Bundle和org.apache.struts.util.MessageResources类(实际上是其子类PropertyMessageResources)的一个实例对应。MessageResources对象中存放了来自资源文件的文本。当应用程序初始化时,这些MessageResources实例被存储在ServletContext中(即application范围内),因此任何一个Web组件都可以访问它们。一个MessageResources对象可以包含多种本地化版本的资源文件的数据。
    Struts应用、子应用模块、Resource Bundle和资源文件之间存在以下关系:
    · 一个Struts应用可以有多个子应用模块,必须有且只有一个默认子应用模块。
    · 一个子应用模块可以有多个Resource Bundle,必须有且只有一个默认的Resource Bundle。
    · 一个Resource Bundle可以有多个资源文件,必须有且只有一个默认资源文件。
 
    下面介绍在Struts应用中访问Resource Bundle的途径。
<!--[if !supportLists]-->1.  <!--[endif]-->通过编程来访问Resource Bundle
在Action积累中定义了getResources(request)方法,它可以返回默认的Message Resources对象,代表当前应用模块使用的默认Resource Bundle。如果要活得特定的MessageResources对象,可以调用Action基类的getResources(request, key)方法,其中参数key和Struts配置文件中的<message-resources>元素的key属性对应。得到了MessageResources对象后,就可以通过它的方法来访问消息文本。
Org.apache.struts.util.MessageResources的getMessage()方法有好几种重载形式,下面列出常用的几种:
· 根据参数置顶的Locale检索对应的资源文件,然后返回和参数key对应的消息文本:
getMessage(java.util.Locale locale, java.lang.String key)
· 根据参数指定的Locale检索对应的资源文件,然后返回和参数key对应的消息文本,args参数用于替换复合消息文本中的参数:
getMessage(java.util.Locale locale, java.lang.String key, java.lang.Object[] args)
· 根据默认的Locale检索对应的资源文件,然后返回和参数key对应的消息文本:
getMessaeg(java.lang.String key)
<!--[if !supportLists]-->2.  <!--[endif]-->使用和Resource Bundle绑定的Struts组件
Struts框架中的许多内在组件和Resource Bundle是绑定在一起的,如:
· ActionMessage类和<html:errors>标签。
每个ActionMessage实例代表Resource Bundle中的一条消息。在调用ActionMessage的构造方法时,需要传递消息key。
对于复合消息,在创建ActionMessage对象时,可以调用带两个参数的构造方法:ActionMessage(java.lang.String key, java.lang.Object[] values),values参数用户替换复合消息中的参数。
在JSP页面中,使用<html:errors>标签,就能读取并显示ActionErrors集合中所有ActionMessage对象包含的消息文本。
· Struts Bean标签库的<bean:message>标签。
        Struts框架包含了一些可以访问应用的消息资源的客户化标签,其中使用最频繁的一个标签为<bean:message>标签。<bean:message>标签从应用的Resource Bundle中获取消息字符串。
        <bean:message>有一个bundle属性,用于指定被访问的Resource Bundle,它和<message-resources>元素的key属性匹配。如果没有设置bundle属性,将访问默认的Resource Bundle。
· 在Validator验证框架中访问Resource Bundle。
· 在声明型异常处理中访问Resource Bundle。
 
5 异常处理的国际化
在处理异常时,也应该考虑对I18N的支持。除非已经对应用抛出的异常消息做本地化,否则不应该直接向终端用户展示原始的异常消息。不懂Java语言的终端用户很难理解从Java虚拟机堆栈中抛出的Java异常消息,如果这些异常消息使用的不是用户的本地语言,那就更加会让用户困惑不解。
应该先把异常捕获,对异常消息进行本地化后,再把它展示给用户。Struts的Resource Bundle和ActionMessage类可以完成这一功能。直接向终端用户显示Java异常绝对是不可取的。即使发生了无法恢复的系统错误,也应该向用户显示本地化的系统错误页。
 
6 小结
本文档介绍了软件的本地化与国际化的概念,然后消息介绍了对Struts应用实现国际化的原理和方法。与国际化密切相关的两个组件是Locale和Resource Bundle:
· Locale:包含了用户的本地化信息,如语言和国家。
· Resource Bundle:包含了多个消息资源文件,每个消息资源文件存放和一种Locale相对应的本地化消息文本。
Struts框架的初始化时,把Resource Bundle(即MessageResources对象)存储在application范围内;在响应用户请求时,把包装用户Locale信息的Locale实例存储在session范围内。Struts框架能自动根据这一Locale实例,从Resource Bundle中检索相应的资源文件,再从资源文件中读取本地化的消息文本。
对Struts应用实现国际化应该遵循以下原则:
· 尽量不在Servlet中使用含非英文字符的常量字符串。
· 对于JSP文件,应该对page指令中的charset属性进行相应的设置。
· 不要在JSP文件中直接包含本地化的消息资源,儿应该把消息资源存放在Resource Bundle的资源文件中。
· 不比在每个JSP或Servlet中设置HTTP请求的字符编码,可以在Servlet过滤器中设置编码:
   HttpServletRequest.setCharaterEncoding(String encoding);
· 尽量使用“UTF-8”作为HTTP请求和响应的字符编码,而不是“GBK”或“GB2312”。
· 充分考虑底层数据库所使用的编码,它可能会给应用程序的移植带来麻烦。
02 septiembre

XML的二十个热点问题

这些日子,几乎每个人都在谈论XML (Extensible Markup Language),但是很少有人真正理解其含义。XML的推崇者认为它能够解决所有HTML不能解决的问题,让数据在不同的操作系统或应用之间进行灵活交换。确实,所有的观察家们都同意XML将引发一场内容发布和知识交换的革命。谁先进入这个领域,谁就能够大获其利。

这里的20个有关XML的热门问题能够让你成为一XML“专家”,或至少让你能够在今后看准XML的发展方向。

1
什么是XML?
11
OSD和CDF与XML的关系如何?
2
XML何以重要?
12
电子商务(e-commerce)和XML?
3
SGML、HTML和XML有什么联系?
13
XML中的层叠样式?
4
如何实现XML?
14
XML如何改进超链接?
5
什么是文件类型定义(DTD)?
15
服务器上支持XML吗?
6
什么是格式完整和有效的文件?
16
谁应该学习XML?
7
如何在浏览器中阅读XML?
17
有哪些编写XML的工具可供我使用?
8
RDF和XML有何联系?
18
XML的国际化?
9
Netscape浏览器中如何实现XML?
19
XML的未来在哪里?
10
Microsoft浏览器中如何实现XML?
20
哪里能学到更多的XML知识?


1.什么是XML?

XML代表扩展标识语言(Extensible Markup Language). 由万维网联盟(W3C)带头, XML在1998年2月10日成为正式的规范.

XML开发者会告诉你XML不是一种语言,而是一个定义其他语言的系统. 你可能已经听说过, 或使用过这些语言中的一种,--如Microsoft支持"推技术"的 Channel Definition Format(CDF).

正从事于XML相关建议工作的W3C, 称XML为"表达数据中结构 的共同语法". 结构化的数据指的是其内容,意义或应用被标记的数据. 例如, HTML中<H1>标记指定文本为某一字体和大小, XML的标记将明确确定信息的种类: <BYLINE>标记可以识别文档的作者, <PRICE>标记可以在一个存货清单中包含某一项目的成本 .

通过将结构,内容和表现分离, 同一个XML源文档只写一次, 可以用不同的方法表现出来: 在计算机屏幕上, 在手提电话显示屏上, 在为盲人服务的设备上翻译成语音, 等等. 它可以在可能开发的任何通讯产品上工作. 一个XML文档因此可以比其书写时的作者和显示技术生存得更久.

所以, XML将不仅限于Internet, 例如, 可以服务于整个出版业, 特别是对于想制作可出现在多种媒体上的文档的人. 一些使用Standard Generalized Markup Language (SGML)多年的大型文档出版商将转向XML. 还有, 独立于平台的XML是为Web开发的, 这是它将最具影响的地方.

XML在Web的真正实力在于它是如何与文档对象模型(Document Object Model,DOM)交互的. DOM定义了访问XML文档数据的接口.

程序员利用DOM可以用标准的方法编写动态的内容. 换句话说, 他们可以使用它来使浏览器文档树中的一部分特定内容按照一定的方式表现, 例如, 当用户将鼠标移至文字上时, 这些文字变成蓝色. Netscape Navigator 和 Microsoft Internet Explorer浏览器都有各自的DOM, 但是他们都称将在其下一版本的浏览器中支持W3C 的标准DOM.

2.XML何以重要?

Web领袖之间的说法是内容至上.可是不幸地是:内容经常和其表现紧密结合.请问你多少次在网页中遇到一个小小的提示:"最好在800x600像素的分辨率上显示"?

XML将帮助解决以上问题, 因为网站建设者不用再指明在哪里显示什么, 而是指明文档 的结构. 例如, 你可以说明文档的标题, 作者, 关联文档的清单, 等等. 然后, 任何一个有XML浏览器的设备都可以给出最适合它的文档版本, 这样的设备可以是一个掌上型计算机, 置顶盒, 或高速的工作站.

但是, 也许XML的最佳特性是其内在的可扩展性. 公司和组织能够扩展XML来满足新的挑战和应用. 一个基于XML的语言已经在使用--微软的Channel Definition Format (CDF)-- 还有更多将出现, 包括 Resource Definition Format (RDF) 和 Open Software Description (OSD).

XML 也允诺成为交换数据 和文档的标准机制. 例如, XML可能成为不同厂商的数据库在Internet上交换信息的一种方法.现在准确地决定XML的方向还有一些早. 但是, 其各种可能性是令人敬畏的,这就是为什么围绕着XML有如此多的激动的一个重要原因.

3.SGML、HTML和XML有什么联系?

SGML是在文字处理应用中表达数据的一个方法. 它已经出现十多年了, XML和HTML都是从SGML 发展而来的文档形式. 因此, 它们都有一些共同点, 如相似的语法和标记的使用.但是HTML是SGML的一个应用, 而XML是SGML的一个子集. 区别是重要的. 基本上HTML不能用来定义新的应用, 而XML可以. 例如,RDF和CDF都是使用XML定义的应用. XML和HTML更象表兄弟, 而不是亲兄弟. 事实上, XML和SGML是兼容的 -- XML文档可以通过任何SGML制作或浏览工具阅读. 但是, XML没有SGML那么复杂, 它是设计用于有限带宽的网络的, 如Internet. XML规范的合作者Tim Bray说, XML的设计出发点是取SGML的优点, 去除复杂的部分, 使其保持轻巧, 可以在Web上工作.

HTML,SGML和XML将继续用于其合适的地方, 它们中的任何一个不会使其他一个废弃. HTML仍是在Web上快速出版数据的最简单的方法, 大部分短期的数据, 如会议议程或广告宣传册. 如果数据会长期使用, 并且需要更多的一些结构, Web建造者将愿意使用XML. 不同于HTML和XML, SGML可能永远不会在Internet上被广泛接受, 因为它从来没有为某个网络协议的需求而设计或优化过. 对于高端的, 复杂结构的出版应用, SGML将继续适用.

4.如何实现XML?

XML将以几个不同的方式应用. 一个是在人机之间交换数据, 如从Web服务器至用户的浏览器. 另外一个是在不同的应用之间交换数据, 或者是机器之间交换数据.

在这些情况下, 你都可能需要三层架构: 后端数据库, 针对数据的处理逻辑的中间层服务器, 以及数据进一步显示和处理的客户端. 数据库可以从多个数据来源接收信息, 可能已经是XML格式的数据. 中间层然后收集数据并在最终的表现层上输出和表现..

现在, 网页有时候以这种方法传送 --CNET的NEWS.COM从一个数据库中发表数据.但是要获得一页的新的视图,如NEWS.COM的新的“打印机友好”选项, 服务器必须产生一个新的页面. 一份适当格式化的XML文档将允许客户端的应用为不同的媒体修改文档的表现形式, 比如为打印机.

5.什么是DTD?

文档类型定义(DTD)是一套关于标记符的语法规则.它告诉你可以在文档中使用哪些标记符,它们应该按什么次序出现,哪些标记符可以出现于其它标记符中,哪些标记符有属性,等等.DTD原来是为使用SGML开发的, 它可以是XML文档的一部分, 但是它通常是一份单独的文档或者一系列文档 因为XML本身不是一种语言,而是定义语言的一个系统,它没有象HTML一样拥有一个通用的DTD.相反, 想使用XML进行数据交换的工业或组织可以定义它们自己的DTD. 如果一个组织想用XML来标识仅在内部使用的文档, 它可以创造自己私有的DTD. 比如 华尔街杂志交互版本拥有一个 DTD 来详细说明每一版, 其中有关于页, 文章, 概要, 标题下署名等等的信息.刊物目前使用SGML DTD,但是它也正在开发一个XML版本. 关于DTD并不是没有争议的. 一些人感到它给商业业务增加了实实在在的价值, 而一些人感觉它限制了创造性. 还有一些人认为DTD有用, 但是还做得不够. 微软正尝试用它的XML数据提议来解决上一个抱怨, 但是批评者说这些改进应该在DTD规范本身进行.

一些供应商, 包括微软, 已经提议了替代DTD的一个方法, 称为schema. 他们已经将其以XML数据提交给了W3C. 就象DTD, Schema提供了文档的规则, 并指出用什么标记符, 标记符的属性, 之间的联系, 等等.但是,不同于DTD, schema可以定义数据类型. 例如, DTD可能有一个标记符 <PRICE>, 而标记符之间的内容可以是数字或字符串. Schema 可以规定只输入数字.这个方法显然有其优点, 特别是用于应用,对象,或数据库之间的数据传输. 唯一的问题是它将成为DTD规范, 还是XML的一个扩展.

6.什么是结构良好和有效的文件?

基本上有两类相关的XML文档: 结构良好的和有效的. 结构良好的 XML文档遵守XML语法的一般规则, 这些规则比HTML和SGML的更为严格. XML的字符数据决不会吊在那里, 没有某种结束标识符, 或者是象<MYTAG></MYTAG> 成对出现的 结束标识符, 或者是一个特别的在右尖括弧前带有一个斜杠的 空元素 标记, 比如 <MYTAG/>; XML 标识总是以左尖括弧或 & 开始; 元素类型和属性名称是大小写区分的; 属性需要引号; 等等.

有效的 XML 文档遵守某个特定的DTD.确认XML文档正确性的工作主要由制作出版工具承担, 而XML浏览器为读取XML文档, 只需要检查其构造的良好性. 这样, 制作工具中的解析器得要检查构造良好性 有效性, 而浏览器仅要考虑寻找已经构造良好的XML.

7 如何在浏览器中阅读XML?

阅读XML文档的工具一般称为XML解析器, 虽然其更正式的名称是XML处理器. XML处理器将数据传送到应用软件, 以做制作, 出版, 查询, 或显示. XML不给应用软件提供 应用程序接口 (API), 它只是把数据传给应用软件. XML处理器不解析非结构良好的数据. NetscapeMicrosoft 都已经将XML解析器包含在其浏览器中, 或正计划将其包含到浏览器中.

XML开发者团体提供免费的XML阅读器和解析器, 来应用到应用软件或XML制作软件:

  • Textuality的 Lark, 来自XML标准的作者之一.

  • Microstar的 AElfred, 一个基于Java的解析器.

  • DataChannel的 DXP, 前身为著名的NXP, 或已经增加了API的Norbert的 (Mikula) XML 解析器.

8 RDF和XML有何联系?

如果XML提供了表达语言的能力,那么XML应用则是特定的语言.资源描述框架(Resource Description Framework,RDF) 是这样的一个应用软件:使用XML的语法进行数据建模.

RDF是一种描述和访问数据方法. 这意味着RDF是关于数据的数据, 或者说元数据. 在Web中, 这些元数据将被用于建立标准的站点地图, 更精确的搜索结果, 和分层次的主题索引. RDF也允许智能书签, 当被索引的网页变化时, 书签随之发生变化. 如果你跟踪内容定期更新的站点, 比如CNET的 NEWS.COM, 将很有用.对于网站建设者,建立可被搜索引擎引用的其网站内容的元数据并不困难. 我们很快就会有商业化的软件, 来自动产生给定站点的RDF文件.

XML元数据也将活跃数据描述和评估的市场. 有许多评级机构在网上出现, 他们评估一切数据, 从保护孩子安全的站点到最佳电影或葡萄酒站点. RDF可以使用的等级的语法来描述评级机构.人们将选择有他们感觉最合适的词汇表的评级机构,词汇表指的是评级机构给不同类型内容评级使用的特别的一套术语 -- 从性和暴力到葡萄酒酸度.

9 Netscape浏览器中如何实现XML?

Netscape将在Communicator/Navigator 5.0中以一个代号为Aurora的交付部件来支持XML元数据. Aurora利用RDF来获得Netscape所称的"桌面信息全面集成."

Aurora在网络、桌面和数据库之间查找和管理信息.它将在桌面上以"窗口"菜单的界面出现,会聚指向当前项目, 研究主题或日常活动等资源的指针.RDF使Aurora的导航条指向不同数据类型(文字处理文档、表格数据、电子邮件消息、数据库内容)的本地文件, 也指向Internet 或Intranet 服务器上的资源(搜索和查询的结果、书签链接等).

Netscape 5.0 版浏览器中提供了一个读取RDF的XML解析器,在产品最终交付前会以beta版出现.除了该RDF的实现, Netscape正计划将一个通用的XML解析器包含在浏览器中, 而其浏览器可以和其他的XML应用软件一起工作, 比如化学标记语言(CML)和数学标记语言(MathML).

"我们要使Navigator成为一个XML平台,"Netscape原理工程师R.V. Guha这样说.Guha 原来开发过MCF (Meta Content Format), MCF 后来加入了RDF规范.

10 Microsoft浏览器中如何实现XML?

微软的 Internet Explorer 4.0 是第一个实现XML的网络浏览器. 微软提供了一对XML处理器:浏览器所携带的用C++写的解析器, 和一个Web建造者可以从中下载和加入他们自己的应用程序的Java解析器的源代码. Java解析器是一个有效的 解析器, 就是说它根据一个DTD或Schema来进行检查.为了提高性能,浏览器所带的C++版的解析器是一个非有效的解析器.

据微软的产品经理Steve Sklepowich称, 这两个解析器都是"通用的", 因为它们不依靠特定的XML应用, 如CDF.由于XML数据和其表现分离, 在一个浏览器本身实际显示XML的能力需要样式表,例如XSL.

同时, 微软使用了它所称的XML数据源对象(XML Data Source Object,XML DSO).它应用了动态HTML的数据捆绑能力, 将一端的XML数据和另一端的HTML数据相链接.IE 4.0访问XML文档,从中查询数据, 然后作为HTML显示出来.

微软也使用了 XML对象模型来让开发者与浏览器中的XML数据进行交互. 它的实现是通过将HTML作为基于文档对象模型(DOM)的对象显现, 尽管HTML 和 DOM 并非直接兼容. DOM 让脚本和程序访问结构化的XML数据.

Sklepowich说, 虽然目前在微软, XML的重心在浏览器, XML将最终出现在"任何HTML已经出现了的地方".Bill Gates 已经公开宣布微软Office 未来的版本将支持 XML, 而且公司也计划支持电子邮件包和制作XML工具的标准.

11 OSD和CDF与XML的关系如何?

CDF和OSD是微软支持的两个XML应用.通过其XML解析器,微软的 Internet Explorer 4.0读CDF文件来驱动和控制推频道所带来的页.根据RDF所做的工作, CDF提议又递交给W3C,以利用RDF的能力来显示不同数据元素之间的联系.

Open Software Description 是用于描述软件部件的词汇表, 带有语法如从属, 版本和平台. OSD 描述如何表现一个部件的特性, 以及如何将该部件安装到计算机上. 它可以用于下载一个完整的软件包, 但是它主要设计用于不断增加的更新. OSD 单独工作或和CDF一起工作, 来定义应用频道. OSD建议由微软和Marimba领导的一组销售商于1997年8月提供给W3C.

12 电子商务(e-commerce)和XML?
CommerceNet是著名的非赢利性网上商务协会,它拥有500多个成员.
数年来CommerceNet努力帮助e-commerce 产品和系统一起工作. 其概念是允许信息在不同目录之间, 从目录到付款系统, 在付款系统之间交换. 已经发现XML可以在两个重要方面帮助实现以上概念: 内容定义和信息交换.

内容定义:CommerceNet 正在定义通用于多种商业事务的数据元素. 这个称作商务核心(Commerce Core)的东西将定义如何给诸如公司名称、地址、价格、条款和数量等事物作标识.

信息交换:开放,基于文本的XML用于服务器之间交换事务信息很理想.CommerceNet 提议用基于XML的通用商务语言(Common Business Language,CBL)来描述产品和服务目录软件, 关于商业规则和系统的元数据, 以及表格和消息的软件. 许多CBL 取自已经存在的 Electronic Data Interchange (EDI) 辞典, EDI辞典识别公认的术语, 如发票和采购订单. 但是CBL超越EDI的商业-到-商业的重点, 包含了零售事务和横向的供应链 -- 从供应商到批发商到零售商.

这样的一个CBL应用是为使目录互用的产品信息互换( Product Information Exchange,PIX)规范. CommerceNet设计PIX, 以帮助供应商和他们的分销商更容易地交换产品数据. 长远的目标是工业组织--而非 CommerceNet--来将CBL作为特定的DTD的一个共同基础使用. 一些着重于工业的初步尝试已经宣布了:

Internt开放支付(OBI): 一个在Internet上进行国际性的商业间购物的标准.OBI基于目前的Internet标准, 如SSL(安全性)、SET(信用卡交易)和X.509(数字认证). OBI的支持者有Commerce OneConnectIntelisysInterWorldMicrosoftNetscapeOpen Market、和Oracle.

开放贸易协议(OTP): 一个在Web上向消费者售物的一致的, 可共同操作的环境. 规则将包括从如何降价促销, 付款选择, 到产品运输, 接收和问题解决. OTP由MasterCard International, DigiCash, CyberCash, Hewlett-Packard, IBM, AT&T Universal Card, Netscape, Royal Bank of Canada, 和一些 其他金融机构和技术公司支持.

Internet内容交换标准(ICE): Vignette, Firefly Network, 和 一些其他公司--包括微软--正在开发一个叫作ICE的规范, 使能够在站点之间交换在线资产, 无论那是内容, 应用程序, 或是元数据. ICE将利用现有的标准, 包括 OPS/P3P (使个人数据可靠交换), CDF,OSDRDF.

13 XML中的层叠样式?

因为XML将内容和表现分离, Web建造者需要新的方法来控制设计, 显示和输出. Style sheet 是问题的答案. 目前, 有三种可用于XML的 样式表:

如果5.0版的浏览器支持XML, XML对现有的CSS标准的支持将会处理大部分基本的风格和页面问题. 但是CSS对于专业出版商可能不够强大. 所以, 另一端存在着DSSSL, 一个在使用SGML的高端出版商中流行的ISO (国际标准组织)标准. 然而, DSSSL是复杂的, 它处理的打印文档管理在Web上很少有用.

现在剩下了XSL,特别为XML而写的样式表.XSL目前上交给了W3C作为一个建议标准,其中的XSL转换部分(XSLT)已经于1999年11月成为正式的规范. 它给了Web开发者和用户较HTML更多的表现灵活性. 例如, HTML的<H2>标识符在所有浏览器上的表现是基本一样的, 但是XSL让开发者指定他们的页面元素如何表现(尽管用户可以在个人设置中重载它).

XSL较CSS更强大, 因为它使Web建造者创建可以动态改变其表现的文档. 例如, 你可以包含这样的程序语句, "如果一个XML元素的属性为数值10, 显示为绿色, 否则为黑色." 或者你可以将"仅供内部使用"作为属性给一个段落标上, 这样它在某些情况下不会出现. XSL被设计用于脚本语言如JavaScript.

14 XML如何改进超链接?

XML超链接比基本的HTML风格的超链接多了一些新的特性, 包括无需手写许多JavaScript代码就能创建"聪明的"链接. 而且在XML, 链接本身成为了对象, 可以象其他对象一样被管理.原来的链接规范--XLL, 或XML链接语言--正被分为两个不同的规范: XPointer 和 XLink.

XPointer: 在HTML, 要链接到一个页面的中间, 页面作者必须在那儿加上定位标识符. 使用XPointer, 你可以"取址到" (不是"连接到")其他人的文本的任何部分. 显而易见, 这样将有助于工作于法律文件, 科学和学术论文, 甚至W3C规范!

XLink: 当用户点击一个HTML超链接时, 当前的网页被连接到的文件替代. XLink令Web建立者给链接增加行为. 例如, 现在, 你必须用一些JavaScript, 使在链接处弹出一个独立的窗口, 但是XLink让Web建立者对链接进行编码来执行一系列动作, 包括弹出一个链接选择的菜单.

另一个应用可以是弹出一个对话框, 可能是一个提醒用户它们正要更新数据库的警告. 链接弹出菜单可能需要用户点击一个框来表示在进一步处理前他们接受义务. 现在, 实现这样的功能要写许多的脚本代码.

XML也让Web建立者创建类似Web环工作的Extended Link, Web环是通过"下一个/前一个"行进来导航的自己选择出来的关于相同主题的网站组. 对于弹出菜单太长的相关链接站点表, Web建立者可以创建一个链接表, 这个表在不同的站点, 页面时会有变化. 用户可以点击一个图标来自动转移到环中的下一个成员. 现在这样的功能需要 CGI scripts, 而Extended Links 提供了一个标准的, 非私有的建立资源间联系的方法.

仍然有更多的问题需要解决, 特别是在行为政策方面. 必须由一种方法来协调以下三方面的关系: 文档作者对链接所建议的行为, 用户所喜好的显示链接信息的方式, 以及是否和何时忽视用户的意愿的政策.

15 服务器上支持XML吗?

XML被设计成供长久使用的, 高价值的文档的储存格式. XML不是只让你定义标识符, 它也允许你定义文档的储存结构. 一篇HTML文档仅存在于一个文件中, 而一个XML文档可以由存放在不同地点的多个文件(称为实体)组成. 这提出了作为文档存储库的XML服务器的概念.

服务器软件供应商已正在支持XML:

Enigma, Insight 4.0
这是一个提供给出版商处理大型文档的专业电子出版软件解决方案. 目前和Insight捆绑在一起的Enigma SGML/XML Style Sheet Editor, 也可以作为一个独立产品提供.

Hynet Technologies, Digital Library System
Digital Library System (DLS) 将文档和文档部件作为标准软件对象进行管理, 允许引入在Adobe FrameMakerMicrosoft Word中创建的文档, 或者SGML/XML文件.

Inso, DynaText Professional Publishing System
这是一个进行索引, 搜索和制作脚本的软件, 它可以工作于运行在Windows NT 3.51 或 4.0, 或 Sun Solaris 2.5上的Microsoft的 IIS和 Netscape的 Enterprise and FastTrack servers.

Open Market, Folio
Open Market的 Folio 4 信息管理和发送产品将XML文档引入带索引的数据库, 以在IP网络上传递内容, 或将内容送至CD-ROM. 在一月份, Open Market宣布增强对XML的支持, 允许文档以它们的本身格式进行索引和保证安全. 同时, Folio产品也将可以和其它基于标准的制作, 解析和生成XML文档的解决方案相互操作. 它的产品包括 Folio siteDirector (分发信息), Folio SecurePublish (事务管理软件), 和 Folio Publisher (电子出版).

WebMethods, Web Automation Server
Web Automation Server 帮助公司将基于浏览器的应用软件和其它应用软件的数据相结合. 它是基于XML的服务器, 使用 WebMethods自己的在机器间进行Web数据交换的WIDL (Web Interface Definition Language). (该公司已经将WIDL作为标准提议上交给 World Wide Web Consortium.)

16 谁应该学习XML?

所有的Web建立者需要足够了解XML,以决定是否使用它.E-commerce站点和管理数据库中大量文档的站点是显然的首选对象.经理可能不需要学习XML语法或如何建立DTD,他们仍要理解XML的潜力并加以利用.如果最终的目的只是让人来读信息, HTML能足够满足标识信息的要求. 但是如果你想要为自动处理数据作准备, 你必须考虑将XML纳入你的出版系统. 并非每一个工作在Web站点的HTML制作者必须成为XML制作者, 但是某些员工应该精通于XML--特别当站点的工作对象是值得为将来使用而管理的数据和文档时.当然,XML的功能也意味着复杂性--一些Web建立者已经发现他们可以在几天内掌握HTML的基础, 而他们可能需要花几个星期来适应XML.只有你自己才能决定是否值得花这些时间.

17 有哪些编写XML的工具可供我使用?

幸运的是, Web建立者不用完全靠他们自己从头开始创建XML了. 市场上已经有了创建, 管理和发送XML的工具, 并且一些公司也在进行开发.

Adobe: 在1998年中期, Adobe将介绍可以输出到XML的 FrameMakerFrameMaker+SGML 的过渡版本. 这些产品的完整版本将能够输入XML. Adobe有 一名代表在 W3C的XML工作组, Adobe也参与了XLink, Cascading Style SheetsRDF的工作, 所以我们可以期待这些技术将在Adobe未来的产品中出现.

Allaire: HomeSite 4.0 和 Cold Fusion 4.0 都预计在今年夏天出品, 它们将支持XML, 包括style sheets. HomeSite 3.0中已经提供了一个CDF附加软件.

ArborText:SGML领域内长期工作的ArborText, 于一月份发布了XML Styler, 一个免费的基于Java的XSL编辑器. 它的图形用户界面可以让我们勿需知道XML语法就能进行编辑. 将来, ArborText 会把XML Styler集成到Adept中, Adept是公司给打印出版提供的XML制作工具.

DataChannel: 一个免费的, 基于Java的有效的解析器, 称为 DXP (DataChannel XML Parser; 基于 Norbert Mikula 著名的NXP). 可以从该公司的Web站点获得. 它新发布的是免费的XML工具包, XML 开发环境, 它包括了一套部件, 帮助人们开始学习和应用XML.

Inso: 该公司提供它称为的"首个集成的, 端对端的, 创建, 转换, 存储, 管理, 索引, 查询XML内容, 将其发布到Web, CD-ROM和打印机上的出版解决方案." 其产品包括 DynaTag 4.0, DynaBase 3.0, DynaText 3.1, 和所附的工具 DynaWeb.

IntraNet Solutions: Intra.doc Management System的下一版本, IntraNet Solution的基于Web的文档管理系统, 将会管理XML部件和文档之间的关系, 提供和第三方XML制作工具的集成链接管理, 完善在浏览器中XML对象的使用, 并在Intra.doc存储库和XML编辑器之间提供一个交互的元数据模型.

Microsoft: 微软希望在年底交付Office 9.0, 据报道, 它将有对XML的支持.

Microstar: ActiveSG/XML 是一套在Internet上设计和配置基于事务的XML/SGML系统的工具和技术. Microstar也提供了免费的Ælfred XML 解析器.

SoftQuad: HTML 编辑器 HotMetal Pro 将很快提供 Live Data Base Pages, 一个让开发者将HTML数据拉入数据库并以XML来返回的附加软件.

Vignette: StoryServer 3.2 在Web上交付能使用XML的应用和内容. 它结合了关系型数据库, 多媒体和XML内容创建的工具. StoryServer 是一个Web内容应用平台, 供建立, 管理, 和交付基于服务的Web应用之用, 比如在线出版, 知识管理, 和电子商务系统. (注: CNET对Vignette有财政上的兴趣.)

XPublish: XPublish是一个XML出版系统, 供Web站点开发和管理, 允许开发者以XML进行制作, 或用XML构件延展目前的HTML文档, 然后将站点以HTML来发布, 以让任何的标准Web浏览器可以访问. 它包含了一个Cascading Style Sheets编辑器.

WebMethods: 该公司制作基于XML的Web Automation 软件, 提供商业应用软件和Web数据的快速集成和直接访问. 它的 Web Interface Definition Language (WIDL) 将和HTML/XML和表格的所有交互自动化, 提供在标准Web协议上表现要求-回答交互方式的一个通用方法.

当然, 如果XML在Web上无处不在, 你可以看到几乎每一种基于Web的应用, 特别是HTML编辑器, 数据库软件, 和电子商务软件, 会迅速加入对XML的各个级别的支持.

18 XML的国际化?

XML将使Web建立者创建真正的国际性站点变得比以前都容易. 因为, 比如 Java, 它由Unicode (ISO 10646)定义, Unicode是一个国际接受的标准, 用于描述世界上所有的字母, 字型, 字符和表意符, 它包括 ASCII ISO 拉丁字符, 日本, 韩国, 中国, 印度, 希腊, 阿拉伯和其它字符. 它甚至允许字符集的混合, 例如, 一篇以日文显示的XML文档可以通过一个元音变音参照到一个德文词汇.

开发者不需要学习任何脚本语言, 以使Unicode在XML文档中生效, 文档在用户的浏览器中用合适的字符集显示出来.

19 XML的未来在哪里?

就围绕着XML的所有活动, 很难预测六个月后它会是什么样. XML和XLL 规范的合作者Tim Bray说, "我们已经制作了一个设计用于通用目的的工具, 大范围的人们对它的关注和应用证明我们是成功的."

在短期内, XML将可能出现在元数据应用上,如RDF.下一个大的影响将随着文档对象模型规范的批准而到来.Bray声称"XML和DOM的结合确实是给Web带来活力的神奇子弹."

XML也应该会帮助电子商务.XML将使电子商务供应商以共同的方法给产品和其相关信息(价格,尺寸,颜色,特性)进行标识, 使用户在Web上对不同的商店进行比较.

同时, Netscape和Microsoft会继续扩大对XML浏览器的支持, 包括有效的和构造良好的XML文档, 更多的XML应用, 为XML设计的style-sheet支持, 和XML超连接协议. 请关注这两个公司以及其它软件供应商在XML制作和出版工具上的进展.

20 哪里能学到更多的XML知识?

XML是一个复杂的事物, 对于所有Web建立者都具有深刻的含意. 如果你想学习更多的东西, 这里是一些可以查看的好地址:

万维网联盟(W3C):

XML常见问题解答:

  • 微软的XML 站点提供了XML如何在一个天气预报和拍卖中工作的演示.

  • Textuality收集了XML规范合作者Tim Bray的关于XML的FAQ和到其它资源的一些链接.

  • 由爱尔兰Cork University College的Peter Flynn代表W3C的XML特别兴趣组维护的XML FAQ.

  • XML信息页面, 达拉斯Summer Institute of Linguistics 的Robin Cover创建的SGML/XML Web页面的一部分.

组织、持续教育:

TOCMAT的web.xml详解

1 定义头和根元素

部署描述符文件就像所有XML文件一样,必须以一个XML头开始。这个头声明可以使用的XML版本并给出文件的字符编码。
DOCYTPE声明必须立即出现在此头之后。这个声明告诉服务器适用的servlet规范的版本(如2.2或2.3)并指定管理此文件其余部分内容的语法的DTD(Document Type Definition,文档类型定义)。
所有部署描述符文件的顶层(根)元素为web-app。请注意,XML元素不像HTML,他们是大小写敏感的。因此,web-App和WEB-APP都是不合法的,web-app必须用小写。

2 部署描述符文件内的元素次序

XML元素不仅是大小写敏感的,而且它们还对出现在其他元素中的次序敏感。例如,XML头必须是文件中的第一项,DOCTYPE声明必须是第二项,而web-app元素必须是第三项。在web-app元素内,元素的次序也很重要。服务器不一定强制要求这种次序,但它们允许(实际上有些服务器就是这样做的)完全拒绝执行含有次序不正确的元素的Web应用。这表示使用非标准元素次序的web.xml文件是不可移植的。
下面的列表给出了所有可直接出现在web-app元素内的合法元素所必需的次序。例如,此列表说明servlet元素必须出现在所有servlet-mapping元素之前。请注意,所有这些元素都是可选的。因此,可以省略掉某一元素,但不能把它放于不正确的位置。
l icon icon元素指出IDE和GUI工具用来表示Web应用的一个和两个图像文件的位置。
l display-name display-name元素提供GUI工具可能会用来标记这个特定的Web应用的一个名称。
l description description元素给出与此有关的说明性文本。
l context-param context-param元素声明应用范围内的初始化参数。
l filter 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。
l filter-mapping 一旦命名了一个过滤器,就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。
llistener servlet API的版本2.3增加了对事件监听程序的支持,事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。
lservlet 在向servlet或JSP页面制定初始化参数或定制URL时,必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。
lservlet-mapping 服务器一般为servlet提供一个缺省的URL:http://host/webAppPrefix/servlet/ServletName。但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时,使用servlet-mapping元素。
lsession-config 如果某个会话在一定时间内未被访问,服务器可以抛弃它以节省内存。可通过使用HttpSession的setMaxInactiveInterval方法明确设置单个会话对象的超时值,或者可利用session-config元素制定缺省超时值。
lmime-mapping 如果Web应用具有想到特殊的文件,希望能保证给他们分配特定的MIME类型,则mime-mapping元素提供这种保证。
lwelcom-file-list welcome-file-list元素指示服务器在收到引用一个目录名而不是文件名的URL时,使用哪个文件。
l error-page error-page元素使得在返回特定HTTP状态代码时,或者特定类型的异常被抛出时,能够制定将要显示的页面。
l taglib taglib元素对标记库描述符文件(Tag Libraryu Descriptor file)指定别名。此功能使你能够更改TLD文件的位置,而不用编辑使用这些文件的JSP页面。
l resource-env-ref resource-env-ref元素声明与资源相关的一个管理对象。
l resource-ref resource-ref元素声明一个资源工厂使用的外部资源。
l security-constraint security-constraint元素制定应该保护的URL。它与login-config元素联合使用
l login-config 用login-config元素来指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。
l security-role security-role元素给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。
l env-entry env-entry元素声明Web应用的环境项。
l ejb-ref ejb-ref元素声明一个EJB的主目录的引用。
l ejb-local-ref ejb-local-ref元素声明一个EJB的本地主目录的应用。

3 分配名称和定制的UL

在web.xml中完成的一个最常见的任务是对servlet或JSP页面给出名称和定制的URL。用servlet元素分配名称,使用servlet-mapping元素将定制的URL与刚分配的名称相关联。
3.1 分配名称
为了提供初始化参数,对servlet或JSP页面定义一个定制URL或分配一个安全角色,必须首先给servlet或JSP页面一个名称。可通过servlet元素分配一个名称。最常见的格式包括servlet-name和servlet-class子元素(在web-app元素内),如下所示:
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>

这表示位于WEB-INF/classes/moreservlets/TestServlet的servlet已经得到了注册名Test。给servlet一个名称具有两个主要的含义。首先,初始化参数、定制的URL模式以及其他定制通过此注册名而不是类名引用此servlet。其次,可在URL而不是类名中使用此名称。因此,利用刚才给出的定义,URL http://host/webAppPrefix/servlet/Test 可用于 http://host/webAppPrefix/servlet/moreservlets.TestServlet 的场所。
请记住:XML元素不仅是大小写敏感的,而且定义它们的次序也很重要。例如,web-app元素内所有servlet元素必须位于所有servlet-mapping元素(下一小节介绍)之前,而且还要位于5.6节和5.11节讨论的与过滤器或文档相关的元素(如果有的话)之前。类似地,servlet的servlet-name子元素也必须出现在servlet-class之前。5.2节"部署描述符文件内的元素次序"将详细介绍这种必需的次序。
例如,程序清单5-1给出了一个名为TestServlet的简单servlet,它驻留在moreservlets程序包中。因为此servlet是扎根在一个名为deployDemo的目录中的Web应用的组成部分,所以TestServlet.class放在deployDemo/WEB-INF/classes/moreservlets中。程序清单5-2给出将放置在deployDemo/WEB-INF/内的web.xml文件的一部分。此web.xml文件使用servlet-name和servlet-class元素将名称Test与TestServlet.class相关联。图5-1和图5-2分别显示利用缺省URL和注册名调用TestServlet时的结果。

程序清单5-1 TestServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to illustrate servlet naming
* and custom URLs.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &copy; 2002 Marty Hall; may be freely used or adapted.
*/

public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Test Servlet") +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>URI: " + uri + "</H2>\n" +
"</BODY></HTML>");
}
}


程序清单5-2 web.xml(说明servlet名称的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- … -->
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>
<!-- … -->
</web-app>

3.2 定义定制的URL
大多数服务器具有一个缺省的serlvet URL:
http://host/webAppPrefix/servlet/packageName.ServletName。虽然在开发中使用这个URL很方便,但是我们常常会希望另一个URL用于部署。例如,可能会需要一个出现在Web应用顶层的URL(如,http://host/webAppPrefix/Anyname),并且在此URL中没有servlet项。位于顶层的URL简化了相对URL的使用。此外,对许多开发人员来说,顶层URL看上去比更长更麻烦的缺省URL更简短。
事实上,有时需要使用定制的URL。比如,你可能想关闭缺省URL映射,以便更好地强制实施安全限制或防止用户意外地访问无初始化参数的servlet。如果你禁止了缺省的URL,那么你怎样访问servlet呢?这时只有使用定制的URL了。
为了分配一个定制的URL,可使用servlet-mapping元素及其servlet-name和url-pattern子元素。Servlet-name元素提供了一个任意名称,可利用此名称引用相应的servlet;url-pattern描述了相对于Web应用的根目录的URL。url-pattern元素的值必须以斜杠(/)起始。
下面给出一个简单的web.xml摘录,它允许使用URL http://host/webAppPrefix/UrlTest而不是http://host/webAppPrefix/servlet/Test或
http://host/webAppPrefix/servlet/moreservlets.TestServlet。请注意,仍然需要XML头、DOCTYPE声明以及web-app封闭元素。此外,可回忆一下,XML元素出现地次序不是随意的。特别是,需要把所有servlet元素放在所有servlet-mapping元素之前。
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/UrlTest</url-pattern>
</servlet-mapping>
URL模式还可以包含通配符。例如,下面的小程序指示服务器发送所有以Web应用的URL前缀开始,以..asp结束的请求到名为BashMS的servlet。
<servlet>
<servlet-name>BashMS</servlet-name>
<servlet-class>msUtils.ASPTranslator</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name>BashMS</servlet-name>
<url-pattern>/*.asp</url-pattern>
</servlet-mapping>
3.3 命名JSP页面
因为JSP页面要转换成sevlet,自然希望就像命名servlet一样命名JSP页面。毕竟,JSP页面可能会从初始化参数、安全设置或定制的URL中受益,正如普通的serlvet那样。虽然JSP页面的后台实际上是servlet这句话是正确的,但存在一个关键的猜疑:即,你不知道JSP页面的实际类名(因为系统自己挑选这个名字)。因此,为了命名JSP页面,可将jsp-file元素替换为servlet-calss元素,如下所示:
<servlet>
<servlet-name>Test</servlet-name>
<jsp-file>/TestPage.jsp</jsp-file>
</servlet>
命名JSP页面的原因与命名servlet的原因完全相同:即为了提供一个与定制设置(如,初始化参数和安全设置)一起使用的名称,并且,以便能更改激活JSP页面的URL(比方说,以便多个URL通过相同页面得以处理,或者从URL中去掉.jsp扩展名)。但是,在设置初始化参数时,应该注意,JSP页面是利用jspInit方法,而不是init方法读取初始化参数的。
例如,程序清单5-3给出一个名为TestPage.jsp的简单JSP页面,它的工作只是打印出用来激活它的URL的本地部分。TestPage.jsp放置在deployDemo应用的顶层。程序清单5-4给出了用来分配一个注册名PageName,然后将此注册名与http://host/webAppPrefix/UrlTest2/anything 形式的URL相关联的web.xml文件(即,deployDemo/WEB-INF/web.xml)的一部分。

程序清单5-3 TestPage.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>
JSP Test Page
</TITLE>
</HEAD>
<BODY BGCOLOR="#FDF5E6">
<H2>URI: <%= request.getRequestURI() %></H2>
</BODY>
</HTML>


程序清单5-4 web.xml(说明JSP页命名的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/TestPage.jsp</jsp-file>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> PageName </servlet-name>
<url-pattern>/UrlTest2/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


4 禁止激活器servlet

对servlet或JSP页面建立定制URL的一个原因是,这样做可以注册从init(servlet)或jspInit(JSP页面)方法中读取得初始化参数。但是,初始化参数只在是利用定制URL模式或注册名访问servlet或JSP页面时可以使用,用缺省URL http://host/webAppPrefix/servlet/ServletName 访问时不能使用。因此,你可能会希望关闭缺省URL,这样就不会有人意外地调用初始化servlet了。这个过程有时称为禁止激活器servlet,因为多数服务器具有一个用缺省的servlet URL注册的标准servlet,并激活缺省的URL应用的实际servlet。
有两种禁止此缺省URL的主要方法:
l 在每个Web应用中重新映射/servlet/模式。
l 全局关闭激活器servlet。
重要的是应该注意到,虽然重新映射每个Web应用中的/servlet/模式比彻底禁止激活servlet所做的工作更多,但重新映射可以用一种完全可移植的方式来完成。相反,全局禁止激活器servlet完全是针对具体机器的,事实上有的服务器(如ServletExec)没有这样的选择。下面的讨论对每个Web应用重新映射/servlet/ URL模式的策略。后面提供在Tomcat中全局禁止激活器servlet的详细内容。
4.1 重新映射/servlet/URL模式
在一个特定的Web应用中禁止以http://host/webAppPrefix/servlet/ 开始的URL的处理非常简单。所需做的事情就是建立一个错误消息servlet,并使用前一节讨论的url-pattern元素将所有匹配请求转向该servlet。只要简单地使用:
<url-pattern>/servlet/*</url-pattern>
作为servlet-mapping元素中的模式即可。
例如,程序清单5-5给出了将SorryServlet servlet(程序清单5-6)与所有以http://host/webAppPrefix/servlet/ 开头的URL相关联的部署描述符文件的一部分。

程序清单5-5 web.xml(说明JSP页命名的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>Sorry</servlet-name>
<servlet-class>moreservlets.SorryServlet</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> Sorry </servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


程序清单5-6 SorryServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to give error messages to
* users who try to access default servlet URLs
* (i.e., http://host/webAppPrefix/servlet/ServletName)
* in Web applications that have disabled this
* behavior.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &copy; 2002 Marty Hall; may be freely used or adapted.
*/

public class SorryServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Invoker Servlet Disabled.";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>" + title + "</H2>\n" +
"Sorry, access to servlets by means of\n" +
"URLs that begin with\n" +
"http://host/webAppPrefix/servlet/\n" +
"has been disabled.\n" +
"</BODY></HTML>");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}


4.2 全局禁止激活器:Tomcat
Tomcat 4中用来关闭缺省URL的方法与Tomcat 3中所用的很不相同。下面介绍这两种方法:
1.禁止激活器: Tomcat 4
Tomcat 4用与前面相同的方法关闭激活器servlet,即利用web.xml中的url-mapping元素进行关闭。不同之处在于Tomcat使用了放在install_dir/conf中的一个服务器专用的全局web.xml文件,而前面使用的是存放在每个Web应用的WEB-INF目录中的标准web.xml文件。
因此,为了在Tomcat 4中关闭激活器servlet,只需在install_dir/conf/web.xml中简单地注释出/servlet/* URL映射项即可,如下所示:
<!--
<servlet-mapping>
<servlet-name>invoker</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
-->
再次提醒,应该注意这个项是位于存放在install_dir/conf的Tomcat专用的web.xml文件中的,此文件不是存放在每个Web应用的WEB-INF目录中的标准web.xml。
2.禁止激活器:Tomcat3
在Apache Tomcat的版本3中,通过在install_dir/conf/server.xml中注释出InvokerInterceptor项全局禁止缺省servlet URL。例如,下面是禁止使用缺省servlet URL的server.xml文件的一部分。
<!--
<RequsetInterceptor
className="org.apache.tomcat.request.InvokerInterceptor"
debug="0" prefix="/servlet/" />
-->

5 初始化和预装载servlet与JSP页面

这里讨论控制servlet和JSP页面的启动行为的方法。特别是,说明了怎样分配初始化参数以及怎样更改服务器生存期中装载servlet和JSP页面的时刻。
5.1 分配servlet初始化参数
利用init-param元素向servlet提供初始化参数,init-param元素具有param-name和param-value子元素。例如,在下面的例子中,如果initServlet servlet是利用它的注册名(InitTest)访问的,它将能够从其方法中调用getServletConfig().getInitParameter("param1")获得"Value 1",调用getServletConfig().getInitParameter("param2")获得"2"。
<servlet>
<servlet-name>InitTest</servlet-name>
<servlet-class>moreservlets.InitServlet</servlet-class>
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>2</param-value>
</init-param>
</servlet>
在涉及初始化参数时,有几点需要注意:
l 返回值。GetInitParameter的返回值总是一个String。因此,在前一个例子中,可对param2使用Integer.parseInt获得一个int。
l JSP中的初始化。JSP页面使用jspInit而不是init。JSP页面还需要使用jsp-file元素代替servlet-class。
l 缺省URL。初始化参数只在通过它们的注册名或与它们注册名相关的定制URL模式访问Servlet时可以使用。因此,在这个例子中,param1和param2初始化参数将能够在使用URL http://host/webAppPrefix/servlet/InitTest 时可用,但在使用URL http://host/webAppPrefix/servlet/myPackage.InitServlet 时不能使用。
例如,程序清单5-7给出一个名为InitServlet的简单servlet,它使用init方法设置firstName和emailAddress字段。程序清单5-8给出分配名称InitTest给servlet的web.xml文件。
程序清单5-7 InitServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to illustrate servlet
* initialization parameters.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &copy; 2002 Marty Hall; may be freely used or adapted.
*/

public class InitServlet extends HttpServlet {
private String firstName, emailAddress;

public void init() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}

public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Init Servlet") +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>Init Parameters:</H2>\n" +
"<UL>\n" +
"<LI>First name: " + firstName + "\n" +
"<LI>Email address: " + emailAddress + "\n" +
"</UL>\n" +
"</BODY></HTML>");
}
}


程序清单5-8 web.xml(说明初始化参数的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>InitTest</servlet-name>
<servlet-class>moreservlets.InitServlet</servlet-class>
<init-param>
<param-name>firstName</param-name>
<param-value>Larry</param-value>
</init-param>
<init-param>
<param-name>emailAddress</param-name>
<param-value>Ellison@Microsoft.com</param-value>
</init-param>
</servlet>
<!-- ... -->
</web-app>

5.2 分配JSP初始化参数
给JSP页面提供初始化参数在三个方面不同于给servlet提供初始化参数。
1)使用jsp-file而不是servlet-class。因此,WEB-INF/web.xml文件的servlet元素如下所示:
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/RealPage.jsp</jsp-file>
<init-param>
<param-name>...</param-name>
<param-value>...</param-value>
</init-param>
...
</servlet>
2)几乎总是分配一个明确的URL模式。对servlet,一般相应地使用以http://host/webAppPrefix/servlet/ 开始的缺省URL。只需记住,使用注册名而不是原名称即可。这对于JSP页面在技术上也是合法的。例如,在上面给出的例子中,可用URL http://host/webAppPrefix/servlet/PageName 访问RealPage.jsp的对初始化参数具有访问权的版本。但在用于JSP页面时,许多用户似乎不喜欢应用常规的servlet的URL。此外,如果JSP页面位于服务器为其提供了目录清单的目录中(如,一个既没有index.html也没有index.jsp文件的目录),则用户可能会连接到此JSP页面,单击它,从而意外地激活未初始化的页面。因此,好的办法是使用url-pattern(5.3节)将JSP页面的原URL与注册的servlet名相关联。这样,客户机可使用JSP页面的普通名称,但仍然激活定制的版本。例如,给定来自项目1的servlet定义,可使用下面的servlet-mapping定义:
<servlet-mapping>
<servlet-name>PageName</servlet-name>
<url-pattern>/RealPage.jsp</url-pattern>
</servlet-mapping>
3)JSP页使用jspInit而不是init。自动从JSP页面建立的servlet或许已经使用了inti方法。因此,使用JSP声明提供一个init方法是不合法的,必须制定jspInit方法。
为了说明初始化JSP页面的过程,程序清单5-9给出了一个名为InitPage.jsp的JSP页面,它包含一个jspInit方法且放置于deployDemo Web应用层次结构的顶层。一般,http://host/deployDemo/InitPage.jsp 形式的URL将激活此页面的不具有初始化参数访问权的版本,从而将对firstName和emailAddress变量显示null。但是,web.xml文件(程序清单5-10)分配了一个注册名,然后将该注册名与URL模式/InitPage.jsp相关联。

程序清单5-9 InitPage.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD><TITLE>JSP Init Test</TITLE></HEAD>
<BODY BGCOLOR="#FDF5E6">
<H2>Init Parameters:</H2>
<UL>
<LI>First name: <%= firstName %>
<LI>Email address: <%= emailAddress %>
</UL>
</BODY></HTML>
<%!
private String firstName, emailAddress;

public void jspInit() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}
%>


程序清单5-10 web.xml(说明JSP页面的init参数的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>InitPage</servlet-name>
<jsp-file>/InitPage.jsp</jsp-file>
<init-param>
<param-name>firstName</param-name>
<param-value>Bill</param-value>
</init-param>
<init-param>
<param-name>emailAddress</param-name>
<param-value>gates@oracle.com</param-value>
</init-param>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> InitPage</servlet-name>
<url-pattern>/InitPage.jsp</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


5.3 提供应用范围内的初始化参数
一般,对单个地servlet或JSP页面分配初始化参数。指定的servlet或JSP页面利用ServletConfig的getInitParameter方法读取这些参数。但是,在某些情形下,希望提供可由任意servlet或JSP页面借助ServletContext的getInitParameter方法读取的系统范围内的初始化参数。
可利用context-param元素声明这些系统范围内的初始化值。context-param元素应该包含param-name、param-value以及可选的description子元素,如下所示:
<context-param>
<param-name>support-email</param-name>
<param-value>blackhole@mycompany.com</param-value>
</context-param>
可回忆一下,为了保证可移植性,web.xml内的元素必须以正确的次序声明。但这里应该注意,context-param元素必须出现任意与文档有关的元素(icon、display-name或description)之后及filter、filter-mapping、listener或servlet元素之前。
5.4 在服务器启动时装载servlet
假如servlet或JSP页面有一个要花很长时间执行的init(servlet)或jspInit(JSP)方法。例如,假如init或jspInit方法从某个数据库或ResourceBundle查找产量。这种情况下,在第一个客户机请求时装载servlet的缺省行为将对第一个客户机产生较长时间的延迟。因此,可利用servlet的load-on-startup元素规定服务器在第一次启动时装载servlet。下面是一个例子。
<servlet>
<servlet-name> … </servlet-name>
<servlet-class> … </servlet-class> <!-- Or jsp-file -->
<load-on-startup/>
</servlet>
可以为此元素体提供一个整数而不是使用一个空的load-on-startup。想法是服务器应该在装载较大数目的servlet或JSP页面之前装载较少数目的servlet或JSP页面。例如,下面的servlet项(放置在Web应用的WEB-INF目录下的web.xml文件中的web-app元素内)将指示服务器首先装载和初始化SearchServlet,然后装载和初始化由位于Web应用的result目录中的index.jsp文件产生的servlet。
<servlet>
<servlet-name>Search</servlet-name>
<servlet-class>myPackage.SearchServlet</servlet-class> <!-- Or jsp-file -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Results</servlet-name>
<servlet-class>/results/index.jsp</servlet-class> <!-- Or jsp-file -->
<load-on-startup>2</load-on-startup>
</servlet>

6 声明过滤器

servlet版本2.3引入了过滤器的概念。虽然所有支持servlet API版本2.3的服务器都支持过滤器,但为了使用与过滤器有关的元素,必须在web.xml中使用版本2.3的DTD。
过滤器可截取和修改进入一个servlet或JSP页面的请求或从一个servlet或JSP页面发出的相应。在执行一个servlet或JSP页面之前,必须执行第一个相关的过滤器的doFilter方法。在该过滤器对其FilterChain对象调用doFilter时,执行链中的下一个过滤器。如果没有其他过滤器,servlet或JSP页面被执行。过滤器具有对到来的ServletRequest对象的全部访问权,因此,它们可以查看客户机名、查找到来的cookie等。为了访问servlet或JSP页面的输出,过滤器可将响应对象包裹在一个替身对象(stand-in object)中,比方说把输出累加到一个缓冲区。在调用FilterChain对象的doFilter方法之后,过滤器可检查缓冲区,如有必要,就对它进行修改,然后传送到客户机。
例如,程序清单5-11帝国难以了一个简单的过滤器,只要访问相关的servlet或JSP页面,它就截取请求并在标准输出上打印一个报告(开发过程中在桌面系统上运行时,大多数服务器都可以使用这个过滤器)。

程序清单5-11 ReportFilter.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

/** Simple filter that prints a report on the standard output
* whenever the associated servlet or JSP page is accessed.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &copy; 2002 Marty Hall; may be freely used or adapted.
*/

public class ReportFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest)request;
System.out.println(req.getRemoteHost() +
" tried to access " +
req.getRequestURL() +
" on " + new Date() + ".");
chain.doFilter(request,response);
}

public void init(FilterConfig config)
throws ServletException {
}

public void destroy() {}
}

一旦建立了一个过滤器,可以在web.xml中利用filter元素以及filter-name(任意名称)、file-class(完全限定的类名)和(可选的)init-params子元素声明它。请注意,元素在web.xml的web-app元素中出现的次序不是任意的;允许服务器(但不是必需的)强制所需的次序,并且实际中有些服务器也是这样做的。但这里要注意,所有filter元素必须出现在任意filter-mapping元素之前,filter-mapping元素又必须出现在所有servlet或servlet-mapping元素之前。
例如,给定上述的ReportFilter类,可在web.xml中作出下面的filter声明。它把名称Reporter与实际的类ReportFilter(位于moreservlets程序包中)相关联。
<filter>
<filter-name>Reporter</filter-name>
<filter-class>moresevlets.ReportFilter</filter-class>
</filter>
一旦命名了一个过滤器,可利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。关于此项工作有两种选择。
首先,可使用filter-name和servlet-name子元素把此过滤器与一个特定的servlet名(此servlet名必须稍后在相同的web.xml文件中使用servlet元素声明)关联。例如,下面的程序片断指示系统只要利用一个定制的URL访问名为SomeServletName的servlet或JSP页面,就运行名为Reporter的过滤器。
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>SomeServletName</servlet-name>
</filter-mapping>
其次,可利用filter-name和url-pattern子元素将过滤器与一组servlet、JSP页面或静态内容相关联。例如,相面的程序片段指示系统只要访问Web应用中的任意URL,就运行名为Reporter的过滤器。
<filter-mapping>
<filter-name>Reporter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
例如,程序清单5-12给出了将ReportFilter过滤器与名为PageName的servlet相关联的web.xml文件的一部分。名字PageName依次又与一个名为TestPage.jsp的JSP页面以及以模式http://host/webAppPrefix/UrlTest2/ 开头的URL相关联。TestPage.jsp的源代码已经JSP页面命名的谈论在前面的3节"分配名称和定制的URL"中给出。事实上,程序清单5-12中的servlet和servlet-name项从该节原封不动地拿过来的。给定这些web.xml项,可看到下面的标准输出形式的调试报告(换行是为了容易阅读)。
audit.irs.gov tried to access
http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html
on Tue Dec 25 13:12:29 EDT 2001.

程序清单5-12 Web.xml(说明filter用法的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<filter>
<filter-name>Reporter</filter-name>
<filter-class>moresevlets.ReportFilter</filter-class>
</filter>
<!-- ... -->
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>PageName</servlet-name>
</filter-mapping>
<!-- ... -->
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/RealPage.jsp</jsp-file>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> PageName </servlet-name>
<url-pattern>/UrlTest2/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


7 指定欢迎页

假如用户提供了一个像http://host/webAppPrefix/directoryName/ 这样的包含一个目录名但没有包含文件名的URL,会发生什么事情呢?用户能得到一个目录表?一个错误?还是标准文件的内容?如果得到标准文件内容,是index.html、index.jsp、default.html、default.htm或别的什么东西呢?
Welcome-file-list元素及其辅助的welcome-file元素解决了这个模糊的问题。例如,下面的web.xml项指出,如果一个URL给出一个目录名但未给出文件名,服务器应该首先试用index.jsp,然后再试用index.html。如果两者都没有找到,则结果有赖于所用的服务器(如一个目录列表)。
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
虽然许多服务器缺省遵循这种行为,但不一定必须这样。因此,明确地使用welcom-file-list保证可移植性是一种良好的习惯。

8 指定处理错误的页面

现在我了解到,你在开发servlet和JSP页面时从不会犯错误,而且你的所有页面是那样的清晰,一般的程序员都不会被它们的搞糊涂。但是,是人总会犯错误的,用户可能会提供不合规定的参数,使用不正确的URL或者不能提供必需的表单字段值。除此之外,其它开发人员可能不那么细心,他们应该有些工具来克服自己的不足。
error-page元素就是用来克服这些问题的。它有两个可能的子元素,分别是:error-code和exception-type。第一个子元素error-code指出在给定的HTTP错误代码出现时使用的URL。第二个子元素excpetion-type指出在出现某个给定的Java异常但未捕捉到时使用的URL。error-code和exception-type都利用location元素指出相应的URL。此URL必须以/开始。location所指出的位置处的页面可通过查找HttpServletRequest对象的两个专门的属性来访问关于错误的信息,这两个属性分别是:javax.servlet.error.status_code和javax.servlet.error.message。
可回忆一下,在web.xml内以正确的次序声明web-app的子元素很重要。这里只要记住,error-page出现在web.xml文件的末尾附近,servlet、servlet-name和welcome-file-list之后即可。

8.1 error-code元素
为了更好地了解error-code元素的值,可考虑一下如果不正确地输入文件名,大多数站点会作出什么反映。这样做一般会出现一个404错误信息,它表示不能找到该文件,但几乎没提供更多有用的信息。另一方面,可以试一下在www.microsoft.com、www.ibm.com 处或者特别是在www.bea.com 处输出未知的文件名。这是会得出有用的消息,这些消息提供可选择的位置,以便查找感兴趣的页面。提供这样有用的错误页面对于Web应用来说是很有价值得。事实上rm-error-page子元素)。由form-login-page给出的HTML表单必须具有一个j_security_check的ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。
例如,程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令,并且失败的登陆将由相同目录中名为login-error.jsp的页面报告。

程序清单5-19 web.xml(说明login-config的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<security-constraint> ... </security-constraint>
<login-config>
<auth-method> FORM </auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/login-error.jsp</form-error-page>
</form-login-config>
</login-config>
<!-- ... -->
</web-app>


9.2 限制对Web资源的访问
现在,可以指示服务器使用何种验证方法了。"了不起,"你说道,"除非我能指定一个来收到保护的URL,否则没有多大用处。"没错。指出这些URL并说明他们应该得到何种保护正是security-constriaint元素的用途。此元素在web.xml中应该出现在login-config的紧前面。它包含是个可能的子元素,分别是:web-resource-collection、auth-constraint、user-data-constraint和display-name。下面各小节对它们进行介绍。
l web-resource-collection
此元素确定应该保护的资源。所有security-constraint元素都必须包含至少一个web-resource-collection项。此元素由一个给出任意标识名称的web-resource-name元素、一个确定应该保护的URL的url-pattern元素、一个指出此保护所适用的HTTP命令(GET、POST等,缺省为所有方法)的http-method元素和一个提供资料的可选description元素组成。例如,下面的Web-resource-collection项(在security-constratint元素内)指出Web应用的proprietary目录中所有文档应该受到保护。
<security-constraint>
<web-resource-coolection>
<web-resource-name>Proprietary</web-resource-name>
<url-pattern>/propritary/*</url-pattern>
</web-resource-coolection>
<!-- ... -->
</security-constraint>
重要的是应该注意到,url-pattern仅适用于直接访问这些资源的客户机。特别是,它不适合于通过MVC体系结构利用RequestDispatcher来访问的页面,或者不适合于利用类似jsp:forward的手段来访问的页面。这种不匀称如果利用得当的话很有好处。例如,servlet可利用MVC体系结构查找数据,把它放到bean中,发送请求到从bean中提取数据的JSP页面并显示它。我们希望保证决不直接访问受保护的JSP页面,而只是通过建立该页面将使用的bean的servlet来访问它。url-pattern和auth-contraint元素可通过声明不允许任何用户直接访问JSP页面来提供这种保证。但是,这种不匀称的行为可能让开发人员放松警惕,使他们偶然对应受保护的资源提供不受限制的访问。
l auth-constraint
尽管web-resource-collention元素质出了哪些URL应该受到保护,但是auth-constraint元素却指出哪些用户应该具有受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role-name元素,以及包含(可选)一个描述角色的description元素。例如,下面web.xml中的security-constraint元素部门规定只有指定为Administrator或Big Kahuna(或两者)的用户具有指定资源的访问权。
<security-constraint>
<web-resource-coolection> ... </web-resource-coolection>
<auth-constraint>
<role-name>administrator</role-name>
<role-name>kahuna</role-name>
</auth-constraint>
</security-constraint>
重要的是认识到,到此为止,这个过程的可移植部分结束了。服务器怎样确定哪些用户处于任何角色以及它怎样存放用户的口令,完全有赖于具体的系统。
例如,Tomcat使用install_dir/conf/tomcat-users.xml将用户名与角色名和口令相关联,正如下面例子中所示,它指出用户joe(口令bigshot)和jane(口令enaj)属于administrator和kahuna角色。
<tomcat-users>
<user name="joe" password="bigshot" roles="administrator,kahuna" />
<user name="jane" password="enaj" roles="kahuna" />
</tomcat-users>
l user-data-constraint
这个可选的元素指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素(合法值为NONE、INTEGRAL或CONFIDENTIAL),并且可选地包含一个description元素。transport-guarantee为NONE值将对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上(并且在未来的HTTP版本中),在INTEGRAL和CONFIDENTIAL之间可能会有差别,但在当前实践中,他们都只是简单地要求用SSL。例如,下面指示服务器只允许对相关资源做HTTPS连接:
<security-constraint>
<!-- ... -->
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
l display-name
security-constraint的这个很少使用的子元素给予可能由GUI工具使用的安全约束项一个名称。
9.3 分配角色名
迄今为止,讨论已经集中到完全由容器(服务器)处理的安全问题之上了。但servlet以及JSP页面也能够处理它们自己的安全问题。
例如,容器可能允许用户从bigwig或bigcheese角色访问一个显示主管人员额外紧贴的页面,但只允许bigwig用户修改此页面的参数。完成这种更细致的控制的一种常见方法是调用HttpServletRequset的isUserInRole方法,并据此修改访问。
Servlet的security-role-ref子元素提供出现在服务器专用口令文件中的安全角色名的一个别名。例如,假如编写了一个调用request.isUserInRole("boss")的servlet,但后来该servlet被用在了一个其口令文件调用角色manager而不是boss的服务器中。下面的程序段使该servlet能够使用这两个名称中的任何一个。
<servlet>
<!-- ... -->
<security-role-ref>
<role-name>boss</role-name> <!-- New alias -->
<role-link>manager</role-link> <!-- Real name -->
</security-role-ref>
</servlet>
也可以在web-app内利用security-role元素提供将出现在role-name元素中的所有安全角色的一个全局列表。分别地生命角色使高级IDE容易处理安全信息。

10 控制会话超时

如果某个会话在一定的时间内未被访问,服务器可把它扔掉以节约内存。可利用HttpSession的setMaxInactiveInterval方法直接设置个别会话对象的超时值。如果不采用这种方法,则缺省的超时值由具体的服务器决定。但可利用session-config和session-timeout元素来给出一个适用于所有服务器的明确的超时值。超时值的单位为分钟,因此,下面的例子设置缺省会话超时值为三个小时(180分钟)。
<session-config>
<session-timeout>180</session-timeout>
</session-config>

11 Web应用的文档化

越来越多的开发环境开始提供servlet和JSP的直接支持。例子有Borland Jbuilder Enterprise Edition、Macromedia UltraDev、Allaire JRun Studio(写此文时,已被Macromedia收购)以及IBM VisuaAge for Java等。
大量的web.xml元素不仅是为服务器设计的,而且还是为可视开发环境设计的。它们包括icon、display-name和discription等。
可回忆一下,在web.xml内以适当地次序声明web-app子元素很重要。不过,这里只要记住icon、display-name和description是web.xml的web-app元素内的前三个合法元素即可。
l icon
icon元素指出GUI工具可用来代表Web应用的一个和两个图像文件。可利用small-icon元素指定一幅16 x 16的GIF或JPEG图像,用large-icon元素指定一幅32 x 32的图像。下面举一个例子:
<icon>
<small-icon>/images/small-book.gif</small-icon>
<large-icon>/images/tome.jpg</large-icon>
</icon>
l display-name
display-name元素提供GUI工具可能会用来标记此Web应用的一个名称。下面是个例子。
<display-name>Rare Books</display-name>
l description
description元素提供解释性文本,如下所示:
<description>
This Web application represents the store developed for
rare-books.com, an online bookstore specializing in rare
and limited-edition books.
</description>

12 关联文件与MIME类型

服务器一般都具有一种让Web站点管理员将文件扩展名与媒体相关联的方法。例如,将会自动给予名为mom.jpg的文件一个image/jpeg的MIME类型。但是,假如你的Web应用具有几个不寻常的文件,你希望保证它们在发送到客户机时分配为某种MIME类型。mime-mapping元素(具有extension和mime-type子元素)可提供这种保证。例如,下面的代码指示服务器将application/x-fubar的MIME类型分配给所有以.foo结尾的文件。
<mime-mapping>
<extension>foo</extension>
<mime-type>application/x-fubar</mime-type>
</mime-mapping>
或许,你的Web应用希望重载(override)标准的映射。例如,下面的代码将告诉服务器在发送到客户机时指定.ps文件作为纯文本(text/plain)而不是作为PostScript(application/postscript)。
<mime-mapping>
<extension>ps</extension>
<mime-type>application/postscript</mime-type>
</mime-mapping>


13 定位TLD

JSP taglib元素具有一个必要的uri属性,它给出一个TLD(Tag Library Descriptor)文件相对于Web应用的根的位置。TLD文件的实际名称在发布新的标签库版本时可能会改变,但我们希望避免更改所有现有JSP页面。此外,可能还希望使用保持taglib元素的简练性的一个简短的uri。这就是部署描述符文件的taglib元素派用场的所在了。Taglib包含两个子元素:taglib-uri和taglib-location。taglib-uri元素应该与用于JSP taglib元素的uri属性的东西相匹配。Taglib-location元素给出TLD文件的实际位置。例如,假如你将文件chart-tags-1.3beta.tld放在WebApp/WEB-INF/tlds中。现在,假如web.xml在web-app元素内包含下列内容。
<taglib>
<taglib-uri>/charts.tld</taglib-uri>
<taglib-location>
/WEB-INF/tlds/chart-tags-1.3beta.tld
</taglib-location>
</taglib>
给出这个说明后,JSP页面可通过下面的简化形式使用标签库。
<%@ taglib uri="/charts.tld" prefix="somePrefix" %>

14 指定应用事件监听程序

应用事件监听器程序是建立或修改servlet环境或会话对象时通知的类。它们是servlet规范的版本2.3中的新内容。这里只简单地说明用来向Web应用注册一个监听程序的web.xml的用法。
注册一个监听程序涉及在web.xml的web-app元素内放置一个listener元素。在listener元素内,listener-class元素列出监听程序的完整的限定类名,如下所示:
<listener>
<listener-class>package.ListenerClass</listener-class>
</listener>
虽然listener元素的结构很简单,但请不要忘记,必须正确地给出web-app元素内的子元素的次序。listener元素位于所有的servlet元素之前以及所有filter-mapping元素之后。此外,因为应用生存期监听程序是serlvet规范的2.3版本中的新内容,所以必须使用web.xml DTD的2.3版本,而不是2.2版本。
例如,程序清单5-20给出一个名为ContextReporter的简单的监听程序,只要Web应用的Servlet-Context建立(如装载Web应用)或消除(如服务器关闭)时,它就在标准输出上显示一条消息。程序清单5-21给出此监听程序注册所需要的web.xml文件的一部分。

程序清单5-20 ContextReporterjava
package moreservlets;

import javax.servlet.*;
import java.util.*;

/** Simple listener that prints a report on the standard output
* when the ServletContext is created or destroyed.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &copy; 2002 Marty Hall; may be freely used or adapted.
*/

public class ContextReporter implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
System.out.println("Context created on " +
new Date() + ".");
}

public void contextDestroyed(ServletContextEvent event) {
System.out.println("Context destroyed on " +
new Date() + ".");
}
}


程序清单5-21 web.xml(声明一个监听程序的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<filter-mapping> … </filter-mapping>
<listener>
<listener-class>package.ListenerClass</listener-class>
</listener>
<servlet> ... </servlet>
<!-- ... -->
</web-app>


15 J2EE元素

本节描述用作J2EE环境组成部分的Web应用的web.xml元素。这里将提供一个简明的介绍,详细内容可以参阅http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf的Java 2 Plantform Enterprise Edition版本1.3规范的第5章。
l distributable
distributable元素指出,Web应用是以这样的方式编程的:即,支持集群的服务器可安全地在多个服务器上分布Web应用。例如,一个可分布的应用必须只使用Serializable对象作为其HttpSession对象的属性,而且必须避免用实例变量(字段)来实现持续性。distributable元素直接出现在discription元素之后,并且不包含子元素或数据,它只是一个如下的标志。
<distributable />
l resource-env-ref
resource-env-ref元素声明一个与某个资源有关的管理对象。此元素由一个可选的description元素、一个resource-env-ref-name元素(一个相对于java:comp/env环境的JNDI名)以及一个resource-env-type元素(指定资源类型的完全限定的类),如下所示:
<resource-env-ref>
<resource-env-ref-name>
jms/StockQueue
</resource-env-ref-name>
<resource-env-ref-type>
javax.jms.Queue
</resource-env-ref-type>
</resource-env-ref>
l env-entry
env-entry元素声明Web应用的环境项。它由一个可选的description元素、一个env-entry-name元素(一个相对于java:comp/env环境JNDI名)、一个env-entry-value元素(项值)以及一个env-entry-type元素(java.lang程序包中一个类型的完全限定类名,java.lang.Boolean、java.lang.String等)组成。下面是一个例子:
<env-entry>
<env-entry-name>minAmout</env-entry-name>
<env-entry-value>100.00</env-entry-value>
<env-entry-type>minAmout</env-entry-type>
</env-entry>
l ejb-ref
ejb-ref元素声明对一个EJB的主目录的应用。它由一个可选的description元素、一个ejb-ref-name元素(相对于java:comp/env的EJB应用)、一个ejb-ref-type元素(bean的类型,Entity或Session)、一个home元素(bean的主目录接口的完全限定名)、一个remote元素(bean的远程接口的完全限定名)以及一个可选的ejb-link元素(当前bean链接的另一个bean的名称)组成。
l ejb-local-ref
ejb-local-ref元素声明一个EJB的本地主目录的引用。除了用local-home代替home外,此元素具有与ejb-ref元素相同的属性并以相同的方式使用。

11 agosto

七夕的由来

      今天是农历七月七,七夕,中国的情人节。嗯,今晚牛郎织女要相会的,哈,每年相会一次,真是苦啊,不过王母娘娘还算是仁慈的,至少这对有情人一年还能见一次面,也不错了
   
 
10 agosto

今天是我的生日

今天是我的生日,自己一个人先庆祝一下吧,没有在爸妈身边过的生日总是让我觉得有一种缺憾的感觉!
 
Foto 1 de 2
Más álbumes (1)
Todavía no se han agregado elementos de lista.