如果您使用的不是 Eclipse 和 Eclipse Google 插件,则可能要通过其他方式来管理 App Engine 应用程序的生成和测试过程。使用 Apache Ant 可轻松地通过命令行或其他适用于 Ant 的 IDE 对项目进行管理。Java SDK 包含一组 Ant 宏,用来执行常规的 App Engine 开发任务,包括启动开发服务器以及将应用程序上传到 App Engine。
本文介绍了一种对大多数项目都很有用的 Ant 生成文件。要跳过说明直接转至用于复制粘贴的完整文件,请参阅完整的生成文件。
如果您的系统尚未安装 Ant,请访问 Apache Ant 网站进行下载并安装。
当安装完 Ant 后命令路径上出现 ant 命令时,即可运行以下命令,以验证它能否正常工作并查看所安装的版本:
ant -version
如使用入门指南中所述,您的 App Engine 项目必须使用针对 Java 网络应用程序的 WAR 标准布局生成目录结构。(SDK 目前尚不支持 WAR 档案文件。)
为此,我们将把所有与项目相关的文件放入名为 Guestbook/ 的单独目录中(对于使用入门指南中所述的留言簿项目),其中名为 src/ 的子目录用于存放 Java 源代码,而名为 war/ 的子目录用于存放已完成的应用程序文件。生成过程将编译源代码,并将经过编译的 Java 字节代码放入 war/ 下的适当位置。您将直接在 war/ 目录下创建其他 WAR 文件。
完整的项目目录应如下所示:
Guestbook/
src/
...Java source code...
META-INF/
...other configuration...
war/
...JSPs, images, data files...
WEB-INF/
...app configuration...
classes/
...compiled classes...
lib/
...JARs for libraries...
要通过命令提示符创建此目录结构,请使用下列命令。如果您使用的是 Windows,请将这些路径中的正斜线 / 更改为反斜线 \。(在 Ant 文件内部,使用正斜线 / 的路径可用于所有操作系统。)
mkdir Guestbook cd Guestbook mkdir -p src/META-INF mkdir -p war/WEB-INF/classes mkdir -p war/WEB-INF/lib
在 Guestbook/ 目录中,创建一个名为 build.xml 且包含以下内容的文件:
<project>
<property name="sdk.dir" location="../appengine-java-sdk" />
<import file="${sdk.dir}/config/user/ant-macros.xml" />
</project>
此生成文件还不能执行任何操作:它不包含带有用于执行任务的指令的“目标”。该文件定义了两种 Ant 属性,我们在稍后定义目标和路径时将会用到。
sdk.dir 属性是指向 App Engine Java SDK 的路径,是您在解压缩 SDK Zip 文件时创建的 appengine-java-sdk 目录。"../appengine-java-sdk" 假定该目录位于项目目录之上的父级目录中。根据需要调整该值。(路径与 build.xml 文件的位置有关。)
<import> 元素载入了一组用于 App Engine 开发的 Ant 宏(包含在 App Engine Java SDK 中)。稍后我们将使用这些宏中的一些宏。
当您编译类时,Java 类路径必须包含 App Engine API 的 JAR 以及已经添加到项目目录 war/WEB-INF/lib/ 的任何其他 JAR(如 DataNucleus JDO/JPA JAR)。应用程序可通过添加到该目录的 JAR 来访问类。另外,SDK 的 lib/shared/ 目录下的 JAR 将位于类路径上。
编辑 build.xml 并在结束 </project> 标签之上添加下列行,以定义包含这些 JAR 的类路径:
<path id="project.classpath">
<pathelement path="war/WEB-INF/classes" />
<fileset dir="war/WEB-INF/lib">
<include name="**/*.jar" />
</fileset>
<fileset dir="${sdk.dir}/lib">
<include name="shared/**/*.jar" />
</fileset>
</path>
应用程序必须在 war/WEB-INF/lib/ 目录中包含 appengine-api-*.jar(其中 * 代表 API 和 SDK 的版本号)这一 SDK 附带的 JAR。App Engine 将检查该 JAR 中的类,以确定您的应用程序所使用的 API 版本。如果您的应用程序使用 App Engine 数据存储区的 JDO 或 JPA 接口,则此应用程序必须在 war/WEB-INF/lib/ 中包含 JDO/JPA 实现。所有这些 JAR 都位于 SDK 的 lib/user/ 目录下。
您可手动将这些 JAR 复制到 WAR 中。但是,在生成过程中完成这些 JAR 的复制无疑更加有用。这可确保您的应用程序使用的这些接口的版本与 SDK 中的相同,即使您在以后升级 SDK 也一样。
所有的 JAR 都必须位于 war/WEB-INF/lib/ 目录下,而不能位于子目录下。该 Ant 目标将使用 copy 任务的 flatten="true" 属性从 ${sdk.dir}/lib/user/ 递归复制 JAR 文件,因此所有的 JAR 最终都位于同一目录下。
编辑 build.xml,并在结束 </project> 标签之上添加下列行:
<target name="copyjars"
description="Copies the App Engine JARs to the WAR.">
<copy
todir="war/WEB-INF/lib"
flatten="true">
<fileset dir="${sdk.dir}/lib/user">
<include name="**/*.jar" />
</fileset>
</copy>
</target>
这将创建一个名为 "copyjars" 的生成目标,可将 JAR 复制到相应位置。要测试该目标,请确保当前工作目录为项目目录 (Guestbook/),然后运行下列命令:
ant copyjars
检查 war/WEB-INF/lib/ 的内容以确保存在 JAR。
编辑 build.xml,并在结束 </project> 标签之上添加下列行:
<target name="compile" depends="copyjars"
description="Compiles Java source and copies other source files to the WAR.">
<mkdir dir="war/WEB-INF/classes" />
<copy todir="war/WEB-INF/classes">
<fileset dir="src">
<exclude name="**/*.java" />
</fileset>
</copy>
<javac
srcdir="src"
destdir="war/WEB-INF/classes"
classpathref="project.classpath"
debug="on" />
</target>
这将定义一个名为 "compile" 的生成目标,它可编译 src/ 目录下的所有 Java 源文件并将经过编译的类存储在 war/WEB-INF/classes/ 下。src/ 下的所有其他文件(如 META-INF/ 目录)均会逐字复制到 war/WEB-INF/classes/。该目标依赖于 "copyjars",因此在必要时将生成两个目标。
要编译项目的源文件,请运行下列命令:
ant compile
这部分介绍了如何使用 Ant 来生成支持数据存储区的 Java 数据对象 (JDO) 接口的项目。如果您的项目不使用 JDO 接口,则可继续下一部分。
如使用入门指南和数据存储区文档中所述,通过 JDO 接口可将类的实例存储到数据存储区中,并在以后作为对象检索。您将介绍如何使用类定义中的 Java 批注来存储和检索实例。
为了使 JDO 能够查看批注,您必须先使用 DataNucleus 访问平台(随 App Engine 一起发布的 JDO 实现)附带的工具编译数据类,然后对其字节代码进行处理。DataNucleus 将该过程称为对类进行“增强”。
App Engine SDK 包含用于增强 JDO 数据类的 Ant 宏。<enhance_war> 宏使用项目的正确类路径来增强项目中的每一个 JDO 数据类。为了实现更准确的控制,可使用 <enhance> 宏。
编辑 build.xml,并在结束 </project> 标签之上添加下列行:
<target name="datanucleusenhance" depends="compile"
description="Performs JDO enhancement on compiled data classes.">
<enhance_war war="war" />
</target>
该目标依赖于 "compile",因此生成该目标将确保这些类在执行增强任务前已编译且为最新。
可使用 Ant 宏来运行开发网络服务器。通过使该目标依赖 "datanucleusenhance" 目标(如果使用的不是 DataNucleus,则依赖 "compile" 目标),使用一个简单的命令即可生成项目并启动服务器。
编辑 build.xml,并在结束 </project> 标签之上添加下列行:
<target name="runserver" depends="datanucleusenhance"
description="Starts the development server.">
<dev_appserver war="war" />
</target>
可使用属性和 <options> 元素为开发服务器指定参数。例如,以下目标可启动使用 8888 端口的服务器,并在 9999 端口上启用远程 Java 调试:
<target name="runserver" depends="datanucleusenhance"
description="Starts the development server.">
<dev_appserver war="war" port="8888" >
<options>
<arg value="--jvm_flag=-Xdebug"/>
<arg value="--jvm_flag=-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9999"/>
</options>
</dev_appserver>
</target>
要生成项目并运行服务器,请使用下列命令:
ant runserver
要停止服务器,请按 Control-C。
您可定义 Ant 任务,将应用程序上传到 App Engine 并执行 AppCfg 提供的其他操作。
<appcfg> 宏将操作的名称视为 action 属性,将指向项目的 WAR 的路径视为 war 属性。该元素可具有可选的 <options> 元素和 <args> 元素。
编辑 build.xml 并添加下列行:
<target name="update" depends="datanucleusenhance"
description="Uploads the application to App Engine.">
<appcfg action="update" war="war" />
</target>
<target name="update_indexes" depends="datanucleusenhance"
description="Uploads just the datastore index configuration to App Engine.">
<appcfg action="update_indexes" war="war" />
</target>
<target name="rollback" depends="datanucleusenhance"
description="Rolls back an interrupted application update.">
<appcfg action="rollback" war="war" />
</target>
<target name="request_logs"
description="Downloads log data from App Engine for the application.">
<appcfg action="request_logs" war="war">
<options>
<arg value="--num_days=5"/>
</options>
<args>
<arg value="logs.txt"/>
</args>
</appcfg>
</target>
以下是由这些指令说明的完整 build.xml 文件:
<project>
<property name="sdk.dir" location="../appengine-java-sdk" />
<import file="${sdk.dir}/config/user/ant-macros.xml" />
<path id="project.classpath">
<pathelement path="war/WEB-INF/classes" />
<fileset dir="war/WEB-INF/lib">
<include name="**/*.jar" />
</fileset>
<fileset dir="${sdk.dir}/lib">
<include name="shared/**/*.jar" />
</fileset>
</path>
<target name="copyjars"
description="Copies the App Engine JARs to the WAR.">
<copy
todir="war/WEB-INF/lib"
flatten="true">
<fileset dir="${sdk.dir}/lib/user">
<include name="**/*.jar" />
</fileset>
</copy>
</target>
<target name="compile" depends="copyjars"
description="Compiles Java source and copies other source files to the WAR.">
<mkdir dir="war/WEB-INF/classes" />
<copy todir="war/WEB-INF/classes">
<fileset dir="src">
<exclude name="**/*.java" />
</fileset>
</copy>
<javac
srcdir="src"
destdir="war/WEB-INF/classes"
classpathref="project.classpath"
debug="on" />
</target>
<target name="datanucleusenhance" depends="compile"
description="Performs JDO enhancement on compiled data classes.">
<enhance_war war="war" />
</target>
<target name="runserver" depends="datanucleusenhance"
description="Starts the development server.">
<dev_appserver war="war" />
</target>
<target name="update" depends="datanucleusenhance"
description="Uploads the application to App Engine.">
<appcfg action="update" war="war" />
</target>
<target name="update_indexes" depends="datanucleusenhance"
description="Uploads just the datastore index configuration to App Engine.">
<appcfg action="update_indexes" war="war" />
</target>
<target name="rollback" depends="datanucleusenhance"
description="Rolls back an interrupted application update.">
<appcfg action="rollback" war="war" />
</target>
<target name="request_logs"
description="Downloads log data from App Engine for the application.">
<appcfg action="request_logs" war="war">
<options>
<arg value="--num_days=5"/>
</options>
<args>
<arg value="logs.txt"/>
</args>
</appcfg>
</target>
</project>