This is an automated email from the ASF dual-hosted git repository.

rzo1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opennlp.git


The following commit(s) were added to refs/heads/main by this push:
     new 1b6ef868 OPENNLP-1567 - OpenNLP Models: Provide a Finder / Loader 
Implementation (#606)
1b6ef868 is described below

commit 1b6ef8683af30df10bc937802fa2389f5094a878
Author: Richard Zowalla <[email protected]>
AuthorDate: Tue Jun 18 08:57:39 2024 +0200

    OPENNLP-1567 - OpenNLP Models: Provide a Finder / Loader Implementation 
(#606)
    
    * OPENNLP-1567 - OpenNLP Models: Provide a Finder / Loader Implementation
---
 README.md                                          |   1 +
 opennlp-tools-models/pom.xml                       | 105 +++++++++
 .../tools/models/AbstractClassPathModelFinder.java | 123 +++++++++++
 .../java/opennlp/tools/models/ClassPathModel.java  |  35 +++
 .../opennlp/tools/models/ClassPathModelEntry.java  |  24 +++
 .../opennlp/tools/models/ClassPathModelFinder.java |  32 +++
 .../opennlp/tools/models/ClassPathModelLoader.java |  56 +++++
 .../models/classgraph/ClassgraphModelFinder.java   |  75 +++++++
 .../models/simple/SimpleClassPathModelFinder.java  | 236 +++++++++++++++++++++
 .../tools/models/AbstractClassPathFinderTest.java  |  56 +++++
 .../tools/models/AbstractClassPathModelTest.java   |  57 +++++
 .../tools/models/AbstractModelLoaderTest.java      |  38 ++++
 .../tools/models/AbstractModelUsageTest.java       |  61 ++++++
 .../classgraph/ClassgraphModelFinderTest.java      |  33 +++
 .../classgraph/ClassgraphModelLoaderTest.java      |  33 +++
 .../classgraph/ClassgraphModelUsageTest.java       |  33 +++
 .../simple/SimpleClassPathModelFinderTest.java     |  34 +++
 .../tools/models/simple/SimpleModelLoaderTest.java |  33 +++
 .../tools/models/simple/SimpleModelUsageTest.java  |  33 +++
 pom.xml                                            |   5 +-
 20 files changed, 1102 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 9177023b..79e69a1a 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,7 @@ You can also follow our [mailing 
lists](http://opennlp.apache.org/mailing-lists.
 Currently, the library has different packages:
 
 * `opennlp-tools` : The core toolkit.
+* `opennlp-tools-models` : A set of classes to load [OpenNLP 
models](https://github.com/apache/opennlp-models) from the classpath.
 * `opennlp-uima` : A set of [Apache UIMA](https://uima.apache.org) annotators.
 * `opennlp-brat-annotator` : A set of annotators for 
[BRAT](http://brat.nlplab.org/)
 * `opennlp-morfologik-addon` : An addon for Morfologik
diff --git a/opennlp-tools-models/pom.xml b/opennlp-tools-models/pom.xml
new file mode 100644
index 00000000..79b4b5f3
--- /dev/null
+++ b/opennlp-tools-models/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+   distributed with this work for additional information
+   regarding copyright ownership.  The ASF licenses this file
+   to you under the Apache License, Version 2.0 (the
+   "License"); you may not use this file except in compliance
+   with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing,
+   software distributed under the License is distributed on an
+   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+   KIND, either express or implied.  See the License for the
+   specific language governing permissions and limitations
+   under the License.
+-->
+
+<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>
+    <parent>
+        <groupId>org.apache.opennlp</groupId>
+        <artifactId>opennlp</artifactId>
+        <version>2.3.4-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>opennlp-tools-models</artifactId>
+    <packaging>jar</packaging>
+    <name>Apache OpenNLP Tools Models</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.opennlp</groupId>
+            <artifactId>opennlp-tools</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>io.github.classgraph</groupId>
+            <artifactId>classgraph</artifactId>
+            <version>${classgraph.version}</version>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- models -->
+        <dependency>
+            <groupId>org.apache.opennlp</groupId>
+            <artifactId>opennlp-models-sentdetect-en</artifactId>
+            <version>${opennlp.models.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.opennlp</groupId>
+            <artifactId>opennlp-models-langdetect</artifactId>
+            <version>${opennlp.models.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${maven.surefire.plugin}</version>
+                <configuration>
+                    <argLine>-Xmx2048m 
-Dorg.slf4j.simpleLogger.defaultLogLevel=off --add-opens 
java.base/jdk.internal.loader=ALL-UNNAMED</argLine>
+                    <forkCount>${opennlp.forkCount}</forkCount>
+                    <useSystemClassLoader>false</useSystemClassLoader>
+                    <failIfNoSpecifiedTests>false</failIfNoSpecifiedTests>
+                </configuration>
+            </plugin>
+
+        </plugins>
+    </build>
+</project>
diff --git 
a/opennlp-tools-models/src/main/java/opennlp/tools/models/AbstractClassPathModelFinder.java
 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/AbstractClassPathModelFinder.java
new file mode 100644
index 00000000..eeaa43d9
--- /dev/null
+++ 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/AbstractClassPathModelFinder.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Enables the detection of OpenNLP models in the classpath. By default, this 
class will search
+ * for JAR files starting with "opennlp-models-*". This wildcard pattern can 
be adjusted by
+ * using the alternative constructor of this class.
+ */
+public abstract class AbstractClassPathModelFinder implements 
ClassPathModelFinder {
+
+  protected static final String JAR = "jar";
+
+  private final String jarModelPrefix;
+  private Set<ClassPathModelEntry> models;
+
+  /**
+   * By default, it scans for "opennlp-models-*.jar".
+   */
+  public AbstractClassPathModelFinder() {
+    this(OPENNLP_MODEL_JAR_PREFIX);
+  }
+
+  /**
+   * @param jarModelPrefix The leafnames of the jars that should be canned 
(e.g. "opennlp.jar").
+   *                       May contain a wildcard glob ("opennlp-*.jar"). It 
must not be {@code null}.
+   */
+  public AbstractClassPathModelFinder(String jarModelPrefix) {
+    Objects.requireNonNull(jarModelPrefix, "modelJarPrefix must not be null");
+    this.jarModelPrefix = jarModelPrefix;
+  }
+
+  @Override
+  public Set<ClassPathModelEntry> findModels(boolean reloadCache) {
+
+    if (this.models == null || reloadCache) {
+      final List<URI> classpathModels = getMatchingURIs("*.bin", getContext());
+      final List<URI> classPathProperties = 
getMatchingURIs("model.properties", getContext());
+
+      this.models = new HashSet<>();
+
+      for (URI model : classpathModels) {
+        URI m = null;
+        for (URI prop : classPathProperties) {
+          if (jarPathsMatch(model, prop)) {
+            m = prop;
+            break;
+          }
+        }
+        this.models.add(new ClassPathModelEntry(model, 
Optional.ofNullable(m)));
+
+      }
+    }
+    return this.models;
+  }
+
+  /**
+   * Subclasses can implement this method to provide additional context to
+   * {@link AbstractClassPathModelFinder#getMatchingURIs(String, Object)}.
+   *
+   * @return a context information. May be {@code null}.
+   */
+  protected abstract Object getContext();
+
+  /**
+   * Return matching classpath URIs for the given pattern.
+   *
+   * @param wildcardPattern the pattern. Must not be {@code null}.
+   * @param context         an object holding context information. It might be 
{@code null}.
+   * @return a list of matching classpath URIs.
+   */
+  protected abstract List<URI> getMatchingURIs(String wildcardPattern, Object 
context);
+
+  protected boolean jarPathsMatch(URI uri1, URI uri2) {
+    final String[] parts1 = parseJarURI(uri1);
+    final String[] parts2 = parseJarURI(uri2);
+
+    if (parts1 == null || parts2 == null) {
+      return false;
+    }
+
+    return parts1[0].equals(parts2[0]);
+  }
+
+  protected String[] parseJarURI(URI uri) {
+    if (JAR.equals(uri.getScheme())) {
+      final String ssp = uri.getSchemeSpecificPart();
+      final int separatorIndex = ssp.indexOf("!/");
+      if (separatorIndex > 0) {
+        final String jarFileUri = ssp.substring(0, separatorIndex);
+        final String entryPath = ssp.substring(separatorIndex + 2);
+        return new String[] {jarFileUri, entryPath};
+      }
+    }
+    return null;
+  }
+
+  protected String getJarModelPrefix() {
+    return jarModelPrefix;
+  }
+
+}
diff --git 
a/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModel.java 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModel.java
new file mode 100644
index 00000000..e89f310a
--- /dev/null
+++ 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModel.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models;
+
+import java.util.Properties;
+
+public record ClassPathModel(Properties properties, byte[] model) {
+
+  public String getModelVersion() {
+    return properties != null ? properties.getProperty("model.version", 
"unknown") : "unknown";
+  }
+
+  public String getModelName() {
+    return properties != null ? properties.getProperty("model.name", 
"unknown") : "unknown";
+  }
+
+  public String getModelSHA256() {
+    return properties != null ? properties.getProperty("model.sha256", 
"unknown") : "unknown";
+  }
+
+}
diff --git 
a/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModelEntry.java
 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModelEntry.java
new file mode 100644
index 00000000..ef09f414
--- /dev/null
+++ 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModelEntry.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models;
+
+import java.net.URI;
+import java.util.Optional;
+
+public record ClassPathModelEntry(URI model, Optional<URI> properties) {
+
+}
diff --git 
a/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModelFinder.java
 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModelFinder.java
new file mode 100644
index 00000000..5603d325
--- /dev/null
+++ 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModelFinder.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models;
+
+import java.util.Set;
+
+public interface ClassPathModelFinder {
+
+  String OPENNLP_MODEL_JAR_PREFIX = "opennlp-models-*.jar";
+
+  /**
+   * Finds OpenNLP models within the classpath.
+   *
+   * @param reloadCache {@code true}, if the internal cache should explicitly 
be reloaded
+   * @return A Set of {@link ClassPathModelEntry ClassPathModelEntries}. It 
might be empty.
+   */
+  Set<ClassPathModelEntry> findModels(boolean reloadCache);
+}
diff --git 
a/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModelLoader.java
 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModelLoader.java
new file mode 100644
index 00000000..a236fa34
--- /dev/null
+++ 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/ClassPathModelLoader.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Objects;
+import java.util.Properties;
+
+/**
+ * Responsible for loading OpenNLP models from the classpath.
+ */
+public class ClassPathModelLoader {
+
+  /**
+   * Loads a {@link ClassPathModel} from a {@link ClassPathModelEntry}
+   *
+   * @param entry must not be {@code null}.
+   * @return a {@link ClassPathModel} containing the model resources.
+   * @throws IOException thrown if something went wrong during reading 
resources from the classpath.
+   */
+  public ClassPathModel load(ClassPathModelEntry entry) throws IOException {
+    Objects.requireNonNull(entry, "entry must not be null");
+    Objects.requireNonNull(entry.properties(), "entry.properties() must not be 
null");
+    Objects.requireNonNull(entry.model(), "entry.model() must not be null");
+
+    final Properties properties = new Properties();
+
+    if (entry.properties().isPresent()) {
+      try (InputStream inputStream = 
entry.properties().get().toURL().openStream()) {
+        properties.load(inputStream);
+      }
+    }
+
+    final byte[] model;
+    try (InputStream inputStream = entry.model().toURL().openStream()) {
+      model = inputStream.readAllBytes();
+    }
+
+    return new ClassPathModel(properties, model);
+  }
+}
diff --git 
a/opennlp-tools-models/src/main/java/opennlp/tools/models/classgraph/ClassgraphModelFinder.java
 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/classgraph/ClassgraphModelFinder.java
new file mode 100644
index 00000000..642dc20b
--- /dev/null
+++ 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/classgraph/ClassgraphModelFinder.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models.classgraph;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ResourceList;
+import io.github.classgraph.ScanResult;
+
+import opennlp.tools.models.AbstractClassPathModelFinder;
+import opennlp.tools.models.ClassPathModelFinder;
+
+/**
+ * Enables the detection of OpenNLP models in the classpath via classgraph.
+ * By default, this class will search for JAR files starting with 
"opennlp-models-*".
+ * This wildcard pattern can be adjusted by using the alternative constructor 
of this class.
+ */
+public class ClassgraphModelFinder extends AbstractClassPathModelFinder 
implements ClassPathModelFinder {
+
+  /**
+   * By default, it scans for "opennlp-models-*.jar".
+   */
+  public ClassgraphModelFinder() {
+    this(OPENNLP_MODEL_JAR_PREFIX);
+  }
+
+  /**
+   * @param modelJarPrefix The leafnames of the jars that should be canned 
(e.g. "opennlp.jar").
+   *                       May contain a wildcard glob ("opennlp-*.jar"). It 
must not be {@code null}.
+   */
+  public ClassgraphModelFinder(String modelJarPrefix) {
+    super(modelJarPrefix);
+  }
+
+  /**
+   * @return a {@link ScanResult} ready for consumption. Caller is responsible 
for closing it.
+   */
+  @Override
+  protected Object getContext() {
+    return new 
ClassGraph().acceptJars(getJarModelPrefix()).disableDirScanning().scan();
+  }
+
+  /**
+   * @param wildcardPattern the pattern. Must not be {@code null}.
+   * @param context         an object holding context information. It might be 
{@code null}.
+   * @return a list of matching classpath uris.
+   */
+  @Override
+  protected List<URI> getMatchingURIs(String wildcardPattern, Object context) {
+    if (context instanceof ScanResult sr) {
+      try (sr; final ResourceList resources = 
sr.getResourcesMatchingWildcard(wildcardPattern)) {
+        return resources.getURIs();
+      }
+    }
+    return Collections.emptyList();
+  }
+
+}
diff --git 
a/opennlp-tools-models/src/main/java/opennlp/tools/models/simple/SimpleClassPathModelFinder.java
 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/simple/SimpleClassPathModelFinder.java
new file mode 100644
index 00000000..0d00f385
--- /dev/null
+++ 
b/opennlp-tools-models/src/main/java/opennlp/tools/models/simple/SimpleClassPathModelFinder.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models.simple;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Locale;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import opennlp.tools.models.AbstractClassPathModelFinder;
+import opennlp.tools.models.ClassPathModelFinder;
+
+/**
+ * Enables the detection of OpenNLP models in the classpath via JDK classes
+ * By default, this class will search for JAR files starting with 
"opennlp-models-*".
+ * This wildcard pattern can be adjusted by using the alternative constructor 
of this class.
+ * <p>
+ * It is a rather simple implementation of scanning the classpath by trying to 
obtain {@link URL urls}
+ * from the actual classpath via a chain of possible options. It might not 
work for every use-case
+ * since it relies on JDK internals only and doesn't account for classloader 
hierarchies or edge-cases.
+ * <p>
+ * It will
+ * (1) Try to see if we have a {@link URLClassLoader} available in the current 
thread.
+ * (2) Try to obtain URLs via the build in classloader via reflections
+ * (requires {@code --add-opens java.base/jdk.internal.loader=ALL-UNNAMED} as 
JVM argument)
+ * (3) Try to use the bootstrap classpath via {@code java.class.path}.
+ * <p>
+ * If you need a more sophisticated solution,
+ * use {@link opennlp.tools.models.classgraph.ClassgraphModelFinder}.
+ */
+public class SimpleClassPathModelFinder extends AbstractClassPathModelFinder 
implements ClassPathModelFinder {
+
+  private static final Logger logger = 
LoggerFactory.getLogger(SimpleClassPathModelFinder.class);
+  private static final String FILE_PREFIX = "file";
+  private static final Pattern CLASSPATH_SEPARATOR_PATTERN = 
Pattern.compile("[;:]");
+  // ; for Windows, : for Linux/OSX
+
+  /**
+   * By default, it scans for "opennlp-models-*.jar".
+   */
+  public SimpleClassPathModelFinder() {
+    this(OPENNLP_MODEL_JAR_PREFIX);
+  }
+
+  /**
+   * @param modelJarPrefix The leafnames of the jars that should be canned 
(e.g. "opennlp.jar").
+   *                       May contain a wildcard glob ("opennlp-*.jar"). It 
must not be {@code null}.
+   */
+  public SimpleClassPathModelFinder(String modelJarPrefix) {
+    super(modelJarPrefix);
+  }
+
+  /**
+   * @return always {@code NULL} as it is not needed for the simple case.
+   */
+  @Override
+  protected Object getContext() {
+    return null; //not needed for the simple case. Just return NULL.
+  }
+
+  /**
+   * @param wildcardPattern the pattern. Must not be {@code null}.
+   * @param context         an object holding context information.
+   *                        It is unused within this implementation.
+   * @return a list of matching classpath uris.
+   */
+  @Override
+  protected List<URI> getMatchingURIs(String wildcardPattern, Object context) {
+    if (wildcardPattern == null) {
+      return Collections.emptyList();
+    }
+
+    final boolean isWindows = isWindows();
+    final List<URL> cp = getClassPathElements();
+    final List<URI> cpu = new ArrayList<>();
+    final Pattern jarPattern = Pattern.compile(asRegex("*" + 
getJarModelPrefix()));
+    final Pattern filePattern = Pattern.compile(asRegex("*" + 
wildcardPattern));
+
+    for (URL url : cp) {
+      if (matchesPattern(url, jarPattern)) {
+        try {
+          for (URI u : getURIsFromJar(url, isWindows)) {
+            if (matchesPattern(u.toURL(), filePattern)) {
+              cpu.add(u);
+            }
+          }
+        } catch (IOException e) {
+          logger.warn("Cannot read content of {}.", url, e);
+        }
+      }
+    }
+
+    return cpu;
+  }
+
+  /**
+   * Escapes a wildcard expressions for usage as a Java regular expression.
+   *
+   * @param wildcard must not be {@code null}.
+   * @return the escaped regex.
+   */
+  private String asRegex(String wildcard) {
+    return wildcard
+        .replace(".", "\\.")
+        .replace("*", ".*")
+        .replace("?", ".");
+  }
+
+  private boolean matchesPattern(URL url, Pattern pattern) {
+    return pattern.matcher(url.getFile()).matches();
+  }
+
+  private List<URI> getURIsFromJar(URL fileUrl, boolean isWindows) throws 
IOException {
+    final List<URI> uris = new ArrayList<>();
+    final URL jarUrl = new URL(JAR + ":" +
+        (isWindows
+            ? fileUrl.toString().replace("\\", "/")
+            : fileUrl.toString())
+        + "!/");
+    final JarURLConnection jarConnection = (JarURLConnection) 
jarUrl.openConnection();
+    try (JarFile jarFile = jarConnection.getJarFile()) {
+      final Enumeration<JarEntry> entries = jarFile.entries();
+      while (entries.hasMoreElements()) {
+        final JarEntry entry = entries.nextElement();
+        if (!entry.isDirectory()) {
+          final URL entryUrl = new URL(jarUrl + entry.getName());
+          try {
+            uris.add(entryUrl.toURI());
+          } catch (URISyntaxException ignored) {
+            //if we cannot convert to URI here, we ignore that entry.
+          }
+        }
+      }
+    }
+
+    return uris;
+  }
+
+  private boolean isWindows() {
+    return System.getProperty("os.name", 
"unknown").toLowerCase(Locale.ROOT).contains("win");
+  }
+
+  /**
+   * Try to obtain URLs from the classpath in the following order:
+   * <p>
+   * (1) Try to see if we have a {@link URLClassLoader}.
+   * (2) Try to obtain URLs via the build in classloader via reflections 
(requires an add opens JVM argument)
+   * (3) Try to use the bootstrap classpath via {@code java.class.path}.
+   *
+   * @return a list of URLs within the classpath.
+   */
+  private List<URL> getClassPathElements() {
+    final ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+    if (cl instanceof URLClassLoader ucl) {
+      return Arrays.asList(ucl.getURLs());
+    } else {
+      final URL[] fromUcp = getURLs(cl);
+      if (fromUcp != null && fromUcp.length > 0) {
+        return Arrays.asList(fromUcp);
+      } else {
+        final String cp = System.getProperty("java.class.path", "");
+        final Matcher matcher = CLASSPATH_SEPARATOR_PATTERN.matcher(cp);
+        final List<URL> jarUrls = new ArrayList<>();
+        while (matcher.find()) {
+          try {
+            jarUrls.add(new URL(FILE_PREFIX, "", matcher.group()));
+          } catch (MalformedURLException ignored) {
+            //if we cannot parse a URL from the system property, just ignore 
it...
+            //we couldn't load it anyway
+          }
+        }
+        return jarUrls;
+      }
+    }
+  }
+
+  /*
+   * Java 9+ Bridge to obtain URLs from classpath.
+   * This requires "--add-opens java.base/jdk.internal.loader=ALL-UNNAMED" as 
JVM argument
+   */
+  private URL[] getURLs(ClassLoader classLoader) {
+    try {
+      final Class<?> builtinClazzLoader = 
Class.forName("jdk.internal.loader.BuiltinClassLoader");
+
+      final Field ucpField = builtinClazzLoader.getDeclaredField("ucp");
+      ucpField.setAccessible(true);
+
+      final Object ucpObject = ucpField.get(classLoader);
+      final Class<?> clazz = Class.forName("jdk.internal.loader.URLClassPath");
+
+      if (ucpObject != null) {
+        final Method getURLs = clazz.getMethod("getURLs");
+
+        return (URL[]) getURLs.invoke(ucpObject);
+      }
+
+    } catch (Exception ignored) {
+      //ok here because we still have a fallback and this is just one step in 
the chain of possible
+      //options to obtain URLs from the classpath
+    }
+    return new URL[0];
+  }
+}
diff --git 
a/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractClassPathFinderTest.java
 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractClassPathFinderTest.java
new file mode 100644
index 00000000..e03f08ce
--- /dev/null
+++ 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractClassPathFinderTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public abstract class AbstractClassPathFinderTest extends 
AbstractClassPathModelTest {
+
+
+  @Test
+  public void testFindOpenNLPModels() {
+    final ClassPathModelFinder finder = getModelFinder();
+
+    final Set<ClassPathModelEntry> models = finder.findModels(false);
+    assertNotNull(models);
+    assertEquals(2, models.size());
+
+    for (ClassPathModelEntry entry : models) {
+      assertNotNull(entry.model());
+      assertNotNull(entry.properties());
+      assertFalse(entry.properties().isEmpty());
+    }
+
+    //call it twice, yields same results
+    final Set<ClassPathModelEntry> reloadedModels = finder.findModels(false);
+    assertNotNull(reloadedModels);
+    assertEquals(models, reloadedModels);
+
+    //call it with reload cache, yields same results
+    final Set<ClassPathModelEntry> cacheReloadedModels = 
finder.findModels(true);
+    assertNotNull(cacheReloadedModels);
+    assertEquals(models, cacheReloadedModels);
+    assertEquals(reloadedModels, cacheReloadedModels);
+  }
+
+}
diff --git 
a/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractClassPathModelTest.java
 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractClassPathModelTest.java
new file mode 100644
index 00000000..478cd46b
--- /dev/null
+++ 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractClassPathModelTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models;
+
+import java.io.IOException;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public abstract class AbstractClassPathModelTest {
+
+  protected ClassPathModel getClassPathModel(String modelJarPrefix) throws 
IOException {
+    return getClassPathModel(modelJarPrefix, false);
+  }
+
+  protected ClassPathModel getClassPathModel(String modelJarPrefix, boolean 
expectNotFound)
+      throws IOException {
+    final ClassPathModelFinder finder = getModelFinder(modelJarPrefix);
+
+    final Set<ClassPathModelEntry> models = finder.findModels(false);
+    assertNotNull(models);
+    if (expectNotFound) {
+      assertEquals(0, models.size());
+      return null;
+    } else {
+      assertFalse(models.isEmpty());
+      final ClassPathModelEntry entry = models.iterator().next();
+      assertNotNull(entry);
+      final ClassPathModelLoader loader = new ClassPathModelLoader();
+      final ClassPathModel model = loader.load(entry);
+      assertNotNull(model);
+      assertNotNull(model.model());
+      assertNotNull(model.properties());
+      return model;
+    }
+  }
+
+  protected abstract ClassPathModelFinder getModelFinder();
+
+  protected abstract ClassPathModelFinder getModelFinder(String pattern);
+}
diff --git 
a/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractModelLoaderTest.java
 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractModelLoaderTest.java
new file mode 100644
index 00000000..7850c934
--- /dev/null
+++ 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractModelLoaderTest.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public abstract class AbstractModelLoaderTest extends 
AbstractClassPathModelTest {
+
+  @Test
+  public void testLoadOpenNLPModel() throws Exception {
+    final ClassPathModel model = 
getClassPathModel("opennlp-models-langdetect-*.jar");
+    assertNotNull(model);
+    assertNotNull(model.model());
+    assertNotNull(model.properties());
+    
assertEquals("2ddf585fac2e02a9dcfb9a4a9cc9417562eaac351be2efb506a2eaa87f19e9d4",
+        model.getModelSHA256());
+    assertEquals("langdetect-183.bin", model.getModelName());
+    assertEquals("1.8.3", model.getModelVersion());
+  }
+
+}
diff --git 
a/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractModelUsageTest.java
 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractModelUsageTest.java
new file mode 100644
index 00000000..bab564d5
--- /dev/null
+++ 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/AbstractModelUsageTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.junit.jupiter.api.Test;
+
+import opennlp.tools.langdetect.LanguageDetector;
+import opennlp.tools.langdetect.LanguageDetectorME;
+import opennlp.tools.langdetect.LanguageDetectorModel;
+import opennlp.tools.sentdetect.SentenceDetector;
+import opennlp.tools.sentdetect.SentenceDetectorME;
+import opennlp.tools.sentdetect.SentenceModel;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public abstract class AbstractModelUsageTest extends 
AbstractClassPathModelTest {
+
+  @Test
+  public void testLanguageDetection() throws IOException {
+    final ClassPathModel model = 
getClassPathModel("opennlp-models-langdetect-*.jar");
+    final LanguageDetectorModel ldModel = new LanguageDetectorModel(new 
ByteArrayInputStream(model.model()));
+    assertNotNull(ldModel);
+    final LanguageDetector languageDetector = new LanguageDetectorME(ldModel);
+    assertNotNull(languageDetector);
+
+    assertEquals("eng",
+        languageDetector.predictLanguage("The English language is pretty 
impressive.").getLang());
+
+  }
+
+  @Test
+  public void testSentenceDetector() throws IOException {
+    final ClassPathModel model = 
getClassPathModel("opennlp-models-sentdetect-*.jar");
+    final SentenceModel sentenceModel = new SentenceModel(new 
ByteArrayInputStream(model.model()));
+    assertNotNull(sentenceModel);
+    final SentenceDetector sentenceDetector = new 
SentenceDetectorME(sentenceModel);
+    assertNotNull(sentenceDetector);
+
+    assertEquals(2, sentenceDetector.sentDetect("Pretty impressive stuff. I 
like it!").length);
+
+  }
+
+}
diff --git 
a/opennlp-tools-models/src/test/java/opennlp/tools/models/classgraph/ClassgraphModelFinderTest.java
 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/classgraph/ClassgraphModelFinderTest.java
new file mode 100644
index 00000000..f5751f98
--- /dev/null
+++ 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/classgraph/ClassgraphModelFinderTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models.classgraph;
+
+import opennlp.tools.models.AbstractClassPathFinderTest;
+import opennlp.tools.models.ClassPathModelFinder;
+
+public class ClassgraphModelFinderTest extends AbstractClassPathFinderTest {
+
+  @Override
+  protected ClassPathModelFinder getModelFinder() {
+    return new ClassgraphModelFinder();
+  }
+
+  @Override
+  protected ClassgraphModelFinder getModelFinder(String pattern) {
+    return new ClassgraphModelFinder(pattern);
+  }
+}
diff --git 
a/opennlp-tools-models/src/test/java/opennlp/tools/models/classgraph/ClassgraphModelLoaderTest.java
 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/classgraph/ClassgraphModelLoaderTest.java
new file mode 100644
index 00000000..c7ef5308
--- /dev/null
+++ 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/classgraph/ClassgraphModelLoaderTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models.classgraph;
+
+import opennlp.tools.models.AbstractModelLoaderTest;
+import opennlp.tools.models.ClassPathModelFinder;
+
+public class ClassgraphModelLoaderTest extends AbstractModelLoaderTest {
+
+  @Override
+  protected ClassPathModelFinder getModelFinder() {
+    return new ClassgraphModelFinder();
+  }
+
+  @Override
+  protected ClassPathModelFinder getModelFinder(String pattern) {
+    return new ClassgraphModelFinder(pattern);
+  }
+}
diff --git 
a/opennlp-tools-models/src/test/java/opennlp/tools/models/classgraph/ClassgraphModelUsageTest.java
 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/classgraph/ClassgraphModelUsageTest.java
new file mode 100644
index 00000000..3ab61506
--- /dev/null
+++ 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/classgraph/ClassgraphModelUsageTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models.classgraph;
+
+import opennlp.tools.models.AbstractModelUsageTest;
+import opennlp.tools.models.ClassPathModelFinder;
+
+public class ClassgraphModelUsageTest extends AbstractModelUsageTest {
+
+  @Override
+  protected ClassPathModelFinder getModelFinder() {
+    return new ClassgraphModelFinder();
+  }
+
+  @Override
+  protected ClassPathModelFinder getModelFinder(String pattern) {
+    return new ClassgraphModelFinder(pattern);
+  }
+}
diff --git 
a/opennlp-tools-models/src/test/java/opennlp/tools/models/simple/SimpleClassPathModelFinderTest.java
 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/simple/SimpleClassPathModelFinderTest.java
new file mode 100644
index 00000000..faab41d7
--- /dev/null
+++ 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/simple/SimpleClassPathModelFinderTest.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models.simple;
+
+import opennlp.tools.models.AbstractClassPathFinderTest;
+import opennlp.tools.models.ClassPathModelFinder;
+
+public class SimpleClassPathModelFinderTest extends 
AbstractClassPathFinderTest {
+
+
+  @Override
+  protected ClassPathModelFinder getModelFinder() {
+    return new SimpleClassPathModelFinder();
+  }
+
+  @Override
+  protected ClassPathModelFinder getModelFinder(String pattern) {
+    return new SimpleClassPathModelFinder(pattern);
+  }
+}
diff --git 
a/opennlp-tools-models/src/test/java/opennlp/tools/models/simple/SimpleModelLoaderTest.java
 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/simple/SimpleModelLoaderTest.java
new file mode 100644
index 00000000..83d7caf1
--- /dev/null
+++ 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/simple/SimpleModelLoaderTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models.simple;
+
+import opennlp.tools.models.AbstractModelLoaderTest;
+import opennlp.tools.models.ClassPathModelFinder;
+
+public class SimpleModelLoaderTest extends AbstractModelLoaderTest {
+
+  @Override
+  protected ClassPathModelFinder getModelFinder() {
+    return new SimpleClassPathModelFinder();
+  }
+
+  @Override
+  protected ClassPathModelFinder getModelFinder(String pattern) {
+    return new SimpleClassPathModelFinder(pattern);
+  }
+}
diff --git 
a/opennlp-tools-models/src/test/java/opennlp/tools/models/simple/SimpleModelUsageTest.java
 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/simple/SimpleModelUsageTest.java
new file mode 100644
index 00000000..65ea5a83
--- /dev/null
+++ 
b/opennlp-tools-models/src/test/java/opennlp/tools/models/simple/SimpleModelUsageTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package opennlp.tools.models.simple;
+
+import opennlp.tools.models.AbstractModelUsageTest;
+import opennlp.tools.models.ClassPathModelFinder;
+
+public class SimpleModelUsageTest extends AbstractModelUsageTest {
+
+  @Override
+  protected ClassPathModelFinder getModelFinder() {
+    return new SimpleClassPathModelFinder();
+  }
+
+  @Override
+  protected ClassPathModelFinder getModelFinder(String pattern) {
+    return new SimpleClassPathModelFinder(pattern);
+  }
+}
diff --git a/pom.xml b/pom.xml
index a406164e..4c8ae881 100644
--- a/pom.xml
+++ b/pom.xml
@@ -177,6 +177,8 @@
                <slf4j.version>1.7.36</slf4j.version>
                <log4j2.version>2.23.1</log4j2.version>
                <jmh.version>1.37</jmh.version>
+               <classgraph.version>4.8.173</classgraph.version>
+               <opennlp.models.version>1.0.0-SNAPSHOT</opennlp.models.version>
                <opennlp.forkCount>1.0C</opennlp.forkCount>
                <coveralls.maven.plugin>4.3.0</coveralls.maven.plugin>
                <jacoco.maven.plugin>0.8.11</jacoco.maven.plugin>
@@ -555,6 +557,7 @@
                <module>opennlp-distr</module>
                <module>opennlp-dl</module>
                <module>opennlp-dl-gpu</module>
-  </modules>
+        <module>opennlp-tools-models</module>
+    </modules>
 
 </project>

Reply via email to