程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> WebSphere >> 基於Agile模式的WebSphere Commerce產品的自動化構建與部署框架(2)

基於Agile模式的WebSphere Commerce產品的自動化構建與部署框架(2)

編輯:WebSphere

使用 Rational Application Developer 實現增量代碼的構建

自動化增量代碼構建流程

自動化構建流程分為多個環節,各環節互為前提條件。本文所介紹的構建 框架將構建過程中的每個環節封裝為 Ant 腳本的子任務,通過 Build 主任務來進行調度。關於 Build 主任 務以及構建腳本框架的創建已經在本系列的第 1 部分中進行了介紹,本文主要對代碼的構建流程進行詳細介 紹。讀者可以參考本文的代碼示例,完成代碼中剩余部分 , 從而實現完整的構建框架。在編寫構建腳本時, 讀者可能需要自定義一些 Ant 標簽,這些自定義標簽的 Java 代碼可統一打包成 jar 文件,並存放於 <Build Script>/lib 目錄下,在 build.xml 文件中通過 <taskdef> 標簽來聲明自定義標簽。( 本文需要讀者在具有一定的 Ant 腳本知識和 Java 編寫經驗的基礎上進行閱讀。)

圖 1. 自動化構建 流程

提取增量代碼

對於增量代碼的構建,第一步就是將新增加的代碼從代碼庫中抽取出來,目前 市面上通用的代碼庫管理工具(SCM)有很多。增量代碼的構建需要代碼管理工具有很好的代碼版本控制的功 能,並且能夠提供命令行的運行方式,這樣便於整個構建流程的自動化執行,當然這一步驟也可以由手工完成 。在構建框架中讀者需要將符合條件的代碼從代碼庫中抽取出來並存放在制定目錄下。

在本文所介紹 的構建框架中,增量代碼存儲在 extract 目錄下。為了便於區別不同版本的增量代碼,可使用時間戳或者 build 標簽進行標記,時間戳或者 build 標簽可作為參數在啟動構建過程時以參數的形式輸入。讀者需要在 構建腳本中定義相應的變量,從而實現輸入變量的讀取。例如清單 1 中 Ant 命令就是將 build label 作為 參數輸入到 Ant 腳本中。

清單 1. 啟動 Ant 腳本時輸入參數

<Build Scirpt>/ant – buildfile build.xml – Dbuildlabel buildlabel

 

對於代碼抽取的條件,讀者可以配置在 build.properties 文件中 , 下面只給出偽代碼示例。讀者可參考清單 2 完成該部分的腳本,並將其定義為 子任務,並添加到主任務中去。

清單 2. 代碼抽取腳本

<target="commone.extract_files" /> 
   <!-- 
    1. 從 build.properties 文件中獲取增量代碼的版本信息(該信息主要適用於多版本開發的產品)
     2. 從 build.propetties 文件中獲取增量代碼所關聯的最小單位信息(如 defect 號 或者 task 號)
           注:任何代碼文件導入到代碼庫時,都需要對應一個任務號或者是 defect 號。這樣代碼管理工

具可以對不同時 
           間導入代碼庫的文件進行區分並管理。
     3. 從 build.properties 文件中讀取 build label 或者 從輸入的參數中讀取。
     4. 判斷是否存在同樣 build lable 的目錄,如果存在則刪除。如果不存在則新建。
     5. 連接 SCM 管理工具,啟動命令行執行代碼抽取
    - -> 
</ target >

代碼在代碼庫中的存放路徑可能各不相同。在本文中需要讀者將抽取出來的代碼 按照工程中的相對路徑存放。關於代碼管理的具體內容不是本文介紹的重點,因此讀者需要咨詢產品的配置管 理團隊完成該部分的內容,並整合到主任務中去。

建立工程的描述文件以及動態生成 Ant 腳本

工程描述文件用於描述抽取出來的增量代碼所對應的工程信息。用於之後該工程的備份,還原和編譯 。由於每次增量代碼所在的工程是不確定的,無法預先編寫好相關的內容。因此該部分腳本每次都需要動態生 成。讀者可以通過定義 Ant 標簽的方式實現該功能 , 並整合到構建腳本中。例如清單 3 中 "generateModifiedProjectsList" 為自定義 Ant 標簽,通過輸入增量代碼所在路徑、文件輸出路 徑以及用於識別工程的全工程描述文件路徑,最終生成出描述存在增量代碼的工程的 xml 文件。該部分代碼 比較簡單,讀者可自行完成 "generateModifiedProjectsList" 標簽的 Java Code 的代碼編寫, 並將其打包成 Jar 文件並通過 classpath 的方式在主任務執行前對其進行加載。

清單 3. 工程描述 文件生成代碼

<generateModifiedProjectsList 
projectsDescription="${build.data.path}/${filetype.xml}/${ProjectDescriptionFile}"
projectsDirectory="${source.src.project.path}"
outputDirectory="${build.generated.path}/${filetype.xml}"
outputFileName="${modifiedProjectsList.xml}"/>

清單 3 中代碼生成的工程描述文件可定義為 ModifiedProjects.xml,並保存在 <BuildScript>/generated/xml 目錄下,該文件的內容如清單 4 所示。

清單 4. 工程描述文件 內容

<?xml version="1.0" encoding="UTF-8"?> 
<projects> 
 <project name="sampleProject"  type="java"/> 
  ………………………………
</projects>

該 xml 文件不僅僅用於記錄存在增量代碼的工程信息,還將用於生成相關的備 份,還原等 Ant 腳本。下面就給出一個示例,通過預先編寫好的 Ant 模板,結合之前創建的存在增量代碼的 工程的描述文件,生成這些工程的增量代碼替換腳本,用以把抽取出來的增量代碼從存儲目錄替換到對應的工 程目錄下。在 Ant 腳本的"建立工程描述"子任務中加入清單 5 中的代碼片段。

清單 5. 動態生成構建腳本

<xsltstyle="${build.data.path}/

${filetype.xsl}/process.projects$workspace.insert.xsl"
    in="${build.generated.path}/${filetype.xml}/${filename.modifiedProjectsList}"
    out="${build.generated.path}/${filetype.ant}/Workspace-insertAparLevelProjects.ant"
reloadstylesheet="yes" force="yes"/>

上段代碼使用了 xsl 做為模板生成 Ant 腳本,其中使 用的 xsl 模板代碼如清單 6 所示。

清單 6. Ant 腳本模板

<xsl:stylesheet 

version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xalan="http://xml.apache.org/xalan"> 
<xsl:output method = "xml"  version="1.0" encoding="UTF-8" omit- 
   xml-declaration="no" indent="yes" xalan:indent-amount="4" /> 
<xsl:param name="projectNodeFromRoot" select="projects//project"/> 
<xsl:param name="actionTarget"/> 
<xsl:template match="/"> 
    <xsl:call-template name="makeScript"/> 
</xsl:template> 
<xsl:template name="makeScript"> 
    <project name="Workspace.backup" default="all"> 
        <xsl:call-template name="makeAllTarget"/> 
        <xsl:call-template name="makeProjectTarget"/> 
        <xsl:call-template name="makeActionTarget"/> 
    </project> 
</xsl:template> 
<xsl:template name="makeAllTarget"> 
     <target name="all"> 
        <xsl:for-each select = "$projectNodeFromRoot"> 
           <antcall> 
 <xsl:attribute name="target"> 
             <xsl:value-of select="@name"/> 
      </xsl:attribute> 
               </antcall> 
        </xsl:for-each> 
    </target> 
</xsl:template> 
<xsl:template name="makeProjectTarget"> 
    <xsl:for-each select = "$projectNodeFromRoot"> 
         <target> 
          <xsl:attribute name="name"> 
<xsl:value-of select="@name"/> 
          </xsl:attribute> 
          <antcall> 
<xsl:attribute name="target"> 
<xsl:value-of select="$actionTarget"/> 
</xsl:attribute> 
              <param> 
  <xsl:attribute name="name"> 
                        projectName 
                      </xsl:attribute>   
  <xsl:attribute name="value"> 
    <xsl:value-of select="@name"/> 
  </xsl:attribute> 
</param> 
</antcall> 
        </target> 
        </xsl:for-each> 
</xsl:template> 
<xsl:param name="actionTarget">insertProject</xsl:param> 
<xsl:template name="makeActionTarget"> 
    <target> 
       <xsl:attribute name="name"> 
           <xsl:value-of select="$actionTarget"/> 
            </xsl:attribute> 
            <copy> 
  <xsl:attribute name="todir"> 
                     <xsl:text> 
                            ${workspace.full.path}/${projectName} 
                          </xsl:text> 
   </xsl:attribute> 
   <xsl:attribute name="preservelastmodified"> 
    <xsl:text>true</xsl:text> 
   </xsl:attribute> 
   <xsl:attribute name="overwrite"> 
         <xsl:text>true</xsl:text> 
    </xsl:attribute> 
    <fileset> 
    <xsl:attribute name="dir"> 
     <xsl:text> 
                            ${source.src.project.path}/${projectName} 
                            </xsl:text> 
   </xsl:attribute> 
              </fileset> 
          </copy> 
        </target> 
     </xsl:template> 
</xsl:stylesheet>

通過參考上面示例代碼,讀者可以完成相應的增量代碼的備份模板、原始 代碼的恢復模板。並將相應的模板生成命令添加到"建立工程描述"子任務中。在代碼構建過程中相 應的 Ant 腳本將會根據模板的定義動態生成。完成動態腳本的生成之後就可進入下環節,調用該腳本進行工 程的恢復和備份。

工程的備份與恢復

為了保證每次的增量代碼都是在同樣的原始代碼的基 礎上進行編譯的,需要在進行增量代碼替換之前將其所在的原始工程進行備份。在完成構建之後,需要將原始 工程還原。還原流程在整個構建過程中會進行兩次,一次是在編譯原始代碼之前,以確保如上一次執行失敗後 發生腳本沒有執行的還原步驟得以執行,第二次是在完成構建之後。工程的備份和還原均為 Ant 腳本的拷貝 和刪除。並以之前生成的增量代碼所在工程描述文件為輸入參數。讀者可自行實現該部分,然後在"工程 備份" 和 "工程恢復" 兩個子任務中分別調用對應的腳本。

工程導入,編譯和代碼打 包

代碼在 RAD 的 WorkSpace 中的編譯過程主要包括:工程的導入和啟動編譯。在完成之前的步驟之 後,就可開始代碼的構建步驟。為了保證每次的增量編譯的正確性,需要首先對原始代碼進行編譯,並將需要 與增量代碼進行比較的工程導出成 jar 包用於之後的 Delta 比較。在完成了原始代碼的編譯和導出之後,可 將增量代碼替換到當前的目標工程中,對該工程重新導入並刷新 WorkSpace 進行增量代碼編譯。兩者在 RAD 中的操作基本一致所以本文將以增量代碼的編譯為例進行介紹。

對於工程的導入主要調用的是 RAD Build Utility 中的 Ant 標簽 "projectImport",該標簽每次可針對一個工程進行導入。在工程 導入時由 Ant 腳本根據模板生成全部工程導入腳本,然後執行該腳本。這裡給出"導入工程"子任 務的代碼示例如下:

清單 7. 工程導入腳本調用

<target name="workspace-

import_workspace_projects"> 
    // 調用 Ant 模板生成工程導入腳本
   <xslt style="${build.data.path}/${filetype.xsl} 
              /process.projects$workspace.import.projects.xsl"
    in="${build.generated.path}/${filetype.xml} 
          /${filename.allWorkspaceProjectsList}"
    out="${build.generated.path}/${filetype.ant} 
          /WorkspaceimportModifiedProjects.ant"
   reloadstylesheet="true" force="true"/> 
  // 執行工程導入腳本
<ant antfile="${build.generated.path}/${filetype.ant}/Workspace- 
importModifiedProjects.ant"/> 
</target>

動態生成的 Workspace-importModifiedProjects.ant 代碼片斷如清單 8 所示, 通過"importProjects" 子任務,讀者可以看到如何調用 Build Utility 中定義的 Ant 標簽模擬 RAD 中工作平台的工程導入的操作。

清單 8. 工程導入腳本

<project  

name="Workspace.import" default="all"> 
   <target name="all"> 
      <antcall target="sampleProject"/> 
       ................ 
   </target> 
       ................ 
  <target name=" sampleProject "> 
      <antcall target="importProjects"> 
          <param name="projectName" value=" sampleProject "/> 
      </antcall> 
  </target> 
      ................ 
   <target name="importProjects"> 
      <projectImport
           projectName="${projectName}"
           projectLocation="${workspace.path}/${projectName}"/> 
   </target> 
</project>

因為本例使用了命令行方式啟動 Build Utility,執行 "Workspace.import" 工程時相關自定義類已經加載到了環境變量中,因此可以直接調用。對於 WorkSpace 中工程的編譯,RAD 可以通過 Build Utility 中自定義的簽"workspaceBuild "來實現 。同時可以通過 "getJavacErrorCount " 標簽收集編譯中出現的錯誤並發送給相關人員。具體代 碼清單 9 所示。

清單 9. 工程編譯腳本

<project  name="Workspace.compile " 

default="all"> 
   <target name="all"> 
      <workspaceBuild FailOnError="false"
                           DebugCompilation="${build.DebugCompilation}"
                           BuildType="${build.buildType}"
                           ShowErrors="${build.ShowErrors}"/> 
      <getJavacErrorCount
                   ProjectName=" count.Analytics-Error"
                   PropertyName="count.Analytics-Error"/> 
                  ................................. 
     <condition property="hasUnexpectedErrors"> 
          <not> 
              <and> 
                  <equals arg1="${count.Analytics-Error}" arg2="0"/> 
                   ................................... 
             </and> 
         </not> 
      </condition> 
      <antcall  target="report"/> 
  </target> 
</project >

該部分代碼分為三個部分,第一部分調用 "workspaceBuild" 標簽啟 動 WorkSpace 編譯過程;第二部分調用 "getJavacErrorCount" 標簽完成對於所有工程中編譯錯 誤的統計;第三部分通過判斷是否存在工程有編譯錯誤並報告。在完成原始工程的編譯以及增量代編譯之後需 要將結果打包,並拷貝到臨時路徑下准備進行 Delta 比較,由於原始工程和增量代碼的腳本相同,本文以原 始代碼為例進行介紹。讀者可自己實現增量代碼工程的打包和拷貝腳本。

清單 10. 動態生成並執行打 包腳本

<target name="workspace-jar_Modified_projects"> 
     // 以之前生成的存在增量代碼的工程描述文件為輸入參數,生成用戶打包的 Ant 腳本
     <xslt style="${build.scripts.logic.path}/${filetype.xsl}/workspace.jarProject.xsl"
       in="${build.generated.path}/${filetype.xml}/${modifiedProjectsList}"
       out="${build.generated.path}/${filetype.ant}/Workspace-jarGmProjects.ant"
           reloadstylesheet="yes" force="yes"/> 
    // 調用剛才生成的 Ant 腳本對存在增量代碼的工程進行打包。       
    <ant antfile="${build.generated.path}/${filetype.ant}/Workspace-jarGmProjects.ant"> 
          <property name="todir" value="${packaging.gmJarFileOutputDir}"/> 
    </ant> 
</target>

對於工程的打包只要使用 Ant 腳本自帶的 jar 標簽。基於 Ant 腳本標簽中的文 件過濾屬性,讀者可根據自己的實際情況定義模板文件。這裡需要說明的是為了提高構建腳本的運行效率。我 們只需要將含有增量代碼的工程進行打包,因此動態生成打包腳本是必要的。

生成增量工程描述文件

當完成了工程的編譯和打包之後,我們就可以對原始工程和包含了增量代碼之後的工程進行比較。為 什麼需要對整個工程進行比較而不是直接抽取出增量代碼呢?因為部分的增量代碼的更新有可能會引起其他子 代碼的更新,例如增量代碼中包含的子類被更新之後,在編譯後的的二進制文件中該子類將是一個單獨的二進 制文件,該文件需要連同其父類的二進制文件一同被更新到運行環境中。

本文采用 MD5 加密算法 對工程中的文件對象進行加密,根據文件內容不同產生的加密值不同的原理來判斷同名文件是否被更新。通過 算法對文件的內容進行加密不僅可以保護文件的內容,同時通過對比兩個內容不同的重名文件進行加密之後所 生成的加密值,能夠清楚地看出文件內容上是否存在差異。為了給參與 Delta 比較的所有文件生成唯一標識 的 MD5 加密值,需要將工程文件解壓。解壓工程時需要為原始工程和含有增量代碼的工程分別在 <Build Scripts>/packaging/unpak 目錄下建立 Delta 和 GM 目錄 (Delta 目錄存放加入了增量代碼的工程, GM 目錄存放的是原始工程 )。將工程以工程名為目錄名解壓到對應的 Delta 或者 GM 目錄路下。同樣在 packaging/temp 目錄下新建 Delta 和 GM 目錄,將解壓後的工程分別拷貝到其下。在拷貝過程中用戶可自行 定義需要拷貝的文件,因為有一些工程的配置文件在開發過程中是不會變動的,因此並不需要加入到 Delta 比較流程中。

圖 2. Delta 和 GM 目錄

相關工程文件解壓完成之後,為了 方便 Delta 和 GM 目錄下同名工程的差異比較,需要為每個工程建立一個文件描述列表,並用 MD5 加密值作 為該文件的唯一標識。這樣在之後的 Delta 比較過程中只需要比較該文件列表。本文使用的是 java 中的 java.security.MessageDigest 類。為了便於比較,完成加密之後的二進制值需要轉換成為 hex 字符串。用 於加密的字符讀者可自行定義。實現文件內容加密和 hex 字符串生成的代碼示例如清單下:

清單 11. 加密算法和加密值轉換代碼

// 用戶將二進制值轉化為可讀字符串的數組
static char[] A_C_HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6', '7', 
 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 
// 讀入文件內容並進行加密,其中 sAlgorithm 為用戶可自定義的加密字符。
public static String getChecksum(File filePath, String  sAlgorithm) 
                 throws NoSuchAlgorithmException, FileNotFoundException,IOException 
{ 
   MessageDigest messagedigest = MessageDigest.getInstance( 
         sAlgorithm); 
   FileInputStream in = new FileInputStream(filePath); 
    byte[] abBuffer = new byte[4096]; 
    int nBytesRead = in.read(abBuffer); 
    while (nBytesRead != -1) 
    { 
       messagedigest.update(abBuffer, 0, nBytesRead); 
       nBytesRead = in.read(abBuffer); 
    } 
   in.close(); 
   return convertByteArrayToHexString(messagedigest.digest()); 
} 
// 將加密內容轉化為字符串形式 
private static String convertByteArrayToHexString(byte[] ab) 
{ 
   String sHex = ""; 
   for (int i = 0; i < ab.length; ++i) 
   { 
    int nHighNibble = (ab[i] & 0xF0) >>> 4; 
    int nLowNibble = ab[i] & 0xF; 
    sHex = sHex + 
           A_C_HEX_CHAR[nHighNibble] + 
           A_C_HEX_CHAR[nLowNibble]; 
   } 
   return sHex; 
}

在遍歷工程目錄並為每個文件生成唯一標識的加密字符的同時,可獲得文件相相對於工程的相對路 徑,該路徑可用於之後的文件提取,因此本框架將這兩者作為唯一標識文件的屬性,加入到文件描述列表中。 在下圖"file.list"中存儲的就是 SampleProject 工程的文件描述信息。針對於 Delta 和 GM 目 錄下的工程均需要建立"file.list"文件。讀者可將用於建立該文件的代碼和生成加密值的代碼整 合為一個 Ant 自定義標簽,並作為一個子任務添加到主任務當中。file.list 文件的具體內容如清單 12 所 示。

圖 3. File.list 文件截圖

清單 12.  File.list 文件內 容

<componentfiles componentname="SampleProject"> 
     <file> 
    <relativepath>com/sample/code/sampleCodeA.class</relativepath> 
    <checksum>9106dd5e35fb1ffa70c8e5179c0ef5f6f133681c</checksum> 
        <operation>remove</operation> 
     </file> 
     <file> 
         <relativepath>com/sample/code/utility/sampleCodeB.class</relativepath> 
         <checksum>5106dd5e31fb1dfa70c8e5179c0ef5f6f233681c</checksum> 
     <operation>remove</operation> 
     </file> 
     <file> 
              ........................................... 
     </file> 
</componentfiles>

清單 12 中 File 屬性的 checksum 值為 MD5 加密之後轉成的 hex 字符 串 ;operation 代表對於該文件的操作;relativepth 為文件在工程目錄下的相對路徑。在初始化文件列表 時,所有的文件的 operation 值均為 remove。當所有進行 Delta 比較的工程均完成 file.list 的建立之後 就可以對 Delta 目錄下 和 GM 目錄的工程進行逐個比較了。

增量代碼比較和提取

完成了工程 描述列表之後,每個工程目錄中均包含有一個 file.list 文件。之後便可通過編寫代碼分別讀取 Delta 和 GM 目錄中同名的工程目錄下的 file.list 文件然後進行比較,查找出相對於原始文件存在變化的 Delta 文 件,並將其按照相對路徑提取出來。此處的代碼讀者可以自行實現。本文只列出邏輯圖供讀者參考 .

圖 4. 增量代碼比較的邏輯圖

當然讀者可以通過其他方式來實現增量代碼的比較,WCBD 的部署功能中支持 Delta 部署,能夠在部署 代碼之前對基准代碼和更新的代碼進行比較找出 Delta 的部分。具體請參考 WebSphere Commerce 信息中心 中關於 WCBD Delta 部署部分的介紹。

將部署資源打包成 WCBD 可部署的資源包

到這裡為止, 我們已經完成了增量代碼的比較和提取,下面就可以將提取出來的增量代碼按照 WCBD 的要求進行打包。本文 介紹的自動化部署框架將利用 WAS 的部署機制通過 WCBD 進行部署,對於進行部署的文件必須要是 WAS 可識 別的,因此需要將生成的 Delta 代碼重新組織成 WAS 可識別的目錄結構。為此需要建立一個新的目錄。該目 錄相當於 EAR 的根目錄並將 Delta 代碼按照相對路徑拷貝到該目錄中 .

例如 partialApp 為新建目 錄,則增量代碼的存儲結構如下所示 :

/partialApp

/partialApp/SampleProjectA.jar/com/sample/code/SampleCodeA.class

/partia lApp/SampleProjectB.jar/com/sample/code/utility /SampleCodeB.class

這裡需要特別注意的是例 子中的"SampleProjectA.jar " 和 "SampleProjectB.jar" 為目錄名而非 JAR 包,請 讀者一定注意,否則在使用 WCBD 進行部署時會替換整個 JAR 包。

結束語

通過本章的介紹, 讀者已經了解了如何通過命令行的方式使用 RAD 的 Build Utility 工具模擬控制台對 WorkSpace 中的工程 進行導入和編譯。同時通過詳細的代碼示例為讀者介紹了如何在構建腳本中使用 Ant 模板技術,增強構建腳 本的靈活性。通過對於構建過程的逐步介紹讀者應該對整個過程由了大致的了解,讀者可以將本文中的代碼補 充完整,從而實現一個完成的增量代碼的構建流程。在本系列的第 3 部分中,將主要介紹如何使用 WebSphere Commerce Build and Deployment 工具對增量代碼進行部署。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved