CentOS 8 安装 Apache UIMA 指南

在 CentOS 8 服务器上从零安装并运行 Apache UIMA 3.6.1:安装 OpenJDK 17 与 Maven,创建 Maven 工程引入 uimaj-core,编写最小分析管线完成运行验证与报错排查。

Apache UIMAApache UIMA2026/10/12

CentOS 8 安装 Apache UIMA 指南

一、环境信息

本文所有命令与输出均在 CentOS 8 服务器上以 root 用户实测取得。

NAME="CentOS Linux"
VERSION="8"
ID="centos"

实测软件版本:

软件实测版本说明
OpenJDK17.0.1dnf 仓库的 java-17-openjdk,UIMA 3.x 要求 JDK 11+
Maven3.9.16Apache 官方二进制包,安装在 /opt
uimaj-core3.6.1Maven Central 当前最新稳定版

内存与磁盘满足常规构建即可,本次实测主机内存 32 GB、12 核 CPU,小型工程构建仅占用数百 MB 堆。

二、前置条件

  1. 服务器可以访问公网 Maven Central 仓库(https://repo.maven.apache.org/maven2)与 Apache 下载站点。
  2. 拥有 root 权限。
  3. 已安装 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 保留全部历史版本,回退只需改版本号重新构建。

七、注意事项

  1. dnf install maven 会连带安装 java-1.8.0 并可能把 alternatives 的 java/javac 重置为 1.8,安装后务必复查 java -version。
  2. java-17-openjdk-headless 没有 javac,必须补装 java-17-openjdk-devel。
  3. uimaj 3.6.1 的 class 文件版本是 61.0(Java 17),用 Java 8 运行其工具类会直接抛 UnsupportedClassVersionError。
  4. JCasGen 的 Jg 类无参数启动会进入 GUI 模式,无图形环境将抛 HeadlessException;命令行模式必须带 -jcasgeninput/-jcasgenoutput 参数。
  5. 官方 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 按提示选择。

十、参考链接