53、生产部署与Ant任务扩展全解析

生产部署与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 的功能,提升项目的开发效率和质量。

内容概要:本文介绍了一个基于冠豪猪优化算法(CPO)的无人机三维路径规划项目,利用Python实现了在复杂三维环境中为无人机规划安、高效、低能耗飞行路径的完整解决方案。项目涵盖空间环境建模、无人机动力学约束、路径编码、多目标代价函数设计以及CPO算法的核心实现。通过体素网格建模、动态障碍物处理、路径平滑技术和多约束融合机制,系统能够在高维、密集障碍环境下快速搜索出满足飞行可行性、安能效最优的路径,并支持在线重规划以适应动态环境变化。文中还提供了关键模块的代码示例,包括环境建模、路径评估和CPO优化流程。; 适合人群:具备一定Python编程基础和优化算法基础知识,从事无人机、智能机器人、路径规划或智能优化算法研究的相关科研人员工程技术人员,尤其适合研究生及有一定工作经验的研发工程师。; 使用场景及目标:①应用于复杂三维环境下的无人机自主导航避障;②研究智能优化算法(如CPO)在路径规划中的实际部署性能优化;③实现多目标(路径最短、能耗最低、安性最高)耦合条件下的工程化路径求解;④构建可扩展的智能无人系统决策框架。; 阅读建议:建议结合文中模型架构代码示例进行实践运行,重点关注目标函数设计、CPO算法改进策略约束处理机制,宜在仿真环境中测试不同场景以深入理解算法行为系统鲁棒性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值