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

tallison pushed a commit to branch branch_3x
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/branch_3x by this push:
     new dc44d8dd4 TIKA-4393 (#2163)
dc44d8dd4 is described below

commit dc44d8dd401c176b49274570de9c433c16bd616f
Author: Tim Allison <[email protected]>
AuthorDate: Thu Mar 13 14:20:03 2025 -0400

    TIKA-4393 (#2163)
    
    (cherry picked from commit 933fae954da025f62d147b367c1a16494e883925)
---
 CHANGES.txt                                        |  2 +
 .../org/apache/tika/xmp/convert/TikaToXMP.java     | 22 +++++------
 .../java/org/apache/tika/xmp/TikaToXMPTest.java    | 44 ++++++++++++++++++++++
 3 files changed, 55 insertions(+), 13 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index ad8aed774..45581e8cc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,7 @@
 Release 3.2.0 - ???
 
+  * Fix concurrency bug in TikaToXMP (TIKA-4393)
+
   * Improve extraction of properties from msg files (TIKA_4381).
 
 Release 3.1.0 - 01/28/25
diff --git a/tika-xmp/src/main/java/org/apache/tika/xmp/convert/TikaToXMP.java 
b/tika-xmp/src/main/java/org/apache/tika/xmp/convert/TikaToXMP.java
index 152b94191..73a2b4f02 100644
--- a/tika-xmp/src/main/java/org/apache/tika/xmp/convert/TikaToXMP.java
+++ b/tika-xmp/src/main/java/org/apache/tika/xmp/convert/TikaToXMP.java
@@ -36,10 +36,13 @@ import org.apache.tika.parser.odf.OpenDocumentParser;
 
 public class TikaToXMP {
     /**
-     * Map from mimetype to converter class Must only be accessed through
-     * <code>getConverterMap</code>
+     * Map from mimetype to converter class
      */
-    private static Map<MediaType, Class<? extends ITikaToXMPConverter>> 
converterMap;
+    private static final Map<MediaType, Class<? extends ITikaToXMPConverter>> 
CONVERTER_MAP = new HashMap<>();
+
+    static {
+        initialize();
+    }
 
     // --- public API implementation---
 
@@ -114,7 +117,7 @@ public class TikaToXMP {
         MediaType type = MediaType.parse(mimetype);
 
         if (type != null) {
-            return (getConverterMap().get(type) != null);
+            return (CONVERTER_MAP.get(type) != null);
         }
 
         return false;
@@ -137,7 +140,7 @@ public class TikaToXMP {
         MediaType type = MediaType.parse(mimetype);
 
         if (type != null) {
-            Class<? extends ITikaToXMPConverter> clazz = 
getConverterMap().get(type);
+            Class<? extends ITikaToXMPConverter> clazz = 
CONVERTER_MAP.get(type);
             if (clazz != null) {
                 try {
                     converter = clazz.getDeclaredConstructor().newInstance();
@@ -154,13 +157,6 @@ public class TikaToXMP {
 
     // --- Private methods ---
 
-    private static Map<MediaType, Class<? extends ITikaToXMPConverter>> 
getConverterMap() {
-        if (converterMap == null) {
-            converterMap = new HashMap<>();
-            initialize();
-        }
-        return converterMap;
-    }
 
     /**
      * Initializes the map with supported converters.
@@ -187,7 +183,7 @@ public class TikaToXMP {
     private static void addConverter(Set<MediaType> supportedTypes,
                                      Class<? extends ITikaToXMPConverter> 
converter) {
         for (MediaType type : supportedTypes) {
-            getConverterMap().put(type, converter);
+            CONVERTER_MAP.put(type, converter);
         }
     }
 }
diff --git a/tika-xmp/src/test/java/org/apache/tika/xmp/TikaToXMPTest.java 
b/tika-xmp/src/test/java/org/apache/tika/xmp/TikaToXMPTest.java
index b16358ee8..da25605f7 100644
--- a/tika-xmp/src/test/java/org/apache/tika/xmp/TikaToXMPTest.java
+++ b/tika-xmp/src/test/java/org/apache/tika/xmp/TikaToXMPTest.java
@@ -23,6 +23,14 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
 import com.adobe.internal.xmp.XMPConst;
 import com.adobe.internal.xmp.XMPException;
 import com.adobe.internal.xmp.XMPIterator;
@@ -225,4 +233,40 @@ public class TikaToXMPTest {
             TikaToXMP.getConverter(null);
         });
     }
+
+    @Test
+    public void testMultithreaded() throws Exception {
+        int numThreads = 10;
+        final int numIterations = 100;
+        ExecutorService executorService = 
Executors.newFixedThreadPool(numThreads);
+        try {
+            ExecutorCompletionService<Integer> executorCompletionService = new 
ExecutorCompletionService<>(executorService);
+            for (int i = 0; i < numThreads; i++) {
+                executorCompletionService.submit(new Callable<Integer>() {
+                    @Override
+                    public Integer call() throws Exception {
+                        for (int j = 0; j < numIterations; j++) {
+                            Metadata m = new Metadata();
+                            setupOOXMLMetadata(m);
+                            m.set(Metadata.CONTENT_TYPE, OOXML_MIMETYPE);
+                            XMPMeta xmp = TikaToXMP.convert(m);
+                            checkOOXMLMetadata(xmp);
+                        }
+                        return 1;
+                    }
+                });
+            }
+            int finished = 0;
+            while (finished < numThreads) {
+                Future<Integer> future = executorCompletionService.poll(1, 
TimeUnit.MINUTES);
+                if (future == null) {
+                    throw new TimeoutException();
+                }
+                future.get();
+                finished++;
+            }
+        } finally {
+            executorService.shutdownNow();
+        }
+    }
 }

Reply via email to