龔永生 (gongys@legend.com)北京市海淀區(qū)上地信息產(chǎn)業(yè)基地開(kāi)拓路7號(hào)聯(lián)想大廈
本文有兩個(gè)目的:實(shí)現(xiàn)每晚構(gòu)建平臺(tái)和探討一個(gè)軟件從需求文檔到設(shè)計(jì)文檔的書(shū)寫(xiě)規(guī)范。
本文有兩個(gè)目的:實(shí)現(xiàn)每晚構(gòu)建平臺(tái)和探討一個(gè)軟件從需求文檔到設(shè)計(jì)文檔的書(shū)寫(xiě)規(guī)范。
每晚構(gòu)建是軟件研發(fā)管理中極具價(jià)值的手段,對(duì)于加快發(fā)現(xiàn)和改正缺陷,降低集成風(fēng)險(xiǎn),提高產(chǎn)品質(zhì)量,加強(qiáng)成員溝通與協(xié)作,縮短產(chǎn)品上市時(shí)間,增加項(xiàng)目開(kāi)發(fā)透明度,提高項(xiàng)目組成員信心和斗志有著非常重要的作用和意義。本文從軟件工程過(guò)程:需求定義,分析,設(shè)計(jì)出發(fā)描述了實(shí)戰(zhàn)每晚構(gòu)建平臺(tái)的大部分過(guò)程。
軟件工程中文檔有著極其重要的地位,良好的文檔風(fēng)格和習(xí)慣是一個(gè)團(tuán)隊(duì)成熟的重要標(biāo)志。目前有些軟件研發(fā)人員特別是剛剛走上崗位的研發(fā)人員對(duì)文檔書(shū)寫(xiě)沒(méi)有一個(gè)統(tǒng)一的概念,他們不知道需要寫(xiě)那些文檔,他們不知道每個(gè)文檔需要寫(xiě)到什么地步,一提到寫(xiě)文檔就抓耳擾腮。本文試圖在這個(gè)方面提供一個(gè)文檔樣例,我不認(rèn)為我的文檔寫(xiě)的很好,只希望能起到拋磚引玉之功效。
為了實(shí)現(xiàn)這兩個(gè)目的,本文首先解釋了每晚構(gòu)建,讓讀者對(duì)這個(gè)術(shù)語(yǔ)有個(gè)比較清楚的認(rèn)識(shí);然后借助于《編寫(xiě)有效用例》中的用例編寫(xiě)技術(shù)編寫(xiě)了每晚構(gòu)建平臺(tái)的用例并附加了其他相關(guān)需求形成了一個(gè)相對(duì)使用的需求定義文檔;接著用面向?qū)ο蟮南到y(tǒng)分析方法對(duì)需求定義進(jìn)行了問(wèn)題空間分析,構(gòu)造出分析模型;為了讀者更好更快地理解設(shè)計(jì),后面是一整節(jié)的相關(guān)開(kāi)源或第三方技術(shù)介紹,有類(lèi)似于"Hello world"的入門(mén)介紹,也有精髓內(nèi)容解析,還有注意點(diǎn)提醒;接著是平臺(tái)兩大系統(tǒng)的設(shè)計(jì)。
整個(gè)每晚構(gòu)建平臺(tái)包括兩大子系統(tǒng):構(gòu)建系統(tǒng)和構(gòu)建信息顯示系統(tǒng)。構(gòu)建系統(tǒng)用ANT構(gòu)建腳本實(shí)現(xiàn);構(gòu)建信息顯示系統(tǒng)是個(gè)典型的web應(yīng)用。兩者都采用了面向?qū)ο蟮姆治龊驮O(shè)計(jì)技術(shù)。
需求定義的閱讀應(yīng)該以用戶(hù)為主,文檔撰寫(xiě)者為輔,需求定義完成的標(biāo)志是兩者達(dá)成了一致;分析章節(jié)的閱讀應(yīng)是文檔撰寫(xiě)者幫助用戶(hù)閱讀,使用戶(hù)理解并相信分析模型是能解決用戶(hù)的問(wèn)題的;設(shè)計(jì)章節(jié)的閱讀主要是系統(tǒng)實(shí)現(xiàn)者,除了要求可實(shí)現(xiàn)性外,設(shè)計(jì)模型還要和分析模型有很強(qiáng)的繼承性和一致性。
整個(gè)文檔分為上中下三篇,上篇主要講述需求定義和分析模型。
![]() ![]() |
![]()
|
構(gòu)建是從代碼庫(kù)中取出一個(gè)開(kāi)發(fā)中的軟件項(xiàng)目的所有最新代碼,放在一個(gè)干凈的環(huán)境下面,編譯源代碼文件,連接,安裝和測(cè)試,并記錄整個(gè)過(guò)程中所有日志的動(dòng)作系列。
構(gòu)建平臺(tái)是一個(gè)構(gòu)建和構(gòu)建信息展示的系統(tǒng)。
每晚構(gòu)建主要是指在開(kāi)發(fā)活動(dòng)不太激烈的情況下,盡量不要干擾開(kāi)發(fā)者的正常開(kāi)發(fā)工作,保證每天執(zhí)行構(gòu)建一次。
在《cvs和nightlybuild技術(shù)》一書(shū)中提到了每晚構(gòu)建的作用:
每個(gè)模塊有一部分成果集成一部分成果可以大大地降低集成風(fēng)險(xiǎn),加強(qiáng)模塊間協(xié)作性錯(cuò)誤的診斷,降低整個(gè)系統(tǒng)的不確定性,可以更好地定位錯(cuò)誤從而加快開(kāi)發(fā)速度,促使模塊間的接口規(guī)范而增強(qiáng)團(tuán)隊(duì)合作,每天都有新系統(tǒng)新成績(jī)是對(duì)每位項(xiàng)目組成員的一個(gè)重要激勵(lì)。
除了這些作用之外,我們還可以從每晚構(gòu)建中為項(xiàng)目管理提供額外的信息:
每晚構(gòu)建的產(chǎn)品,每晚構(gòu)建過(guò)程的記錄,每晚構(gòu)造過(guò)程中的測(cè)試記錄,分析測(cè)試質(zhì)量的測(cè)試覆蓋率分析,構(gòu)建對(duì)象中項(xiàng)目組成員的貢獻(xiàn)和項(xiàng)目組成情況分析(本文稱(chēng)為項(xiàng)目度量)。
![]() ![]() |
![]()
|
關(guān)于需求定義的文檔我們一般稱(chēng)之為《需求規(guī)格說(shuō)明書(shū)》,這個(gè)文檔的任務(wù)在于比較明確地確定項(xiàng)目的目的和范圍,提出功能需求和非功能需求,功能需求也叫行為需求,一般可以用用例來(lái)描述,如果所有需求的全集我們稱(chēng)為問(wèn)題域,則功能需求我們可以稱(chēng)之為問(wèn)題域的本質(zhì)。軟件研發(fā)公司一般都有《需求規(guī)格說(shuō)明書(shū)》的框架,規(guī)定了書(shū)寫(xiě)需求定義文檔的"綱",下面就普遍格式對(duì)需求進(jìn)行描述。
每晚構(gòu)建平臺(tái)是提供一個(gè)自動(dòng)化的信息系統(tǒng),用來(lái)輔助構(gòu)建者構(gòu)建系統(tǒng),盡量做到自動(dòng)化,最大化地減輕構(gòu)建者的工作負(fù)擔(dān);記錄構(gòu)建過(guò)程的詳細(xì)記錄,給項(xiàng)目經(jīng)理、組織層次的軟件開(kāi)發(fā)過(guò)程改進(jìn)人員評(píng)估和管理軟件開(kāi)發(fā)項(xiàng)目提供相關(guān)數(shù)據(jù),為項(xiàng)目成員和組織領(lǐng)導(dǎo)增加軟件項(xiàng)目開(kāi)發(fā)的透明度。
構(gòu)建信息關(guān)心者是關(guān)心構(gòu)建過(guò)程的記錄信息的人員,包括項(xiàng)目經(jīng)理、過(guò)程改進(jìn)人員、項(xiàng)目成員和組織領(lǐng)導(dǎo)。
根據(jù)我們前面的構(gòu)建平臺(tái)的定義,實(shí)現(xiàn)這樣的系統(tǒng)主要有兩個(gè)用例和兩種角色。如下圖:

- 用例一:構(gòu)建
名稱(chēng):構(gòu)建
級(jí)別:user goal
范圍:每晚構(gòu)建平臺(tái)
主要角色:構(gòu)建者
前提:主要角色已登錄成功場(chǎng)景:
- 構(gòu)建者要求系統(tǒng)從代碼庫(kù)中取出某個(gè)項(xiàng)目的所有源代碼
- 構(gòu)建者編譯連接所有源代碼,系統(tǒng)產(chǎn)生項(xiàng)目軟件
- 構(gòu)建者要求安裝構(gòu)建出的項(xiàng)目軟件,系統(tǒng)安裝軟件
- 構(gòu)建者測(cè)試軟件,系統(tǒng)測(cè)試并記錄測(cè)試信息
擴(kuò)展:
* 系統(tǒng)出錯(cuò):
*.1系統(tǒng)保存出錯(cuò)信息 - 用例二:瀏覽構(gòu)建信息
名稱(chēng):瀏覽構(gòu)建信息
級(jí)別:user goal
范圍:每晚構(gòu)建平臺(tái)
主要角色:構(gòu)建信息關(guān)心者
前提:已執(zhí)行構(gòu)建成功場(chǎng)景:
- 構(gòu)建信息關(guān)心者進(jìn)入構(gòu)建信息地址
- 系統(tǒng)顯示所有的構(gòu)建信息的目錄
- 相應(yīng)信息選擇感興趣的構(gòu)建信息
- 系統(tǒng)顯示相應(yīng)的信息
擴(kuò)展:
* 系統(tǒng)出錯(cuò):
*.1系統(tǒng)保存出錯(cuò)信息
- 要求用web技術(shù),采用j2ee體系;
- 采用cvs open souce系統(tǒng)作為項(xiàng)目源代碼版本控制工具;
- 數(shù)據(jù)字典
構(gòu)建信息:構(gòu)建出的產(chǎn)品;單元測(cè)試日志(成功與否,失敗則要提供相應(yīng)失敗原因);單元測(cè)試的覆蓋率;構(gòu)建過(guò)程的日志;項(xiàng)目組成員的工作量(代碼行數(shù))和項(xiàng)目源代碼樹(shù)中各個(gè)目錄和非二進(jìn)制文件的行數(shù);項(xiàng)目源代碼樹(shù)。
- 其他需求
- 盡量采用第三方開(kāi)源代碼軟件;
- 盡量使構(gòu)建用例自動(dòng)化;
- 能構(gòu)建多個(gè)項(xiàng)目;
- 實(shí)現(xiàn)用java 語(yǔ)言寫(xiě)的項(xiàng)目的構(gòu)建,但要考慮未來(lái)對(duì)c,c++程序的構(gòu)建的擴(kuò)展。
![]() ![]() |
![]()
|
系統(tǒng)分析要對(duì)問(wèn)題空間的本質(zhì)進(jìn)行分析造模,形成"要做什么"的深刻了解,盡可能地和用戶(hù)達(dá)成共識(shí)。我這里既使用了傳統(tǒng)的造模工具也利用了面向?qū)ο蠓治龅姆椒▽W(xué)來(lái)產(chǎn)生文檔。這一節(jié)中我首先對(duì)這些需求所涉及的子系統(tǒng)進(jìn)行定位,看看他們之間的關(guān)系,這里我把這種分析稱(chēng)為上下文分析;然后看看我們關(guān)心的子系統(tǒng)數(shù)據(jù)流如何,得出數(shù)據(jù)流圖;最后我分析每晚構(gòu)建平臺(tái)的"類(lèi)"模型。本節(jié)所有內(nèi)容的全體構(gòu)成了對(duì)需求的分析模型。

通過(guò)分析,我們可以看出整個(gè)每晚構(gòu)建平臺(tái)的運(yùn)作由6個(gè)實(shí)體構(gòu)成,它們之間的關(guān)系如系統(tǒng)關(guān)系圖所示。
構(gòu)建系統(tǒng)完成需求定義中的軟件項(xiàng)目的構(gòu)建工作,它從CVS系統(tǒng)中獲取項(xiàng)目源代碼,并對(duì)源代碼進(jìn)行編譯連接安裝和測(cè)試工作,產(chǎn)生的構(gòu)建信息通知給構(gòu)建信息展示系統(tǒng),構(gòu)建信息關(guān)心者通過(guò)構(gòu)建信息展示系統(tǒng)瀏覽構(gòu)建信息。用藍(lán)色標(biāo)記表示的為本平臺(tái)的組成部分。
下面的數(shù)據(jù)流程圖更好地更詳細(xì)地理解了需求,從這個(gè)流程圖可知,構(gòu)建系統(tǒng)主要和CVS系統(tǒng)打交道,并會(huì)生成構(gòu)建信息供構(gòu)建信息展示系統(tǒng)使用。構(gòu)建系統(tǒng)由取出項(xiàng)目代碼、產(chǎn)生項(xiàng)目度量數(shù)據(jù)、編譯連接安裝測(cè)試刪除和編譯連接安裝(發(fā)布版)四個(gè)任務(wù)組成;構(gòu)建信息展示系統(tǒng)和構(gòu)建信息關(guān)心者打交道,主要完成瀏覽構(gòu)建信息的任務(wù)。用黃色線(xiàn)條表示的存儲(chǔ)單元"項(xiàng)目源代碼目錄"是個(gè)臨時(shí)存儲(chǔ)單元。

最后需指出的是本分析模型實(shí)現(xiàn)的用例一為全自動(dòng)版本,是比較符合"最大化的減輕構(gòu)建者工作負(fù)擔(dān)"的需求的。
- 類(lèi)關(guān)系
類(lèi)BuildAdmin,ProjectBuild是project類(lèi)的子類(lèi),因?yàn)閺哪撤N意義上來(lái)說(shuō),它們兩者都是項(xiàng)目,不把OSScheduler類(lèi)列為project類(lèi)的子類(lèi)是因?yàn)镺SScheduler類(lèi)很簡(jiǎn)單,可能是操作系統(tǒng)的一行腳本或者一行配置。
另外BuildAdmin負(fù)責(zé)所有ProjectBuild服務(wù)execute方法的執(zhí)行,所以它和ProjectBuild的關(guān)系為一個(gè)復(fù)合關(guān)系;OSScheduler類(lèi)要啟動(dòng)BuildAdmin的execute方法,所以O(shè)SScheduler和BuildAdmin有單向關(guān)系。
另外構(gòu)建信息存放的位置是本平臺(tái)的關(guān)鍵,為了讓所有的類(lèi)對(duì)這些位置有一個(gè)共同的視圖,BuildInfoLog類(lèi)是其它類(lèi)的父類(lèi)。
- 類(lèi)描述
經(jīng)過(guò)分析不難知道,所有的構(gòu)建信息用非結(jié)構(gòu)化數(shù)據(jù)來(lái)描述比較合適。在這里可以做一個(gè)決策:要求他們是獨(dú)立的,也就是說(shuō)進(jìn)入相應(yīng)的信息地址,它們可以自我顯示;需求定義要求用www的方式瀏覽這些信息,所以同時(shí)要求他們對(duì)普通的browser是可瀏覽的。我們沒(méi)必要用類(lèi)來(lái)描述這些信息。
構(gòu)建顯示信息系統(tǒng)只有一個(gè)類(lèi)來(lái)顯示構(gòu)建信息目錄:每晚構(gòu)建顯示類(lèi)(NightlyBuild)
另外從上下文圖和數(shù)據(jù)流程圖,我們可以得到我們構(gòu)建系統(tǒng)的類(lèi):操作系統(tǒng)定時(shí)服務(wù)(OSScheduler),構(gòu)建管理服務(wù)(BuildAdmin),應(yīng)用項(xiàng)目構(gòu)建服務(wù)(ProjectBuild)。
兩個(gè)系統(tǒng)之間有一個(gè)定義構(gòu)建信息存放位置的類(lèi)BuildInfoDir,這個(gè)類(lèi)定義了兩個(gè)系統(tǒng)之間的協(xié)議。
注意書(shū)寫(xiě)約定:
${變量名}為取變量名所代表的值的含義。- 構(gòu)建信息存放位置類(lèi)
類(lèi)名 構(gòu)建信息存放位置類(lèi) 類(lèi)英文名 BuildInfoDir 成員變量 變量名 變量說(shuō)明 nightly_Build_Tags 保存所有的構(gòu)建標(biāo)簽,是構(gòu)建標(biāo)簽列表 logTopDir 保存構(gòu)建管理服務(wù)運(yùn)行的日志的目錄 statCVSTopDir 保存了所有應(yīng)用項(xiàng)目的項(xiàng)目度量結(jié)果的頂層路徑 projectLogTopDir 保存了所有應(yīng)用項(xiàng)目構(gòu)建服務(wù)實(shí)例運(yùn)行日志的頂層路徑 testCoverTopDir 保存了所有應(yīng)用項(xiàng)目的測(cè)試覆蓋率計(jì)算結(jié)果的頂層路徑 distTopDir 保存了所有應(yīng)用項(xiàng)目的發(fā)布版的頂層路徑 testTopDir 保存了所有應(yīng)用項(xiàng)目的測(cè)試結(jié)果的頂層路徑 方法 方法名 參數(shù) 執(zhí)行步驟 方法說(shuō)明
-
每晚構(gòu)建顯示類(lèi)
類(lèi)名 每晚構(gòu)建顯示類(lèi) 類(lèi)英文名 NightlyBuild 成員變量 變量名 變量說(shuō)明 方法 方法名 參數(shù) 執(zhí)行步驟 方法說(shuō)明 list_ Buildinfo_table . (1)讀取nightly_Build_Tags內(nèi)容,格式化顯示每個(gè)構(gòu)建標(biāo)簽指示的構(gòu)建信息目錄。 .
-
操作系統(tǒng)定時(shí)服務(wù)
類(lèi)名 操作系統(tǒng)定時(shí)服務(wù) 類(lèi)英文名 OSScheduler 成員變量 變量名 變量說(shuō)明 方法 方法名 參數(shù) 執(zhí)行步驟 方法說(shuō)明 excute . - 利用系統(tǒng)當(dāng)前時(shí)間形成日志文件名;
- 執(zhí)行BuildAdmin的excute()方法,并把BuildAdmin的正常輸出和錯(cuò)誤輸出記錄到日志文件名中;
- 保存日志文件到${logTopDir}指定的目錄中。
啟動(dòng)構(gòu)建管理服務(wù)并記錄日志
-
構(gòu)建管理服務(wù)
類(lèi)名 構(gòu)建管理服務(wù) 類(lèi)英文名 BuildAdmin 成員變量 變量名 變量說(shuō)明 cvsroot 保存了cvsroot環(huán)境變量 buildDir 保存了臨時(shí)存放應(yīng)用項(xiàng)目源代碼的路徑 方法 方法名 參數(shù) 執(zhí)行步驟 方法說(shuō)明 cvs_check_out 應(yīng)用項(xiàng)目cvs系統(tǒng)中的名字 - 利用cvsroot 登錄cvs系統(tǒng);
- 執(zhí)行
cvs co
指令,把參數(shù)制定的應(yīng)用項(xiàng)目源代碼取出并放在成員變量${buildDir}
指定的目錄/項(xiàng)目名字/目錄下。
從cvs中取出項(xiàng)目源代碼 statcvs - module:應(yīng)用項(xiàng)目cvs系統(tǒng)中的名字;
- project_Build_Tag:每個(gè)項(xiàng)目的當(dāng)前構(gòu)建標(biāo)簽
(1) 對(duì)源代碼進(jìn)行項(xiàng)目度量,并把結(jié)果放在${statCVSTopDir}目錄下的${project_Build_Tag}目錄下。 對(duì)源代碼進(jìn)行度量 execute . 對(duì)每一個(gè)應(yīng)用項(xiàng)目執(zhí)行: - cvs_check_out
- statcvs
- 執(zhí)行ProjectBuild的execute()方法
.
-
應(yīng)用項(xiàng)目構(gòu)建服務(wù)
類(lèi)名 構(gòu)建管理服務(wù) 類(lèi)英文名 BuildAdmin 成員變量 變量名 變量說(shuō)明 project_Build_Tag 保存了當(dāng)前正在構(gòu)建的項(xiàng)目由項(xiàng)目名稱(chēng)和當(dāng)前系統(tǒng)時(shí)間組成的構(gòu)建標(biāo)簽 方法 方法名 參數(shù) 執(zhí)行步驟 方法說(shuō)明 test_project . - 為測(cè)試而編譯連接源代碼;
- 安裝測(cè)試版產(chǎn)品;
- 測(cè)試產(chǎn)品;
- 計(jì)算測(cè)試覆蓋率;
- 輸出測(cè)試結(jié)果到
${testTopDir}
指定目錄下的${project_Build_Tag}
目錄下; - 輸出測(cè)試覆蓋率結(jié)果到
${testCoverTopDir}
指定目錄下的${project_Build_Tag}
目錄下;
. dist_project . - 編譯連接源代碼;
- 安裝發(fā)布版產(chǎn)品到
${distTopDir}
指定目錄下的${project_Build_Tag}
目錄下。
. execute . - 執(zhí)行test_project方法
- 執(zhí)行dist_project方法
- 把兩個(gè)方法的日志合并成一個(gè)日志文件,命名為
${project_Build_Tag}.txt
,并把其放在${ projectLogTopDir }
指定的目錄下。
.
- 構(gòu)建信息存放位置類(lèi)
- 執(zhí)行場(chǎng)景
- 構(gòu)建場(chǎng)景
- 1.操作系統(tǒng)定時(shí)服務(wù)類(lèi)開(kāi)始執(zhí)行;
- 1. 1啟動(dòng)BuildAdmin對(duì)象的execute方法
- 針對(duì)每個(gè)被管理的項(xiàng)目,執(zhí)行下列步驟:
- #begin
- 1.1.1根據(jù)當(dāng)前系統(tǒng)時(shí)間和項(xiàng)目名稱(chēng)生成該項(xiàng)目的構(gòu)建標(biāo)簽,并記錄構(gòu)建標(biāo)簽
- 1.1.3以構(gòu)建標(biāo)簽為參數(shù)生成項(xiàng)目的ProjectBuild對(duì)象
- 1.1.2調(diào)用cvs_check_out方法,從cvs系統(tǒng)中取出該項(xiàng)目的源代碼
- 1.1.3執(zhí)行statcvs方法,生成項(xiàng)目度量數(shù)據(jù)并保存到相應(yīng)的位置
- 1.1.4調(diào)用該P(yáng)rojectBuild對(duì)象的execute方法,完成項(xiàng)目的測(cè)試和安裝,并產(chǎn)生相應(yīng)的構(gòu)建信息
- #end
- 瀏覽構(gòu)建信息場(chǎng)景
我們已經(jīng)說(shuō)過(guò),各個(gè)構(gòu)建信息能實(shí)現(xiàn)在browser中的自我展示,所以Nightlybuild對(duì)象只需通過(guò)某種格式顯示各個(gè)構(gòu)建信息的目錄,構(gòu)建信息請(qǐng)求者可以通過(guò)這些目錄請(qǐng)求各個(gè)構(gòu)建信息。
1.NightlyBuild對(duì)象接到瀏覽構(gòu)建信息的請(qǐng)求,通過(guò)對(duì)自己保存的構(gòu)建信息目錄和構(gòu)建標(biāo)簽列表組織構(gòu)建信息目錄。
- 構(gòu)建場(chǎng)景
![]() ![]() |
![]()
|
- word 文檔書(shū)寫(xiě)排版工具
- powerpoint,圖片組織繪畫(huà)工具
- visio 繪制數(shù)據(jù)流圖,ER圖等的工具
- rational rose,繪制UML圖形的工具
- windows 附件中的畫(huà)圖來(lái)截取圖片
- 操作系統(tǒng)的全屏打印功能
本文是實(shí)戰(zhàn)每晚構(gòu)建系列的第二篇,主要敘述在設(shè)計(jì)構(gòu)建平臺(tái)時(shí)要考慮的一些開(kāi)源或第三方技術(shù),其中既有有類(lèi)似于'Hello world'的入門(mén)介紹,也有精髓內(nèi)容解析,還有注意點(diǎn)提醒。
在進(jìn)行設(shè)計(jì)之前,我們有必要了解一些開(kāi)源或第三方在項(xiàng)目構(gòu)建方面的技術(shù)。學(xué)習(xí)這些技術(shù)的最好方式是弄到一份,仔細(xì)閱讀文檔,實(shí)踐一些小的例子,在工作當(dāng)中使用之。
為了讓大家更好地了解后面的設(shè)計(jì),本節(jié)出了介紹基本知識(shí)外,還介紹了這個(gè)工具的主要特點(diǎn)中的三點(diǎn):多個(gè)文件組成配置文件,目標(biāo)依賴(lài)性,擴(kuò)展,另外講述了ant配置腳本的面向?qū)ο筇匦浴?
- 簡(jiǎn)述
Ant是Apache開(kāi)源運(yùn)動(dòng)中的一份子,它是和大家所熟悉的Make系統(tǒng)一樣的基于Java的構(gòu)建工具。他克服了Make的os依賴(lài)性,同樣也可以調(diào)用os特有的指令。不像Make使用操作系統(tǒng)的腳本命令,ant使用java 類(lèi)來(lái)擴(kuò)展自身。Ant的配置文件是流行的xml格式。
下面是一個(gè)最簡(jiǎn)單的build.xml文件:
<?xml version="1.0" encoding="ISO-8859-1"?> <project name="projectTemplate" default="init" basedir="."> <target name="init" > <property name="lib.dir" value="lib"/> <echo message="Hello ${user.name}! lib.dir is set to ${lib.dir}" > </echo> </target> </project>
運(yùn)行ant命令將產(chǎn)生下面的結(jié)果:
gongys$ ant gongys$ Hello gongys! lib.dir is set to lib
在這個(gè)簡(jiǎn)單的build.xml顯示了ant配置文件定義目標(biāo)(target),定義屬性(property),訪(fǎng)問(wèn)屬性的方法,其中${user.name}是個(gè)系統(tǒng)屬性。
- 多個(gè)xml文件定義ant配置文件
下面我們給出一個(gè)相對(duì)復(fù)雜的build.xml文件:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE project [ <!ENTITY build-abstract SYSTEM "file:./build-abstract.xml"> ]> <project name="projectTemplate" default="init" basedir="."> <target name="init" depends="init.variables"> <property name="lib.dir" value="lib"/> <echo message="Hello ${user.name}! lib.dir is set to ${lib.dir}" /> <echo message="build.dir is set to ${build.dir} in build-abstract.xml " > </echo> </target> <target name=" clean" depends="init" > <del dir="${build.dir}"/> </target> &build-abstract; </project>
其中
<!ENTITY build-abstract SYSTEM "file:./build-abstract.xml">
定義了一個(gè)名為build-abstract
的實(shí)體,其內(nèi)容為當(dāng)前目錄下的build-abstract.xml
文件。&build-abstract;
引用了這個(gè)實(shí)體,這樣在build.xml
文件就可以用build-abstract.xml
定義的目標(biāo)啦。下面是
build-abstract.xml
的內(nèi)容:<target name="init.variables"> <property name="build.dir" value="tempbuild"/> </target>
- 開(kāi)發(fā)和定義自己的task
ant是一個(gè)可以擴(kuò)充的構(gòu)建工具,開(kāi)發(fā)者可以開(kāi)發(fā)自己的java類(lèi)來(lái)擴(kuò)充ant。下面是一個(gè)簡(jiǎn)單的擴(kuò)充類(lèi):
package com.mydomain; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; public class MyVeryOwnTask extends Task { private String msg; // The method executing the task public void execute() throws BuildException { System.out.println(msg); } // The setter for the "message" attribute public void setMessage(String msg) { this.msg = msg; } }
這個(gè)擴(kuò)展任務(wù)將有一個(gè)屬性message,ant在執(zhí)行這個(gè)任務(wù)時(shí)會(huì)調(diào)用execute方法。下面是在build.xml配置文件中使用這個(gè)擴(kuò)展的示例:
<?xml version="1.0"?> <project name="OwnTaskExample" default="main" basedir="."> <taskdef name="mytask" classname="com.mydomain.MyVeryOwnTask"> <classpath> <pathelement location="where/u/put/the/class/"/> </classpath> <target name="main"> <mytask message="Hello World! MyVeryOwnTask works!"/> </target> </project>
- 目標(biāo)依賴(lài)性
了解ant的另外一點(diǎn)是target的依賴(lài)性,上面這個(gè)比較復(fù)雜一點(diǎn)的build.xml的依賴(lài)性如下圖所示:
這樣的依賴(lài)圖使得執(zhí)行命令ant init 時(shí)先執(zhí)行init.variable目標(biāo)中的指令,執(zhí)行clean目標(biāo)時(shí)先執(zhí)行依次執(zhí)行init.variables和init目標(biāo)。
到目前為止,還沒(méi)有哪一個(gè)集成工具開(kāi)發(fā)出自動(dòng)分析ant配置文件依賴(lài)性圖的插件,但是命令行下已經(jīng)有了。
這個(gè)工具名叫
vizant
,也就是一個(gè)實(shí)現(xiàn)了擴(kuò)展ant任務(wù)的jar文件,還包含了一些文檔和例子,下面是我產(chǎn)生上面的目標(biāo)依賴(lài)圖的build.xml
<?xml version="1.0"?> <!-- $Id: build.xml,v 1.1 2003/04/29 10:25:12 gongys Exp $ --> <project name="Vizant" basedir="." default="dot"> <property name="build" location="output"/> <property name="vizant.antfile" value="${buildfile}"/> <property name="dot.format" value="png"/> <target name="init"> <echo message="${vizant.antfile}" /> <tstamp/> <mkdir dir="${build}"/> </target> <target name="defvizant"> <taskdef name="vizant" classname="net.sourceforge.vizant.Vizant" classpath="vizant.jar"/> </target> <target name="vizant" depends="defvizant,init"> <vizant antfile="${vizant.antfile}" outfile="${build}/build.dot" uniqueref="true"/> </target> <target name="dot" depends="vizant"> <exec executable="${basedir}/dot.exe" v <arg line="-T${dot.format} ${build}/build.dot -o ${build}/out.${dot.format}"/> </exec> </target> </project>
你在要分析的項(xiàng)目目錄下執(zhí)行如下命令便可在output/out.png的依賴(lài)圖形文件。
gongys$ ant -f vizant/build.xml -Dbuildfile=build.xml
-f vizant/build.xml
定義了ant配置文件,-Dbuildfile=build.xml
定義了要分析的ant配置文件。 - Ant配置腳本的面向?qū)ο笮?/strong>
從上面可以知道一個(gè)ant的配置腳本可以由多個(gè)配置文件組成,一個(gè)配置文件由目標(biāo)和屬性定義語(yǔ)句組成。我們可以把屬性看成是面向?qū)ο笾械某蓡T變量,目標(biāo)看成是方法,這樣一個(gè)配置文件就定義了一個(gè)"類(lèi)",而且它的成員都是靜態(tài)的,就是說(shuō)不需要生成"對(duì)象"。一個(gè)類(lèi)是可以運(yùn)行的如果它的配置文件的頂級(jí)元素是<project>,這就好像我們的java類(lèi)實(shí)現(xiàn)了
public static void main(String[] args)
方法一樣。可以用xml中的定義和引用實(shí)體的方式來(lái)申明一個(gè)"類(lèi)"繼承了另一個(gè)"類(lèi)",這樣我們可以實(shí)現(xiàn)面向?qū)ο螽?dāng)中的"類(lèi)繼承層次圖";我們可以用<ant>任務(wù)來(lái)實(shí)現(xiàn)跨對(duì)象之間的調(diào)用(要求這些對(duì)象的類(lèi)是可以運(yùn)行的),這樣就形成了"對(duì)象協(xié)作圖";我們可以用<antcall>和目標(biāo)的depends屬性來(lái)實(shí)現(xiàn)對(duì)象內(nèi)部的"方法調(diào)用"。注意Ant配置腳本的面向?qū)ο竽P蜎](méi)辦法實(shí)現(xiàn)方法重載或覆蓋。
大部分集成工具都集成了junit單元測(cè)試插件,并有向?qū)椭鷮?xiě)單元測(cè)試。Junit發(fā)行包的文檔很詳細(xì)地介紹了Junit的設(shè)計(jì)概念和所使用的設(shè)計(jì)模式。在這里我簡(jiǎn)單地說(shuō)明如何寫(xiě)測(cè)試用例、在ant配置文件中調(diào)用測(cè)試用例和產(chǎn)生測(cè)試報(bào)告的方法。
- 寫(xiě)測(cè)試用例
下面是在eclipse junit向?qū)?duì)MyCode類(lèi)編寫(xiě)的測(cè)試用例TestMyCode文件基礎(chǔ)上寫(xiě)的代碼:
import junit.framework.TestCase; /* * Created on 2003-4-30 * * To change the template for this generated file go to * Window>Preferences>Java>Code Generation>Code and Comments */ /** * @author gongys * * To change the template for this generated type comment go to * Window>Preferences>Java>Code Generation>Code and Comments */ public class TestMyCode extends TestCase { MyCode myFixture=null; /** * Constructor for TestTest. * @param arg0 */ public TestTest(String arg0) { super(arg0); } /* * @see TestCase#setUp() */ protected void setUp() throws Exception { super.setUp(); myFixture = new MyCode(); System.out.println("setup"); } /* * @see TestCase#tearDown() */ protected void tearDown() throws Exception { super.tearDown(); myFixture = null; System.out.println("teardown"); } public void testSetName() { myFixture.setName("gongys") assertEquals("gongys", myFixture.getName()); System.out.println("testSetName"); System.out.println(this.getName()); } public void testSetAge() { System.out.println("testSetAge"); myFixture.setAge (12) assertEquals(12,myFixture.getAge()); System.out.println(this.getName()); } }
有幾點(diǎn)需要特殊指出:
- 一個(gè)TestCase子類(lèi)中可以包含多個(gè)test方法,test方法的原型必須是public void testXXX();
- 在執(zhí)行過(guò)程中,junit框架為每一個(gè)test方法實(shí)例化一個(gè)TestCase子類(lèi);
- 執(zhí)行testCase的順序如下:setUp(),testXXX(),teardown();
- fixture是指為每一個(gè)測(cè)試方法準(zhǔn)備的東西:比如數(shù)據(jù)庫(kù)連接,此時(shí)的目標(biāo)等,一般在setUp()中設(shè)置,testXXX()中使用,teardown()中釋放。
運(yùn)行這個(gè)測(cè)試的結(jié)果如下:
setup testSetName testSetName teardown setup testSetAge testSetAge teardown
- ant使用測(cè)試用例
利用ant 的junit任務(wù)和其子任務(wù)test可以在ant配置文件中執(zhí)行單元測(cè)試,如下所示:
<target name="outter_unittest" depends="init"> <junit printsummary="yes" fork="yes" haltonfailure="no" > <classpath> <fileset dir="${build.dir}"> <include name="TestMyCode.class" /> <include name="MyCode.class" /> </fileset> <pathelement location="${lib.dir}/${junit.jar}"/> </classpath> <formatter type="xml"/>> <!--this specify the output format of junit --> <test name="TestMyCode" todir="tempjunit" />> <!--this will run all testXXX methods of the TestMyCode and generate the output to dir tempjunit , the output file is TEST-TestMyCode .xml --> </junit> </target>
需要注意的是:
- 要正確設(shè)置
junit
任務(wù)的classpath
子元素,classpath
至少要包含三樣?xùn)|西,TestCase
子類(lèi)比如TestMyCode
,你測(cè)試的代碼的java
類(lèi)比如MyCode
,和junit.jar
; - 可以使用
formatter
子元素設(shè)置junit
任務(wù)中test
任務(wù)的輸出的格式; test
任務(wù)可以設(shè)置輸出文件的名字和目錄;- junit任務(wù)還有一個(gè)子任務(wù)batchtest可以用通配符來(lái)指定TestCase子類(lèi)。
- 要正確設(shè)置
- Ant中生成測(cè)試報(bào)告
在上面的一節(jié)中我們談到j(luò)unit任務(wù)可以生成測(cè)試結(jié)果,并輸出到指定的文件和目錄中,在ant中,我們還可以用junitreport任務(wù)對(duì)這些測(cè)試結(jié)果進(jìn)行處理,生成html文件:
<junitreport todir="./tempjunit "> <fileset dir="./tempjunit "> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="./report/html"/> </junitreport>
junitreport任務(wù)首先把fileset中指定的測(cè)試結(jié)果歸集成一個(gè)xml文件,接著用子任務(wù)report轉(zhuǎn)化成html文件,子任務(wù)report的format屬性指定生成的結(jié)果是框架的還是沒(méi)框架的。
![]() ![]() |
![]()
|
cactus單元測(cè)試工具是對(duì)junit框架的擴(kuò)充,使junit的思想和便利同樣用于Browser/Server web應(yīng)用程序中的測(cè)試,具體的來(lái)說(shuō)就是測(cè)試servlet,jsp和filter。本節(jié)講述cactus 單元測(cè)試原理,servlet測(cè)試用例的書(shū)寫(xiě)(jsp,filter的測(cè)試用例的書(shū)寫(xiě)請(qǐng)參照cactus文檔),如何配置ant運(yùn)行這樣的測(cè)試。
-
cactus 單元測(cè)試原理
Cactus提供了好幾個(gè)擴(kuò)展
JUnit Testcase
的子類(lèi)和相應(yīng)的redirector,上面的工作原理圖解釋了cactus測(cè)試的工作原理。其中
YYYTestCase = ( ServletTestCase
子類(lèi)| FilterTestCase
子類(lèi) |JspTestCase
子類(lèi))XXX我們寫(xiě)的testcase名字的后半部分。
下面我們分步驟解釋在我們的
cactus Testcase
子類(lèi)里頭的每一個(gè)testXXX()
方法的具體情況:- JUnit 測(cè)試運(yùn)行器調(diào)用
YYYTestCase.runTest()
方法。這個(gè)方法尋找beginXXX(WebRequest)
方法,如果找到則執(zhí)行。 傳給beginXXX(WebRequest)
方法的參數(shù)WebRequest
可用來(lái)設(shè)置 HTTP頭, HTTP 參數(shù),這些參數(shù)將被發(fā)送到第2步的Redirector
代理。 YYYTestCase.runTest()
方法打開(kāi)連向Redirector
代理的HTTP 連接,beginXXX(WebRequest)
方法設(shè)置的HTTP協(xié)議參數(shù)將被送到代理。Redirector
代理在服務(wù)端作為YYYTestCase
的代理(其實(shí)我們的YYYTestCase被實(shí)例化兩次,一次在客戶(hù)端被JUnit 測(cè)試運(yùn)行器實(shí)例化,一次在服務(wù)器端被代理實(shí)例化,客戶(hù)端實(shí)例執(zhí)行beginXXX() and endXXX()
方法,服務(wù)端實(shí)例執(zhí)行Junit 測(cè)試用例的方法setup(),testXXX(),and teardown())
。Redirector 代理有下列事情可做:- 用java的內(nèi)省功能創(chuàng)建服務(wù)端實(shí)例;
- 設(shè)置一些缺省對(duì)象;
- 按照客戶(hù)端實(shí)例的意愿創(chuàng)建session。
- 執(zhí)行Junit 測(cè)試用例的方法
setup(),testXXX(),and teardown();
- 我們的 testXXX()方法調(diào)用服務(wù)端代碼來(lái)進(jìn)行測(cè)試,使用
assertEquals()
方法對(duì)測(cè)試結(jié)果和預(yù)期結(jié)果進(jìn)行比較,如果兩者相符為測(cè)試成功,否則為測(cè)試失??; - 如果測(cè)試失敗,Redirector 代理將捕獲testXXX()方法拋出的的異常;
- Redirector 代理將異常信息返回給客戶(hù)端的JUnit 測(cè)試運(yùn)行器,JUnit 測(cè)試運(yùn)行器可以生成測(cè)試報(bào)告;
- 如果沒(méi)有異常出現(xiàn),
YYYTestCase.runTest()
方法尋找endXXX(org.apache.cactus.WebResponse)
endXXX(com.meterware.httpunit.WebResponse)
(后者用在和httpunit集成中) 方法,如果找到則執(zhí)行。endXXX方法中,我們可以檢查返回的HTTP 頭, Cookies 和output stream ,這個(gè)檢查可以借助于Junit的 assertEquals或者cactus提供的幫助類(lèi)。
在這里需要提出的一點(diǎn)就是:代理不會(huì)去真正執(zhí)行servlet,或filter,或jsp的代碼,你需要在testXXX方法中調(diào)用或模仿這些代碼。
- JUnit 測(cè)試運(yùn)行器調(diào)用
-
書(shū)寫(xiě)servlet測(cè)試用例
import java.io.IOException; import junit.framework.Test; import junit.framework.TestSuite; import org.apache.cactus.ServletTestCase; import org.apache.cactus.WebRequest; import org.apache.cactus.WebResponse; public class TestSampleServlet extends ServletTestCase { public TestSampleServlet(String theName) { super(theName); } public static Test suite() { return new TestSuite(TestSampleServlet.class); } //這個(gè)方法在服務(wù)端運(yùn)行,用來(lái)設(shè)置fixture public void setup(){ } //這個(gè)方法在服務(wù)端運(yùn)行,用來(lái)釋放fixture public void teardown(){ } //這個(gè)方法在客戶(hù)端運(yùn)行,可以用來(lái)設(shè)置請(qǐng)求參數(shù) public void beginSaveToSessionOK(WebRequest webRequest) { webRequest.addParameter("testparam", "it works!"); webRequest.setURL("localhost", "test", "SampleServlet" ,"gongys", "name=gongys"); } //這個(gè)方法在服務(wù)端運(yùn)行,用來(lái)具體進(jìn)行代碼測(cè)試 public void testSaveToSessionOK() throws IOException { SampleServlet servlet = new SampleServlet(); servlet.saveToSession(request); System.out.println(this.request.getPathInfo()); System.out.println(this.request.getParameter("name")); this.response.getWriter().println("gongys"); assertEquals("it works!", session.getAttribute("testAttribute")); } //這個(gè)方法在客戶(hù)端執(zhí)行,用來(lái)驗(yàn)證返回結(jié)果 public void endSaveToSessionOK(WebResponse theResponse){ System.out.println(theResponse.getText()); } }
-
配置ant運(yùn)行cactus測(cè)試
- 類(lèi)路徑的設(shè)置
我們要按照下面的圖設(shè)置客戶(hù)端(ant junit任務(wù)中)設(shè)置classpath,并把右半部分所示的類(lèi)放到服務(wù)器或者webapp的類(lèi)路徑上
- 客戶(hù)端cactus.properties
我們知道,cactus需要redirector 代理才能工作,我們除了把這些代理考到相應(yīng)的webapp的類(lèi)路徑(對(duì)于filter和servlet代理)或webapp路徑(對(duì)于jsp代理)外,我們還需要告訴客戶(hù)端測(cè)試實(shí)例到哪里去找這些代理,下面是cactus.properties的內(nèi)容:
cactus.contextURL = http://localhost:8080/test
其中test為被測(cè)試webapp的上下文路徑。
cactus.properties也必須放在ant junit任務(wù)的classpath中。
- 服務(wù)器(假設(shè)為tomcat 4.12)server.xml的設(shè)置
我們必須在server.xml中添加cactus redirector代理,使得這些代理能接受客戶(hù)端測(cè)試實(shí)例傳過(guò)來(lái)的請(qǐng)求。詳細(xì)添加辦法請(qǐng)參見(jiàn)cactus 文檔。
有了正確的junit 類(lèi)路徑的設(shè)置,其他的就合正常的junit測(cè)試一樣。
- 類(lèi)路徑的設(shè)置
clover覆蓋率計(jì)算工具通過(guò)在被測(cè)源代碼中插入相關(guān)指令,在被測(cè)源代碼被執(zhí)行時(shí)這些指令被執(zhí)行,用以統(tǒng)計(jì)被測(cè)源代碼被執(zhí)行的次數(shù),clover利用一個(gè)數(shù)據(jù)庫(kù)來(lái)保存這些數(shù)據(jù)。Clover還提供了訪(fǎng)問(wèn)這個(gè)數(shù)據(jù)庫(kù)的工具,并產(chǎn)生html報(bào)告文檔。
-
配置ant運(yùn)行clover分析
clover實(shí)現(xiàn)了一些ant任務(wù),下面是ant中定義這些任務(wù)的代碼
<taskdef resource="clovertasks" > <classpath> <pathelement location="${clover.jar}"/> </classpath> </taskdef>
下面的代碼初始化clover數(shù)據(jù)庫(kù):
<target name="with.clover" depends="init"> <!-- 刪除${build.dir}使得重新編譯源代碼 --> <delete dir="${build.dir}" /> <mkdir dir="${build.dir}" /> <clover-setup initString="${user.home}/${ANTLOG_FILE_NOEXT}.db" /> </target>
下面的代碼產(chǎn)生clover分析,格式為html,結(jié)果放在tempcloverreport目錄中:
<target name="clover.html" > <delete dir="tempcloverreport"></delete> <mkdir dir="tempcloverreport" /> <property name="clover.html" value="ok"<>/property> <clover-report> <current outfile="tempcloverreport"> <format type="html"/> </current> </clover-report> </target> <!-- 下面用一個(gè)目標(biāo)來(lái)初始化clover,編譯源代碼,unittest單元測(cè)試和clover分析--> <target name="clover_report" depends="with.clover, compile,unittest, clover.html"> </target>
這個(gè)任務(wù)的工作原理為,with.clover在初始化clover數(shù)據(jù)庫(kù)后,監(jiān)視compile;在javac編譯java源代碼時(shí)把記錄代碼執(zhí)行的相關(guān)指令插入到j(luò)ava源代碼中;在單元測(cè)試時(shí),這些插入的代碼就開(kāi)始記錄被測(cè)試代碼的執(zhí)行次數(shù),把結(jié)果輸出到clover數(shù)據(jù)庫(kù)中;clover.html目標(biāo)根據(jù)數(shù)據(jù)庫(kù)中的數(shù)據(jù)生成html文件。
需要注意的幾點(diǎn):
- 如果是執(zhí)行cactus類(lèi)的client/server測(cè)試,在服務(wù)端的類(lèi)徑中必須包含clover.jar類(lèi);
- clover 是一個(gè)商業(yè)工具,但可以得到30天的評(píng)估license;
- clover 在編譯過(guò)程中改變了代碼的執(zhí)行路徑,在產(chǎn)品發(fā)布時(shí)必須單獨(dú)執(zhí)行compile目標(biāo)。
- Clover 分析結(jié)果
下面是Clover 分析結(jié)果的圖示,讀者可以自己看出從這個(gè)分析中能得到什么。第一個(gè)圖是顯示一個(gè)項(xiàng)目的整體覆蓋率情況,第二個(gè)圖顯示了每一個(gè)類(lèi)每行代碼的覆蓋情況。
statcvs是一個(gè)利用cvs reporsitory log生成項(xiàng)目度量的工具,這些度量包括每個(gè)作者的代碼量,每個(gè)目錄或文件的代碼行數(shù)。使用statcvs先要學(xué)會(huì)使用cvs。
- Ant 中使用cvs
Ant 中使用cvs是通過(guò)cvs任務(wù)來(lái)完成的:
<property name="cvsroot" value=":pserver:anonymous@10.1.36.135:/data/src" /> <!--取出源代碼,放在tmp目錄下,相當(dāng)于執(zhí)行cvs co -d ${base.path}/tmp/${location} --> <cvs cvsRoot="${cvsroot}" package="${location}" dest="${base.path}/tmp" /> <!-- 執(zhí)行cvs log ,結(jié)果放在tmp.log中--> <cvs dest="${base.path}/tmp/${location}" command="log" output="${base.path}/tmp/${location}/cvs.log"/>
- Ant 中使用statcvs
Statcvs實(shí)現(xiàn)了一個(gè)ant任務(wù),下面是ant中定義這個(gè)任務(wù)的代碼:
<taskdef name="statcvs" classname="net.sf.statcvs.ant.StatCvsTask"> <classpath> <pathelement path="${statcvs.jar}"/> </classpath> <</taskdef>
下面是使用statcvs任務(wù)產(chǎn)生項(xiàng)目度量數(shù)據(jù)的代碼,結(jié)果是一些html文件,放在${statcvs.htmldir}目錄下:
<statcvs projectName="${location}" projectDirectory="${base.path}/tmp/${location}" cvsLogFile="${base.path}/tmp/${location}/cvs.log" outputDirectory="${statcvs.htmldir}" />
velocity模版系統(tǒng)比起jsp模版來(lái)說(shuō)有比較大的好處:
- 實(shí)現(xiàn)視圖和控制代碼的完全隔離
在jsp中,我們可以嵌入執(zhí)行代碼,jsp本質(zhì)是具有格式化代碼和控制代碼混合能力,雖然大家發(fā)明了好多方法、設(shè)計(jì)模式和最佳實(shí)踐,可是不能從根本上消除jsp編寫(xiě)員混合格式化代碼和控制代碼的惡習(xí);而在velocity模版系統(tǒng)中,這種混合不可能存在,你不可能在velocity的.vm文件中通過(guò)代碼
Person p = new Person()
生成一個(gè)Java對(duì)象,這些業(yè)務(wù)對(duì)象只能在控制中生成并放到context中。 - 安全
jsp文件被編譯之后形成了一個(gè)類(lèi)似于servlet的東西,幾乎可以在jsp中干任何事,你可以在jsp中寫(xiě) System.exit(0)來(lái)關(guān)掉java虛擬機(jī),或利用別的什么漏洞。
這里只說(shuō)這些好處,關(guān)于其他的大家可以到網(wǎng)上去查或自己總結(jié)。下面我要介紹一下velocity模版系統(tǒng)工作機(jī)制和關(guān)于velocity的設(shè)置問(wèn)題。
- velocity模版系統(tǒng)工作機(jī)制
我們以在servlet環(huán)境下的模版系統(tǒng)為例(當(dāng)然控制還可以由其他代碼來(lái)實(shí)現(xiàn))??刂瓶梢詫?shí)例化一些業(yè)務(wù)對(duì)象比如Person 放到context 中(執(zhí)行context的相關(guān)方法),控制在接著裝載相關(guān)的視圖的模版比如PersonInfo.vm,產(chǎn)生Template實(shí)例,并讓這個(gè)實(shí)例解釋自己生成輸出比如html格式流,Template實(shí)例在解釋模版的時(shí)候會(huì)根據(jù)模版文件中的指令訪(fǎng)問(wèn)context中的業(yè)務(wù)對(duì)象。
所以要使這個(gè)模式工作,重要的一點(diǎn)是控制必須和視圖就context中的業(yè)務(wù)對(duì)象的名字達(dá)成一致,這就是控制和視圖的協(xié)議。
- velocity的設(shè)置
velocity運(yùn)行的第一個(gè)任務(wù)就是初始化,執(zhí)行Velocity.init方法。無(wú)參數(shù)的init方法會(huì)采用缺省的屬性配置,在velocity.jar 中的org.apache.velocity.runtime.defaults.velocity.properties位置;使用有參數(shù)的init方法,參數(shù)傳遞的是一個(gè)屬性文件或java.util.Properties 對(duì)象,參數(shù)中定義的屬性會(huì)覆蓋缺省的屬性設(shè)置,沒(méi)定義的屬性會(huì)采用缺省的屬性設(shè)置。
比較有用的屬性設(shè)置是讀取模版文件時(shí)采用的字符集、產(chǎn)生輸出流時(shí)使用的編碼、模版所在的位置和模版裝載器:
input.encoding = gbk output.encoding = gbk file.resource.loader.path = templates
![]() ![]() |
![]()
|
- word 文檔書(shū)寫(xiě)排版工具
- powerpoint,圖片組織繪畫(huà)工具
- visio 繪制數(shù)據(jù)流圖,ER圖等的工具
- rational rose,繪制UML圖形的工具
- windows 附件中的畫(huà)圖來(lái)截取圖片
- 操作系統(tǒng)的全屏打印功能
本文是實(shí)戰(zhàn)每晚構(gòu)建系列的第三篇,利用第二篇文章中敘述的開(kāi)源技術(shù)對(duì)第一篇中的分析模型進(jìn)行設(shè)計(jì)和實(shí)現(xiàn)。
1、構(gòu)建信息顯示系統(tǒng)的設(shè)計(jì)
這是一個(gè)典型的web應(yīng)用系統(tǒng),不過(guò)非常簡(jiǎn)單。根據(jù)《面向?qū)ο蟮南到y(tǒng)分析和設(shè)計(jì)》所描述的,設(shè)計(jì)主要對(duì)四個(gè)部分進(jìn)行描述:
- 問(wèn)題域的細(xì)化:考慮將來(lái)實(shí)現(xiàn)語(yǔ)言的特性和利用某些設(shè)計(jì)模式,對(duì)分析模型進(jìn)行細(xì)化,并作某些權(quán)衡。實(shí)現(xiàn)對(duì)未來(lái)系統(tǒng)"如何做事情"的描述。
- 人機(jī)界面設(shè)計(jì):考慮和使用者的交互,對(duì)信息顯示的布局和接收用戶(hù)指令或數(shù)據(jù)的行為進(jìn)行設(shè)計(jì)。
- 存儲(chǔ)設(shè)計(jì):考慮如何保存業(yè)務(wù)數(shù)據(jù),主要考慮現(xiàn)在的存儲(chǔ)方式,同時(shí)也要考慮未來(lái)存儲(chǔ)方式的可能變化,即業(yè)務(wù)對(duì)象和存儲(chǔ)方式的耦合性問(wèn)題。
- 系統(tǒng)接口設(shè)計(jì):主要考慮本系統(tǒng)和外在系統(tǒng)之間的合作協(xié)議,特別是不要忘了和操作系統(tǒng)或驅(qū)動(dòng)程序的接口。
我們?cè)诜治瞿P椭袥](méi)有考慮實(shí)現(xiàn)的問(wèn)題,現(xiàn)在我們必須細(xì)化構(gòu)建信息顯示系統(tǒng)的分析模型,使之達(dá)到可實(shí)現(xiàn)之地步。
根據(jù)前面velocity模版系統(tǒng)地介紹,我們擴(kuò)充一下顯示系統(tǒng)的類(lèi)圖。如下面的"顯示系統(tǒng)設(shè)計(jì)類(lèi)圖"可知,由于Java語(yǔ)言不支持多繼承,而且原來(lái)分析模型的BuildInfoLog類(lèi)只有成員變量,我們把這個(gè)類(lèi)改變成Java語(yǔ)言當(dāng)中的接口來(lái)實(shí)現(xiàn),我們把NightlyBuild變成了實(shí)現(xiàn)這個(gè)接口的一個(gè)類(lèi),因此同樣擁有BuildInfoLog的數(shù)據(jù)成員。另外NightlyBuild還繼承了Java.util.Hashtable類(lèi),使得其表現(xiàn)為一個(gè)哈西表,這個(gè)哈西表以項(xiàng)目的名字為Key,以該項(xiàng)目的構(gòu)建標(biāo)簽列表為值。根據(jù)MVC模式的精神,NightlyBuild類(lèi)是個(gè)業(yè)務(wù)對(duì)象類(lèi),應(yīng)該從信息顯示的格式化功能中解放出來(lái),所以NightlyBuild類(lèi)原來(lái)的list_ Buildinfo_table方法放到buildinfo_list.vm模版文件中實(shí)現(xiàn)。同時(shí)我們?cè)黾恿艘粋€(gè)NightlyBuildServlet類(lèi)來(lái)響應(yīng)構(gòu)建信息關(guān)心者的訪(fǎng)問(wèn)需求,它是velocity中VelocityServlet類(lèi)的子類(lèi),因此NightlyBuildServlet任MVC模式中的控制角色。
在下面的"顯示系統(tǒng)MVC視圖"中可以看出,業(yè)務(wù)對(duì)象NightlyBuild對(duì)象以nightlyBuild的名字保存在模型context中,當(dāng)控制NightlybuildServlet用模版文件buildinfo_list.vm來(lái)滿(mǎn)足構(gòu)建信息關(guān)心者瀏覽構(gòu)建信息目錄的要求時(shí),buildinfo_list.vm文件格式化保存在context中的NightlyBuild對(duì)象的信息并形成視圖供瀏覽者瀏覽。
人機(jī)界面設(shè)計(jì)就是設(shè)計(jì)瀏覽者和系統(tǒng)交互的方式,以及系統(tǒng)展示信息的布局。構(gòu)建信息顯示系統(tǒng)是個(gè)典型的web應(yīng)用,瀏覽者首先在其瀏覽器中輸入構(gòu)建信息顯示系統(tǒng)的URL,系統(tǒng)就會(huì)把構(gòu)建信息目錄顯示給瀏覽者,瀏覽者通過(guò)點(diǎn)擊構(gòu)建信息目錄頁(yè)面上的超鏈就可以訪(fǎng)問(wèn)相應(yīng)的信息。我們前面說(shuō)過(guò)每個(gè)構(gòu)建信息會(huì)在瀏覽器中自己顯示自己,所以現(xiàn)在的任務(wù)就是設(shè)計(jì)構(gòu)建信息目錄顯示頁(yè)面。
下面的圖就是構(gòu)建信息目錄顯示頁(yè)面,其中${key}表示某個(gè)項(xiàng)目的名字,${nightly_build_tag}為某個(gè)項(xiàng)目某次構(gòu)建的標(biāo)簽。
管理日志超鏈的url為${logTopDir}/
構(gòu)建日志超鏈的url為${projectLogTopDir}/${nightly_build_tag}.txt
度量信息超鏈的url為${logTopDir}/${nightly_build_tag}
測(cè)試記錄超鏈的url為${testTopDir}/${nightly_build_tag}
覆蓋率超鏈的url為${testCoverTopDir}/${nightly_build_tag}
產(chǎn)品超鏈的url為${distTopDir}/${nightly_build_tag}
每晚構(gòu)建平臺(tái) | ||||||
${key} | ||||||
構(gòu)建序列 | 構(gòu)建日志 | 構(gòu)建日志 | 度量信息 | 測(cè)試記錄 | 覆蓋率 | 產(chǎn)品 |
1 | 管理日志 | 構(gòu)建日志 | 度量信息 | 測(cè)試記錄 | 覆蓋率 | 產(chǎn)品 |
2 | ||||||
... | ||||||
${key} | ||||||
構(gòu)建序列 | 構(gòu)建日志 | 構(gòu)建日志 | 度量信息 | 測(cè)試記錄 | 覆蓋率 | 產(chǎn)品 |
1 | 管理日志 | 構(gòu)建日志 | 度量信息 | 測(cè)試記錄 | 覆蓋率 | 產(chǎn)品 |
2 | ||||||
... |
存儲(chǔ)設(shè)計(jì)主要是指數(shù)據(jù)庫(kù)的設(shè)計(jì)或者文件格式的設(shè)計(jì),構(gòu)建信息顯示系統(tǒng)使用文件系統(tǒng)作為存儲(chǔ)目標(biāo)。構(gòu)建信息顯示系統(tǒng)的存儲(chǔ)設(shè)計(jì)主要工作是定義分析模型中BuildInfoDir類(lèi)也就是設(shè)計(jì)模型中BuildInfoDir接口的變量的值和格式。
1.3.1 BuildInfoDir接口
類(lèi)名/接口名 | 構(gòu)建信息存放位置接口 | 類(lèi)英文名 | BuildInfoDir |
成員變量 | |||
變量名 | 變量說(shuō)明 | 缺省值或值 | |
webAppDir | 是應(yīng)用服務(wù)器存放web應(yīng)用程序的根目錄,這個(gè)值根據(jù)系統(tǒng)安裝而定。 | ||
nightlyWebAppName | 構(gòu)建信息顯示系統(tǒng)web應(yīng)用程序的名字,同變量webAppDir一起構(gòu)成了構(gòu)建信息顯示系統(tǒng)應(yīng)用程序的根目錄。 | "nightlybuild" | |
nightly_Build_Tags | 保存所有的構(gòu)建標(biāo)簽,是構(gòu)建信息顯示系統(tǒng)web應(yīng)用程序根目錄下的一個(gè)文件的名字。 | "nightBuildLog" | |
logTopDir | 相對(duì)于顯示系統(tǒng)應(yīng)用程序的根目錄下的一個(gè)目錄名,保存構(gòu)建管理服務(wù)運(yùn)行的日志的目錄 | "adminLogs" | |
statCVSTopDir | 相對(duì)于顯示系統(tǒng)應(yīng)用程序的根目錄下的一個(gè)目錄名,保存了所有應(yīng)用項(xiàng)目的項(xiàng)目度量結(jié)果的頂層路徑 | "statCVSes" | |
projectLogTopDir | 相對(duì)于顯示系統(tǒng)應(yīng)用程序的根目錄下的一個(gè)目錄名,保存了所有應(yīng)用項(xiàng)目構(gòu)建服務(wù)實(shí)例運(yùn)行日志的頂層路徑 | "projectLogs" | |
testCoverTopDir | 相對(duì)于顯示系統(tǒng)應(yīng)用程序的根目錄下的一個(gè)目錄名,保存了所有應(yīng)用項(xiàng)目的測(cè)試覆蓋率計(jì)算結(jié)果的頂層路徑 | "testCovers" | |
distTopDir | 保存了所有應(yīng)用項(xiàng)目的發(fā)布版的頂層路徑,值和變量webAppDir相同 | ||
testTopDir | 相對(duì)于顯示系統(tǒng)應(yīng)用程序的根目錄下的一個(gè)目錄名,保存了所有應(yīng)用項(xiàng)目的測(cè)試結(jié)果的頂層路徑 | "tests" |
下面是nightly_Build_Tags所指的文件格式:
每行包括一個(gè)項(xiàng)目構(gòu)建標(biāo)簽,構(gòu)建標(biāo)簽的格式為項(xiàng)目名-yyyymmdd_HHMMSS的格式
比如可能的文件內(nèi)容如下:
nightlybuild-20030312_080100 cover-20030312_080100 nightlybuild -20030312_080100 cover-20030313_080100 |
這個(gè)文件內(nèi)容表示:兩個(gè)項(xiàng)目分別為nightlybuild和cover,他們?cè)?003年3月12日和13日的早上8點(diǎn)1分得到了構(gòu)建。
系統(tǒng)接口設(shè)計(jì)主要描述被設(shè)計(jì)的系統(tǒng)與外界系統(tǒng)特別是操作系統(tǒng)的接口的設(shè)計(jì)和描述。構(gòu)建信息顯示系統(tǒng)和構(gòu)建系統(tǒng)的接口就是存儲(chǔ)設(shè)計(jì)中的BuildInfoDir接口,和操作系統(tǒng)無(wú)接口。
![]() ![]() |
![]()
|
考慮到ant腳本的局限性,我們對(duì)構(gòu)建系統(tǒng)分析模型作如下調(diào)整:
因?yàn)镺SScheduler是個(gè)操作系統(tǒng)定時(shí)服務(wù)的腳本或配置,所以無(wú)法繼承BuildInfoLog類(lèi),我們?cè)黾右粋€(gè)LogAdmin類(lèi)用來(lái)啟動(dòng)和記錄BuildAdmin運(yùn)行的日志。為什么我們必須在OSScheduler和BuildAdmin中插入一個(gè)LogAdmin類(lèi)?這是因?yàn)閍nt腳本無(wú)法自己記錄自己的輸出,必須靠調(diào)用者,我們必須使用<ant>任務(wù)來(lái)運(yùn)行和記錄另外一個(gè)類(lèi)的輸出。同樣BuildAdmin會(huì)用<ant>任務(wù)來(lái)運(yùn)行和記錄ProjectBuild的輸出日志。
LogAdmin用<ant>任務(wù)實(shí)現(xiàn)對(duì)BuildAdmin的單項(xiàng)聯(lián)系;至于BuildAdmin和ProjectBuild之間的一對(duì)多的關(guān)系可用多個(gè)<ant>任務(wù)實(shí)現(xiàn)。由于要運(yùn)行,所以類(lèi)LogAdmin,BuildAdmin和ProjectBuild都必須是可運(yùn)行的(runnable)。
一個(gè)項(xiàng)目的構(gòu)建應(yīng)該可以分成固定不變的腳本和項(xiàng)目相關(guān)腳本兩部分,這個(gè)需求可以和設(shè)計(jì)模式中的模版方法模式的目的相吻合。
類(lèi)ProjectBuild的test_project方法要完成下列步驟:
- 準(zhǔn)備測(cè)試環(huán)境,包括為測(cè)試目的而進(jìn)行的編譯、打包、安裝,其中編譯和打包與具體項(xiàng)目有關(guān);
- 測(cè)試,這個(gè)任務(wù)與具體項(xiàng)目有關(guān);
- 生成測(cè)試報(bào)告,它與項(xiàng)目無(wú)關(guān);
- 生成覆蓋率信息,它也與項(xiàng)目無(wú)關(guān)。
類(lèi)ProjectBuild的dist_project方法要完成的步驟有編譯、打包和安裝,安裝與項(xiàng)目無(wú)關(guān)。這樣運(yùn)用了模版方法模式之后,構(gòu)建系統(tǒng)的設(shè)計(jì)類(lèi)圖就變成了構(gòu)建系統(tǒng)設(shè)計(jì)類(lèi)圖二所示。
下面的序列圖顯示了模版方法調(diào)用抽象方法的序列:
步驟1.1針對(duì)每個(gè)被管理的應(yīng)用項(xiàng)目調(diào)用步驟1.1.1;
步驟1.1.1.4.1 test_project是個(gè)模版方法,compile(),compiletestcases(),test_war(),test()是子類(lèi)ProjectBuild要實(shí)現(xiàn)的方法;
步驟1.1.1.4.2 dist_project是個(gè)模版方法,compile(),dist_war()是子類(lèi)ProjectBuild要實(shí)現(xiàn)的方法。
不需要。
見(jiàn)構(gòu)建信息顯示系統(tǒng)。
構(gòu)建系統(tǒng)和操作系統(tǒng)的接口在OSScheduler。在Linux下可以實(shí)現(xiàn)成一個(gè)調(diào)用ant LogAdmin的shell 可執(zhí)行文件,并配置crond每晚某個(gè)時(shí)刻執(zhí)行這個(gè)可執(zhí)行文件。
![]() ![]() |
![]()
|
在這節(jié)中充分利用本文章系列中篇中所有的技術(shù),并顯示了部分源代碼。
在實(shí)現(xiàn)時(shí),第一個(gè)要考慮的就是類(lèi)如何與源文件對(duì)應(yīng),這些源文件又是如何組織的,表示這些信息的圖表稱(chēng)為部署圖。圖表的格式不一定要很標(biāo)準(zhǔn),這要能表達(dá)意思就行。
從每晚構(gòu)建部署圖可以看出,這些類(lèi)被分別組織在兩個(gè)不同的目錄下:work_nightly和work_nightlybuild。work_nightly目錄存放的是跨項(xiàng)目的構(gòu)建信息,稱(chēng)為每晚構(gòu)建平臺(tái)構(gòu)建系統(tǒng)中的構(gòu)建管理子系統(tǒng),除了包括實(shí)現(xiàn)BuildAdmin,BuildInfoLog和LogAdmin的源代碼外,還有應(yīng)用服務(wù)器目錄Tomcat412,編譯和測(cè)試程序時(shí)常用jar類(lèi)庫(kù)目錄lib,測(cè)試b/s架構(gòu)的程序的配置信息目錄cactusconf和生成ant配置文件依賴(lài)性圖解的vizant目錄。work_nightlybuild是一個(gè)支持每晚構(gòu)建項(xiàng)目的目錄,在這里是構(gòu)建信息顯示系統(tǒng)項(xiàng)目,這個(gè)目錄包括了類(lèi)ProjectBuild和ProjectBuildAbstract的源代碼,同時(shí)還具有一個(gè)web項(xiàng)目該有的的文件和目錄。值得指出的是為了不至于有多份實(shí)現(xiàn)BuildInfoLog類(lèi)的源代碼,在具體項(xiàng)目中包括了一個(gè)指向構(gòu)建管理子系統(tǒng)頂級(jí)目錄的文件稱(chēng)為指針文件。類(lèi)ProjectBuild、ProjectBuildAbstract和指針文件組成了構(gòu)建系統(tǒng)中的項(xiàng)目構(gòu)建子系統(tǒng)。
文件BuildInfoLog.properties實(shí)現(xiàn)存儲(chǔ)接口,內(nèi)容如下:
###=================================### logTopDir= "adminLogs" statCVSTopDir="statCVSes" testTopDir="tests" distTopDir="/usr/tomcat412/webapp/" nightly_Build_Tags=" nightBuildLog" projectLogTopDir="projectLogs" testCoverTopDir="testCovers" webAppDir="/usr/tomcat412/webapp/" nightlyWebAppName="nightlybuild" ###=============================### |
這個(gè)文件實(shí)現(xiàn)的目錄結(jié)構(gòu)如下,這個(gè)目錄結(jié)構(gòu)在構(gòu)建信息顯示系統(tǒng)的web應(yīng)用程序中創(chuàng)建。
<project name="BuildAdminSystem" default="execute" basedir="." > <!-- 定義方法 --> <target name="init" > <tstamp> <format property="DSTAMP" pattern="yyyyMMdd_HHmmss" locale="cn"/> </tstamp> <!-- 繼承BuildInfoLog --> <property file="BuildInfoLog.properties"/> </target> <target name="execute" depends="init" > <echo> ########################### LogAdmin.xml:target execute the BuildAdmin.xml's output will put into file: ${webAppDir}${nightlyWebAppName}/${logTopDir}/${logNamePrefix}-${DSTAMP}.txt ########################### </echo> <!-- 跨類(lèi)的方法調(diào)用ant task --> <ant antfile="BuildAdmin.xml" inheritAll="false" output="${webAppDir}${nightlyWebAppName}/${logTopDir}/${logNamePrefix}-${DSTAMP}.txt" > <!-- 傳遞參數(shù) --> <property name="tagTime" value="${DSTAMP}"/> </ant> </target> </project> |
<project name="BuildAdminSystem" default="execute" basedir="." > <!-- 定義方法 --> <target name="init" > <!-- 繼承BuildInfoLog --> <property file="BuildInfoLog.properties"/> <property name="cvsroot" value=":pserver:anonymous@10.1.36.135:/data/src" /> <property name="buildtemp.dir" value="../buildtemp" /> <property name="lib.dir" location="lib" /> <property name="AJPPORT" value="9887" /> <property name="HTTPPORT" value="9888" /> <property name="HTTPSPORT" value="9889" /> <property name="statcvs.jar" location="${lib.dir}/statcvs-0.1.3.jar"/> <taskdef name="statcvs" classname="net.sf.statcvs.ant.StatCvsTask"> <classpath> <pathelement path="${statcvs.jar}"/> </classpath> </taskdef> </target> <target name="execute" > <!-- you can add another module here --> <antcall target="build_One_Module"> <param name="module" value="nightlybuild"/> </antcall> <!-- you can add another module here --> <!-- <antcall target="build_One_Module"> <param name="module" value="testcvs"/> </antcall> --> </target> <!-- 方法調(diào)用之一depends --> <target name="build_One_Module" depends="init"> <!-- 方法調(diào)用之一antcall --> <antcall target="cvs_Check_Out"> <!-- the CVS module name which includes a build.xml itself --> <!-- 傳遞參數(shù) --> <param name="module" value="${module}"/> </antcall> <!-- 生成構(gòu)建標(biāo)簽 --> <property name="nightly_Build_Tag" value="${module}-${tagTime}" /> <echo> ############################################### BuildAdmin.xml:target build_One_Module: nightly_Build_Tag is set to ${nightly_Build_Tag} ############################################### </echo> <echo> ############################################### BuildAdmin.xml:target build_One_Module: append ${nightly_Build_Tag} to file ${webAppDir}/${nightlyWebAppName}/${nightly_Build_Tags} ############################################### </echo> <!-- 記錄構(gòu)建標(biāo)簽 --> <echo append="true" file="${webAppDir}/${nightlyWebAppName}/${nightly_Build_Tags}" > ${nightly_Build_Tag} </echo> <!-- 生成項(xiàng)目度量信息 --> <antcall target="statCVS"> <param name="module" value="${module}"/> <param name="build_tag" value="${nightly_Build_Tag}" /> </antcall> <echo> ############################################### BuildAdmin.xml:target build_One_Module: the software is checked out in ${buildtemp.dir}/tmp/${module} let's begin to build it and the output will be put into ${webAppDir}${nightlyWebAppName}/${projectLogTopDir}/${nightly_Build_Tag}.txt ############################################### </echo> <ant dir="${buildtemp.dir}/tmp/${module}" target="execute" inheritAll="false" output="${webAppDir}${nightlyWebAppName}/${projectLogTopDir}/${nightly_Build_Tag}.txt" > <property name="nightly_Build_Tag" value="${nightly_Build_Tag}" /> <property name="AJPPORT" value="${AJPPORT}" /> <property name="HTTPPORT" value="${HTTPPORT}" /> <property name="HTTPSPORT" value="${HTTPSPORT}" /> </ant> <!-- <ant dir="../work_nightlybuild" antfile="ProjectBuild.xml" target="execute" inheritAll="false" output="${webAppDir}${nightlyWebAppName}/${projectLogTopDir}/${nightly_Build_Tag}.txt" > <property name="nightly_Build_Tag" value="${nightly_Build_Tag}" /> <property name="AJPPORT" value="${AJPPORT}" /> <property name="HTTPPORT" value="${HTTPPORT}" /> <property name="HTTPSPORT" value="${HTTPSPORT}" /> </ant> --> </target> <target name="cvs_Check_Out" > <!-- cvs checkout --> <echo> ############################################### BuildAdmin.xml:target cvs_Check_Out: the software will be checked out in ${buildtemp.dir}/tmp/${module} ############################################### </echo> <delete dir="${buildtemp.dir}/tmp/${module}" /> <mkdir dir="${buildtemp.dir}/tmp"/> <cvs cvsRoot="${cvsroot}" package="${module}" dest="${buildtemp.dir}/tmp" /> <cvs dest="${buildtemp.dir}/tmp/${module}" command="log" output="${buildtemp.dir}/tmp/${module}/cvs.log"/> </target> <!-- 生成項(xiàng)目度量信息 --> <target name="statCVS" depends="init" > <echo> ############################################### BuildAdmin.xml:target statCVS: the software will be measureed in ${webAppDir}${nightlyWebAppName}/${statCVSTopDir}/${build_tag} ############################################### </echo> <delete dir="${webAppDir}${nightlyWebAppName}/${statCVSTopDir}/${build_tag}" /> <mkdir dir="${webAppDir}${nightlyWebAppName}/${statCVSTopDir}/${build_tag}"/> <statcvs projectName="${module}" projectDirectory="${buildtemp.dir}/tmp/${module}" cvsLogFile="${buildtemp.dir}/tmp/${location}/cvs.log" outputDirectory="${webAppDir}${nightlyWebAppName}/${statCVSTopDir}/${build_tag}" /> </target> </project> |
顯示界面模版在velocity模版文件buildinfo_list.vm中實(shí)現(xiàn)。這個(gè)文件引用了nightlyfrag.vm文件,另外定義宏headerCell的文在為GlobalMacros.vm。
模版文件buildinfo_list.vm:
<html> <head> <meta http-equiv="content-type" content="text/html; charset=GB2312"> <title>每晚構(gòu)建</title> <link href="css/default.css" type="text/css" rel="stylesheet"> </head> <body bgcolor="#ffffff"> <center> <table border="2" cellspacing="0" cellpadding="3" bordercolor="#000000"> <tr> <td class="page-title" bordercolor="#000000" align="left" nowrap> <font size="+2"><b> 每晚構(gòu)建 </b> </font> </td> </tr> </table> <p/> <table border="1" cellspacing="0" cellpadding="3"> #parse( "nightlyfrag.vm" ) </table> </center> </html> |
模版文件nightlyfrag.vm:
#headerCell("header-center" "序號(hào)") #headerCell("header-left" "管理日志" ) #headerCell("header-left" "構(gòu)建日志" ) #headerCell("header-left" "度量信息" ) #headerCell("header-left" "測(cè)試記錄" ) #headerCell("header-left" "覆蓋率" ) #headerCell("header-left" "產(chǎn)品" ) <ul> #foreach( $key in $BuildList.keySet() ) <tr> <td colspan="10" class="title">$key</td> </tr> #set($body=$BuildList.get($key)) #foreach($entry in $body) #set($tagtime=$BuildList.getTagTime($entry)) <tr> <td class="row-center">$velocityCount </td> <td class="row-left"><a href="${BuildList.LogTopDir}/${BuildList.logNamePrefix}${tagtime}.txt" >管理日志</a></td> <td class="row-left"><a href="${BuildList.projectLogTopDir}/${entry}.txt" >構(gòu)建日志</a></td> <td class="row-left"><a href="${BuildList.statCVSTopDir}/${entry}" >度量信息</a></td> <td class="row-left"><a href="${BuildList.testTopDir}/${entry}" >測(cè)試記錄</a></td> <td class="row-left"><a href="${BuildList.testCoverTopDir}/${entry}" >覆蓋率</a></td> <td class="row-left"><a href="/${entry}" >產(chǎn)品</a></td> </tr> #end #end </ul> |
宏定義文件GlobalMacros.vm:
#macro (headerCell $classStyle $body) <td class="$classStyle"> <b> $body </b> </td> #end |
![]() ![]() |
![]()
|
到此所有工作結(jié)束,我們可以享受自動(dòng)構(gòu)建帶來(lái)的效益和好處。值得提及的是保持分析、設(shè)計(jì)和實(shí)現(xiàn)文檔的一致性非常困難而且非常重要,工作要不斷地進(jìn)行反復(fù)。編寫(xiě)良好的文檔,保持優(yōu)秀的寫(xiě)作習(xí)慣需要單位和個(gè)人共同的努力。
![]() ![]() |
![]()
|
- word 文檔書(shū)寫(xiě)排版工具
- powerpoint,圖片組織繪畫(huà)工具
- visio 繪制數(shù)據(jù)流圖,ER圖等的工具
- rational rose,繪制UML圖形的工具
- windows 附件中的畫(huà)圖來(lái)截取圖片
- 操作系統(tǒng)的全屏打印功能
- 進(jìn)一步學(xué)習(xí)面向?qū)ο蟮南到y(tǒng)分析和設(shè)計(jì):《面向?qū)ο蟮南到y(tǒng)分析和設(shè)計(jì)》Ronald J. Norman
- 《實(shí)用面向?qū)ο筌浖こ探坛獭芬笕死?田金蘭 馬曉勤 譯
- 良好的用例編寫(xiě)風(fēng)格可以從這里獲得:《編寫(xiě)有效用例》 Alistair Cockburm
- 進(jìn)一步理解cvs和nightlybuild技術(shù)的相關(guān)背景資料:《cvs和nightlybuild技術(shù)》 楊錦方
- cvs源代碼版本系統(tǒng)在:http://www.cvshome.org
- statcvs 項(xiàng)目工作量分析工具在:http://statcvs.sf.net/
- clover測(cè)試覆蓋率分析工具在: http://www.cortexebusiness.com.au/
- ant構(gòu)建工具在:http://ant.apache.org
- junit單元測(cè)試工具在:http://www.junit.org
- apache web程序測(cè)試工具在:http://jakarta.apache.org/cactus/
![]() |
||
|
![]() |
龔永生,你可以通過(guò) gongys@legend.com與他聯(lián)系。 |