Ubuntu 22.04 安装 Apache UIMA 指南
在 Ubuntu 22.04 服务器上从零安装并运行 Apache UIMA 3.6.1:通过 apt 安装 OpenJDK 17 与 Maven,创建工程引入 uimaj-core,编写最小分析管线完成运行验证与报错排查。
Ubuntu 22.04 安装 Apache UIMA 指南
一、环境信息
本文所有命令与输出均在 Ubuntu 22.04 服务器上以 root 用户实测取得。
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
实测软件版本:
| 软件 | 实测版本 | 说明 |
|---|---|---|
| OpenJDK | 17.0.20.1 | apt 仓库 openjdk-17-jdk-headless,UIMA 3.x 要求 JDK 11+ |
| Maven | 3.6.3 | apt 仓库自带版本 |
| uimaj-core | 3.6.1 | Maven Central 当前最新稳定版 |
二、前置条件
- 服务器可以访问公网 Maven Central 仓库(https://repo.maven.apache.org/maven2)。
- 拥有 root 权限。
- apt 源可用(默认软件源即可)。
三、版本选型
- Apache UIMA:选用 Maven Central 上
org.apache.uima:uimaj-core最新稳定版 3.6.1,属于当前 3.6.x 维护线。 - JDK:UIMA 3.x 要求 JDK 11+,实测使用 apt 提供的 OpenJDK 17(17.0.20.1)。
- Maven:Ubuntu 22.04 apt 仓库提供 3.6.3,实测可满足构建;但必须显式锁定 maven-compiler-plugin 版本(见 4.3 节),否则老插件会把 source/target 回退到 5 导致编译失败。如需更新版本可改用 Apache 官方二进制包,本文以 apt 路径为准。
四、部署步骤
4.1 安装 OpenJDK 17 与 Maven
apt-get update
apt-get install -y openjdk-17-jdk-headless maven
验证:
java -version
mvn -version
实测回显:
openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1-22.04-Ubuntu, mixed mode, sharing)
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "6.8.0-138-generic", arch: "amd64", family: "unix"
apt 安装的 Maven 默认即使用 OpenJDK 17 运行,无需额外配置 JAVA_HOME。
4.2 设置 JVM 堆与 MAVEN_OPTS
新建 /etc/profile.d/uima-build.sh,把 Maven 进程堆限定为 256-512 MB:
cat > /etc/profile.d/uima-build.sh <<'EOF'
export MAVEN_OPTS="-Xms256m -Xmx512m"
EOF
chmod +x /etc/profile.d/uima-build.sh
source /etc/profile.d/uima-build.sh
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。两个关键点:显式锁定 maven-compiler-plugin 3.13.0(apt 版 Maven 默认解析到 3.1,不支持 release 17);runtime 引入 slf4j-jdk14 消除 slf4j NOP 告警。
<?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>
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):
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
执行前需先解析一次依赖把 jar 下载到本地仓库:mvn -B dependency:resolve。
生成结果实测:
src/main/java/com/example/uima/RoomNumber.java
再执行构建:
mvn -B package
mvn -B dependency:build-classpath -Dmdep.outputFile=target/cp.txt -q
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 2:46:35 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 验证(进程存活窗口内执行):
7236 RunAE -Xms256m -Xmx512m
五、配置说明
- 环境变量统一写入
/etc/profile.d/uima-build.sh(MAVEN_OPTS),重新登录自动生效。 - Maven 本地仓库默认在
/root/.m2/repository,磁盘紧张时用mvn -Dmaven.repo.local=/data/m2repo ...指定其他位置。 - JVM 堆:Maven 阶段走
MAVEN_OPTS,java 直跑阶段在命令行加-Xms256m -Xmx512m。示例管线实际峰值远低于 512 MB。 - 描述符
import location=相对描述符文件所在目录解析;跨目录共享类型时建议统一放在同一 descriptors 目录。 - 如系统存在多个 JDK,可用
update-alternatives --config java切换默认 java;Maven 则由 JAVA_HOME 决定。
六、升级与多版本管理
- 升级 UIMA:修改 pom.xml 的
<uima.version>后重新执行 JCasGen 与mvn -B package。3.x 线内 API 兼容性好;从 UIMA 2 升级需检查 JCas 与 API 弃用项。 - 多版本 JDK:Ubuntu 可通过
apt install openjdk-21-jdk-headless等并存多个版本,update-alternatives --config java切换默认。 - 换用新版 Maven:下载 Apache 官方二进制包解压到 /opt/apache-maven-3.9.x,PATH 中将其 bin 目录前置即可,apt 版无需卸载。
- Maven Central 保留全部历史版本,降级只需改版本号重建。
七、注意事项
- apt 的 Maven 3.6.3 默认绑定的 maven-compiler-plugin 是 3.1,不识别
maven.compiler.release,必须显式锁定插件版本,否则报Source option 5 is no longer supported。 - 用 java 直接运行 uimaj-tools 里的工具类时 classpath 至少包含 uimaj-tools、uimaj-core 与 slf4j-api 三个 jar,缺 slf4j-api 会报
NoClassDefFoundError: org/slf4j/Logger。 - JCasGen 无参数启动会尝试打开 Swing 图形界面,无图形环境抛 HeadlessException,务必使用命令行参数。
- 官方 GUI 工具(CAS Visual Debugger、Document Analyzer、CPM)在服务器无图形环境不可用,自动化场景请走 java 直跑或 API。
- Ubuntu 的 ANSI_X3.4-1968 平台编码(见 mvn -version 回显)不影响 UTF-8 源码编译,工程内已通过
project.build.sourceEncoding=UTF-8固定。
八、常见问题
Q1:编译报 Source option 5 is no longer supported. Use 7 or later.?
实测定位:apt Maven 3.6.3 默认解析的 maven-compiler-plugin 3.1 忽略 maven.compiler.release 属性。解决:pom 中显式声明 maven-compiler-plugin 3.13.0 并配置 <release>17</release>(见 4.3 节)。
Q2:运行 JCasGen 报 NoClassDefFoundError: org/slf4j/Logger?
实测定位:Jg 类依赖 slf4j-api,手工拼 classpath 时容易漏掉。解决:把 $HOME/.m2/repository/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar 加入 -cp;仅有 api 时会打印 SLF4J NOP 提示但功能正常,要日志可再加 slf4j-jdk14。
Q3:JCasGen 启动即抛 java.awt.HeadlessException?
实测定位:不带参数运行 Jg 会进入 GUI 模式。解决:命令行模式必须带 -jcasgeninput descriptors/TypeSystem.xml -jcasgenoutput src/main/java。
Q4:运行工程报 ClassNotFoundException: com.example.uima.RunPipeline?
实测定位:classpath 拼接错误或未执行 dependency:build-classpath 生成 target/cp.txt。解决:按 4.5 节重新生成 cp.txt,并用 -cp "target/classes:$(cat target/cp.txt)" 启动。
Q5:java -version 显示的不是 17?
执行 update-alternatives --config java 选择 openjdk-17 条目;或检查 JAVA_HOME 是否被其他 profile 覆盖。
九、卸载方式
apt-get remove -y maven openjdk-17-jdk-headless
apt-get autoremove -y
rm -f /etc/profile.d/uima-build.sh
rm -rf /opt/uima-demo
rm -rf /root/.m2
十、参考链接
- 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
- Ubuntu OpenJDK 软件包页:https://packages.ubuntu.com/jammy/openjdk-17-jdk-headless