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>

Reply via email to