You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by ta...@apache.org on 2017/01/10 19:46:51 UTC
tika git commit: TIKA-2234 -- get rid of ThreadLocal
Repository: tika
Updated Branches:
refs/heads/master 86dbde4ec -> 66f6310ee
TIKA-2234 -- get rid of ThreadLocal
Project: http://git-wip-us.apache.org/repos/asf/tika/repo
Commit: http://git-wip-us.apache.org/repos/asf/tika/commit/66f6310e
Tree: http://git-wip-us.apache.org/repos/asf/tika/tree/66f6310e
Diff: http://git-wip-us.apache.org/repos/asf/tika/diff/66f6310e
Branch: refs/heads/master
Commit: 66f6310eec8e8aade078f0ca32a07399008ef1b1
Parents: 86dbde4
Author: tballison <ta...@mitre.org>
Authored: Tue Jan 10 14:46:42 2017 -0500
Committer: tballison <ta...@mitre.org>
Committed: Tue Jan 10 14:46:42 2017 -0500
----------------------------------------------------------------------
.../java/org/apache/tika/metadata/Metadata.java | 39 +-----------
.../java/org/apache/tika/utils/DateUtils.java | 34 +++++-----
.../org/apache/tika/metadata/TestMetadata.java | 65 +++++++++++++++++---
.../parser/image/ImageMetadataExtractor.java | 13 ++--
.../OOXMLWordAndPowerPointTextHandler.java | 4 +-
5 files changed, 85 insertions(+), 70 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tika/blob/66f6310e/tika-core/src/main/java/org/apache/tika/metadata/Metadata.java
----------------------------------------------------------------------
diff --git a/tika-core/src/main/java/org/apache/tika/metadata/Metadata.java b/tika-core/src/main/java/org/apache/tika/metadata/Metadata.java
index 08e4fa8..a5528c9 100644
--- a/tika-core/src/main/java/org/apache/tika/metadata/Metadata.java
+++ b/tika-core/src/main/java/org/apache/tika/metadata/Metadata.java
@@ -16,14 +16,11 @@
*/
package org.apache.tika.metadata;
-import static org.apache.tika.utils.DateUtils.MIDDAY;
-import static org.apache.tika.utils.DateUtils.UTC;
import static org.apache.tika.utils.DateUtils.formatDate;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
-import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
@@ -35,6 +32,7 @@ import java.util.Properties;
import java.util.TimeZone;
import org.apache.tika.metadata.Property.PropertyType;
+import org.apache.tika.utils.DateUtils;
/**
* A multi-valued metadata container.
@@ -92,24 +90,8 @@ public class Metadata implements CreativeCommons, Geographic, HttpHeaders,
/**
* Some parsers will have the date as a ISO-8601 string
* already, and will set that into the Metadata object.
- * So we can return Date objects for these, this is the
- * list (in preference order) of the various ISO-8601
- * variants that we try when processing a date based
- * property.
*/
- private static final DateFormat[] iso8601InputFormats = new DateFormat[] {
- // yyyy-mm-ddThh...
- createDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", UTC), // UTC/Zulu
- createDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", null), // With timezone
- createDateFormat("yyyy-MM-dd'T'HH:mm:ss", null), // Without timezone
- // yyyy-mm-dd hh...
- createDateFormat("yyyy-MM-dd' 'HH:mm:ss'Z'", UTC), // UTC/Zulu
- createDateFormat("yyyy-MM-dd' 'HH:mm:ssZ", null), // With timezone
- createDateFormat("yyyy-MM-dd' 'HH:mm:ss", null), // Without timezone
- // Date without time, set to Midday UTC
- createDateFormat("yyyy-MM-dd", MIDDAY), // Normal date format
- createDateFormat("yyyy:MM:dd", MIDDAY), // Image (IPTC/EXIF) format
- };
+ private static final DateUtils DATE_UTILS = new DateUtils();
private static DateFormat createDateFormat(String format, TimeZone timezone) {
SimpleDateFormat sdf =
@@ -129,22 +111,7 @@ public class Metadata implements CreativeCommons, Geographic, HttpHeaders,
* @return parsed date, or <code>null</code> if the date can't be parsed
*/
private static synchronized Date parseDate(String date) {
- // Java doesn't like timezones in the form ss+hh:mm
- // It only likes the hhmm form, without the colon
- int n = date.length();
- if (date.charAt(n - 3) == ':'
- && (date.charAt(n - 6) == '+' || date.charAt(n - 6) == '-')) {
- date = date.substring(0, n - 3) + date.substring(n - 2);
- }
-
- // Try several different ISO-8601 variants
- for (DateFormat format : iso8601InputFormats) {
- try {
- return format.parse(date);
- } catch (ParseException ignore) {
- }
- }
- return null;
+ return DATE_UTILS.tryToParse(date);
}
/**
http://git-wip-us.apache.org/repos/asf/tika/blob/66f6310e/tika-core/src/main/java/org/apache/tika/utils/DateUtils.java
----------------------------------------------------------------------
diff --git a/tika-core/src/main/java/org/apache/tika/utils/DateUtils.java b/tika-core/src/main/java/org/apache/tika/utils/DateUtils.java
index 6ccc74e..a358320 100644
--- a/tika-core/src/main/java/org/apache/tika/utils/DateUtils.java
+++ b/tika-core/src/main/java/org/apache/tika/utils/DateUtils.java
@@ -50,32 +50,25 @@ public class DateUtils {
*/
public static final TimeZone MIDDAY = TimeZone.getTimeZone("GMT-12:00");
- private static ThreadLocal<DateFormat> createDateFormat(String format, TimeZone timezone) {
+ private static DateFormat createDateFormat(String format, TimeZone timezone) {
final SimpleDateFormat sdf =
new SimpleDateFormat(format, new DateFormatSymbols(Locale.US));
if (timezone != null) {
sdf.setTimeZone(timezone);
}
- return new ThreadLocal<DateFormat>() {
- @Override
- public DateFormat initialValue() {
- return sdf;
- }
- };
+ return sdf;
}
/**
- * Some parsers will have the date as a ISO-8601 string
- * already, and will set that into the Metadata object.
* So we can return Date objects for these, this is the
* list (in preference order) of the various ISO-8601
* variants that we try when processing a date based
* property.
*/
- private static final List<ThreadLocal<DateFormat>> ISO_8601_INPUT_FORMATS = loadDateFormats();
+ private final List<DateFormat> iso8601InputFormats = loadDateFormats();
- private static List<ThreadLocal<DateFormat>> loadDateFormats() {
- List<ThreadLocal<DateFormat>> dateFormats = new ArrayList<>();
+ private List<DateFormat> loadDateFormats() {
+ List<DateFormat> dateFormats = new ArrayList<>();
// yyyy-mm-ddThh...
dateFormats.add(createDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", UTC)); // UTC/Zulu
dateFormats.add(createDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", null)); // With timezone
@@ -149,15 +142,24 @@ public class DateUtils {
/**
* Tries to parse the date string; returns null if no parse was possible.
*
- * This is thread safe because it relies on threadlocal dateformats.
+ * This is not thread safe! Wrap in synchronized or create new {@link DateUtils}
+ * for each class.
*
* @param dateString
* @return
*/
- public static Date tryToParse(String dateString) {
- for (ThreadLocal<DateFormat> df : ISO_8601_INPUT_FORMATS) {
+ public Date tryToParse(String dateString) {
+ // Java doesn't like timezones in the form ss+hh:mm
+ // It only likes the hhmm form, without the colon
+ int n = dateString.length();
+ if (dateString.charAt(n - 3) == ':'
+ && (dateString.charAt(n - 6) == '+' || dateString.charAt(n - 6) == '-')) {
+ dateString = dateString.substring(0, n - 3) + dateString.substring(n - 2);
+ }
+
+ for (DateFormat df : iso8601InputFormats) {
try {
- return df.get().parse(dateString);
+ return df.parse(dateString);
} catch (java.text.ParseException e){
}
http://git-wip-us.apache.org/repos/asf/tika/blob/66f6310e/tika-core/src/test/java/org/apache/tika/metadata/TestMetadata.java
----------------------------------------------------------------------
diff --git a/tika-core/src/test/java/org/apache/tika/metadata/TestMetadata.java b/tika-core/src/test/java/org/apache/tika/metadata/TestMetadata.java
index b9bb14f..e44b277 100644
--- a/tika-core/src/test/java/org/apache/tika/metadata/TestMetadata.java
+++ b/tika-core/src/test/java/org/apache/tika/metadata/TestMetadata.java
@@ -17,14 +17,7 @@
package org.apache.tika.metadata;
//JDK imports
-import java.util.Date;
-import java.util.Properties;
-import org.apache.tika.utils.DateUtils;
-import org.junit.Test;
-
-
-//Junit imports
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -32,6 +25,24 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.Random;
+import java.util.TimeZone;
+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 org.apache.tika.utils.DateUtils;
+import org.junit.Test;
+
+//Junit imports
+
/**
* JUnit based tests of class {@link org.apache.tika.metadata.Metadata}.
*/
@@ -372,5 +383,43 @@ public class TestMetadata {
// Fetch as the aliases
assertEquals(message, meta.get(Metadata.DESCRIPTION));
assertEquals(message, meta.get("testDescriptionAlt"));
- }
+ }
+
+ @Test
+ public void testMultithreadedDates() throws Exception {
+ int numThreads = 10;
+ ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
+ ExecutorCompletionService<Integer> executorCompletionService = new ExecutorCompletionService<Integer>(executorService);
+ for (int i = 0; i < numThreads; i++) {
+ executorCompletionService.submit(new MetadataDateAdder());
+ }
+ int finished = 0;
+ while (finished < numThreads) {
+ Future<Integer> future = executorCompletionService.take();
+ if (future != null && future.isDone()) {
+ Integer retVal = future.get();
+ finished++;
+ }
+ }
+
+ }
+
+ private class MetadataDateAdder implements Callable<Integer> {
+ private final Random random = new Random();
+ @Override
+ public Integer call() throws Exception {
+ for (int i = 0; i < 1000; i++) {
+ Metadata m = new Metadata();
+ long start = new Date().getTime();
+ start += random.nextInt(1000000);
+ Date now = new Date(start);
+ DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
+ m.set(TikaCoreProperties.CREATED, df.format(now));
+ df.setTimeZone(TimeZone.getTimeZone("UTC"));
+ assertTrue(Math.abs(now.getTime() - m.getDate(TikaCoreProperties.CREATED).getTime()) < 2000);
+
+ }
+ return 1;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/tika/blob/66f6310e/tika-parsers/src/main/java/org/apache/tika/parser/image/ImageMetadataExtractor.java
----------------------------------------------------------------------
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/image/ImageMetadataExtractor.java b/tika-parsers/src/main/java/org/apache/tika/parser/image/ImageMetadataExtractor.java
index ccc84d8..0c2cf4d 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/image/ImageMetadataExtractor.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/image/ImageMetadataExtractor.java
@@ -341,12 +341,9 @@ public class ImageMetadataExtractor {
static class ExifHandler implements DirectoryHandler {
// There's a new ExifHandler for each file processed, so this is thread safe
- private static final ThreadLocal<SimpleDateFormat> DATE_UNSPECIFIED_TZ = new ThreadLocal<SimpleDateFormat>() {
- @Override
- protected SimpleDateFormat initialValue() {
- return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
- }
- };
+ private final SimpleDateFormat dateUnspecifiedTz =
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
+
public boolean supports(Class<? extends Directory> directoryType) {
return directoryType == ExifIFD0Directory.class ||
@@ -487,7 +484,7 @@ public class ImageMetadataExtractor {
// Unless we have GPS time we don't know the time zone so date must be set
// as ISO 8601 datetime without timezone suffix (no Z or +/-)
if (original != null) {
- String datetimeNoTimeZone = DATE_UNSPECIFIED_TZ.get().format(original); // Same time zone as Metadata Extractor uses
+ String datetimeNoTimeZone = dateUnspecifiedTz.format(original); // Same time zone as Metadata Extractor uses
metadata.set(TikaCoreProperties.CREATED, datetimeNoTimeZone);
metadata.set(Metadata.ORIGINAL_DATE, datetimeNoTimeZone);
}
@@ -495,7 +492,7 @@ public class ImageMetadataExtractor {
if (directory.containsTag(ExifIFD0Directory.TAG_DATETIME)) {
Date datetime = directory.getDate(ExifIFD0Directory.TAG_DATETIME);
if (datetime != null) {
- String datetimeNoTimeZone = DATE_UNSPECIFIED_TZ.get().format(datetime);
+ String datetimeNoTimeZone = dateUnspecifiedTz.format(datetime);
metadata.set(TikaCoreProperties.MODIFIED, datetimeNoTimeZone);
// If Date/Time Original does not exist this might be creation date
if (metadata.get(TikaCoreProperties.CREATED) == null) {
http://git-wip-us.apache.org/repos/asf/tika/blob/66f6310e/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLWordAndPowerPointTextHandler.java
----------------------------------------------------------------------
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLWordAndPowerPointTextHandler.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLWordAndPowerPointTextHandler.java
index 8cd84d9..b3a9f68 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLWordAndPowerPointTextHandler.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLWordAndPowerPointTextHandler.java
@@ -144,7 +144,7 @@ public class OOXMLWordAndPowerPointTextHandler extends DefaultHandler {
private OOXMLWordAndPowerPointTextHandler.EditType editType = OOXMLWordAndPowerPointTextHandler.EditType.NONE;
-
+ private DateUtils dateUtils = new DateUtils();
public OOXMLWordAndPowerPointTextHandler(XWPFBodyContentsHandler bodyContentsHandler,
Map<String, String> hyperlinks) {
this.bodyContentsHandler = bodyContentsHandler;
@@ -313,7 +313,7 @@ public class OOXMLWordAndPowerPointTextHandler extends DefaultHandler {
String editDateString = atts.getValue(W_NS, "date");
Date editDate = null;
if (editDateString != null) {
- editDate = DateUtils.tryToParse(editDateString);
+ editDate = dateUtils.tryToParse(editDateString);
}
bodyContentsHandler.startEditedSection(editAuthor, editDate, editType);
this.editType = editType;