Apache Anakia 配置参数详解
Apache Anakia(velocity-1.7)配置参数详解:AnakiaTask 各属性逐个实测,含增量检查语义、编码、projectFile 导航、includes/excludes 与模板上下文变量。
Apache Anakia 配置参数详解
目标:系统梳理 Apache Anakia(velocity-1.7 内置 AnakiaTask)的全部配置面——十个 XML 属性逐一实测(含 lastModifiedCheck 三方时间戳语义、velocityPropertiesFile 编码机制、projectFile 相对解析规则、includes/excludes 过滤)、模板上下文变量、生产配置模板,所有示例在 Ubuntu 22.04(Ant 1.10.18 + velocity-1.7)实测通过。
一、环境信息
本篇所有命令与回显均在以下环境中实测采集:
- 操作系统:Ubuntu 22.04.5 LTS,x86_64
- JDK:OpenJDK 17.0.20.1
- 构建工具:Apache Ant 1.10.18(
ANT_HOME=/opt/apache-ant-1.10.18) - 组件版本:Apache Anakia(
org.apache.velocity:velocity:1.7,jar 449505 字节,位于/opt/anakia-lib/) - 实验目录:
/opt/anakia-demo(工程结构与《快速入门》一致:src/xml/源、styles/模板) - 执行用户:root
💡 技术拓展:模板引擎对比——Anakia 的渲染核心是 Velocity 1.7(VTL 语法),同类 Java 模板引擎还有 FreeMarker(更严格的空值处理与内置格式化)与 Thymeleaf(自然模板)。Anakia 的独特价值在于把 JDOM 元素树直接暴露给模板上下文,模板侧不需要任何 Java 胶水代码即可遍历 XML;代价是绑定 Velocity 1.7 运行时(2.x 已无 Anakia)。
二、前置条件
本篇示例可直接执行,前提:Ant 已安装、四个运行时 jar 就位、验收工程可跑。核验命令与实测回显:
ant -version
回显(实测):
Apache Ant(TM) version 1.10.18 compiled on September 3 2026
cd /opt/anakia-demo && ant
回显(实测):
Buildfile: /opt/anakia-demo/build.xml
docs:
[anakia] Transforming into: /opt/anakia-demo/docs
BUILD SUCCESSFUL
Total time: 0 seconds
(本次运行所有源未变化,lastModifiedCheck="true" 触发跳过,仅输出 Transforming into: 一行——这本身就是参数在工作的证据。)环境安装过程见《Ubuntu 22.04 安装 Apache Anakia 指南》或《CentOS 8 安装 Apache Anakia 指南》。
三、参数查看与修改方式
Anakia 没有随发行包分发的手册页,最权威的参数来源是 jar 内字节码。反编译 AnakiaTask 看真实 API 面:
cd /tmp && rm -rf ancls && mkdir ancls && cd ancls && unzip -q /opt/anakia-lib/velocity-1.7.jar 'org/apache/velocity/anakia/*' && javap -classpath . org.apache.velocity.anakia.AnakiaTask
回显(实测):
Compiled from "AnakiaTask.java"
public class org.apache.velocity.anakia.AnakiaTask extends org.apache.tools.ant.taskdefs.MatchingTask {
org.jdom.input.SAXBuilder builder;
java.io.File baseDir;
public org.apache.velocity.anakia.AnakiaTask();
public void setBasedir(java.io.File);
public void setDestdir(java.io.File);
public void setExtension(java.lang.String);
public void setStyle(java.lang.String);
public void setProjectFile(java.lang.String);
public void setTemplatePath(java.io.File);
public void setVelocityPropertiesFile(java.io.File);
public void setLastModifiedCheck(java.lang.String);
public void execute() throws org.apache.tools.ant.BuildException;
public org.apache.velocity.anakia.AnakiaTask$Context createContext();
}
两个关键事实:其一,AnakiaTask 继承自 org.apache.tools.ant.taskdefs.MatchingTask,因此 Ant 通用的 includes/excludes 模式过滤属性对它可用(继承方法不出现在 javap 列表里,实测验证见 5.6 节);其二,setLastModifiedCheck 收 java.lang.String(内部按 false/no/off 等价解析),XML 里写布尔语义字符串即可。
修改方式即编辑构建文件中 <anakia> 元素的 XML 属性,保存后下次运行立即生效——Anakia 无独立进程、无端口、无常驻状态,唯一的外部配置文件是可选的 velocity.properties(见 5.4 节)。
四、参数总览
<anakia> 属性总表(默认值均经字节码或运行实测确认):
| 属性 | 类型 | 默认值 | 作用 | 必填 |
|---|---|---|---|---|
| basedir | File(路径) | 无 | XML 源目录,递归扫描其中 .xml 文件 | Yes(实测缺省会直接抛错停止) |
| destdir | File(路径) | 无 | HTML 输出目录,不存在时自动创建 | Yes(实测缺失报 “destdir attribute must be set!”) |
| extension | String | .html | 输出文件扩展名 | No |
| style | String | 无 | Velocity 模板名,相对 templatePath 解析 | Yes(实测缺失报 “style attribute must be set!”) |
| templatePath | File(路径) | 无 | Velocity 文件资源加载器根目录(模板查找基准) | 与 velocityPropertiesFile 二选一 |
| velocityPropertiesFile | File(路径) | 当前目录下 velocity.properties | Velocity 引擎属性文件(编码、日志等) | No |
| projectFile | String | 无 | 项目导航文件(相对 basedir 解析),注入 $project 上下文 | No |
| lastModifiedCheck | String | true | 增量检查:源文件/模板/导航文件均不比产物新时跳过转换 | No |
| includes | String(Ant 模式) | 无 | 参与转换的文件模式(继承自 MatchingTask) | No |
| excludes | String(Ant 模式) | 无 | 排除的文件模式(继承自 MatchingTask) | No |
要点:basedir 与 destdir 是唯一必填对;style + templatePath 组成模板定位机制;lastModifiedCheck 默认开启(构造器置 true,字节码实测);includes/excludes 不是 AnakiaTask 自己的方法,来自 MatchingTask 基类。
五、核心参数分类详解
5.1 输入输出:basedir、destdir、extension
basedir 下的 .xml 文件递归扫描,子目录结构在 destdir 下原样重建,文件名保持不变、仅按 extension 换扩展名。实测工程源目录含子目录 internal/:
cd /opt/anakia-demo && rm -rf docs && ant
回显(实测):
Buildfile: /opt/anakia-demo/build.xml
docs:
[mkdir] Created dir: /opt/anakia-demo/docs
[anakia] Transforming into: /opt/anakia-demo/docs
[anakia] Input: index.xml
[anakia] Output: /opt/anakia-demo/docs/index.html
[anakia] Input: internal/secret.xml
[anakia] Output: /opt/anakia-demo/docs/internal/secret.html
[anakia] Input: notes.xml
[anakia] Output: /opt/anakia-demo/docs/notes.html
BUILD SUCCESSFUL
Total time: 0 seconds
Input:/Output: 行显示相对 basedir 的路径(internal/secret.xml → docs/internal/secret.html),目录层级被完整保留。extension 改为 .htm、.txt 等任意后缀时输出文件名随之变化——Anakia 本质是「XML 转任意文本」,HTML 只是默认目标格式。
5.2 模板定位:style 与 templatePath
templatePath 是 Velocity 文件资源加载器的根目录,style 是相对该根目录的模板名。实测传绝对路径会直接失败(style="/opt/anakia-demo/styles/site.vsl"):
[anakia] Transforming into: /opt/anakia-demo/docs
[anakia] SEVERE: ResourceManager : unable to find resource '/opt/anakia-demo/styles/site.vsl' in any resource loader.
BUILD FAILED
/opt/anakia-demo/build.xml:22: org.apache.velocity.exception.ResourceNotFoundException: Unable to find resource '/opt/anakia-demo/styles/site.vsl'
原因:加载器把绝对路径当相对名拼到 templatePath 之下。正确写法(本工程默认):
<anakia basedir="${docs.src}" destdir="${docs.dest}"
extension=".html"
style="site.vsl"
templatePath="${basedir}/styles"
lastModifiedCheck="true"/>
style 支持 sub/site.vsl 这类相对子路径(即模板可按目录组织在 templatePath 之下)。templatePath 同时决定了 Velocity 对模板的修改检测基准,模板文件更新后无需重启任何进程(每次 ant 都是全新 JVM)。
5.3 增量检查:lastModifiedCheck
字节码实测确认默认值与跳过条件:构造器里 lastModifiedCheck = true;setLastModifiedCheck(String) 仅对 false/no/off(忽略大小写)置 false。跳过判定比较三方时间戳——源 XML、模板(字节码字段 styleSheetLastModified)、导航文件(projectFileLastModified),任一比产物新就重新转换:
65: aload_0
66: getfield #9 // Field lastModifiedCheck:Z
69: ifeq 120
72: aload 4
74: invokevirtual #54 // Method java/io/File.lastModified:()J
77: aload_3
78: invokevirtual #54 // Method java/io/File.lastModified:()J
81: lcmp
82: ifgt 120
85: aload_0
86: getfield #5 // Field styleSheetLastModified:J
89: aload_3
90: invokevirtual #54 // Method java/io/File.lastModified:()J
93: lcmp
94: ifgt 120
行为实测一:源未变化时二次运行,全部跳过(只输出 Transforming into:,无任何 Input:/Output: 行):
Buildfile: /opt/anakia-demo/build.xml
docs:
[anakia] Transforming into: /opt/anakia-demo/docs
BUILD SUCCESSFUL
Total time: 0 seconds
行为实测二:只改模板(site.vsl 内容更新),不改任何 XML——实测 3 个文件全部重新转换:
sed -i 's|Velocity 1.7<|Velocity 1.7 v2<|' styles/site.vsl && ant 2>&1 | grep -c "Input:"
回显(实测):
3
生成文件尾行随之更新为 Generated by Apache Anakia / Velocity 1.7 v2,确认模板时间戳参与增量判定。行为实测三:lastModifiedCheck="false" 时源未变化也全量重转(实测 3 条 Input: 行重现),适合 CI 里需要产物时间戳严格一致的场景。生产站点建议保持默认 true,把全量重转留给 CI 清理 destdir 后执行。
5.4 引擎配置:velocityPropertiesFile 与编码
Anakia 内部为每次构建实例化一个 VelocityEngine,velocityPropertiesFile 指向的属性文件是调整引擎行为的入口(输出/输入编码、资源加载器、日志等)。字节码实测默认查找逻辑:未显式指定时,默认 new File("velocity.properties")——即 ant 运行时当前目录下的同名文件;该文件不存在且未设 templatePath 时直接报错:
No template path and could not locate velocity.properties file:
显式指定的推荐写法(绝对定位、不依赖当前目录):
velocityPropertiesFile="${basedir}/styles/velocity.properties"
styles/velocity.properties 实测内容(也是中文站点的关键配置):
output.encoding=UTF-8
input.encoding=UTF-8
编码实测全过程:未配置时(系统 file.encoding = ANSI_X3.4-1968),生成 HTML 中文全部为 ?;即使通过 ANT_OPTS 把 -Dfile.encoding=UTF-8 传进 JVM 也无效——因为 Anakia 写文件时读取的是 Velocity 的 output.encoding 属性,缺省回退 ISO-8859-1。配置 output.encoding=UTF-8 后重跑,HTML 中文完整(实测回显见 7.2 节)。input.encoding 影响 .vsl 模板文件本身的读取编码,模板含中文注释时必须与文件实际编码一致。
5.5 项目导航:projectFile 与 $project 上下文
projectFile 指向一个导航 XML(惯例名 navigation.xml),解析结果以 $project 注入模板上下文,用于渲染统一菜单/站点名。它相对 basedir 解析,不是相对工程根目录——实测传绝对路径时的告警(不中断构建):
[anakia] Project file is defined, but could not be located: /opt/anakia-demo/src/xml/opt/anakia-demo/navigation.xml
路径被按「basedir + projectFile 字符串」拼接(src/xml/ 下拼出了 src/xml/opt/...)。正确做法:把 navigation.xml 放进 basedir(src/xml/)并写相对名:
projectFile="navigation.xml"
实测导航文件(位于 src/xml/navigation.xml):
<?xml version="1.0" encoding="UTF-8"?>
<project name="anakia-demo">
<title>Anakia Demo Site</title>
<menu>
<item name="Home" href="index.html"/>
<item name="Notes" href="notes.html"/>
</menu>
</project>
模板加一行 <p>site: $project.getChild("title").getTextTrim()</p> 后的生成结果(实测):
<h1>Apache Anakia 快速上手</h1>
<p>site: Anakia Demo Site</p>
注意:navigation.xml 本身也是 basedir 下的 .xml,会被一并转换成 navigation.html(实测确认);不需要它出现在产物里时用 excludes 排除,见 5.6。
5.6 文件过滤:includes 与 excludes(继承自 MatchingTask)
MatchingTask 是 Ant 内建的模式过滤基类,includes/excludes 接收标准 Ant 路径模式(逗号分隔,支持 ** 递归)。实测排除导航文件:
<anakia basedir="${docs.src}" destdir="${docs.dest}"
extension=".html"
style="site.vsl"
templatePath="${basedir}/styles"
velocityPropertiesFile="${basedir}/styles/velocity.properties"
projectFile="navigation.xml"
lastModifiedCheck="true"
excludes="navigation.xml"/>
cd /opt/anakia-demo && rm -rf docs && ant && ls docs
回显(实测):
Buildfile: /opt/anakia-demo/build.xml
docs:
[mkdir] Created dir: /opt/anakia-demo/docs
[anakia] Transforming into: /opt/anakia-demo/docs
[anakia] Input: index.xml
[anakia] Output: /opt/anakia-demo/docs/index.html
[anakia] Input: internal/secret.xml
[anakia] Output: /opt/anakia-demo/docs/internal/secret.html
[anakia] Input: notes.xml
[anakia] Output: /opt/anakia-demo/docs/notes.html
BUILD SUCCESSFUL
Total time: 0 seconds
index.html
internal
notes.html
产物中已无 navigation.html,而 $project 导航功能不受影响(projectFile 解析独立于转换扫描)。includes 同机制:如 includes="**/*.xml" 之外的草稿目录可用 excludes="internal/**" 整目录排除(子目录模式同样生效,实测 internal/secret.xml 即靠目录递归进入处理的)。
六、模板上下文变量与工具类
Anakia 为每个源文件构建的 Velocity 上下文暴露以下键(createContext() 字节码常量实测提取):
| 键 | 值类型 | 用途 | 实测 |
|---|---|---|---|
| root | org.jdom.Element | 当前文档根元素(字节码:Document.getRootElement()) | 模板遍历 $root.getChildren("section") 全程使用 |
| project | org.jdom.Document | projectFile 解析出的导航文档 | 5.5 节实测 |
| xpath | XPathTool | XPath 查询工具,applyTo(xpath, Document/Element/List) 返回 NodeList | 本节实测 |
| escape | Escape | HTML 实体转义,Escape.getText(String)(javap 实测签名) | API 实测 |
| treeWalk | TreeWalker | 元素树遍历器 | 字节码常量 |
| relativePath | String | 当前源文件相对 basedir 的路径(渲染相对链接用) | 字节码常量 |
| date | Date | 转换时间戳 | 字节码常量 |
| xmlout | OutputWrapper | 输出包装器 | 字节码常量 |
XPathTool API(javap 实测):
public class org.apache.velocity.anakia.XPathTool {
public org.apache.velocity.anakia.XPathTool();
public org.apache.velocity.anakia.NodeList applyTo(java.lang.String, org.jdom.Document);
public org.apache.velocity.anakia.NodeList applyTo(java.lang.String, org.jdom.Element);
public org.apache.velocity.anakia.NodeList applyTo(java.lang.String, java.util.List);
}
$xpath 运行时实测——模板加一行 <p>xpath count: $xpath.applyTo("section", $root).size()</p>,缺依赖时真实报错:
[anakia] org.apache.velocity.exception.MethodInvocationException: Invocation of method 'applyTo' in class org.apache.velocity.anakia.XPathTool threw exception java.lang.NoClassDefFoundError: com/werken/xpath/XPath at site.vsl[line 15, column 32]
补齐依赖链后(werken-xpath-0.9.4.jar 75859 字节 + saxpath-1.0-FCS.jar 23563 字节 + antlr-2.7.2.jar 358273 字节,均 Maven 中央仓库,sha256 实测:a26c9554…c7ccd7、2724f32d…003f3a1、2a532069…84dc0f4d4),再次运行成功:
<p>xpath count: 2</p>
(2 即 index.xml 里 <section> 的个数。)结论:基础四件套足够 90% 的站点模板;只有模板真的使用 $xpath 时才需要追加这三个历史 jar。NodeList 实现了 java.util.List(javap 实测),模板里可直接 .size()、#foreach 遍历、下标访问。
七、生产配置模板
适用场景:文档站点持续构建(源 XML 与模板进版本库,构建机任意规模即可,Anakia 无常驻进程)。目录布局与完整构建文件:
site-project/
├── build.xml
├── styles/
│ ├── site.vsl
│ └── velocity.properties
└── src/xml/
├── navigation.xml # 导航(projectFile 用)
├── index.xml
└── draft/ # 草稿目录(excludes 排除)
<?xml version="1.0" encoding="UTF-8"?>
<project name="site" default="docs" basedir=".">
<property name="docs.src" location="${basedir}/src/xml"/>
<property name="docs.dest" location="${basedir}/docs"/>
<taskdef name="anakia" classname="org.apache.velocity.anakia.AnakiaTask">
<classpath>
<pathelement location="/opt/anakia-lib/velocity-1.7.jar"/>
<pathelement location="/opt/anakia-lib/commons-lang-2.6.jar"/>
<pathelement location="/opt/anakia-lib/commons-collections-3.2.2.jar"/>
<pathelement location="/opt/anakia-lib/jdom-1.1.3.jar"/>
</classpath>
</taskdef>
<target name="docs" description="Build site with Anakia">
<mkdir dir="${docs.dest}"/>
<anakia basedir="${docs.src}" destdir="${docs.dest}"
extension=".html"
style="site.vsl"
templatePath="${basedir}/styles"
velocityPropertiesFile="${basedir}/styles/velocity.properties"
projectFile="navigation.xml"
lastModifiedCheck="true"
excludes="navigation.xml"/>
</target>
<target name="clean">
<delete dir="${docs.dest}"/>
</target>
</project>
逐项说明:velocityPropertiesFile 显式指定避免「当前目录侥幸」;output.encoding=UTF-8/input.encoding=UTF-8 写进 styles/velocity.properties 支撑中文站点;projectFile="navigation.xml" 放 basedir 内并靠 excludes 阻止它被转成 HTML;lastModifiedCheck="true" 保持默认增量;配套 clean target 供 CI 全量重建。配合环境变量 ANT_OPTS="-Xms256m -Xmx512m" 控制构建 JVM 堆(验证方式见《Ubuntu 22.04 安装 Apache Anakia 指南》4.2 节)。该结构在本环境实测可运行(各属性回显见第五章)。
八、常见问题(FAQ)
Q1:转换中途某文件渲染失败,为什么 BUILD SUCCESSFUL?
AnakiaTask 对模板解析/加载失败会直接 BUILD FAILED(实测 ParseErrorException 即如此),但对单个文件的渲染期异常只记录 Error:/Error: 堆栈后继续处理其余文件,构建仍显示 BUILD SUCCESSFUL。实测 $xpath 缺 jar 时该源文件没有产出 HTML,构建却成功。排查习惯:CI 里对 [anakia] Error: 或 SEVERE: 关键字做日志断言,或比对 Input: 与 Output: 行数是否一致。
Q2:lastModifiedCheck 关掉能解决「改了模板但产物没更新」吗?
不需要关。实测模板时间戳参与增量判定(styleSheetLastModified 与产物比较),改模板后下一次 ant 会自动重转全部引用它的文件(5.3 节行为实测二)。只有当构建系统时间戳异常(如代码仓库检出时间早于产物)时才需要 lastModifiedCheck="false" 或先 clean。
Q3:velocity.properties 放哪里最稳?
用 velocityPropertiesFile 属性写绝对/工程相对路径。默认查找是 ant 运行时当前目录下的 velocity.properties,换个目录执行 ant 就失效;且无 templatePath 时缺文件直接报错(5.4 节字节码与报错实测)。
Q4:$xpath 报 NoClassDefFoundError: com/werken/xpath/XPath 怎么补依赖?
依次补 werken-xpath-0.9.4、saxpath-1.0-FCS、antlr-2.7.2 三个 jar 到 taskdef classpath(实测报错链:缺 werken → 补后再报 antlr/TokenStreamException → 补 antlr 后成功,回显见第六章)。不用 $xpath 就不用补。
九、参考链接
- Anakia 官方文档(Velocity 引擎站点内):https://velocity.apache.org/engine/devel/anakia.html
- Velocity 1.7 官方手册(VTL 语法):https://velocity.apache.org/engine/1.7/user-guide.html
- Ant MatchingTask 模式过滤:https://ant.apache.org/manual/dirtasks.html
- JDOM 1.1.3 API:https://jdom.org/docs/apidocs/1.1.3/
- Ant taskdef 任务手册:https://ant.apache.org/manual/Tasks/taskdef.html
- Maven 中央仓库 velocity 1.7:https://repo1.maven.org/maven2/org/apache/velocity/velocity/1.7/