【IT168技术文档】
灵活的开发工作区使得构建过程中的某种形式的连续集成很受欢迎,并且 CruiseControl 是实现这种连续构建过程的流行框架。尽管集成 CruiseControl 和常见 Java 应用程序已经得到充分的证明,但是还需要一些额外的步骤来使 CruiseControl 更有效地处理 BEA WebLogic Workshop 应用程序。
本文将展示如何使用 CruiseControl 有 效管理 WebLogic Workshop 应用程序,从而允许您将 Workshop 应用程序应用到您自己的连续集成构建过程中。该集成的关键部分是编写可以解释 Workshop 的编译器输出的代码。本文还包含用来实现这项操作的自定义 Ant 任务的下载。
使用 CruiseControl 实现连续集成
CruiseControl 是一种开源产品,由 ThoughtWorks 出版发行,并获得了一个活跃社区的支持 。其目的是提供一种连续构建和测试过程的框架。 CruiseControl 可以使用多种源控件系统,并且可以通过各种可以想像到的方式发布结果,结果中包括最终的 Lava Lamp 解决方案。 CruiseControl 以 Java 为基础,并使用 Ant 作为其构建机制。
在构建我的工作区中的最新 WebLogic 项目之前,开发团队没有使用过每晚构建( nightly build )技术。事实上,他们只在新版本即将到期之前构建他们的应用程序,在那时承受所有的痛苦和折磨。花费大量时间设法获取需要构建的代码的故事就没有必要一一赘述了,因为我相信大家都已耳熟能详。因此,除了全新的 Workshop 环境之外,开发团队还用 CVS 设立了一个正确的源控件系统,并且越来越接近每晚构建的天堂。直到我们阅读有关 CruiseControl 的读物才得知, CruiseControl 是一种没有采用 每晚构建技术,但确实能够进行连续构建和测试的产品。
CruiseControl 之后的基本概念是构建您自己的代码,并在每次将代码加入源控制系统中时进行单元测试。在每晚构建系统中使用 CruiseControl 有两大主要优势。第一个优势是向开发人员提供相关即时反馈和开发过程连续性。不需要等到明天或者下周才知道构建是否成功,开发人员在几分钟之内就可以得到反馈,而且他们对正在编写的代码仍记忆犹新,因此,非常易于修复错误代码。第二个优势是 CruiseControl 不但可以执行单元测试,而且可以报告这些测试是否成功。使用每晚构建技术来模拟这种行为非常困难。
集成CruiseControl和WebLogic Workshop
CruiseControl 可以很好地使用常见 Java 应用程序,但是还需要一些额外的步骤来使 CruiseControl 更有效地使用 WebLogic Workshop 应用程序。本文中使用的方法包含如下几种:
1.设置标准的 CruiseControl 安装;本文中未作详细描述。
2.创建一种 Ant 脚本来编译 Workshop 应用程序。
3.开发自定义 Ant 任务来解释 Workshop 的编译器输出,并将其转换为 XML 。
4.为 CruiseControl 创建一种主 Ant 脚本。
5.写一份小的 XSL 文档,将 XML 转换为有用的 HTML 报告。
本文的其余部分将说明如何执行这些步骤。
设置CruiseControl
CruiseControl 的 web 站点中包含有关设置连续构建环境的优秀文档。本文将在此作一些简要概述。
在下载和编译 CruiseControl 之后,需要创建一份基本配置文件,文件名为 config.xml 。该文件包含将构建的所有项目。我通常将一个项目和一个 CVS 模块视为相等,主要是因为这样做适合我们的环境。每一个 <project/> 元素都指向源控制系统中的 CruiseControl ,通知它多长时间进行一次投票,以及如果检测到变更,需要运行哪一份 Ant 文件。
在该例中,我将使用一个 Workshop 项目(称作 MikesProject )。为了使事情更加简单,我将使用 CVS 中的模块名。
示例 CruiseControl 配置文件如下所示。与典型的 Java 项目不同,此处的重点是 <log/> 元素中的数据。它引用了一个日志文件, Workshop 构建过程必须创建该文件,并通知 CruiseControl 将这个文件与常见构建输出合并。
清单 1. CruiseControl 配置文件,合并自定义构建输出文件
<cruisecontrol>
<!-- MikesProject Project -->
<project name="MikesProject" buildafterfailed="false">
<bootstrappers>
<currentbuildstatusbootstrapper file="logs/MikesProject/currentbuildstatus.txt"/>
</bootstrappers>
<modificationset quietperiod="60">
<cvs localworkingcopy="checkout/MikesProject"/>
</modificationset>
<schedule interval="300">
<ant buildfile="masterbuild.xml" target="MikesProject">
</schedule>
<log dir="logs/MikesProject">
<merge file="checkout/MikesProject/build.log.xml" />
</log>
<publishers>
<currentbuildstatuspublisher file="logs/MikesProject/currentbuildstatus.txt"/>
<htmlemail mailhost="localhost"
skipusers="false"
returnaddress="cruisecontrol@your.domain.com"
defaultsuffix="@your.domain.com"
css="/opt/cruisecontrol-2.1.6/reporting/jsp/css/cruisecontrol.css"
xsldir="/opt/cruisecontrol-2.1.6/reporting/jsp/xsl"
logdir="logs/MikesProject">
<always address="mike"/>
</htmlemail>
</publishers>
</project>
</cruisecontrol>
有关设置标准的 CruiseControl 分布的详细说明,请遵照 web 站点 中的使用说明。
Workshop构建过程
设置构建过程(对每晚 / 每周 / 连续构建来说都是相似的 ) 的下一步是能够使用 Ant 在命令行中编译 Workshop 应用程序。 Workshop 提供了一种易于使用的 Ant 任务(称为 wlwBuild );此外, Workshop 将创建一种简单的 Ant 脚本,为您提供更高的起点。在 Workshop 中,在 Tool 菜单中选择 Application Property 。在弹出的对话框中,选择 Build 节点,然后点击右边的 Export to Ant file 按钮。 Workshop 将 Ant 文件保存在应用程序目录(称作 exported_build.xml ,通常重命名为 build.xml 。)中。由此产生的脚本将调用 wlwBuild Ant 任务,并在编译时引用(在本文的示例中)项目的 MikesProject.work 文件。
该脚本需要进行一些编辑,才能用在台式机以外的更多地方中。默认情况下,它将包含到达应用程序在工作站中所在位置的硬编码路径。 Workshop 将生成两种具有硬编码路径的属性标签,其名称分别为 app.local.directory 和 project.local.directory 。默认情况下, Ant 将项目目录(实际上是 build.xml 文件所在的目录)用作其工作目录,并相应的设置 ${basedir} 变量。因此,应该将这两种标签的路径更改为相对路径:
<property name="app.local.directory" value="${basedir}/../" />
<property name="project.local.directory" value="${basedir}" />
因为我们的工作站是基于 Windows 的,而我们的构建服务器运行的是 HP-UX ,所以我们已经在项目中添加了两份文件,即 Windows 2000.build.properties 和 HPUX.build.properties 。这两份文件包含两个属性名称 / 值对,分别与 BEA 安装路径和 WebLogic 安装路径有关。因此, Ant 脚本的第二行就如下所示:
<property file="${os.name}.build.properties"/>
Ant 将加载适当的文件和属性。在创建 Ant 文件时必须严格遵守纪律,以确保与 WebLogic 相关的路径的所有引用都使用属性文件中的属性。
一旦创建了 Ant 脚本,您就会想在工作站的命令行中测试该脚本。
此处需要注意的一点是:即使 Workshop 允许您通过 Workshop IDE 运行 Ant 脚本,也不要在测试时依赖这一点。运行在命令行环境中与在 Workshop 中编译有很大的区别;我花费了很长的时间,试图挑选出那些成功编译了无法在命令行中编译的应用程序的不可思议的 Workshop 。
一旦在工作站中编译好应用程序,就可以将它们加入源控制系统中。并在构建系统中检验整个应用程序,并设法在命令行中编译该应用程序。这看起来似乎是非常乏味的,但是它可以防止出现更多令人痛苦的事情。
解释编译器输出
您可能已经注意到,典型的 Workshop 应用程序使用很多不同的工具来编译应用程序,每种工具都有自己的输出格式。这是设法集成 CruiseControl 和 Workshop 应用程序时问题的关键所在。 CruiseControl 根据命令的返回值(对于 Unix-savvy ,该值是 $? )来确定构建是否成功,如果返回值为 0 ,则说明成功,如果为其他值,则说明失败。好极了!但是开发人员需要返回值以外的更多信息,如果构建失败的话。 CruiseControl 可以解释普通 Java 编译器( javac 和 javelin )的输出,但是它不知道如何解释 EJB/RMI/JSP 编译器输出。因此,我们必须教会它如何执行该操作。第一步是将编译器输出(或者构建日志)转换为简单的 XML 文件,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<build error="error message text but only if build failed"
time="1 hour 40 minutes 23 seconds">
<target name="ignored" time="ignored">
<task location="ignored" name="javac" time="ignored">
<message priority="warn">
<![CDATA[preformatted compiler warning messages]]>
</message>
<message priority="error">
<![CDATA[preformatted compiler error messages]]>
</message>
</task>
<task location="ignored" name="jar" time="ignored">
<message priority="info">
<![CDATA[message w/name of jar or war file]]>
</message>
</task>
</target>
</build>
创建该文件的方法是捕获一份文件( build.log )中的构建输出,然后运行自定义 Ant 任务,将该输出转换为具有上述格式的 XML 文件,从而生成在 CruiseControl's config.xml 文件中的 <log/> 节( stanza )引用的 build.log.xml 文件。清单 1 说明了该输出文件是如何被引用的。 CruiseControl 可以将 build.log.xml 文件与它可能已经收集的其他任何输出进行合并,这些输出中包括 JUnit 测试结果和部署输出。
Ant 任务(在附带的下载中作为 WlwBuildToXml.java 提供)是反复试验的结果,其可以捕获各种错误以及编写代码来捕获错误。它最初以 Text2Xml Ant 任务 为基础。代码一点也不优美(至少不如我已经添加的代码),但是它非常适用。 80/20 (或者 90/10 )规则在这里也很适用。该 Ant 任务捕获大约 90% 的编译错误,足以使我们保持轻松的心情。剩余 10% 的错误很少发生,所以仔细研读构建日志比试图通过编写代码来捕获和正确地报告这些错误要简单得多。
使用以下命令行编译 Ant 任务,假设 WebLogic 的 安装目录是 
然后,创建类的 JAR :
jar cf WlwBuildToXml.jar WlwBuildToXml.class
下一节将展示如何使用这种新的 Ant 任务。
CruiseControl Masterbuild脚本
我们现在可以创建一份主 Ant 脚本(在本示例中称为 masterbuild.xml ), CruiseControl 可以使用该脚本运行构建。乍看上去,使用两份构建脚本似乎有些古怪,但是其优势是可以区分应用程序特定的构架逻辑,包括 wlwBuild 任务和 CruiseControl 的特殊信息,例如,如何解释构建输出。您获得的好处可能有所不同。
默认情况下, Ant 只将构建输出发送到 stdout 。 WlwBuildToXml Ant 任务仅可以处理日志文件,但是 <record/> Ant 任务可以在日志文件中保存所有的输出。
Ant 是使用 Java 编写的,并且一次失败被看作是一个 Java 异常。如果 Ant 任务出现一个异常,则该异常就会返回给调用方,并且其并发任务会被忽略。这种行为需要进行调整,因为 Ant 需要调用自定义任务,而不用考虑 wlwBuild 任务中发生了什么,然后,仅传递所有可能已经发生的异常。幸运的是,收集 Ant-Contrib 任务 的那些善良的人们预见到这种需求,并包含了一个 <trycatch/> 任务。由此而生成的构建文件如下所示,它在 <finally/> 任务中具有重要位置。
<project name="masterbuild" default="BuildMikesProject" basedir=".">
<property name="MikesProject.home" value="${basedir}/checkout/MikesProject"/>
<property name="MikesProject.build.log" value="${MikesProject.home}/build.log"/>
<!-- our custom wlwBuild output to xml class -->
<taskdef name="wlwBuild2xml" classname="WlwBuildToXml"> <classpath>
<pathelement location="WlwBuildToXml.jar"/>
</classpath>
</taskdef>
<!-- Ant contributed tasks from http://ant-contrib.sourceforge.net/-->
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="ant-contrib.jar"/>
</classpath>
</taskdef>
<target name="BuildMikesProject">
<trycatch>
<try>
!-- here's a good place to tag CVS if you want to.
Ant will only get here if the build was successful. -->
</target>
</project>
一旦创建了该文件,就可以使用目标为 BuildMikesProject 的 Ant 在构建服务器上测试该文件。
生成有用的报告
在 CruiseControl 已经完成它调用(在我们的示例中为 BuildMikesProject )的初始任务之后,它会将发送到 stdout 的所有输出与在 CruiseControl config.xml 文件中的 <log><merge.../></log> 元素中找到的所有日志进行合并。合并文件将生成一份非常大的 XML 文件,它包含 WlwBuildToXml Ant 任务创建的 build.log.xml 文件。
当 CruiseControl 发布程序生成一份 HTM 报告(包括 HTMLEmail 发布程序)时,它将运行 $CC_HOME/reporting/jsp/xsl 目录中的所有 XSL 脚本,并假定 $CC_HOME 是 CruiseControl 的本地目录。这些 XSL 文件的输出与 XML 日志发生冲突,因为其主体是 HTML 文档。如果需要将 WlwBuildToXml Ant 任务捕获的错误添加到文档中,那么需要创建另一份 XSL 文件,并将该文件保存在 $CC_HOME/reporting/jsp/xsl 目录中。文件 wlw-errors.xsl 如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xslt="http://xml.apache.org/xslt">
<xsl:output method="html"/>
<xsl:variable name="errors.list"
select="cruisecontrol/build//target/task/message[@priority='error']"/>
<xsl:template match="/">
<!-- if there were errors -->
<xsl:if test="cruisecontrol/build/@error">
<table align="center" cellpadding="2" cellspacing="0" border="1" width="98%">
<tr>
<td class="errors-sectionheader" colspan ="2">
<b>Errors occurred during build:
(<xsl:value-of select="count($errors.list)"/>)</b>
</td>
</tr>
<tr>
<th class="errors-sectionheader">Target</th>
<th class="errors-sectionheader">Message</th>
</tr>
<xsl:apply-templates select="$errors.list"/>
</table>
<br/>
</xsl:if>
</xsl:template>
<!--deal with the data -->
<xsl:template match="cruisecontrol/build//target/task/message">
<tr>
<xsl:if test="position() mod 2=0">
<xsl:attribute name="class">errors-oddrow</xsl:attribute>
</xsl:if>
<xsl:if test="position() mod 2!=0">
<xsl:attribute name="class">errors-evenrow</xsl:attribute>
</xsl:if>
<td valign="top" class="errors-data">
<xsl:value-of select="ancestor::target/@name"/>
</td>
<td class="errors-data">
<xsl:value-of select="text()"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
就这些了! 启动 CruiseControl ,并对您的源控件知识库进行变更。如果所有正确部分都在适当的位置上,那么 CruiseControl 会给您发送一封电子邮件,该电子邮件包含一份格式良好的构建报告。下图是一份示例报告。 
图 1. 执行样式表得到的结果
结束语
本文讲解了如何通过集成 WebLogic Workshop 构建的结果来扩展标准的 CruiseControl 部署。设置和扩展 CruiseControl 看似非常复杂,但有相关的优秀文档和活动邮件列表可供参考。一旦完成设置,您就可以在开发团队中非常快速地看到肯定的结果。