This is an automated email from the ASF dual-hosted git repository.
tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git
The following commit(s) were added to refs/heads/main by this push:
new 745d229f65 [TIKA-4934] tika-bundle-standard: do not embed dependencies
that are OSGi bundles (#3260)
745d229f65 is described below
commit 745d229f65f744e76c52a1ebf263371b93820b3d
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Mon Oct 5 20:40:19 2026 +0200
[TIKA-4934] tika-bundle-standard: do not embed dependencies that are OSGi
bundles (#3260)
Dependencies that ship proper OSGi manifests (commons-*, pdfbox, fontbox,
bouncycastle, jsoup, asm, xz, xmpcore, dd-plist) are no longer embedded
and must be deployed as separate bundles, together with pdfbox-io and
commons-logging 1.4+. This also fixes PDF parsing, which failed with a
NoClassDefFoundError because pdfbox-io was never embedded.
mime4j (unsatisfiable commons-io version range), rome (needs jdom2) and
jackcess (needs POI) stay embedded.
BundleIT now installs every jar in test-bundles, checks that the
optional imports of external dependencies are wired and parses a PDF
inside the container.
Assisted-By: Claude Opus 5.5 <[email protected]>
---
tika-bundles/tika-bundle-standard/pom.xml | 19 ---
.../test/java/org/apache/tika/bundle/BundleIT.java | 135 +++++++++++++++++----
tika-bundles/tika-bundle-standard/test-bundles.xml | 21 ++++
3 files changed, 130 insertions(+), 45 deletions(-)
diff --git a/tika-bundles/tika-bundle-standard/pom.xml
b/tika-bundles/tika-bundle-standard/pom.xml
index 3172c3b7b8..64aad04bef 100644
--- a/tika-bundles/tika-bundle-standard/pom.xml
+++ b/tika-bundles/tika-bundle-standard/pom.xml
@@ -101,39 +101,20 @@
tika-encoding-detector-*|
tika-langdetect-*|
jakarta.activation|
- commons-compress|
- xz|
- commons-codec|
- commons-csv|
- commons-io|
- commons-exec|
- commons-collections4|
- dd-plist|
junrar|
- pdfbox|
pdfbox-tools|
- fontbox|
- bcjmail-jdk18on|
- bcprov-jdk18on|
- bcpkix-jdk18on|
- bcutil-jdk18on|
poi|
poi-scratchpad|
poi-ooxml|
poi-ooxml-lite|
- commons-math3|
curvesapi|
xmlbeans|
jackcess|
jackcess-encrypt|
- commons-lang3|
- jsoup|
- asm|
juniversalchardet|
vorbis-java-core|
vorbis-java-tika|
metadata-extractor|
- xmpcore|
json-simple|
boilerpipe|
rome|
diff --git
a/tika-bundles/tika-bundle-standard/src/test/java/org/apache/tika/bundle/BundleIT.java
b/tika-bundles/tika-bundle-standard/src/test/java/org/apache/tika/bundle/BundleIT.java
index baf5fb4fdc..4e23b6635e 100644
---
a/tika-bundles/tika-bundle-standard/src/test/java/org/apache/tika/bundle/BundleIT.java
+++
b/tika-bundles/tika-bundle-standard/src/test/java/org/apache/tika/bundle/BundleIT.java
@@ -20,12 +20,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.io.File;
+import java.lang.reflect.Method;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -36,6 +42,7 @@ import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;
+import org.xml.sax.ContentHandler;
/**
* Integration test that boots an Apache Felix OSGi container, installs the
@@ -83,31 +90,24 @@ public class BundleIT {
ctx = framework.getBundleContext();
// Install all bundles first, then start.
- // tika-core requires osgi.serviceloader capabilities that are
- // provided by tika-bundle-standard, so both must be installed
- // before either can resolve. tika-core also imports the org.commonmark
- // packages (Markdown serialization), so those bundles must be present
too.
- Bundle commonsIo = install("commons-io.jar");
- Bundle commonsSecureXml = install("commons-secure-xml.jar");
- Bundle commonmark = install("commonmark.jar");
- Bundle commonmarkTables = install("commonmark-ext-gfm-tables.jar");
- Bundle commonmarkStrikethrough =
install("commonmark-ext-gfm-strikethrough.jar");
- Bundle tikaCore = install("tika-core.jar");
- Bundle tikaBundle = install("tika-bundle-standard.jar");
-
- commonsIo.start();
- commonsSecureXml.start();
- commonmark.start();
- commonmarkTables.start();
- commonmarkStrikethrough.start();
- tikaCore.start();
- tikaBundle.start();
- }
+ //
+ // tika-core requires osgi.serviceloader capabilities that are
provided by tika-bundle-standard,
+ // so both must be installed before either can resolve.
+ //
+ // The test-bundles directory also holds the dependencies of both that
are OSGi bundles themselves.
+ List<Bundle> bundles = new ArrayList<>();
+ try (DirectoryStream<Path> jars =
Files.newDirectoryStream(TEST_BUNDLES, "*.jar")) {
+ for (Path jar : jars) {
+ bundles.add(ctx.installBundle(jar.toUri().toString()));
+ }
+ }
+ assertNotNull(findBundle("org.apache.tika.core"), "tika-core bundle
not installed");
+ assertNotNull(findBundle("org.apache.tika.bundle-standard"),
+ "tika-bundle-standard not installed");
- private static Bundle install(String filename) throws Exception {
- File f = TEST_BUNDLES.resolve(filename).toFile();
- assertTrue(f.exists(), "Bundle not found: " + f);
- return ctx.installBundle(f.toURI().toString());
+ for (Bundle bundle : bundles) {
+ bundle.start();
+ }
}
@AfterAll
@@ -135,6 +135,47 @@ public class BundleIT {
assertTrue(hasBundle, "Standard bundle not found");
}
+ @Test
+ public void testAllBundlesActive() {
+ for (Bundle b : ctx.getBundles()) {
+ assertEquals(Bundle.ACTIVE, b.getState(), "Bundle not active: " +
b.getSymbolicName());
+ }
+ }
+
+ @Test
+ public void testExternalDependenciesWired() throws Exception {
+ // All imports of tika-bundle-standard are optional, so check that the
+ // packages of dependencies that are not embedded are actually wired.
+ Bundle tikaBundle = findBundle("org.apache.tika.bundle-standard");
+ assertNotNull(tikaBundle, "tika-bundle-standard not found");
+ for (String className : new String[]{
+ "com.adobe.internal.xmp.XMPMetaFactory",
+ "com.dd.plist.PropertyListParser",
+ "org.apache.commons.codec.digest.DigestUtils",
+ "org.apache.commons.collections4.MapUtils",
+ "org.apache.commons.compress.archivers.ArchiveStreamFactory",
+ "org.apache.commons.csv.CSVFormat",
+ "org.apache.commons.exec.CommandLine",
+ "org.apache.commons.io.IOUtils",
+ "org.apache.commons.lang3.StringUtils",
+ "org.apache.commons.math3.util.FastMath",
+ "org.apache.fontbox.ttf.TrueTypeFont",
+ "org.apache.pdfbox.Loader",
+ "org.apache.pdfbox.io.RandomAccessRead",
+ "org.bouncycastle.cms.CMSSignedData",
+ "org.bouncycastle.jce.provider.BouncyCastleProvider",
+ "org.jsoup.Jsoup",
+ "org.objectweb.asm.ClassReader"}) {
+ assertNotNull(tikaBundle.loadClass(className), className);
+ }
+ Bundle commonsCompress =
findBundle("org.apache.commons.commons-compress");
+ assertNotNull(commonsCompress, "commons-compress bundle not found");
+
assertNotNull(commonsCompress.loadClass("org.tukaani.xz.XZInputStream"));
+ Bundle tikaCore = findBundle("org.apache.tika.core");
+ assertNotNull(tikaCore, "tika-core bundle not found");
+
assertNotNull(tikaCore.loadClass("org.apache.commons.xml.secure.SecureSAXParserFactory"));
+ }
+
@Test
public void testDetectorServiceRegistered() throws Exception {
ServiceReference<?>[] refs = ctx.getAllServiceReferences(
@@ -183,6 +224,48 @@ public class BundleIT {
"Should have lots of parsers, found " + size);
}
+ @Test
+ public void testPdfParsing() throws Exception {
+ byte[] pdf = null;
+ try (ZipInputStream zip = new ZipInputStream(
+ BundleIT.class.getResourceAsStream("/test-documents.zip"))) {
+ for (ZipEntry entry = zip.getNextEntry(); entry != null; entry =
zip.getNextEntry()) {
+ if ("testPDF.pdf".equals(entry.getName())) {
+ pdf = zip.readAllBytes();
+ }
+ }
+ }
+ assertNotNull(pdf, "testPDF.pdf not found");
+
+ Bundle tikaCore = findBundle("org.apache.tika.core");
+ Class<?> metadataClass =
tikaCore.loadClass("org.apache.tika.metadata.Metadata");
+ Class<?> tisClass =
tikaCore.loadClass("org.apache.tika.io.TikaInputStream");
+ Class<?> contextClass =
tikaCore.loadClass("org.apache.tika.parser.ParseContext");
+ Method parse =
tikaCore.loadClass("org.apache.tika.parser.Parser").getMethod("parse",
+ tisClass, ContentHandler.class, metadataClass, contextClass);
+
+ Object metadata = metadataClass.getConstructor().newInstance();
+ metadataClass.getMethod("set", String.class, String.class)
+ .invoke(metadata, "Content-Type", "application/pdf");
+ ContentHandler handler = (ContentHandler) tikaCore
+ .loadClass("org.apache.tika.sax.BodyContentHandler")
+ .getConstructor(int.class).newInstance(-1);
+
+ // Uses PDFParser directly: parsing through the registered
DefaultParser
+ // service recurses, as TikaActivator feeds it back to itself.
+ Object parser = findBundle("org.apache.tika.bundle-standard")
+ .loadClass("org.apache.tika.parser.pdf.PDFParser")
+ .getConstructor().newInstance();
+ try (AutoCloseable tis = (AutoCloseable) tisClass.getMethod("get",
byte[].class)
+ .invoke(null, (Object) pdf)) {
+ parse.invoke(parser, tis, handler, metadata,
contextClass.getConstructor().newInstance());
+ }
+
+ Method get = metadataClass.getMethod("get", String.class);
+ assertEquals("Apache Tika - Apache Tika", get.invoke(metadata,
"dc:title"));
+ assertTrue(handler.toString().contains("Apache Tika"), "PDF content
not extracted");
+ }
+
@Test
public void testTikaClassLoadable() throws Exception {
// Verify key Tika classes can be loaded from the bundle's classloader
@@ -199,7 +282,7 @@ public class BundleIT {
assertNotNull(tikaBundle.loadClass("org.apache.tika.parser.microsoft.ooxml.OOXMLParser"));
}
- private Bundle findBundle(String symbolicName) {
+ private static Bundle findBundle(String symbolicName) {
for (Bundle b : ctx.getBundles()) {
if (symbolicName.equals(b.getSymbolicName())) {
return b;
diff --git a/tika-bundles/tika-bundle-standard/test-bundles.xml
b/tika-bundles/tika-bundle-standard/test-bundles.xml
index 4335b28a58..1cf9aca433 100644
--- a/tika-bundles/tika-bundle-standard/test-bundles.xml
+++ b/tika-bundles/tika-bundle-standard/test-bundles.xml
@@ -29,8 +29,29 @@
<includes>
<include>org.apache.tika:tika-core</include>
<include>org.apache.tika:tika-bundle-standard</include>
+ <!-- Dependencies that are OSGi bundles themselves (not embedded) -->
+ <include>com.adobe.xmp:xmpcore</include>
+ <include>com.googlecode.plist:dd-plist</include>
+ <include>commons-codec:commons-codec</include>
<include>commons-io:commons-io</include>
+ <include>commons-logging:commons-logging</include>
+ <include>org.apache.commons:commons-collections4</include>
+ <include>org.apache.commons:commons-compress</include>
+ <include>org.apache.commons:commons-csv</include>
+ <include>org.apache.commons:commons-exec</include>
+ <include>org.apache.commons:commons-lang3</include>
+ <include>org.apache.commons:commons-math3</include>
<include>org.apache.commons:commons-secure-xml</include>
+ <include>org.apache.pdfbox:fontbox</include>
+ <include>org.apache.pdfbox:pdfbox</include>
+ <include>org.apache.pdfbox:pdfbox-io</include>
+ <include>org.bouncycastle:bcjmail-jdk18on</include>
+ <include>org.bouncycastle:bcpkix-jdk18on</include>
+ <include>org.bouncycastle:bcprov-jdk18on</include>
+ <include>org.bouncycastle:bcutil-jdk18on</include>
+ <include>org.jsoup:jsoup</include>
+ <include>org.ow2.asm:asm</include>
+ <include>org.tukaani:xz</include>
<include>org.commonmark:commonmark</include>
<include>org.commonmark:commonmark-ext-gfm-tables</include>
<include>org.commonmark:commonmark-ext-gfm-strikethrough</include>