-
Notifications
You must be signed in to change notification settings - Fork 392
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make SimpleDateFormat usages thread-local to fix date parsing corruption
SimpleDateFormat is not threadsafe since it mutates fields internally during calls to parse and format. This changes previously static instances of SimpleDateFormat into static thread-local instances to avoid the the possibility of data becoming corrupted as a result of concurrent accesses to these instances from multiple threads
- Loading branch information
1 parent
8d5c1bf
commit cecf824
Showing
3 changed files
with
56 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
.../java/org/openx/data/jsonserde/objectinspector/primitive/ThreadLocalSimpleDateFormat.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package org.openx.data.jsonserde.objectinspector.primitive; | ||
|
||
import java.text.ParseException; | ||
import java.text.SimpleDateFormat; | ||
import java.util.Date; | ||
import java.util.TimeZone; | ||
|
||
/** | ||
* Wraps a {@link SimpleDateFormat} instance with a {@link ThreadLocal} since the former is not thread safe and | ||
* mutates more than a few underlying fields directly during parsing. When used from multiple fields, this can | ||
* corrupt data in a way that may or may not cause exceptions (ie: can cause silent corruption). | ||
*/ | ||
final class ThreadLocalSimpleDateFormat { | ||
private final ThreadLocal<SimpleDateFormat> threadLocal; | ||
|
||
ThreadLocalSimpleDateFormat(final String pattern) { | ||
this(pattern, null); | ||
} | ||
|
||
ThreadLocalSimpleDateFormat(final String pattern, final TimeZone timeZone) { | ||
try { | ||
SimpleDateFormat format = new SimpleDateFormat(pattern); | ||
if (timeZone != null) { | ||
format.setTimeZone(timeZone); | ||
} | ||
} catch (Exception e) { | ||
throw new IllegalArgumentException("Failed to create ThreadLocalSimpleDateFormat with pattern: " + pattern, e); | ||
} | ||
this.threadLocal = new ThreadLocal<SimpleDateFormat>() { | ||
@Override | ||
public SimpleDateFormat initialValue() { | ||
SimpleDateFormat format = new SimpleDateFormat(pattern); | ||
if (timeZone != null) { | ||
format.setTimeZone(timeZone); | ||
} | ||
return format; | ||
} | ||
}; | ||
} | ||
|
||
public Date parse(String source) throws ParseException { | ||
return threadLocal.get().parse(source); | ||
} | ||
|
||
public String format(Date date) { | ||
return threadLocal.get().format(date); | ||
} | ||
|
||
public String format(long value) { | ||
return threadLocal.get().format(value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters