Skip to content

Commit b69760a

Browse files
committed
Merge branch 'master' into ng-3.0
2 parents f8af119 + 84bbd8e commit b69760a

File tree

8 files changed

+99
-24
lines changed

8 files changed

+99
-24
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ language: java
22

33
# Since Jackson 2.7, build requires jdk7, although module itself works on jdk6 still (for now)
44
jdk:
5-
- oraclejdk7
6-
- oraclejdk8
5+
- openjdk7
6+
- openjdk8
77

88
# Below this line is configuration for deploying to the Sonatype OSS repo
99
# http://blog.xeiam.com/2013/05/configure-travis-ci-to-deploy-snapshots.html

release-notes/CREDITS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,8 @@ Bertrand Renuart (brenuart@github)
636636
(2.9.1)
637637
* Contributed #1749: StdDateFormat: performance improvement of '_format(..)' method
638638
(2.9.1)
639+
* Contributed #1759: Reuse `Calendar` instance during parsing by `StdDateFormat`
640+
(2.9.1)
639641

640642
Kevin Gallardo (newkek@github)
641643
* Reported #1658: Infinite recursion when deserializing a class extending a Map,

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Project: jackson-databind
1717
(suggested by Bertrand R)
1818
#1749: StdDateFormat: performance improvement of '_format(..)' method
1919
(contributed by Bertrand R)
20+
#1759: Reuse `Calendar` instance during parsing by `StdDateFormat`
21+
(contributed by Bertrand R)
2022
- Fix `DelegatingDeserializer` constructor to pass `handledType()` (and
2123
not type of deserializer being delegated to!)
2224

src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -397,14 +397,7 @@ public StringBuffer format(Date date, StringBuffer toAppendTo,
397397
protected void _format(TimeZone tz, Locale loc, Date date,
398398
StringBuffer buffer)
399399
{
400-
Calendar cal = _calendar;
401-
if (cal == null ) {
402-
_calendar = cal = (Calendar)CALENDAR.clone();
403-
}
404-
if (!cal.getTimeZone().equals(tz) ) {
405-
cal.setTimeZone(tz);
406-
}
407-
// Note: Calendar locale not updated since we don't need it here...
400+
Calendar cal = _getCalendar(tz);
408401
cal.setTime(date);
409402

410403
pad4(buffer, cal.get(Calendar.YEAR));
@@ -561,7 +554,7 @@ protected Date parseAsISO8601(String dateStr, ParsePosition pos)
561554
}
562555
}
563556

564-
protected Date _parseAsISO8601(String dateStr, ParsePosition pos)
557+
protected Date _parseAsISO8601(String dateStr, ParsePosition bogus)
565558
throws IllegalArgumentException, ParseException
566559
{
567560
final int totalLen = dateStr.length();
@@ -570,10 +563,8 @@ protected Date _parseAsISO8601(String dateStr, ParsePosition pos)
570563
if ((_timezone != null) && ('Z' != dateStr.charAt(totalLen-1))) {
571564
tz = _timezone;
572565
}
573-
Calendar cal = new GregorianCalendar(tz, _locale);
574-
if (_lenient != null) {
575-
cal.setLenient(_lenient.booleanValue());
576-
}
566+
Calendar cal = _getCalendar(tz);
567+
cal.clear();
577568
String formatStr;
578569
if (totalLen <= 10) {
579570
Matcher m = PATTERN_PLAIN.matcher(dateStr);
@@ -645,8 +636,7 @@ protected Date _parseAsISO8601(String dateStr, ParsePosition pos)
645636
throw new ParseException(String.format(
646637
"Cannot parse date \"%s\": invalid fractional seconds '%s'; can use at most 9 digits",
647638
dateStr, m.group(1).substring(1)
648-
),
649-
pos.getErrorIndex());
639+
), start);
650640
}
651641
// fall through
652642
case 3:
@@ -669,7 +659,9 @@ protected Date _parseAsISO8601(String dateStr, ParsePosition pos)
669659
throw new ParseException
670660
(String.format("Cannot parse date \"%s\": while it seems to fit format '%s', parsing fails (leniency? %s)",
671661
dateStr, formatStr, _lenient),
672-
pos.getErrorIndex());
662+
// [databind#1742]: Might be able to give actual location, some day, but for now
663+
// we can't give anything more indicative
664+
0);
673665
}
674666

675667
private static int _parse4D(String str, int index) {
@@ -721,6 +713,18 @@ protected void _clearFormats() {
721713
_formatRFC1123 = null;
722714
}
723715

716+
protected Calendar _getCalendar(TimeZone tz) {
717+
Calendar cal = _calendar;
718+
if (cal == null ) {
719+
_calendar = cal = (Calendar)CALENDAR.clone();
720+
}
721+
if (!cal.getTimeZone().equals(tz) ) {
722+
cal.setTimeZone(tz);
723+
}
724+
cal.setLenient(isLenient());
725+
return cal;
726+
}
727+
724728
protected static <T> boolean _equals(T value1, T value2) {
725729
if (value1 == value2) {
726730
return true;

src/test/java/com/fasterxml/jackson/databind/deser/jdk/MapDeserializationTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ public void testIntBooleanMap() throws Exception
225225
String JSON = "{ \"1\" : true, \"-1\" : false }";
226226
Map<Object,Object> result = MAPPER.readValue
227227
(JSON, new TypeReference<HashMap<Integer,Boolean>>() { });
228-
229228
assertNotNull(result);
230229
assertEquals(HashMap.class, result.getClass());
231230
assertEquals(2, result.size());

src/test/java/com/fasterxml/jackson/databind/deser/jdk/UntypedDeserializationTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ public void testSampleDoc() throws Exception
176176
// and that's all folks!
177177
}
178178

179+
@SuppressWarnings("unlikely-arg-type")
179180
public void testUntypedMap() throws Exception
180181
{
181182
// to get "untyped" default map-to-map, pass Object.class

src/test/java/com/fasterxml/jackson/databind/misc/RaceCondition738Test.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public void testRepeatedly() throws Exception {
6363
}
6464

6565
void runOnce(int round, int max) throws Exception {
66-
final ObjectMapper mapper = getObjectMapper();
66+
final ObjectMapper mapper = newObjectMapper();
6767
Callable<String> writeJson = new Callable<String>() {
6868
@Override
6969
public String call() throws Exception {
@@ -92,8 +92,4 @@ public String call() throws Exception {
9292
}
9393
}
9494
}
95-
96-
private static ObjectMapper getObjectMapper() {
97-
return new ObjectMapper();
98-
}
9995
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.fasterxml.jackson.databind.misc;
2+
3+
import java.io.IOException;
4+
import java.util.ArrayList;
5+
import java.util.Date;
6+
import java.util.List;
7+
import java.util.concurrent.*;
8+
import java.util.concurrent.atomic.AtomicInteger;
9+
10+
import com.fasterxml.jackson.databind.*;
11+
12+
public class ThreadSafety1759Test extends BaseMapTest
13+
{
14+
/*
15+
/**********************************************************
16+
/* Test methods
17+
/**********************************************************
18+
*/
19+
20+
public void testCalendarForDeser() throws Exception
21+
{
22+
final ObjectMapper mapper = newObjectMapper();
23+
24+
final int numThreads = 4;
25+
final int COUNT = 3000;
26+
final AtomicInteger counter = new AtomicInteger();
27+
28+
// IMPORTANT! Use different timestamp for every thread
29+
List<Callable<Throwable>> calls = new ArrayList<Callable<Throwable>>();
30+
for (int thread = 1; thread <= numThreads; ++thread) {
31+
final String json = quote(String.format("2017-01-%02dT16:30:49Z", thread));
32+
final long timestamp = mapper.readValue(json, Date.class).getTime();
33+
34+
calls.add(createCallable(thread, mapper, json, timestamp, COUNT, counter));
35+
}
36+
37+
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
38+
List<Future<Throwable>> results = new ArrayList<>();
39+
for (Callable<Throwable> c : calls) {
40+
results.add(executor.submit(c));
41+
}
42+
executor.shutdown();
43+
for (Future<Throwable> f : results) {
44+
Throwable t = f.get(5, TimeUnit.SECONDS);
45+
if (t != null) {
46+
fail("Exception during processing: "+t.getMessage());
47+
}
48+
}
49+
assertEquals(numThreads * COUNT, counter.get());
50+
}
51+
52+
private Callable<Throwable> createCallable(final int threadId,
53+
final ObjectMapper mapper,
54+
final String json, final long timestamp,
55+
final int count, final AtomicInteger counter)
56+
{
57+
return new Callable<Throwable>() {
58+
@Override
59+
public Throwable call() throws IOException {
60+
for (int i = 0; i < count; ++i) {
61+
Date dt = mapper.readValue(json, Date.class);
62+
if (dt.getTime() != timestamp) {
63+
return new IllegalArgumentException("Wrong timestamp (thread id "+threadId+", input: "+json+": should get "+timestamp+", got "+dt.getTime());
64+
}
65+
counter.addAndGet(1);
66+
}
67+
return null;
68+
}
69+
};
70+
}
71+
}

0 commit comments

Comments
 (0)