記得在去年在
Web應用程式自動部署作業-使用 Tomcat 及 Ant 一文中介紹過如何透過 Ant 完成 Tomcat 上的 Web 應用程式自動部署作業。過了一年後相關的設定也有了修正,且目前因為計畫將部署程序移轉到
Maven 上,所以先在此將作法登錄下來,算是一個歷程的完結。
第二版的部署作業最大的改變就是將原本那個超大的 build.xml 檔予以解散,並將系統的、專案專屬的設定項目都切割開來,目的當然在簡化每一個設定檔的大小。以下從目錄結構開始介紹起:
網頁應用系統標準目錄結構
現行所有的網頁應用程式目錄結構係依據
Application Developer's Guide: Deployment (也可參考
Web Application Archives ) 文中所描述基本結構,配合後續單一檔案維護要求而設定。目錄結構如下:
網頁應用系統標準目錄結構
WEB-ROOT 網站根目錄,目錄中會包含 Ant 建置主檔
├─build 建置過程中之暫存目錄
├─build.ant Ant 建置主檔附屬支援目錄
├─conf 網站參考設定檔
├─css 應用系統目錄:依各專案不同而有增減
├─distribute 建置完成輸出目錄
├─images 應用系統目錄:依各專案不同而有增減
├─src 原始程式檔:主要為該專案之 servlet、javabean 等原始檔存放目錄
├─war.backup 遠端程式備份目錄:儲存遠端伺服器上原本運作中之網頁應用系統壓縮包
└─WEB-INF Web Application Archives 標準目錄
└─lib 該應用系統有關之類別庫一律置放於此目錄下
ANT 部署說明
所有的部署動作由 WEB-ROOT 下的 build.xml 開始,透過 build.ant/ 目錄下的輔助設定及類似 Ant 副程式的機制,完成自動化部署作業。而在系統建置過程中將參考 conf/ 目錄下之內容以建構出完整的網頁應用系統部署套件。
如何使用 build.xml 部署建置檔
常在取得一份 .war 檔後,先使用 7-zip 等檔案解壓縮工具將檔案解壓到專案目錄中,此例為取得一個 QuerySalary.war 檔案。附圖是這個壓縮檔案的內容。(註:war 檔的本質是 Zip 格式)
解壓縮 .war 檔後,即可從命令列中檢查該專案所附的 build.xml 設定檔提供那些功能,請切換到專案所在目錄中並鍵入以下指令:
檢查 build.xml 提供功能
ant -projecthelp
以下是 QuerySalary 專案的 ant -projecthelp
另一個專案的 ant -projecthelp
通常來說要部署一個專案時只要鍵入以下指令即可!當然請先確定該建置檔是否提供相對應之部署功能。
各階段部署應用系統的方式
# 部署至正式主機
ant production
# 部署至開發用主機
ant dev
# 部署至使用者測試主機
ant uat
下圖為某次部署作業之畫面擷圖:
每個專案總會有些差異,這類的差異由 build.ant/ 目錄下的檔案進行調整,稍候介紹。
build.xml 內容說明
以下為 build.xml 之基本設定:
build.xml
<?xml version="1.0" encoding="Big5"?>
<!-- 專案名稱,識別用 -->
<project name="Forms">
<!-- 初始設定區段 -->
<target description="初始設定作業" name="init">
<!-- 設定系統環境變數之識別碼 -->
<property environment="env"/>
<!-- 載入基本屬性設定檔 -->
<property file="${basedir}/build.ant/build.properties"/>
<!-- 載入資料庫屬性設定檔 -->
<property file="${basedir}/build.ant/database.properties"/>
<!-- 載入部署作業屬性設定檔 -->
<property file="${basedir}/build.ant/deploy.properties"/>
<!-- 開始建置時間 -->
<tstamp>
<format pattern="yyyy-MM-dd HH:mm:ss" property="build.time"/>
</tstamp>
<!-- 建立 CLASSPATH 設定 -->
<path id="build.classpath">
<fileset dir="${lib.path}">
<include name="**/*.jar"/>
</fileset>
</path>
<!-- Configure the custom Ant tasks for the Tomcat 5.0 Manager application -->
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask"/>
<taskdef name="list" classname="org.apache.catalina.ant.ListTask"/>
<taskdef name="reload" classname="org.apache.catalina.ant.ReloadTask"/>
<taskdef name="resources" classname="org.apache.catalina.ant.ResourcesTask"/>
<taskdef name="roles" classname="org.apache.catalina.ant.RolesTask"/>
<taskdef name="start" classname="org.apache.catalina.ant.StartTask"/>
<taskdef name="stop" classname="org.apache.catalina.ant.StopTask"/>
<taskdef name="undeploy" classname="org.apache.catalina.ant.UndeployTask"/>
</target>
<!-- 顯示 ant 目前的 properties 設定值 -->
<target depends="init" name="echoProperties"
description="顯示 ant 當前 properties 設定值" >
<echoproperties/>
</target>
<target depends="init" description="清除輸出目錄" name="clean">
<!-- 刪掉打包過程暫存目錄 -->
<delete dir="${build.path}" quiet="true"/>
<!-- 刪掉最終輸出檔案目錄 -->
<delete dir="${target.path}" quiet="true"/>
<!-- 刪掉所有名為 .bak 之檔案 -->
<delete quiet="true">
<fileset dir="${basedir}">
<include name="**/*.bak"/>
</fileset>
</delete>
</target>
<!-- 建立系統所需環境(目錄及檔案) -->
<target depends="clean" name="prepare-system">
<ant antfile="${basedir}/build.ant/system.xml" inheritrefs="true"/>
</target>
<!-- 建立專案所需環境(目錄及檔案) -->
<target depends="prepare-system" name="prepare-project">
<ant antfile="${basedir}/build.ant/project.xml" inheritrefs="true"/>
</target>
<!-- 測試是否有 struts.xml ant 設定檔,目前未使用 -->
<target depends="prepare-project" name="test-struts">
<available property="struts.xml.present"
file="${basedir}/build.ant/struts.xml" type="file" />
</target>
<!-- 建立專案所需環境(目錄及檔案) -->
<target name="prepare-struts" depends="test-struts"
if="struts.xml.present" >
<ant antfile="${basedir}/build.ant/struts.xml" inheritrefs="true" />
</target>
<!-- 編譯 java 原始程式(包含 servlet 及一般類別庫)-->
<target name="compile" depends="prepare-struts"
description="編譯 java 原始程式" >
<javac deprecation="${deprecation}" destdir="${classes.path}"
srcdir="${java.source.path}" debug="on" debuglevel="lines">
<classpath refid="build.classpath"/>
</javac>
</target>
<!-- 建立 javadoc 文件,僅限於已設定 JAVADOC 環境變數時才會啟動 -->
<target if="env.JAVADOC" name="javadoc" depends="compile"
description="建立 JavaDoc 文件,只在已設定環境變數 JAVADOC 時執行此作業" >
<ant antfile="${basedir}/build.ant/javadoc.xml" inheritrefs="true" />
</target>
<!-- 開發測試環境中需要準備的動作 -->
<target depends="javadoc" name="dev-prepare">
<!-- 開發環境中所需的 log4j 相關設定資料 -->
<property name="log4j.properties.path" value="${dev.deploy.webroot}"/>
<property name="logfile.path" value="${dev.deploy.webroot}"/>
<!-- 呼叫 genConfig task 以便建立 web.xml 檔案 -->
<antcall target="genConfig" inheritAll="true"/>
</target>
<!-- 使用者驗收測試環境中需要準備的動作 -->
<target depends="compile" name="uat-prepare">
<!-- 使用者驗收測試環境中所需的 log4j 相關設定資料 -->
<property name="log4j.properties.path" value="${uat.deploy.webroot}"/>
<property name="logfile.path" value="${uat.deploy.webroot}"/>
<!-- 呼叫 genConfig task 以便建立 web.xml 檔案 -->
<antcall target="genConfig" inheritAll="true"/>
</target>
<!-- 正式環境中所需要先行準備的動作 -->
<target depends="compile" name="production-prepare">
<!-- 開發環境中所需的 log4j 相關設定資料 -->
<property name="log4j.properties.path"
value="${production.deploy.webroot}"/>
<property name="logfile.path" value="${production.deploy.webroot}"/>
<!-- 呼叫 genConfig task 以便建立 web.xml 檔案 -->
<antcall target="genConfig" inheritAll="true"/>
</target>
<!-- 初始化 XDoclet 設定 -->
<target name="genWeb.xml">
<path id="xdoclet.classpath">
<!-- xdoclet 類別庫路徑在 build.properties 檔中自行捉取環境變數設定 -->
<fileset dir="${xdoclet.lib.path}">
<include name="*.jar"/>
</fileset>
<pathelement location="${classes.path}"/>
</path>
<taskdef name="webdoclet"
classname="xdoclet.modules.web.WebDocletTask"
classpathref="xdoclet.classpath"/>
<!-- Generate servlet and JSP Tag "stuff" -->
<webdoclet destDir="${basedir}/WEB-INF"
mergeDir="${basedir}/conf"
force="true" verbose="false">
<fileset dir="${java.source.path}">
<include name="**/*.java" />
</fileset>
<deploymentdescriptor distributable="true"
displayname="${application.name}"
description="${application.description}" >
<!-- 要特別傳給 xDoclet 用的設定變數 -->
<configParam name="application-name"
value="${application.name}"/>
<configParam name="application-description"
value="${application.description}"/>
<configParam name="log4j-init-file"
value="${log4j.properties.path}/${log4j.properties.file}"/>
</deploymentdescriptor>
</webdoclet>
</target>
<!-- 建立網頁設定檔 - web.xml -->
<target name="genConfig" depends="genWeb.xml">
<!-- 自動調整 log4j.properties 中的 log 檔檔名 -->
<replaceregexp byline="true"
file="${target.path}/${log4j.properties.file}"
match="log4j.appender.FileLog.file=default.exception.log"
replace="log4j.appender.FileLog.file=${logfile.path}/${log4j.exception.file}"/>
<!-- 調整的變數內容 -->
<echo>LogFile.Path: ${logfile.path}</echo>
<echo>LogFile.Name: ${log4j.properties.file}</echo>
<echo>${target.path}/${log4j.exception.file}</echo>
<!-- 用於修改 datasource 設定之內容 -->
<!--
<replaceregexp byline="true" file="${web.xml}"
match="${DATASOURCE}" replace="${data.source}"/>
-->
</target>
<!-- 將編譯出來的 .class 予以打包 -->
<target depends="compile" name="package"
description="將編譯出的 .class 檔壓製成單一 .jar 檔,放入 web root 下的 WEB-INF/lib 目錄中">
<jar basedir="${classes.path}" jarfile="${jar.name}"/>
<delete dir="${classes.path}" quiet="true"/>
</target>
<!-- 將整個WEB ROOT目錄打包成.war部署檔,此部署檔已完整包含開發所需各原始程式。 -->
<target depends="package" name="build">
<war warfile="${war.name}" webxml="${web.xml}">
<fileset dir="${build.path}" excludes="**/*.jar"/>
<lib dir="${lib.path}"/>
</war>
</target>
<!-- DEV,通常會採用 JSP Container 的自動部署功能,因此使用 FTP 上傳方式處理 -->
<target name="dev" if="dev.deploy.server" depends="dev-prepare, build"
description="部署 DEV 測試環境" >
<echo>Sending file(s) to ${dev.deploy.server}...</echo>
<ftp binary="yes" depends="yes" password="${dev.deploy.password}"
server="${dev.deploy.server}" userid="${dev.deploy.user}">
<fileset dir="${target.path}"/>
</ftp>
</target>
<!-- UAT,通常與 DEV 同一台主機,因此也採 FTP 部署方式處理 -->
<target name="uat" if="uat.deploy.server" depends="uat-prepare, build"
description="部署 UAT 測試環境" >
<echo>Sending file(s) to ${uat.ftp.server}...</echo>
<ftp binary="yes" depends="yes" password="${uat.deploy.password}"
server="${uat.deploy.server}" userid="${uat.deploy.user}">
<fileset dir="${output.path}"/>
</ftp>
</target>
<!-- Production, -->
<!--
若使用 JRun 4 時必須採用 FTP 上傳方式,但目前多數已改用 Tomcat Application Server,
可在關閉自動部署(校能考量)後,透過 Tomcat 內建的管理功能進行部署作業!
-->
<!-- catalina-ant.jar -->
<target name="production" if="production.deploy.server.1"
depends="production-prepare, build"
description="部署 production 正式環境" >
<echo>
Deploying ${application.name}.war to ${production.deploy.server.1}…
</echo>
<!-- 移除原指定路徑上之應用系統,應用系統名稱為 deploy.properties 中所定義 -->
<undeploy url="http://${production.deploy.server.1}/manager"
username="${production.tomcat.user.1}"
password="${production.tomcat.password.1}"
path="/${application.name}"/>
<!-- 傳送檔案 ( log4j.properties 設定檔 ) -->
<echo>
Sending ${log4j.properties.file} to ${production.deploy.server.1}…
</echo>
<ftp binary="yes" depends="yes"
server="${production.deploy.server.1}"
userid="${production.deploy.user.1}"
password="${production.deploy.password.1}">
<!-- 避開 .war 檔 -->
<fileset dir="${target.path}" excludes="**/*.war"/>
</ftp>
<sleep seconds="5" />
<!-- 重新部署 -->
<deploy url="http://${production.deploy.server.1}/manager"
username="${production.tomcat.user.1}"
password="${production.tomcat.password.1}"
path="/${application.name}" war="${war.name}"/>
<tstamp>
<format pattern="yyyy-MM-dd HH:mm:ss" property="complete.time"/>
</tstamp>
<echo>Deploy complete at ${complete.time}</echo>
</target>
</project>
build.ant/ 相關配置檔案說明
build.ant/build.properties 基本參數設定
此檔為 Ant 建置過程中的標準參數設定檔,通常使用時直接保留原始設定檔即可,不需另行修改。
build.ant/database.properties 資料庫參數設定
此檔為資料庫連線有關的參數設定檔,通常是在 xdoclet 建置 web.xml 配置檔時修改相關參數時使用。目前因採用 Servlet Containner 提供的 Connection Pool 或其他原因之故並未真正使用本設定檔。
build.ant/deploy.properties 部署參數設定
此檔為專案部署有關之參數設定檔。內容及說明如下:
build.ant/deploy.properties 內容說明
# 定義應用程式名稱, 請用英文,且 .war 壓縮包將以此為名
application.name=QuerySalary
# 定義應用程式名稱, 請用英文
application.description=FITEL QuerySalary System
# log4j 記錄器設定 - 給 log4j init servlet 使用,
# 為避免不同專案混淆造成混亂,直接以專案名稱設定
log4j.properties.file=${application.name}-log4j.properties
# 此檔案會放在指定之 ${deploy.webroot}/ 下
log4j.exception.file=${application.name}-exception.log
# 正式環境之 FTP 部署設定
# 若有多台時以 .2, .3, … 方式登記,但系統的 webroot 應統一!
production.deploy.server.1=production.server
production.deploy.user.1=production.user
production.deploy.password.1=production.password
# webroot 只有一組
production.deploy.webroot=C:/JRun4/servers/EHR
# 此為 Tomcat 管理界面使用,JRun 不使用此法
production.tomcat.user.1=
production.tomcat.password.1=
# 開發人員環境 FTP 部署設定
# 無測試主機,故設定與 正式環境 相同!
dev.deploy.server=${production.deploy.server.1}
dev.deploy.user=${production.deploy.user.1}
dev.deploy.password=${production.deploy.password.1}
dev.deploy.webroot=${production.deploy.webroot}
# 此為 Tomcat 管理界面使用,JRun 不使用此法
dev.tomcat.user=${production.tomcat.user}
dev.tomcat.password=${production.tomcat.password}
# 使用者驗收環境 FTP 部署設定
# 無測試主機,故設定與 開發環境 相同!
uat.deploy.server=${dev.deploy.server}
uat.deploy.user=${dev.deploy.user}
uat.deploy.password=${dev.deploy.password}
uat.deploy.webroot=${dev.deploy.webroot}
# 此為 Tomcat 管理界面使用,JRun 不使用此法
uat.tomcat.user=${dev.tomcat.user}
uat.tomcat.password=${dev.tomcat.password}
若有兩台以上正式主機時,除了在 build.ant/deploy.properties 中設定 production.deploy.server.2 等等變數外,尚必須連帶修改 build.xml 檔內容如下: (其他 Application Server 亦請比照辦理!)
build.xml
<!-- Production, JRun 4 -->
<target depends="production-prepare, build"
description="部署 Production 正式環境"
name="production" if="production.deploy.server.1">
<echo>Sending files to ${production.deploy.server.1}...</echo>
<ftp binary="yes" depends="yes"
server="${production.deploy.server.1}"
userid="${production.deploy.user.1}"
password="${production.deploy.password.1}">
<fileset dir="${distribute.path}"/>
</ftp>
<!-- 若有兩台以上主機時,移除以下區段之註解並修改主機編號即可! -->
<!--
<echo>Sending files to ${production.deploy.server.2}...</echo>
<ftp binary="yes" depends="yes"
server="${production.deploy.server.2}"
userid="${production.deploy.user.2}"
password="${production.deploy.password.2}">
<fileset dir="${distribute.path}"/>
</ftp>
-->
<tstamp>
<format pattern="yyyy-MM-dd HH:mm:ss" property="complete.time"/>
</tstamp>
<echo>Deploy complete at ${complete.time}</echo>
</target>
build.ant/system.xml 系統基礎作業部署設定檔
此檔受 build.xml 呼叫,用於建置網頁應用程式之基本目錄結構。整個網頁應用程式之暫存目錄會放在 WEB-ROOT/build/ (在 build.ant/build.properties 內設定)下,而且通常不做任何修改。
build.ant/project.xml 專案作業部署設定檔
此檔受 build.xml 呼叫,用於配置專案所需目錄或檔案之用。必須視專案之目錄需求進行對應之調整。
build.ant/project.xml
<?xml version="1.0" encoding="Big5"?>
<project name="project.prepare" default="project.all">
<target description="建立輸出所需相關目錄" name="project.mkdir">
<!-- 有額外的目錄要納進專案時,請在此處增加之 -->
<!-- 注意:所有要打包的目錄必須先行建立在 ${build.path} 指定的目錄下 -->
<mkdir dir="${build.path}/images"/>
<mkdir dir="${build.path}/css"/>
</target>
<target description="複製系統檔案" depends="project.mkdir"
name="project.copy">
<!-- 若有額外的檔案要納進專案時,請在此處增加之 -->
<!-- 注意:所有要打包的檔案/目錄必須複製到 ${build.path} 指定的目錄中 -->
<copy todir="${build.path}">
<fileset dir="${basedir}" excludes="**/*.bak" includes="**/*.jsp"/>
<fileset dir="${basedir}" excludes="**/*.bak" includes="**/*.html"/>
<fileset dir="${basedir}" includes="**/*.xml"
excludes="**/build.xml,**/web.xml,**/build.ant/**,**/build/**" />
</copy>
<copy todir="${build.path}/images">
<fileset dir="${basedir}/images"/>
</copy>
<copy todir="${build.path}/css">
<fileset dir="${basedir}/css"/>
</copy>
</target>
<target description="執行所有動作" depends="project.copy" name="project.all" />
</project>
build.ant/javadoc.xml javadoc 文件建置設定檔
此檔受 build.xml 呼叫,用於建置 src/ 下所有 java 原始碼之 javadoc 文字檔。必須先設定一個環境變數 JAVADOC 時,才能在建置過程中觸發此項作業。
build.ant/javadoc.xml
<?xml version="1.0" encoding="Big5"?>
<project name="javadoc" default="javadoc">
<target name="javadoc" if="env.JAVADOC"
description="建立 JavaDoc 文件,只在已設定環境變數 JAVADOC 時執行此作業">
<mkdir dir="${javadoc.path}"/>
<!-- 必須設定欲產生那一個 package 下的文件,如紅色字所標示的內容 -->
<javadoc author="true"
bottom="Copyright by FITEL. Co., 2005"
classpath="${classes.path}"
classpathref="build.classpath"
description="請視需要調整 packagenames 屬性之內容"
destdir="${javadoc.path}"
doctitle="○○企業 ${ant.project.name} 專案類別庫說明文件"
header="<b>FITEL ${ant.project.name} JavaDcos </b>"
packagenames="tw.net.fitel.ehr"
protected="true" sourcepath="${java.source.path}"
use="true"
version="true"
windowtitle="○○企業 ${ant.project.name} 專案類別庫說明文件">
<link href="http://java.sun.com/j2se/1.4.2/docs/api/index.html"/>
<link href="http://java.sun.com/j2ee/1.4/docs/api/index.html"/>
</javadoc>
</target>
</project>
以下是執行情形:
未設定 JAVADOC 環境變數時:
已設定 JAVADOC 環境變數時:
conf/ 目錄說明
conf/commons-logging.properties
conf/ejb-resourcerefs.xml
conf/error-pages.xml
conf/log4j.properties
conf/mime-mappings.xml
conf/servlets.xml
conf/web-settings.xml
conf/welcomefiles.xml