Richard Zowalla created OPENNLP-1954:
----------------------------------------

             Summary: Make OpenNLP jars native-image friendly (reachability 
metadata, reflection fixes, docs, CI smoke test)
                 Key: OPENNLP-1954
                 URL: https://issues.apache.org/jira/browse/OPENNLP-1954
             Project: OpenNLP
          Issue Type: Improvement
          Components: Build, Packaging and Test, Documentation
            Reporter: Richard Zowalla


h2. Description

OpenNLP is a library, so we don't compile it to native code ourselves. 
Applications that use OpenNLP do that, through the GraalVM native-maven-plugin, 
Quarkus, Spring AOT and similar tools. The goal of this ticket is that such an 
application builds a working image with no extra OpenNLP-specific 
configuration, as long as it only uses built-in components and loads models 
from files or streams.

Background: dev@ thread "GraalVM native compilation" (Sep 2026): [dev@ 
discussion|https://lists.apache.org/thread/5bp2zb15y34wcwo58x6w1ttz7qsnbfts]

*In scope:* opennlp-api, opennlp-runtime, opennlp-ml-* (without DL), 
opennlp-model-resolver, opennlp-formats.

*Out of scope (separate proposals if there's demand):*
* Publishing native binaries of any kind (CLI, gRPC server). These would need a 
LEGAL review first.
* A native shared library / C API ({{native-image --shared}}, {{@CEntryPoint}}).
* Native support for opennlp-dl / ONNX Runtime (JNI). We only document its 
current status.
* Performance work. Native image improves startup time and memory footprint, 
not peak throughput.

h2. Known problem areas on main

# *Loading factories and extensions by class name.* {{ExtensionLoader}}, 
{{BaseToolFactory}}, {{TrainerFactory}} and {{DataIndexerFactory}} load classes 
by name. Factory class names also come from model manifests ({{BaseModel}}) and 
from XML feature generator descriptors ({{GeneratorFactory}}). Every built-in 
{{*Factory}}, trainer and {{AbstractXmlFeatureGeneratorFactory}} implementation 
needs to be registered.
# *Reflective constructors*
#* {{GenericModelReader}} / {{GenericModelWriter}}: reader and writer class 
looked up by model type
#* {{ClassPathModelLoader}}: {{modelType.getConstructor(InputStream.class)}}
#* {{DownloadUtil}}: {{type.getConstructor(Path.class)}}
#* {{ParallelNegLogLikelihood}}: {{taskClass.getConstructor(...)}}
#* Where it's cheap, replace these with explicit factories or lambdas (e.g. an 
{{EnumMap}} or {{Function<InputStream, T>}}) instead of adding metadata.
# *Java serialization.* {{BaseModel}} ({{readObject}}) and {{SvmDoccatModel}} 
({{ObjectInputStream}}) need serialization metadata, or we document them as 
unsupported.
# *Snowball stemmers.* {{Among}} uses {{MethodHandles.findVirtual}} on the 
stemmer classes, so we either register those methods or change the generated 
code.
# *Bundled resources* need a resource config:
#* stopword lists
#* {{uax29/*.txt}}
#* normalizer tables (CaseFolding, confusables, emoji-*)
#* {{ner-default-features.xml}}, {{pos-default-features.xml}}
#* {{opennlp.version}}
# *{{SimpleClassPathModelFinder}}* reads {{jdk.internal.loader.*}} and scans 
the classpath. That can't work in a native image. It should fail fast with a 
clear message: check the {{org.graalvm.nativeimage.imagecode}} system property, 
which avoids a dependency on the Graal SDK. The docs should point to file-based 
model loading instead.
# *CLI {{ArgumentParser}}* uses {{Proxy.newProxyInstance}}. This is low 
priority because the CLI isn't a target, but proxy metadata for the {{*Params}} 
interfaces is cheap to add.

h2. Proposed sub-tasks

# *Reachability metadata.* Add 
{{META-INF/native-image/org.apache.opennlp/<artifactId>/reachability-metadata.json}}
 to each affected module. Generate a baseline with the native-image-agent while 
running the test suite, then trim it by hand to what's actually needed.
# *Reduce reflection.* Replace reflective constructors in internal code paths 
with explicit factories (item 2 above). Reflection stays only where the class 
name comes from outside, i.e. manifests and descriptors.
# *Classpath model finder.* Make {{SimpleClassPathModelFinder}} detect 
native-image runtime and fail fast (item 6).
# *Guard test (JVM).* Add a unit test that fails when a new built-in factory, 
trainer or feature generator is added without a metadata entry, so the metadata 
doesn't silently go stale.
# *Native smoke test in CI*
#* Add a {{native}} Maven profile and a non-deployed IT module built with 
{{native-maven-plugin}}.
#* The test covers sentence detection, tokenization, POS tagging and NER, with 
models loaded from files.
#* Add a GitHub Actions job on {{graalvm/setup-graalvm}} (CE). It can start as 
non-blocking.
# *User documentation.* Add a "Using OpenNLP in a native image" section to 
opennlp-docs covering:
#* what works out of the box
#* loading models from files or streams instead of classpath scanning
#* registering custom factories, extensions and feature generators
#* Java serialization limits
#* opennlp-dl / ONNX Runtime status
#* sample {{native-maven-plugin}} config
#* CE caveats (startup and footprint improve, peak throughput does not)

h2. Acceptance criteria

* A sample app using only OpenNLP jars from Maven Central builds with 
{{native-maven-plugin}} and no user-supplied OpenNLP config. The native binary 
correctly runs sentence detection, tokenization, POS tagging and NER on models 
loaded from files.
* The native smoke test runs in CI.
* Unsupported paths (classpath model finder, DL module) fail with a clear error 
message and are documented.
* The documentation is published with the release.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to