
Java SDK for building analytics pipelines that process and enrich unstructured text data with annotations, type systems, and modular analysis engines.
[Apache UIMA][UIMA] helps you managing unstructured data (such as texts) that is enriched useful information. For example, if you want to identify a mention of an entity in a text or possible link that entity to a reference dataset, then Apache UIMA provides:
Note the Apache UIMA Java SDK only provides a framework for building analytics but it does not provide any analytics. However, there are various third-parties that build on Apache UIMA and that provide collections of analysis components or ready-made solutions.
Apache UIMA v3.6.0 and later requires Java version 17 or later.
Running the Eclipse plugin tooling for UIMA requires you start Eclipse 4.25 (2022-09) or later using a Java 17 or later.
Running the migration tool on class files requires running with a Java JDK, not a Java JRE.
The supported platforms are: Windows, Linux, and macOS. Other Java platform implementations should work but have not been significantly tested.
Many of the scripts in the /bin directory invoke Java. They use the value of the environment
variable, JAVA_HOME, to locate the Java to use; if it is not set, they invoke java expecting to find
an appropriate Java in your PATH variable.
You can add the Apache UIMA Java SDK to your project easily in most build tools by importing it from [Maven Central][MAVEN-CENTRAL]. For example if you use Maven, you can add the following dependency to your project:
<dependency>
<groupId>org.apache.uima</groupId>
<artifactId>uimaj-core</artifactId>
<version>3.6.0</version>
</dependency>
Next, we give a few brief examples of how to use the Apache UIMA Java SDK and the Apache uimaFIT library. Apache uimaFIT is a separate dependency that you can add:
<dependency>
<groupId>org.apache.uima</groupId>
<artifactId>uimafit-core</artifactId>
<version>3.6.0</version>
</dependency>
The type system defines the type of information that we want to attach to the unstructured information (here a text document). In our example, we want to identify mentions of entities, so we define a type my.Entity with a feature category which can be used to store the category the entity belongs to.
To illustrate the information UIMA internally maintains about the annotation schema, we write the generated schema as XML to screen.
String TYPE_NAME_ENTITY = "my.Entity";
String TYPE_NAME_TOKEN = "my.Token";
String FEAT_NAME_CATEGORY = "category";
var tsd = UIMAFramework.getResourceSpecifierFactory().createTypeSystemDescription();
tsd.addType(TYPE_NAME_TOKEN, "", CAS.TYPE_NAME_ANNOTATION);
var entityTypeDesc = tsd.addType(TYPE_NAME_ENTITY, "", CAS.TYPE_NAME_ANNOTATION);
entityTypeDesc.addFeature(FEAT_NAME_CATEGORY, "", CAS.TYPE_NAME_STRING);
tsd.toXML(System.out);
Now we create a Common Analysis Structure (CAS) object into which we store the text that we want to analyse.
Again, to illustrate the information that UIMA internally stores in the CAS object, we write an XML representation of the object to screen.
var cas = CasFactory.createCas(tsd);
cas.setDocumentText("Welcome to Apache UIMA.");
cas.setDocumentLanguage("en");
CasIOUtils.save(cas, System.out, SerialFormat.XMI_PRETTY);
Now, we create an annotation of the type my.Entity to identify the mention of Apache UIMA in the example text.
Finally, we iterate over all annotations in the CAS and print them to screen. This includes the default DocumentAnnotation that is always created by UIMA as
well as the my.Entity annotation that we created ourselves.
var entityType = cas.getTypeSystem().getType(TYPE_NAME_ENTITY);
var entity = cas.createAnnotation(entityType, 11, 22);
cas.addFsToIndexes(entity);
for (var anno : cas.<Annotation>select(entityType)) {
System.out.printf("%s: [%s]%n", anno.getType().getName(), anno.getCoveredText());
}
In order to organize different types of analysis into steps, we usually package them into individual analysis engines. We illustrate now how such components can be built and how they can be put executed as an analysis pipeline.
class TokenAnnotator extends CasAnnotator_ImplBase {
public void process(CAS cas) throws AnalysisEngineProcessException {
var tokenType = cas.getTypeSystem().getType(TYPE_NAME_TOKEN);
var bi = BreakIterator.getWordInstance();
bi.setText(cas.getDocumentText());
int begin = bi.first();
int end;
for (end = bi.next(); end != BreakIterator.DONE; end = bi.next()) {
var token = cas.createAnnotation(tokenType, begin, end);
cas.addFsToIndexes(token);
begin = end;
}
}
}
class EntityAnnotator extends CasAnnotator_ImplBase {
public void process(CAS cas) throws AnalysisEngineProcessException {
var tokenType = cas.getTypeSystem().getType(TYPE_NAME_TOKEN);
var entityType = cas.getTypeSystem().getType(TYPE_NAME_ENTITY);
for (var token : cas.<Annotation>select(tokenType)) {
if (Character.isUpperCase(token.getCoveredText().charAt(0))) {
var entity = cas.createAnnotation(entityType, token.getBegin(), token.getEnd());
cas.addFsToIndexes(entity);
}
}
}
}
cas = CasFactory.createCas(tsd);
cas.setDocumentText("John likes Apache UIMA.");
cas.setDocumentLanguage("en");
var pipeline = AnalysisEngineFactory.createEngineDescription(
AnalysisEngineFactory.createEngineDescription(TokenAnnotator.class),
AnalysisEngineFactory.createEngineDescription(EntityAnnotator.class));
SimplePipeline.runPipeline(cas, pipeline);
for (var anno : cas.<Annotation>select(entityType)) {
System.out.printf("%s: [%s]%n", anno.getType().getName(), anno.getCoveredText());
}