2004年08月13日

5万成本吞噬5百万利润:审纪人力资源

审纪人力资源部——管理顾问咨询日记
本刊记者 尹春洋/文
编者按: 用财务的眼光看待人力资源部,用定量思路考量最定性的企业管理。
和顾问公司一道,深入困境中的企业,面对面解决经营中最实际的问题……
企业亏损永远在情理之中,数额却总是在意料之外。无法自查自身的真正原因,就如医生无法给自己治病一样。华威集团就是在这种情况下决定利用“外脑”。
当张鸿玉的顾问团队介入这家企业的时候,企业里充斥着对连续两年亏损的迷茫和无奈,组织内部的信任也跌到了谷底,账面上的现金刚够支付顾问佣金。
5万元的部门成本
500万的管理黑洞

2004年1月15日   华威集团 高层年度总结会
华威集团的管理层希望通过深入的部门检讨,来找到造成亏损的真正原因,毕竟2003年巨大的财务亏空得有个说法。 “企业出现亏损,在坐的都有责任,通过各部门的综合评议,造成亏损的原因有7个方面,其中主要是有业务拓展能力不足……” 集团副总经理在说这段话的同时展示了一张图表(见图1)。
顾问公司发现,在这张图表中没有提到人力资源部,事后了解到,人力资源部门作为保障性部门没有参加利润流失调查。顾问公司很清楚,如果没有考察人力资源部门,企业永远也别想在作业层面上找到解决问题的实质。多年的管理咨询经历证明:但凡一个企业出现大面积持续亏损,企业的人力资源系统一定出现了重大问题。如同一个人病入膏肓,在中医的诊断中一定有一句是:气虚血弱。
总结会议还在继续进行,集团副总经理向大家提供了另一张表格,来分析在过去一年里,公司各项投入和营收(见图2)。
虽然造成企业亏损的原因很多,但华威集团人事的混乱是造成亏损的重要因素。这个运营成本还不足5万元的部门,所造成的直接或间接的损失却超过了500万。
一张错误的组织结构图
150万的人力损失

2004年1月19日   调查人力资源部进行到第3天
“在中国的企业中,有90%以上的组织结构图是错误的。”张鸿玉开始向客户高层解释自己的意图:“华威的组织机构图是错误的,这直接导致了管理上的混乱。”(见图3)
表面上隶属关系清晰的公司结构图,每个员工都拥有明确的上下级关系,而实际上,企业更多呈现出矩阵式管理和项目组管理两种形式。例如:华威集团每个运输组“安检”人员从行政上属于运输分公司,却直接对总公司的安全部门负责,同时,又是线路研发部门的成员,如果参照现有的组织结构图,内部沟通就会出现交叉和混乱。
“结合华威的实际,我们规划了新的组织结构图(见图4、5)。在新的管理矩阵中,每个人的组织角色是清晰的,沟通也将变得顺畅。”张鸿玉给自己的意见作了一个小结。会议出现了一阵混乱,人们互相议论,最后,华威集团的市场部总监表达了不同的意见:“张先生,你刚才提到,因为组织结构图的错误,我们损失了150万,你能为我们说的具体一点吗,150万是怎样被损失掉的?”
顾问公司为大家剖析了华威的不久前发生的一个案例:2003年9月,华威集团从韩国高薪聘请了一位物流专家,担任新服务产品研究开发中心的副主任。在常规的组织机构图上,并不能发现这次引进的错误。引进的专家虽然在行政上隶属于研发中心,但是却处在“回程研究项目组”。在这个组里,韩国专家的水平远远超过其他成员,最后导致了工作组资源不对称,不仅效率低下,也大大影响了韩国专家的工作热情。
新的组织结构图,把员工的能力、薪资需求按照1-5分进行分类。一个能力达到5分的员工,将会要求其他队员也超过3分,否则TEAM内部就会出现配置失衡。
2003年10月,为了适应韩国专家,研发部增加了新的设备,更换了内部的研发系统,也造成了原有中方员工的集体辞职;11月底,在研发无法继续的情况下,韩国专家与华威集团解除了劳动协议。
设备重复建设损失、研发前期投入、新产品延迟带来的渠道浪费、重组团队所需要的培训费用……直接损失也已经远远超过了150万元。
无奈的31日
有形的24万元流失
2004年2月28日 人力资源系统调整中期总结会
新员在入职三个月内离职,无论是什么原因,对招聘方都是不利的。每月的30、31日,是辞职最集中的时间。无论是主动离职还是被辞退,大部分的工作刚好完成,薪水也正好在月底截止,走掉的人或许各有心事,但是对于企业人力资源部来说,新员工在试用期离职,无论如何都是让人无奈的。
华威集团在2003年吸纳的新员中,有124个人在入职三个月内离职(见图2),如果按照每个员工每月薪水2000元,平均在公司服务1个月来计算,华威集团在2003年因为招聘新员不利一项,就损失了24.8万元。新员在进入公司以后的前1、2个月,大都不能为企业带来价值,企业还要为其支付培训和管理成本。类似的消耗在财务的账面上无法集中体现,损失却实实在在地发生了。
新员的离职有两种形式,其中一种是辞退,这部分的比例越高,说明企业招聘机制的漏洞越大;另外一种是员工自动离开,这部分则取决于人力资源部对于企业内生态的建设。
“企业就像是一个鱼缸,拥有自己的生态,招聘就是放一条新的鱼在里面;你需要考虑的不仅是那条新鱼的好坏,更要为那条鱼的未来打算。”
顾问公司为华威设计了新的招聘流程(见图6),在原有的招聘流程里,增加了许多内部需求调查的内容,整个流程让岗位需求更为细化。比如:销售岗位需要的是热爱销售工作的人,而不再单纯要求有销售经验,因为一些从业多年的人往往不能和企业共同面对市场逆境;更加看中学习能力和远景,企业的未来如果不是个人向往的方向,再优秀的人才,也不会把他的聪明和才智贡献给你构筑新人的沟通机制,当一个新人进入企业,人力资源部门会保证每周和新员工进行一次沟通,倾听他们对公司认识,以及工作中遇到的困难。
节省24万元并不是企业的最终目的,如果可以让企业获得有竞争力的团队,就是250万也是值得的。但是,过高的新员离职,所反映的人力体制缺陷,是务必要得到解决的。
花两毛维护100名经理人的人脉网
2004年4月10日华威客户回访日
这天,顾问人员在华威见到了李莉。她曾经是华威的销售总监,一年前离开华威的时候,正是华威的危难之际。2003年4月,华威由于合作伙伴撕毁和约,200辆商品车停留在天津中转基地没有办法运往全国,来自厂方的压力让华威面临巨大的信誉危机。运送商品轿车的车辆是特制的,分成6位和8位两种,为了保证商品车的安全,所有的运输车辆都安装有GPS卫星定位系统,如果运输车离开规定的高速公路,就会自动熄火,而且,车辆的位置也会在华威的电脑系统上显示。
在未经总公司同意的情况下,李莉私自雇佣了当地没有卫星定位的运输改装车完成了商品车的运输任务。虽然最后还是保住了定单,获得客户的肯定,李莉还是因为违反公司的规定而被要求辞职。
离开华威集团的李莉很快进入了处在吉林长春的一汽大众。在随后的一年里,李莉一直和华威的老同事保持联系,也了解到华威在2003年业绩全面下滑,当年一起工作的老同事先后离开,华威越来越变的陌生。
2004年2月20日,李莉收到了一张华威寄来的明信片,右下脚的署名是华威的董事长,信上说,李莉离开华威已经一年了,希望有时间回来坐坐。很快,其他的一些老同事也收到了类似的卡片,卡片上介绍了华威的近况以及一些憧憬和感谢……
李莉并不知道,这是华威在人力资源管理改革后的一项长期策略。人力资源部将所有离开华威的员工都进行了登记和整理,专门有人负责定期和他们保持联络,一方面是做给现在的员工看,另一方面也向离职的员工通报华威的业务和近况。因为,通过顾问公司的研究发现,在离开的员工中有接近一半的人依然从事着与商品车物流相关的工作,其中十分之一还进入了华威的上游企业,成为了华威的客户。即使是成为竞争对手,也存在很大的相互合作的空间。这些离职的员工甚至比华威现有的员工更了解华威的优势和劣势,他们知道华威的强势是什么,也知道怎样可以在竞争中轻易打败华威。
从2月份开始,华威的人力资源部分开始全面改善和离职员工之间的关系,不但改善了华威在业内的口碑,也从上百名离开的员工那里获得了很多商业合作的机会,而这一切也只是用了每次两毛钱的成本。
以上所提到的只是张鸿玉倡导的“审纪人力资源部”中的一部分,毕竟,人力资源在每个企业中的作用和位置各异,面临的专业环境也有所不同,相同只能是对于人力资源客观、冷静的认识。往往离企业领导者越近的部门越容易成为管理的死角。在引进质量管理、目标管理的今天,人们还没有找到系统的人力资源测量方法,更多还只能停留在理论层面。用量化来评价人力资源这个最具有定性色彩的工作,始终需要很多的变通和魄力。
说明:本文中所使用案例及数字由北京卡洛斯管理顾问公司总经理张鸿玉先生提供,张鸿玉曾就职于英国多家管理咨询机构,并获得英国管理学博士学位,拥有数年跨国公司人力资源管理及咨询经验。为了回避不必要的影响,文章中“华威集团”、“销售总监李莉”为化名。

信息块:
背景介绍:
华威集团成立于1999年6月,主营业务是物流,几年来,公司依靠为一汽大众、上海别克提供全国范围商品车运送、储运服务,实现了原始积累,并成为国内小有名气的物流企业。自2002年以来,公司出现了连续的亏损。2004年,委托管理顾问公司进入企业开展内部调查。截止记者发稿时止,管理顾问公司对华威的人力资源部门进行了系统的改革,其中对同行震动最大的,是利用财务管理上的测量方法来评价人力资源工作的得失,并使用具体有效的方法彻底改变了一个传统物流企业的被动竞争局面。

 

华威集团的离职人员联络技巧:
1.员工离职时诚恳地要求留下联络方式。
2.每年给员工寄生日卡和新年卡,由董事或副总经理亲笔签名。
3.把离职员工列入内部刊物邮寄名单。
4.定期为离职员工寄发写有公司近况和经营业务的电子邮件。
5.邀请积极联络的员工不定期回公司参观,和现有员工交流。
6.在部分大规模的年会、表彰会上邀请对公司有杰出贡献的离职员工以嘉宾身份参加或致辞。

什么是Command模式

简单的说Command模式就是一个包含一个execute()方法和多个标准的get()、set()方法的普通的java类,其中execute()方法用来执行Command中编写的业务逻辑。

Command模式的优点是:

1)统一了业务逻辑的入口,用户只能通过Command类中的execute()方法来执行业务逻辑。(用户可以通过get()、set()方法改写Command中相应的域的值来控制execute()方法的流程,也可以通过上下文context控制。
2)使用业务逻辑对用户透明。用户只需执行一个execute()方法来运行业务逻辑,
execute()方法具体如何执行对用户透明。
3)规范了用户的编码风格。每个Command都必须实现一个execute()方法,execute()方法是Command类中编写的所以的业务逻辑的入口。由execute()方法统一调配你编写的业务方法。

2 什么是EJB Command模式

EJB Command模式是在Command模式的基础上发展起来的,EJB Command的模式将远程调用用到的对EJB的remote接口和home接口调用的机制以及remote接口和home接口的实现进行了封装。这样使得EJB对于用户来讲是透明的,用户只需编写普通的java类不用考虑EJB的调用。用户在调用Command中的execute()方法时,execute()方法可以自动将用户编写的Command类在远程的stateless session bean中执行,执行完毕后将执行结果返回客户端。

EJB Command模式的特点:
1)EJB Command模式使业务逻辑的执行在一次网络调用中完成,实现业务逻辑与EJB完全解耦。
2)将业务逻辑放在普通的轻量级的Command Beans中实现,用户不必考虑EJB,可以方便开发提高开发速度,降低用户开发的难度。
3)EJB Command模式可以方便的设定开关,使得Command即可在EJB容器中运行也可以在普通的web容器中运行。这样开发时可在本地的web容器上调试测试,发布时再将Command放在服务器的EJB容器中运行,提高开发效率。
4)Command可以作为EJB层的facade。
3 EJB Command模式的优点
1) 将经常变动的业务逻辑放在轻量级的Command Beans中,有利于快速开发。
2) 将业务逻辑与表现逻辑分开。
3) 强制业务逻辑的每一次执行在一次网络调用中完成
4) 将client与EJB解耦。
5) Command可以放在远程的stateless session bean 中执行也可以在本地执行,方便客户端测试。

4 EJB Command模式的缺点
1) 粗粒度的事务管理,每一个command是一个事务,command模式不能将事务细分。
2) 粗粒度的异常处理,command只能抛出CommandException,无法细分不同的异常。
3) EJB Command是放在stateless session bean中执行的因此无法保存状态。
但是他还是有一定的缺点的

1 粗粒度的事务管理,每一个command是一个事务,command模式不能将事务细分。
2 粗粒度的异常处理,command只能抛出CommandException,无法细分不同的异常。
3 EJB Command是放在stateless session bean中执行的因此无法保存状态。

 俗话说得好:会干的不如会说的。你想仅仅凭着熟练的技能和勤恳的工作,就在职场游刃有余、出人头地,未免有些天真了。虽然能力加勤奋很重要,但会说话,却能让你工作起来更轻松,并且可能帮助你加薪、升职。

  1、应答上司交代的工作:我立即去办。

  冷静、迅速地做出这样的回应,会让上司直观地感觉你是一个工作讲效率、处理问题果断,并且服从领导的好下属。如果你犹豫不决,只会让上司不快,会给上司留下优柔寡断的印象,下次重要的机会可能就轮不到你了。

  2、传递坏消息时:我们似乎碰到一些情况……

  一笔业务出现麻烦,或市场出现危机,如果你立刻冲到上司的办公室报告这个坏消息,就算不关你的事,也会让上司怀疑你对待危机的能力,弄不好还会惹得上司的责骂,成为出气筒。

  正确的方式是你可以从容不迫地说:我们似乎碰到一些情况……千万不要乱了阵脚,要让上司觉得事情并没有到不可收拾的地步,并且感到你会与他并肩作战,解决问题。

  3、体现团队精神:XX的主意真不错!

  小马的创意或设计得到了上司的欣赏,虽然你心里为自己不成功的设计而难过,甚至有些妒忌,你还是要在上司的听力范围内夸夸小马:小马的主意真不错。在明争暗斗的职场,善于欣赏别人,会让上司认为你本性善良,并富有团队精神,从而给你更多的信任。

  4、如果你不知道某件事:让我再认真地想一想,2点前答复您好吗?

  上司问了你某个与业务有关的问题,你不知道如何作答,千万不要说“不知道”。而“让我再认真地想一想,2点前答复您好吗?”不仅暂时让你解围,也让上司认为你不轻率行事,而是个三思而后行的人。当然,要记得按时给出答复。

  5、请同事帮忙:这个策划没有你真不行啊!

  有个策划,你一个人搞不定,得找个比较内行的人帮忙,怎么开口呢?你可以诚恳地说:这个策划没有你真不行啊!同事为了不负自己内行的形象,通常是不会拒绝的。当然,事后要记得感谢人家。

  6、拒绝黄段子:这种话好像不适合在办公室讲哦!

  男人有时总喜欢说些黄段子,并且不大注意场合。如果有男同事对你开“黄腔”,让你无法忍受,这句话可以让他们识趣地闭嘴。

  7、减轻工作量:我知道这件事很重要,我们不妨先排一排手头的工作,按重要性排出先后顺序。

  首先,强调你了解这项工作的重要性,然后请求上司指示,将这项工作与其它工作一起排出

  先后顺序,不露痕迹地让上司知道你的工作量其实很大,如果不是非你不可,有些事就可交给其他人或延期处理。

  8、承认过失:是我一时疏忽,不过幸好……

  犯错误在所难免,所以勇于承认自己的过失很重要,推卸责任只会使你错上加错。不过,承认过失也有诀窍,就是不要让所有的错误都自己扛,这句话可以转移别人的注意力,淡化你的过失。

  9、打破冷场的话题:我很想知道您对这件事的看法……

  当你与上司相处时,有时不得不找点话题,以打破冷场。不过,这正是你赢得上司青睐的好机会,最恰当的话题就是谈一些与公司有关、上司很关心又熟悉的话题。当上司滔滔不绝地发表看法时,也会对你这样一个谦虚的听众欣赏有加。

  10、面对批评:谢谢你告诉我,我会仔细考虑你的建议的。

  面对批评或责难,不管自己有没有不当之处,都不要将不满写在脸上,但要让对方知道,你已接受到他的信息,不卑不亢让你看起来又自信又稳重,更值得敬重。

Abstract: This tutorial provides a quick introduction to the Unified Modeling Language? 

The heart of object-oriented problem solving is the construction of a model. The model abstracts the essential details of the underlying problem from its usually complicated real world. Several modeling tools are wrapped under the heading of the UML?, which stands for Unified Modeling Language?. The purpose of this course is to present important highlights of the UML.

At the center of the UML are its nine kinds of modeling diagrams, which we describe here.

Some of the sections of this course contain links to pages with more detailed information. And every section has short questions. Use them to test your understanding of the section topic.

Divider line

Why is UML important?

Let’s look at this question from the point of view of the construction trade. Architects design buildings. Builders use the designs to create buildings. The more complicated the building, the more critical the communication between architect and builder. Blueprints are the standard graphical language that both architects and builders must learn as part of their trade.

Writing software is not unlike constructing a building. The more complicated the underlying system, the more critical the communication among everyone involved in creating and deploying the software. In the past decade, the UML has emerged as the software blueprint language for analysts, designers, and programmers alike. It is now part of the software trade. The UML gives everyone from business analyst to designer to programmer a common vocabulary to talk about software design.

The UML is applicable to object-oriented problem solving. Anyone interested in learning UML must be familiar with the underlying tenet of object-oriented problem solving — it all begins with the construction of a model. A model is an abstraction of the underlying problem. The domain is the actual world from which the problem comes.

Models consist of objects that interact by sending each other messages. Think of an object as “alive.” Objects have things they know (attributes) and things they can do (behaviors or operations). The values of an object’s attributes determine its state.

Classes are the “blueprints” for objects. A class wraps attributes (data) and behaviors (methods or functions) into a single distinct entity. Objects are instances of classes.

Divider line

Use case diagrams

Use case diagrams describe what a system does from the standpoint of an external observer. The emphasis is on what a system does rather than how.

Use case diagrams are closely connected to scenarios. A scenario is an example of what happens when someone interacts with the system. Here is a scenario for a medical clinic.

    “A patient calls the clinic to make an appointment for a yearly checkup. The receptionist finds the nearest empty time slot in the appointment book and schedules the appointment for that time slot. “

A use case is a summary of scenarios for a single task or goal. An actor is who or what initiates the events involved in that task. Actors are simply roles that people or objects play. The picture below is a Make Appointment use case for the medical clinic. The actor is a Patient. The connection between actor and use case is a communication association (or communication for short).

Use case

Actors are stick figures. Use cases are ovals. Communications are lines that link actors to use cases.

A use case diagram is a collection of actors, use cases, and their communications. We’ve put Make Appointment as part of a diagram with four actors and four use cases. Notice that a single use case can have multiple actors.

Use case diagram

Use case diagrams are helpful in three areas.

  • determining features (requirements). New use cases often generate new requirements as the system is analyzed and the design takes shape.
  • communicating with clients. Their notational simplicity makes use case diagrams a good way for developers to communicate with clients.
  • generating test cases. The collection of scenarios for a use case may suggest a suite of test cases for those scenarios.

More details

Self test

Divider line

Class diagrams

A Class diagram gives an overview of a system by showing its classes and the relationships among them. Class diagrams are static — they display what interacts but not what happens when they do interact.

The class diagram below models a customer order from a retail catalog. The central class is the Order. Associated with it are the Customer making the purchase and the Payment. A Payment is one of three kinds: Cash, Check, or Credit. The order contains OrderDetails (line items), each with its associated Item.

Class diagram

UML class notation is a rectangle divided into three parts: class name, attributes, and operations. Names of abstract classes, such as Payment, are in italics. Relationships between classes are the connecting links.

Our class diagram has three kinds of relationships.

  • association — a relationship between instances of the two classes. There is an association between two classes if an instance of one class must know about the other in order to perform its work. In a diagram, an association is a link connecting two classes.
  • aggregation — an association in which one class belongs to a collection. An aggregation has a diamond end pointing to the part containing the whole. In our diagram, Order has a collection of OrderDetails.
  • generalization — an inheritance link indicating one class is a superclass of the other. A generalization has a triangle pointing to the superclass. Payment is a superclass of Cash, Check, and Credit.

An association has two ends. An end may have a role name to clarify the nature of the association. For example, an OrderDetail is a line item of each Order.

A navigability arrow on an association shows which direction the association can be traversed or queried. An OrderDetail can be queried about its Item, but not the other way around. The arrow also lets you know who “owns” the association’s implementation; in this case, OrderDetail has an Item. Associations with no navigability arrows are bi-directional.

The multiplicity of an association end is the number of possible instances of the class associated with a single instance of the other end. Multiplicities are single numbers or ranges of numbers. In our example, there can be only one Customer for each Order, but a Customer can have any number of Orders.

This table gives the most common multiplicities.

    Multiplicities Meaning
    0..1 zero or one instance. The notation n . . m indicates n to m instances.
    0..*  or  * no limit on the number of instances (including none).
    1 exactly one instance
    1..* at least one instance

Every class diagram has classes, associations, and multiplicities. Navigability and roles are optional items placed in a diagram to provide clarity.

More details

Self test

Divider line

Packages and object diagrams

To simplify complex class diagrams, you can group classes into packages. A package is a collection of logically related UML elements. The diagram below is a business model in which the classes are grouped into packages.

Package diagram

Packages appear as rectangles with small tabs at the top. The package name is on the tab or inside the rectangle. The dotted arrows are dependencies. One package depends on another if changes in the other could possibly force changes in the first.

Object diagrams show instances instead of classes. They are useful for explaining small pieces with complicated relationships, especially recursive relationships.

This small class diagram shows that a university Department can contain lots of other Departments.

Recursive class diagram

The object diagram below instantiates the class diagram, replacing it by a concrete example.

Object diagram

Each rectangle in the object diagram corresponds to a single instance. Instance names are underlined in UML diagrams. Class or instance names may be omitted from object diagrams as long as the diagram meaning is still clear.

Self test

Divider line

Sequence diagrams

Class and object diagrams are static model views. Interaction diagrams are dynamic. They describe how objects collaborate.

A sequence diagram is an interaction diagram that details how operations are carried out — what messages are sent and when. Sequence diagrams are organized according to time. The time progresses as you go down the page. The objects involved in the operation are listed from left to right according to when they take part in the message sequence.

Below is a sequence diagram for making a hotel reservation. The object initiating the sequence of messages is a Reservation window.

Sequence diagram

The Reservation window sends a makeReservation() message to a HotelChain. The HotelChain then sends a makeReservation() message to a Hotel. If the Hotel has available rooms, then it makes a Reservation and a Confirmation.

Each vertical dotted line is a lifeline, representing the time that an object exists. Each arrow is a message call. An arrow goes from the sender to the top of the activation bar of the message on the receiver’s lifeline. The activation bar represents the duration of execution of the message.

In our diagram, the Hotel issues a self call to determine if a room is available. If so, then the Hotel creates a Reservation and a Confirmation. The asterisk on the self call means iteration (to make sure there is available room for each day of the stay in the hotel). The expression in square brackets, [ ], is a condition.

The diagram has a clarifying note, which is text inside a dog-eared rectangle. Notes can be put into any kind of UML diagram.

More details

Self test

Divider line

Collaboration diagrams

Collaboration diagrams are also interaction diagrams. They convey the same information as sequence diagrams, but they focus on object roles instead of the times that messages are sent. In a sequence diagram, object roles are the vertices and messages are the connecting links.

Collaboration diagram

The object-role rectangles are labeled with either class or object names (or both). Class names are preceded by colons ( : ).

Each message in a collaboration diagram has a sequence number. The top-level message is numbered 1. Messages at the same level (sent during the same call) have the same decimal prefix but suffixes of 1, 2, etc. according to when they occur.

Self test

Divider line

Statechart diagrams

Objects have behaviors and state. The state of an object depends on its current activity or condition. A statechart diagram shows the possible states of the object and the transitions that cause a change in state.

Our example diagram models the login part of an online banking system. Logging in consists of entering a valid social security number and personal id number, then submitting the information for validation.

Logging in can be factored into four non-overlapping states: Getting SSN, Getting PIN, Validating, and Rejecting. From each state comes a complete set of transitions that determine the subsequent state.

State diagram

States are rounded rectangles. Transitions are arrows from one state to another. Events or conditions that trigger transitions are written beside the arrows. Our diagram has two self-transition, one on Getting SSN and another on Getting PIN.

The initial state (black circle) is a dummy to start the action. Final states are also dummy states that terminate the action.

The action that occurs as a result of an event or condition is expressed in the form /action. While in its Validating state, the object does not wait for an outside event to trigger a transition. Instead, it performs an activity. The result of that activity determines its subsequent state.

More details

Self test

Divider line

Activity diagrams

An activity diagram is essentially a fancy flowchart. Activity diagrams and statechart diagrams are related. While a statechart diagram focuses attention on an object undergoing a process (or on a process as an object), an activity diagram focuses on the flow of activities involved in a single process. The activity diagram shows the how those activities depend on one another.

For our example, we used the following process.

    “Withdraw money from a bank account through an ATM.”

The three involved classes (people, etc.) of the activity are Customer, ATM, and Bank. The process begins at the black start circle at the top and ends at the concentric white/black stop circles at the bottom. The activities are rounded rectangles.

Activity diagram

Activity diagrams can be divided into object swimlanes that determine which object is responsible for which activity. A single transition comes out of each activity, connecting it to the next activity.

A transition may branch into two or more mutually exclusive transitions. Guard expressions (inside [ ]) label the transitions coming out of a branch. A branch and its subsequent merge marking the end of the branch appear in the diagram as hollow diamonds.

A transition may fork into two or more parallel activities. The fork and the subsequent join of the threads coming out of the fork appear in the diagram as solid bars.

Self test

Divider line

Component and deployment diagrams

A component is a code module. Component diagrams are physical analogs of class diagram. Deployment diagrams show the physical configurations of software and hardware.

The following deployment diagram shows the relationships among software and hardware components involved in real estate transactions.

Deployment diagram

The physical hardware is made up of nodes. Each component belongs on a node. Components are shown as rectangles with two tabs at the upper left.

Self test

Divider line

UML Tools

Creating and modifying UML diagrams can be labor and time intensive. But in constructing the diagrams for this short course, we cut our efforts far short using Borland Together ControlCenter, which is the premier UML modeling tool.

Borland Together ControlCenter is available from Borland? Software Corporation at www.borland.com.

Borland ControlCenter always keeps diagrams and code in sync. But it’s much more than a mere modeling tool. Borland ControlCenter accelerates development for teams using Java and leading application servers to build e-business and enterprise applications. Borland ControlCenter also supports teams using C++ and IDL, delivering wider coverage and support for large development organizations. Borland’s “platform and building blocksTM” architecture delivers deep integration across all aspects of software development: model-pattern-edit-test-compile-debug-version-doc-metric-audit-provision-assemble-deploy-run, leading to an environment in which business experts, modelers, and developers find they can work more productively, increasing the competitive value of what they build and reducing time to market.

Divider line

For the latest up-to-date techniques in the Unified Modeling Language and Agile Software Development Processes, subscribe to The Coad Letter. Visit The Borland Developer Network for all of the latest information on how to deliver better software faster.

TO_DATE格式
Day:
dd number 12
dy abbreviated fri
day spelled out friday
ddspth spelled out, ordinal twelfth
Month:
mm number 03
mon abbreviated mar
month spelled out march
Year:
yy two digits 98
yyyy four digits 1998

24小时格式下时间范围为: 0:00:00 – 23:59:59….
12小时格式下时间范围为: 1:00:00 – 12:59:59 ….
1.
日期和字符转换函数用法(to_date,to_char)

2.
select to_char( to_date(222,’J'),’Jsp’) from dual

显示Two Hundred Twenty-Two

3.
求某天是星期几
select to_char(to_date(‘2002-08-26′,’yyyy-mm-dd’),’day’) from dual;
星期一
select to_char(to_date(‘2002-08-26′,’yyyy-mm-dd’),’day’,'NLS_DATE_LANGUAGE = American’) from dual;
monday
设置日期语言
ALTER SESSION SET NLS_DATE_LANGUAGE=’AMERICAN’;
也可以这样
TO_DATE (‘2002-08-26′, ‘YYYY-mm-dd’, ‘NLS_DATE_LANGUAGE = American’)

4.
两个日期间的天数
select floor(sysdate – to_date(‘20020405′,’yyyymmdd’)) from dual;

5. 时间为null的用法
select id, active_date from table1
UNION
select 1, TO_DATE(null) from dual;

注意要用TO_DATE(null)

6.
a_date between to_date(‘20011201′,’yyyymmdd’) and to_date(‘20011231′,’yyyymmdd’)
那么12月31号中午12点之后和12月1号的12点之前是不包含在这个范围之内的。
所以,当时间需要精确的时候,觉得to_char还是必要的
7. 日期格式冲突问题
输入的格式要看你安装的ORACLE字符集的类型, 比如: US7ASCII, date格式的类型就是: ‘01-Jan-01′
alter system set NLS_DATE_LANGUAGE = American
alter session set NLS_DATE_LANGUAGE = American
或者在to_date中写
select to_char(to_date(‘2002-08-26′,’yyyy-mm-dd’),’day’,'NLS_DATE_LANGUAGE = American’) from dual;
注意我这只是举了NLS_DATE_LANGUAGE,当然还有很多,
可查看
select * from nls_session_parameters
select * from V$NLS_PARAMETERS

8.
select count(*)
from ( select rownum-1 rnum
from all_objects
where rownum <= to_date(‘2002-02-28′,’yyyy-mm-dd’) – to_date(‘2002-
02-01′,’yyyy-mm-dd’)+1
)
where to_char( to_date(‘2002-02-01′,’yyyy-mm-dd’)+rnum-1, ‘D’ )
not
in ( ‘1′, ‘7′ )

查找2002-02-28至2002-02-01间除星期一和七的天数
在前后分别调用DBMS_UTILITY.GET_TIME, 让后将结果相减(得到的是1/100秒, 而不是毫秒).

9.
select months_between(to_date(‘01-31-1999′,’MM-DD-YYYY’),
to_date(‘12-31-1998′,’MM-DD-YYYY’)) “MONTHS” FROM DUAL;
1

select months_between(to_date(‘02-01-1999′,’MM-DD-YYYY’),
to_date(‘12-31-1998′,’MM-DD-YYYY’)) “MONTHS” FROM DUAL;

1.03225806451613
10. Next_day的用法
Next_day(date, day)

Monday-Sunday, for format code DAY
Mon-Sun, for format code DY
1-7, for format code D

11
select to_char(sysdate,’hh:mi:ss’) TIME from all_objects
注意:第一条记录的TIME 与最后一行是一样的
可以建立一个函数来处理这个问题
create or replace function sys_date return date is
begin
return sysdate;
end;

select to_char(sys_date,’hh:mi:ss’) from all_objects;
12.
获得小时数

SELECT EXTRACT(HOUR FROM TIMESTAMP ‘2001-02-16 2:38:40′) from offer
SQL> select sysdate ,to_char(sysdate,’hh’) from dual;

SYSDATE TO_CHAR(SYSDATE,’HH’)
——————– ———————
2003-10-13 19:35:21 07

SQL> select sysdate ,to_char(sysdate,’hh24′) from dual;

SYSDATE TO_CHAR(SYSDATE,’HH24′)
——————– ———————–
2003-10-13 19:35:21 19

获取年月日与此类似
13.
年月日的处理
select older_date,
newer_date,
years,
months,
abs(
trunc(
newer_date-
add_months( older_date,years*12+months )
)
) days
from ( select
trunc(months_between( newer_date, older_date )/12) YEARS,
mod(trunc(months_between( newer_date, older_date )),
12 ) MONTHS,
newer_date,
older_date
from ( select hiredate older_date,
add_months(hiredate,rownum)+rownum newer_date
from emp )
)

14.
处理月份天数不定的办法
select to_char(add_months(last_day(sysdate) +1, -2), ‘yyyymmdd’),last_day(sysdate) from dual

16.
找出今年的天数
select add_months(trunc(sysdate,’year’), 12) – trunc(sysdate,’year’) from dual

闰年的处理方法
to_char( last_day( to_date(‘02′ || :year,’mmyyyy’) ), ‘dd’ )
如果是28就不是闰年

17.
yyyy与rrrr的区别
‘YYYY99 TO_C
——- —-
yyyy 99 0099
rrrr 99 1999
yyyy 01 0001
rrrr 01 2001

18.不同时区的处理
select to_char( NEW_TIME( sysdate, ‘GMT’,'EST’), ‘dd/mm/yyyy hh:mi:ss’) ,sysdate
from dual;

19.
5秒钟一个间隔
Select TO_DATE(FLOOR(TO_CHAR(sysdate,’SSSSS’)/300) * 300,’SSSSS’) ,TO_CHAR(sysdate,’SSSSS’)
from dual

2002-11-1 9:55:00 35786
SSSSS表示5位秒数

20.
一年的第几天
select TO_CHAR(SYSDATE,’DDD’),sysdate from dual
310 2002-11-6 10:03:51

21.计算小时,分,秒,毫秒
select
Days,
A,
TRUNC(A*24) Hours,
TRUNC(A*24*60 – 60*TRUNC(A*24)) Minutes,
TRUNC(A*24*60*60 – 60*TRUNC(A*24*60)) Seconds,
TRUNC(A*24*60*60*100 – 100*TRUNC(A*24*60*60)) mSeconds
from
(
select
trunc(sysdate) Days,
sysdate – trunc(sysdate) A
from dual
)

select * from tabname
order by decode(mode,’FIFO’,1,-1)*to_char(rq,’yyyymmddhh24miss’);

//
floor((date2-date1) /365) 作为年
floor((date2-date1, 365) /30) 作为月
mod(mod(date2-date1, 365), 30)作为日.
23.next_day函数
next_day(sysdate,6)是从当前开始下一个星期五。后面的数字是从星期日开始算起。
1 2 3 4 5 6 7
日 一 二 三 四 五 六

Java本身是一种设计的非常简单,非常精巧的语言,所以Java背后的原理也很简单,归结起来就是两点:

1、JVM的内存管理

理解了这一点,所有和对象相关的问题统统都能解决

2、JVM Class Loader

理解了这一点,所有和Java相关的配置问题,包括各种App Server的配置,应用的发布问题统统都能解决

App Class Loader
|—– EJB Class Loader
|—– Web App Class Loader

如果在App Class Loader级别配置,是全局可见的。如果打包在EJB里面,那么就不会影响到Web Application,反之亦然,如果你在WEB-INF下面放置Hibernate,也不会影响到EJB。放在EJB Class Loader或者放在Web App Class Loader级别主要就是在局部范围内有效,不影响到其它的应用。

试想,如果在一个Weblogic上面配置多个虚拟域,你使用www.bruce.com域名,开发你的网站,我使用www.fankai.com开发我的网站,那么当然不希望我们的Hibernate相互干扰,所以就可以放在 EJB Class Loader级别来配置Hibernate。

进一步阐述一下EJB Class Loader的问题:

先再次强调一下,Hibernate和EJB,和App Server不存在兼容性问题,他们本来就是不相关的东西,就好像JDBC,相信没有人会认为JDBC和EJB不兼容吧,Hibernate也是一样,它只和JDBC驱动,和数据库有兼容性问题,而和EJB,和App Server完全是不搭界的两回事。凡是认为Hibernate和EJB不兼容的人,其实是都是因为对EJB学习的不到家,把责任推到Hibernate身上了。

我前面提到过Class Loader的层次,这里不重复了,总之我们先来看看Class Loader的作用范围:

((Boot Strap)) Class Loader:
load JRE\lib\rt.jar, sunrsasign.jar, charsets.jar, jce.jar, jsse.jar, plugin.jar
Ext Class Loader:
load JRE\lib\ext目录下的库文件, load JRE\classes目录下的类
App Class Loader:
load CLASSPATH变量指定路径下的类

以上的load路径都是写死在JVM的C++源代码里面的,不能改变,详细请见王森的《Java深度历险》

在一个特定的App Server上,Class Loader会继续向下继承,继承的层次会根据不同的App Server有所不同,但是肯定不会变的就是:

EJB Class Loader:

继承自App Class Loader,继承层次根据App Server有所不同,一个EJB Class Loader它的load Class的范围仅限于JAR或者EAR范围之内。

Web App Class Loader:

继承自App Class Loader,继承层次根据App Server有所不同,一个Web App Class Loader:它的load Class的范围在 WEB-INF\lib下的库文件和WEB-INF\classes目录下的class文件。

Web App Class Loader很好理解,大家毕竟用的很多,App Server上的一个Web Application会创建一个Web App Class Loader的实例去负责load class,所以如果你想让Hibernate只在这个Web Application内生效,把它放到WEB-INF\lib下去就好了。

如果你把Hibernate放到了CLASSPATH变量指定的路径下,而你在WEB-INF\lib也放了一份,那么Web App Class Loader由于load范围所限,它会首先找到WEB-INF\lib下的那份Hibernate,按照它的配置来初始化Hibernate。

如果你把Hibernate放到了CLASSPATH变量指定的路径下,但你在WEB-INF\lib什么都没有放,那么Web App Class Loader由于load范围所限,它根本什么都找不到,于是它把load Hibernate的责任交给上一级的Class Loader,这样直到App Class Loader,它找到了Hibernate,按照它的配置来初始化Hibernate。

EJB Class Loader稍微复杂一点,不那么容易理解。App Server会针对每一个EJB包文件创建一个EJB Class Loader的实例,例如:

((Hello Robbin)).jar
((Hello Bruce)).jar

当你把这两个jar发布到App Server上以后,会创建两个EJB Class Loader的实例,分别去load这两个EJB包,比如说:

CLEJB_Robbin是load ((Hello Robbin)).jar的
CLEJB_Bruce是load ((Hello Bruce)).jar的

那么CLEJB_Robbin的load范围就仅仅限于HelloRobbin.jar之内,它load不到HelloRobbin.jar之外的任何文件,当然它也load不到HelloBruce.jar。

说到这里,我相信大家应该已经明白为什么EJB规范不允许EJB有IO操作了吧?因为EJB Class Loader根本找不到jar包之外的文件!!!

如果现在你想实现HelloRobbin.jar和HelloBruce.jar的互相调用,那么该怎么办?他们使用了不同的EJB Class Loader,相互之间是找不到对方的。解决办法就是使用EAR。

现在假设HelloRobbin.jar和HelloBruce.jar都使用了Hibernate,看看该怎么打包和发布:

HelloEJB.ear

|—— ((Hello Robbin)).jar
|—— ((Hello Bruce)).jar
|—— Hibernate2.jar
|—— pojo.jar (定义所有的持久对象和hbm文件的jar包)
|—— cglib-asm.jar
|—— commons-beanutils.jar
|—— commons-collections.jar
|—— commons-lang.jar
|—— commons-logging.jar
|—— dom4j.jar
|—— odmg.jar
|—— log4j.jar
|—— jcs.jar
|—— Hibernate.properties
|—— log4j.properties
|—— cache.ccf
|—— META-INF\application.xml (J2EE规范的要求,定义EAR包里面包括了哪几个EJB)

除此之外,按照EJB规范要求,HelloRobbin.jar和HelloBruce.jar还必须指出调用jar包之外的类库的名称,这需要在jar包的manifest文件中定义:

((Hello Robbin)).jar
|—— META-INF\MANIFEST.MF

MANIFEST.MF中必须包括如下一行:

Class-Path: log4j.jar hibernate2.jar cglib-asm.jar commons-beanutils.jar commons-collections.jar commons-lang.jar
commons-logging.jar dom4j.jar jcs.jar odmg.jar jcs.jar pojo.jar

这样就OK了,当把HelloEJB.ear发布到App Server上以后,App Server创建一个EJB Class Loader实例load EAR包里面的EJB,再根据EJB的jar包里面的MANIFEST.MF指出的Class-Path去寻找相应的jar包之外的类库。

所以一个EAR包有点类似一个Web Application,EJB Class Loader的load范围也就是EAR范围之内,它load不到EAR之外的文件。除非把Hibernate定义到CLASSPATH指定的路径下,在这种情况下,EJB Class Loader找不到Hibernate,只能交给上一级的Class Loader,最后由App Class Loader找到Hibernate,进行初始化。

由于EAR这样load Class规则,假设Robbin和Bruce都在同一个Weblogic上运行自己的网站,而我们都不希望自己的程序里面的Hibernate配置被对方的搞乱掉,那么我们就可以这样来做:

Robbin’s Website:

Robbin.ear

|——– robbin.war (把Web Application打包)
|——– robbin.jar (把开发的EJB打包)
|——– Hibernate2.jar
……………………..
|——– META-INF\application.xml

Bruce’s Website:

Bruce.ear
|——– bruce.war (把Web Application打包)
|——– bruce.jar (把开发的EJB打包)
|——– Hibernate2.jar
……………………..
|——– META-INF\application.xml

这样在同一个App Server上运行,就可以互相不干扰。

#################################################
richardluo 发表自dev2dev@bea

了解ClassLoader
1, 什么是 ClassLoader?
Java 程序并不是一个可执行文件,是需要的时候,才把装载到 JVM中。ClassLoader 做的工作就是 JVM 中将类装入内存。 而且,Java ClassLoader 就是用 Java 语言编写的。这意味着您可以创建自己的 ClassLoader
ClassLoader 的基本目标是对类的请求提供服务。当 JVM 需要使用类时,它根据名称向 ClassLoader 请求这个类,然后 ClassLoader 试图返回一个表示这个类的 Class 对象。 通过覆盖对应于这个过程不同阶段的方法,可以创建定制的 ClassLoader。
2, 一些重要的方法
A) 方法 loadClass
ClassLoader.loadClass() 是 ClassLoader 的入口点。该方法的定义如下:
Class loadClass( String name, boolean resolve );
name JVM 需要的类的名称,如 Foo 或 java.lang.Object。
resolve 参数告诉方法是否需要解析类。在准备执行类之前,应考虑类解析。并不总是需要解析。如果 JVM 只需要知道该类是否存在或找出该类的超类,那么就不需要解析。

B) 方法 defineClass
defineClass 方法是 ClassLoader 的主要诀窍。该方法接受由原始字节组成的数组并把它转换成 Class 对象。原始数组包含如从文件系统或网络装入的数据。defineClass 管理 JVM 的许多复杂、神秘和倚赖于实现的方面 — 它把字节码分析成运行时数据结构、校验有效性等等。不必担心,您无需亲自编写它。事实上,即使您想要这么做也不能覆盖它,因为它已被标记成final的。

C) 方法 findSystemClass
findSystemClass 方法从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用 defineClass 将原始字节转换成 Class 对象,以将该文件转换成类。当运行 Java 应用程序时,这是 JVM 正常装入类的缺省机制。(Java 2 中 ClassLoader 的变动提供了关于 Java 版本 1.2 这个过程变动的详细信息。) 对于定制的 ClassLoader,只有在尝试其它方法装入类之后,再使用 findSystemClass。原因很简单:ClassLoader 是负责执行装入类的特殊步骤,不是负责所有类。例如,即使 ClassLoader 从远程的 Web 站点装入了某些类,仍然需要在本地机器上装入大量的基本 Java 库。而这些类不是我们所关心的,所以要 JVM 以缺省方式装入它们:从本地文件系统。这就是 findSystemClass 的用途。

D) 方法 resolveClass
正如前面所提到的,可以不完全地(不带解析)装入类,也可以完全地(带解析)装入类。当编写我们自己的 loadClass 时,可以调用 resolveClass,这取决于 loadClass 的 resolve 参数的值。

E) 方法 findLoadedClass
findLoadedClass 充当一个缓存:当请求 loadClass 装入类时,它调用该方法来查看 ClassLoader 是否已装入这个类,这样可以避免重新装入已存在类所造成的麻烦。应首先调用该方法。

3, 怎么组装这些方法
1) 调用 findLoadedClass 来查看是否存在已装入的类。
2) 如果没有,那么采用那种特殊的神奇方式来获取原始字节。
3) 如果已有原始字节,调用 defineClass 将它们转换成 Class 对象。
4) 如果没有原始字节,然后调用 findSystemClass 查看是否从本地文件系统获取类。
5) 如果 resolve 参数是 true,那么调用 resolveClass 解析 Class 对象。
6) 如果还没有类,返回 ClassNotFoundException。

4,Java 2 中 ClassLoader 的变动
1)loadClass 的缺省实现
定制编写的 loadClass 方法一般尝试几种方式来装入所请求的类,如果您编写许多类,会发现一次次地在相同的、很复杂的方法上编写变量。 在 Java 1.2 中 loadClass 的实现嵌入了大多数查找类的一般方法,并使您通过覆盖 findClass 方法来定制它,在适当的时候 findClass 会调用 loadClass。 这种方式的好处是您可能不一定要覆盖 loadClass;只要覆盖 findClass 就行了,这减少了工作量。

2)新方法:findClass
loadClass 的缺省实现调用这个新方法。findClass 的用途包含您的 ClassLoader 的所有特殊代码,而无需要复制其它代码(例如,当专门的方法失败时,调用系统 ClassLoader)。

3) 新方法:getSystemClassLoader
如果覆盖 findClass 或 loadClass,getSystemClassLoader 使您能以实际 ClassLoader 对象来访问系统 ClassLoader(而不是固定的从 findSystemClass 调用它)。

4) 新方法:getParent
为了将类请求委托给父代 ClassLoader,这个新方法允许 ClassLoader 获取它的父代 ClassLoader。当使用特殊方法,定制的 ClassLoader 不能找到类时,可以使用这种方法。
父代 ClassLoader 被定义成创建该 ClassLoader 所包含代码的对象的 ClassLoader。

2004年07月30日

LOG4J的配置之简单使它遍及于越来越多的应用中了:

Log4J配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。择其一二使用就够用了,

log4j.rootLogger=DEBUG,CONSOLE,A1,im
log4j.addivity.org.apache=true

# 应用于控制台

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=DEBUG
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d – %c -%-4r [%t] %-5p %c %x – %m%n
#log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD] n%c[CATEGORY]%n%m[MESSAGE]%n%n

#应用于文件

log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=file.log
log4j.appender.FILE.Append=false
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[framework] %d – %c -%-4r [%t] %-5p %c %x – %m%n
# Use this layout for LogFactor 5 analysis

# 应用于文件回滚

log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING_FILE.Threshold=ERROR
log4j.appender.ROLLING_FILE.File=rolling.log
log4j.appender.ROLLING_FILE.Append=true
log4j.appender.ROLLING_FILE.MaxFileSize=10KB
log4j.appender.ROLLING_FILE.MaxBackupIndex=1
log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d – %c -%-4r [%t] %-5p %c %x – %m%n

#应用于socket
log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
log4j.appender.SOCKET.RemoteHost=localhost
log4j.appender.SOCKET.Port=5001
log4j.appender.SOCKET.LocationInfo=true
# Set up for Log Facter 5
log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n

# Log Factor 5 Appender
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000

# 发送日志给邮件

log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold=FATAL
log4j.appender.MAIL.BufferSize=10
log4j.appender.MAIL.From=web@www.wuset.com
log4j.appender.MAIL.SMTPHost=www.wusetu.com
log4j.appender.MAIL.Subject=Log4J Message
log4j.appender.MAIL.To=web@www.wusetu.com
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern=[framework] %d – %c -%-4r [%t] %-5p %c %x – %m%n

# 用于数据库
log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test
log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
log4j.appender.DATABASE.user=root
log4j.appender.DATABASE.password=
log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES (‘[framework] %d – %c -%-4r [%t] %-5p %c %x – %m%n’)
log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d – %c -%-4r [%t] %-5p %c %x – %m%n

log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.File=SampleMessages.log4j
log4j.appender.A1.DatePattern=yyyyMMdd-HH’.log4j’
log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout

#自定义Appender

log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender

log4j.appender.im.host = mail.cybercorlin.net
log4j.appender.im.username = username
log4j.appender.im.password = password
log4j.appender.im.recipient = corlin@cybercorlin.net

log4j.appender.im.layout=org.apache.log4j.PatternLayout
log4j.appender.im.layout.ConversionPattern =[framework] %d – %c -%-4r [%t] %-5p %c %x – %m%n

经常和 Oracle DB 打交道的同学注意这个文章了:

http://www.huihoo.com/oracle/howto/Oracle-HOWTO.html

2004年07月29日

  每一个地方,都有属于自己地方色彩的话语,那叫做“方言”;每一个行业,都会有属于自己的话语,我们就叫它“术语”;那么,如果大学校园里也有那么一些话,是属于学生们的呢?随着网络时代的崛起,大学生们也拥有了自己的一套话语系统,用来在网络上交流,旁人一般是很难理解其中含义的,我们把这些话称作———“网话”。接下去就将为您解密校园十大“网话”。

  【给我个理由先】

  给我个不知道这句话的理由先!

  这句话的典故,出自于周星驰大哥的《大话西游》。这是典型的广东话句式,但是被石板鱼(周星驰的“御用”配音)用在普通话上面,效果一点都不差呢。大学生在网络上面强词夺理,是不是经常会把这句话抬出来?也难怪,这句话表面上看来的确很理直气壮啊。虽然仔细想想,其中的底气不是很足,但是只要你用足了语气语调,那么相信对面的那个人———哦,是网络另一端的那个人———一定会被你的气势压倒的,就算你是一千一万个没理由。

  流行指数:★★★★

  【FT(faint的缩写)】

  这句“网话”,校园外的人恐怕一时半会儿难以理解。ft就是faint的缩写,就是晕倒的意思。但是这个词语同时又具有中文的色彩:晕倒就是昏过去,用上海话讲,就是“昏脱”。有两个好事之徒,把“昏脱”故意念成“分特”,听上去自以为可爱很多。于是,ft也就成了“分特”的拼音开头字母的缩写了。

  ft这个词语在大学BBS上面实在是太出名了,不论被告知何等让人爆笑、痛哭的新闻,总是一句“ft”,足可以表达你的一切反应情绪。于是,一个人找了个女朋友,开心得手舞足蹈,要ft;陪女朋友逛街购物,花去大洋若干,要ft;回头被女朋友一脚踢开,又要ft。反正就是见面就ft,天天都ft。

  流行指数:★★★★★

  【郁闷】

  当然,这句“网话”是绝大多数人都认识的,而且可以清楚地说出其含义。但是,放到大学这个特殊环境中,“郁闷”也就呈现出许多种特殊的意思。这种情绪,在大四的毕业生中,传染、蔓延得特别快。到了大四,课已经很少很少,一边考研,一边找工作,一边郁闷。找不到工作的,当然郁闷;找到工作的,对薪酬不满意,也会郁闷;考不上研究生,当然郁闷得要命;考上研究生,面对将近一年的没有压力无所事事,不郁闷又能干嘛呢?

  流行指数:★★★★

  【……的说】

  这句“网话”也曾经让我“郁闷”了半天,因为始终搞不清楚它到底是什么意思。后来朋友跟我说,这个只不过是放在句末的语气词,没有实质性的含义。我这才恍然大悟,ft一回。后来,经过笔者的亲身体验,多方考证,发现这个“……的说”,其中还是有一点规律可循的。

  “他又被女朋友抛弃的说。”这句话,往往指这件事情还是道听途说,没有实际求证过,因为对于事件的真实性,保留一份质疑。“她竟然已经跟会计事务所签约的说!”这句话就表示对这件事情非常的震惊,对于“她”能够签约会计事务所表示出强烈的不满,因为“她”平时成绩还没有我好呢。话里透着一丝心酸和不服气。

  总之,“……的说”在不同语境下有不同的情绪表达,要仔细区分哦。

  流行指数:★★★★

  【人品问题】

  这句“网话”,就相当于过去人们把一切都归结为“作风问题”一样,现在就归在“人品问题”上了。当一个人对另外一个人评价是“人品问题”的时候,语气中多少带有一点不屑。听说某人找工作碰壁、跟女朋友分手,便有些幸灾乐祸地说一声:“他么,人品问题呀!”一句话就把人家给“定性”了。虽然说者并非有意,但多少有些武断呢。

  流行指数:★★★

  【**】

  这可能是一句脏话,但是又不算十分的脏,而且似乎朗朗上口,十分带劲,所以在大学校园中,这句话的使用频率极高。男生用很激昂的语气说来,多少带有那么一点豪爽吧!这句“网话”也是适用于各种场合,只要表示惊叹,都可以拿来用用。不过奉劝一声,女生最好不要学习这句“网话”,因为毕竟男女有别,这句话稍微粗俗了一些。

  流行指数:★★★★★

  【我晕】

  这句话的前身是“倒”。跟“ft”差不多,都是对于某件让人大跌眼镜的事情,表示出最大的惊诧。所以,除了“晕倒”,没有别的表达方式了。这个词常常可以与“竟然”等连用。“那个一学期根本没来上过课的他竟然考上了研究生,我晕!”由此可见,被“晕”的对象,往往是极其厉害的,总是能够做出出人意表的事情来,才有资格让人家“晕”。

  流行指数:★★★★★

  【我闪先】

  “兄弟我闪先!”多么具有卡通意味的话啊。使用这句话最多的,估计就是班里逃课逃得最多的那位老兄。上课之前到教室里亮个相,看看老师是不是点名。如果不点名,那么就在老师转身写黑板的时候,扔给同桌一句“我闪先”,便消失得没有踪影了;如果点名,那就坐在座位上,老老实实喊一声“到”,然后,在课间休息的时候,再找机会“闪”。

  看到这里你应该明白了,这个“闪”可不是说他会发光,而是逃课的意思,跟普通话中“闪开”的意思有点接近。但是这句“网话”具有更深层次的引申含义。他们的“闪”,还带有迅雷不及掩耳之势,趁人不备(尤其是趁老师不备),便溜之大吉。于是,毕业的时候,可以深情地朗诵一句:“轻轻的,我闪了,不带走一片云彩。”岂不是很有诗意?

  流行指数:★★★★

  【大跌隐形眼镜】

  大学里的“网话”,就是要跟别人的有所不同。“大跌眼镜”是过时的说法,在大学里,要跌的,至少也是“博士伦”级的隐形眼镜。其实,两者表达的意思都差不多,但是,大学生爱美、追求时尚的精神却可从中窥见一斑。进了大学,估计10个中有8个会去配上一副隐形眼镜,在学校圣诞舞会的时候可以派上用场。那个时候,少了一副“架梁”的你,是否感到自信无比呢?在这样的情况下,如果你再说一声“大跌眼镜”,可就与实际情况不符了,为了显示你的优雅,大可说:“真是让我大跌隐形眼镜啊!”这才符合“校情”呢。其实这么一来,无论你是不是近视,都可以附庸风雅一回,谁知道你是否真的戴着隐形眼镜呢?

  流行指数:★★★

  【牛人与歇菜】

  首先要声明,校园“网话”中的“牛人”,跟我们以往耳熟能详的“狼孩”“猪孩”有本质的区别,“牛人”并非是在牛圈中长大,而跟所有学生一样,住在学生宿舍里。但是说他们是“牛人”,那是因为他们往往在学习或者工作中,表现得“牛气冲天”“牛气逼人”。“牛人”不分男女,有能力者皆有资格当选。比如班上一个女生,年年考试拿一等奖学金,四年大学的成绩绩点竟然高达3.9(满分为4.0),那么我们大可以扶住自己的隐形眼镜不让它掉出来,同时赞叹一声“牛人”。

  与之恰恰相反的,倘若是一个不甚了了的人,长相、学习、工作都相当的平庸,那么也常常会被别人挪揄。明明自己想了个颇不错的点子,却被同学说:“得了吧,你歇菜吧!”一句话就把刚露头的灵感给打没了。

  不过,这些都是同学之间的调侃,“牛人”不一定永远“牛”下去,“歇菜”的人也不一定永远都“菜”。只要踏实努力,“歇菜”也会变“牛人”哦。

  流行指数:★★★★

2004年07月27日

内容简介:本文通过例子讲解了如何利用Java的特性快速编写安全可靠的NT服务,并展示了Java的多线程如何实施,以及如何应用套接字实现网络服务。

关键词:Java  JntSvc.exe NT服务 多线程 套接字编程

 

一、NT服务介绍

所谓NT服务,实际上是一类特殊的应用程序所谓NT服务,实际上就是一个可以在系统启动时自动在一定身份下启动的伴随系统长时间存在的进程。象FTP server、HTTP server、脱机打印等都是采用NT服务的形式提供的。这实际上类似Unix的root daemon进程。NT服务归纳起来,NT服务又以下几个特征:

1、可以自启动,不需要交互启动。这对于服务器来说是一个重要的特征。当然,你可以决定服务是否自启动,甚至可以屏蔽某个服务。
    2、NT服务没有用户界面,基本上类似一个DOS 程序,因为NT服务必须长时间运行,所以不想普通win32进程一样有自己的界面。但是NT服务可以同用户有界面交互,这是一类特殊的服务进程。可以通过NT的任务管理器来看到服务进程。
    3、NT服务通过SCM(Services Control Manager)接口来管理,安装、启动、停止、撤除等都需要SCM的接口功能来进行。控制面板的服务控制器就是利用SCM接口来管理系统中的所有服务的。实际上,还有一些可以控制服务的程序或者命令,有net.exe 、服务器管理器等 、SCM.exe等。
    4、这些进程都以一定的身份运行,以方便进行服务器资源的存取。一般情况下使用域中的LocalSystem账号运行,此账号对本机上的大多数资源(除非特别禁止)有完全的存取权限,这样可以保证服务程序的“强大”。但是,也有些服务采用特别的账号运行,你也可以特别设定一个服务的帐号。
    5、由系统自动以线程方式运行,一般情况下不过多占用系统资源,这同普通的进程有所区别,如果不采用线程方式,一般进程往往消耗整个CPU资源。一般需要时时存在,又不能过多消耗资源的任务以服务来实现最合适。

 

二、Java编写服务的准备

1、作为本地化的实现,实现NT服务的Java程序当然不是100%纯Java,单靠标准类库是无法实现我们的编写NT服务的目的,所以MS提供了一套SDK for Java(本文采用的是Microsoft SDK for Java 4.0),提到了如何利用MS提供的扩展类库和相应的工具,实现符合Windows平台需要的程序。其中包括了实现NT服务的所需要的类库API框架以及将Java编译的class文件组装成标准的NT服务程序的工具。SDK的下载路径可以从www.microsoft.com/java/查找到。

2、安装完SDK后可以看到在安装目录下有jntsvc目录,此目录就包含了service.zip文件,它实际上是一个NT services的类库框架,封装了一些NT服务实现细节,使得我们可以按照框架舒服实现我们关心的细节。将service.zip展开至开发机器的系统安装Service库到Java扩展库\Winnt\java\TrustLib下,如果在其他操作系统下进行开发,参照此系统目录进行安装文件。

3、在该目录下还有一个jntsvc.exe文件,也就是Java NT Service的意思啦。她可以帮助您实现将按照SDK提供的框架实现的编译后的class文件组装成一个标准的NT服务可执行文件。JntSvc帮助我们在已经编译好的.class文件基础上设置了所有NT服务程序必须的特征,是很重要的工具,得到NT服务取决于如何有效利用她。为了我们能够方便从任何其他目录的控制台窗口调用她,我们将JntSvc.exe所在的目录全路径加入path环境变量。这可以通过设置系统属性的高级属性页当中进行环境变量的设定。

4、按照要求,我们写好各项代码,然后编译编写Java程序,得到class文件。我们当然不会在Vj Studio中启动她,因为它目前还没有可执行文件的入口,系统无法启动她。为了得到NT服务程序,我们需要在class文件所在目录的控制台窗口执行一个命令:X:>jntsvc *.class /OUT:ECHOSvc.exe /SVCMAIN:EchoSvc          ”/SERVICENAME:ECHOSvc”。具体的Jntsvc的参数我们可以看一看jntsvc -?得到,这里的意思大概是:将当前目录下的所有class文件组装成一个NT服务进程exe文件,文件名为EchoSvc.exe,服务的启动入口在echosvc.class中,在注册表中相应的服务名称为/Servicename参数指定的EchoSvc。如果有多个多个NT服务需要组装在一个Exe文件中,还可以在 /Out参数后指定每一个服务展示名称。/SVCMAIN参数指定服务的入口,所谓入口是指服务启动之初是从哪一个类的实例开始的。”/SERVICENAME:”参数指定了该服务将以什么名称出现。这些参数都是jntsvc.exe实用工具需要组装服务所必须的信息,根据这些信息将编译后的.class文件按照win32格式要求得到一个可执行文件。

需要注意的是,这个exe文件的运行必须要有JVM存在,她实际上是通过解释.class来实现服务提供的。如果需要另外的扩展包,可以通过在/Classpath参数指定另外的扩展包的位置。所以在安装Java编写得到的NT服务的机器上必须存在JVM。如果是拥有IE5.x那么不用操心这个问题,IE核心组件已经包括了JVM;但是如果是IE6版本,则需要到MS的网站上下载JVM。如果您讲SDK for Java安装在服务器上就更方便了。

5、如果没有什么错误,您将得到一个可执行文件echosvc.exe。像大多数服务可执行文件一样,它可以将自己安装到系统中: echosvc.exe ?install,这一个过程将会往系统注册表添加一些项目,特别是关于服务的项目,SCM也可以列出这个服务了。我们可以在控制台下采用DOS NT服务控制命令Net start/stop来测试服务是否真像普通服务一样可以按照标准方式来控制,当然在服务管理器当中启停该服务更不会有问题。

 

三、Echo服务的样例

当系统载入服务进程时,入口是在EchoSvc的构造函数中,我们可以看到此构造函数带有同一般程序的入口main()类似的参数。
import com.ms.service.* ;

 public class EchoSvc extends Service 

{     static Thread mainSvc=null ; //定义服务主线程

       public  EchoSvc (String[] args) //构造此服务

       {

         CheckPoint(1000);    

              setRunning(ACCEPT_SHUTDOWN | ACCEPT_PAUSE_CONTINUE |ACCEPT_STOP); // 该项服务接受的关于服务控制的命令

              mainSvc = new Thread((Runnable) new  MainSvcThread());

        mainSvc.start();     

       System.out.println( ”The Echo Service Was Started Successfully!”);//纪录事件,可以通过事件察看器看到

       }

}

CheckPoint是 Service的同步方法,指示系统正改变服务的状态,需要让系统等待1秒。这里我们启动的是一个线程,实际上相当于一个进程,她是服务进程的主线程。在这个线程中我们响应SCM对此服务的控制。大致的表达为:

public class MainSvcThread implements Runnable //实现线程控制

{     

public static boolean STOP = false;  //由系统来控制的内部变量,决定着服务进程(线程)的启动、暂停等

      public static boolean PAUSE = false; 

  

         public void run()

         {

        while (!STOP)

                 {  

                        while (!PAUSE && !STOP) 

                        {

                              。。。//此处为服务控制逻辑,下面会充实此处

                      }

                try 

                {Thread.sleep(5000);//休眠5秒后实现暂停或者停止} 

                catch (InterruptedException e) 

{ }

              }

            try 

                     {Thread.sleep(1000);}

                     catch (InterruptedException ie) 

                     {}

         }

         }  //Run结束  

}

在服务逻辑控制当中,我们会具体实现Echo服务。我们的Echo服务监听2002端口,接收客户端任何一行输入,然后加上“Echo:”后返回。如果客户端输入一个quit词组那么服务认为这是客户关闭此套接字的命令,会自动关闭当前的套接字连接,停止对当前连接的服务。具体的实现(EchoThread.java的代码):

public void run()

       {

        String line;

        DataInputStream in;

        PrintWriter out;

        boolean exitflag=false;

 

       try

       {

        in=new DataInputStream(so.getInputStream()) ;//获取套接字的输入流

        out=new PrintWriter(new DataOutputStream(so.getOutputStream())) ;

      out.println(“You have connected to EchoSvc!”);  //发送问候

        out.flush(); 

        while((line=in.readLine())!=null) //读取

         {

               line=line.trim();

               if (line.equalsIgnoreCase(“quit”) )

               {

           out.println(“ECHO:” + line );

                 out.flush();

return;

}

               else

               {

               out.println(“ECHO:” + line );

               out.flush(); 

               }

         }

        in.close();

        out.close();

}

catch(IOException ioe)

              {}

}

Echo服务主要就是将客户发送的字符回显给客户,并加上Echo:的前缀,以表明是从服务器返回的内容。如果客户输入“quit”那么表示这是要求服务器停止服务的表现。

如何调试NT服务进程工程。如果直接将此函数调用来提供客户端的ECHO套接字服务,逻辑上是没有什么错误,但是就是无法支持多个用户同时访问。为了能够提供多服务,允许同时又多个用户连接此服务器(这种情况在很多网络服务都不可少),我们可以将此逻辑在由MainSvcTread创建的线程中实现,而且可以允许多个用户同时访问此服务。具体的表达在MainSvcTread的run函数中实现:

while(ListenThreadCount<maxSocket) //如果当前启动的线程数在系统允许的范围内

{

          server=li.accept(); //监听

          EchoThread p=new EchoThread(server,this);//创建实现该服务的具体逻辑对象,是一个支持线程的类

          Thread t=new Thread(g,(Runnable)p) ; //将当前线程并入线成组

          t.start(); //启动服务线程

          ListenThreadCount++; //修改当前线程数量

       }

参照上面提到的工具Jntsvc.exe可以帮助你讲编译好的.class文件组装成exe文件,运行此文并加上-install参数可以自动帮助您讲些好的服务添加到注册表中,可以通过服务管理器或者相当的实用程序来如同其他服务一样来进行控制了。撤除服务采用-uninstall参数。

本例程采用套接字、多线程实现技术来解释实现Java编写NT服务,实际上类似这样的很多网络方面的服务都可以按照此规范实现,譬如POP3服务、FTP服务,甚至WWW服务等。我们也接触过像Tomcat、Jrun等Java应用服务器在NT平台的启动往往采用NT服务形式,那么通过此例你也可以尝试编写自己的Java服务应用。

    最后,如果需要调试NT服务的逻辑,可以采用一个变通的办法。我们在EchoSvc.java中添加一个Main静态方法,然后产生一个EchoSvc的实例,这样就是一个标准的VJ产生的Exe文件,利用Vj的调试功能我们可以排除隐藏的错误。一旦调通后,我们注释掉main静态方法,编译后就可以得到一个调试好的NT服务。

 

四、为什么要采用Java编写NT服务

比较VC等“原装”NT服务开发方式而言,Java开发模式可以更快捷,因为几乎所有得服务框架通过扩展Services类就可以达到,省下不少复杂的细节处理。Java语言提供了丰富的类库,可以为自己使用,可以提高效率,而且编写的程序结构清晰容易理解,方便以后维护。

Java提供的异常处理模式,可以让我们写好结构良好,更加安全的代码。试想如果编写的服务进程由于采用VC编写却忘记对某块内存的释放,那么服务启动后一段时间由于内存泄漏造成服务性能下降,甚至系统崩溃;但是Java本身的语言特性可以使我们不用时刻提防内存管理,可以更加关注服务逻辑本身,是的实现起来更加有效率。

采用VC如果编写多线程服务进程,虽然可以实现,但是会相当麻烦。而服务进程多线程几乎是每一个性能良好的服务必备特征,Java语言本身可以提供这方面良好的支持,同时Java自身对网络的天然良好支持,使各种网络套接字编程容易。

最后,如果不采用其他扩展库,我们很容易将此服务逻辑实现在其他操作系统上。一个编写好的NT服务程序,可以在去掉对Ms的相关本地化扩展实现的类引用后,方便移植到其他例如Linux等平台上,尽可能向Java的“一次编写、到处可运行”的理想境界靠拢。

 

五、源码

/*所附的ZIP文件报含示例的全部工程文件,还有编译后的NT服务的可执行文件,您可以直接测试此服务exe文件的安装、服务启停*/

/*EchoSvc.java*/

import com.ms.service.* ;

public class EchoSvc extends Service 

{

       static Thread mainSvc=null ; //定义主线程

 

       public  EchoSvc (String[] args) //构造服务

       {

                CheckPoint(1000);    //服务是系统的一部分,作为Log纪录,可以帮助用户理解系统故障

                     setRunning(ACCEPT_SHUTDOWN | ACCEPT_PAUSE_CONTINUE |ACCEPT_STOP);

                     mainSvc = new Thread((Runnable) new  MainSvcThread());

            mainSvc.start();     

                     System.out.println( ”The Echo Service Was Started Successfully!”);

       }

}

/*————– EchoSvc.java源码结束——————-*/

 

/*MainSvcThread.java*/

import java.io.*;

import java.net.*;

public class MainSvcThread implements Runnable //实现线程控制多线程接口

{     /将启动一组线程来监听多个服务请求

         public static boolean STOP = false;  //由系统来控制的内部变量,决定着服务进程(线程)的启动、暂停等

      public static boolean PAUSE = false; 

         public int ListenThreadCount=0;  //本服务支持的当前线程数量

         int maxSocket=10;  //最大支持的同时连结数

         int SvcPort=2002;  //服务监听的端口

         

         public void run()

         {

        try

               {

           while (!STOP)

                 {  

                        while (!PAUSE && !STOP) 

                        {

                               {//创建监听服务器

                             Socket server;  

                             ServerSocket  li=new ServerSocket(SvcPort);  //创建服务器端套接字

                             ThreadGroup g=new ThreadGroup(“EchoThreads”); //创建一组线程

                    System.out.println(“Echo service starting…”);  //记录在Log中

                             while(ListenThreadCount<maxSocket)

                                      {

                                             server=li.accept();  //监听

                                             EchoThread p=new EchoThread(server,this); //创建服务单线程

                                             Thread t=new Thread(g,(Runnable)p) ; //创建新线程

                                             t.start(); //启动服务线程

                                             ListenThreadCount++; //当前线程的数量

                                      }

                      }

                   try 

                   {

                     Thread.sleep(5000);//暂停5秒

                    } 

                    catch (InterruptedException e) 

{   }

              }

               try 

                         {

                           Thread.sleep(1000);

                         }

                        catch (InterruptedException ie) 

                       {  }

          }

              }

              catch (IOException ioe)

              {} 

         }  //Run结束  

}

/*————– MainSvcThread.java源码结束——————-*/

 

/*EchoThread.java*/

import java.io.*;

import java.net.*;

/*实现每一个客户连接到此NT服务时的服务器端的线程单元逻辑*/

public class EchoThread implements Runnable   //实现线程接口

{

    Socket so=null;//套接字

       MainSvcThread p;  //一个指向父线程的指针,EchoThread的线程是服务线程的创建的子线程

       public void run()

              {

               String line;

               DataInputStream in; //套接字上的输入流

               PrintWriter out;   //套接字上的输出流,带缓冲

               boolean exitflag=false;

              try

              {

               in=new DataInputStream(so.getInputStream()) ;//获取套接字的输入流

               out=new PrintWriter(new DataOutputStream(so.getOutputStream())) ;

            out.println(“You have connected to EchoSvc!”);  //发送问候

               out.flush();   //必须刷新缓冲区内的内容

 

               while((line=in.readLine())!=null && ! exitflag)

                {

                      line=line.trim();

                      if (line.equalsIgnoreCase(“quit”) )

                      {//如果是退出命令,则关闭当前套接字上的输入输出流

                            in.close();

                out.flush();

                            out.close();

p.ListenThreadCount –; //主线程的服务线程单元数量控制

                            return;   //退出当前的服务逻辑线程单元

                      }

                      else

                      {

                      out.println(“ECHO:” + line );

                      out.flush(); 

                      }

                 }

          in.close();

                out.close();

                p.ListenThreadCount –;

              }

              catch(IOException ioe)

              {}

       }

       

  EchoThread(Socket s,MainSvcThread parent)

       {

              so=s;

              p= parent;

       }

}

/*————– EchoThread.java源码结束——————-*/