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();
+ }
+ }
}