CentOS 8 安装 Apache Xalan 指南

在 CentOS 8 从零到一安装 Apache Xalan-J 2.7.3:装 OpenJDK 17 与 Maven、建工程引依赖,命令行与 API 双路径实测,含版本选型、升级卸载与常见问题。

Apache XalanApache Xalan2026/10/12

CentOS 8 安装 Apache Xalan 指南

目标:在 CentOS 8 服务器上从零到一安装 Apache Xalan-J(XSLT 处理器,本文以 Java 版 2.7.3 为准),覆盖前置 JDK/Maven、Maven 工程引依赖、命令行 Process 与 Java Transformer 两条转换路径的验证、JVM 堆设置、升级/多版本管理与卸载。本文全部命令与回显已在 CentOS Linux 8.5.2111 + OpenJDK 17.0.1 真实环境逐条实测通过。

一、环境信息

项目值
操作系统CentOS Linux 8.5.2111(x86_64)
软件源BaseOS / AppStream(vault 仓库)
JDKOpenJDK 17.0.1(java-17-openjdk-17.0.1.0.12-2.el8_5)
Maven3.5.4(Red Hat 3.5.4-5)
Xalan-Jxalan:xalan:2.7.3 + xalan:serializer:2.7.3(Maven Central 实测 release 版)
示例工程目录/opt/xalan-demo
管理账号root(密码 q1w2e3r4)

二、前置条件

  1. root 权限(本文全部以 root 执行)。
  2. 服务器可访问 yum 仓库与 Maven 中央仓库 repo.maven.apache.org。
  3. Xalan-J 运行依赖 JVM:需 JDK 8 及以上(本文实测 OpenJDK 17,向下兼容 8/11)。

确认网络与仓库可用:

dnf repoinfo | grep -E "repo-id|baseurl"

✅ 实测回显:

Repo-baseurl       : https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.5.2111/AppStream/x86_64/os/
Repo-baseurl       : https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.5.2111/BaseOS/x86_64/os/

三、版本选型说明

3.1 Xalan-J 版本现状

Xalan-J 通过 Maven Central 分发(CentOS 8 官方仓库无 xalan RPM 包)。实测 https://repo1.maven.org/maven2/xalan/xalan/maven-metadata.xml:

  • 最新 release:2.7.3(lastUpdated 2023-05-19),配套 xalan:serializer:2.7.3;
  • 历史版本线:2.1.0 ~ 2.7.3,其中 2.7.2、2.7.3 为常用稳定版;
  • 选型建议:新部署直接用 2.7.3(含多年累积修复,且对高版本 JDK 兼容性最好)。

3.2 Xalan-J 与 JDK/JAXP 的关系

Xalan-J 是 JAXP(javax.xml.transform)标准接口的一组实现,不是独立进程,而是一组 jar:

jar作用关键类
xalan-2.7.3.jarXSLT 引擎(解释型 + XSLTC 编译型)org.apache.xalan.xslt.Process(命令行)、org.apache.xalan.processor.TransformerFactoryImpl(解释型工厂)、org.apache.xalan.xsltc.trax.TransformerFactoryImpl(XSLTC 工厂)
serializer-2.7.3.jar结果序列化输出org.apache.xml.serializer.*

实测关键点:xalan-2.7.3.jar 内含 META-INF/services/javax.xml.transform.TransformerFactory,内容为 org.apache.xalan.processor.TransformerFactoryImpl。即只要 xalan.jar 出现在 classpath,TransformerFactory.newInstance() 默认就会切到 Xalan 解释型实现;且 xalan:xalan:2.7.3 的 pom 不传递依赖 serializer(实测 dependency:tree 仅显式声明的两个 jar),必须显式引入 serializer,否则运行时报 NoClassDefFoundError: org/apache/xml/serializer/SerializerTrace。

3.3 与 JDK 内置 XSLT 处理器的差异

JDK 自带一份 Xalan 内置 fork(包名 com.sun.org.apache.xalan.internal.*)。实测在无 xalan.jar 的 classpath 下运行 TransformerFactory.newInstance().getClass().getName():

com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl

两者关系与差异:

  • 内置版随 JDK 发布,版本滞后且不可单独升级;Xalan-J 独立 jar 可自行选 2.7.2/2.7.3,随时升级;
  • Xalan-J 提供命令行工具 org.apache.xalan.xslt.Process、XSLTC 独立编译器(把样式表预编译为字节码 translet)、解释型与编译型双引擎可切换;
  • 内置版仅保留 XSLTC 引擎,无命令行 Process、无 translet 预编译分发能力。

四、部署步骤

4.1 安装 OpenJDK 17 与 Maven

dnf install -y java-17-openjdk-devel maven

安装完成后先确认版本(注意:CentOS 8 的坑——默认 java 不会自动切到 17):

java -version

⚠️ 实测回显(安装后默认仍为 1.8):

openjdk version "1.8.0_312"
OpenJDK Runtime Environment (build 1.8.0_312-b07)

用 alternatives 把默认 JVM 切到 17:

alternatives --set java java-17-openjdk.x86_64
alternatives --set javac java-17-openjdk.x86_64
java -version
javac -version

✅ 实测回显:

openjdk version "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Environment 21.9 (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM 21.9 (build 17.0.1+12-LTS, mixed mode, sharing)
javac 17.0.1

4.2 配置 JAVA_HOME 与系统编码

写入 /etc/profile.d/java17.sh,登录与开机自动生效:

cat > /etc/profile.d/java17.sh <<'EOF'
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export LANG=C.UTF-8
EOF

source /etc/profile.d/java17.sh
mvn -version

✅ 实测回显:

Apache Maven 3.5.4 (Red Hat 3.5.4-5)
Maven home: /usr/share/maven
Java version: 17.0.1, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64

💡 LANG=C.UTF-8 必须设置:实测 POSIX 缺省 locale 下裸 javac 按 US-ASCII 读取 UTF-8 源码会报 error: unmappable character (0xE9) for encoding US-ASCII,命令行传中文 -PARAM 也会乱码。详见第七节。

4.3 创建 Maven 工程

mkdir -p /opt/xalan-demo/src/main/java/com/example /opt/xalan-demo/data /opt/xalan-demo/out
cat > /opt/xalan-demo/pom.xml <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>xalan-demo</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>xalan</groupId>
      <artifactId>xalan</artifactId>
      <version>2.7.3</version>
    </dependency>
    <dependency>
      <groupId>xalan</groupId>
      <artifactId>serializer</artifactId>
      <version>2.7.3</version>
    </dependency>
  </dependencies>
</project>
EOF

💡 project.build.sourceEncoding=UTF-8 保证 Maven 编译不受系统 locale 影响(实测同一份含中文注释的源码,Maven 构建成功、裸 javac 报 unmappable character 错误)。

4.4 引入依赖并构建

cd /opt/xalan-demo
mvn -B package
mvn -B dependency:tree | grep -E "xalan|serializer"

✅ 实测回显(dependency:tree 摘要):

[INFO] +- xalan:xalan:jar:2.7.3:compile
[INFO] \- xalan:serializer:jar:2.7.3:compile
[INFO] BUILD SUCCESS

构建产物在 target/classes,本地仓库 jar 位于 /root/.m2/repository/xalan/。

4.5 路径一:命令行 Process 直接变换

先准备样例数据(data/books.xml 与 data/books.xsl 全文见《Apache Xalan 快速入门与集成示例》同款样例,此处给最小可跑版本):

cd /opt/xalan-demo
export CP=/root/.m2/repository/xalan/xalan/2.7.3/xalan-2.7.3.jar:/root/.m2/repository/xalan/serializer/2.7.3/serializer-2.7.3.jar
java -cp $CP org.apache.xalan.xslt.Process -IN data/books.xml -XSL data/books.xsl -OUT out/cli.html
head -7 out/cli.html

⚠️ 实测注意:-OUT 指向的输出目录必须预先存在(上面 4.3 已 mkdir out),否则报 XSLT Error (java.io.FileNotFoundException): out/cli.html (No such file or directory),Process 不会自动创建目录。

✅ 实测回显(out/cli.html 头部):

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>图书清单</title>
</head>
<body>
<h1>图书清单(关键字:编程)</h1>

参数化变换(-PARAM 传入关键字 Java):

java -cp $CP org.apache.xalan.xslt.Process -IN data/books.xml -XSL data/books.xsl -PARAM keyword Java -OUT out/cli-java.html
grep -o "共 [0-9]* 本匹配" out/cli-java.html

✅ 实测回显:

共 2 本匹配

4.6 路径二:Java Transformer API 变换

创建 src/main/java/com/example/XalanDemo.java:

package com.example;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.File;

/**
 * Xalan-J 快速入门:TransformerFactory + Transformer 完成 XSLT 变换。
 * 用法: java com.example.XalanDemo <xml> <xsl> <out> [paramValue]
 */
public class XalanDemo {
    public static void main(String[] args) throws Exception {
        // 1. 打印当前 JAXP 实际加载的 TransformerFactory 实现类
        TransformerFactory factory = TransformerFactory.newInstance();
        System.out.println("TransformerFactory 实现类: " + factory.getClass().getName());

        // 2. 准备输入源与输出目标
        Source xmlSource = new StreamSource(new File(args[0]));
        Source xslSource = new StreamSource(new File(args[1]));
        Result result = new StreamResult(new File(args[2]));

        // 3. 用样式表编译 Transformer,并设置输出属性与外部参数
        Transformer transformer = factory.newTransformer(xslSource);
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        if (args.length > 3) {
            transformer.setParameter("keyword", args[3]);
        }

        // 4. 执行转换
        transformer.transform(xmlSource, result);
        System.out.println("转换完成, 输出文件: " + args[2]);
    }
}

重新打包并运行(默认工厂 + 显式切换 XSLTC 两种方式各跑一次):

mvn -B -q package
java -cp "target/classes:$CP" com.example.XalanDemo data/books.xml data/books.xsl out/api.html
java -Djavax.xml.transform.TransformerFactory=org.apache.xalan.xsltc.trax.TransformerFactoryImpl \
     -cp "target/classes:$CP" com.example.XalanDemo data/books.xml data/books.xsl out/api-xsltc.html

✅ 实测回显:

TransformerFactory 实现类: org.apache.xalan.processor.TransformerFactoryImpl
转换完成, 输出文件: out/api.html
TransformerFactory 实现类: org.apache.xalan.xsltc.trax.TransformerFactoryImpl
转换完成, 输出文件: out/api-xsltc.html

💡 第一行为默认结果:classpath 有 xalan.jar 时,ServiceLoader 依 3.2 节的服务文件选中解释型实现;用 -D 系统属性可无缝切到 XSLTC 编译型。OpenJDK 17 实测两种实现均可正常运行,无需 --add-opens。

五、配置说明

5.1 环境变量与 JVM 堆

修改位置:

  • 全局环境(JAVA_HOME、LANG):/etc/profile.d/java17.sh(4.2 节已建);
  • Maven 构建进程堆:环境变量 MAVEN_OPTS,或写入 /etc/profile.d/maven.sh / ~/.mavenrc;
  • 命令行 java 进程堆:直接在 java 命令后加 -Xms/-Xmx(不读取 MAVEN_OPTS/JAVA_OPTS)。

XSLTC 编译大样式表、转换大文档时建议堆设为 256~512MB:

# Maven 构建进程
MAVEN_OPTS="-Xms256m -Xmx512m" mvn -B clean package

# 命令行变换进程
java -Xms256m -Xmx512m -cp $CP org.apache.xalan.xslt.Process -IN data/books.xml -XSL data/books.xsl -OUT out/cli.html

实测验证:把带堆参数的进程保持存活后抓取回显——

ps -ef | grep [j]ava

✅ 实测回显(CentOS 8,MAVEN_OPTS 生效的 Maven 构建进程):

java -Xms256m -Xmx512m -classpath /usr/share/java/plexus-classworlds.jar -Dclassworlds.conf=/usr/share/maven/bin/m2.conf -Dmaven.home=/usr/share/maven -Dlibrary.jansi.path=/usr/share/maven/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/opt/xalan-demo
jps -v

✅ 实测回显(命令行 java 进程):

1149 MemoryDemo -Xms256m -Xmx512m

程序内 Runtime.getRuntime().maxMemory() 实测打印 JVM 最大堆(maxMemory): 512 MB,与 -Xmx512m 一致。

5.2 JAXP 工厂选择

  • 系统属性方式:-Djavax.xml.transform.TransformerFactory=org.apache.xalan.xsltc.trax.TransformerFactoryImpl(XSLTC)或 org.apache.xalan.processor.TransformerFactoryImpl(解释型),优先级最高;
  • 不设置时:classpath 有 xalan.jar → 解释型实现(ServiceLoader);无 xalan.jar → JDK 内置 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
  • 也可写入 $JAVA_HOME/conf/jaxp.properties(JDK 9+)实现全局默认,无需每个命令加 -D。

5.3 Xalan 自身无配置文件

Xalan-J 没有独立服务进程与配置文件,一切行为由 JAXP 系统属性、命令行参数与代码内 setAttribute/setOutputProperty 控制,无需开机自启服务(因此本文不涉及 systemd 单元管理)。

六、升级与多版本管理

  • 升级:修改 pom 中两个依赖的 <version>(如 2.7.2 → 2.7.3)后重新 mvn -B clean package 即可;2.7.x 系列间 API 兼容,实测 2.7.3 在 OpenJDK 17 正常编译运行;
  • 多版本共存:为不同版本建独立 lib 目录,运行时用 -cp 指定对应目录,例如 /opt/xalan-2.7.2/lib/ 与 /opt/xalan-2.7.3/lib/ 各放一对 xalan-*.jar + serializer-*.jar;
  • 禁止同一 classpath 混放两个版本的 xalan.jar,实测会因 META-INF/services 重复导致工厂选择不确定;
  • 升级后用 java -cp <对应xalan.jar> org.apache.xalan.Version 核对版本号(实测输出 Xalan Java 2.7.3)。

七、注意事项

  1. 系统编码三坑(实测):POSIX locale 下 (a) 裸 javac 报 unmappable character ... US-ASCII,解法 javac -encoding UTF-8 或设 LANG=C.UTF-8;(b) 命令行 -PARAM 传中文值乱码并导致匹配失败,解法同上;(c) Java 程序 System.out 中文变问号,解法同上。
  2. 输出目录:Process 不自动创建 -OUT 父目录,先 mkdir -p out。
  3. serializer 缺失:只放 xalan.jar 运行报 NoClassDefFoundError: org/apache/xml/serializer/SerializerTrace;java -jar 直跑时依赖 jar 必须按 MANIFEST Class-Path: xercesImpl.jar xml-apis.jar serializer.jar 的精确文件名与 xalan.jar 同目录放置(放 serializer-2.7.3.jar 无效,实测须改名 serializer.jar)。
  4. 错误退出路径二次异常:样式表 XML 不合法时,Process 先打印 [Fatal Error] ... SAXParseException(真正原因),随后可能再抛 RuntimeException: ... "format" is null(错误收尾路径的二次异常),定位以第一条为准;-EDUMP 生成的转储文件记录的是二次异常堆栈。
  5. JDK 17 兼容性:实测 Xalan-J 2.7.3 解释型与 XSLTC 引擎、命令行、Maven 构建均正常,无需 --add-opens;如使用 2.7.1 及更早版本遇到模块化访问限制,优先升级到 2.7.3。
  6. alternatives 默认 JVM:dnf install java-17-openjdk-devel 后默认 java 仍是 1.8(实测),必须 alternatives --set 切换,否则 Maven 用错 JDK。

八、常见问题

Q1: 运行时报 java.lang.NoClassDefFoundError: org/apache/xml/serializer/SerializerTrace 怎么办?

答:classpath 缺 serializer。Maven 工程确认显式声明 xalan:serializer:2.7.3(2.7.3 的 xalan pom 不传递该依赖);java -cp 命令确认两个 jar 都在;java -jar 方式确认 serializer.jar 与 xalan.jar 同目录。

Q2: TransformerFactory.newInstance() 拿到的是 com.sun.org.apache.xalan.internal...(JDK 内置),不是独立版 Xalan?

答:说明运行时 classpath 没有 xalan.jar,或被更高优先级的系统属性覆盖。检查 -cp;确认后仍想换实现,加 -Djavax.xml.transform.TransformerFactory=org.apache.xalan.processor.TransformerFactoryImpl(解释型)或 org.apache.xalan.xsltc.trax.TransformerFactoryImpl(XSLTC)。

Q3: 命令行 -PARAM 传中文后转换结果里是问号/乱码?

答:locale 问题。执行 export LANG=C.UTF-8 后重试(实测同一命令在 POSIX locale 下 -PARAM keyword 数据库 乱码、设 LANG 后输出 <h1>图书清单(关键字:数据库)</h1> 正常)。

Q4: javac 编译含中文注释的源码报 114 个 unmappable character 错误?

答:裸 javac 按 US-ASCII 读源码。改用 javac -encoding UTF-8 ...;走 Maven 构建则无此问题(pom 已配 sourceEncoding)。

Q5: Process 报 FileNotFoundException: out/cli.html (No such file or directory)?

答:输出目录不存在,Process 不会自动创建。先 mkdir -p out 再执行。

Q6: org.apache.xalan.xsltc.cmdline.Transform 报 Translet errors:Cannot find class 'data/books.xml'?

答:实测 2.7.3 中该命令实际生效的参数顺序是 XML 文档在前、translet 类名在后(与部分帮助文本相反)。正确用法:java -cp "out/translet:$CP" org.apache.xalan.xsltc.cmdline.Transform data/books.xml BooksTranslet。另注意它失败时退出码仍为 0,脚本判断要靠输出内容。

九、卸载方式

# 1. 删除示例工程与本地部署的 jar 目录
rm -rf /opt/xalan-demo

# 2. 清理 Maven 本地仓库中的 Xalan 组件(可选)
rm -rf /root/.m2/repository/xalan

# 3. 卸载 Maven 与 JDK(如服务器无其他用途;可单独卸载)
dnf remove -y maven
dnf remove -y java-17-openjdk-devel

# 4. 删除环境变量脚本
rm -f /etc/profile.d/java17.sh

💡 若服务器仍需运行其他 Java 应用,只执行第 1、2 步即可;Xalan-J 以 jar 形式随工程分发,删除工程目录与本地仓库缓存即完成卸载。

十、参考链接