Apache Anakia 配置参数详解

Apache Anakia(velocity-1.7)配置参数详解:AnakiaTask 各属性逐个实测,含增量检查语义、编码、projectFile 导航、includes/excludes 与模板上下文变量。

Apache AnakiaAnakia2026/10/13

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> 属性总表(默认值均经字节码或运行实测确认):

属性类型默认值作用必填
basedirFile(路径)无XML 源目录,递归扫描其中 .xml 文件Yes(实测缺省会直接抛错停止)
destdirFile(路径)无HTML 输出目录,不存在时自动创建Yes(实测缺失报 “destdir attribute must be set!”)
extensionString.html输出文件扩展名No
styleString无Velocity 模板名,相对 templatePath 解析Yes(实测缺失报 “style attribute must be set!”)
templatePathFile(路径)无Velocity 文件资源加载器根目录(模板查找基准)与 velocityPropertiesFile 二选一
velocityPropertiesFileFile(路径)当前目录下 velocity.propertiesVelocity 引擎属性文件(编码、日志等)No
projectFileString无项目导航文件(相对 basedir 解析),注入 $project 上下文No
lastModifiedCheckStringtrue增量检查:源文件/模板/导航文件均不比产物新时跳过转换No
includesString(Ant 模式)无参与转换的文件模式(继承自 MatchingTask)No
excludesString(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() 字节码常量实测提取):

键值类型用途实测
rootorg.jdom.Element当前文档根元素(字节码:Document.getRootElement())模板遍历 $root.getChildren("section") 全程使用
projectorg.jdom.DocumentprojectFile 解析出的导航文档5.5 节实测
xpathXPathToolXPath 查询工具,applyTo(xpath, Document/Element/List) 返回 NodeList本节实测
escapeEscapeHTML 实体转义,Escape.getText(String)(javap 实测签名)API 实测
treeWalkTreeWalker元素树遍历器字节码常量
relativePathString当前源文件相对 basedir 的路径(渲染相对链接用)字节码常量
dateDate转换时间戳字节码常量
xmloutOutputWrapper输出包装器字节码常量

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 就不用补。

九、参考链接