生产部署与Ant任务扩展全解析
生产部署相关内容
在生产部署中,不同的应用服务器有着各自独特的部署流程。
BEA WebLogic部署
在
<serverdeploy>
元素内部有一个
<weblogic>
元素。要使用它,需要在类路径中包含
weblogic.jar
文件,可通过
classpath
属性来实现。示例代码如下:
<serverdeploy
action="deploy"
source="${webapp.path}">
<weblogic
application="${target.appname}"
component="webapp:${target.server}"
server="t3://${target.server}:7001"
username="${target.username}"
password="${target.password}"
classpath="${env.WEBLOGIC_HOME}/lib/weblogic.jar"
/>
</serverdeploy>
需要注意的是,WebLogic 7.0自带了一份Ant。建议重命名它的
ant.bat
和
ant.sh
文件,避免意外使用该版本,因为当路径中有多个Ant脚本或批处理文件版本时,可能会不小心使用旧版本的Ant,并且可能无法将可选库添加到适当的目录。
HP Bluestone应用服务器部署
该应用服务器自带一个部署任务
<hpas-deploy>
,可将WAR或EAR文件上传到正在运行的HP - AS应用服务器实例。它使用作为属性提供的账户和密码对请求进行授权,推测它使用自定义的有线协议与JMX服务器通信。
定义任务及部署的示例代码如下:
<taskdef name="hpas-deploy"
classname="com.hp.mwlabs.tools.pacman.ant.HPASDeploy" />
<target name="deploy" depends="init"
description="Deploy to HP-AS server">
<hpas-deploy
host="${target.server}"
uri="${target.appname}"
port="2000"
username="${target.username}"
password="${target.password}"
jarfile="${webapp.path}">
</hpas-deploy>
</target>
也可以在任务中指定一个
<fileset>
来上传一组文件,但此时不能再指定部署路径,工具会使用文件集中每个文件的名称。显然,单文件部署更灵活。
不过,这个任务存在一个主要缺陷,它不能在正常的Ant执行环境中工作,只能在供应商的RadPak Ant GUI工具中使用,这使得无法通过该任务进行自动化构建和部署。
其他服务器部署
还有许多应用服务器没有明确的Ant支持。为这些服务器创建构建文件进行部署的过程通常是:查看其文档和示例部署脚本,然后在Ant中复制这些步骤。基于URL的管理应用程序可以使用
<get>
请求;辅助程序可以使用
<java>
和
<exec>
调用;支持热部署的服务器可以使用
<copy>
调用。
批处理文件通常是最有信息价值的来源,因为它们显示了调用基于Java的程序所需的类路径和参数。可以在应用程序中用单个
<java>
调用替换每个这样的脚本文件。通常,为新的服务器类型编写一个可用的构建文件可能需要一两天时间,但编写完成后可以反复使用。
验证部署
为了确保部署成功,可采用创建时间戳文件的方法。
-
创建时间戳文件
:首先为文件命名并指定位置。
<property name="timestamp.filename"
value="timestamp.txt"/>
<property name="timestamp.path"
location="${build.dir}/${timestamp.filename}"/>
通过获取当前日期和时间并保存到文件中:
<target name="make-timestamp" depends="init" >
<tstamp>
<format property="buildtime"
pattern="yyyy-MM-dd'T'HH:mm:ss" />
</tstamp>
<echo file="${timestamp.path}"
message="build.timestamp=${buildtime}" />
</target>
-
将时间戳文件添加到应用程序
:在
<war>任务中添加另一个文件集,并为目标添加新的依赖项。
<target name="make-war"
depends="compile,make-webxml,make-web-docs,make-timestamp,make-soap-api">
<war destfile="${warfile}"
compress="false"
update="true"
webxml="${build.webinf.dir}/web.xml">
<classes dir="${build.classes.dir}"/>
<webinf dir="${build.dir}" includes="index/**"/>
<webinf dir="${struts.dir}/lib" includes="*.tld,*.dtd"/>
<webinf dir="${build.webinf.dir}" includes="antbook.tld"/>
<fileset dir="${build.dir}" includes="${timestamp.filename}"/>
<fileset dir="web"/>
...
</war>
</target>
- 验证时间戳 :通过以下目标验证部署是否成功。
<target name="verify-uptodate"
depends="install" >
<property name="verify.url"
value="${test.url}/${timestamp.filename}" />
<property name="verify.local.path"
location="${dist.dir}/deployed-on-${target.server}.txt"
/>
<waitfor timeoutproperty="deployment.failed"
maxwait="30"
maxwaitunit="second">
<http url="${verify.url}" />
</waitfor>
<fail if="deployment.failed">
timestamp page not found at ${verify.url}"
</fail>
<get src="${verify.url}"
dest="${verify.local.path}" />
<condition property="verify.uptodate.successful">
<filesmatch
file1="${timestamp.path}"
file2="${verify.local.path}"
/>
</condition>
<loadfile property="verify.expected"
srcFile="${timestamp.path}" />
<loadfile property="verify.found"
srcFile="${verify.local.path}" />
<fail unless="verify.uptodate.successful">
file match failed;
expected [${verify.expected}]
found [${verify.found}]
</target>
这个目标有三个阶段:创建到远程时间戳的URL后,使用
<waitfor>
等待文件存在;如果文件存在,使用
<get>
任务将其检索并保存到本地文件;最后使用
<condition>
测试比较两个文件。如果它们不相等,构建将失败,并给出有用的错误消息。
生产部署最佳实践
生产部署有两个核心实践:严谨和与运维部门合作。
-
严谨
:设计构建文件时不偷工减料,不对系统做过多假设,例如应用程序的安装位置。同时,要包含大量测试。功能测试和系统配置的幸福测试有助于定位问题,如果功能测试失败,可能是程序或系统的问题;如果配置测试失败,则是系统的问题。测试还能验证程序在不同应用服务器上的运行情况,尽管目前没有核心方法解决所有问题,但使用相同的应用服务器会有帮助。
-
与运维部门合作
:理想的服务器是运行良好,运维人员甚至忘记其位置和登录方式的服务器。可以将运维需求视为用例,将遇到的问题视为缺陷进行记录、跟踪、测试和修复。
Ant任务扩展相关内容
当Ant的内置功能无法满足项目需求时,就需要对其进行扩展。扩展Ant并不复杂,只需要少量的Java编码就能编写新的Ant任务。
什么是Ant任务
一个Java类成为Ant任务的定义很简单,它必须有一个
execute()
方法。Ant的核心引擎有相对复杂的内省机制,允许任务以多种方式插入其中。
最简单的Ant任务示例
package org.example.antbook.tasks;
public class SimpleTask {
public void execute() {
System.out.println(">>>> SimpleTask <<<<");
}
}
将Java类变成Ant任务的有效规则是:
execute
方法必须是公共的且无参数,类必须能够使用无参数构造函数实例化(即类不能是抽象的)。
execute
方法可以有返回值,但会被忽略,并且在定义任务时会生成警告。
execute
方法可以抛出异常,这会导致构建适当地失败。
需要注意的是,在任务执行期间写入
System.out
或
System.err
是允许的,但Ant会捕获输出并将其记录到适当的日志级别。建议扩展
org.apache.tools.ant.Task
并使用提供的日志记录方法。
编译和在同一构建中使用任务
通过以下构建文件可以在同一构建中编译并使用任务:
<?xml version="1.0" ?>
<project name="tasks" default="main">
<property name="build.dir" location="build"/>
<target name="init">
<mkdir dir="${build.dir}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="src" destdir="${build.dir}"/>
</target>
<target name="simpletask" depends="compile">
<taskdef name="simpletask"
classname="org.example.antbook.tasks.SimpleTask"
classpath="${build.dir}"
/>
<simpletask/>
</target>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="main" depends="simpletask"/>
</project>
使用
<taskdef>
的技巧是在编译任务后、执行任务前进行定义。当集成第三方任务到构建文件时,可以在目标外指定
<taskdef>
,使任务在该构建文件中全局定义。
任务生命周期
Ant从XML任务声明到Java类的映射是一种非正式的数据绑定奇迹。在介绍属性和元素如何映射到Java方法之前,需要了解任务生命周期。构建文件的处理有不同阶段,实现任务的对象在这些阶段都会被使用。
综上所述,生产部署和Ant任务扩展是项目开发中重要的环节。在生产部署中,要根据不同的应用服务器选择合适的部署方式,并通过验证机制确保部署成功。而Ant任务扩展则为解决项目中遇到的各种构建问题提供了有效的途径。
Ant API 基础
Ant API 是编写自定义 Ant 任务的重要基础,以下是一些关键的 API 相关内容。
日志记录
Ant 有自己的日志记录机制,在任务执行期间,写入
System.out
或
System.err
的内容会被 Ant 捕获并记录到相应的日志级别。
System.out
对应
MSG_INFO
级别,
System.err
对应
MSG_ERR
级别。建议扩展
org.apache.tools.ant.Task
类,并使用其提供的日志记录方法,这样可以更好地与 Ant 的日志系统集成。
任务属性和元素映射
Ant 通过内省机制将 XML 任务声明中的属性和元素映射到 Java 类的方法。例如,对于 XML 任务中的属性,Ant 会尝试调用 Java 类中对应的 setter 方法。以下是一个简单示例:
import org.apache.tools.ant.Task;
public class AttributeTask extends Task {
private String message;
public void setMessage(String message) {
this.message = message;
}
@Override
public void execute() {
log("The message is: " + message);
}
}
在 XML 中使用该任务时:
<taskdef name="attributetask" classname="com.example.AttributeTask" />
<attributetask message="Hello, Ant!" />
Ant 会自动调用
setMessage
方法设置属性值。
任务获取数据的方式
Ant 任务获取数据的方式有多种,以下是常见的几种。
属性获取
可以通过 Ant 的属性机制为任务提供数据。在 XML 中定义属性,然后在任务中获取这些属性的值。例如:
<property name="my.property" value="Property Value" />
<taskdef name="propertytask" classname="com.example.PropertyTask" />
<propertytask property="${my.property}" />
在 Java 任务类中:
import org.apache.tools.ant.Task;
public class PropertyTask extends Task {
private String property;
public void setProperty(String property) {
this.property = property;
}
@Override
public void execute() {
log("The property value is: " + property);
}
}
文件集操作
任务可以对文件集进行操作。例如,遍历文件集中的文件并执行相应的操作。以下是一个简单的文件集操作任务示例:
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import java.io.File;
public class FileSetTask extends Task {
private FileSet fileSet;
public void addFileset(FileSet fileSet) {
this.fileSet = fileSet;
}
@Override
public void execute() throws BuildException {
if (fileSet == null) {
throw new BuildException("No fileset specified");
}
DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject());
File basedir = scanner.getBasedir();
String[] files = scanner.getIncludedFiles();
for (String file : files) {
log("Processing file: " + new File(basedir, file).getPath());
}
}
}
在 XML 中使用该任务:
<taskdef name="filesettask" classname="com.example.FileSetTask" />
<filesettask>
<fileset dir="src" includes="**/*.java" />
</filesettask>
创建基本的 Ant 任务子类
创建基本的 Ant 任务子类通常需要扩展
org.apache.tools.ant.Task
类,并实现
execute
方法。以下是一个简单的示例:
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
public class BasicTask extends Task {
@Override
public void execute() throws BuildException {
log("Executing basic Ant task");
}
}
在 XML 中使用该任务:
<taskdef name="basictask" classname="com.example.BasicTask" />
<basictask />
错误处理
在任务执行过程中,可能会遇到各种错误。Ant 任务可以通过抛出
BuildException
来处理错误。例如:
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
public class ErrorHandlingTask extends Task {
@Override
public void execute() throws BuildException {
try {
// 模拟可能出错的操作
int result = 1 / 0;
} catch (ArithmeticException e) {
throw new BuildException("Error occurred: " + e.getMessage(), e);
}
}
}
当任务执行时遇到错误,会抛出
BuildException
,Ant 会捕获该异常并终止构建过程,并输出错误信息。
测试 Ant 任务
为了确保 Ant 任务的正确性,需要对其进行测试。可以使用 JUnit 等测试框架来测试 Ant 任务。以下是一个简单的测试示例:
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Echo;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TaskTest {
@Test
public void testTask() {
Project project = new Project();
project.init();
Echo echo = new Echo();
echo.setProject(project);
echo.setMessage("Test message");
try {
echo.execute();
} catch (BuildException e) {
// 处理异常
}
}
}
通过创建 Ant 项目实例,并将任务与项目关联,然后执行任务,最后根据任务的执行结果进行断言。
执行外部程序和 Java 程序
执行外部程序
Ant 任务可以通过
<exec>
任务来执行外部程序。以下是一个执行
ls
命令的示例:
<exec executable="ls">
<arg value="-l" />
</exec>
在 Java 任务中也可以使用
Execute
类来执行外部程序:
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.util.Execute;
import org.apache.tools.ant.util.LogStreamHandler;
import java.io.IOException;
public class ExternalProgramTask extends Task {
@Override
public void execute() throws BuildException {
try {
Execute exe = new Execute(new LogStreamHandler(this, getProject().getProperty("verbose") != null? Execute.INPUT_BUFFER_SIZE : 0, Execute.INPUT_BUFFER_SIZE));
exe.setCommandline(new String[]{"ls", "-l"});
exe.execute();
} catch (IOException e) {
throw new BuildException("Error executing external program: " + e.getMessage(), e);
}
}
}
执行 Java 程序
可以使用
<java>
任务在 Ant 中执行 Java 程序。以下是一个示例:
<java classname="com.example.MyJavaClass" fork="true">
<classpath>
<pathelement location="lib/myjar.jar" />
</classpath>
</java>
在 Java 任务中也可以通过
Java
类来执行 Java 程序:
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Java;
public class JavaProgramTask extends Task {
@Override
public void execute() throws BuildException {
Java javaTask = (Java) getProject().createTask("java");
javaTask.setClassname("com.example.MyJavaClass");
javaTask.setFork(true);
javaTask.createClasspath().createPathelement().setLocation("lib/myjar.jar");
javaTask.execute();
}
}
支持任意命名的元素和属性
Ant 任务可以支持任意命名的元素和属性。通过使用
DynamicAttribute
和
DynamicElement
接口,可以动态处理这些元素和属性。以下是一个示例:
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.util.DynamicAttribute;
import org.apache.tools.ant.util.DynamicElement;
import java.util.HashMap;
import java.util.Map;
public class DynamicTask extends Task implements DynamicAttribute, DynamicElement {
private Map<String, String> attributes = new HashMap<>();
private Map<String, Object> elements = new HashMap<>();
@Override
public void setDynamicAttribute(String uri, String localName, String value) throws BuildException {
attributes.put(localName, value);
}
@Override
public Object createDynamicElement(String uri, String localName) throws BuildException {
Object element = new Object();
elements.put(localName, element);
return element;
}
@Override
public void execute() throws BuildException {
log("Attributes: " + attributes);
log("Elements: " + elements);
}
}
在 XML 中使用该任务:
<taskdef name="dynamictask" classname="com.example.DynamicTask" />
<dynamictask customAttribute="value">
<customElement />
</dynamictask>
构建任务库
当编写了多个自定义 Ant 任务后,可以将它们打包成一个任务库。以下是构建任务库的步骤:
1.
编写任务类
:编写多个自定义 Ant 任务类。
2.
创建 JAR 文件
:将这些任务类编译成字节码,并打包成 JAR 文件。
3.
定义任务库
:在 XML 中使用
<taskdef>
定义任务库。
<taskdef resource="com/example/anttasks.properties" classpath="lib/anttasks.jar" />
在
anttasks.properties
文件中定义任务名称和对应的类名:
mytask=com.example.MyTask
支持多个版本的 Ant
为了使自定义 Ant 任务能够在多个版本的 Ant 中使用,需要注意以下几点:
-
使用稳定的 API
:避免使用特定版本 Ant 独有的 API,尽量使用通用的、稳定的 API。
-
进行兼容性测试
:在不同版本的 Ant 中测试自定义任务,确保其能够正常工作。
总结
生产部署和 Ant 任务扩展在项目开发中起着至关重要的作用。在生产部署方面,针对不同的应用服务器,我们需要采用合适的部署方式,并通过验证机制确保部署的成功。而 Ant 任务扩展则为解决项目中复杂的构建问题提供了强大的工具。通过编写自定义 Ant 任务,我们可以根据项目的具体需求对 Ant 进行扩展,提高构建过程的自动化和灵活性。同时,在编写和使用自定义任务时,要注意遵循 Ant 的规范和最佳实践,包括任务的定义、数据获取、错误处理、测试等方面。通过这些方法,可以更好地利用 Ant 的功能,提升项目的开发效率和质量。
超级会员免费看
635

被折叠的 条评论
为什么被折叠?



