junhong

2006年4月13日 #

软件项目管理

软件项目管理

      软件项目管理是为了使软件项目能够按照预定的成本、进度、质量顺利完成,而对人员(People)、产品(Product)、过程(Process)和项目(Project)进行分析和管理的活动。

      软件项目管理的根本目的是为了让软件项目尤其是大型项目的整个软件生命周期(从分析、设计、编码到测试、维护全过程)都能在管理者的控制之下,以预定成本按期,按质的完成软件交付用户使用。而研究软件项目管理为了从已有的成功或失败的案例中总结出能够指导今后开发的通用原则,方法,同时避免前人的失误。

      软件项目管理的提出是在20世纪70年代中期的美国,当时美国国防部专门研究了软件开发不能按时提交,预算超支和质量达不到用户要求的原因,结果发现70%的项目是因为管理不善引起的,而非技术原因。于是软件开发者开始逐渐重视起软件开发中的各项管理。到了20世纪90年代中期,软件研发项目管理不善的问题仍然存在。据美国软件工程实施现状的调查,软件研发的情况仍然很难预测,大约只有10%的项目能够在预定的费用和进度下交付。

      1995年,据统计,美国共取消了810亿美元的商业软件项目,其中31%的项目未做完就被取消,53%的软件项目进度通常要延长50%的时间,只有9%的软件项目能够及时交付并且费用也控制在预算之内。

      软件项目管理和其他的项目管理相比有相当的特殊性。首先,软件是纯知识产品,其开发进度和质量很难估计和度量,生产效率也难以预测和保证。其次,软件系统的复杂性也导致了开发过程中各种风险的难以预见和控制。Windows这样的操作系统有1500万行以上的代码,同时有数千个程序员在进行开发,项目经理都有上百个。这样庞大的系统如果没有很好的管理,其软件质量是难以想象的。

      软件项目管理的内容主要包括如下几个方面:人员的组织与管理,软件度量软件项目计划风险管理软件质量保证软件过程能力评估,软件配置管理等。

      这几个方面都是贯穿、交织于整个软件开发过程中的,其中人员的组织与管理把注意力集中在项目组人员的构成、优化;软件度量把关注用量化的方法评测软件开发 中的费用、生产率、进度和产品质量等要素是否符合期望值,包括过程度量和产品度量两个方面;软件项目计划主要包括工作量、成本、开发时间的估计,并根据估 计值制定和调整项目组的工作;风险管理预测未来可能出现的各种危害到软件产品质量的潜在因素并由此采取措施进行预防;质量保证是保证产品和服务充分满足消 费者要求的质量而进行的有计划,有组织的活动;软件过程能力评估是对软件开发能力的高低进行衡量;软件配置管理针对开发过程中人员、工具的配置、使用提出管理策略。因为大家对人力资源管理和软件过程能力比较有兴趣,下面就详细的对这两方面展开讨论。

一.软件项目的计划

      软件项目计划是一个软件项目进入系统实施的启动阶段,主要进行的工作包括:确定详细的项目实施范围、定义递交的工作成果、评估实施过程中主要的风险、制定项目实施的时间计划、成本和预算计划、人力资源计划等。

      软件项目管理过程从项目计划活动开始,而第一项计划活动就是估算:需要多长时间、需要多少工作量、以及需要多少人员。此外,我们还必须估算所需要的资源(硬件及软件)和可能涉及到的风险。

      为了估算软件项目的工作量和完成期限,首先需要预测软件规模。度量软件规模的常用方法有直接的方法——LOC(代码行),间接的方法——FP(功能点)。这两种方法各有优缺点,应该根据软件项目的特点选择适用的软件规模度量方法。

      根据项目的规模可以估算出完成项目所需的工作量,我们可以使用一种或多种技术进行估算,这些技术主要分为两大:分解和经验建模。分解技术需要划分出主要的软件功能,接着估算实现每一个功能所需的程序规模或人月数。经验技术的使用是根据经验导出的公式来预测工作量和时间。可以使用自动工具来实现某一特定的经验模型。

      精确的项目估算一般至少会用到上述技术中的两种。通过比较和协调使用不同技术导出的估算值,我们可能得到更精确的估算。软件项目估算永远不会是一门精确的科学,但将良好的历史数据与系统化的技术结合起来能够提高估算的精确度。

      当对软件项目给予较高期望时,一般都会进行风险分析。在标识、分析和管理风险上花费的时间和人力可以从多个方面得到回报:更加平稳的项目进展过程;更高的跟踪和控制项目的能力;由于在问题发生之前已经做了周密计划而产生的信心。

      对于一个项目管理者,他的目标是定义所有的项目任务,识别出关键任务,跟踪关键任务的进展情况,以保证能够及时发现拖延进度的情况。为此,项目管理者必须制定一个足够详细的进度表,以便监督项目进度并控制整个项目。

      常用的制定进度计划的工具主要有Gantt图和工程网络两种。Gantt图具有悠久历史、直观简明、容易学习、容易绘制等优点,但是,它不能明显地表示各 项任务彼此间的依赖关系,也不能明显地表示关键路径和关键任务,进度计划中的关键部分不明确。因此,在管理大型软件项目时,仅用Gantt图是不够的,不 仅难于做出既节省资源又保证进度的计划,而且还容易发生差错。

      工程网络不仅能描绘任务分解情况及每项作业的开始时间和结束时间,而且还能清楚地表示各个作业彼此间的依赖关系。从工程网络图中容易识别出关键路径和关键 任务。因此,工程网络图是制定进度计划的强有力的工具。通常,联合使用Gantt图和工程网络这两种工具来制定和管理进度计划,使它们互相补充、取长补 短。

      进度安排是软件项目计划的首要任务,而项目计划则是软件项目管理的首要组成部分。与估算方法和风险分析相结合,进度安排将为项目管理者建立起一张计划图。

二.软件项目的控制

      对于软件开发项目而言,控制是十分重要的管理活动。下面介绍软件工程控制活动中的质量保证和配置管理。其实上面所提到的风险分析也可以算是软件工程控制活动的一类。而进度跟踪则起到连接软件项目计划和控制的作用。

      软件质量保证(SQA,Software Quality Insurance)是在软件过程中的每一步都进行的“保护性活动”。SQA主要有基于非执行的测试(也称为评审)、基于执行的测试(即通常所说的测试)和程序正确性证明。

      软件评审是最为重要的SQA活动之一。它的作用是,在发现及改正错误的成本相对较小时就及时发现并排除错误。审查和走查是进行正式技术评审的两类具体方 法。审查过程不仅步数比走审多,而且每个步骤都是正规的。由于在开发大型软件过程中所犯的错误绝大数是规格说明错误或设计错误,而正式的技术评审发现这两 类错误的有效性高达75%,因此是非常有效的软件质量保证方法。

      软件配置管理(SCM,Software configuration management)是应用于整个软件过程中的保护性活动,它是在软件整个生命周期内管理变化的一组活动。

      软件配置由一组相互关联的对象组成,这些对象也称为软件配置项,它们是作为某些软件工程活动的结果而产生的。除了文档、程序和数据这些软件配置项之外,用于开发软件的开发环境也可置于配置控制之下。

      一旦一个配置对象已被开发出来并且通过了评审,它就变成了基线。对基线对象的修改导致建立该对象的版本。版本控制是用于管理这些对象而使用的一组规程和工具。

      变更控制是一种规程活动,它能够在对配置对象进行修改时保证质量和一致性。配置审计是一项软件质量保证活动,它有助于确保在进行修改时仍然保持质量。状态报告向需要知道关于变化的信息的人,提供有关每项变化的信息。

三、软件项目管理的组织模式

      软件项目可以是一个单独的开发项目,也可以与产品项目组成一个完整的软件产品项目。如果是订单开发,则成立软件项目组即可;如果是产品开发,需成立软件项 目组和产品项目(负责市场调研和销售),组成软件产品项目组。公司实行项目管理时,首先要成立项目管理委员会,项目管理委员会下设项目管理小组、项目评审 小组和软件产品项目组。

      3.1、项目管理委员会项目管理委员会是公司项目管理的最高决策机构,一般由公司总经理、副总经理组成。主要职责如下:
(1)依照项目管理相关制度管理项目;
(2)监督项目管理相关制度的执行;
(3)对项目立项、项目撤消进行决策;
(4)任命项目管理小组组长、项目评审委员会主任、项目组组长.

      3.2、项目管理小组项目管理小组对项目管理委员会负责,一般由公司管理人员组成。主要职责如下:
(1)草拟项目管理的各项制度;
(2)组织项目阶段评审;
(3)保存项目过程中的相关文件和数据;
(4)为优化项目管理提出建议。

      3.3、项目评审小组项目评审小组对项目管理委员会负责,可下设开发评审小组和产品评审小组,一般由公司技术专家和市场专家组成。主要职责如下:
(1)对项目可行性报告进行评审;
(2)对市场计划和阶段报告进行评审;
(3)对开发计划和阶段报告进行评审;
(4)项目结束时,对项目总结报告进行评审。

      3.4、软件产品项目组软件产品项目组对项目管理委员会负责,可下设软件项目组和产品项目组。软件项目组和产品项目组分别设开发经理和产品经理。成员一般 由公司技术人员和市场人员构成。主要职责是:根据项目管理委员会的安排具体负责项目的软件开发和市场调研及销售工作。

四、软件项目管理的内容

      从软件工程的角度讲,软件开发主要分为六个阶段:需求分析阶段、概要设计阶段、详细设计阶段、编码阶段、测试阶段、安装及维护阶段。不论是作坊式开发,还是团队协作开发,这六个阶段都是不可缺少的。根据公司实际情况,公司在进行软件项目管理时,重点将软件配置管理、项目跟踪和控制管理、软件风险管理及项目策划活动管理四方面内容导入软件开发的整个阶段。在20世纪80年代初,著名软件工程专家B.W.Boehm总结出了软件开发时需遵循的七条基本原则,同样,在进行软件项目管理时,也应该遵循这七条原则。它们是:
(1)用分阶段的生命周期计划严格管理;
(2)坚持进行阶段评审;
(3)实行严格的产品控制;
(4)采用现代程序设计技术;
(5)结果应能够清楚地审查;
(6)开发小组地人员应该少而精;
(7)承认不断改进软件工程实践的必要性。

五、编写《软件项目计划书》

      项目组成立的第一件事是编写《软件项目计划书》,在计划书中描述开发日程安排、资源需求、项目管理等各项情况的大体内容。计划书主要向公司各相关人员发放,使他们大体了解该软件项目的情况。对于计划书的每个内容,都应有相应具体实施手册,这些手册是供项目组相关成员使用的。

六、软件配置管理

      是否进行配置管理与软件的规模有关,软件的规模越大,配置管理就显得越重要。软件配置管理简称SCM(Software Configuration Management的缩写),是在团队开发中,标识、控制和管理软件变更的一种管理。配置管理的使用取决于项目规模和复杂性以及风险水平。

      6.1、目前软件开发中面临的问题:在有限的时间、资金内,要满足不断增长的软件产品质量要求;开发的环境日益复杂,代码共享日益困难,需跨越的平台增多;程序的规模越来越大;软件的重用性需要提高;软件的维护越来越困难。

      6.2、软件配置管理应提供的功能:
      在ISO9000.3中,对配置管理系统的功能作了如下描述:唯一地标识每个软件项的版本;标识共同构成一完整产品的特定版本的每一软件项的版本;控制由 两个或多个独立工作的人员同时对一给定软件项的更新;控制由两个或多个独立工作的人员同时对一给定软件项的更新;按要求在一个或多个位置对复杂产品的更新 进行协调;标识并跟踪所有的措施和更改;这些措施和更改是在从开始直到放行期间,由于更改请求或问题引起的。

      6.3、版本管理软件配置管理分为版本管理、问题跟踪和建立管理三个部分,其中版本管理是基础。版本管理应完成以下主要任务:
 
      建立项目;
      重构任何修订版的某一项或某一文件;
      利用加锁技术防止覆盖; ?当增加一个修订版时要求输入变更描述;
      提供比较任意两个修订版的使用工具;
      采用增量存储方式;
      提供对修订版历史和锁定状态的报告功能;
      提供归并功能;
      允许在任何时候重构任何版本;
      权限的设置;
      晋升模型的建立;
      提供各种报告。

七. 人员组织与管理

      软件开发中的开发人员是最大的资源。对人员的配置、调度安排贯穿整个软件过程,人员的组织管理是否得当,是影响对软件项目质量的决定性因素。

      首先在软件开发的一开始,要合理的配置人员,根据项目的工作量、所需要的专业技能,再参考各个人员的能力、性格、经验,组织一个高效、和谐的开发小组。一 般来说,一个开发小组人数在5到10人之间最为合适,如果项目规模很大,可以采取层级式结构,配置若干个这样的开发小组。

      在选择人员的问题上,要结合实际情况来决定是否选入一个开发组员。并不是一群高水平的程序员在一起就一定可以组成一个成功的小组。作为考察标准,技术水 平、与本项目相关的技能和开发经验、以及团队工作能力都是很重要的因素。一个一天能写一万行代码但却不能与同事沟通融洽的程序员,未必适合一个对组员之间 通讯要求很高的项目。还应该考虑分工的需要,合理配置各个专项的人员比例。例如一个网站开发项目,小组中有页面美工、后台服务程序、数据库几个部分,应该合理的组织各项工作的人员配比。对于一个中型农技110网站,对数据采集量要求较高,一个人员配比方案可以是2个美工、2个后台服务程序编写、3个数据采集整理人员。

     可以用如下公式来对候选人员能力进行评分,达到一定分数的则可以考虑进入开发组,但这个公式不包含对人员数量配比的考虑。
     Score=∑WiCi(i=1to8)
      Ci是对项目组人员各项能力的评估。其值含义如下

      在决定一个开发组的开发人员数量时,除了考虑候选人素质以外,还要综合考虑项目规模、工期、预算、开发环境等因素的影响,下面是一个基于规模、工期和开发环境的人员数量计算公式:
      L=Ck*K1/3*td4/3
      L:开发规模,以代码行LOC为度量td:开发时间K:人员数
      Ck:技术常数表示开发环境的优劣
      取值2000:表示开发环境差,没有系统的开发方法,缺乏文档规范化设计;
      取值8000:表示开发环境较好;
      取值11000:表示开发环境优。

      在组建开发组时,还应充分估计到开发过程中的人员风险。由于工作环境、待遇、工作强度、公司的整体工作安排和其他无法预知的因素,一个项目尤其是开发周期 较长的项目几乎无可避免的要面临人员的流入流出。如果不在项目初期对可能出现的人员风险进行充分的估计,作必要的准备,一旦风险转化为现实,将有可能给整 个项目开发造成巨大的损失。以较低的代价进行及早的预防是降低这种人员风险的基本策略。具体来说可以从以下几个方面对人员风险进行控制:

      a.保证开发组中全职人员的比例,且项目核心部分的工作应该尽量由全职人员来担任, 以减少兼职人员对项目组人员不稳定性的影响。
      b.建立良好的文档管理机制,包扩项目组进度文档、个人进度文档、版本控制文档、整体技术文档、个人技术文档、源代码管理等。一旦出现人员的变动,比如某个组员因病退出,替补的组员能够根据完整的文档尽早接手工作。
      c.加强项目组内技术交流,比如定期开技术交流会,或根据组内分工建立项目组内部的开发小组,是开发小组内的成员能够相互熟悉对方的工作和进度,能够在必要的时候替对方工作。
      d.对于项目经理,可以从一开始就指派一个副经理在项目中协同项目经理管理项目开发工作,如果项目经理退出开发组,副经理可以很快接手。但是只建议在项目经理这样的高度重要的岗位采用这种冗余复制的策略来预防人员风险,否则将大大增加项目成本。
      e.为项目开发提供尽可能好的开发环境,包括工作环境、待遇、工作进度安排等等,同 时一个优秀的项目经理应该能够在项目组内营造一种良好的人际关系和工作氛围。良好的开发环境对于稳定项目组人员以及提高生产效率都有不可忽视的作用。

八.软件过程能力评估

      软件过程能力描述了一个开发组织开发软件开发高质量软件产品的能力。现行的国际标准主要有两个:ISO9000.3和CMM

      ISO9000.3是ISO9000质量体系认证中关于计算机软件质 量管理和质量保证标准部分。它从管理职责、质量体系、合同评审、设计控制、文件和资料控制、采购、顾客提供产品的控制、产品标识和可追溯性、过程控制、检 验和试验、检验/测量和试验设备的控制、检验和试验状态、不合格品的控制、纠正和预防措施、搬运/贮存/包装/防护和交付、质量记录的控制、内部质量审 核、培训、服务、统计系统等二十个方面对软件质量进行了要求。

      CMM(能力成熟度模型)是美国卡纳基梅隆大学软件工程研究所(CMU/SEI)于1987年提出的评估和指导软件研发项目管理的一系列方法,用5个不断进化的层次来描述软件过程能力。现在CMM是2.0版本。

      ISO9000和CMM的共同点是二者都强调了软件产品的质量。所不同的是,ISO9000强调的是衡量的准则,但没有告诉软件开发人员如何达到好的目 标,如何避免差错。CMM则提供了一整套完善的软件研发项目管理的方法。它可告诉软件开发组织,如果要在原有的水平上提高一个等级,应该关注哪些问题,而 这正是改进软件过程的工作。

      CMM描述了五个级别的软件过程成熟度(初始级,可重复级,已定义级,已定量管理级,优化级),成熟度反映了软件过程能力的大小。

      初始级特点是软件机构缺乏对软件过程的有效管理,软件过程是无序的,有时甚至是混乱的,对过程几乎没有定义,其软件项目的成功来源于偶尔的个人英雄主义而 非群体行为,因此它不是可重复的;可重复级的特点是软件机构的项目计划和跟踪稳定,项目过程可控,项目的成功是可重复的;已定义级的特点在于软件过程已被 提升成标准化过程,从而更加具有稳定性、可重复性和可控性;已定量管理级的软件机构中软件过程和软件产品都有定量的目标,并被定量地管理,因而其软件过程 能力是可预测的,其生产的软件产品是高质量的;优化级的特点是过程的量化反馈和先进的新思想、新技术促进过程不断改进,技术和过程的改进改进被作为常规的 业务活动加以计划和管理。

      CMM是科学评价一个软件企业开发能力的标准,但要达到较高的级别也非常困难,根据1995年美国所做的软件产业成熟度的调查,在美国的软件产业中, CMM成熟度等级为初始级的竟占70%,为可重复级的占15%,为定义级的所占比例小于10%,为管理级的所占比例小于5%,为优化级的所占比例小于l %。而国内企业的水平就更加堪优,到目前为止,只有东软一家达到优化级,少数几家能够达到可定义级。尽快改变这种局面,科学化、规范化、高效的进行软件开 发活动,从整体提高我国软件行业的水平,是国内软件企业的当务之急,也是专业人员应该为自己制定的目标。如果有一天也能指挥一个数千人的庞大开发队伍,操 作Windows这样巨型规模的软件项目,并生产出高质量的产品,才有理由宣称自己的软件项目管理能力达到了一个“自主自足”的水平。

九. 为什么要有项目管理?

      没有项目管理,项目也有可能成功。但没有管理的项目,很难保证项目的利润空间,对公司来说,亏损的风险就大。所以我们要有项目管理,以保证公司在总体上是盈利的,注意不是每一个项目都要盈利。

      另外,有了项目管理,就有了管理改进的基础,无论刚开始的项目管理多么糟糕,只要有管理,就有了改进的可能性,至于能不能得到改进,以及改进的快慢,则取 决于两个因素:一个是人,特别是各级管理者;另一个是利益。关键是"利益",准确的说是"利益的分配",在权责利明确的前提下,人才能充分的发挥作用。还 需要指出的是"利益"是多元的,这里的多元不仅指利益的具体形式,而且指利益的受众是多元的,包括客户方相关人员个人的利益。


十. 为什么要有专职的项目经理?

      专业化是一个趋势,因为在专业化的条件下,可以有效降低成本,提高利润率。项目经理的工作内容归根到底只有一项:识别并管理风险。这项工作的目的是控制项目成本。

      由于项目的风险是多方面的,而且风险的表现形式也是多种多样的。从风险范围上来说,既有公司内部风险,也有和客户交流、合作的风险;从风险的类型上来说,既有管理风险,也有技术风险;从风险产生的阶段来说,包括了从业务分析到上线后维护的项目周期各个阶段。

      我认为一个项目经理是否优秀,主要是看他/她能在多大程度上提前识别并消除风险,而不是弥补和解决了多少问题(风险未被及时识别或妥善处理,就会转换成问题)。当然能弥补和解决问题的项目经理也是相当合格的,但还不够优秀。

十一. 项目组的范围界限在哪里?

      项目组的范围界限可以有三种划分:

      1、包括客户方所有参与该项目的立项、调研、审批、测试和使用人员,包括开发商市场开发、管理审批、商务谈判、后勤保障和具体负责该项目开发的人员;

      2、包括客户方项目经理、业务需求提出人和测试人,包括开发商具体负责该项目开发的人员;

      3、仅包括开发商具体负责该项目开发的人员。

      大部分人在思想上可以接受范围1,而在实务中接受的是范围3。而我个人认为项目经理,特别是开发商方面的项目经理应该采用的是范围2。

      对项目组范围理解不同,将影响项目经理对工作的处理方式,范围1实际上是很虚的,在项目管理实务操作中没有太大的意义;而范围3实质是把客户方和该项目有 密切关系的人与开发商具体负责该项目开发的人对立起来,也就是所谓的甲方、乙方。在这种对立的前提下处理项目的分歧和矛盾,效果肯定要打折扣。

      而按范围2来理解,在项目管理实务中项目经理就必须要让客户方和该项目有密切关系的人也接受这一观点,从而拆除双方之间的"障碍",达到相互信任、相互尊 重、共同协商解决问题的良性氛围,以达到降低项目外部风险的目的。当然,这样就增大了项目经理工作的难度,但对项目的成功则是很重要的。

十二. 怎样才能算是一个成功的项目?

      对"成功项目"的标准解释为:项目范围、项目成本、项目开发时间、客户满意度四点达到要求。我认为其实只有一点--利益。项目范围、客户满意度主要代表客 户的利益,项目成本主要代表开发商的利益,项目开发时间同时影响双方的利益。但每一个人关心的"利益"是不同的。

十三. 软件项目管理的成功原则

      1平衡原则

   在我们讨论软件项目为什么会失败时可以列出了很多的原因,答案有很多,如管理问题、技术问题、人员问题等等,但是有一个根本的思想问题是最容易忽视的, 也是软件系统的用户、软件开发商、销售代理商最不想正视的,那就是:需求、资源、工期、质量四个要素之间的平衡关系问题。

  需求定义 了"做什么",定义了系统的范围与规模,资源决定了项目的投入(人、财、物),工期定义了项目的交付日期,质量定义了做出的系统好到什么程度,这四个要素 之间是有制约平衡关系的。如果需求范围很大,要在较少的资源投入下,很短的工期内,很高的质量要求来完成某个项目,那是不现实的,要么需要增加投资,要么 工程延期;如果需求界定清楚了,资源固定了,对系统的质量要求很高,则可能需求延长工期。

  对于上述四个要素之间的平衡关系最容易犯的一个错误,就是鼓吹"多快好省"四个字,"多快好省",多么理想的境界啊?需求越多越好,工期越短越好,质量越高越好,投入越少越好,这是用户最常用的口号。

多:需求越多越好吗?

   软件系统实施的基本原则是"全局规划,分步实施,步步见效",需求可以多,但是需求一定要分优先级,要分清企业内的主要矛盾与次要矛盾,根据 PARETO的80-20原则,企业中的80%的问题可以用20%的投资来解决,如果你要大而全,对不起,你那20%的次要问题是需要你花费80%的投资 的!而这一点恰恰是很多软件用户所不能忍受的。

快:真能快起来吗?

  "快"是用户、软件开发商都希望的。传统企业里 强调资金的周转情况,软件企业里强调的是人员的周转情况,开发人员应尽快做完一个项目再做另外一个项目,通过快速的启动项目、结束项目来承担更多的项目, 来获利。但是"快"不是主观的拍脑袋定工期就可以完成的,工期的定义一定要基于资源的状况、需求的多少与质量的需求来进行推算的。软件毕竟需要一行代码一 行代码的写出来,他的工作量是客观的,并非?quot;人有多大胆,地有多大产"式的精神鼓动就可以短期完成的。

省:省到什么程度?

  "一分钱一分货",这是中国的俗话,他是符合价值规律的。甲方希望少投入,乙方希望降低自己的生产成本,省到乙方仅能保本的时候,再省,乙方就亏损了。

  正视这四个要素之间的平衡关系是软件用户、开发商、代理商成熟理智的表现,否则系统的成功就失去了一块最坚实的理念基础。

  企业实施IT系统的首要目标是要成功,而不是失败,企业可以容忍小的成功,但不一定容忍小的失败,所以需要真正理解上述四个要素的平衡关系,确保项目的成功。

      2高效原则

   在需求、资源、工期、质量四个要素中,很多的项目决策者是将进度放在首位的,现在市场的竞争越来越激烈,"产品早上市一天,就早挣一天钱,挣的就比花的 多,所以一定要多挣",基于这样一个理念,软件开发越来越追求开发效率,大家从技术、工具、管理上寻求更多更好的解决之道。

  基于高效的原则,对项目的管理需要从几个方面来考虑:
  要选择精英成员
  目标要明确,范围要清楚
  沟通要及时、充分
  要在激励成员上下工夫

      3分解原则

  "化繁为简,各个击破"是自古以来解决复杂问题的不二法门,对于软件项目来讲,可以将将大的项目划分成几个小项目来做,将周期长的项目化分成几个明确的阶段。

   项目越大对项目组的管理人员、开发人员的要求越高,参与的人员越多,需要协调沟通的渠道越多,周期越长,开发人员也容易疲劳,将大项目拆分成几个小项 目,可以降低对项目管理人员的要求,减少项目的管理风险,而且能够充分地将项目管理的权力下放,充分调动人员的积极性,目标会比较具体明确,易于取得阶段 性的成果,使开发人员有成就感。

  作者主管过的一个产品开发项目代号为SB,该项目前期投入了5人做需求,时间达3个多月,进入开发 阶段后,投入了15人,时间达10个月之久,陆续进行了3次封闭开发,在此过程中经历了需求的裁剪、开发人员的变更、技术路线的调整,项目组成员的压力极 大,大家疲惫不堪,产品上市时间拖期达4个月。项目完工后总结下来的很致命的一个教训就是应该将该项目拆成3个小的项目来做,进行阶段性版本化发布,以缓 解市场上的压力,减少项目组成员的挫折感,提高大家的士气。

      4实时控制原则

  在一家大型的软件公司中, 有一位很有个性的项目经理,该项目经理很少谈起什么管理理论,也未见其有什么明显的管理措施,但是他连续做成多个规模很大的软件项目,而且应用效果很好。 作者一直很奇怪他为什么能做的如此成功,经过仔细观察,终于发现他的管理可以用"紧盯"2字来概括,即每天他都要仔细检查项目组每个成员的工作,从软件演 示到内部的处理逻辑、数据结构等,一丝不苟,如果有问题,改不完是不能去休息的。正是在他这种简单的措施下,支撑他完成了很多大的项目,当然他也是相当的 辛苦,通常都是在凌晨才去休息。我们并非要推崇这种做法,这种措施也有他的问题,但是,这种实践却说明了一个很朴实的道理:如果你没有更好的办法,就要辛 苦一点,实时控制项目的进展,要将项目的进展情况完全的实时的置于你的控制之下。

  上述的方法中对项目经理的个人能力、牺牲精神要求 是很高,我们需要有一种进行实时控制项目进度的机制,依靠一套规范的过程来保证实时监控项目的进度。如在微软的管理策略中强?quot;每日构建",这确 实是是一种不错的方法,即每天要进行一次系统的编译链接,通过编译链接来检查进度、检查接口、发现进展中的问题、大家互相鼓励互相监督。

  实时控制确保项目经理能够及时发现问题、解决问题,保证项目具有很高的可见度,保证项目的正常进展。

      5分类管理原则

   对于不同的软件项目其项目目标差别很大,项目规模也是不同的,应用领域是不同的,采用的技术路线差别也很大,因而,针对每个项目的不同特点,其管理的方 法、管理的侧重点应该是不同的。就像古人讲的,"因材施教","对症下药"。对于小项目你肯定不能象管理大项目那样去做,对于产品开发类的项目,你也不可 能象管理系统集成类的项目那样去做,项目经理需要根据项目的特点,制订不同的项目管理的方针政策。如,下表是作者为一家应用软件公司制订的项目管理的方 针:

  在该案例中,将项目分成了订单类项目与非订单类项目,非订单类项目是指由公司根据市场的需求开发一个标准产品的项目,而订单类 是指针对某个具体的客户定制软件的项目,订单类的项目根据需要协调的资源的范围有划分成了公司级、部门级、个人级三类,非订单类根据估算的工作量的大小也 分成了A、B、C三类,估算的工作量超过720人天的为A类,超过360人天的为B类,360人天以下的为C类。不同类的项目管理的侧重点是不同的,从立 项手续的完备性、计划的严格层度、周报的完备层度、规范的严格层度、跟踪的实时性、是否进行阶段总结、是否核算项目成本、是否严格进行阶段评审等多个方面 来考虑,以确保管理的可行性。

      6简单有效原则

  项目经理在进行项目管理的过程中,往往会得到开发人员这 样的抱怨"太麻烦了,浪费时间,没有用处",这是很普遍的一种现象。当然这样的抱怨要从2个方面来分析,一方面从开发人员本身可能存在不理解,或者逆反心 理的情况,另一方面,项目经理也要反思:我所采取的管理措施是否简单有效?搞管理不是搞学术研究,没有完美的管理,只有有效的管理,而项目经理往往试图堵 住所有的漏洞,解决所有的问题,恰恰是这种理想,会使项目的管理陷入一个误区,作茧自缚,最后无法实施有效的管理,导致项目的失败。

      7规模控制原则

  该原则是和上面提到的其他原则相配合使用的,即要控制项目组的规模,不要人数太多,人数多了,进行沟通的渠道就多了,管理的复杂度就高了,对项目经理的要求也就高了。在微软的MSF中,有一个很明确的原则就是要控制项目组的人数不要超过10人,当然这不是绝对的,也和项目经理的水平有很大关系。但是人员"贵精而不贵多",这是一个基本的原则,这和我们上面提到的高效原则、分解原则是相辅相成的。
 
十四. 软件项目管理在管理思维中的空白

  空白1:为效益而实施项目管理
  
   为什么我们要实施项目管理,是为了提高项目的效益。这里所指的项目的效益是一个综合性的指标,包括低风险、高产出等。为此我们不难得出我们在实施项目管 理应该掌握的度。即:引入项目管理后所产生的效益减去项目管理的成本后必须大于未引入项目管理时的效益。由于引入项目管理后所产生的效益与项目管理的复杂 度(项目管理的成本)并非线性相关的,因此项目管理的复杂度必然存在一个最优值,这就是我们应该把握的度。也许上面的说法比较抽象。一个实际行之可效的判 断项目管理的度规则就是:大家认可并且能够准确地理解和实施。拿美国项目管理专家James P Lewis的话说就是KISS原则(Keep it simple and stupid),拿物理学家爱因斯坦的话说就是:Keep it simple but not too simple.

  空白2:考虑所处环境
  
   任何系统都是建立在一个具体的系统环境中的,一般情况下受上一级系统影响最为显著,这是系统论的观点。项目管理是企业管理的下属层次,因此在很大程度上 项目管理的成功与否常常受企业管理的制度制约(比如说设备采购的批复等待会延误工期),这就是为什么常常会出现计划不如变化来的快的原因。因为我们在制定 计划时根本就没有考虑自身和客户双方的企业管理的环境,所以我们的计划在实施过程中会受到企业管理环境因素的影响。我敢跟你打赌:在没有人事激励机制常常 拖欠或故意克扣员工工资但获得CMM5认证的公司开发效率不会比一个没有实施项目管理的开发团队的效率高多少。因为恶劣的公司人事制度扼杀了开发人员的天 才和积极性。因此,作为一个项目管理者,审视自身的项目所处的企业环境并做出准确的判断是非常有必要的。缺少良好的项目环境,项目管理者的心血常常白费。 这往往是我们中的一些项目经理在不同的公司里项目管理表现大相径庭的原因。
  
  此外,正是基于企业环境这样一个观点,目前美国 PMI,日本ENAA等提出了项目管理成熟度模型(OPM3和P2M),改变了传统PMBOK的缺陷(忽略外部因素和自身的灵活性)。有兴趣的项目管理者 可以参看有关项目管理成熟度和企业管理方面(建议参看职业经理人方面)的资料。

  空白3:合理评判软件项目管理
  
  我们总是把过多的项目失败归罪到项目经理的名头上。他们的角色常常是替罪羊而不是领导者,他们拥有的更多的是责任而绝非职权。实际上项目失败并非完全决定于项目管理,比如说信息系统过 低的报价。一个项目按时在预算范围内完成了而另外一个则没有按时完成,这不意味着第一个项目管理得比较好。因为前者可能是项目时间和成本宽松的项目而后者 根本就是不可能完成的项目。前者项目管理的意义在于获得较高的项目效益而后者的意义在于避免更大的项目损失。很可惜,充满了浮躁的软件企业没有诸如此类的 意识,一些项目在未开始前注定就是失败的,项目经理们一上手便被扣以一责任人的镣铐。因此,项目管理有无具体效果,需要合理地进行评判,单纯以出效益为上 的观点未必有失偏颇。

  空白4:心理学的必要性
  
  没有一个领域像软件项目管理中人的因素更为重要,在软件领域没有 实现自动化之前,一切试图取代人的主要作用的机制都是收效甚微的。人的行为是心智活动的表现。开发人员的心理活动决定了其在开发的表现。合适的压力能够勾 起开发人员的成功欲望但是过大的压力却直接影响着项目参与者的身心健康。特别是后者一直以来都未能引起软件开发界的重视。很多人曾经有过不明不白的辞职经 历,在没有学习《管理心理学》之前,笔者对这些人的"过激"行为有时想想都觉得奇怪。作为一个软件项目管理者,不了解和掌握管理心理学,很难针对复杂多变 的人的因素采取合理的应对措施,同时自身的心理健康也未必能够得到保证。为此笔者建议有条件的软件企业,可以通过聘用心理顾问来处理员工的心理问题,以此 缓和由于工作压力而导致的员工之间矛盾冲突和项目坍塌。

  空白5:尊重常识,系统性考虑问题
  
  这个观点笔者在《软 件项目管理原则谈》已经重申过。就像不要指望人一秒钟跑二十米一样指望项目中有过多的奇迹出现。可惜我们中的大多项目管理者在进行项目管理时依然实施"大 跃进"。我们的管理者都知道自然规律不可违抗性,但是却很少有人意识到一些社会规律的不可违抗性。他们总以为唯物的主观能动性能够替代实际,产生奇迹。加 班被认为是解决资源匮乏的唯一途径,通过开发人员"无上"的生产力来达成项目的成功。很少有人会意识到加班造成的疲劳会再次使工作效率降低这一事实。这是 一种缺乏常识和系统性思考问题的表现。诸如此类的表现还有"唯工具论"和"唯方法论"。
  
  实际上,项目管理涉及各个方方面面,一味提高某一方面作用而忽略该方面对其它方面的影响,并不能提高项目管理的层次和最终产出,这是制止我们的项目管理者走偏激(极端)立场的一剂良药,希望项目管理者们能有所意识。

  空白6:学会思考
  
   项目管理不是拿来主义,需要项目管理者进行认真的思考。这就是为什么我们项目管理者中不乏PMP和IPMP但是项目却未能如愿以偿的原因。理论和实践的 差距极大地挫伤项目管理者的积极性。"证书无用论"所持的观点其依据也在于此。理论是一种完美的抽象,而现实是各种条件的集合。我们的项目管理者在实践上 往往生搬硬套而忽略其依存条件,这就是招聘项目经理"唯经验论"的来源。一位项目管理者跟我交流的时候提到无法使用挣值(Earned Value)的概念,原因是公司人事部和财务部不愿意出示员工的收入清单。我建议他将挣值换为挣时(Earned Time),以时间替代成本。从项目进度的意义上来看这两者其实是一致的,问题马上得到了解决。可惜的是我们的项目管理者往往未学会思考具体概念的真正含 义之前并匆匆上驴,提着长枪去和风车做斗争去了(注:唐吉诃德)。

  空白7:学会计划
  
  现实中我们往往用补救措施代替计划,其效果便如软件缺陷的 放大效应。在项目经理的招聘中,你听到的只是几个项目管理白痴问你项目出了什么问题应该怎样解决的提问,这些项目管理白痴在不断地做各种问题假设,而你必 须根据假设采取各种符合这些项目管理白痴口味的回答。但是,作为项目管理的来说,项目管理的真正意义在于事先预防各种偏离项目目标的问题出现而不是在于解 决问题。古话说得好"磨刀不误砍柴工"。你不能期望癌症有100%的治愈率,但是你可以通过合理的生活习惯和锻炼来防止癌症的出现。我们在进行项目管理 时,首先应该考虑如何防止问题的出现,虽然它不能保证所有的问题(风险)都可以避免,但是通过计划,你将拥有更多问题(风险)应对储备,能够在问题出现时 有备无患。一个只会在问题出现时考虑应对措施的项目经理只是一个失败的项目经理。其项目结果无异是把健康交给医生而不是自己。作为项目管理的定位来说,项 目管理应该是"管理会计"的角色而不是"成本会计"的角色。
  
  最后,以某电影的台词来结束本文;人为什么犯病?简单的东西想复杂 了,复杂的东西想简单了,人就会犯病"。拿这句台词来形容我们目前的项目管理状况一点也不为过。软件项目管理是一个从"自发"走向"自觉"的过程,也是一 个从经验主义走向理性主义的过程。软件项目管理是一个主动的管理,而这一切,需要广大项目管理者的项目管理思维和积极实践。

 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1231828

posted @ 2007-03-19 22:12 junhong 阅读(326) | 评论 (0)编辑 收藏

WSDL Info

A WSDL document defines services as collections of network endpoints, or ports. In WSDL, the abstract definition of endpoints and messages is separated from their concrete network deployment or data format bindings. This allows the reuse of abstract definitions: messages, which are abstract descriptions of the data being exchanged, and port types which are abstract collections of operations. The concrete protocol and data format specifications for a particular port type constitutes a reusable binding. A port is defined by associating a network address with a reusable binding, and a collection of ports define a service. Hence, a WSDL document uses the following elements in the definition of network services:

Services are defined using six major elements:

  • types, which provides data type definitions used to describe the messages exchanged.
  • message, which represents an abstract definition of the data being transmitted. A message consists of logical parts, each of which is associated with a definition within some type system.
  • portType, which is a set of abstract operations. Each operation refers to an input message and output messages.
  • binding, which specifies concrete protocol and data format specifications for the operations and messages defined by a particular portType.
  • port, which specifies an address for a binding, thus defining a single communication endpoint.
  • service, which is used to aggregate a set of related ports.


If the values being passed are part1=1, part2=2, part3=3, the request format would be as follows for each port:

port1: GET, URL="http://example.com/o1/A1B2/3"
port2: GET, URL="http://example.com/o1?p1=1&p2=2&p3=3
port3: POST, URL="http://example.com/o1", PAYLOAD="p1=1&p2=2&p3=3"

For each port, the response is either a GIF or a JPEG image.

Example 6. GET and FORM POST returning GIF or JPG

<definitions .... >
<message name="m1">
<part name="part1" type="xsd:string"/>
<part name="part2" type="xsd:int"/>
<part name="part3" type="xsd:string"/>
</message>

<message name="m2">
<part name="image" type="xsd:binary"/>
</message>

<portType name="pt1">
<operation name="o1">
<input message="tns:m1"/>
<output message="tns:m2"/>
</operation>
</portType>

<service name="service1">
<port name="port1" binding="tns:b1">
<http:address location="http://example.com/"/>
</port>
<port name="port2" binding="tns:b2">
<http:address location="http://example.com/"/>
</port>
<port name="port3" binding="tns:b3">
<http:address location="http://example.com/"/>
</port>
</service>

<binding name="b1" type="pt1">
<http:binding verb="GET"/>
<operation name="o1">
<http:operation location="o1/A(part1)B(part2)/(part3)"/>
<input>
<http:urlReplacement/>
</input>
<output>
<mime:content type="image/gif"/>
<mime:content type="image/jpeg"/>
</output>
</operation>
</binding>

<binding name="b2" type="pt1">
<http:binding verb="GET"/>
<operation name="o1">
<http:operation location="o1"/>
<input>
<http:urlEncoded/>
</input>
<output>
<mime:content type="image/gif"/>
<mime:content type="image/jpeg"/>
</output>
</operation>
</binding>

<binding name="b3" type="pt1">
<http:binding verb="POST"/>
<operation name="o1">
<http:operation location="o1"/>
<input>
<mime:content type="application/x-www-form-urlencoded"/>
</input>
<output>
<mime:content type="image/gif"/>
<mime:content type="image/jpeg"/>
</output>
</operation>
</binding>
</definitions>

4.2 How the HTTP GET/POST Binding Extends WSDL

The HTTP GET/POST Binding extends WSDL with the following extension elements:

<definitions .... >
<binding .... >
<http:binding verb="nmtoken"/>
<operation .... >
<http:operation location="uri"/>
<input .... >
<-- mime elements -->
</input>
<output .... >
<-- mime elements -->
</output>
</operation>
</binding>

<port .... >
<http:address location="uri"/>
</port>
</definitions>

These elements are covered in the subsequent sections.

4.3 http:address

The location attribute specifies the base URI for the port. The value of the attribute is combined with the values of the location attribute of the http:operation binding element. See section 4.5 for more details.

4.4 http:binding

The http:binding element indicates that this binding uses the HTTP protocol.

<definitions .... >
<binding .... >
<http:binding verb="nmtoken"/>
</binding>
</definitions>

The value of the required verb attribute indicates the HTTP verb. Common values are GET or POST, but others may be used. Note that HTTP verbs are case sensitive.

posted @ 2007-01-28 16:24 junhong 阅读(336) | 评论 (0)编辑 收藏

SAX2.0

/*
自从XML真正形成以来(我认为是Org.W3C组织发布XML标准时开始),XML得到了很快的发展,
 很多厂商都有推出了自己的XML解析器,Apachexalan,IBMxerces,sunJDOM,不过这些都是在
 基于JAXP(java API for XML processing),JDK 1.4.0开始的后续j2sdk里都附加了JAXP,这给开发人员
 带来了很大的方便,这使得我们在处理一般的XML功能上的问题时不再需要去用第三方的XML处理器了.
 随着XML的迅速发展,SAX也从1.0到了现在的2.0(还是能够和1.0兼容),结构上有了一些较大的变化.

 DOM(document object model)每次读取XML节点时都要把它load内存里 来,在文档很大时,就显得很慢了,SAX(simple API for XML),是一个XML解析器的接口,它比DOM更低级一些,它是一种基于事件和回调模式的XML处理方式因此在解析速度上DOM是没法比 的(当要解析的XML文档很大的时更是如此).那么在SAX中事件响应(event)是什么呢我个人认为这一点和Swing,AWT中的事件义有点相似的,都有是指在触发某些特定的行为时所做的处理,:mouse click事件等到这里则是指碰到特定的XML节点的所做的处理,如文档开始(startDocument),文档结束 (endDocument),元素开始(startElement)等很多,大家看一下SAXAPI中的方法名字就知道有哪些事件了,基本上可以做到见 文知义的.在只想分析XML内容(只读),要求高性能,灵活性 能够定位错误信息(SAX能够定位错误的行列位置),最好用SAX来做一般情况下SAX是按下面的原理去使用的:
  <1>设置事件处理器(SAX 1.0是使用一个通过继承HandlerBase类的实例来设置的,SAX 2.0则是继承DefaultHandler,还有用XMLReader方式的,在原理上没有很大的区别)
  <2>载入要解析的内容
  <3>在需要解析的事件方法里(具体参见SAX API文档)加入自己的控制逻辑.
  <4>重复<3>直到解析完为止.                 
 
 在这里我自己写了一个描述电影海报信息的XML文件(file.xml),SAX2.0写了一个很简单的XML内容阅读器来解析它和大家交流一下自己的心得.程序在我的机器上经过了测试的(OS: win2k Advanced Server(English version),
 Intel pentium CPU, 256M RAM)
*/

import org.w3c.dom.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;

class MyXMLReader extends DefaultHandler
{  
 //Fields
 private int index;
 private Locator locator;

 //Constructor
 public MyXMLReader(){
  super(); //it must be done !
 }

 //nain method
 public static void main(String[] args){
  try{   
      SAXParserFactory sf  = SAXParserFactory.newInstance();
      SAXParser sp = sf.newSAXParser();
      MyXMLReader reader = new MyXMLReader();
      sp.parse(new InputSource("film.xml"),reader);
  }
  catch(Exception e){
   e.printStackTrace();
  }
 } 

 //Response the startDocument event
 public void startDocument() {
  System.out.println("\n********************************* (: 元旦电影海报 :) ***********************************\n");
 }  

//Response the startElement event
 public void startElement(String uri, String localName, String qName, Attributes attrs){  
  if( qName.equalsIgnoreCase("film") ){   
   index ++;   
   int attrCount = attrs.getLength();
   for( int i = 0; i < attrCount; i ++ ){
    String attrName = attrs.getQName(i);
    if( attrName.equalsIgnoreCase("name") ){
     System.out.println("\t" + index + ",片名:<<" +  attrs.getValue(i) + ">>");
    }
    if( attrName.equalsIgnoreCase("price") ){
     System.out.println("\t票价:" + attrs.getValue(i) );     
    }
    if( attrName.equalsIgnoreCase("station") ){
     System.out.println("\t放映地点:" + attrs.getValue(i) );     
    }
    if( attrName.equalsIgnoreCase("time") ){
     System.out.println("\t放映时间:" + attrs.getValue(i) );
    }
    if( attrName.equalsIgnoreCase("describtion") ){
     System.out.println("\t影片简介:" + attrs.getValue(i) );
    }
    System.out.println();
  }
 }

 //Response the endDocument event
 public void endDocument(){
  System.out.println("\t\t\t\t\t\t\t------ 共有" + index + "场电影要放映");  
 }

 //Response the endElement event
 public void endElement(String uri, String localName, String qName){
     //add your codes if neccessary ...
 }

 //Print the fata error information
 public void fatalError(SAXParseException e){
  System.out.println("\nFatal error information -->");
  System.out.println("\t" + e.getMessage());
  System.out.println("\tAt line " + locator.getLineNumber() +
                  ",column " + locator.getColumnNumber());
 }
 
 //Print the usual error information
 public void error(SAXParseException e){
  System.out.println("\nUsual error information -->");  
  System.out.println("\t" + e.getMessage());
  System.out.println("\tAt line " + locator.getLineNumber() +
                  ",column " + locator.getColumnNumber());
 }
 
 //Print the warning information
 public void warning(SAXParseException e){
  System.out.println("\nWarning information -->");    
  System.out.println("\t" + e.getMessage());  
  System.out.println("\tAt line " + locator.getLineNumber() +
                  ",column " + locator.getColumnNumber());
 }

 //Store the error locator object
 public void setDocumentLocator(Locator lct){
  locator = lct;
 }

}//End class MyXMLReader

: film.xml完全的内容:

<?xml version="1.0" encoding="GB2312"?>
   <!-- 2003年元月1号长沙市各大影院落放映列表 -->
   <common city="ChangSha China" date="01/01/2003">
     <film name="英雄" price="30" station="田汉大剧场" time="19:00"
        describtion="国产最新大片,张艺谋导演,梁朝伟,张曼玉,李连杰等众多大明星主演">
  </film>
  <film name="无间道" price="20" station="长沙市演艺中心" time="15:00"
        describtion="韩国大片">
  </film>
  <film name="武士" price="20" station="湖南省电影院" time="17:00"
        describtion="韩国大片,有点像英雄">
  </film>  
  <film name="长排山之战" price="15" station="长沙市电影超市A1" time="19:00"
        describtion="反映对越自卫反击战时期中国军人的故事片">
      </film>
  <film name="高山下的花环" price="15" station="长沙市电影超市A2" time="19:00"
        describtion="反映对越自卫反击战时期中国军人的故事片">
  </film>  
    <film name="这里的黎明静悄悄" price="15" station="长沙市电影超市A3" time="19:00"
        describtion="反映对越自卫反击战时期中国军人的故事片">
  </film>
  <film name="子夜" price="15" station="长沙市电影超市B1" time="19:00"
        describtion="反映对越自卫反击战时期中国军人的故事片">
  </film>
  </common>

 

posted @ 2006-11-24 10:21 junhong 阅读(638) | 评论 (0)编辑 收藏

Thinking in java review 2

     摘要: 1.      The only place a label is useful in Java is right before an iteration statement. And that means right before—it does no good to put any other statement bet...  阅读全文

posted @ 2006-06-28 17:46 junhong 阅读(1767) | 评论 (0)编辑 收藏

Thinking in java review

1、  Java  中所有的 member function 都是动态绑定

2、  Java 中所有的对象都是通过 new 来动态产生

3、  所有的容器 (collection) Object array( 对象数组 e.g String []str=new String[10]) 内都包含都是对象的 reference

4、  java 中, java 编译器有责任产生“将 stack 指针前后移动“的程序代码,所以 它必须能够完全掌握它所编译的的程序中“存在 stack 里头的所有数据的实际大小和存活时间“, 如此一来便会限制程序的弹性。 由于这个限制,尽管我们可以将对象的 reference 存储与 stack 内,但却不能将一般的 java 对象放在 stack
特例:( primitive types
基本型别会经常被使用,如果使用 new 来产生此类极小,极简单的变量,会因“ new 将对象置于 heap 之上“ 而效率不好。因此对于此类变量 java 采取 c/c++ 的方式,也就是不用 new 分配器空间,而是产生一种所谓的 ”automatic “变量,存于 static

5、  当你产生某个存储对象的数组,真正产生的是个存储 reference 的数组。此数组建立之后,其中的每一个 reference 都会被自动设为某个特殊的值 null,

6、  { String s=new String(“ddd”);} s 这个 reference 会在生存空间之外消失无踪,但是, s 先前所指的那个 String 对象仍然会继续占用内存

7、  Class 内的基本型别变量都有初值,但是在函数内的 local variable 是没有初值的

8、  Java 中,所有传递的对象的场合,传递的都是对象的 reference.

9、  Return 说明这个函数运行结束,返回到其调用函数。 Return 2 :返回一个数 2 给调用者,同时结束本函数的运行。

10、              Class 内的 non-static data(state) method , 都是和特定的对象绑定的,一般情况下,你的产生某个对象,再通过该对象取用其数据和函数。所以 non-static 数据 / 函数必须知道他们隶属于哪一个对象,才有办法运行 .static 函数内不能使用 non-static 数据和函数

11、              (object1= = object2 ) 两个对象的 reference = = 比较得是两个对象的内存的地址。所以我们不能使用 = = 来测试两个对象的内容是否相等。如果想测试对象的内容是否相等,应该使用 equal(), 任何一个对象都拥有这个函数。不过你自己的 class 需要 override 这个函数,否则默认的 equal() 函数是还是比较的两个对象的内存地址。
Java
标准程序库中的大多数 class override equal(), 所以他们都会比较对象的内容是否相等。

12、              位运算符都是作用于基本正数类型。该运算符主要是针对硬件编程使用(我们用得不多,)

待续

 

 

posted @ 2006-06-27 17:40 junhong 阅读(1794) | 评论 (0)编辑 收藏

关于输入框中显示双引号和单引号


关于输入框中显示双引号和单引号
关于输入框中显示双引号和单引号

前台显示解决办法:
方法一:
单引号<input type="text" value="'">
双引号<input type="text" value='"'>
方法二:
单引号<input type="text" value="&#39;">
双引号<input type="text" value="&#34;">


从后台读取数据前台显示解决办法:
我们从数据库中读取值到前台显示时应该加入转换

JavaScript版本:

<% @Language="JavaScript" %>
<%
function ForamtValue(oStr)
{
switch(typeof(oStr))
{
case "date" :
//直接toString()转换,可以加入丰富的显示方式
sStr = (new Date(oStr)).toString();
break;
default :
sStr = String(oStr);
}
sStr = sStr.replace(/\"/g,"&#34;"); //输入框中显示双引号问题
sStr = sStr.replace(/\'/g,"&#39;"); //输入框中显示单引号问题
return sStr;
}
%>

<%
//测试
var str = "\"灰豆宝宝.net(魔幻季节)\"";
var str = new Date();
%>
<br>
<input type="text" value="<%=str%>" style="width:200px">[不能正常显示]<br>
<input type="text" value="<%=ForamtValue(str)%>" style="width:200px">[正常显示]<br>


VBScript版本:

<% @Language="VBScript" %>
<%
function ForamtValue(oStr)
Select Case VarType(oStr)
Case "vbDate"
'直接toString()转换,可以加入丰富的显示方式
sStr = CDate(oStr)
Case Else
sStr = CStr(oStr)
End Select
sStr = Replace(sStr,"""","&#34;") '输入框中显示双引号问题
sStr = Replace(sStr,"'","&#39;") '输入框中显示单引号问题
ForamtValue = sStr
End Function
%>

<%
'测试
Dim str
str = """'灰豆宝宝.net(魔幻季节)'"""
%>
<br>
<input type="text" value="<%=str%>" style="width:200px">[不能正常显示]<br>
<input type="text" value="<%=ForamtValue(str)%>" style="width:200px">[正常显示]<br>

posted @ 2006-04-19 23:39 junhong 阅读(635) | 评论 (0)编辑 收藏

how to use ant to deply your web application


First of all, if you develop your application by Eclipse, it means you have had the ant tool and you need not to download the internet.

Projects

A project has three attributes:

Attribute Description Required
name the name of the project. No
default the default target to use when no target is supplied. No; however, since Ant 1.6.0, every project includes an implicit target that contains any and all top-level tasks and/or types. This target will always be executed as part of the project's initialization, even when Ant is run with the -projecthelp option.
basedir the base directory from which all path calculations are done. This attribute might be overridden by setting the "basedir" property beforehand. When this is done, it must be omitted in the project tag. If neither the attribute nor the property have been set, the parent directory of the buildfile will be used. No

Optionally, a description for the project can be provided as a top-level <description> element (see the description type).

Each project defines one or more targets. A target is a set of tasks you want to be executed. When starting Ant, you can select which target(s) you want to have executed. When no target is given, the project's default is used.

Targets

A target can depend on other targets. You might have a target for compiling, for example, and a target for creating a distributable. You can only build a distributable when you have compiled first, so the distribute target depends on the compile target. Ant resolves these dependencies.

It should be noted, however, that Ant's depends attribute only specifies the order in which targets should be executed - it does not affect whether the target that specifies the dependency(s) gets executed if the dependent target(s) did not (need to) run.

Ant tries to execute the targets in the depends attribute in the order they appear (from left to right). Keep in mind that it is possible that a target can get executed earlier when an earlier target depends on it:

						
								<target name="A"/>
<target name="B" depends="A"/>
<target name="C" depends="B"/>
<target name="D" depends="C,B,A"/>

Suppose we want to execute target D. From its depends attribute, you might think that first target C, then B and then A is executed. Wrong! C depends on B, and B depends on A, so first A is executed, then B, then C, and finally D.

In a chain of dependencies stretching back from a given target such as D above, each target gets executed only once, even when more than one target depends on it. Thus, executing the D target will first result in C being called, which in turn will first call B, which in turn will first call A. After A, then B, then C have executed, execution returns to the dependency list of D, which will not call B and A, since they were already called in process of dependency resolution for C and B respectively as dependencies of D. Had no such dependencies been discovered in processing C and B, B and A would have been executed after C in processing D's dependency list.

A target also has the ability to perform its execution if (or unless) a property has been set. This allows, for example, better control on the building process depending on the state of the system (java version, OS, command-line property defines, etc.). To make a target sense this property, you should add the if (or unless) attribute with the name of the property that the target should react to. Note: Ant will only check whether the property has been set, the value doesn't matter. A property set to the empty string is still an existing property. For example:

						
								<target name="build-module-A" if="module-A-present"/>
						
				
						
								<target name="build-own-fake-module-A" unless="module-A-present"/>
						
				

In the first example, if the module-A-present property is set (to any value), the target will be run. In the second example, if the module-A-present property is set (again, to any value), the target will not be run.

If no if and no unless attribute is present, the target will always be executed.

Important: the if and unless attributes only enable or disable the target to which they are attached. They do not control whether or not targets that a conditional target depends upon get executed. In fact, they do not even get evaluated until the target is about to be executed, and all its predecessors have already run.

The optional description attribute can be used to provide a one-line description of this target, which is printed by the -projecthelp command-line option. Targets without such a description are deemed internal and will not be listed, unless either the -verbose or -debug option is used.

It is a good practice to place your tstamp tasks in a so-called initialization target, on which all other targets depend. Make sure that target is always the first one in the depends list of the other targets. In this manual, most initialization targets have the name "init".

If the depends attribute and the if/unless attribute are set, the depends attribute is executed first.

A target has the following attributes:

Attribute Description Required
name the name of the target. Yes
depends a comma-separated list of names of targets on which this target depends. No
if the name of the property that must be set in order for this target to execute. No
unless the name of the property that must not be set in order for this target to execute. No
description a short description of this target's function. No

A target name can be any alphanumeric string valid in the encoding of the XML file. The empty string "" is in this set, as is comma "," and space " ". Please avoid using these, as they will not be supported in future Ant versions because of all the confusion they cause. IDE support of unusual target names, or any target name containing spaces, varies with the IDE.

Targets beginning with a hyphen such as "-restart" are valid, and can be used to name targets that should not be called directly from the command line.

Tasks

A task is a piece of code that can be executed.

A task can have multiple attributes (or arguments, if you prefer). The value of an attribute might contain references to a property. These references will be resolved before the task is executed.

Tasks have a common structure:

						
								<nameattribute1="value1" attribute2="value2" ... />
						
				

where name is the name of the task, attributeN is the attribute name, and valueN is the value for this attribute.

There is a set of built-in tasks , along with a number of optional tasks , but it is also very easy to write your own .

All tasks share a task name attribute. The value of this attribute will be used in the logging messages generated by Ant.

Tasks can be assigned an id attribute:

						
								<taskname id="taskID" ... />
						
				

where taskname is the name of the task, and taskID is a unique identifier for this task. You can refer to the corresponding task object in scripts or other tasks via this name. For example, in scripts you could do:

						
								<script ... >
task1.setFoo("bar");
</script>

to set the foo attribute of this particular task instance. In another task (written in Java), you can access the instance via project.getReference("task1").

Note1: If "task1" has not been run yet, then it has not been configured (ie., no attributes have been set), and if it is going to be configured later, anything you've done to the instance may be overwritten.

Note2: Future versions of Ant will most likely not be backward-compatible with this behaviour, since there will likely be no task instances at all, only proxies.

Properties

A project can have a set of properties. These might be set in the buildfile by the property task, or might be set outside Ant. A property has a name and a value; the name is case-sensitive. Properties may be used in the value of task attributes. This is done by placing the property name between "${" and "}" in the attribute value. For example, if there is a "builddir" property with the value "build", then this could be used in an attribute like this: ${builddir}/classes. This is resolved at run-time as build/classes.

Built-in Properties

Ant provides access to all system properties as if they had been defined using a <property> task. For example, ${os.name} expands to the name of the operating system.

For a list of system properties see the Javadoc of System.getProperties .

In addition, Ant has some built-in properties:

				
						basedir             the absolute path of the project's basedir (as set
with the basedir attribute of <project>).
ant.file the absolute path of the buildfile.
ant.version the version of Ant
ant.project.name the name of the project that is currently executing;
it is set in the name attribute of <project>.
ant.java.version the JVM version Ant detected; currently it can hold
the values "1.1", "1.2", "1.3", "1.4" and "1.5".

Example Buildfile

				
						<project name="MyProject" default="dist" basedir=".">
<description>
simple example build file
</description>
<!-- set global properties for this build -->
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>

<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>

<target name="compile" depends="init"
description="compile the source " >
<!-- Compile the java code from ${src} into ${build} -->
<javac srcdir="${src}" destdir="${build}"/>
</target>

<target name="dist" depends="compile"
description="generate the distribution" >
<!-- Create the distribution directory -->
<mkdir dir="${dist}/lib"/>

<!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
<jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
</target>

<target name="clean"
description="clean up" >
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>

Notice that we are declaring properties outside any target. As of Ant 1.6 all tasks can be declared outside targets (earlier version only allowed <property>,<typedef> and <taskdef>). When you do this they are evaluated before any targets are executed. Some tasks will generate build failures if they are used outside of targets as they may cause infinite loops otherwise (<antcall> for example).

We have given some targets descriptions; this causes the projecthelp invocation option to list them as public targets with the descriptions; the other target is internal and not listed.

Finally, for this target to work the source in the src subdirectory should be stored in a directory tree which matches the package names. Check the <javac> task for details.

Token Filters

A project can have a set of tokens that might be automatically expanded if found when a file is copied, when the filtering-copy behavior is selected in the tasks that support this. These might be set in the buildfile by the filter task.

Since this can potentially be a very harmful behavior, the tokens in the files must be of the form @token@, where token is the token name that is set in the <filter> task. This token syntax matches the syntax of other build systems that perform such filtering and remains sufficiently orthogonal to most programming and scripting languages, as well as with documentation systems.

Note: If a token with the format @token@ is found in a file, but no filter is associated with that token, no changes take place; therefore, no escaping method is available - but as long as you choose appropriate names for your tokens, this should not cause problems.

Warning: If you copy binary files with filtering turned on, you can corrupt the files. This feature should be used with text files only.

Path-like Structures

You can specify PATH- and CLASSPATH-type references using both ":" and ";" as separator characters. Ant will convert the separator to the correct character of the current operating system.

Wherever path-like values need to be specified, a nested element can be used. This takes the general form of:

				
						    <classpath>
<pathelement path="${classpath}"/>
<pathelement location="lib/helper.jar"/>
</classpath>

The location attribute specifies a single file or directory relative to the project's base directory (or an absolute filename), while the path attribute accepts colon- or semicolon-separated lists of locations. The path attribute is intended to be used with predefined paths - in any other case, multiple elements with location attributes should be preferred.

As a shortcut, the <classpath> tag supports path and location attributes of its own, so:

				
						    <classpath>
<pathelement path="${classpath}"/>
</classpath>

can be abbreviated to:

				
						    <classpath path="${classpath}"/>

In addition, DirSet s, FileSet s, and FileList s can be specified via nested <dirset>, <fileset>, and <filelist> elements, respectively. Note: The order in which the files building up a FileSet are added to the path-like structure is not defined.

				
						    <classpath>
<pathelement path="${classpath}"/>
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
<pathelement location="classes"/>
<dirset dir="${build.dir}">
<include name="apps/**/classes"/>
<exclude name="apps/**/*Test*"/>
</dirset>
<filelist refid="third-party_jars"/>
</classpath>

This builds a path that holds the value of ${classpath}, followed by all jar files in the lib directory, the classes directory, all directories named classes under the apps subdirectory of ${build.dir}, except those that have the text Test in their name, and the files specified in the referenced FileList.

If you want to use the same path-like structure for several tasks, you can define them with a <path> element at the same level as targets, and reference them via their id attribute - see References for an example.

A path-like structure can include a reference to another path-like structure via nested <path> elements:

				
						    <path id="base.path">
<pathelement path="${classpath}"/>
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
<pathelement location="classes"/>
</path>

<path id="tests.path">
<path refid="base.path"/>
<pathelement location="testclasses"/>
</path>

The shortcuts previously mentioned for <classpath> are also valid for <path>.For example:

				
						
								
								 <path id="base.path">
<pathelement path="${classpath}"/>
</path>

can be written as:

				
						 <path id="base.path" path="${classpath}"/>
				
		
				
						generally, you need not to write your builder.xml file started from scratch. you can just modify the following
part to meet your demand.
				
						<project name="javastep" default="deploy" basedir=".">
				
		
				
						<!-- ===================== Property Definitions =========================== -->
				
		
				
						    <!--
         All properties should be defined in this section.
         Any host-specific properties should be defined
         in the build.properties file.
				
						  In this app, the following properties are defined in build.properties:
				
		
				
						   o  tomcat.home     - the home directory of your Tomcat installation
        o  webapps.home    - the place to copy the war file to deploy it
    -->
				
						  <property file="build.properties" />
 
				
						  <property name="app.home"          value="." />
  <property name="app.name"          value="javastep" />
  <property name="javadoc.pkg.top"   value="hello" />
				
						  <property name="src.home"          value="${app.home}/src"/>
  <property name="lib.home"          value="${app.home}/WebRoot/WEB-INF/lib"/>
 
  <property name="classes.home"       value="${app.home}/WebRoot/WEB-INF/classes/"/>
  <property name="deploy.home"       value="${app.home}/deploy"/>
  <property name="doc.home"          value="${app.home}/doc"/>
  <property name="web.home"          value="${app.home}/WebRoot"/>
				
						  <property name="build.home"        value="${app.home}/build"/>
  <property name="build.classes"     value="${build.home}/WEB-INF/classes"/>
  <property name="build.lib"         value="${build.home}/WEB-INF/lib"/>
				
						<!-- ==================== Compilation Classpath =========================== -->
				
		
				
						    <!--
         This section creates the classpath for compilation.
    -->
				
						  <path id="compile.classpath">
				
		
				
						    <!-- The object files for this application -->
    <pathelement location="${classes.home}"/>
				
						    <!-- The lib files for this application -->
    <fileset dir="${lib.home}">
      <include name="*.jar"/>
      <include name="*.zip"/>
    </fileset>
				
						    <!-- All files/jars that Tomcat makes available -->
				
		
				
						
								
  </path>
				
						
								
<!-- ==================== Build Targets below here========================= -->
				
						
								
<!-- ==================== "help" Target =================================== -->
				
						    <!--
         This is the default ant target executed if no target is specified.
         This helps avoid users just typing 'ant' and running a
         default target that may not do what they are anticipating...
    -->
				
						 <target name="help" >
   <echo message="Please specify a target! [usage: ant &lt;targetname&gt;]" />
   <echo message="Here is a list of possible targets: "/>
   <echo message="  clean-all.....Delete build dir, all .class and war files"/>
   <echo message="  prepare.......Creates directories if required" />
   <echo message="  compile.......Compiles source files" />
   <echo message="  build.........Build war file from .class and other files"/>
   <echo message="  deploy........Copy war file to the webapps directory" />
   <echo message="  javadoc.......Generates javadoc for this application" />
 </target>
				
						<!-- ==================== "clean-all" Target ============================== -->
				
		
				
						   <!--
          This target should clean up any traces of the application
          so that if you run a new build directly after cleaning, all
          files will be replaced with what's current in source control
   -->
				
						 <target name="clean-all" >
    <delete dir="${build.home}"/>
    <delete dir="${classes.home}"/>
    <delete dir="${deploy.home}"/>
				
						    <!-- can't delete directory if Tomcat is running -->
    <delete dir="${webapps.home}/${app.name}" failonerror="false"/>
				
						    <!-- deleting the deployed .war file is fine even if Tomcat is running -->
    <delete dir="${webapps.home}/${app.name}.war" />
				
						    <!-- delete the javadoc -->
    <delete dir="${doc.home}"/>
				
						 </target>
				
		
				
						<!-- ==================== "prepare" Target ================================ -->
				
		
				
						    <!--
          This target is executed prior to any of the later targets
          to make sure the directories exist. It only creates them
          if they need to be created....
          Other, similar, preparation steps can be placed here.
    -->
				
						  <target name="prepare">
				
		
				
						    <echo message="Tomcat Home = ${tomcat.home}" />
    <echo message="webapps Home = ${webapps.home}" />
				
						    <mkdir dir="${classes.home}"/>
    <mkdir dir="${deploy.home}"/>
				
						    <mkdir dir="${doc.home}"/>
    <mkdir dir="${doc.home}/api"/>
				
						    <mkdir dir="${build.home}"/>
    <mkdir dir="${build.home}/WEB-INF" />
    <mkdir dir="${build.home}/WEB-INF/classes" />
    <mkdir dir="${build.home}/WEB-INF/lib" />
				
						  </target>
				
		
				
						<!-- ==================== "compile" Target ================================ -->
				
		
				
						    <!--
          This only compiles java files that are newer
          than their corresponding .class files.
     -->
				
						  <target name="compile" depends="prepare" >
    <javac srcdir="${src.home}" destdir="${classes.home}" debug="yes" >
        <classpath refid="compile.classpath"/>
    </javac>
  </target>
				
						<!-- ==================== "build" Target ================================== -->
				
		
				
						    <!--
          This target builds the war file for the application
          by first building the directory structure of the
          application in ${build.home} and then creating the
          war file using the ant <war> task
     -->
				
						  <target name="build" >
				
		
				
						    <!-- Copy all the webapp content (jsp's, html, tld's, xml, etc. -->
    <!-- Note that this also copies the META-INF directory -->
    <copy    todir="${build.home}">
      <fileset dir="${web.home}"/>
    </copy>
				
						    <!-- Now, copy all the Java class files -->
    <copy    todir="${build.home}/WEB-INF/classes">
      <fileset dir="${classes.home}"/>
    </copy>
				
						    <!-- Now, copy all the properties files, etc that go on the classpath -->
    <copy    todir="${build.home}/WEB-INF/classes">
      <fileset dir="${src.home}">
         <include name="**/*.properties" />
         <include name="**/*.prop" />
      </fileset>
    </copy>
				
						    <!-- Now, copy all the jar files we need -->
    <copy    todir="${build.home}/WEB-INF/lib">
      <fileset dir="${lib.home}" />
    </copy>
				
						    <!-- Create the <war> file -->
    <jar jarfile="${deploy.home}/${app.name}.war"
         basedir="${build.home}"/>
				
						  </target>
				
		
				
						<!-- ==================== "deploy" Target ================================= -->
				
		
				
						    <!--
         This target simply copies the war file from the deploy
         directory into the Tomcat webapp directory.
     -->
				
						  <target name="deploy" depends="build" >
				
		
				
						    <!-- Copy the contents of the build directory -->
    <copy todir="${webapps.home}"  file="${deploy.home}/${app.name}.war" />
				
						  </target>
				
		
				
						<!-- ==================== "doc" Target ==================================== -->
				
		
				
						    <!--
         This task creates javadoc. It is dependent upon only the
         'compile' target so it is not executed in a normal build.
         As a result, the target needs to be run on its own.
    -->
				
						  <target name="javadoc" depends="compile">
      <javadoc sourcepath = "${src.home}"
                  destdir = "${doc.home}/api"
             packagenames = "${javadoc.pkg.top}.*"/>
  </target>
 
<!-- ==================== "test" Target ================================== -->
				
						    <!--
        This task runs all test cases. It invokes each test case individually.
        The "test-all" target is tied back to the "struts-test" target which
        actually runs the tests. This allows other test targets to be created
        in this section while maintaining the ability to run each test target
        individually. All individual test targets should be added to the
        "depends" attribute of the "test-all" target to provide a single
        target that runs all tests.
-->
				
						  <target name="test-all" depends="struts-tests" />
				
		
				
						  <target name="struts-tests" depends="build" >
				
		
				
						      <junit printsummary="yes" >
				
		
				
						          <classpath >
              <pathelement location="${classes.home}"/>
              <pathelement location="${build.home}"/>
              <pathelement location="${build.home}/WEB-INF/classes"/>
              <path refid="compile.classpath"/>
          </classpath>
				
						          <formatter type="plain" />
          <test name="hello.mocktest.TestHelloAction" />
          <test name="hello.mocktest.TestHelloActionMultiple" />
      </junit>
   
  </target>
</project>

 

posted @ 2006-04-19 21:41 junhong 阅读(385) | 评论 (0)编辑 收藏

how to deal with the Frame when using sitemesh

  1. install the sitemesh described in the above article.
  2. To be able to specify which mappers will be applied to a request, create the file [web-app]/WEB-INF/sitemesh.xml that contains the following:

    <sitemesh>
        <property name="decorators-file" value="/WEB-INF/decorators.xml" />
        <excludes file="${decorators-file}" />
    
        <page-parsers>
            <parser content-type="text/html"
                class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
            <parser content-type="text/html;charset=ISO-8859-1"
                class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
        </page-parsers>
    
        <decorator-mappers>
            <mapper class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper">
                <param name="config" value="${decorators-file}" />
            </mapper>
        </decorator-mappers>
    </sitemesh>
    

    in this example, the only mapper that will be applied is the ConfigDecoratorMapper, and that will only be applied to responses of type text/html or text/html;charset=ISO-8859-1. Responses of any other content type (eg image/gif) will be ignored by Sitemesh. Additionally, any files that match a pattern specified in the excludes file (in this case '/WEB-INF/decorators.xml') will not be touched by Sitemesh.

  3. The excludes file points to an XML file that contains an <excludes /> block similar to the following: add the following code in your decorators.xml

    <decorators defaultdir="/decorators">
     <excludes>
      <pattern>/plainPage.jsp</pattern>
      <pattern>/ewebeditor/*.*</pattern>
     </excludes>
     <decorator name="main" page="main.jsp">
      <pattern>/*</pattern>
     </decorator>
    </decorators>

posted @ 2006-04-13 23:46 junhong 阅读(1076) | 评论 (0)编辑 收藏

how to use sitemesh

  • Copy sitemesh-2.2.1.jar into [web-app]/WEB-INF/lib.
  • Create the file [web-app]/WEB-INF/decorators.xml that contains the following:

    <decorators>
    </decorators>
  • (Optional) Create the file [web-app]/WEB-INF/sitemesh.xml that contains the following:
    <sitemesh>
        <property name="decorators-file" value="/WEB-INF/decorators.xml" />
        <excludes file="${decorators-file}" />
    
        <page-parsers>
            <parser content-type="text/html"
                class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
            <parser content-type="text/html;charset=ISO-8859-1"
                class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
        </page-parsers>
    
        <decorator-mappers>
            <mapper class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper">
                <param name="config" value="${decorators-file}" />
            </mapper>
        </decorator-mappers>
    </sitemesh>
    
  • Add the following to [web-app]/WEB-INF/web.xml within the <web-app> tag:

    <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>sitemesh</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    Decorators are the pages that "decorate" the original, requested page (the page that is handed to the SiteMesh filter from the web container). Most (HTML) decorators are a combination of:
    • meta tags (keywords, description, author)
    • stylesheet (CSS)
    • header
    • navigation
    • footer
    • copyright notice
    First, define what different navigation/layout schemes you need. For example: Do I need a default decorator (a standard one for all pages)? Do I have a special layout for the index page? Is the header needed for my documentation files? Do I need printable version of my website?

    Web Application Structure

    Here is an example structure of a web application. This is not needed for SiteMesh to work.

    /decorators

    Directory containing all decorator files (e.g. main.jsp, printable.jsp).

    /includes

    Directory containing all files to be included into other files (e.g. header.jsp, footer.jsp, copyright.jsp).

    /images

    Directory containing all images (e.g. background.gif, logo.gif).

    /styles

    Directory containing all .CSS styles (e.g. ie4.css, ns4.css).

    /scripts

    Directory containing all scripts (JavaScript, VBScript files).

    Good practices:

    • Define a stylesheet to use in the entire application and include it using this script.
    • Use includes in your decorators (e.g. includes/navigation.jsp, includes/style.jsp).
    • Try not to refer to the absolute root ("/") path. Use <%=request.getContextPath()%>/ instead. This will make life easier when moving your web application under another context path.
    • Making your decorators compatible with multiple browsers (IE, Mozilla, Opera, ...) will (probably) make your entire application (all decorated pages) compatible.
    • Be careful when using frames, because decorators may NOT be applied to frames (FrameSetDecoratorMapper).

    My First Decorator

    Basically, all you need to know is what decorator tags you can use. The title, head and body tags are most used.
    Here is an example of a decorator (save it as /decorators/main.jsp):

    1: <%--
    2: % This is the main decorator for all SOMECOMPANY INTRANET pages.
    3: % It includes standard caching, style sheet, header, footer and copyright notice.
    4: --%>
    5: <%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %>
    6: <%@ include file="/includes/cache.jsp" %>
    7: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    8: <html>
    9: <head>
    10: <title><decorator:title default="INTRANET" /></title>
    11: <decorator:head />
    12: <%@ include file="/includes/style.jsp" %>
    13: </head>
    14: <body bgcolor="#FFFFFF" background="<%=request.getContextPath()%>/images/bg.gif">
    15: <script type="text/javascript">window.status = "Loading: <decorator:title default="INTRANET" />...";</script>
    16: <%@ include file="/includes/header.jsp"%>
    17: <table width="100%" border="0" cellspacing="0" cellpadding="0">
    18: <tr>
    19: <td height="20" nowrap> </td>
    20: </tr>
    21: <tr>
    22: <td width="1%" nowrap> </td>
    23: <td width="16%" valign="top" nowrap>
    23: <script type="text/javascript">window.status = "Loading: Navigation...";</script>
    24: <%@ include file="/includes/navigation.jsp" %>
    25: </td>
    26: <td width="2%" nowrap> </td>
    27: <td valign="top">
    28: <br>
    29: <script type="text/javascript">window.status = "Loading: Document body...";</script>
    30: <div class="docBody"><decorator:body /></div>
    31: </td>
    32: <td width="1%" nowrap> </td>
    33: </tr>
    34: </table>
    35: <br>
    36: <%@ include file="/includes/footer.jsp" %>
    37: <%@ include file="/includes/copyright.jsp" %>
    38: <script type="text/javascript">window.status = "Done";</script>
    39: </body>
    40: </html>

    • Line 1-4:
      An explanation of the decorator. This way different people working on the decorator are quickly up to speed.
    • Line 5:
      This is needed for the decorator: tags to work (also needed on all pages that work with inline decorators (page:applyDecorator).
    • Line 6:
      Sets the necessary response headers to let the browser cache the page. Omit this line if your application is real dynamic (changing data).
    • Line 10:
      If the requested page doesn't have a title, the default title is used ("INTRANET").
    • Line 15:
      The status bar gets a message when the page is loading.
    • Line 30:
      The entire body of the requested page has the docBody class. This way the navigation and body do not have to have the same font.

    Now open WEB-INF/decorators.xml with your favorite editor and let SiteMesh know there is a decorator (with a mapping):

    <decorators defaultdir="/decorators">
        <decorator name="main" page="main.jsp">
              <pattern>/*</pattern>
        </decorator>
    </decorators>
    

    Now deploy the web application, go to the welcome page, and the main decorator will be applied.

  • When a page has been parsed, it then has to be mapped to a decorator. This mapping is performed by a chain of DecoratorMappers (referred to as mappers from here on).

    For each request, the first mapper in the chain is asked which decorator should be used. It is passed across a reference to the Page object and HttpServletRequest. It returns either a Decorator object, if it knows which decorator to be used, or null. If null is returned, the next mapper in the chain is queried. This whole process is repeated until there are no more mappers in the chain, or one of the mappers returns a valid decorator. If no mappers return a decorator, the page is not decorated at all and served in its original state.

    This way the mappers are chained together and queried is known as the Chain of Responsibility design pattern.

    Examples of mappers:

    • Determine decorator based on path of requested page.
    • Use different decorators based on time, locale or browser.
    • Use simplified decorators for search-engine robots.
    • Switch decorators based on a URL parameter, request attribute or meta-tag.
    • Use custom decorators based on user's saved settings...

    The main implementation of DecoratorMapper is ConfigDecoratorMapper which reads the decorators and mappings from /WEB-INF/decorators.xml. The appropriate decorator is then applied depending on the URL pattern.

    DecoratorMappers are simple to write and the distribution includes some samples that demonstrate how to write them and how flexible they can be. These are:

    AgentDecoratorMapper Can determine the user-agent (i.e. web-browser) requesting a page, and map to a suitable Decorator.
    ConfigDecoratorMapper Default implementation of DecoratorMapper. Reads decorators and mappings from the config property (default '/WEB- INF/decorators.xml').
    CookieDecoratorMapper Will map a suitable decorator based on a cookie value.
    EnvEntryDecoratorMapper Allows the reference to a web-app environment entry for the decorator name, and falls back to ConfigDecoratorMapper's behavior if no matching environment entry is found.
    FileDecoratorMapper Will treat the name of the decorator as a file-name to use (in the context of the web-app).
    FrameSetDecoratorMapper Will use the specified decorator when the Page is an instance of HTMLPage and isFrameSet() returns true. The name of this decorator should be supplied in the decorator property - if no decorator property is supplied, no decorator is applied to frame based pages.
    InlineDecoratorMapper Used to determine the correct Decorator when using inline decorators.
    LanguageDecoratorMapper Can determine the preferred language set in the browser requesting a page, and map to a suitable Decorator (using the "Accept-Language" HTTP header).
    PageDecoratorMapper The actual Page determines the Decorator to be used.

    The 'meta.decorator' and 'decorator' properties of the page are accessed and if any of them contain the name of a valid Decorator, that Decorator shall be applied.

    ParameterDecoratorMapper Will choose the decorator based on request parameters.

    The ParameterDecoratorMapper is configured via three properties.

    decorator.parameter - the parameter which contains the name of the decorator which will be mapped. The default is "decorator".

    For example if decorator.parameter is "foobar" then myurl.jsp?foobar=mydecorator will map to the decorator named "mydecorator".

    You can also supply an optional 'confirmation parameter'. The decorator will only be mapped if the parameter named parameter.name is in the request URI and the value of that parameter is equal to the parameter.value property.

    For example assuming parameter.name=confirm and parameter.value=true the URI myurl.jsp?decorator=mydecorator&confirm=true will map the decorator mydecorator. where as the URIs myurl.jsp?decorator=mydecorator and myurl.jsp?decorator=mydecorator&confirm=false will not return any decorator.

    SessionDecoratorMapper

    Will look at a session attribute to find the name of an appropriate decorator to use. If the session attribute is present, the mapper will not do anything and allow the next mapper in the chain to select a decorator.

    By default, it will look at the 'decorator' session attribute, however this can be overriden by configuring the mapper with a 'decorator.parameter' property.

    PrintableDecoratorMapper Will check to see whether 'printable=true' is supplied as a request parameter and if so, use the specified decorator instead. The name of this decorator should be supplied in the decorator property.
    RobotDecoratorMapper Will use the specified decorator when the requester is identified as a robot (also known as spider, crawler, ferret) of a search engine. The name of this decorator should be supplied in the decorator property.

    An example of a custom DecoratorMapper could be one that displays different Decorators based on time (e.g. morning, afternoon, Christmas, etc).

    Custom mapper configuration

    To be able to specify which mappers will be applied to a request, create the file [web-app]/WEB-INF/sitemesh.xml that contains the following:

    <sitemesh>
        <property name="decorators-file" value="/WEB-INF/decorators.xml" />
        <excludes file="${decorators-file}" />
    
        <page-parsers>
            <parser content-type="text/html"
                class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
            <parser content-type="text/html;charset=ISO-8859-1"
                class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
        </page-parsers>
    
        <decorator-mappers>
            <mapper class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper">
                <param name="config" value="${decorators-file}" />
            </mapper>
        </decorator-mappers>
    </sitemesh>
    

    In this example, the only mapper that will be applied is the ConfigDecoratorMapper, and that will only be applied to responses of type text/html or text/html;charset=ISO-8859-1. Responses of any other content type (eg image/gif) will be ignored by Sitemesh. Additionally, any files that match a pattern specified in the excludes file (in this case '/WEB-INF/decorators.xml') will not be touched by Sitemesh.

    The excludes file points to an XML file that contains an <excludes /> block similar to the following:

    <decorators>
        <excludes>
            <pattern>/plainPage.jsp</pattern>
            <pattern>/plain/*.jsp</pattern>
        </excludes>
    </decorators>
    

    The above example would prevent /plainPage.jsp and any JSP pages in the /plain directory from being decorated. (Note that the pattern matching follows exactly the same rules as the decorator mappings used by the ConfigDecoratorMapper.)

    Typically the <excludes /> block is just added at the start of the decorators.xml file, however this is not a requirement and any other XML file can be specified instead by changing the excludes file specified in sitemesh.xml. This might be useful if for example the ConfigDecoratorMapper is not being used in your deployment.

    Note that preventing pages from being decorated by adding them to the excludes list superceeds, and is a better approach than, the old method of mapping the pages to a non-existent decorator. This is because when pages were mapped to a non-existent decorator they were still buffered internally by Sitemesh. By using the exclude list Sitemesh will not let the request pass straight through to the servlet container without any buffering.

    Default mapper configuration

    If sitemesh.xml is not found in the WEB-INF dir, the default mapper configuration will be used. The default mapper configuration is defined in sitemesh-default.xml (packaged inside the jar) and consists of the following mappers:

    • PageDecoratorMapper
    • FrameSetDecoratorMapper
    • PrintableDecoratorMapper
    • FileDecoratorMapper
    • ConfigDecoratorMapper

    By default only content of type text/html will be decorated by Sitemesh.



    for more detail, please refer to http://www.opensymphony.com/sitemesh/dm.html

  • posted @ 2006-04-13 23:41 junhong 阅读(2841) | 评论 (0)编辑 收藏