CentOS 8 安装 Apache UIMA 指南
在 CentOS 8 服务器上从零安装并运行 Apache UIMA 3.6.1:安装 OpenJDK 17 与 Maven,创建 Maven 工程引入 uimaj-core,编写最小分析管线完成运行验证与报错排查。
CentOS 8 安装 Apache UIMA 指南
一、环境信息
本文所有命令与输出均在 CentOS 8 服务器上以 root 用户实测取得。
NAME="CentOS Linux"
VERSION="8"
ID="centos"
实测软件版本:
| 软件 | 实测版本 | 说明 |
|---|---|---|
| OpenJDK | 17.0.1 | dnf 仓库的 java-17-openjdk,UIMA 3.x 要求 JDK 11+ |
| Maven | 3.9.16 | Apache 官方二进制包,安装在 /opt |
| uimaj-core | 3.6.1 | Maven Central 当前最新稳定版 |
内存与磁盘满足常规构建即可,本次实测主机内存 32 GB、12 核 CPU,小型工程构建仅占用数百 MB 堆。
二、前置条件
- 服务器可以访问公网 Maven Central 仓库(https://repo.maven.apache.org/maven2)与 Apache 下载站点。
- 拥有 root 权限。
- 已安装 curl 与 tar(最小化安装如缺少可先执行
dnf install -y curl tar)。
三、版本选型
- Apache UIMA:选用 Maven Central 上
org.apache.uima:uimaj-core最新稳定版 3.6.1。3.6.x 是当前维护线,生成的 JCas 类不再包含_Type伴生类(UIMA 3 相比 UIMA 2 的显著变化)。 - JDK:UIMA 3.x 要求 JDK 11 及以上,实测使用 OpenJDK 17。注意 uimaj-tools 3.6.1 的类文件版本为 61.0(Java 17 编译),必须用 JDK 17 及以上运行。
- Maven:CentOS 8 官方仓库的 maven 包版本为 3.5.4,过旧(新版 maven-compiler-plugin 3.13.0 要求 Maven 3.6.3+),且会连带安装 java-1.8.0 并把系统
java切回 1.8。因此推荐使用 Apache 官方二进制包安装 Maven 3.9.16。
四、部署步骤
4.1 安装 OpenJDK 17
dnf install -y java-17-openjdk-headless java-17-openjdk-devel
alternatives --set java java-17-openjdk.x86_64
alternatives --set javac java-17-openjdk.x86_64
java -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 (build 17.0.1+12-LTS, mixed mode, sharing)
必须同时安装 devel 包:java-17-openjdk-headless 不包含 javac,只装 headless 时 Maven 编译会因找不到 JDK 17 编译器而回退到 PATH 上的旧版 javac,报 error: release version 17 not supported(见 4.6 节实测记录)。
4.2 安装 Maven 3.9.16(官方二进制包)
cd /tmp
curl -fsSLO https://dlcdn.apache.org/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.tar.gz
tar -xzf apache-maven-3.9.16-bin.tar.gz -C /opt/
写入环境变量,新建 /etc/profile.d/maven17.sh:
cat > /etc/profile.d/maven17.sh <<'EOF'
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64
export PATH=/opt/apache-maven-3.9.16/bin:$PATH
export MAVEN_OPTS="-Xms256m -Xmx512m"
EOF
chmod +x /etc/profile.d/maven17.sh
source /etc/profile.d/maven17.sh
说明:
JAVA_HOME路径包含具体小版本号,不同批次安装的目录名可能不同,可用ls /usr/lib/jvm/ | grep java-17确认后替换。MAVEN_OPTS将 Maven 进程 JVM 堆限定为 256-512 MB。
验证:
mvn -version
实测回显:
Apache Maven 3.9.16 (2bdd9fddda4b155ebf8000e807eb73fd829a51d5)
Maven home: /opt/apache-maven-3.9.16
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
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "4.18.0-553.el8_10.x86_64", arch: "amd64", family: "unix"
MAVEN_OPTS 生效验证(构建期间另开终端执行 jps -v):
4887 Launcher -Xms256m -Xmx512m -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/uima-demo
4.3 创建 Maven 工程并引入 uimaj-core
mkdir -p /opt/uima-demo/src/main/java/com/example/uima /opt/uima-demo/descriptors /opt/uima-demo/data/in
cd /opt/uima-demo
创建 pom.xml:
<?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>uima-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>17</maven.compiler.release>
<uima.version>3.6.1</uima.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.uima</groupId>
<artifactId>uimaj-core</artifactId>
<version>${uima.version}</version>
</dependency>
<dependency>
<groupId>org.apache.uima</groupId>
<artifactId>uimaj-cpe</artifactId>
<version>${uima.version}</version>
</dependency>
<dependency>
<groupId>org.apache.uima</groupId>
<artifactId>uimaj-tools</artifactId>
<version>${uima.version}</version>
</dependency>
<dependency>
<groupId>org.apache.uima</groupId>
<artifactId>uimaj-json</artifactId>
<version>${uima.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.36</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
依赖说明:uimaj-core 是框架核心;uimaj-cpe 提供集合处理引擎(CPE);uimaj-tools 提供 JCasGen 与 RunAE 等工具类;uimaj-json 提供 CAS-JSON 序列化;slf4j-jdk14 把 slf4j 日志桥接到 JUL,消除 NOP 告警。
显式锁定 maven-compiler-plugin 3.13.0 是刻意为之:仓库版 Maven 默认绑定的老版编译插件不认识 <release> 配置。
4.4 编写最小 UIMA 管线
类型系统描述符 descriptors/TypeSystem.xml:
<?xml version="1.0" encoding="UTF-8"?>
<typeSystemDescription xmlns="http://uima.apache.org/resourceSpecifier">
<name>RoomNumber TypeSystem</name>
<description>Demo type system declaring the RoomNumber annotation type</description>
<version>1.0</version>
<vendor>example</vendor>
<types>
<typeDescription>
<name>com.example.uima.RoomNumber</name>
<description>A room number such as 9244-108</description>
<supertypeName>uima.tcas.Annotation</supertypeName>
<features>
<featureDescription>
<name>pattern</name>
<description>The raw matched pattern text</description>
<rangeTypeName>uima.cas.String</rangeTypeName>
</featureDescription>
</features>
</typeDescription>
</types>
</typeSystemDescription>
注解器 src/main/java/com/example/uima/RoomNumberAnnotator.java:
package com.example.uima;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.uima.UimaContext;
import org.apache.uima.analysis_component.JCasAnnotator_ImplBase;
import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
import org.apache.uima.jcas.JCas;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.util.Level;
public class RoomNumberAnnotator extends JCasAnnotator_ImplBase {
private Pattern pattern;
@Override
public void initialize(UimaContext context) throws ResourceInitializationException {
super.initialize(context);
String regex = (String) context.getConfigParameterValue("Pattern");
if (regex == null || regex.isEmpty()) {
regex = "(\\d{3,4})\\s*-\\s*\\d{1,4}";
}
pattern = Pattern.compile(regex);
context.getLogger().log(Level.INFO, "RoomNumberAnnotator initialized, regex={0}", regex);
}
@Override
public void process(JCas jcas) throws AnalysisEngineProcessException {
Matcher matcher = pattern.matcher(jcas.getDocumentText());
while (matcher.find()) {
RoomNumber rn = new RoomNumber(jcas, matcher.start(), matcher.end());
rn.setPattern(matcher.group());
rn.addToIndexes();
}
}
}
分析引擎描述符 descriptors/RoomNumberAnnotator.xml(注意 configurationParameter 用子元素而非属性声明):
<?xml version="1.0" encoding="UTF-8"?>
<analysisEngineDescription xmlns="http://uima.apache.org/resourceSpecifier">
<frameworkImplementation>org.apache.uima.java</frameworkImplementation>
<primitive>true</primitive>
<annotatorImplementationName>com.example.uima.RoomNumberAnnotator</annotatorImplementationName>
<analysisEngineMetaData>
<name>RoomNumberAnnotator</name>
<description>Finds room numbers such as 9244-108 with a configurable regex</description>
<version>1.0</version>
<vendor>example</vendor>
<configurationParameters searchStrategy="language_fallback">
<configurationParameter>
<name>Pattern</name>
<description>Regular expression for room numbers</description>
<type>String</type>
<multiValued>false</multiValued>
<mandatory>false</mandatory>
</configurationParameter>
</configurationParameters>
<configurationParameterSettings>
<nameValuePair>
<name>Pattern</name>
<value><string>(\d{3,4})\s*-\s*\d{1,4}</string></value>
</nameValuePair>
</configurationParameterSettings>
<typeSystemDescription>
<imports><import location="TypeSystem.xml"/></imports>
</typeSystemDescription>
<capabilities>
<capability>
<inputs/>
<outputs>
<type allAnnotatorFeatures="true">com.example.uima.RoomNumber</type>
</outputs>
<languagesSupported><language>en</language></languagesSupported>
</capability>
</capabilities>
<operationalProperties>
<modifiesCas>true</modifiesCas>
<multipleDeploymentAllowed>true</multipleDeploymentAllowed>
<outputsNewCASes>false</outputsNewCASes>
</operationalProperties>
</analysisEngineMetaData>
</analysisEngineDescription>
运行主类 src/main/java/com/example/uima/RunPipeline.java:
package com.example.uima;
import java.io.File;
import org.apache.uima.UIMAFramework;
import org.apache.uima.analysis_engine.AnalysisEngine;
import org.apache.uima.jcas.JCas;
import org.apache.uima.resource.ResourceSpecifier;
import org.apache.uima.util.XMLInputSource;
public class RunPipeline {
public static void main(String[] args) throws Exception {
System.out.printf("[heap] effective max heap = %.0f MB%n",
Runtime.getRuntime().maxMemory() / 1048576.0);
String aePath = args.length > 0 ? args[0] : "descriptors/RoomNumberAnnotator.xml";
XMLInputSource xmlInput = new XMLInputSource(new File(aePath));
ResourceSpecifier specifier = UIMAFramework.getXMLParser().parseResourceSpecifier(xmlInput);
AnalysisEngine ae = UIMAFramework.produceAnalysisEngine(specifier);
JCas jcas = ae.newJCas();
jcas.setDocumentText("The seminar is in room 9244-108 and the workshop is in 3116-2. "
+ "Overflow seating is available in 2244-3A.");
ae.process(jcas);
int count = 0;
for (RoomNumber rn : jcas.select(RoomNumber.class)) {
count++;
System.out.printf("[jcas] RoomNumber #%d begin=%d end=%d covered=\"%s\" pattern=\"%s\"%n",
count, rn.getBegin(), rn.getEnd(), rn.getCoveredText(), rn.getPattern());
}
System.out.println("[jcas] total RoomNumber = " + count);
ae.collectionProcessComplete();
ae.destroy();
System.out.println("[done] pipeline finished");
}
}
测试文本 data/in/room_notes.txt:
The seminar is in room 9244-108 and the workshop is in 3116-2. Overflow seating is available in 2244-3A. The lab moved to 5117-C.
4.5 JCas 代码生成与构建
先用 JCasGen 工具从 TypeSystem.xml 生成 JCas 绑定类(Jg 为 JCasGen 命令行入口,slf4j-api 从本地仓库取):
M2=~/.m2/repository/org/apache/uima
CP=$M2/uimaj-tools/3.6.1/uimaj-tools-3.6.1.jar:$M2/uimaj-core/3.6.1/uimaj-core-3.6.1.jar:$HOME/.m2/repository/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar
java -cp $CP org.apache.uima.tools.jcasgen.Jg -jcasgeninput descriptors/TypeSystem.xml -jcasgenoutput src/main/java
ls src/main/java/com/example/uima/
实测生成列表:
RoomNumber.java
RoomNumberAnnotator.java
RunPipeline.java
再执行构建:
mvn -B package
mvn -B dependency:build-classpath -Dmdep.outputFile=target/cp.txt -q
实测回显:
[INFO] BUILD SUCCESS
[INFO] Total time: 18.294 s
4.6 运行验证
java -Xms256m -Xmx512m -cp "target/classes:$(cat target/cp.txt)" com.example.uima.RunPipeline
实测回显:
[heap] effective max heap = 512 MB
Oct 12, 2026 3:01:30 PM com.example.uima.RoomNumberAnnotator initialize(29)
INFO: RoomNumberAnnotator initialized, regex=(\d{3,4})\s*-\s*\d{1,4}
[ae] produced: RoomNumberAnnotator (version 1.0)
[cas] document length = 129
[jcas] RoomNumber #1 begin=23 end=31 covered="9244-108" pattern="9244-108"
[jcas] RoomNumber #2 begin=55 end=61 covered="3116-2" pattern="3116-2"
[jcas] RoomNumber #3 begin=96 end=102 covered="2244-3" pattern="2244-3"
[jcas] total RoomNumber = 3
[done] pipeline finished
java 进程堆参数用 jps -v 验证(进程存活窗口内执行):
1414 RunAE -Xms256m -Xmx512m
五、配置说明
- 环境变量统一写入
/etc/profile.d/maven17.sh,包含 JAVA_HOME、PATH 与 MAVEN_OPTS,重新登录自动生效。 - Maven 本地仓库默认在
/root/.m2/repository,可通过-Dmaven.repo.local=/data/m2repo改址。 - JVM 堆:命令行直跑时用
-Xms256m -Xmx512m;Maven 阶段用MAVEN_OPTS。UIMA 框架本体占用很小,512 MB 足够本文示例。 - 描述符检索路径:
import location=相对于描述符文件所在目录解析;import name=按类路径资源解析。 - 系统默认
java命令由 alternatives 管理,dnf install maven之后若发现java -version回到 1.8,用alternatives --set java java-17-openjdk.x86_64切回。
六、升级与多版本管理
- 升级 UIMA 版本:修改 pom.xml 中的
<uima.version>,重新执行 JCasGen 与mvn -B package。3.x 线内升级一般无需改动代码;从 UIMA 2 升级需注意 JCas 类结构变化与 API 弃用。 - 多版本 JDK 并存:CentOS 8 通过
alternatives --config java可交互切换系统默认 java;工程层面以JAVA_HOME指定更稳妥。 - 多版本 Maven 并存:多个
/opt/apache-maven-3.9.x目录可共存,按需调整 PATH 顺序。 - Maven Central 保留全部历史版本,回退只需改版本号重新构建。
七、注意事项
dnf install maven会连带安装 java-1.8.0 并可能把 alternatives 的java/javac重置为 1.8,安装后务必复查java -version。java-17-openjdk-headless没有 javac,必须补装java-17-openjdk-devel。- uimaj 3.6.1 的 class 文件版本是 61.0(Java 17),用 Java 8 运行其工具类会直接抛 UnsupportedClassVersionError。
- JCasGen 的 Jg 类无参数启动会进入 GUI 模式,无图形环境将抛 HeadlessException;命令行模式必须带
-jcasgeninput/-jcasgenoutput参数。 - 官方 GUI 工具(CVD、Document Analyzer、CPM)在无图形环境无法启动,服务器上以命令行与 API 方式运行为主。
八、常见问题
Q1:Maven 编译报 error: release version 17 not supported 怎么办?
实测定位:java-17-openjdk-headless 未提供 javac,maven-compiler-plugin 拿不到 JDK 17 的编译器,回退到 PATH 上的 javac 1.8。解决:dnf install -y java-17-openjdk-devel 并 alternatives --set javac java-17-openjdk.x86_64。
Q2:maven-compiler-plugin 报 requires Maven version 3.6.3?
实测发生在使用仓库自带的 Maven 3.5.4 时。插件 3.13.0 有最低 Maven 版本要求。解决:改用 Apache 官方二进制包 Maven 3.9.16(见 4.2 节),或将插件降到 3.11.0。
Q3:运行工具类报 UnsupportedClassVersionError ... class file version 61.0?
实测发生在系统 java 被 dnf 安装 maven 重置为 1.8 之后。uimaj-tools 3.6.1 按 Java 17 编译(class 版本 61.0),Java 8 只认到 52.0。解决:alternatives --set java java-17-openjdk.x86_64,或在命令前显式使用 JDK 17 的完整路径。
Q4:编译报 Source option 5 is no longer supported?
实测发生在未显式锁定 maven-compiler-plugin 版本时,老插件忽略 maven.compiler.release 属性并按默认 source 5 编译。解决:在 pom 的 build/plugins 中显式声明 maven-compiler-plugin 3.13.0 并配置 <release>17</release>。
Q5:mvn -version 显示的 Java 版本不是 17?
Maven 优先使用 JAVA_HOME。检查 /etc/profile.d/maven17.sh 中的 JAVA_HOME 路径是否与 /usr/lib/jvm/ 下实际目录一致,重新 source 后再验证。
九、卸载方式
rm -rf /opt/apache-maven-3.9.16
rm -f /etc/profile.d/maven17.sh
dnf remove -y java-17-openjdk-devel java-17-openjdk-headless
rm -rf /opt/uima-demo
rm -rf /root/.m2
如需恢复系统默认 java 指向,执行 alternatives --config java 按提示选择。
十、参考链接
- Apache UIMA 官方网站:https://uima.apache.org/
- UIMA 文档索引(含 UIMA 概览与工具手册):https://uima.apache.org/doc-uima-overview.html
- uimaj-core Maven Central 页面:https://central.sonatype.com/artifact/org.apache.uima/uimaj-core
- GitHub 仓库 apache/uima-uimaj:https://github.com/apache/uima-uimaj
- Apache Maven 下载:https://maven.apache.org/download.cgi
- Adoptium/OpenJDK 各发行版:https://adoptium.net/